为了更直观的了解输入表结构,请看下面来在《加密与解密》第三版中的图示: /*本程序旨在说明如何阅读IMAGE_IMPORT_DESCRIPTOR(IID)输入表结构*/ #include <windows.h> #include <stdio.h> //ImageDirectoryEntryToData //微软的ImageHlp库中提供了大量有关对PE操作得到API #include <imagehlp.h> #pragma comment(lib,"imagehlp.lib") /************************************************************************ 概念: 1.输入表(IID): 数据目录第二个成员指向输入表,是一个以IMAGE_IMPORT_DESCRIPTOR(IID)结构开 始,以一个空的IMAGE_IMPORT_DESCRIPTOR结构结束;每一个隐式链接进来的dll都 有一个IID 2.输入名称表(INT) 由IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk指明其RVA,INT是一个 IMAGE_THUNK_DATA结构数组,并以一个内容为0的元素结束数组中每一个元素指向一个 IMAGE_IMPORT_BY_NAME结构 3.输入地址表(IAT) 由IMAGE_IMPORT_DESCRIPTOR.FirstThunk指明其RVA;与INT非常相似,同样是一个 IMAGE_THUNK_DATA结构数组;只是在pe加载时其中内容被填充为导入函数真正的地址 ************************************************************************/ /************************************************************************/ /* 读取输入表,输出到文件 */ /************************************************************************/ BOOL ReadImportTable(HMODULE hInstance,PIMAGE_IMPORT_DESCRIPTOR pImportDesc) { PBYTE pImageBase=(PBYTE)hInstance; FILE* pfile=fopen("ImportTable.log","w"); if (!pfile) { MessageBox(NULL,"Open or Create ImportTable.log Failed!","ERROR",MB_ICONWARNING); return FALSE; } /* 引入表实际上是一个IMAGE_IMPORT_DESCRIPTOR结构数组。 每个结构包含PE文件引入函数的一个相关DLL的信息。 该数组以一个全0的成员结尾. */ while(pImportDesc->FirstThunk!=NULL) { /*获取DLL名称*/ DWORD dllNameRVA=pImportDesc->Name; PCHAR dllName=(PCHAR)(pImageBase+dllNameRVA); fprintf(pfile,"/nImport DLL:%s>>>>>>>>>>>>>>>/n",dllName); /*获得IMAGE_THUNK_DATA数组*/ //FirstThunk 与 OriginalFirstThunk 非常相似 //FirstThunk里面的地址在运行时会填入导入函数的实际地址 //而不再是IMAGE_IMPORT_BY_NAME结构的地址 DWORD OFthunkDataRVA=pImportDesc->OriginalFirstThunk; PIMAGE_THUNK_DATA pOFthunkData=(PIMAGE_THUNK_DATA)(pImageBase+OFthunkDataRVA); DWORD FthunkDataRVA=pImportDesc->FirstThunk; PIMAGE_THUNK_DATA pFthunkData=(PIMAGE_THUNK_DATA)(pImageBase+FthunkDataRVA); /*遍历IMAGE_THUNK_DATA数组,获取导入函数名称/地址(INT/IAT)*/ while(pOFthunkData->u1.AddressOfData!=NULL&& pFthunkData->u1.AddressOfData!=NULL) { //VC6.0和VS2008对于u1.AddressOfData定义不相同, //但本质是相同的,都是四个字节大小,都保存的是RVA //VS2008:DWORD AddressOfData; //VC6.0: PIMAGE_IMPORT_BY_NAME AddressOfData; /*获取导入函数名*/ //DWORD ImportByNameRVA=pthunkData->u1.AddressOfData; DWORD ImportByNameRVA=(DWORD)pOFthunkData->u1.AddressOfData; PIMAGE_IMPORT_BY_NAME pImportByName=(PIMAGE_IMPORT_BY_NAME)(pImageBase+ImportByNameRVA); PCHAR funcName=(PCHAR)pImportByName->Name; fprintf(pfile,"Import Func:%s/t",funcName); /*获取导入函数地址*/ fprintf(pfile,"@0x%08x -> 0x%08x/n",(DWORD)pFthunkData,*(PDWORD)pFthunkData); pFthunkData++; pOFthunkData++; } pImportDesc++; } fclose(pfile); return TRUE; } /************************************************************************/ /* 获得输入表(IID)的两种方法 */ /************************************************************************/ //方法1:通过ImageDirectoryEntryToData获得IID //ImageDirectoryEntryToData可以获得可选文件头中的所有数据目录,根据第三个参数 PIMAGE_IMPORT_DESCRIPTOR GetIATMethod1(HMODULE hInstance) { PIMAGE_IMPORT_DESCRIPTOR pImportDesc ; ULONG uSize ; pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hInstance, TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&uSize) ; return pImportDesc; } //方法2:通过自己定位可选文件头获得IID PIMAGE_IMPORT_DESCRIPTOR GetIATMethod2(HMODULE hInstance) { PBYTE pImageBase=(PBYTE)hInstance; /*获得DOS文件头*/ PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)hInstance; if(pDosHeader->e_magic!= IMAGE_DOS_SIGNATURE) { MessageBox(NULL,"DOS文件头有误!","ERROR",MB_ICONWARNING); return NULL; } /*获得PE头*/ PIMAGE_NT_HEADERS pNTHeaders=(PIMAGE_NT_HEADERS)(pImageBase+pDosHeader->e_lfanew); if(pNTHeaders->Signature != IMAGE_NT_SIGNATURE) { MessageBox(NULL,"无效的PE文件!","ERROR",MB_ICONWARNING); return NULL; } /*根据PE头获得可选文件头*/ PIMAGE_OPTIONAL_HEADER pOptionalHeader=&pNTHeaders->OptionalHeader; /*根据可选文件头获得IID*/ DWORD ImportTableRVA=pOptionalHeader->DataDirectory[1].VirtualAddress; PIMAGE_IMPORT_DESCRIPTOR pImportDesc=(PIMAGE_IMPORT_DESCRIPTOR)(pImageBase+ImportTableRVA); return pImportDesc; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. //hInstance其实就是指向PE在内存中的映像 //在程序任何位置都可以通过GetModuleHandle得到 //HMODULE hInstance =GetModuleHandle(NULL); PIMAGE_IMPORT_DESCRIPTOR pImportDesc=NULL; //方法1:通过ImageDirectoryEntryToData获得IAT pImportDesc=GetIATMethod1(hInstance); //方法2:通过自己定位可选文件头获得IAT //pImportDesc=GetIATMethod2(hInstance); if(!pImportDesc) { MessageBox(NULL,"GetIAT Failed!","ERROR",MB_ICONWARNING); return 0; } if(!ReadImportTable(hInstance,pImportDesc)) { MessageBox(NULL,"ReadImportTable Failed!","ERROR",MB_ICONWARNING); return 0; } MessageBox(NULL,"ReadImportTable Succeed!","SUCCEED",MB_OK); return 0; }