PE第五课——导入表
 
课堂笔记 上节课讲的是数据目录表,这节课是导入表,导入表好像是数据目录表的一种吧,也不清楚,暂时先这么理解。
现在需要用到库函数已经可以不需要导入表了,可以用 LoadLibarary 或者是 GetProcAddress 来获取函数地址直接使用,但是我们还是有必要学这个。
比较重要的结构体定义如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 typedef  struct  _IMAGE_IMPORT_DESCRIPTOR  {    union  {         DWORD   Characteristics;                     DWORD   OriginalFirstThunk;              } DUMMYUNIONNAME;     DWORD   TimeDateStamp;                                                                                                                                                              DWORD   ForwarderChain;                      DWORD   Name;     DWORD   FirstThunk;                      } IMAGE_IMPORT_DESCRIPTOR; typedef  struct  _IMAGE_THUNK_DATA32  {    union  {         DWORD ForwarderString;               DWORD Function;                      DWORD Ordinal;         DWORD AddressOfData;             } u1; } IMAGE_THUNK_DATA32; typedef  IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;typedef  struct  _IMAGE_IMPORT_BY_NAME  {    WORD    Hint;     CHAR   Name[1 ]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; 
 
然后这次就写了一个输出导入表的一个函数,之前出了一些问题,好像是 LordPE 写了那个 EXE 文件,然后导致拿到的文件头就貌似不太一样,调了很久挺难受,我还一直以为是 RvaToFrva 的问题,因为那里 return 0 啊什么的,后面调试发现 rva 的值很异常才去看那个文件结构的,010一看发现 MS-DOS 头都被改了。
我们找到了 rva 之后呢都要通过那个函数转为在 buffer 的变量。
首先找到数据目录表索引,也就是在扩展头的 DataDirectory 的内容,这其实是一个指针,然后我们通过加一定的偏移找到一些对应的数据目录表,具体偏移用宏定义表示出来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #define  IMAGE_DIRECTORY_ENTRY_EXPORT          0    #define  IMAGE_DIRECTORY_ENTRY_IMPORT          1    #define  IMAGE_DIRECTORY_ENTRY_RESOURCE        2    #define  IMAGE_DIRECTORY_ENTRY_EXCEPTION       3    #define  IMAGE_DIRECTORY_ENTRY_SECURITY        4    #define  IMAGE_DIRECTORY_ENTRY_BASERELOC       5    #define  IMAGE_DIRECTORY_ENTRY_DEBUG           6    #define  IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7    #define  IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8    #define  IMAGE_DIRECTORY_ENTRY_TLS             9    #define  IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10    #define  IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11    #define  IMAGE_DIRECTORY_ENTRY_IAT            12    #define  IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13    #define  IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14    
 
我们可以看到,1就是导入表的一个偏移。
找到了导入表之后,它有一个虚拟地址,找到虚拟地址之后转 Frva 加上读出的buffer就找到了那个导入表所在的结构体地址啦。它有一个 Name 字段指向名字所在的地址,不过也是虚拟地址,需要转 frva 之后加上 buffer。
当字段为空时,说明已经到了结尾,所以我们循环根据这个条件来。
然后就是一堆的数据结构了,最后我们能找到所有动态链接库的所有导入的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 #include <windows.h>  #include <stdio.h>  #include "main.h"  int  main ()   {    FILE* fd = fopen ("C:\\Users\\xia0ji233\\Desktop\\Home\\C++\\test.exe" , "rb" );     char * buffer = (char *)malloc (0x100000 );     if  (buffer == NULL ) {         perror ("malloc fail" );         exit (0 );     }     if  (fd == NULL ) {         perror ("NO such File" );         exit (0 );     }     fread (buffer, 1 , 0x100000 , fd);     PIMAGE_DOS_HEADER pheader = (PIMAGE_DOS_HEADER)buffer;     printf ("MS-DOS INFO:\n" );     printf ("MAGIC HEADER: " );     fwrite ((char *)&pheader->e_magic, 2 , 1 , stdout);     putchar (10 );     printf ("PE OFFSET:%x\n" , pheader->e_lfanew);     printf ("PE INFO:\n" );     PIMAGE_NT_HEADERS ReadNTHeaders = (PIMAGE_NT_HEADERS)(&(buffer[pheader->e_lfanew]));     printf ("PE Magic Header:" );     fwrite (&ReadNTHeaders->Signature, 2 , 1 , stdout);     putchar (10 );     printf ("Standard Header info:" );     printf ("Platform:" );     switch  (ReadNTHeaders->FileHeader.Machine)     {     case  IMAGE_FILE_MACHINE_I386:         printf ("I386" );         break ;     case  IMAGE_FILE_MACHINE_IA64:         printf ("Intel 64" );         break ;     case  IMAGE_FILE_MACHINE_AMD64:         printf ("AMD 64" );         break ;     default :         printf ("UNKnown Platform" );         break ;     }     putchar (10 );     printf ("Optional PE Header:" );     printf ("ImageBase:%08x\n" , ReadNTHeaders->OptionalHeader.ImageBase);     PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION (ReadNTHeaders);     PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeaders->FileHeader;     for  (int  i = 0 ; i < pFileHeader->NumberOfSections; i++) {         printf ("Name(区段名称):%s\n" , ReadSectionHeader[i].Name);         printf ("Voffset(起始的相对虚拟地址):%08X\n" , ReadSectionHeader[i].VirtualAddress);         printf ("VSize(区段大小):%08X\n" , ReadSectionHeader[i].SizeOfRawData);         printf ("ROffset(文件偏移):%08X\n" , ReadSectionHeader[i].PointerToRawData);         printf ("RSize(文件中区段大小):%08X\n" , ReadSectionHeader[i].Misc.VirtualSize);         printf ("标记(区段的属性):%08X\n\n" , ReadSectionHeader[i].Characteristics);     }     ImportTable (buffer);     system ("pause" ); } DWORD RvaToFrva (DWORD Rva, char * buffer)   {         PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;     PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);     PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION (pNt);     if  (Rva < pSection[0 ].VirtualAddress) {         return  Rva;     }     for  (int  i = 0 ; i < pNt->FileHeader.NumberOfSections; i++) {         if  (Rva >= pSection[i].VirtualAddress && Rva <= pSection[i].Misc.VirtualSize + pSection[i].VirtualAddress) {                                       return  Rva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;         }     }     return  Rva; } void  ImportTable (char  *buffer)   {    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;     PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);     PIMAGE_DATA_DIRECTORY pImportDir = (PIMAGE_DATA_DIRECTORY)(pNt->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_IMPORT);     PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(buffer + RvaToFrva (pImportDir->VirtualAddress, buffer));     while  (pImport->Name) {         char * DllName = (char *)(RvaToFrva (pImport->Name, buffer) + buffer);           printf ("DllName: %s\n" , DllName);          printf ("日期时间标志:%08X\n" , pImport->TimeDateStamp);          printf ("ForwarderChain:%08X\n" , pImport->ForwarderChain);          printf ("名称OFFSET:%08X\n" , pImport->Name);          printf ("FirstThunk:%08X\n" , pImport->FirstThunk);          printf ("OriginalFirstThunk:%08X\n\n" , pImport->OriginalFirstThunk);         PIMAGE_THUNK_DATA pIat = (PIMAGE_THUNK_DATA)(RvaToFrva (pImport->OriginalFirstThunk, buffer) + buffer);         DWORD index = 0 ;         DWORD ImportOffset = 0 ;         while  (pIat->u1. Ordinal != 0 ) {             PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToFrva (pIat->u1. AddressOfData, buffer) + buffer);             printf ("API 名称:%s\n" , pName->Name);             printf ("Hint:%04X\n" , pName->Hint);             printf ("ThunkValue:%08X\n\n"  ,pIat->u1.F unction);             pIat++;         }         pImport++;     } } 
 
运行结果