2009-01-03 50 views
0

是否可以編寫一個執行以下操作的C函數?C中的一種自修改程序

  1. 分配大量內存堆中
  2. 寫入機器代碼在它
  3. 執行這些機器指令

當然,我會在堆棧的狀態恢復到什麼它是在手動執行那些機器指令之前,但我想知道這是否可行。

+0

也許這個問題應該作爲一個副本來關閉? – 2009-01-03 04:44:53

+0

請參閱http://stackoverflow.com/questions/397064/calling-code-stored-in-the-heap-from-vc – Eclipse 2009-01-03 05:16:10

回答

8

肯定是可能。出於各種原因,我們花了很多努力在過去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移動到它。這對於安全目的而言是非常有用的,因爲我們只需要而不是就可以讓您採取這種完全控制。

2

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。

那麼更好,首先檢查一下你的操作系統是否有效。

0

RE:手工恢復堆棧

如果按照你所生成機器碼裏面你的平臺/編譯器的調用約定,那麼你不應該做任何手動堆棧恢復。編譯器會爲你做的,當你做

* pfunc(參數)

應該添加任何必要的適當的前或後調用堆棧操作步驟。

但是,只要確保遵循生成的代碼中的正確約定。