前幾天羣裏有人給了個病毒樣本
拿來一看很奇怪,是個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的方法,很直接):
- VOID ChangeExeOEP( PVOID pBuffer)
- {
- /*將文件映射到內存,通過內存中的句柄獲取文件DOS頭*/
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
- /*通過DOS頭獲取PE頭*/
- PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (DWORD)pBuffer);
- /*通過PE頭中OptionalHeader結構中的AddressOfEntryPoint和ImageBase相加獲取程序入口點*/
- DWORD dwOEP = pNtHeader->OptionalHeader.AddressOfEntryPoint + pNtHeader->OptionalHeader.ImageBase;
- /*初始化跳轉數組,其中0x52和0x45分別爲push edx指令和inc ebp,作用前面解釋過。
- 0x68爲push指令,後面四個0x00爲預留的原始入口點地址,0xC3爲retn指令*/
- BYTE JmpArray[] = {0x4D,0x5A,0x52,0x45,0x68,0x00,0x00,0x00,0x00,0xC3};
- /*數組的第五位起填入之前預留的原始入口點地址*/
- *(DWORD*)(JmpArray + 5) = dwOEP;
- memcpy( pBuffer,JmpArray,10 );
- /*將AddressOfEntryPoint元素置零*/
- pNtHeader->OptionalHeader.AddressOfEntryPoint = 0;
- }
用該方法修改notepad.exe,以下是修改前和修改後的notepad.exe的對比。僅此兩處修改,其餘均未變:
修改之後notepad照常運行,而入口點卻是0了:
就先寫到這了,權當學習記錄了。