2016-02-26 22 views
-1

我試圖複製一個功能,我必須一個可執行頁面,並從那裏運行它,但我似乎有一些問題。 這裏是我的代碼:複製功能到可執行頁面和調用

#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; 
} 

我知道一些代碼在技術上是UB((INT)& goo-(INT)&富),但在這種情況下表現良好。

我的問題是爲什麼這不按預期工作? 在我看來,我把一個頁面映射爲可執行文件,並複製了一個現有的函數,我只是在調用它。

我在想什麼?

這對Linux的mmap會有不同的表現嗎? 在此先感謝

+2

您已經給出了答案:因爲它是_undefined behaviour_。製作可執行代碼不僅僅是將二進制模式從A複製到B,搬遷。 – Olaf

+3

我並不認爲'foo'和'goo'實際上可以保證連續放置在內存中。此外,這些函數可能包含與位置有關的代碼,這些代碼在將它們重新定位到另一個地址時不起作用。 –

+2

另請參閱相關(可能重複)的問題[1](http://stackoverflow.com/questions/3717499/copy-and-call-function)和[2](http://stackoverflow.com/questions/4546071/copy-a-function-in-memory-and-execute-it) – Jester

回答

2

正如每個人都已經在評論中指出,這是完全未定義的行爲,永遠不應該期望工作。然而,我用你的代碼和調試器一起玩過,並意識到它不工作的原因(至少在Cygwin gcc編譯器中)是不正確地創建f2以指向存儲分配內存的指針地址,即buf。你想指向buf指向的內存。因此,你的任務應該是

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

隨着這種變化,你的代碼爲我執行。但即使它起作用,只要您對程序進行任何其他更改,它也可能會再次中斷。

1

那麼我在調試模式下試用了MVSC 2008的代碼。編譯器恰好創建了帶有相對偏移量的jmp表,並且&foo&goo只是該表中的條目。

所以,即使您已經成功創建了一個可執行緩衝區並複製了代碼(遠遠超過了有用的...),現在相對跳轉指向了不同的位置,並且(在我的示例中)很快陷入了int 3陷阱!

TL/DR:由於編譯器可以隨意安排其代碼,並且儘可能多的使用相對偏移量,所以不能依賴於複製可執行代碼。這實在是未定義行爲:

  • 如果編譯器已經足夠聰明,只是產生類似:

    mov AX, 4 
    ret 
    

    它可以工作

  • 如果編譯器具有生成更復雜的代碼相對跳躍它只是打破

結論:你c如果你有,例如,如果你用匯編代碼的二進制機器代碼的完全控制,並知道你不會有任何搬遷問題

1

需要聲明foogoostatic或將要關閉Incremental Linking一個只複製可執行代碼。

增量鏈接用於縮短構建應用程序時的鏈接時間,正常和遞增鏈接的可執行文件之間的區別在於增量鏈接的可執行文件之間的區別在於每個函數調用通過鏈接器發出的額外的JMP指令。

這些JMP允許鏈接器在內存中移動這些函數,而不更新引用該函數的所有CALL指令。但正是這個JMP在你的案例中引發了問題。聲明函數爲static可防止鏈接器創建此額外的JMP指令。

相關問題