第三章:PE文件頭
PE中涉及的地址有四類,它們分別為:
虛擬內(nèi)存地址(VA)
相對(duì)虛擬內(nèi)存地址(RVA)
文件偏移地址(FOA)
特殊地址
?沒(méi)有物理內(nèi)存對(duì)應(yīng)的頁(yè)面被標(biāo)記為dirty的頁(yè)面,一般存儲(chǔ)在一個(gè)名為“交換文件”的磁盤文件中。在WINDOWS XP系統(tǒng)中,交換文件為pagefile.sys
?進(jìn)程本身的VA被解釋為:進(jìn)程的基地址+相對(duì)虛擬內(nèi)存地址
?RVA是相對(duì)基地址的偏移,即RVA是虛擬內(nèi)存中用來(lái)定位某個(gè)特定位置的地址,該地址的值是這個(gè)特定位置距離某個(gè)模塊基地址的偏移量,所以說(shuō)RVA是針對(duì)某個(gè)模塊而存在的。
?RVA是相對(duì)于模塊而言的,VA是相對(duì)于整個(gè)地址空間而言的。
?文件偏移地址是指某個(gè)位置距離文件頭的偏移。
數(shù)據(jù)目錄
?PE中的數(shù)據(jù)目錄這個(gè)數(shù)據(jù)結(jié)構(gòu)記錄了所有可能的數(shù)據(jù)類型,目前已定義的有15種:包括導(dǎo)出表,導(dǎo)入表,資源表,異常表,屬性證書表,重定位表,
??調(diào)試數(shù)據(jù),architecture,Global Ptr,線程局部存儲(chǔ),加載配置表,綁定導(dǎo)入表,IAT,延遲導(dǎo)入表和CLR運(yùn)行時(shí)頭部。
??//屬性證書表驗(yàn)證PE合法性?;IAT 導(dǎo)入地址表定義所有導(dǎo)入函數(shù)的VA,程序可以直接跳轉(zhuǎn)到此處執(zhí)行。
??//CLR運(yùn)行時(shí)頭部:.NET框架的重要信息,加載器通過(guò)此處信息來(lái)加載托管代碼所需的dll.
??
節(jié):存放不同類型數(shù)據(jù)(比如代碼,數(shù)據(jù),常量,資源等)的地方,不同的節(jié)具有不同的訪問(wèn)權(quán)限。節(jié)是PE文件中存放代碼或數(shù)據(jù)的基本單元。
??一個(gè)目標(biāo)文件中的所有代碼可以組合成單個(gè)節(jié),或者每個(gè)函數(shù)占一個(gè)節(jié),一個(gè)節(jié)中的所有原始數(shù)據(jù)必須被加載到連續(xù)的內(nèi)存空間中。
??數(shù)據(jù)類型不同,分屬不同的數(shù)據(jù)目錄,但其訪問(wèn)屬性相同,便被歸類到同一個(gè)節(jié)中,這個(gè)節(jié)最終可能會(huì)占用一個(gè)或多個(gè)頁(yè)面。
??.data 聲明的是初始化的數(shù)據(jù);.data? 聲明的是未初始化的數(shù)據(jù)(.data?的數(shù)據(jù)在磁盤中不存在,但在內(nèi)存中存在);.code聲明的是可執(zhí)行的代碼
對(duì)齊:PE中規(guī)定三類對(duì)齊:數(shù)據(jù)在內(nèi)存中對(duì)齊,數(shù)據(jù)在文件中對(duì)齊,資源文件中資源數(shù)據(jù)對(duì)齊
?節(jié)在內(nèi)存的對(duì)齊單位必須至少是一個(gè)頁(yè)的大小,32bit OS,這個(gè)值是4KB,64bit 8KB
?提高磁盤利用率,通常會(huì)以一個(gè)物理扇區(qū)的大小作為對(duì)齊粒度的值,512B=200H,所以數(shù)據(jù)段,代碼段等起始地址都是200h的倍數(shù)原因。
?資源字節(jié)碼部分一般要求以雙字方式對(duì)齊
16位系統(tǒng)下PE結(jié)構(gòu)分兩部分:DOS頭(DOS MZ頭 + DOS Stub)和冗余數(shù)據(jù)(PE頭 + PE數(shù)據(jù)區(qū))
32位OS下,正好相反,DOS頭成為冗余數(shù)據(jù) PE文件結(jié)構(gòu):DOS頭(DOS MZ頭 + DOS Stub)和冗余數(shù)據(jù)(PE頭 + 節(jié)表 + 節(jié)內(nèi)容)
????????????????????PE文件頭部(DOS頭 + PE頭)? PE數(shù)據(jù)區(qū)( 節(jié)表 + 節(jié)內(nèi)容)
typedef struct _IMAGE_FILE_HEADER {?
?WORD Machine;? ???//匯編偽指令?.386來(lái)生成相應(yīng)平臺(tái)上的匯編代碼?
?WORD NumberOfSections;? //始終等于節(jié)表中的節(jié)表項(xiàng)數(shù)量 否則加載會(huì)失敗
?DWORD TimeDateStamp;?
?DWORD PointerToSymbolTable;?
?DWORD NumberOfSymbols;
?WORD SizeOfOptionalHeader;? //確定IMAGE_OPTIONAL_HEADER32的大小
?WORD Characteristics;?//common pe file is 010fh, due to DLL file is 210eh
} IMAGE_FILE_HEADER,? *PIMAGE_FILE_HEADER;
?????
節(jié)表數(shù)量不確定,由_IMAGE_FILE_HEADER.NumberOfSections確定,但每個(gè)節(jié)的大小為40B
typedef struct _IMAGE_NT_HEADERS
{
+00h DWORD Signature
+04h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS, *PIMAGE_NT_HEADERS32;
IMAGE_NT_HEADERS=4BYTEPE標(biāo)識(shí)符 + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER32 總共240字節(jié) = 4 + 20 + 216
4BYTEPE標(biāo)識(shí)符:00004550h,修改任何字節(jié),32bit OS將無(wú)法加載些文件,對(duì)病毒文件可以修改下來(lái)預(yù)防啟動(dòng)
IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint 相當(dāng)PE頭 28h偏移量,用IDA可以自動(dòng)定位這個(gè)地址,許多病毒就是劫持這個(gè)值
IMAGE_OPTIONAL_HEADER32.CheckSum? 對(duì)大多數(shù)這個(gè)數(shù)為0,但對(duì)一些內(nèi)核模式的驅(qū)動(dòng)程序和系統(tǒng)dll,必須存在并正確。
代碼段一般不可寫,如在代碼段中寫數(shù)據(jù),運(yùn)行會(huì)出異常,壓縮軟件因?yàn)橐貙懘a段,它會(huì)設(shè)置為可寫,壓縮解壓后再運(yùn)行就不會(huì)異常了。
16個(gè)數(shù)據(jù)目錄, 節(jié)表放的就是所謂的段(數(shù)據(jù)段,代碼段什么的)
.data?數(shù)據(jù)節(jié)或叫數(shù)據(jù)段, .rdata 只讀數(shù)據(jù)段,部分用來(lái)存放數(shù)據(jù)目錄中的IAT。
內(nèi)存映射文件字節(jié)碼在內(nèi)存映射大致為:
???PE文件頭(pe header) + 代碼(.code) + 導(dǎo)入表(.rdata) + 數(shù)據(jù)(.data)
PE映像
???【PE映像頭】PE文件頭部(DOS頭 + PE頭)? PE數(shù)據(jù)區(qū)( 節(jié)表 + 節(jié)內(nèi)容)
???
???ollyICE里 memory map window
???PE文件頭(pe header)【確切叫內(nèi)存映射文件頭】 = PE文件頭部(DOS頭 + PE頭) +? 節(jié)表
???代碼(.code) + 導(dǎo)入表(.rdata) + 數(shù)據(jù)(.data) = 節(jié)內(nèi)容
???
???FOA 與 RVA 轉(zhuǎn)換
???FOA ?節(jié)表中PointerToRawData + offset
???RVA??節(jié)表中VirtualAddress + offset
???VA??IMAGE_OPTIONAL_HEADER32中ImageBase + RVA
;------------------------
; PE文件頭中的定位
; 戚利
; 2006.2.28
;------------------------.386.model flat,stdcalloption casemap:noneinclude windows.inc
include user32.inc
includelib user32.libinclude kernel32.inc
includelib kernel32.libinclude imagehlp.inc
includelib imagehlp.lib;數(shù)據(jù)段.data
szText db 'HelloWorld',0
szOut db '地址為:%08x',0
szOutIAT db 'IAT地址為:%08x',0
szBuffer db 256 dup(0)szExeFile db 'c:\windows\system32\kernel32.dll',0
szOut1 db 'MapFileAndCheckSum 函數(shù)與算法函數(shù)相等 kernel32.dll的校驗(yàn)和為:%08x',0
dwCheckSum dd ?;代碼段.code
;---------------------
; 將內(nèi)存偏移量RVA轉(zhuǎn)換為文件偏移
; lp_FileHead為文件頭的起始地址
; _dwRVA為給定的RVA地址
;---------------------
_RVAToOffset proc _lpFileHead,_dwRVAlocal @dwReturnpushadmov esi,_lpFileHeadassume esi:ptr IMAGE_DOS_HEADERadd esi,[esi].e_lfanewassume esi:ptr IMAGE_NT_HEADERSmov edi,_dwRVAmov edx,esiadd edx,sizeof IMAGE_NT_HEADERSassume edx:ptr IMAGE_SECTION_HEADERmovzx ecx,[esi].FileHeader.NumberOfSections;遍歷節(jié)表.repeatmov eax,[edx].VirtualAddressadd eax,[edx].SizeOfRawData ;計(jì)算該節(jié)結(jié)束RVA,;不用Misc的主要原因是有些段的Misc值是錯(cuò)誤的!.if (edi>=[edx].VirtualAddress)&&(edi<eax)mov eax,[edx].VirtualAddresssub edi,eax ;計(jì)算RVA在節(jié)中的偏移mov eax,[edx].PointerToRawDataadd eax,edi ;加上節(jié)在文件中的的起始位置jmp @F.endifadd edx,sizeof IMAGE_SECTION_HEADER.untilcxzassume edx:nothingassume esi:nothingmov eax,-1
@@:mov @dwReturn,eaxpopadmov eax,@dwReturnret
_RVAToOffset endp;-------------------
; 定位到指定索引的數(shù)據(jù)目錄項(xiàng)所在數(shù)據(jù)的起始地址
; _lpHeader 頭部基地址
; _index 數(shù)據(jù)目錄表索引,從0開(kāi)始
; _dwFlag1
; 為0表示_lpHeader是PE映像頭
; 為1表示_lpHeader是內(nèi)存映射文件頭
; _dwFlag2
; 為0表示返回RVA+模塊基地址
; 為1表示返回FOA+文件基地址
; 為2表示返回RVA
; 為3表示返回FOA
; 返回eax=指定索引的數(shù)據(jù)目錄項(xiàng)的數(shù)據(jù)所在地址
;-------------------
_rDDEntry proc _lpHeader,_index,_dwFlag1,_dwFlag2local @ret,@ret1,@ret2local @imageBasepushadmov esi,_lpHeaderassume esi:ptr IMAGE_DOS_HEADERadd esi,[esi].e_lfanew ;PE標(biāo)識(shí)assume esi:ptr IMAGE_NT_HEADERSmov eax,[esi].OptionalHeader.ImageBase ;程序的建議裝載地址mov @imageBase,eaxadd esi,0078h ;指向DataDirectoryxor eax,eax ;索引*8mov eax,_indexmov bx,8mul bxmov ebx,eax ; 取出指定索引數(shù)據(jù)目錄項(xiàng)的位置,是RVAmov eax,dword ptr [esi][ebx]mov @ret1,eax.if _dwFlag1==0 ;_lpHeader是PE映像頭 .if _dwFlag2==0 ;RVA+模塊基地址add eax,_lpHeader mov @ret,eax.elseif _dwFlag2==1 ;無(wú)意義,返回FOA invoke _RVAToOffset,_lpHeader,eaxmov @ret,eax .elseif _dwFlag2==2 ;RVAmov @ret,eax.elseif _dwFlag2==3 ;FOAinvoke _RVAToOffset,_lpHeader,eaxmov @ret,eax.endif.else ;_lpHeader是內(nèi)存映射文件頭.if _dwFlag2==0 ;RVA+模塊基地址add eax,@imageBasemov @ret,eax.elseif _dwFlag2==1 ;FOA+文件基地址;先將RVA轉(zhuǎn)換為文件偏移invoke _RVAToOffset,_lpHeader,eaxmov @ret2,eax add eax,_lpHeadermov @ret,eax.elseif _dwFlag2==2 ;RVAmov @ret,eax.elseif _dwFlag2==3 ;FOA;先將RVA轉(zhuǎn)換為文件偏移invoke _RVAToOffset,_lpHeader,eaxmov @ret,eax.endif.endifpopadmov eax,@retret
_rDDEntry endp;-------------------
; 定位到指定索引的節(jié)表項(xiàng)
; _lpHeader 頭部基地址
; _index 表示第幾個(gè)節(jié)表項(xiàng),從0開(kāi)始
; _dwFlag1
; 為0表示_lpHeader是PE映像頭
; 為1表示_lpHeader是內(nèi)存映射文件頭
; _dwFlag2
; 為0表示返回RVA+模塊基地址
; 為1表示返回FOA+文件基地址
; 為2表示返回RVA
; 為3表示返回FOA
; 返回eax=指定索引的節(jié)表項(xiàng)所在地址
;-------------------
_rSection proc _lpHeader,_index,_dwFlag1,_dwFlag2local @ret,@ret1,@ret2local @imageBasepushadmov esi,_lpHeaderassume esi:ptr IMAGE_DOS_HEADERadd esi,[esi].e_lfanew ;PE標(biāo)識(shí)assume esi:ptr IMAGE_NT_HEADERSmov eax,[esi].OptionalHeader.ImageBase ;程序的建議裝載地址mov @imageBase,eaxmov eax,[esi].OptionalHeader.NumberOfRvaAndSizesmov bx,8mul bxadd esi,0078h ;指向DataDirectoryadd esi,eax ;加上DataDirectory的大小,指向節(jié)表開(kāi)始xor eax,eax ;索引*40mov eax,_indexmov bx,40mul bxadd esi,eax ;索引項(xiàng)所在地址.if _dwFlag1==0 ;_lpHeader是PE映像頭 .if _dwFlag2==0 ;RVA+模塊基地址mov eax,esi mov @ret,eax.elsesub esi,_lpHeadermov eax,esimov @ret,eax.endif.else ;_lpHeader是內(nèi)存映射文件頭.if _dwFlag2==0 ;RVA+模塊基地址sub esi,_lpHeaderadd esi,@imageBasemov @ret,eax.elseif _dwFlag2==1 ;FOA+文件基地址mov eax,esimov @ret,eax.elsesub esi,_lpHeadermov eax,esimov @ret,eax.endif.endifpopadmov eax,@retret
_rSection endp;-------------------
; 通過(guò)調(diào)用API函數(shù)計(jì)算校驗(yàn)和
; kernel32.dll的校驗(yàn)和為:0011e97e
;-------------------
_checkSum1 proc _lpExeFile local @cSum,@hSumlocal @retpushadinvoke MapFileAndCheckSum,_lpExeFile,\addr @hSum,addr @cSummov eax,@cSummov @ret,eax popad mov eax,@retret
_checkSum1 endp;-------------------
; 自己編寫程序計(jì)算校驗(yàn)和
;-------------------
_checkSum2 proc _lpExeFilelocal hFile,dwSize,hBaselocal @sizelocal @retpushad;打開(kāi)文件invoke CreateFile,_lpExeFile,GENERIC_READ,\FILE_SHARE_READ,NULL,OPEN_EXISTING,\FILE_ATTRIBUTE_NORMAL,0mov hFile,eaxinvoke GetFileSize,hFile,NULLmov dwSize,eax;為文件分配內(nèi)存,并讀入invoke VirtualAlloc,NULL,dwSize,\MEM_COMMIT,PAGE_READWRITEmov hBase,eaxinvoke ReadFile,hFile,hBase,dwSize,addr @size,NULL;關(guān)閉文件invoke CloseHandle,hFile;第一步,將CheckSum清零 mov ebx,hBaseassume ebx:ptr IMAGE_DOS_HEADERmov ebx,[ebx].e_lfanewadd ebx,hBaseassume ebx:ptr IMAGE_NT_HEADERSmov [ebx].OptionalHeader.CheckSum,0assume ebx:ptr nothingmov ecx,dwSizemov esi,hBase;第二步,按字進(jìn)位加,溢出忽略push ecxinc ecxshr ecx,1xor ebx,ebxclc
loc1:lodsw ;load a word from esi to axadc bx,axloop loc1invoke VirtualFree,hBase,dwSize,MEM_DECOMMIT;第三步,加文件長(zhǎng)度 pop eaxadd eax,ebx mov @ret,eax
@exit:popad mov eax,@retret
_checkSum2 endpstart:;PEHeader.exe導(dǎo)入表數(shù)據(jù)所在VAinvoke _rDDEntry,00400000h,01h,0,0 invoke wsprintf,addr szBuffer,addr szOutIAT,eaxinvoke MessageBox,NULL,offset szBuffer,NULL,MB_OK;PEHeader.exe導(dǎo)入表數(shù)據(jù)在文件地址FOAinvoke _rDDEntry,00400000h,01h,0,3 invoke wsprintf,addr szBuffer,addr szOut,eaxinvoke MessageBox,NULL,offset szBuffer,NULL,MB_OK;PEHeader.exe第2個(gè)節(jié)表項(xiàng)在內(nèi)存的VA地址invoke _rSection,00400000h,01h,0,0 invoke wsprintf,addr szBuffer,addr szOut,eaxinvoke MessageBox,NULL,offset szBuffer,NULL,MB_OK;PEHeader.exe第2個(gè)節(jié)表項(xiàng)在文件的偏移invoke _rSection,00400000h,01h,0,3 invoke wsprintf,addr szBuffer,addr szOut,eaxinvoke MessageBox,NULL,offset szBuffer,NULL,MB_OK;計(jì)算校驗(yàn)和invoke _checkSum1,addr szExeFilemov dwCheckSum,eaxinvoke _checkSum2,addr szExeFile.if eax==dwCheckSuminvoke wsprintf,addr szBuffer,addr szOut1,eaxinvoke MessageBox,NULL,offset szBuffer,NULL,MB_OK.endifinvoke ExitProcess,NULLend start
?
總結(jié)
以上是生活随笔為你收集整理的PE学习(三)第三章:PE文件头的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。