当前位置: 首页 > news >正文

解析PE文件的导入表和导出表

原理可以参考《逆向工程核心原理》这本书。

#include <windows.h>
#include <winnt.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>// 函数声明
DWORD RvaToOffset(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer, DWORD rva);
void ParseImportTable(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer);
void ParseExportTable(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer);// RVA转文件偏移
DWORD RvaToOffset(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer, DWORD rva) {PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) {if (rva >= pSectionHeader[i].VirtualAddress &&rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize) {return rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;}}return 0;
}// 解析PE文件导入表
void ParseImportTable(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer) {DWORD importRVA = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;if (importRVA == 0) {_tprintf(_T("No import table found\n"));return;}std::cout << "importRVA: " << importRVA << std::endl;// 计算导入表在文件中的位置//这行代码是PE文件解析中的关键步骤,用于获取PE文件节表(Section Table)的起始地址。//IMAGE_FIRST_SECTION 宏: //1. 从NT头开始 (DWORD_PTR)(ntheader)     //2. 加上OptionalHeader字段的偏移量//3. 再加上可选头的实际大小//4. 结果就是节表的起始地址      PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);DWORD importOffset = 0;for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) {if (importRVA >= pSectionHeader[i].VirtualAddress &&importRVA < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize) {importOffset = importRVA - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;break;}}if (importOffset == 0) {_tprintf(_T("Failed to locate import table\n"));return;}std::cout << "importOffset: " << importOffset << std::endl;PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)pFileBuffer + importOffset);_tprintf(_T("\nImport Table:\n"));while (pImportDescriptor->Name != 0) {// 获取DLL名称DWORD dllNameOffset = RvaToOffset(pNtHeaders, pFileBuffer, pImportDescriptor->Name);if (dllNameOffset == 0) {_tprintf(_T("DLL: [Invalid RVA: 0x%08X]\n"), pImportDescriptor->Name);} else {LPCSTR dllName = (LPCSTR)((DWORD_PTR)pFileBuffer + dllNameOffset);_tprintf(_T("DLL: %hs\n"), dllName);}// 解析导入函数PIMAGE_THUNK_DATA pThunk;if (pImportDescriptor->OriginalFirstThunk != 0) {pThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pImportDescriptor->OriginalFirstThunk));} else {pThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pImportDescriptor->FirstThunk));}_tprintf(_T("  Functions:\n"));while (pThunk->u1.AddressOfData != 0) {if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) {// 按序号导入_tprintf(_T("    Ordinal: %d\n"), IMAGE_ORDINAL(pThunk->u1.Ordinal));} else {// 按名称导入PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pThunk->u1.AddressOfData));_tprintf(_T("    Name: %hs\n"), pImportByName->Name);}pThunk++;}pImportDescriptor++;}
}// 解析PE文件导出表
void ParseExportTable(PIMAGE_NT_HEADERS pNtHeaders, LPVOID pFileBuffer) {DWORD exportRVA = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;if (exportRVA == 0) {_tprintf(_T("No export table found\n"));return;}// 计算导出表在文件中的位置DWORD exportOffset = RvaToOffset(pNtHeaders, pFileBuffer, exportRVA);if (exportOffset == 0) {_tprintf(_T("Failed to locate export table\n"));return;}PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)pFileBuffer + exportOffset);_tprintf(_T("\nExport Table:\n"));// 获取模块名称LPCSTR moduleName = (LPCSTR)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pExportDirectory->Name));_tprintf(_T("Module: %hs\n"), moduleName);// 获取导出函数数量DWORD numberOfFunctions = pExportDirectory->NumberOfFunctions;DWORD numberOfNames = pExportDirectory->NumberOfNames;_tprintf(_T("Total functions: %d, Named functions: %d\n"), numberOfFunctions, numberOfNames);// 获取导出函数地址表、名称表和序号表DWORD* pAddressTable = (DWORD*)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pExportDirectory->AddressOfFunctions));DWORD* pNameTable = (DWORD*)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pExportDirectory->AddressOfNames));WORD* pOrdinalTable = (WORD*)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pExportDirectory->AddressOfNameOrdinals));// 打印所有导出函数_tprintf(_T("Exported functions:\n"));for (DWORD i = 0; i < numberOfNames; i++) {LPCSTR functionName = (LPCSTR)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, pNameTable[i]));WORD ordinal = pOrdinalTable[i];DWORD functionRVA = pAddressTable[ordinal];// 检查是否为转发函数if (functionRVA >= exportRVA && functionRVA < exportRVA + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) {LPCSTR forwarderName = (LPCSTR)((DWORD_PTR)pFileBuffer + RvaToOffset(pNtHeaders, pFileBuffer, functionRVA));_tprintf(_T("  %hs (Ordinal: %d) -> Forwarded to: %hs\n"), functionName, ordinal + pExportDirectory->Base, forwarderName);} else {_tprintf(_T("  %hs (Ordinal: %d) at RVA: 0x%08X\n"), functionName, ordinal + pExportDirectory->Base, functionRVA);}}// 打印仅按序号导出的函数if (numberOfNames < numberOfFunctions) {_tprintf(_T("\nFunctions exported by ordinal only:\n"));for (DWORD i = 0; i < numberOfFunctions; i++) {bool isNamed = false;for (DWORD j = 0; j < numberOfNames; j++) {if (pOrdinalTable[j] == i) {isNamed = true;break;}}if (!isNamed && pAddressTable[i] != 0) {_tprintf(_T("  Ordinal: %d at RVA: 0x%08X\n"), i + pExportDirectory->Base, pAddressTable[i]);}}}
}int _tmain22(int argc, TCHAR* argv[]) {if (argc < 2) {_tprintf(_T("Usage: %s <PE file>\n"), argv[0]);return 1;}HANDLE hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile == INVALID_HANDLE_VALUE) {_tprintf(_T("Error opening file: %d\n"), GetLastError());return 1;}DWORD fileSize = GetFileSize(hFile, NULL);//这行代码使用Windows API函数VirtualAlloc在进程的虚拟地址空间中分配一块内存区域。LPVOID pFileBuffer = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_READWRITE);DWORD bytesRead;ReadFile(hFile, pFileBuffer, fileSize, &bytesRead, NULL);CloseHandle(hFile);// 检查DOS头PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {_tprintf(_T("Invalid DOS signature\n"));VirtualFree(pFileBuffer, 0, MEM_RELEASE);return 1;}// 获取NT头PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pFileBuffer + pDosHeader->e_lfanew);if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {_tprintf(_T("Invalid PE signature\n"));VirtualFree(pFileBuffer, 0, MEM_RELEASE);return 1;}// 根据32/64位调用不同的解析函数if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {_tprintf(_T("32-bit PE file detected\n"));} else if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {_tprintf(_T("64-bit PE file detected\n"));} else {_tprintf(_T("Unknown PE format\n"));VirtualFree(pFileBuffer, 0, MEM_RELEASE);return 1;}// 解析导入表和导出表ParseImportTable(pNtHeaders, pFileBuffer);ParseExportTable(pNtHeaders, pFileBuffer);VirtualFree(pFileBuffer, 0, MEM_RELEASE);return 0;
}
http://www.xdnf.cn/news/1455193.html

相关文章:

  • 准确率可达99%!注意力机制+UNet,A会轻松收割!
  • 20250904的学习笔记
  • HTML + CSS 创建图片倒影的 5 种方法
  • 大数据毕业设计选题推荐-基于大数据的儿童出生体重和妊娠期数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • 加密货币武器化:恶意npm包利用以太坊智能合约实现隐蔽通信
  • 性能堪比claude sonnet4,免费无限使用!claude code+魔搭GLM4.5在ubuntu上安装完整流程
  • Cadence OrCAD Capture绘制复用管脚封装的方法图文教程
  • 蔚来8月狂卖3.1万辆,反超理想引热议!
  • C++ opencv+gstreamer编译,C++ opencv4.5.5+gstreamer1.0 -1.24.12 编译 ,cmake 4.0.0
  • OpenCV: Mat存储方式全解析-单通道、多通道内存布局详解
  • 0904网络设备配置与管理第二次授课讲义
  • 如何用仓库路线完成一个音视频实战项目:FFmpeg + SDL 简易播放器
  • 把开发环境丢云上,我的电脑风扇再也没转过!
  • 【EasyExcel】Excel工具类2.0
  • C++ STL 中 `std::list` 双向链表容器的几个关键成员函数:`empty()`、`front()` 和 `pop_front()`
  • 【机器学习】HanLP+Weka+Java算法模型
  • 指针高级(3)
  • Redlock:为什么你的 Redis 分布式锁需要不止一个节点?
  • ​浏览器存储
  • 设计模式:中介者模式(Mediator Pattern)
  • 力扣190:颠倒二进制位
  • MySQL主从复制进阶(GTID复制,半同步复制)
  • SpringMVC —— 响应和请求处理
  • 手写 Tomcat
  • STM32启动模式配置
  • 一个开源的企业官网简介
  • RTSP H.265 与 RTMP H.265 的差异解析:标准、扩展与增强实现
  • 设备监控系统如何为重工业实现设备预测性维护
  • 【智谱清言-GLM-4.5】StackCube-v1 任务训练结果不稳定性的分析
  • uniapp中使用echarts并且支持pc端的拖动、拖拽和其他交互事件