第四章:導入表
windos加載器會一并加載導入表中的dll,并修改相應指令調用的函數地址。
IMAGE_NT_HEADERS STRUCT{
?Signature DWORD ?
?FileHeader IMAGE_FILE_HEADER <>
?OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
}IMAGE_NT_HEADERS ENDS
IMAGE_OPTIONAL_HEADER32{
?...
?...
?DataDirectory IMAGE_DATA_DIRECTORY 16 dup(<>) ;0078h //相對IMAGE_NT_HEADERS偏移量
?...
}
IMAGE_DATA_DIRECTORY STRUCT{
?VirtualAddress DWORD ?
?isize?DWORD ?
}
導入表: OptionalHeader.DataDirectory[1]
IAT: OptionalHeader.DataDirectory[12]
導入表數據的起始是一組導入表描述符結構,每組20B,全0結尾,所以上面isize都是20B的整數倍。
IMAGE_IMPORT_DESCRIPTOR STRUCT{
?union{
??Characteristics?dd ?
??OriginalFirstThunk?dd ??;//橋1?指向_IMAGE_THUNK_DATA數組
?}ends
?TimeDateStamp?dd ?
?ForwarderCharin?dd ??;//鏈表的前一結構
?Name1?dd ?????;//指向連接庫名字的指針
?FirstThunk?dd ??;//橋2
}
OriginalFirstThunk 指向一個數組,數組成員_IMAGE_THUNK_DATA,其實就是DWORD,最高位0,表示導入函數是個數值(RVA),為1,導入函數是符號。
導入函數是個數值(RVA)指向的是IMGE_IMPORT_BY_NAME
IMGE_IMPORT_BY_NAME{
?Hint?dd???//dll對每個函數進行編號,這個就是,可以用編號也可以用名字來訪問
?Name1?db??
}
雙橋結構的導入表在文件中存在兩份內容完全相同的地址列表,橋2指向的地址列表叫IAT,橋1的叫INT(Import Name Table)
Borland公司的link.exe只保留橋2.?單橋結構的導入表無法執行綁定導入操作
橋1:(hint - name)
IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].OriginalFirstThunk.Hint
IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].OriginalFirstThunk.Name1
橋2:
IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].FirstThunk?IAT中的值(VA)
IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].OriginalFirstThunk 與IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].FirstThunk?IAT中的值 在文件中相同的;
PE加載后進程空間中的 IAT的值 被修改為真實的VA,此時通過(hint - name)的橋斷了。
IAT表: 相同鏈接庫的函數的VA放在一起最近DWORD全零結束。
定位IAT表有兩種文件,1.直接OptionalHeader.DataDirectory[12]?? 2.橋2:IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1].FirstThunk
導入表: OptionalHeader.DataDirectory[1] 僅僅指IMAGE_IMPORT_DESCRIPTOR結構數組。
遍歷導入表信息,可以猜測大致功能,應用逆向與病毒分析。
綁定導入機制
WINDOWS加載程序負責IAT的修正工作,加載前提前修正就是綁定。WINDOWS加載程序也會對綁定過的進行驗證
IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[11].VirtualAddress
HEX查看
IMAGE_NT_HEADERS+78h偏移到數據目錄基地址 再加8*11D到Bond Import Table Address
Bond Import Table Address指向的是IMGE_BOND_IMPORT_DESCRIPTOR
IMGE_BOND_IMPORT_DESCRIPTOR STRUCT{
?TimeDateStamp?dword???;時間chuo??與dll中IMAGE_FILE_HEADER的TimeDateStamp相同,dll更新過,則重新修正
?OffsetModuleName?word ? ;指向DLL名稱? //以第一個IMGE_BOND_IMPORT_DESCRIPTOR為基準
?NumberofModuleForwarderRefs word ??;ModuleForwarderRef 數日?//描述緊接些結構后另一結構IMGE_BOND_FORWARD_REF數組元素個數
}
IMGE_BOND_FORWARD_REF STRUCT{
?TimeDateStamp?dword??
?OffsetModuleName?word ?
?Reserved word ?
}
IAT可以不連續的,只要JMP目標地址和導入表的FirstThun字段指針指向正確的位置就OK。
導入表可以不用放在.rdata,只要找個合適的可讀間隙,想修改相應引用處就OK。
?
?
//test.asm.386.model flat,stdcalloption casemap:noneinclude windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib;數據段.data
sz1 db 'Shell_TrayWnd',0
hTray dd ?;代碼段.code
start:invoke FindWindow,addr sz1,0mov hTray,eaxinvoke ShowWindow,hTray,SW_SHOWinvoke EnableWindow,hTray,TRUEinvoke ExitProcess,NULLend start
?
//GetImportTableInfo.asm
;------------------------
; PE文件頭中的定位
; 戚利
; 2006.2.28
;------------------------.386.model flat,stdcalloption casemap:noneinclude windows.inc
include user32.inc
includelib user32.libinclude kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib;數據段.data
szBuffer db 256 dup(0)szExeFile db 'test.exe',0
dwCheckSum dd ?.const
szErrFormat db '這個文件不是PE格式的文件!',0
szNotFound db '無法查找',0
szMsg1 db 0dh,0ahdb '--------------------------------------------------------',0dh,0ahdb '導入表所處的節:%s',0dh,0ahdb '--------------------------------------------------------',0dh,0ah,0
szMsgImport db 0dh,0ahdb '導入庫:%s',0dh,0ahdb '-----------------------------',0dh,0ah,0dh,0ahdb 'OriginalFirstThunk %08x',0dh,0ahdb 'TimeDateStamp %08x',0dh,0ahdb 'ForwarderChain %08x',0dh,0ahdb 'FirstThunk %08x',0dh,0ahdb '-----------------------------',0dh,0ah,0dh,0ah,0
szMsg2 db '%08u %s',0dh,0ah,0
szMsg3 db '%08u(無函數名,按序號導入)',0dh,0ah,0
szErrNoImport db 0dh,0ahdb '未發現該文件有導入函數',0dh,0ah,0dh,0ah,0
;代碼段.code
;---------------------
; 將內存偏移量RVA轉換為文件偏移
; lp_FileHead為文件頭的起始地址 baseaddress
; _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;遍歷節表.repeatmov eax,[edx].VirtualAddressadd eax,[edx].SizeOfRawData ;計算該節結束RVA,;不用Misc的主要原因是有些段的Misc值是錯誤的!.if (edi>=[edx].VirtualAddress)&&(edi<eax)mov eax,[edx].VirtualAddresssub edi,eax ;計算RVA在節中的偏移mov eax,[edx].PointerToRawDataadd eax,edi ;加上節在文件中的的起始位置jmp @F.endifadd edx,sizeof IMAGE_SECTION_HEADER.untilcxzassume edx:nothingassume esi:nothingmov eax,-1
@@:mov @dwReturn,eaxpopadmov eax,@dwReturnret
_RVAToOffset endp;------------------
; 錯誤Handler
;------------------
_Handler proc _lpExceptionRecord,_lpSEH,\_lpContext,_lpDispathcerContextpushadmov esi,_lpExceptionRecordmov edi,_lpContextassume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXTmov eax,_lpSEHpush [eax+0ch]pop [edi].regEbppush [eax+8]pop [edi].regEippush eaxpop [edi].regEspassume esi:nothing,edi:nothingpopadmov eax,ExceptionContinueExecutionret
_Handler endp;------------------------
; 獲取RVA所在節的名稱
;------------------------
_getRVASectionName 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_HEADER ;節表的第一個movzx ecx,[esi].FileHeader.NumberOfSections;遍歷節表.repeatmov eax,[edx].VirtualAddressadd eax,[edx].SizeOfRawData ;計算該節結束RVA.if (edi>=[edx].VirtualAddress)&&(edi<eax)mov eax,edxjmp @F.endifadd edx,sizeof IMAGE_SECTION_HEADER.untilcxzassume edx:nothingassume esi:nothingmov eax,offset szNotFound
@@:mov @dwReturn,eaxpopadmov eax,@dwReturnret
_getRVASectionName endp;--------------------
; 獲取PE文件的導入表
;--------------------
_getImportInfo proc _lpFile,_lpPeHead,_dwSizelocal @szBuffer[1024]:bytelocal @szSectionName[16]:bytepushadmov edi,_lpPeHeadassume edi:ptr IMAGE_NT_HEADERSmov eax,[edi].OptionalHeader.DataDirectory[8].VirtualAddress.if !eaxinvoke crt_printf,addr szErrNoImportjmp _Ret.endifinvoke _RVAToOffset,_lpFile,eaxadd eax,_lpFilemov edi,eax ;計算引入表所在文件偏移位置assume edi:ptr IMAGE_IMPORT_DESCRIPTORinvoke _getRVASectionName,_lpFile,[edi].OriginalFirstThunkinvoke wsprintf,addr @szBuffer,addr szMsg1,eax ;顯示節名 ;eax某個節表項地址,第一個元素為節名invoke crt_printf,addr @szBuffer.while [edi].OriginalFirstThunk || [edi].TimeDateStamp ||\[edi].ForwarderChain || [edi].Name1 ||\[edi].FirstThunkinvoke _RVAToOffset,_lpFile,[edi].Name1add eax,_lpFileinvoke wsprintf,addr @szBuffer,addr szMsgImport,eax,\[edi].OriginalFirstThunk,[edi].TimeDateStamp,\[edi].ForwarderChain,[edi].FirstThunkinvoke crt_printf,addr @szBuffer;獲取IMAGE_THUNK_DATA列表到EBX.if [edi].OriginalFirstThunkmov eax,[edi].OriginalFirstThunk.elsemov eax,[edi].FirstThunk.endifinvoke _RVAToOffset,_lpFile,eaxadd eax,_lpFilemov ebx,eax.while dword ptr [ebx];按序號導入;.if dword ptr [ebx] & IMAGE_ORDINAL_FLAG32 ;IMAGE_ORDINAL_FLAG32 應該等于80000000h.if dword ptr [ebx] & 80000000hmov eax,dword ptr [ebx]and eax,0ffffhinvoke wsprintf,addr @szBuffer,addr szMsg3,eax.else ;按名稱導入 invoke _RVAToOffset,_lpFile,dword ptr [ebx]add eax,_lpFileassume eax:ptr IMAGE_IMPORT_BY_NAMEmovzx ecx,[eax].Hintinvoke wsprintf,addr @szBuffer,\addr szMsg2,ecx,addr [eax].Name1assume eax:nothing.endifinvoke crt_printf,addr @szBufferadd ebx,4.endwadd edi,sizeof IMAGE_IMPORT_DESCRIPTOR.endw
_Ret:assume edi:nothingpopadret
_getImportInfo endp_openFile proclocal @hFile,@dwFileSize,@hMapFile,@lpMemoryinvoke CreateFile,addr szExeFile,GENERIC_READ,\FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,\OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL.if eax!=INVALID_HANDLE_VALUEmov @hFile,eaxinvoke GetFileSize,eax,NULLmov @dwFileSize,eax.if eaxinvoke CreateFileMapping,@hFile,\ ;內存映射文件NULL,PAGE_READONLY,0,0,NULL.if eaxmov @hMapFile,eaxinvoke MapViewOfFile,eax,\FILE_MAP_READ,0,0,0.if eax;獲得文件在內存的映象起始位置mov @lpMemory,eaxassume fs:nothingpush ebppush offset _ErrFormatpush offset _Handlerpush fs:[0]mov fs:[0],esp;檢測PE文件是否有效mov esi,@lpMemoryassume esi:ptr IMAGE_DOS_HEADER;判斷是否有MZ字樣.if [esi].e_magic!=IMAGE_DOS_SIGNATUREjmp _ErrFormat.endif;調整ESI指針指向PE文件頭add esi,[esi].e_lfanewassume esi:ptr IMAGE_NT_HEADERS;判斷是否有PE字樣.if [esi].Signature!=IMAGE_NT_SIGNATUREjmp _ErrFormat.endif;到此為止,該文件的驗證已經完成。為PE結構文件;接下來分析分件映射到內存中的數據,并顯示主要參數;@lpMemory Maps a view of a file mapping into the address space of a calling process;esi assume esi:ptr IMAGE_NT_HEADERS;顯示導入表invoke _getImportInfo,@lpMemory,esi,@dwFileSizejmp _ErrorExit_ErrFormat:invoke MessageBox,NULL,offset szErrFormat,\NULL,MB_OK
_ErrorExit:pop fs:[0]add esp,0chinvoke UnmapViewOfFile,@lpMemory.endifinvoke CloseHandle,@hMapFile.endifinvoke CloseHandle,@hFile.endif.endif
@@: ret
_openFile endpstart:call _openFileinvoke ExitProcess,NULLend start
總結
以上是生活随笔為你收集整理的PE学习(四)第四章:导入表的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。