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


0 0