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++; } }
运行结果