-1

Im trying to copy a function i have to an executable page and run it from there, but i seem to be having some problems. Here is my code:

#include <stdio.h>
#include <string.h>
#include <windows.h>

int foo()
{
    return 4;
}
int goo()
{
    return 5;
}
int main()
{
    int foosize = (int)&goo-(int)&foo;
    char* buf = VirtualAlloc(NULL, foosize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (buf == NULL)
    {
        printf("Failed\n");
        return 1;
    }

    printf("foo %x goo %x size foo %d\n", &foo, &goo, foosize);

    memcpy (buf, (void*)&foo, foosize);

    int(*f)() = &foo;
    int ret1 = f();
    printf("ret 1 %d\n", ret1);

    int(*f2)() = (int(*)())&buf;
    int ret2 = f2 (); // <-- crashes here
    printf("ret2 %d\n", ret2);

    return 0;
}

I know some of the code is technically UB ((int)&goo-(int)&foo), but it behaves fine in this case.

My question is why is this not working as expected? It seems to me i mapped a page as executable and copied an existing function there and im just calling it.

What am i missing?

Would this behave differently on linux with mmap? Thanks in advance

deller
  • 490
  • 3
  • 12
  • 2
    You already gave the answer: because it is _undefined behaviour_. There is more to making executable code than just copying a binary pattern from A to B, e.g. relocation. – too honest for this site Feb 26 '16 at 15:16
  • 3
    I don't think there is any guarantee that `foo` and `goo` will actually be in order let alone consecutively placed in memory. Also the functions may contain position-dependent code that does not work when you relocate them to another address. – Kurt Stutsman Feb 26 '16 at 15:16
  • 2
    See also the related (possibly duplicate) questions [1](http://stackoverflow.com/questions/3717499/copy-and-call-function) and [2](http://stackoverflow.com/questions/4546071/copy-a-function-in-memory-and-execute-it) – Jester Feb 26 '16 at 15:22
  • 1
    Step through the assembly code with your favorite debugger. – Jabberwocky Feb 26 '16 at 15:33

3 Answers3

2

As everyone has already stated in comments, this is totally undefined behavior and should never really expect to work. However, I played with your code some with the debugger and realized the reason it's not working (at least in Cygwin gcc compiler) is you're creating f2 incorrectly to point to the the address of the pointer storing the allocated memory, namely buf. You want to point to the memory that buf points to. Therefore, your assignment should be

int(*f2)() = (int(*)())buf;

With that change, your code executes for me. But even if it works, it might break again as soon as you make any additional changes to the program.

Kurt Stutsman
  • 3,994
  • 17
  • 23
1

Well I made a try of your code with MVSC 2008 in debug mode. Compiler happens to create a jmp table with relative offsets, and &foo and &goo are just entries in that table.

So even if you have successfully created an executable buffer and copied the code (much more than was useful...) the relative jump now points to a different location and (in my example) soon fell in a int 3 trap!

TL/DR: as compiler can arrange its code at will, and as many jump use relative offsets, you cannot rely on copying executable code. It is really Undefined Behaviour:

  • if compiler had been smart enough to just generate something like :

    mov AX, 4
    ret
    

    it could have worked

  • if compiler has generated more complicated code with a relative jump it just breaks

Conclusion: you can only copy executable code if you have full control on the binary machine code for example if you used assembly code and know you will have no relocation problem

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

You need to declare foo and goo as static or will have to disable Incremental Linking.

Incremental linking is used to shorten the linking time when building your applications, the difference between normally and incrementally linked executables is that in incrementally linked ones each function call goes through an extra JMP instruction emitted by the linker.

These JMPs allow the linker to move the functions around in memory without updating all the CALL instructions that reference the function. But it's exactly this JMP that causes problems in your case. Declaring a function as static prevents the linker from creating this extra JMP instruction.

Community
  • 1
  • 1
nom
  • 256
  • 3
  • 16