views:

423

answers:

4

Is it possible to write a C function that does the following?

  1. Allocate a bunch of memory in the heap
  2. Writes machine code in it
  3. Executes those machines instructions

Of course, I would have to restore the state of the stack to what it was prior to the execution of those machine instructions manually, but I want to know if this is feasible in first place.

+3  A: 

This is very similar to this question :)

Foredecker
Sorry, I didn't know.
Eduardo León
+2  A: 

Read calling code stored in the heap from vc++. On posix, mprotect seems to be appropriate (look into man mprotect):

char *mem = malloc(sizeof(code));
mprotect(mem, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC);
memcpy(mem, code, sizeof(code));
// now arrange some code to jump to mem. But read the notes here on casting 
// from void* to a function pointer:
// http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html

However, it says:

Whether PROT_EXEC has any effect different from PROT_READ is architecture- and kernel version-dependent. On some hardware architectures (e.g., i386), PROT_WRITE implies PROT_READ.

So better, first check whether on your operation system, that works.

Johannes Schaub - litb
A: 

RE: manually restoring the stack

If you follow the calling conventions used by your platform / compiler inside the machine code you generate, then you shouldn't have to do any manual stack restoring. The compiler will do that for you, when you do

*pfunc(args)

it should add any appropriate pre or post call stack manipulation steps that are necessary.

Just make sure that you follow the right conventions inside the generated code, however.

Scott Wisniewski
+6  A: 

It's certainly possible. For various reasons, we've spent a lot of effort of the last 30-40 years trying to make it as difficult as possible, but it is possible. In most systems now, there are hardware and software mechanisms that attempt to protect data space from being executed.

The basics, though, are fairly straightforward: you construct a piece of code, and assemble it, either by hand or4 via a compiler. You then need a fragment of code space, so you insert the code into your program

unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... };  // Random numbers, just as an example

since you wanted to use the heap you need to malloc the space

void * myspace ;
if((myspace= malloc(sizeof(prgm))) != NULL) {
     memcpy(myspace, pgrm, sizeof(pgrm));
} else { // allocation error
}

Now, what you need is a way to get the program counter to point to that chunk of data that is also your chunk of code. Here's where you need a little craftiness. Setting the program counter is no big deal; that's just a JUMP instruction for your underlying machine. But how to do that?

One of the easiest ways is by purposefully messing with the stack. The stack, again conceptually, looks something like this (the details depend on both your OS and compiler pairs, and on your hardware):

    | subroutine return addr |
    | parameters ...         |
    | automatic variables    |

The basic trick here is to sneakily get the address of your code into the return address; when a routine returns, it basically jumps to that return addrfess. If you can fake it out, the PC will be set to where you like.

So, what you need is a routine, let's call it "goThere()"

void goThere(void * addr){
    int a ;     // observe above; this is the first space 
                // on the stack following the parameters
    int * pa;   // so we use it's address

    pa = (&a - (sizeof(int)+(2*sizeof(void*))) ;  // so use the address
                // but back up by the size of an int, the pointer on the
                // stack, and the return address
    // Now 'pa' points to the routine's return add on the stack.
    *pa = addr; // sneak the address of the new code into return addr
    return ;    // and return, tricking it into "returning"
                // to the address of your special code block
}

Will it work? Well, maybe, depending on the hardware and OS. Most modern OS's will protect the heap (via memory mapping or similar) from the PC moving into it. This is a useful thing for security purposes, because we'd just as well not let you take that kind of complete control.

Charlie Martin