// 02 遍历导入表.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> //************************************ // Method: IsPeFile // FullName: IsPeFile // Access: public // Returns: bool 成功失败 // Qualifier: // Parameter: TCHAR * szPath 路径 //************************************ bool IsPeFile(TCHAR* szPath) { BOOL bSuccess = TRUE; //1 将PE文件读取到内存 HANDLE hFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL ); DWORD dwSize = GetFileSize(hFile, NULL); DWORD dwRubbish = 0; unsigned char * pBuf = new unsigned char[dwSize]; ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL); //2 判断是否是PE文件 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf; if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { bSuccess = FALSE; goto Error; } PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pBuf + pDos->e_lfanew); if (pNt->Signature != IMAGE_NT_SIGNATURE) { bSuccess = FALSE; goto Error; } Error: if (pBuf != NULL) { delete[]pBuf; } if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } return bSuccess; } //************************************ // Method: RvaToOffect // FullName: RvaToOffect // Access: public // Returns: DWORD // Qualifier: 将RVA转换为Offect // Parameter: DWORD rva 要转换的RVA // Parameter: unsigned char * pFile 存储pe文件内容的缓冲区 //************************************ DWORD RvaToOffect(DWORD rva, unsigned char* pFile) { //1 找到NT头 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFile; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pFile + pDos->e_lfanew); //2 找到数据目录表 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); //3 判断要转换的位置是不是PE头部 if (rva < pSection->VirtualAddress) { return rva; } //4 在数据目录表中遍历,进行计算 for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { if ( (rva >= pSection->VirtualAddress) && (rva <= pSection->VirtualAddress + pSection->Misc.VirtualSize) ) { return rva - pSection->VirtualAddress + pSection->PointerToRawData; } pSection++; } return -1; } int _tmain(int argc, _TCHAR* argv[]) { //1 将PE文件读取到内存 HANDLE hFile = CreateFile( L"D:\Test.exe", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL ); DWORD dwSize = GetFileSize(hFile, NULL); DWORD dwRubbish = 0; unsigned char * pBuf = new unsigned char[dwSize]; ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL); //2 找到dos头 PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(pBuf); //3 找到nt头 PIMAGE_NT_HEADERS pNt = PIMAGE_NT_HEADERS(pBuf + pDos->e_lfanew); //4 找到扩展头 PIMAGE_OPTIONAL_HEADER pOption = &(pNt->OptionalHeader); //5 找到数据目录表 PIMAGE_DATA_DIRECTORY pDataDirectory = pOption->DataDirectory; //6 找到导入表的数据目录 PIMAGE_DATA_DIRECTORY pExportDirectory = (pDataDirectory + 1); //7解析导入表的数据目录 DWORD dwImportOffect = RvaToOffect(pExportDirectory->VirtualAddress, pBuf); PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(dwImportOffect + pBuf); //8 解析导入表 while (pImport->OriginalFirstThunk != 0) { //8.1通过INT的RVA得到INT在文件中的位置 PIMAGE_THUNK_DATA32 pInt = (PIMAGE_THUNK_DATA32)(RvaToOffect(pImport->OriginalFirstThunk, pBuf) + pBuf); char* pDllName = (char*)(RvaToOffect(pImport->Name,pBuf)+pBuf); printf("%s ", pDllName); while (pInt->u1.Ordinal!=0) { //8.2得到位置之后,判断一下最高位是不是1 if ((pInt->u1.Ordinal & 80000000)>>31 != 1) { //8.2.1如果不是1的话,说明它既有名称也有序号,需要使用PIMAGE_IMPORT_BY_NAME AddressOfData去解析 PIMAGE_IMPORT_BY_NAME pNameAndOrder = (PIMAGE_IMPORT_BY_NAME) (RvaToOffect(pInt->u1.AddressOfData, pBuf) + pBuf); printf(" 序号为:%x",pNameAndOrder->Hint); printf(" 名称为:%s ",pNameAndOrder->Name); } else { //8.2.2如果是1的话,说明它没有名称只有序号,直接使用DWORD Ordinal得到序号 printf(" 序号为:%x ", pInt->u1.Ordinal & 0x7FFFFFFF); } pInt++; } pImport++; } return 0; }