解析PE文件
最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。
PE文件主要有DOS头,NT头(包含PE头,可选PE头,可选PE头中又包含着一个数据目录,里面包含了其他表单的RVA和大小),节表
表单主要有导出表,导入表,重定位表,资源表。除了资源表外,其他表单的各项数据都用代码实现了打印。
首先通过CreateFile打开文件,获得文件句柄,随后调用CreateFileMapping生成内存映射对象,再调用MapViewOfFile获得文件映射基址。
DOS头RVA=(IMAGE_DOS_HEADER)映射基址;
NT头RVA=(IMAGE_NT_HEADERS)DOS头+DOS头->e_lfanew的值;
PE头RVA=&((IMAGE_FILE_HEADER)NT头->FileHeader);
可选PE头RVA=&((IMAGE_OPTIONAL_HEADER)NT头->OptionalHeader);
节表RVA=IMAGE_FIRST_SECTION(NT头);//节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)
节表数量:PE头->NumberOfSections;
导入表、导出表、重定位表的RVA和长度分别在可选PE头的->DataDirectory的第三、第一、第六个数组当中的VirtualAddress和size成员中(注意数组索引从0开始)
注意,在解析前,先要判断当前文件是否为PE文件:以下2个条件缺一不可。
1.dos头的值,即dos->e_magic是否等于0x5A4D(MZ)
2.NT头,即DOS头(RVA)+DOS->e_lfnew的值 得到的即为NT头的RVA 查看NT头的值,即NT->signatrue的值是否为0x00004550(PE)
下面po上代码:
1 #include<Windows.h> 2 #include<iostream> 3 #include<time.h> 4 using namespace std; 5 6 typedef struct _MApFileH_STRUCT 7 { 8 HANDLE hFile; //文件句柄 9 HANDLE hFileMap; //映射文件句柄 10 LPVOID pMapImageBase; //映像基址 11 } MApFileH_STRUCT, *PMApFileH_STRUCT; 12 /*一、加载要打开的文件 13 采用内存映射的方式将文件加载到内存中,内存映射函数包括:CreateFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile和FlushViewOfFile。 14 定义一个函数,如果是一个PE文件则返回true, 否则返回false:*/ 15 BOOL LoadFile(LPTSTR pFileName, PMApFileH_STRUCT pFileStruct)//加载文件 并将文件句柄、 16 { 17 if (pFileName == nullptr) 18 { 19 return false; 20 } 21 HANDLE hFile = NULL; 22 HANDLE hFileMap = NULL; 23 LPVOID pMapImageBase = NULL; 24 memset(pFileStruct, 0, sizeof(MApFileH_STRUCT)); 25 //1、只读方式打开文件,返回文件句柄 26 hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 27 if (!hFile) 28 { 29 return false; 30 } 31 //2、创建内存映射文件对象 32 hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0); 33 if (!hFileMap) 34 { 35 CloseHandle(hFile); 36 return false; 37 } 38 //3、创建内存映射文件的视图 39 pMapImageBase = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); 40 if (!pMapImageBase) 41 { 42 CloseHandle(hFile); 43 CloseHandle(hFileMap); 44 return false; 45 } 46 pFileStruct->hFile = hFile; 47 pFileStruct->hFileMap = hFileMap; 48 pFileStruct->pMapImageBase = pMapImageBase; 49 return true; 50 } 51 //二、程序执行完毕后,回收资源 52 void UnLoadFile(PMApFileH_STRUCT pFileStruct) 53 { 54 if (pFileStruct->hFile) 55 { 56 CloseHandle(pFileStruct->hFile); 57 } 58 59 if (pFileStruct->hFileMap) 60 { 61 CloseHandle(pFileStruct->hFileMap); 62 } 63 64 if (pFileStruct->pMapImageBase) 65 { 66 //撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄 67 UnmapViewOfFile(pFileStruct->pMapImageBase); 68 } 69 } 70 //三、文件格式检测 71 bool IsPEFile(LPVOID pMapImageBase)//如果1.基地址为0 2.DOC头的e_magic不为5A4D(MZ) 3.NT头的Signature不为00004550(PE) 任一成立,则该文件不是PE文件 72 { 73 PIMAGE_DOS_HEADER pDosH = nullptr;//指向空指针 74 PIMAGE_NT_HEADERS32 pNtH = nullptr; 75 if (!pMapImageBase) 76 { 77 return false; 78 } 79 80 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase; 81 if (pDosH->e_magic != IMAGE_DOS_SIGNATURE) // 5A4D "MZ" 82 { 83 return false; 84 } 85 // pDH->e_lfanew保存PIMAGE_NT_HEADERS32的偏移地址,加上基址pDH即为MAGE_NT_HEADERS的地址 86 pNtH = (PIMAGE_NT_HEADERS32)((DWORD)pDosH + pDosH->e_lfanew); //基地址+DOS头的最后一个元素取得的值 即为下一个NT头的地址 87 if (pNtH->Signature != IMAGE_NT_SIGNATURE) //"PE" 88 { 89 return false; 90 } 91 return true; 92 } 93 94 //四、读取FileHeader和OptionalHeader的内容 95 PIMAGE_DOS_HEADER GetDOSHeaders(LPVOID pMapImageBase)//1、获取指向IMAGE_DOS_HEADERS结构的指针 96 { 97 PIMAGE_DOS_HEADER pDosH = nullptr; 98 if (!IsPEFile(pMapImageBase)) 99 { 100 return nullptr; 101 } 102 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase; 103 return pDosH; 104 } 105 PIMAGE_NT_HEADERS GetNtHeader(LPVOID pMapImageBase)//2、获取指向IMAGE_NT_HEADERS结构的指针 106 { 107 PIMAGE_DOS_HEADER pDosH = nullptr; 108 PIMAGE_NT_HEADERS pNtH = nullptr; 109 if (!IsPEFile(pMapImageBase)) 110 { 111 return nullptr; 112 } 113 114 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase; 115 pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDosH + pDosH->e_lfanew); 116 return pNtH; 117 } 118 PIMAGE_FILE_HEADER GetFileHeader(LPVOID pMapImageBase)//3、获取指向IMAGE_FILE_HEADER结构的指针 119 { 120 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase); 121 if (!pNtH) 122 { 123 return nullptr; 124 } 125 return &(pNtH->FileHeader); 126 } 127 PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID pMapImageBase)//4、获取指向IMAGE_OPTIONAL_HEADER结构的指针 128 { 129 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase); 130 if (!pNtH) 131 { 132 return nullptr; 133 } 134 return &(pNtH->OptionalHeader); 135 } 136 137 PIMAGE_SECTION_HEADER GetSectionone(LPVOID pMapImageBase)//5、获取指向第一个节头的指针 138 { 139 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//得到NT头 140 if (!pNtH) 141 { 142 return nullptr; 143 } 144 //节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小) 145 return IMAGE_FIRST_SECTION(pNtH);//通过公式计算 得到SECTION的首地址 146 } 147 148 short GetdwSectionCount(LPVOID pMapImageBase)//6、节头的数量 通过File Hander的NumberOfSections得出 149 { 150 PIMAGE_FILE_HEADER pFileHeader = GetFileHeader(pMapImageBase); 151 if (pFileHeader != NULL) 152 { 153 return pFileHeader->NumberOfSections; 154 } 155 } 156 //五、地址转换 157 //1.相对虚拟地址转文件地址(文件地址=映射基址+文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点))) 158 DWORD RVAtoFA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA 159 { 160 PIMAGE_SECTION_HEADER pSectionHH = GetSectionone(pMapImageBase);//指向节表 161 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头 162 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量 163 for (int iNum = 0; iNum < dwSectionCount; iNum++) 164 { 165 if (dwRVA >= pSectionHH->VirtualAddress && dwRVA < (pSectionHH->VirtualAddress + pSectionHH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内 166 { 167 return (DWORD)pMapImageBase + dwRVA - pSectionHH->VirtualAddress + pSectionHH->PointerToRawData; 168 /*则文件地址=映射基址 + 文件偏移地址( RVA- VirtualAddress + RawAddress) 169 = 映射基址 + RVA - VirtualAddress + RawAddress*/ 170 } 171 pSectionHH++;//指向下一个节表 172 } 173 return 0; 174 } 175 //2.相对虚拟地址转文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点))) 176 DWORD RVAtoFVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA 177 { 178 PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表 179 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头 180 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量 181 for (int iNum = 0; iNum < dwSectionCount; iNum++) 182 { 183 if (dwRVA >= pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内 184 { 185 return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData; 186 } 187 pSectionH++;//指向下一个节表 188 } 189 return 0; 190 } 191 //3.虚拟地址转文件偏移地址 192 DWORD VAtoFVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA 193 { 194 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头 195 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址 196 DWORD dwRVA = dwVA - dwIB;//将要转换的VA先转换为RVA 197 PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表 198 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头 199 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量 200 for (int iNum = 0; iNum < dwSectionCount; iNum++) 201 { 202 if (dwRVA>pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内 203 { 204 return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData;//则文件偏移地址=RVA - VirtualAddress + RawAddress 205 } 206 pSectionH++;//指向下一个节表 207 } 208 return 0; 209 } 210 //4.相对虚拟地址转虚拟地址 211 DWORD RVAtoVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA 212 { 213 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头 214 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址 215 return dwRVA + dwIB; 216 } 217 //5.虚拟地址转相对虚拟地址 218 DWORD VAtoRVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA 219 { 220 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头 221 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址 222 return dwVA - dwIB; 223 } 224 //六、输出文件头信息 225 void ShowFileHeaderInfo(PMApFileH_STRUCT pFileStruct) 226 { 227 char szOutText[1024] = { 0 }; 228 PIMAGE_DOS_HEADER pDosH = nullptr;//DOS头 229 pDosH = GetDOSHeaders(pFileStruct->pMapImageBase); 230 if (!pDosH) 231 { 232 printf("获取DOS头失败\n"); 233 return; 234 } 235 printf("Dos Header:\n"); 236 printf("e_magic: %04lX\n", pDosH->e_magic); 237 /*省略若干个信息*/ 238 printf("\n"); 239 PIMAGE_NT_HEADERS pNtH = nullptr; //NT头 240 pNtH = GetNtHeader(pFileStruct->pMapImageBase); 241 if (!pNtH) 242 { 243 printf("获取文件头失败\n"); 244 return; 245 } 246 printf("NT Header:\n"); 247 printf("Signature: %08x\n", pNtH->Signature); 248 PIMAGE_FILE_HEADER pFileH = nullptr;//PE头 249 //将信息按十六进制格式化 250 pFileH = GetFileHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的File结构的首地址 251 char *szFileHeaderFormat = "\nIMAGE_FILE_HEADER:\n\ 252 Machine: %04lX\n\ 253 NumberOfSections: %04lX\n\ 254 TimeDateStamp: %04lX\n\ 255 PointerToSymbolTable:%08X\n\ 256 NumberOfSymbols: %08lX\n\ 257 SizeOfOptionalHeader:%04lX\n\ 258 Characteristics: %04lX\n\n"; 259 sprintf(szOutText, szFileHeaderFormat, pFileH->Machine, pFileH->NumberOfSections, pFileH->TimeDateStamp, pFileH->PointerToSymbolTable, pFileH->NumberOfSymbols, 260 pFileH->SizeOfOptionalHeader, pFileH->Characteristics); 261 printf("%s", szOutText); 262 memset(szOutText, 0, sizeof(szOutText)); 263 264 PIMAGE_OPTIONAL_HEADER pOFileH = nullptr; //可选PE头 265 char *szFileOptHeaderFormat = "\nIMAGE_OPTIONAL_HEADER:\n\ 266 Entry Point: %08lX\n\ 267 Image Base: %08lX\n\ 268 Code Base: %08lX\n\ 269 Data Base: %08lX\n\ 270 Image Size: %08lX\n\ 271 Headers Size: %08lX\n\ 272 Section Alignment:%08lX\n\ 273 File Alignment: %08lX\n\ 274 Subsystem: %08lX\n\ 275 Check Sum: %04lX\n\ 276 Dll Flags: %04lX\n\n"; 277 pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址 278 if (!pOFileH) 279 { 280 printf("Get File Optional Header failed!\n"); 281 return; 282 } 283 sprintf(szOutText, szFileOptHeaderFormat, pOFileH->AddressOfEntryPoint, pOFileH->ImageBase, pOFileH->BaseOfCode, pOFileH->BaseOfData, 284 pOFileH->SizeOfImage, pOFileH->SizeOfHeaders, pOFileH->SectionAlignment, pOFileH->FileAlignment, pOFileH->Subsystem, pOFileH->CheckSum, pOFileH->DllCharacteristics); 285 printf("%s", szOutText); 286 287 288 PIMAGE_SECTION_HEADER pSectionH = nullptr; //节表 289 pSectionH = GetSectionone(pFileStruct->pMapImageBase);//通过基址得到NT头 再通过公式算出第一个节的地址 290 DWORD dwSectionCount = pFileH->NumberOfSections;//通过PE头的NumberOfSections成员 得到节的数量 291 printf("name:\tVirtual Size\tVirtual Address\tRaw Size\tRaw Address\t\n"); 292 for (DWORD dwNum = 0; dwNum < dwSectionCount; dwNum++) 293 { 294 /*printf("name:%s,Virtual Size:%08x,Virtual Address:%08x,Raw Size:%08x,Raw Address:%08x\n", 295 pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData);*/ 296 printf("%s:\t%08x\t%08x\t%08x\t%08x\n", pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData); 297 pSectionH++; 298 } 299 printf("\n"); 300 301 /*通过可选PE头的DataDirectory数组(该数组为IMAGE_DATA_DIRECTORY结构 里面是各表的RVA和大小)*/ 302 pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址 303 printf("导入表的相对偏移地址为:0x%08x\n", pOFileH->DataDirectory[1].VirtualAddress); 304 printf("导入表的大小为:0x%08x\n", pOFileH->DataDirectory[1].Size);//已对比 取值正确 305 int iDllCount;//dll的数量 306 int iFuncCount;//dll中函数的数量 307 308 long lImportRVA = pOFileH->DataDirectory[1].VirtualAddress;//导入表的位置 309 //得到第一个导入表的地址 310 PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, lImportRVA)); 311 /*上下2条同理,都是通过所需地址的RVA求得在文件中的VA,上面那条求得文件的FVA后,要手动加上映像基地址;下面那条直接返回FRVA(文件地址) 312 VA=虚拟地址 RVA=相对虚拟地址 IB基地址 FVA=文件地址 FRVA=文件偏移地址(相对开头)FIB=映射基地址(即pFileStruct->pMapImageBase)*/ 313 //PIMAGE_IMPORT_DESCRIPTOR pImportH2 = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(d_nt, d_nIB, CCC->DataDirectory[1].VirtualAddress, NULL); 314 for (iDllCount = 0; pImportTable->FirstThunk != NULL; iDllCount++) 315 { 316 char* pszDllName = (char*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->Name);//将存放DLL名称的地址转换成字符指针 317 printf("DLL Name:%s\t INT:0x%08x,Name RVA:0x%08x,IAT:0x%08x\n", 318 pszDllName, pImportTable->OriginalFirstThunk, pImportTable->Name, pImportTable->FirstThunk); 319 320 //获得导入的DLL中的数据 321 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->OriginalFirstThunk)); 322 for (iFuncCount = 0; pThunk->u1.Function != 0; iFuncCount++) 323 { 324 if (pThunk->u1.AddressOfData & 0x80000000) //如果导入表的输入名称表(INT)的最高位为1,则为按序号导入 325 { 326 printf("\t[%d]\t IAT:%04x\t 导入序号:%04x\n", iFuncCount, pThunk->u1.AddressOfData, pThunk->u1.AddressOfData & 0xffff); 327 } //这里的pThunk->u1.AddressOfData &0xffff是为了让32位的二进制最后以16位显示出来 328 else //不为1,则函数名称以字符串形式 此时变为指向PIMAGE_IMPORT_BY_NAME结构的RVA 329 { 330 PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)RVAtoFA(pFileStruct->pMapImageBase, pThunk->u1.AddressOfData); 331 printf("\t[%d]\t Hint:%04x\t Name:%s\n", iFuncCount, pImportName->Hint, pImportName->Name); 332 } 333 pThunk++; 334 } 335 printf("-----------------------------------------------------------------------------------------\n"); 336 pImportTable++; 337 } 338 printf("该PE文件导入表共有%d个DLL\n\n", iDllCount); 339 //PIMAGE_IMPORT_DESCRIPTOR pImportTable; //导入表由这样的结构的数组构成,最后以一个该结构全为0作为结束标志 340 //PIMAGE_THUNK_DATA pThunkNum;//上述结构中,有2个成员导入名称表(INT)和导入地址表(IAT)是该类型的结构 为1时直接输出 341 //PIMAGE_IMPORT_BY_NAME pImportName;//当ITD的最高位不为1时,IAT的值变成了指向这样一个结构的RVA 342 343 /*导出表*/ 344 //PIMAGE_OPTIONAL_HEADER pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase); 345 346 DWORD dwExportRVA = pOFileH->DataDirectory[0].VirtualAddress;//导出表的相对偏移地址 347 if (dwExportRVA == 0) 348 goto Relocation; 349 printf("导出表的相对偏移地址:0x%08x\n", pOFileH->DataDirectory[0].VirtualAddress); 350 printf("导出表长度:0x%08x\n", pOFileH->DataDirectory[0].Size); 351 PIMAGE_EXPORT_DIRECTORY dwExportTable = (PIMAGE_EXPORT_DIRECTORY)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, dwExportRVA));//求得导出表的文件地址 352 printf("--------------------------------------------------------------------------------\n"); 353 printf("Member\t\t\t Offset\t Size\t\t Value\n"); 354 printf("--------------------------------------------------------------------------------\n"); 355 printf("Characteristics\t\t %08x\t Dword\t\t %08x\n", dwExportRVA, dwExportTable->Characteristics); 356 printf("TimeDateStamp\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD), dwExportTable->TimeDateStamp); 357 printf("MajorVersion\t\t %08x\t Word\t\t %04x\n", dwExportRVA + sizeof(DWORD)* 2, dwExportTable->MajorVersion); 358 printf("MinorVersion\t\t %08x\t Word\t\t %04x\n", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD), dwExportTable->MinorVersion); 359 printf("Name\t\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD)* 2, dwExportTable->Name); 360 printf("Base\t\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 3 + sizeof(WORD)* 2, dwExportTable->Base); 361 printf("NumberOfFunctions\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 4 + sizeof(WORD)* 2, dwExportTable->NumberOfFunctions); 362 printf("NumberOfNames\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 5 + sizeof(WORD)* 2, dwExportTable->NumberOfNames); 363 printf("AddressOfFunctions\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 6 + sizeof(WORD)* 2, dwExportTable->AddressOfFunctions); 364 printf("AddressOfNames\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 7 + sizeof(WORD)* 2, dwExportTable->AddressOfNames); 365 printf("AddressOfNameOrdinals\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 8 + sizeof(WORD)* 2, dwExportTable->AddressOfNameOrdinals); 366 367 char* szFuncName = NULL;//用于输出函数名字 368 for (DWORD dwCount = 0; dwCount < dwExportTable->NumberOfFunctions; dwCount++) //导出序号不大于导出函数的值 369 { 370 DWORD dwExportNum = dwExportTable->Base + (dwCount * 1);//导出序号 371 DWORD dwFuncAddrRVA = dwExportTable->AddressOfFunctions + (dwCount*sizeof(DWORD));//导出函数地址的rva 372 DWORD dwFuncNameAddrRVA = dwExportTable->AddressOfNames + (dwCount*sizeof(DWORD));//存放着导出函数名字地址的地址 373 DWORD dwFuncNameNumRVA = dwExportTable->AddressOfNameOrdinals + (dwCount*sizeof(WORD));//导出函数名字的序号的rva 374 DWORD dwFuncNameRVA = *(DWORD*)RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameAddrRVA);//将存放着函数名字RVA的RVA转换成文件地址 解引用获得函数名字RVA 375 szFuncName = (char*)(RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameRVA));//再将函数名字RVA转换成文件地址,取得文件名字 376 printf("Ordinals:%08x Functions RVA:%08x Name Ordinal:%08x Name RVA:%08X Name:%s\n\n", dwExportNum, dwFuncAddrRVA, dwFuncNameNumRVA, dwFuncNameAddrRVA, szFuncName); 377 } 378 Relocation: 379 /*重定位表*/ 380 DWORD dwRelocationRVA = pOFileH->DataDirectory[5].VirtualAddress;//重定位表rva 381 DWORD dwdwRelocationSize = pOFileH->DataDirectory[5].Size;//重定位表的大小 382 printf("\n重定位表的相对偏移地址:0x%08x\n", pOFileH->DataDirectory[5].VirtualAddress); 383 printf("重定位表的大小:0x%08x\n", pOFileH->DataDirectory[5].Size); 384 if (dwRelocationRVA == 0) 385 { 386 printf("该文件没有重定位表\n"); 387 return; 388 } 389 PIMAGE_BASE_RELOCATION pRelocationTable;//重定位表的文件地址 390 //当前位置的RVA不能大于重定位表最后一块数据的RVA(起始位置+大小) 391 while (dwRelocationRVA<(pOFileH->DataDirectory[5].VirtualAddress + pOFileH->DataDirectory[5].Size)) 392 { 393 pRelocationTable = (PIMAGE_BASE_RELOCATION)(RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA));//取得当前块文件地址 394 int dwdwRelocationSize = (pRelocationTable->SizeOfBlock - 8) / 2;//这个块中需要重定位的值的个数 (该块前8位分别是起始地址和长度) 395 printf("VirtualAddress:%08x\t SizeOfBlock:%08x\t ltems:%d\n", pRelocationTable->VirtualAddress, pRelocationTable->SizeOfBlock, dwdwRelocationSize); 396 for (int iNum = 0; iNum < dwdwRelocationSize; iNum++) 397 { 398 WORD pRelocationTable_Chunk = *(WORD*)RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA + 8 + sizeof(WORD)*iNum); 399 if (pRelocationTable_Chunk != 0) 400 { 401 printf("ltem:%04x\t 需要重定位的数据的RVA:%08x\n", pRelocationTable_Chunk, (pRelocationTable_Chunk ^ 0x3000) + pRelocationTable->VirtualAddress); 402 /*重定位地址=当前块内的偏移(当前2字节的值(需要通过文件地址取出来)^ 0x3000)+当前块的基址*/ 403 } 404 } 405 printf("--------------------------------------------------------------\n"); 406 dwRelocationRVA = dwRelocationRVA + pRelocationTable->SizeOfBlock; 407 } 408 } 409 410 411 MApFileH_STRUCT pFileStruct = { nullptr, nullptr, nullptr }; 412 int main() 413 { 414 LPTSTR pFilePath = TEXT("E:\\学习\\C++\\静态解析PE文件测试\\解析PE文件测试\\kkk.dll"); 415 if (!LoadFile(pFilePath, &pFileStruct))//载入文件内容 416 { 417 return -1; 418 } 419 420 if (!IsPEFile(pFileStruct.pMapImageBase))//检查文件是否为PE文件 421 { 422 UnLoadFile(&pFileStruct); 423 return -1; 424 } 425 ShowFileHeaderInfo(&pFileStruct); 426 UnLoadFile(&pFileStruct); 427 system("pause"); 428 }