學習“免殺技術”前應該掌握的“基礎知識”
一、 什么是PE文件?
在Windows下所謂PE文件即Portable Executable,意為可移植的可執(zhí)行的文件。常見的.EXE、.DLL、.OCX、.SYS、.COM都是PE文件。PE文件有一個共同特點:前兩個字節(jié)為4D 5A(MZ)。如果一個文件前兩個字節(jié)不是4D 5A則其肯定不是可執(zhí)行文件。比如用16進制文本編輯器打開一個".xls"文件其前兩個字節(jié)為:0XD0 0XCF;打開一個".pdf"其前兩個字節(jié)為:0X25 0X50。
PE文件結構:DOS頭+PE頭+節(jié)表+.data/.rdata/.text。而今天我們就來具體了解一下PE文件的DOS頭和PE頭的結構成員與部分成員的作用。注意:一個exe文件本身是一個PE文件,但是由于包含dll庫,所以一個exe文件也是許多PE文件組成的(包含多個dll)一個PE文件
1. DOS頭:共40H(64字節(jié))
DOS頭中聲明用的寄存器(我們可以看到e_ss、e_sp、e_ip、e_cs還是16位的寄存器),所以在32位/64為系統(tǒng)中用到的只有兩個成員了(第一個和最后一個):
- e_magic:判斷一個文件是不是PE文件;
- e_lfanew:相對于文件首的偏移量,用于找到PE頭;
2. PE頭
PE頭分為標準PE頭和可選PE頭,其同為NT結構的成員:
- //NT頭
- //pNTHeader = dosHeader + dosHeader->e_lfanew;
- struct _IMAGE_NT_HEADERS{
- 0x00 DWORD Signature; //PE文件標識:ASCII的"PE"
- 0x04 _IMAGE_FILE_HEADER FileHeader;
- 0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;
- };
根據DOS頭的e_lfanew成員我們就可以找到NT頭,NT頭的第一個成員是"PE"(0X50 0X45 0X00 0X00四字節(jié)的簽名,可以在上圖00000100H地址處觀察),后兩個成員則分別是標準PE頭(_IMAGE_FILE_HEADER)和可選PE頭(_IMAGE_OPTIONAL_HEADER)。
3. 幾個重點的數據成員
(1) 文件對齊(FileAlignment)和內存對齊(SectionAlignment):
一個PE文件加載進內存中可能大于在硬盤上的大小,并且無論是在內存中還是硬盤上,都是是分塊管理(分節(jié)),一塊和一塊存儲空間之間是空隙。在硬盤上空隙有可能小于內存中空隙;在內存中空隙較大(相較于硬盤)。而存在間隙的原因則是分塊管理。
分塊的一個原因是節(jié)省硬盤:比如notepad.exe,由于是早期的程序,當時硬盤容量比較小,編譯器在生成可執(zhí)行文件時,不僅要考慮效率問題使得內存對齊/文件對齊,還需要設計成節(jié)省硬盤空間的結構。所以這種結構遵循的對齊原則:內存對齊(1000H)和硬盤對齊(200H),對齊的補充數據(0X0000)便是間隙。硬盤的對齊值較小,補充間隙自然小,因此同一個可執(zhí)行程序在內存中可能比在硬盤上大。但是現如今的硬盤空間更大,所以編譯器生成的可執(zhí)行程序在硬盤上與內存中對齊方式都是1000H。統(tǒng)一對齊為1000H的目的依舊是提高效率。
而分塊的另一個目的是節(jié)省內存空間,比如同時在電腦上運行登錄多個QQ賬號,就需要運行多次QQ可執(zhí)行程序。而代碼段為只讀數據需要一份即可,數據段則需要為每個賬號均開辟一份,,多個QQ程序共享代碼塊,單獨使用數據塊,這樣就節(jié)省了多份代碼塊的內存。(這些塊是使用結構體來維護的,分塊即創(chuàng)建結構體)。
(2) 鏡像地址/基址ImageBase的作用:
FileBuffer是磁盤上.exe文件在內存中的一份拷貝,但是FileBuffer無法直接在內存中運行,必須經過PE loader(裝載器)裝載以后成為ImageBuffer。ImageBuffer是FileBuffer的"拉伸"。即".exe–>FileBuffer–>ImageBuffer"
- .exe首地址(基址)為0
- FileBuffer首地址也為0
- ImageBuffer首地址為ImageBase
- 而真正的程序入口地址是:ImageBase + AddressOfEntryPoint(OEP)
一個exe文件默認鏡像地址為400000H(有可能不是,總之有一個默認值),如果一個exe文件中用到了多個dll,而dll文件作為一個PE文件,其默認鏡像地址也均是400000H,操作系統(tǒng)不會修改exe的鏡像基址。因為.exe先被加載,在.exe中才加載的dll庫,由于400000已經被.exe占用,所以裝載器會修改dll的鏡像基址。而采用ImageBase + OEP的目的也就是:采用偏移地址的方式可以更方便地修改基址,使得任何一個dll文件基址修改后程序依舊不會出錯。比如:dll和exe基址有沖突,本只需要將沖突的.dll的文件基址修改為600000H(假設是編譯器為其分配的是600000H);如果不采用"基址+偏移地址"的方式,而采用絕對地址,那么要修改的就不是一個基址為600000H了,而是dll中所有的地址統(tǒng)一加上200000H(因為原來默認為400000H)。
二、 匯編基礎知識
1. 寄存器
顧名思義,寄存器就是暫時存儲數據的地方,寄存器被設計在CPU內部,對于一個匯編程序員來說,CPU中最主要的部分就是寄存器了。寄存器是程序員能通過指令讀寫的部件,程序員通過改變寄存器的值間接的控制CPU
- eax:拓展累加寄存器;
- ecx:循環(huán)計數器;
- edx:數據寄存器;
- ebx:基址寄存器;
2. 堆棧
堆棧是連續(xù)的內存單元,存取方式遵循"先進后出"原則,棧是一種特殊的存儲方式,特殊在最先進入這個空間的數據卻是最后出去的。但是堆和棧不是同一個概念,棧一般由編譯器自動分配釋放,存儲函數的參數值、局部變量值等;而堆,一般由程序員分配釋放,程序結束時可能由OS(系統(tǒng))回收。
- esp:棧頂;
- ebp:棧底;
- esi:拓展目地指針;
- edi:拓展目地指針;
- eip:指令指針。
3. 匯編指令
匯編指令有5類:
- 數據傳輸指令:mov
- 邏輯計算指令:add
- 串操作指令:movs
- 控制轉移指令:jmp
- 處理器控制指令:nop
其中1,2,4類指令對免殺有用。
4. 常用免殺匯編指令
- mov ebp,9:傳送指令
- push ebp :進棧指令
- pop ebp :出棧指令
- add esp,8 :加法指令
- sub esp,8 :減法指令
- inc ecx :增量指令
- dec ecx :減量指令
- jmp 00000001 :無條件跳轉指令
- call 00000001 :調用指令
- xchg:交換指令
- pushad:壓棧8個寄存器
- popad:彈出8個寄存器(先進后出)
三、免殺常用等價替換匯編指令修改方法
A開頭:
- add 改adc
- ADD 改ADC
- ADD 1 改 sub -1
- add dword ptr ss:[ebp-130],edx ---------adc dword ptr ss:[ebp-130],edx
- ADD [EAX],CH----------------------------ADD [EAX],DH
- ADD [EAX],BH 0038 ----------------------ADD [EAX+40],AL 0040 40
- ADD [EAX+EAX*2+46],AL ------------------ADD [EAX+EAX*2+46],CL
- ADD [EAX+40],DL 0050 40 ----------------0058 40 ADD [EAX+40],DL
- ADD AH,CH 00EC -------------------------00F4 ADD AH,DH
- add dword ptr ss:[ebp-130],edx -------- adc dword ptr ss:[ebp-130],edx
C開頭:
- CMP 改SUB
- call 復件_(4).004CF607 ----------------- push 復件_(4).004CF607
- CMP DWORD PTR DS:[100170A4],0 -------------sub DWORD PTR DS:[100170A4],0
- CALL ---------看到了CALL跟隨進去看NOP就可以把CALL的地址該成NOP
- 方法2--看下附近有沒有MOV修該成NOP看下可以免殺不??梢缘脑捲揦OR
- 方法3--看附近jnz跳轉該下跳轉的地址/可免殺不/
- CALL EAX |CALL EBX
- 比效指令 CMP:看下是個比效指令 在看下JNZ條件轉移指令
- 就是說CMP比效正確就跳那我們可以把CMP用NOP掉在把JNZ該成JMP
- 不進行CMP比效
- CMP ESI,1
- call 改 jmp
D開頭:
- DAA 組合的十進制加法調整指令 --------DAS 減法的十進制調整.
J開頭:
- JE 改 JNB
- JNZ 改 JNL
- jnz 改 JB
- JE 改 JNA
- je 改 jb
- jnz 改 jg
- js 改 jp
- je 改 jle
- jnz 改 jle
- je 改 jge
- JE 改 jnz
- JE 改 JB
- JNS 改 POP ECX
- JNS 改 jnc-jnb
- JNB 改 JGE
- jnb short fsg2_0.0040015D----------------ja short fsg2_0.0040015D
- JMP NEAR [1071c]---------------------JMP NEAR [1071B]
- jnz--je-jmp修改中要看下跳的地址是不是很重要說明[1]
- JNZ 00874E85--MOV EAX,88B6D0 可以是該成JE 00874E85--MOV EAX,88B6D0
L開頭:
- LEA EBP,[ESP+10] 改 LEA EBP,[ESP+10]
M開頭:
- MOVSX 改 MOVZX
- MOV EBP,ESP 改 AND AH,CH
- MOV [EBP-18],ESP 改 MOV [EBP-18],AH
- MOV EAX,[ESP+10] 改 MOV EAX,[ESP+10]
- MOV [ESP+10],EBP 改 MOV [ESP+10],EBP
- mov [ebp-256], eax 改 adc [ebp-226], eax
- MOV EDI,[EBP+10] 改 MOV EDI,[EBP+11]
- MOV EBX,DWORD PTR DS:[ESI] 改 XOR EBX,DWORD PTR DS:[ESI]
- MOV EBP,ESP--------AND AH,CH
- MOV EBX,DWORD PTR DS:[ESI]---------XOR EBX,DWORD PTR DS:[ESI]
P開頭:
- push 改call
- PUSH EBX PUSH EDI
- PUSH ESI PUSH EAX
- PUSH EDI PUSH ESI
- PUSH EAX PUSH EBX
- pop 改 nop
S開頭:
- sbb 改adc
- sub 改mov
- SHL 改 SAL
- SAR 改 SHR
- sub ebp,7---------- add ebp,-7
- sub ebx,eax----------sbb esi,ecx
- SBB ECX,DWORD PTR DS:[ESI+2]----------ADC ECX,DWORD PTR DS:[ESI+2]
- sub ebx,eax----------sbb esi,ecx
X開頭:
- xor 改sub
- XOR [EAX],AL-------改--------MOV [EAX],AL
- XOR EAX,EAX-----改-------OR EAX,EAX