是否可以編寫一個執行以下操作的C函數?C中的一種自修改程序
- 分配大量內存堆中
- 寫入機器代碼在它
- 執行這些機器指令
當然,我會在堆棧的狀態恢復到什麼它是在手動執行那些機器指令之前,但我想知道這是否可行。
是否可以編寫一個執行以下操作的C函數?C中的一種自修改程序
當然,我會在堆棧的狀態恢復到什麼它是在手動執行那些機器指令之前,但我想知道這是否可行。
肯定是可能。出於各種原因,我們花了很多努力在過去30 - 40年努力使其儘可能地困難,但這是可能的。現在在大多數系統中,有硬件和軟件機制試圖保護數據空間不被執行。
但是,基礎知識相當簡單:您構建一段代碼,然後通過編譯器手動或組合它。那麼你需要的代碼空間碎片,所以你插入的代碼到你的程序
unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... }; // Random numbers, just as an example
,因爲你想用堆你需要的malloc空間現在
void * myspace ;
if((myspace= malloc(sizeof(prgm))) != NULL) {
memcpy(myspace, pgrm, sizeof(pgrm));
} else { // allocation error
}
,你需要的是讓程序計數器指向也是您的代碼塊的數據塊。這就是你需要一點狡猾的地方。設置程序計數器沒有什麼大不了的;這只是底層機器的JUMP指令。但如何做到這一點?
最簡單的方法之一是有目的地搞亂堆棧。堆棧,再從概念上,看起來像這樣(的細節取決於雙方你的操作系統和編譯器對,以及您的硬件):
| subroutine return addr | | parameters ... | | automatic variables |
這裏的基本技巧是悄悄讓你的代碼的地址進入退貨地址;當例程返回時,它基本上跳轉到返回地址。如果你能僞裝出來,電腦將被設置爲你喜歡的地方。
所以,你需要的是一個套路,我們把它稱爲 「goThere()」
void goThere(void * addr){
int a ; // observe above; this is the first space
// on the stack following the parameters
int * pa; // so we use it's address
pa = (&a - (sizeof(int)+(2*sizeof(void*))) ; // so use the address
// but back up by the size of an int, the pointer on the
// stack, and the return address
// Now 'pa' points to the routine's return add on the stack.
*pa = addr; // sneak the address of the new code into return addr
return ; // and return, tricking it into "returning"
// to the address of your special code block
}
可以嗎?好吧,也許,這取決於硬件和操作系統。大多數現代操作系統將保護堆(通過內存映射或類似)從PC移動到它。這對於安全目的而言是非常有用的,因爲我們只需要而不是就可以讓您採取這種完全控制。
這是非常相似的this question :)
讀calling code stored in the heap from vc++。在POSIX,mprotect
似乎是適當的(看看man mprotect
):
char *mem = malloc(sizeof(code));
mprotect(mem, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC);
memcpy(mem, code, sizeof(code));
// now arrange some code to jump to mem. But read the notes here on casting
// from void* to a function pointer:
// http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html
然而,它說:
無論PROT_EXEC具有PROT_READ不同任何影響建築 - 和內核版本相關。在某些硬件體系結構上(例如i386),PROT_WRITE意味着PROT_READ。
那麼更好,首先檢查一下你的操作系統是否有效。
RE:手工恢復堆棧
如果按照你所生成機器碼裏面你的平臺/編譯器的調用約定,那麼你不應該做任何手動堆棧恢復。編譯器會爲你做的,當你做
* pfunc(參數)
應該添加任何必要的適當的前或後調用堆棧操作步驟。
但是,只要確保遵循生成的代碼中的正確約定。
也許這個問題應該作爲一個副本來關閉? – 2009-01-03 04:44:53
請參閱http://stackoverflow.com/questions/397064/calling-code-stored-in-the-heap-from-vc – Eclipse 2009-01-03 05:16:10