1. 程式人生 > >入口點為0的程式

入口點為0的程式

前幾天群裡有人給了個病毒樣本

拿來一看很奇怪,是個exe檔案,但入口點顯示卻是0

用OD載入後會提示出錯:

之後問了一下同事,大概瞭解了一下原理:

Windows系統載入PE檔案後,會通過PE檔案的特定結構讀取各種資訊。

而該PE檔案的各種資訊都是完整的,可以正常被讀取。

      相關的PE結構撿主要的在這裡大概說一下:

      檔案開始是一個IMAGE_DOS_HEADER的結構,以資料0x4D5A(ASCII字元為"MZ")開頭的e_magic元素。從該結構體開頭開始計算偏移量0x3c處為一個DWORD值——被稱為e_lfanew,該值為IMAGE_NT_HEADER結構體(即俗稱的PE頭)的偏移量。PE頭分三部分:第一部分是一個名為Signature的欄位,固定為0x4550(ASCII字元為"PE");第二部分從PE頭開始偏移0x4,名為FileHeader的結構;第三部分從PE頭開始偏移0x18,名為OptionalHeader。這個OptionalHeader中的AddressOfEntryPoint與ImageBase兩元素之和即是PE檔案被載入到記憶體中執行時,正式開始執行功能的程式碼起始點。

該檔案的AddressOfEntryPoint為0,而ImageBase為400000.

那麼嘗試在OD下用Ctrl+G查詢該地址:

 

可見固定的e_magic元素被識別為了彙編指令dec ebp和pop edx

而後面的8個位元組則是被人為修改的。

push edx是為了恢復前面"字元Z"被誤認為是指令所造成的出棧操作

而後jmp到自己的修改過的一段程式碼處:

在執行到這個retn指令後,棧中出現的才是原本的程式入口點:

執行retn之後,程式會自動執行到這個入口點(本身還有一層UPX加殼):

總結一下:

其實就是將程式原本的入口點(AddressOfEntryPoint)修改為0,這樣在載入PE檔案的時候就會從檔案的DOS頭開始作為二進位制指令開始執行。只要固定的e_magic不動就不會影響整體的PE結構識別,這樣修改之後的幾個位元組,來實現向程式原本入口點的直接或間接跳轉。

用以下程式碼可以輕鬆實現任意一個PE檔案的修改(跳轉方法沒有用jmp,比較麻煩。採用了push+retn的方法,很直接):

  1. VOID ChangeExeOEP( PVOID pBuffer)  
  2. {  
  3.     /*將檔案對映到記憶體,通過記憶體中的控制代碼獲取檔案DOS頭*/
  4.     PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;  
  5.     /*通過DOS頭獲取PE頭*/
  6.     PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (DWORD
    )pBuffer);  
  7.     /*通過PE頭中OptionalHeader結構中的AddressOfEntryPoint和ImageBase相加獲取程式入口點*/
  8.     DWORD dwOEP = pNtHeader->OptionalHeader.AddressOfEntryPoint + pNtHeader->OptionalHeader.ImageBase;  
  9.     /*初始化跳轉陣列,其中0x52和0x45分別為push edx指令和inc ebp,作用前面解釋過。 
  10.     0x68為push指令,後面四個0x00為預留的原始入口點地址,0xC3為retn指令*/
  11.     BYTE JmpArray[] = {0x4D,0x5A,0x52,0x45,0x68,0x00,0x00,0x00,0x00,0xC3};  
  12.     /*陣列的第五位起填入之前預留的原始入口點地址*/
  13.     *(DWORD*)(JmpArray + 5) = dwOEP;  
  14.     memcpy( pBuffer,JmpArray,10 );  
  15.     /*將AddressOfEntryPoint元素置零*/
  16.     pNtHeader->OptionalHeader.AddressOfEntryPoint = 0;  
  17. }  

用該方法修改notepad.exe,以下是修改前和修改後的notepad.exe的對比。僅此兩處修改,其餘均未變:

修改之後notepad照常執行,而入口點卻是0了:

就先寫到這了,權當學習記錄了。