是否有可能通過動態分配內存,寫一些彙編操作碼給它創造一個動態功能(如的0x90爲0xC2爲NOP RET),創建一個函數指針,它指向的是動態內存並執行它像一個C程序中的常規函數?C函數指針:我可以跳轉到堆內存彙編代碼嗎?
目標應該是一個普通的x86 Linux系統。
是否有可能通過動態分配內存,寫一些彙編操作碼給它創造一個動態功能(如的0x90爲0xC2爲NOP RET),創建一個函數指針,它指向的是動態內存並執行它像一個C程序中的常規函數?C函數指針:我可以跳轉到堆內存彙編代碼嗎?
目標應該是一個普通的x86 Linux系統。
該內存沒有堆內存(請參閱下面的註釋)。此外,我認爲你不能改變堆內存的執行權限。
在Linux上,您可以使用以下方法:
#include <sys/mman.h>
size_t size = 0x1000; // 1 page
// this will be mapped somewhere between /lib/x86_64-linux-gnu/ld-2.15.so
// and stack (see note and memory map below)
void *code = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0);
// use `code` to write your instructions
// then store location at which you want to jump to in `fp`
void *fp = ...;
mprotect(code, size, PROT_READ | PROT_EXEC);
// use some inline assembly to jump to fp
注意:在Linux上,用戶映射內存位於不同的區域(像從400000000000
和最多疊加在x86 Linux和可能是在x64上的7f0000000000
)。堆位於程序的ELF段之後,位於區域之前,可用於mmap
。堆本身可以直接使用brk
系統調用進行分配(現在替代爲malloc
)。看到這個例子(得到了我的Ubuntu 12.10 64位):
➜ ~ ps
PID TTY TIME CMD
9429 pts/3 00:00:07 zsh
20069 pts/3 00:00:00 git-credential-
22626 pts/3 00:00:00 ps
➜ ~ cat /proc/9429/maps
00400000-004a2000 r-xp 00000000 08:01 6291468 /bin/zsh5
006a1000-006a2000 r--p 000a1000 08:01 6291468 /bin/zsh5
006a2000-006a8000 rw-p 000a2000 08:01 6291468 /bin/zsh5
006a8000-006bc000 rw-p 00000000 00:00 0
01a51000-01fd8000 rw-p 00000000 00:00 0 [heap]
...
7f6529d61000-7f6529d91000 rw-p 00000000 00:00 0
...
7f652d0d3000-7f652d0d5000 rw-p 00023000 08:01 44833271 /lib/x86_64-linux-gnu/ld-2.15.so
7fffd7c7f000-7fffd7cae000 rw-p 00000000 00:00 0 [stack]
7fffd7dff000-7fffd7e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
正如你所看到的,heap
不是可執行文件(並且是理所當然的),所以你不能用malloc
獲得可執行內存。
許多系統很長一段時間都在虛擬內存頁面上有標誌,告訴它們是否可以包含可執行代碼。內存分配堆最有可能不會有這個「可執行」標誌集。所以不,你不能直接做到這一點。
如果您想這樣做,您必須使用操作系統特定的功能,並且可能必須以「管理員」或「根」身份運行該程序才能夠執行此操作,儘管似乎並不必要。
我不認爲你需要成爲根,分配可執行內存不是這樣一個系統危害的事情,並有其用途(幾乎所有當前的瀏覽器在JavaScript引擎中)。 –
更不用說越來越多的無處不在的.Net框架。 – Blindy
一般來說是的,但你需要進入系統特定的東西來這樣做。我猜想這並不令人感到意外,因爲您將使用二進制彙編指令的事實使其非常清晰。
您需要注意的是,您不能假設現代操作系統上的堆內存是可執行的,因此您可能需要跳過一些環節才能實現。您不能只調用malloc()
並假定返回的指針指向可以執行代碼的內存。
在Linux中,您可以使用mmap()
來要求內核爲您映射一些內存,並且通過在調用中指定PROT_EXEC
標誌,您可以要求它使內存可執行。
在Windows下,您可以使用[VirtualProtect](http://msdn.microsoft.com/zh-cn/library/aa366898%28v=vs.85%29。aspx) – Asaf
我會說你甚至不能假定堆內存是可執行的。但是你並不需要這樣的記憶。看到我的答案。 –
類似的,但不是相同的東西 - 'int main = 0xc290;'編譯併成功運行。 – ugoren
@ugoren僅在某些情況下。 – glglgl
@glglgl,在問題描述的情況下。 – ugoren