WARNING: This is an exploit. Do not execute this code.
//shellcode.c
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main() {
int *ret; //ret pointer for manipulating saved return.
ret = (int *)&ret + 2; //setret to point to the saved return
//value on the stack.
(*ret) = (int)shellcode; //change the saved return value to the
//address of the shellcode, so it executes.
}
can anyone give me a better explanation ?
Apparently, this code attempts to change the stack so that when the
mainfunction returns, program execution does not return regularly into the runtime library (which would normally terminate the program), but would jump instead into the code saved in theshellcodearray.1)
int *ret;defines a variable on the stack, just beneath the
mainfunction’s arguments.2)
ret = (int *)&ret + 2;lets the
retvariable point to aint *that is placed twoints abovereton the stack. Supposedly that’s where the return address is located where the program will continue whenmainreturns.2)
(*ret) = (int)shellcode;The return address is set to the address of the
shellcodearray’s contents, so thatshellcode‘s contents will be executed whenmainreturns.shellcodeseemingly contains machine instructions that possibly do a system call to launch/bin/sh. I could be wrong on this as I didn’t actually disassembleshellcode.P.S.: This code is machine- and compiler-dependent and will possibly not work on all platforms.
Reply to your second question:
retis declared as anint*, therefore assigning anint(such as(int)&ret) to it would be an error. As to why 2 is added and not any other number: apparently because this code assumes that the return address will lie at that location on the stack. Consider the following:This code assumes that the call stack grows downward when something is pushed on it (as it indeed does e.g. with Intel processors). That is the reason why a number is added and not subtracted: the return address lies at a higher memory address than automatic (local) variables (such as
ret).From what I remember from my Intel assembly days, a C function is often called like this: First, all arguments are pushed onto the stack in reverse order (right to left). Then, the function is called. The return address is thus pushed on the stack. Then, a new stack frame is set up, which includes pushing the
ebpregister onto the stack. Then, local variables are set up on the stack beneath all that has been pushed onto it up to this point.Now I assume the following stack layout for your program:
At the bottom lies
ret(which is a 32-bit integer). Above it is the savedebpregister (which is also 32 bits wide). Above that is the 32-bit return address. (Above that would bemain‘s arguments —argcandargv— but these aren’t important here.) When the function executes, the stack pointer points atret. The return address lies 64 bits “above”ret, which corresponds to the+ 2inIt is
+ 2becauseretis aint*, and anintis 32 bit, therefore adding 2 means setting it to a memory location 2 × 32 bits (=64 bits) above(int*)&ret… which would be the return address’ location, if all the assumptions in the above paragraph are correct.Excursion: Let me demonstrate in Intel assembly language how a C function might be called (if I remember correctly — I’m no guru on this topic so I might be wrong):
Inside main, the following might happen:
See also: Description of the procedure call sequence in C for another explanation of this topic.