In WinDbg I can search the memory for bytes using the s command, e.g.
s 0012ff40 L?2000 48 65 6c 6c 6f
Is there also a way to include unknown bytes in the search sequence, e.g.
s 0012ff40 L?2000 48 65 ?? ?? ?? 6c 6f
where ?? is a byte with an arbitrary value?
Idea
How about doing ((memory XOR 48 65 00 00 00 6c 6f) AND FF FF 00 00 00 FF FF) and compare that against 00 00 00 00 00 00 00? But I don't know how to do that in WinDbg either.
Am not sure if the search command supports wild card. But you can use .foreach command, to achieve what you want.
Here is a sample that i used to search a memory pattern such as ff ?? 00
.foreach (hit {s -[1]b 00007ffabc520000 L100 ff }) {db hit L3; s ${hit}+2 L1 00}
Here is a brief description of how it works :
NOTE - Open up the debugger help from windbg to get complete documentation. That is within Windbg, Help | Contents
{s -[1]b 00007ffabc520000 L100 ff }
Use -[1] flag with s, so that only the memory address is given as the output.
s ${hit}+2 L1 00
For each hit, pass that memory address to the next search command. Increase the memory by the number of bytes that you want to skip and mention the last part of search pattern.
db hit L3
From the memory that has the beginning of the patter, dump the entire length. This is just to confirm that we are getting the right results!
Hope this helps. In case you need further clarification, i can try to provide that as well.
We can use pykd to achieve this. Find the downloads linked from PyKD Wiki or PyKD Downloads. When using WinDbg Preview, copy the DLLs into
%LOCALAPPDATA%\DBG\EngineExtensions
for 64 bit or
%LOCALAPPDATA%\DBG\EngineExtensions32
for 32 Bit.
Since this is only the WinDbg extension, you also need the Python module as well:
pip install pykd
Use the power of Python to do what WinDbg can't do. Save the following script in a good place for WinDbg, i.e. in a short path without spaces.
from pykd import *
import sys
import re
import struct
if len(sys.argv)<4:
print("Wildcard search for memory")
print("Usage:", sys.argv[0], "<address> <length> <pattern> [-v]", sep=" ")
print(" <address>: Memory address where searching begins.")
print(" This can be a WinDbg expression like ntdll!NtCreateThreadEx.")
print(" <length> : Number of bytes that will be considered as the haystack.")
print(" <pattern>: Bytes that you're looking for. May contain ?? for unknown bytes.")
print(" [-v] : (optional) Verbose output")
print()
print("Examples:")
print(" ", sys.argv[0], "00770000 L50 01 02 03 ?? 05")
print(" will find 01 02 03 04 05 or 01 02 03 FF 05, if present in memory")
sys.exit(0)
verbose = False
if sys.argv[-1][0:2] == "-v":
verbose = True
if verbose:
for n in range(1, len(sys.argv)):
print(f"param {n}: " + sys.argv[n])
address = expr(sys.argv[1])
if verbose: print("Start address:", "0x{:08x}".format(address), sep=" ")
length = sys.argv[2]
length = length.replace("L?","") # consider large address range syntax
length = length.replace("L","") # consider address range syntax
length = expr(length)
if verbose: print("Length:", "0n"+str(length), "bytes", sep=" ")
regex = b""
for n in range(3, len(sys.argv) - 1 if verbose else 0):
if sys.argv[n] == "??":
regex += bytes(".", "ascii")
else:
char = struct.pack("B", expr(sys.argv[n]))
if char == b".":
regex += struct.pack("B", ord("\\"))
regex += char
if verbose: print("Regex:", regex, sep=" ")
memorycontent = loadBytes(address, length)
if verbose: print("Memory:", memorycontent, sep=" ")
result = re.search(regex, bytes(memorycontent))
print("Found:", ' '.join("0x{:02x}".format(x) for x in result.group(0)), "at address", "0x{:08x}".format(address+result.start()))
The script constructs a Regex for a Bytes object. It uses . for the wildcard and escapes literal . to \..
Let's prepare a proper sample in WinDbg:
0:006> .dvalloc 1000
Allocated 1000 bytes starting at 00900000
0:000> eu 0x00900000 "Test.with.regex"
0:000> db 0x00900000 L0n30
00900000 54 00 65 00 73 00 74 00-2e 00 77 00 69 00 74 00 T.e.s.t...w.i.t.
00900010 68 00 2e 00 72 00 65 00-67 00 65 00 78 00 h...r.e.g.e.x.
Load the PyKD extension, so we'll be able to run the script:
0:006> .load pykd
and run the script:
0:000> !py d:\debug\scripts\memwild.py 00900000 L10 2e ?? 77
Found: 0x2e 0x00 0x77 at address 0x00900008
If the range of the search is not insanely large you could copy/paste the hex dump into sublime text and just do a find with regex mode enabled. For example I was looking for (1200 < X < 2400)
add esp, X
ret
In sublime text I searched using the regex 81 c4 .. .. .. 00 c3 and found an address with instructions for
add esp,600h
ret
Related
I have a small socket filter type eBPF program, where I'm trying to print a protocol value read from __sk_buff context:
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, offsetof(struct __sk_buff, protocol)),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
BPF_MOV64_IMM(BPF_REG_2, 4),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
...
I create a raw socket and bind it to the lo interface, then setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF, ...). It compiles and loads with no problems, however whenever I ping 127.0.0.1 I never see traces in the trace_pipe.
So, to make sure that it BPF_FUNC_trace_printk actually can work, I changed it so that it prints a static string on the stack, and it does print on every packet hitting the loopback.
What am I doing wrong?
Read the friendly manual :)
I don't believe you are calling the bpf_trace_printk() helper correctly (BPF_FUNC_trace_prink is just an integer, by the way). Its signature, commented in the kernel UAPI header bpf.h or in the bpf-helpers man page, is as follows:
long bpf_trace_printk(const char *fmt, u32 fmt_size, ...);
This means that the first argument must be a constant, null-terminated format string, not an integer like you do.
What does clang do?
I understand you are attaching your eBPF programs to sockets and cannot compile the whole program from C. However, why not compile that specific part as a generic networking eBPF program to see what the bytecode should look like? Let's write the C code:
#include <linux/bpf.h>
static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) BPF_FUNC_trace_printk;
int printk_proto(struct __sk_buff *skb) {
char fmt[] = "%d\n";
bpf_trace_printk(fmt, sizeof(fmt), skb->protocol);
return 0;
}
Compile to an object file. For the record this would not load, unless we provide both a valid licence string (because bpf_trace_prink() needs a GPL-compatible program) and a compatible program type at load time. But it does not matter in our case, we just want to look at the generated instructions.
$ clang -O2 -g -emit-llvm -c prink_protocol.c -o - | \
llc -march=bpf -mcpu=probe -filetype=obj -o prink_protocol.o
Dump the bytecode:
$ llvm-objdump -d prink_protocol.o
prink_protocol.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <printk_proto>:
0: b4 02 00 00 25 64 0a 00 w2 = 680997
1: 63 2a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r2
2: 61 13 10 00 00 00 00 00 r3 = *(u32 *)(r1 + 16)
3: bf a1 00 00 00 00 00 00 r1 = r10
4: 07 01 00 00 fc ff ff ff r1 += -4
5: b4 02 00 00 04 00 00 00 w2 = 4
6: 85 00 00 00 06 00 00 00 call 6
7: b4 00 00 00 00 00 00 00 w0 = 0
8: 95 00 00 00 00 00 00 00 exit
We can see that on the first two instructions, the program writes the format string (in little endian) onto the stack: 680997 is 0x000a6425, \0\nd%. r2 still contains the length for the format string. The protocol value is stored in r3, the third argument for the call to bpf_trace_prink().
I need some help to understand the avcC atom structure of a particular mp4 sample I am trying to analyze.
Hex dump:
00 00 00 38 61 76 63 43 01 64 00 1F FF E1 00 1C 67 64 00 1F AC D9 80
50 05 BB 01 6A 02 02 02 80 00 00 03 00 80 00 00 1E 07 8C 18 CD 01 00
05 68 E9 7B 2C 8B FD F8 F8 00 00 00 00 13 63 6F 6C 72
This is what I understand from the above:
00 00 00 38 Size of avcC atom
61 76 63 43 avcC signature
01 configurationVersion
64 AVCProfileIndication
00 profile_compatibility
1F AVCLevelIndication
FF 111111b + lengthSizeMinusOne
E1 111b + numOfSequenceParameterSets (in this case, 1 SPS)
00 1C SPS length (in this case, 28 bytes)
67 64 00 1F AC D9 80 50 05 BB 01 6A 02 02 02 80 00 00 03 00 80 00 00 1E 07 8C 18 CD SPS data (28 bytes as per above)
01 numOfPictureParameterSets (in this case, 1 PPS)
00 05 PPS length
This is where the problem begins. Based on the PPS length given by the previous bytes, the next 5 bytes should be the PPS data: 68 E9 7B 2C 8B
However according to the avcC header, the total length of the atom is 56 bytes (0x38), which means that the following 4 bytes should be included: FD F8 F8 00
But the problem is that the PPS length is given as 5 bytes (0x05). So what exactly are these final 4 bytes?
Then follows the header of the colr atom:
00 00 00 13 size of colr atom
63 6F 6C 72 colr signature
Which I have checked and is indeed 19 bytes in length (0x13).
The problem is with the avcC atom and with that particular mp4 sample I am analyzing (I've checked other samples too and they didn't have this peculiarity).
You can find the sample here.
EDIT
mp4info tool from the bento4 suite reports the following as the avcC atom's size: 8+48
And mp4dump reports:
AVC SPS: [6764001facd9805005bb016a02020280000003008000001e078c18cd]
AVC PPS: [68e97b2c8b]
So it correctly reports the total size of the atom as 56 bytes (0x38) based on what is found in the avcC header, but the SPS/PPS data are analyzed the same way as above. I still don't understand what the final 4 bytes are or where do they belong.
I dind't get any answer but fortunately a bit more careful reading of ISO 14496-15 solved this issue:
if( profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 144 )
{
bit(6) reserved = ‘111111’b;
unsigned int(2) chroma_format;
bit(5) reserved = ‘11111’b;
unsigned int(3) bit_depth_luma_minus8;
bit(5) reserved = ‘11111’b;
unsigned int(3) bit_depth_chroma_minus8;
unsigned int(8) numOfSequenceParameterSetExt;
for (i=0; i< numOfSequenceParameterSetExt; i++) {
unsigned int(16) sequenceParameterSetExtLength;
bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit;
}
}
Apparently a sequence of 4+ bytes may exist at the end of an avcC atom depending on the profile used. In my sample above the profile is 100 (0x64), hence it meets the criteria. So the last 4 bytes are:
FD = bits 111111 are reserved, remaining 01 means chroma subsampling 4:2:0
F8 = bits 11111 are reserved, remaining 000 means luma bit depth is 8
F8 = bits 11111 are reserved, remaining 000 means chroma bit depth is 8
00 = zero SPS extensions
I'm attempting to search for an arbitrarily long byte string in WinDbg and print out the address if an integer in the vicinity meets some criteria.
Pseudo-register $t0 contains the starting address I want to search.
Here's something that, based on the Windows docs, maybe could work (though it clearly doesn't).
.foreach (place { s -[1] #$t0 L?30000 00 00 00 00 00 20 00 00 }) { .if ( (place +0x8) <= 0x1388) { .printf "0x%x\n", place } }
Search
First, the search command doesn't quite work correctly. I only want the address of the match (not the data).
s -[1] #$t0 L?30000 00 00 00 00 00 20 00 00
The docs say that the 1 flag will only return the address. When I issue that command, WinDbg replies
^ Syntax error in 's -1 #$t0 L?30000 00 00 00 00 00 20 00 00 '
If I leave out the -1, it finds two matches.
What am I doing wrong here?
Condition
I don't think the condition is behaving the way I want. I want to look at the third dword starting at place, i.e. place+8, and verify that it's smaller than 5000 (decimal). The .if inside the .foreach isn't printing a meaningful value for place (i.e. the address returned from the search). I think it's dereferencing place first and comparing the value of that integer to 5000. How do I look at the value of, say, *(int*)(place+8)?
Documentation?
The docs are not helping me very much. They only have sparse examples, none of which correspond to what I need.
Is there better documentation somewhere besides MS's Hardware Dev Center?
you can start writing JavaScript for a more legible way of scripting
old way
0:000> s -b vect l?0x1000 4d
00007ff7`8aaa0000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
00007ff7`8aaa00d4 4d 90 80 d2 df f9 82 d3-4d 90 80 d2 52 69 63 68 M.......M...Rich
00007ff7`8aaa00dc 4d 90 80 d2 52 69 63 68-4c 90 80 d2 00 00 00 00 M...RichL.......
0:000> s -[1]b vect l?0x1000 4d
0x00007ff7`8aaa0000
0x00007ff7`8aaa00d4
0x00007ff7`8aaa00dc
using javascript
function search(addr,len)
{
var index = []
var mem = host.memory.readMemoryValues(addr,len)
for(var i = 0; i < len; i++)
{
if(mem[i] == 0x4d)
{
index.push(addr+i)
}
}
return index
}
executed will return address like which you can manipulate further
0:000> dx -r1 #$scriptContents.search(0x00007ff78aaa0000,1000)
#$scriptContents.search(0x00007ff78aaa0000,1000) : 140701160046592,140701160046804,140701160046812
length : 0x3
[0x0] : 0x7ff78aaa0000
[0x1] : 0x7ff78aaa00d4
[0x2] : 0x7ff78aaa00dc
improving the script a little to find something based on first result
we will try to find the index of Rich string that follows the character 'M'
modified script
function search(addr,len)
{
var index = []
var Rich = []
var result = []
var mem = host.memory.readMemoryValues(addr,len)
for(var i = 0; i < len; i++)
{
if(mem[i] == 0x4d)
{
index.push(addr+i)
var temp = host.memory.readMemoryValues(addr+i+4,1,4)
host.diagnostics.debugLog(temp +"\t")
if(temp == 0x68636952)
{
Rich.push(addr+i)
}
}
}
result.push(index)
result.push(Rich)
return result
}
result only the third occurance of char "M" is followed by Rich string
0:000> dx -r2 #$scriptContents.search(0x00007ff78aaa0000,1000)
3 3548576223 1751345490 #$scriptContents.search(0x00007ff78aaa0000,1000) : 140701160046592,140701160046804,140701160046812,140701160046812
length : 0x2
[0x0] : 140701160046592,140701160046804,140701160046812
length : 0x3
[0x0] : 0x7ff78aaa0000
[0x1] : 0x7ff78aaa00d4
[0x2] : 0x7ff78aaa00dc
[0x1] : 140701160046812
length : 0x1
[0x0] : 0x7ff78aaa00dc
0:000> s -b vect l?0x1000 4d
00007ff7`8aaa0000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
00007ff7`8aaa00d4 4d 90 80 d2 df f9 82 d3-4d 90 80 d2 52 69 63 68 M.......M...Rich
00007ff7`8aaa00dc 4d 90 80 d2 52 69 63 68-4c 90 80 d2 00 00 00 00 M...RichL.......
load the extensension jsprovider.dll .load jsprovider
write a script say foo.js
load the script .scriptload ...\path\foo.js
execute any functions inside the js you wrote with dx #$scriptContents.myfunc(myargs)
see below using cdb just for ease of copy paste windbg works just as is
F:\>type mojo.js
function hola_mojo ()
{
host.diagnostics.debugLog("hola mojo this is javascript \n")
}
F:\>cdb -c ".load jsprovider;.scriptload .\mojo.js;dx #$scriptContents.hola_mojo();q" cdb | f:\usr\bin\grep.exe -A 6 -i reading
0:000> cdb: Reading initial command '.load jsprovider;.scriptload .\mojo.js;dx #$scriptContents.hola_mojo();q'
JavaScript script successfully loaded from 'F:\mojo.js'
hola mojo this is javascript
#$scriptContents.hola_mojo()
quit:
If I read this part of the documentation
s [-[[Flags]Type]] Range Pattern
correctly, you cannot leave out Type when specifying flags. That's because the flags are inside two square brackets. Otherwise it would have been noted as s [-[Flags][Type]] Range Pattern.
Considering this, the example works:
0:000> .dvalloc 2000
Allocated 2000 bytes starting at 00ba0000
0:000> eb 00ba0000 01 02 03 04 05 06 07 08 09
0:000> eb 00ba1000 01 02 03 04 05 06 07 08 09
0:000> s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08
0x00ba0000
0x00ba1000
Also note that you'll have a hidden bug for the use of place: it should be ${place}. By default, that will work with the address (line break for readability on SO):
0:000> .foreach (place {s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08 })
{ .if ( (${place} +0x8) < 0xba1000) { .printf "0x%x\n", ${place} } }
0xba0000
In order to read a DWord from that address, use the dwo() MASM oerator (line break for readability on SO):
0:000> .foreach (place {s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08 })
{ .if ( (dwo(${place} +0x8)) < 0xba1000)
{ .printf "0x%x = 0x%x\n", ${place}, dwo(${place}+8) } }
0xba0000 = 0x9
0xba1000 = 0x9
When using Desfire native wrapped APDUs to communicate with the card, which parts of the command and response must be used to calculate CMAC?
After successful authentication, I have the following session key:
Session Key: 7CCEBF73356F21C9191E87472F9D0EA2
Then when I send a GetKeyVersion command, card returns the following CMAC which I'm trying to verify:
<< 90 64 00 00 01 00 00
>> 00 3376289145DA8C27 9100
I have implemented CMAC algorithm according to "NIST special publication 800-38B" and made sure it is correct. But I don't know which parts of command and response APDUs must be used to calculate CMAC.
I am using TDES, so MAC is 8 bytes.
I have been looking at the exact same issue for the last few days and I think I can at least give you some pointers. Getting everything 'just so' has taken some time and the documentation from NXP (assuming you have access) is a little difficult to interpret in some cases.
So, as you probably know, you need to calculate the CMAC (and update your init vec) on transmit as well as receive. You need to save the CMAC each time you calculate it as the init vec for the next crypto operation (whether CMAC or encryption etc).
When calculating the CMAC for your example the data to feed into your CMAC algorithm is the INS byte (0x64) and the command data (0x00). Of course this will be padded etc as specified by CMAC. Note, however, that you do not calculate the CMAC across the entire APDU wrapping (i.e. 90 64 00 00 01 00 00) just the INS byte and data payload is used.
On receive you need to take the data (0x00) and the second status byte (also 0x00) and calculate the CMAC over that. It's not important in this example but order is important here. You use the response body (excluding the CMAC) then SW2.
Note that only half of the CMAC is actually sent - CMAC should yield 16 bytes and the card is sending the first 8 bytes.
There were a few other things that held me up including:
I was calculating the session key incorrectly - it is worth double checking this if things are not coming out as you'd expect
I interpreted the documentation to say that the entire APDU structure is used to calculate the CMAC (hard to read them any other way tbh)
I am still working on calculating the response from a Write Data command correctly. The command succeeds but I can't validate the CMAC. I do know that Write Data is not padded with CMAC padding but just zeros - not yet sure what else I've missed.
Finally, here is a real example from communicating with a card from my logs:
Authentication is complete (AES) and the session key is determined to be F92E48F9A6C34722A90EA29CFA0C3D12; init vec is zeros
I'm going to send the Get Key Version command (as in your example) so I calculate CMAC over 6400 and get 1200551CA7E2F49514A1324B7E3428F1 (which is now my init vec for the next calculation)
Send 90640000010000 to the card and receive 00C929939C467434A8 (status is 9100).
Calculate CMAC over 00 00 and get C929939C467434A8A29AB2C40B977B83 (and update init vec for next calculation)
The first half of our CMAC from step #4 matches the 8 byte received from the card in step #3
Sry for my English,- its terrible :) but it's not my native language. I'm Russian.
Check first MSB (7 - bit) of array[0] and then shiffting this to the left. And then XOR if MSB 7 bit was == 1;
Or save first MSB bit of array[0] and after shiffting put this bit at the end of array[15] at the end (LSB bit).
Just proof it's here:
https://www.nxp.com/docs/en/application-note/AN10922.pdf
Try this way:
Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
First u have to encrypt 16 bytes (zeros) with SesionKey;
enc_aes_128_ecb(Zeros);
And u get EncryptedData.
EncryptedData <- 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC
Check bit 7 [MSB - LSB] of EncryptedData[0] == 1? switch i to true;
bool i = false;
if (EncryptedData[0] & 0x80){
i = true;
}
Then do Shiffting of all EncryptedData to 1 bit <<.
ShiftLeft(EncryptedData,16);
And now, when i == true - XOR the last byte [15] with 0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Save it as KEY_1.
Try bit 7 [MSB - LSB] of ShiftedEncryptedData[0] == 1?
i = false;
if (ShiftedEncryptedData[0] & 0x80){
i = true;
}
Then do Shiffting of all ShiftedEncryptedData to 1 bit <<.
ShiftLeft(ShiftedEncryptedData,16);
And now, when i == true - XOR the last byte [15] with 0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
Save it as KEY_2.
Now we take our Data (6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
As Michael say's - pad command with 0x80 0x00...
XOR Data with KEY_2 - if command was padded, or KEY_1 if don't.
If we have more like 16 bytes (32 for example) u have to XOR just last 16 bytes.
Then encrypt it:
enc_aes_128_ecb(Data);
Now u have a CMAC.
CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Key_1 <- 7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Key_2 <- F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
CMAC <- CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
C/C++ function:
void ShiftLeft(byte *data, byte dataLen){
for (int n = 0; n < dataLen - 1; n++) {
data[n] = ((data[n] << 1) | ((data[n+1] >> 7)&0x01));
}
data[dataLen - 1] <<= 1;
}
Have a nice day :)
I got a small question.
Say I have the following code inside a console application :
printf("Enter name: ");
scanf("%s", &name);
I would like to exploit this vulnerability and enter the following shell code (MessageboxA):
6A 00 68 04 21 2F 01 68 0C 21 2F 01 6A 00 FF 15 B0 20 2F 01
How can I enter my shell code (Hex values) through the console ?
If I enter the input as is, it treats the numbers as chars and not as hex values.
Thanks a lot.
You could use as stdin a file with the desired content or use the echo command.
Suppose your shell code is AA BB CC DD (obviously this is not a valid shellcode):
echo -e "\xAA\xBB\xCC\xDD" | prog