I was reading a basic article on writing a shellcode (execve using stack method) here: http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html
In step 6: It pushes a null character because the string "/bin/sh" is null terminated. After that, it pushes the string "/bin/sh" in reverse order
Why is the string pushed to the stack in reverse order and why is the null character that terminates the string pushed "before" pushing the string onto the stack?
The string which should be put onto the stack is following:
//bin/sh + '\0'(null terminator) + alignment(3 additional null characters) - gives 3 DWORDs (12 bytes)
To do that, we have to execute a set of instructions in a proper order:
xor eax, eax ;zero out full 32 bits of eax register
push eax ;0x00000000
push 0x68732f6e ;hs/n
push 0x69622f2f ;ib//
Why in such an order ?
Because of the stack nature. Elements should be put into it in reverse order, to be properly read out of it in the future. Stack is a data structure which has first-in, last-out (FILO) ordering (opposite to heap). It means the first item that is put into a stack is the last item to come out of it. As the stack changes in size, it grows upward toward lower memory addresses:
memory
.---------------.-- 00 <-- top / low addresses
| . | x+1
| /|\ | x+2
| | | .
| stack | .
|grows up toward| .
|lower addresses| x+n
'---------------'-- FF <-- bottom / high addresses
Now, what about these 2 double words: 0x68732f6e (hs/n)
and 0x69622f2f (ib//)
? How are they related to //bin/sh
?
Looking at 0x68732f6e
we can see byte-reversal effect of shown byte by byte, actually stored in memory, 4 bytes: 0x6e 0x2f 0x73 0x68 (n/sh)
. It is connected with IA-32 architecture specific endianess, which has to be taken under consideration while manually putting bytes into the stack. On the x86 processor values are stored in little-endian (opposite to big endian on SPARC processors) byte order, which means the least significant byte is stored first (the little end comes first):
byte3 byte2 byte1 byte0
will be arranged in memory as follows:
base address+0 byte0
base address+1 byte1
base address+2 byte2
base address+3 byte3
So finally, to visualize how the memory space is filled, take a look below:
.--------- push eax ;0x00000000
| .----- push 0x68732f6e ;hs/n bytes reversed
| | .- push 0x69622f2f ;ib// bytes reversed
| | |
| | | register
| | '> |69|62|2f|2f| (ib//) memory
| | | | | | ..
| | | | | '-------> x: 2f '/'
| | | | '----------> x+1: 2f '/'
| | | '-------------> x+2: 62 'b'
| | '----------------> x+3: 69 'i'
| | little endian
| |
| '----> |68|73|2f|6e| (hs/n)
| | | | |
| | | | '-------> x+4: 6e 'n'
| | | '----------> x+5: 2f '/'
| | '-------------> x+6: 73 's'
| '----------------> x+7: 68 'h'
|
'--------> |00|00|00|00| (\0\0\0\0)
| | | |
| | | '-------> x+8: 00 '\0'
| | '----------> x+9: 00 '\0'
| '-------------> x+10: 00 '\0'
'----------------> x+11: 00 '\0'
..
You can examin it by using gdb:
(gdb) x/12b $sp
0xbfb530b0: 0x2f 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68
0xbfb530b8: 0x00 0x00 0x00 0x00
(gdb) x/12c $sp
0xbfb530b0: 47 '/' 47 '/' 98 'b' 105 'i' 110 'n' 47 '/' 115 's' 104 'h'
0xbfb530b8: 0 '\0' 0 '\0' 0 '\0' 0 '\0'
(gdb) x/3w $sp
0xbfb530b0: 0x69622f2f 0x68732f6e 0x00000000