簡體   English   中英

Linux:執行手動加載到內存的代碼

[英]Linux: executing code that is loaded to memory manually

我正在使用Linux上的函數指針並嘗試執行此C程序:

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

int myfun() 
{
    return 42;
}

int main()
{
    char data[500];
    memcpy(data, myfun, sizeof(data));
    int (*fun_pointer)() = (void*)data;
    printf("%d\n", fun_pointer());

    return 0;
}

不幸的是,它在fun_pointer()調用上發生了段fun_pointer() 我懷疑它與一些內存標志有關,但我沒有找到有關它的信息。

你能解釋為什么這段代碼會出現錯誤嗎? 沒有看到固定的data數組大小,沒有調用函數成功就可以了。

UPD:最后我發現,內存段應標記為使用可執行的mprotect系統調用調用PROT_EXEC標志。 此外,內存段應由mmap函數返回,如POSIX規范中所述。 使用PROT_EXEC標志的mmap內存分配的代碼相同(並且有效):

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int myfun() 
{
    return 42;
}

int main()
{
    size_t size = (char*)main - (char*)myfun;
    char *data = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE,
        MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    memcpy(data, myfun, size);

    int (*fun_pointer)() = (void*)data;
    printf("%d\n", fun_pointer());

    munmap(data, size);
    return 0;
}

此示例應符合-fPIC gcc選項,以確保函數中的代碼與位置無關。

那里有幾個問題:

除了Diask的答案,你可能想要使用一些JIT編譯技術(在內存中生成可執行代碼),你應該確保包含代碼的內存區域是可執行的(參見mprotect(2)NX位 ;通常是出於安全原因,調用堆棧不可執行)。 您可以使用GNU閃電 (快速發出慢速機器代碼), asmjitlibjitLLVMGCCJIT (能夠慢慢發出快速優化的機器代碼)。 你也可以在一些臨時文件/tmp/emittedcode.c發出一些C代碼,fork一個編譯命令gcc -Wall -O -fPIC -shared /tmp/emittedcode.c -o /tmp/emittedcode.so然后dlopen(3)共享對象/tmp/emittedcode.so並使用dlsym(3)按名稱查找函數指針。

另見這個這個這個這個那個答案。 閱讀有關蹦床代碼閉包延續CPS的信息

當然,將代碼從一個區域復制到另一個區域通常不起作用(它必須是與位置無關的代碼才能使其工作,或者您需要自己的重定位機制,有點像鏈接器一樣)。

這是因為這條線錯了:

memcpy(data, myfun, sizeof(data));

您正在復制函數的代碼(已編譯)而不是函數的地址。

myfun和&myfun會有相同的地址,所以要進行memcpy操作,你必須使用一個函數指針,然后從它的地址復制。

例:

int (*p)(); 
p = myfun; 
memcpy(data, &p, sizeof(data));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM