PE学习(四)第四章:导入表
来源:互联网 发布:网络问政平台 编辑:程序博客网 时间:2024/04/29 07:51
第四章:导入表
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,stdcall option casemap:noneinclude windows.incinclude user32.incincludelib user32.libinclude kernel32.incincludelib kernel32.lib;数据段 .datasz1 db 'Shell_TrayWnd',0hTray dd ?;代码段 .codestart: invoke FindWindow,addr sz1,0 mov hTray,eax invoke ShowWindow,hTray,SW_SHOW invoke EnableWindow,hTray,TRUE invoke ExitProcess,NULL end start
//GetImportTableInfo.asm;------------------------; PE文件头中的定位; 戚利; 2006.2.28;------------------------ .386 .model flat,stdcall option casemap:noneinclude windows.incinclude user32.incincludelib user32.libinclude kernel32.incincludelib kernel32.libinclude msvcrt.incincludelib msvcrt.lib;数据段 .dataszBuffer db 256 dup(0)szExeFile db 'test.exe',0dwCheckSum dd ?.constszErrFormat db '这个文件不是PE格式的文件!',0szNotFound db '无法查找',0szMsg1 db 0dh,0ah db '--------------------------------------------------------',0dh,0ah db '导入表所处的节:%s',0dh,0ah db '--------------------------------------------------------',0dh,0ah,0szMsgImport db 0dh,0ah db '导入库:%s',0dh,0ah db '-----------------------------',0dh,0ah,0dh,0ah db 'OriginalFirstThunk %08x',0dh,0ah db 'TimeDateStamp %08x',0dh,0ah db 'ForwarderChain %08x',0dh,0ah db 'FirstThunk %08x',0dh,0ah db '-----------------------------',0dh,0ah,0dh,0ah,0szMsg2 db '%08u %s',0dh,0ah,0szMsg3 db '%08u(无函数名,按序号导入)',0dh,0ah,0szErrNoImport db 0dh,0ah db '未发现该文件有导入函数',0dh,0ah,0dh,0ah,0;代码段 .code;---------------------; 将内存偏移量RVA转换为文件偏移; lp_FileHead为文件头的起始地址baseaddress; _dwRVA为给定的RVA地址;---------------------_RVAToOffset proc _lpFileHead,_dwRVA local @dwReturn pushad mov esi,_lpFileHead assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew assume esi:ptr IMAGE_NT_HEADERS mov edi,_dwRVA mov edx,esi add edx,sizeof IMAGE_NT_HEADERS assume edx:ptr IMAGE_SECTION_HEADER movzx ecx,[esi].FileHeader.NumberOfSections ;遍历节表 .repeat mov eax,[edx].VirtualAddress add eax,[edx].SizeOfRawData ;计算该节结束RVA, ;不用Misc的主要原因是有些段的Misc值是错误的! .if (edi>=[edx].VirtualAddress)&&(edi<eax) mov eax,[edx].VirtualAddress sub edi,eax ;计算RVA在节中的偏移 mov eax,[edx].PointerToRawData add eax,edi ;加上节在文件中的的起始位置 jmp @F .endif add edx,sizeof IMAGE_SECTION_HEADER .untilcxz assume edx:nothing assume esi:nothing mov eax,-1@@: mov @dwReturn,eax popad mov eax,@dwReturn ret_RVAToOffset endp;------------------; 错误Handler;------------------_Handler proc _lpExceptionRecord,_lpSEH,\ _lpContext,_lpDispathcerContext pushad mov esi,_lpExceptionRecord mov edi,_lpContext assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT mov eax,_lpSEH push [eax+0ch] pop [edi].regEbp push [eax+8] pop [edi].regEip push eax pop [edi].regEsp assume esi:nothing,edi:nothing popad mov eax,ExceptionContinueExecution ret_Handler endp;------------------------; 获取RVA所在节的名称;------------------------_getRVASectionName proc _lpFileHead,_dwRVA local @dwReturn pushad mov esi,_lpFileHead assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew assume esi:ptr IMAGE_NT_HEADERS mov edi,_dwRVA mov edx,esi add edx,sizeof IMAGE_NT_HEADERS assume edx:ptr IMAGE_SECTION_HEADER;节表的第一个 movzx ecx,[esi].FileHeader.NumberOfSections ;遍历节表 .repeat mov eax,[edx].VirtualAddress add eax,[edx].SizeOfRawData ;计算该节结束RVA .if (edi>=[edx].VirtualAddress)&&(edi<eax) mov eax,edx jmp @F .endif add edx,sizeof IMAGE_SECTION_HEADER .untilcxz assume edx:nothing assume esi:nothing mov eax,offset szNotFound@@: mov @dwReturn,eax popad mov eax,@dwReturn ret_getRVASectionName endp;--------------------; 获取PE文件的导入表;--------------------_getImportInfo proc _lpFile,_lpPeHead,_dwSize local @szBuffer[1024]:byte local @szSectionName[16]:byte pushad mov edi,_lpPeHead assume edi:ptr IMAGE_NT_HEADERS mov eax,[edi].OptionalHeader.DataDirectory[8].VirtualAddress .if !eax invoke crt_printf,addr szErrNoImport jmp _Ret .endif invoke _RVAToOffset,_lpFile,eax add eax,_lpFile mov edi,eax ;计算引入表所在文件偏移位置 assume edi:ptr IMAGE_IMPORT_DESCRIPTOR invoke _getRVASectionName,_lpFile,[edi].OriginalFirstThunk invoke wsprintf,addr @szBuffer,addr szMsg1,eax ;显示节名;eax某个节表项地址,第一个元素为节名 invoke crt_printf,addr @szBuffer .while [edi].OriginalFirstThunk || [edi].TimeDateStamp ||\ [edi].ForwarderChain || [edi].Name1 ||\ [edi].FirstThunk invoke _RVAToOffset,_lpFile,[edi].Name1 add eax,_lpFile invoke wsprintf,addr @szBuffer,addr szMsgImport,eax,\ [edi].OriginalFirstThunk,[edi].TimeDateStamp,\ [edi].ForwarderChain,[edi].FirstThunk invoke crt_printf,addr @szBuffer ;获取IMAGE_THUNK_DATA列表到EBX .if [edi].OriginalFirstThunk mov eax,[edi].OriginalFirstThunk .else mov eax,[edi].FirstThunk .endif invoke _RVAToOffset,_lpFile,eax add eax,_lpFile mov ebx,eax .while dword ptr [ebx] ;按序号导入 ;.if dword ptr [ebx] & IMAGE_ORDINAL_FLAG32;IMAGE_ORDINAL_FLAG32 应该等于80000000h .if dword ptr [ebx] & 80000000h mov eax,dword ptr [ebx] and eax,0ffffh invoke wsprintf,addr @szBuffer,addr szMsg3,eax .else ;按名称导入 invoke _RVAToOffset,_lpFile,dword ptr [ebx] add eax,_lpFile assume eax:ptr IMAGE_IMPORT_BY_NAME movzx ecx,[eax].Hint invoke wsprintf,addr @szBuffer,\ addr szMsg2,ecx,addr [eax].Name1 assume eax:nothing .endif invoke crt_printf,addr @szBuffer add ebx,4 .endw add edi,sizeof IMAGE_IMPORT_DESCRIPTOR .endw_Ret: assume edi:nothing popad ret_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_VALUE mov @hFile,eax invoke GetFileSize,eax,NULL mov @dwFileSize,eax .if eax invoke CreateFileMapping,@hFile,\ ;内存映射文件 NULL,PAGE_READONLY,0,0,NULL .if eax mov @hMapFile,eax invoke MapViewOfFile,eax,\ FILE_MAP_READ,0,0,0 .if eax ;获得文件在内存的映象起始位置 mov @lpMemory,eax assume fs:nothing push ebp push offset _ErrFormat push offset _Handler push fs:[0] mov fs:[0],esp ;检测PE文件是否有效 mov esi,@lpMemory assume esi:ptr IMAGE_DOS_HEADER ;判断是否有MZ字样 .if [esi].e_magic!=IMAGE_DOS_SIGNATURE jmp _ErrFormat .endif ;调整ESI指针指向PE文件头 add esi,[esi].e_lfanew assume esi:ptr IMAGE_NT_HEADERS ;判断是否有PE字样 .if [esi].Signature!=IMAGE_NT_SIGNATURE jmp _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,0ch invoke UnmapViewOfFile,@lpMemory .endif invoke CloseHandle,@hMapFile .endif invoke CloseHandle,@hFile .endif .endif@@: ret_openFile endpstart: call _openFile invoke ExitProcess,NULL end start
- PE学习(四)第四章:导入表
- Windows PE 第四章 导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- PE文件结构详解(四)PE导入表
- 【PE学习-------导入表】
- PE文件详解(四)--导入表1
- PE文件详解(四)--导入表2
- PE学习1-导入表
- PE学习日记----导入表
- PE文件结构详解(四)PE导入表&nbs…
- PE导入表(输入表)学习笔记
- 【转】Hash及TopK算法
- Java程序与C语言的区别
- 【转载】神经网络编程入门
- Jdbc连接MySQL数据库
- 3.19
- PE学习(四)第四章:导入表
- 3.19
- Linux_基础_目录结构
- javascript 获取表单中radio选中值
- MongoDB 让secondary支持读操作
- 数据库第二次作业
- dvsdk_4_00_00_22_dm3730 Makefile内容
- Extend the CPS concept, remodel the company organization 扩展CPS概念,对企业信息系统进行重新建模
- View的onMeasure参数理解