-
-
[原创][15Pb培训第三阶段课后小项目]PE解析工具
-
发表于: 2013-12-3 16:30 5422
-
【原创】[15Pb培训第三阶段课后小项目]PE解析工具
本工具只能简单的现实PE文件的解析,尚未添加编辑功能,处于未完成阶段.
如文中本文中有错误或者本人理解错误以及不透彻的地方,希望大神们指点一二,在此不胜感激.
下面先贴上工具主界面图片:
一. PE头部
.DOS头
PE文件总体可分为DOS部首,PE文件头,节表,以及节内容四大部分.是以”MZ”(5A4Dh)开始的,用一个64字节大小的IMAGE_DOS_HEADER_STRUCT结构来表示DOS文件头.跟着DOS部首的就是PE文件头,在DOS文件头的e_lfanew字段(也就是文件偏移0x3C)保存着PE文件头的相对文件偏移.
.PE文件头
struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
PE文件头以一个大小为0xF8h的IMAGE_NT_HEADERS结构体表示,结构第一个字段Signature是该文件头的标志,它总是0x4550h(“PE”),可以根据该字段与DOS文件头的e_magic字段来判断一个文件是否是PE文件.第二个字段是一个大小为0x14h字节大小的IMAGE_FILE_HEADER结构体.
struct _IMAGE_FILE_HEADER {
WORD Machine; // 运行平台
WORD NumberOfSections; // 文件节的数目
DWORD TimeDateStamp; // 文件创建时间
DWORD PointerToSymbolTable; // 指向符号表(调试使用)
DWORD NumberOfSymbols; // 符号表中符号个数(调试使用)
WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32结构大小
WORD Characteristics;
}IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
第三字段是一个大小为0xE0h的IMAGE_OPTIONAL_HEADER32的结构体.
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; // 标志
BYTE MajorLinkerVersion; // 链接器主版本号
BYTE MinorLinkerVersion; // 链接器次版本号
DWORD SizeOfCode; // 所有代码区段的总大小
DWORD SizeOfInitializedData; // 所有初始化数据区段的总大小
DWORD SizeOfUninitializedData; // 所有未初始化数据区段的总大小
DWORD AddressOfEntryPoint; // 程序执行入口RVA
DWORD BaseOfCode; // 代码区段起始RVA
DWORD BaseOfData; // 数据区段起始RVA
DWORD ImageBase; // 程序默认装载基址
DWORD SectionAlignment; // 内存中区段的对齐值(一般为1000)
DWORD FileAlignment; // 文件中区段的对齐值(一般为200)
WORD MajorOperatingSystemVersion; // 操作系统主版本号
WORD MinorOperatingSystemVersion; // 操作系统次版本号
WORD MajorImageVersion; // 用户自定义主版本号
WORD MinorImageVersion; // 用户自定义次版本号
WORD MajorSubsystemVersion; // 所需要子系统主版本号
WORD MinorSubsystemVersion; // 所需要子系统次版本号
DWORD Win32VersionValue; // 保留,一般为0
DWORD SizeOfImage; // 映像装入内存后的总尺寸
DWORD SizeOfHeaders; // 所有头部的总大小
DWORD CheckSum; // 映像校验和
WORD Subsystem; // 文件子系统
WORD DllCharacteristics; // 显示DLL特性的标志
DWORD SizeOfStackReserve; // 初始化堆栈大小
DWORD SizeOfStackCommit; // 初始化实际堆栈大小
DWORD SizeOfHeapReserve; // 初始化保留堆栈大小
DWORD SizeOfHeapCommit; // 初始化实际保留堆栈大小
DWORD LoaderFlags; // 与调试相关,默认为0
DWORD NumberOfRvaAndSizes; // 数据目录表的项数
// 数据目录数组
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
IMAGE_DATA_DIRECTORY结构体定义如下:
struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 指向的RVA
DWORD Size; // 数据的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
实现逻辑:由于本工具用的是MFC实现的,实现起来非常简单.
1. 给每个文本框定义一个cstring类型的变量.
2. 直接把需要显示的成员格式化到对应的变量.
3. 最后用UpdateData(FALSE)更新到界面就OK.
下面放上实现的主要代码:
VOID CMFC_PEditor_Dlg::ShowPE_Info(PVOID lpImage) { if (nullptr == lpImage) { MessageBox(_T("文件地址为空"),_T("错误")); PostMessage(WM_CLOSE,NULL,NULL); return; } PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS32 pNT32 = (PIMAGE_NT_HEADERS32)((LONG)lpImage+pDos->e_lfanew); PIMAGE_FILE_HEADER pFileHead = &pNT32->FileHeader; // 把文件信息显示到窗口对应的控件上 m_strMachine.Format(_T("%04X"),pFileHead->Machine); m_strNumberOfSections.Format(_T("%04X"),pFileHead->NumberOfSections); m_strTimeDateStamp.Format(_T("%p"),pFileHead->TimeDateStamp); m_strPointerToSymbolTable.Format(_T("%p"),pFileHead->PointerToSymbolTable); m_strNumberOfSymbols.Format(_T("%p"),pFileHead->NumberOfSymbols); m_strSizeOfOptionalHeader.Format(_T("%04X"),pFileHead->SizeOfOptionalHeader); m_strCharacteristics.Format(_T("%p"),pFileHead->Characteristics); // 显示扩展头部信息 PIMAGE_OPTIONAL_HEADER32 pOptionalHead = &pNT32->OptionalHeader; m_strBaseOfCode.Format(_T("%p"),pOptionalHead->BaseOfCode); m_strBaseOfData.Format(_T("%p"),pOptionalHead->BaseOfData); m_strSizeOfImage.Format(_T("%p"),pOptionalHead->SizeOfImage); m_strSizeOfHeader.Format(_T("%p"),pOptionalHead->SizeOfHeaders); m_strSectionAlignment.Format(_T("%p"),pOptionalHead->SectionAlignment); m_strFileAlignment.Format(_T("%p"),pOptionalHead->FileAlignment); m_strSubSystem.Format(_T("%p"),pOptionalHead->Subsystem); // 文件信息 m_strAddrOfEntryPoint.Format(_T("%p"),pOptionalHead->AddressOfEntryPoint); m_strImageBase.Format(_T("%p"),pOptionalHead->ImageBase); UpdateData(FALSE); }
m_lisSection.SetExtendedStyle(LVS_EX_FULLROWSELECT ); TCHAR strTitle[6][10] = {_T("节名"),_T("虚拟大小"),_T("虚拟偏移"), _T("原始大小"),_T("原始偏移"),_T("特征值")}; for (int i = 0; i < 6; i++) { m_lisSection.InsertColumn(i,strTitle[i],0,80); } 2. VOID CSection::ShowSection(CONST PVOID lpImage) { if (nullptr == lpImage) { return; } PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS32 pNT32 = (PIMAGE_NT_HEADERS32)((LONG)lpImage+pDos->e_lfanew); PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNT32);// 取得第一个节表所在位置 DWORD dwIndex = 0; // 子项的索引值(可视为引导) LVITEM lvItem = {0}; lvItem.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM|LVIF_STATE; for (int i = 0; i < pNT32->FileHeader.NumberOfSections; i++) { WCHAR wTemp[30]; lvItem.iItem = dwIndex; MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,(char*)pSection->Name,8,wTemp,30); int n = GetLastError(); lvItem.pszText = wTemp; m_lisSection.InsertItem(&lvItem); CString strbuff; strbuff.Format(_T("%p"),pSection->Misc); m_lisSection.SetItemText(dwIndex,1,strbuff); strbuff.Format(_T("%p"),pSection->VirtualAddress); m_lisSection.SetItemText(dwIndex,2,strbuff); strbuff.Format(_T("%p"),pSection->SizeOfRawData); m_lisSection.SetItemText(dwIndex,3,strbuff); strbuff.Format(_T("%p"),pSection->PointerToRawData); m_lisSection.SetItemText(dwIndex,4,strbuff); strbuff.Format(_T("%p"),pSection->Characteristics); m_lisSection.SetItemText(dwIndex,5,strbuff); dwIndex++; pSection++; }
VOID CDirectory::ShowDirectory() { if (nullptr == m_lpFileImage) { MessageBox(_T("文件地址为空"),_T("错误")); PostMessage(WM_CLOSE,NULL,NULL); return ; } PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_lpFileImage; PIMAGE_NT_HEADERS32 pNT32 = (PIMAGE_NT_HEADERS32)((LONG)m_lpFileImage+pDos->e_lfanew); // pDir指向目录表 PIMAGE_DATA_DIRECTORY pDir = pNT32->OptionalHeader.DataDirectory; // 输出目录 m_strExport_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strExport_Size.Format(_T("%p"),pDir->Size); pDir++; // 输入目录 m_strImport_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strImport_Size.Format(_T("%p"),pDir->Size); pDir++; // 资源目录 m_strResources_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strReception_Size.Format(_T("%p"),pDir->Size); pDir++; // 异常目录 m_strException_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strException_Size.Format(_T("%p"),pDir->Size); pDir++; // 安全目录 m_strSecurity_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strSecurity_Size.Format(_T("%p"),pDir->Size); pDir++; // 重定位目录 m_strBase_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strBase_Size.Format(_T("%p"),pDir->Size); pDir++; // 调试目录 m_strDebug_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strDebug_Size.Format(_T("%p"),pDir->Size); pDir++; // 版权目录 m_strCopyright_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strCopyright_Size.Format(_T("%p"),pDir->Size); pDir++; // 全局指针目录 m_strGlobal_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strGlobal_Size.Format(_T("%p"),pDir->Size); pDir++; // TLS目录 m_strTLS_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strTLS_Size.Format(_T("%p"),pDir->Size); pDir++; // 载入配置目录 m_strLoadConfiguration_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strLoadConfiguration_Size.Format(_T("%p"),pDir->Size); pDir++; // 绑定输入目录 m_strBoundImport_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strBoundImport_Size.Format(_T("%p"),pDir->Size); pDir++; // IAT目录 m_strIAD_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strIAT_Size.Format(_T("%p"),pDir->Size); pDir++; // 延迟导入目录 m_strDelayImport_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strDelayImport_Size.Format(_T("%p"),pDir->Size); pDir++; // COM目录 m_strCOM_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strCOM_Size.Format(_T("%p"),pDir->Size); pDir++; // 保留 m_strTemp_RVA.Format(_T("%p"),pDir->VirtualAddress); m_strTemp_Size.Format(_T("%p"),pDir->Size); UpdateData(FALSE); }
VOID CExport::ShowExport(CONST LPVOID lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS32 pNT32 = (PIMAGE_NT_HEADERS32)((LONG)lpImage+pDos->e_lfanew); // pExportdir指向数据目录表中的输出表目录 PIMAGE_DATA_DIRECTORY pExportDir = pNT32->OptionalHeader.DataDirectory; if (0 == pExportDir->VirtualAddress) { MessageBox(_T("此文件不存在输出表"),_T("提示")); PostMessage(WM_CLOSE,NULL,NULL); return; } // 获得输出表的Offset; DWORD dwExportOfffset = m_pRVAToOffset->RVAToOffset(lpImage,pExportDir->VirtualAddress); // pExport指向输出表 PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)lpImage+dwExportOfffset); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 输出表信息更新到窗口 m_strOffset.Format(_T("%p"),dwExportOfffset); // 输出表偏移 m_strCharacteristics.Format(_T("%p"),pExport->Characteristics); // 特征值 m_strBase.Format(_T("%p"),pExport->Base); // 索引基数 m_strName.Format(_T("%p"),pExport->Name); // 模块名RVA m_strNumberOfFun.Format(_T("%p"),pExport->NumberOfFunctions); // 导出表成员总数 m_strNumberOfName.Format(_T("%p"),pExport->NumberOfNames); // 以函数名导出的个数 m_strAddrFun.Format(_T("%p"),pExport->AddressOfFunctions); // 导出函数地址表 m_strAddrFunName.Format(_T("%p"),pExport->AddressOfNames); // 函数名称地址表 m_strAddrNum.Format(_T("%p"),pExport->AddressOfNameOrdinals); // 导出序列号数组 // 把模块名RVA转换成Offset再加上文件所在地址,得到模块名的Offset PCHAR pstrName = (PCHAR)lpImage + m_pRVAToOffset->RVAToOffset(lpImage,pExport->Name); m_strSzName = pstrName; // 模块名称字符串 UpdateData(FALSE); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// PDWORD pEAT = (PDWORD)((DWORD) // 导出表RVA转换成Offset再加上文件地址等于导出地址表 lpImage + m_pRVAToOffset->RVAToOffset(lpImage,pExport->AddressOfFunctions)); PDWORD pENT = (PDWORD)((DWORD) // 函数名称RVA转换成Offset再加上文件地址等于函数名称地址表 lpImage + m_pRVAToOffset->RVAToOffset(lpImage,pExport->AddressOfNames)); PWORD pEIT = (PWORD)((DWORD) // 函数序号RVA转换成Offset再加上文件地址等于函数序号地址表 lpImage + m_pRVAToOffset->RVAToOffset(lpImage,pExport->AddressOfNameOrdinals)); LVITEM lvItem = {0}; lvItem.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM|LVIF_STATE; CString strbuff; DWORD dwOffset = 0; int j = 0; // 遍历整个导出表中的导出函数地址 for (DWORD i=0; i<pExport->NumberOfFunctions; i++) { // 如果该地址为nullptr,则遍历下一个地址 if (!pEAT[i]) { continue; } // 遍历以名称导出的函数 for (DWORD dwIndex = 0; dwIndex<pExport->NumberOfFunctions; dwIndex++) { // 比较序号是否有函数序号与其对应 if (pEIT[dwIndex] == i) { // 如果序号数组中的序号与函数名的序号相符 lvItem.iItem = j; TCHAR szName[8] = {0}; wsprintf(szName,_T("%04X"),i + pExport->Base); lvItem.pszText = szName; m_lisExport.InsertItem(&lvItem); // 插入序号 strbuff.Format(_T("%p"),pEAT[j]); m_lisExport.SetItemText(j,1,strbuff); // 插入RVA // 把函数RVA转换成Offset dwOffset = m_pRVAToOffset->RVAToOffset(lpImage,pEAT[j]); strbuff.Format(_T("%p"),dwOffset); m_lisExport.SetItemText(j,2,strbuff); // 插入偏移 pstrName = (PCHAR)lpImage + m_pRVAToOffset->RVAToOffset(lpImage,pENT[dwIndex]); USES_CONVERSION; strbuff = A2W(pstrName); m_lisExport.SetItemText(j,3,strbuff); // 插入函数名 j++; break; } // 如果遍历完整个序号数组,都没找到与函数相符的序号 else if ( dwIndex == pExport->NumberOfFunctions-1 ) { // 该序号没有对应的函数名, lvItem.iItem = j; TCHAR szName[8] = {0}; wsprintf(szName,_T("%04X"),i + pExport->Base); lvItem.pszText = szName; m_lisExport.InsertItem(&lvItem); // 插入序号 strbuff.Format(_T("%p"),pEAT[j]); m_lisExport.SetItemText(j,1,strbuff); // 插入RVA // 把函数RVA转换成Offset dwOffset = m_pRVAToOffset->RVAToOffset(lpImage,pEAT[j]); strbuff.Format(_T("%p"),dwOffset); m_lisExport.SetItemText(j,2,strbuff); // 插入偏移 strbuff.Format(_T("%s"),_T("-")); // 用"-"表示无对应的函数名 m_lisExport.SetItemText(j,3,strbuff); // 插入函数名 j++; } } } }
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创][15Pb培训第三阶段课后小项目]PE解析工具 5423
- [新手]CVE-2010-3951漏洞利用例子 5297
- [原创]新人入手第一个游戏外挂,附上详细制作过程 170847
- [求助]为何不能在新人投稿版块发帖子了 4613
- [求助]修改代码后运行出错误 4389