[英]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閃電 (快速發出慢速機器代碼), asmjit , libjit , LLVM , GCCJIT (能夠慢慢發出快速優化的機器代碼)。 你也可以在一些臨時文件/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.