PE文件解析
當你好不容易弄出來一個木馬(具體可參考這里)的時候,卻被殺毒軟件輕易的就檢測出來了,那一切豈不是白費了。Win10中的windows defender基于流量檢測很容易把常見的木馬程序檢測出來,那怎么繞過這些檢測?
對于此問題,打算開一個專題進行木馬免殺的分析,先從最最重要的PE文件說起,要攻擊Windows系統(tǒng),如果不懂PE文件,面對殺毒軟件,則只能束手就擒了。
PE文件
PE(Portable Execute)文件是Windows下可執(zhí)行文件的總稱,常見的有DLL,EXE,OCX,SYS等,事實上,一個文件是否是PE文件與其擴展名無關,PE文件可以是任何擴展名
PE全稱是Portable Execute,實際上,這種文件格式?jīng)]有做到Portable Execute,只能用在Windwos。
基本結構
32位PE的基本結構如下,64位的大同小異。

如下是qq的16進制打開后解析出相應的字段,和上圖是匹配的。

Dos文件頭和Dos塊
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
? ?WORD ? e_magic; // Magic number
? ?WORD ? e_cblp; // Bytes on last page of file
? ?WORD ? e_cp; // Pages in file
? ?WORD ? e_crlc; // Relocations
? ?WORD ? e_cparhdr; // Size of header in paragraphs
? ?WORD ? e_minalloc; // Minimum extra paragraphs needed
? ?WORD ? e_maxalloc; // Maximum extra paragraphs needed
? ?WORD ? e_ss; // Initial (relative) SS value
? ?WORD ? e_sp; // Initial SP value
? ?WORD ? e_csum; // Checksum
? ?WORD ? e_ip; // Initial IP value
? ?WORD ? e_cs; // Initial (relative) CS value
? ?WORD ? e_lfarlc; // File address of relocation table
? ?WORD ? e_ovno; // Overlay number
? ?WORD ? e_res[4]; // Reserved words
? ?WORD ? e_oemid; // OEM identifier (for e_oeminfo)
? ?WORD ? e_oeminfo; // OEM information; e_oemid specific
? ?WORD ? e_res2[10]; // Reserved words
? ?LONG ? e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
對于PE文件來說,有用的是最后一個字段,這個字段指出了真正的PE頭在文件中的位置。

PE文件頭
typedef struct _IMAGE_NT_HEADERS {
? ?DWORD Signature;
? ?IMAGE_FILE_HEADER FileHeader;
? ?IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
Signature是00004550h,也就是字符'P'和'E'。對于OptionalHeader盡管按照字面的理解是可選的,實際上在每一個PE文件中都是需要的。

每個結構體的具體信息如下:
-
IMAGEFILEHEADER

最后一個字段Characteristics代表文件的屬性。如果是PE文件,這個字段的值是010fh,如果是dll文件,這個字段的值是210eh。
-
IMAGEOPTIONALHEADER32

AddressOfEntryPoint指出文件被執(zhí)行時的入口地址,這個地址是RVA(稍后會介紹)。如果像在程序運行前執(zhí)行一段程序,那么可以把這個入口地址指向你想執(zhí)行的程序。
ImageBase指出文件優(yōu)先載入的地址,一般exe默認優(yōu)先裝入的地址是400000h,而dll是10000000h
SectionAlignment指定了節(jié)被裝入內存后的對齊單位,F(xiàn)ileAlignment指定了節(jié)存儲在磁盤文件時的對齊單位
DataDirectory?

很重要的結構,由16個相同的IMAGEDATADIRECTORY結構組成。其中有導入表、導出表、資源表數(shù)據(jù)塊。
節(jié)表和節(jié)
Windows裝載器在裝載DOS部分,PE文件頭部分和節(jié)表頭部分不進行任何處理,而裝載節(jié)的時候根據(jù)節(jié)的屬性做不同的處理。下圖展示了PE文件到內存的映射。

節(jié)表?

VirtualAddress指出節(jié)被裝載到內存后的偏移位置
PointerToRawData指出節(jié)在磁盤文件中所處的位置
RVA和文件偏移的轉換
RVA應該時PE文件相關最重要的概念了,導入表導出表等的定位都需要用到RVA。

下圖展示了RVA的含義:

相關計算:

msf中巧妙調用ReflectiveLoader
在msf中,反射dll注入使用的非常普遍。這段代碼后續(xù)會詳細講解,這是msf遠程控制最核心的一段代碼,"小馬"(stager)拉"大馬"(stage)得以實現(xiàn),基于的就是這段代碼。
?def asm_invoke_dll(opts={})
asm = %Q^
; prologue
? ? ? ? ?dec ebp ? ? ? ? ? ? ? ; 'M'
? ? ? ? ?pop edx ? ? ? ? ? ? ? ; 'Z'
? ? ? ? ?call $+5 ; call next instruction
? ? ? ? ?pop ebx ? ? ? ? ? ? ? ; get the current location (+7 bytes)
? ? ? ? ?push edx ? ? ? ? ? ? ?; restore edx
? ? ? ? ?inc ebp ? ? ? ? ? ? ? ; restore ebp
? ? ? ? ?push ebp ? ? ? ? ? ? ?; save ebp for later
? ? ? ? ?mov ebp, esp ? ? ? ? ?; set up a new stack frame
; Invoke ReflectiveLoader()
; add the offset to ReflectiveLoader() (0x????????)
? ? ? ? ?add ebx, #{"0x%.8x" % (opts[:rdi_offset] - 7)}
? ? ? ? ?call ebx ? ? ? ? ? ? ?; invoke ReflectiveLoader()
; Invoke DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
? ? ? ? ?push edi ? ? ? ? ? ? ?; push the socket handle
? ? ? ? ?push 4 ; indicate that we have attached
? ? ? ? ?push eax ? ? ? ? ? ? ?; push some arbitrary value for hInstance
? ? ? ? ?mov ebx, eax ? ? ? ? ?; save DllMain for another call
? ? ? ? ?call ebx ? ? ? ? ? ? ?; call DllMain(hInstance, DLL_METASPLOIT_ATTACH, socket)
; Invoke DllMain(hInstance, DLL_METASPLOIT_DETACH, exitfunk)
; push the exitfunk value onto the stack
? ? ? ? ?push #{"0x%.8x" % Msf::Payload::Windows.exit_types[opts[:exitfunk]]}
? ? ? ? ?push 5 ; indicate that we have detached
? ? ? ? ?push eax ? ? ? ? ? ? ?; push some arbitrary value for hInstance
? ? ? ? ?call ebx ? ? ? ? ? ? ?; call DllMain(hInstance, DLL_METASPLOIT_DETACH, exitfunk)
^
?end
其中 add ebx,#{"0x%.8x" % (opts[:rdi_offset] - 7)}
用來獲取ReflectiveLoader的虛擬地址。rdi_offset獲取的是ReflectiveLoader的RVA地址。這段代碼翻譯過來就是ebx-7 = 基址,然后再加上RVA就得到了ReflectiveLoader的虛擬地址。
公眾號
更多內容,歡迎關注我的公眾號:無情劍客。
