壳的编写(3)-- 获取被加壳文件的PE信息
我们将获取PE信息的操作单独封装到一个类文件中,在这个文件中,我们需要定义一个结构_PE_INFO用于封装PE相关信息并供外部调用,除此之外我们还需要定义RVA转文件偏移,文件偏移转,获取PE文件的信息,修复重定位信息,获取导出全局变量的文件偏移,设置新OEP,添加区段等函数。
在Pack_Dll中添加一个CProcessingPE类,在ProcessingPE.h文件中内容如下:
#pragma once #include <Windows.h> #include <string.h> #include <stdlib.h> // 关键PE信息 typedef struct _PE_INFO { DWORD dwOEP; // 入口点 DWORD dwImageBase; // 映像基址 PIMAGE_DATA_DIRECTORY pDataDir; // 数据目录指针 IMAGE_DATA_DIRECTORY stcExport; // 导出目录 PIMAGE_SECTION_HEADER pSectionHeader; // 区段表头部指针 }PE_INFO, *PPE_INFO; class CProcessingPE { public: CProcessingPE(void); ~CProcessingPE(void); public: DWORD RVAToOffset(ULONG uRvaAddr); // RVA转文件偏移 DWORD OffsetToRVA(ULONG uOffsetAddr); // 文件偏移转RVA BOOL GetPeInfo(LPVOID lpImageData, DWORD dwImageSize, PPE_INFO pPeInfo); // 获取PE文件的信息 void FixReloc(DWORD dwLoadImageAddr); // 修复重定位信息 PVOID GetExpVarAddr(LPCTSTR strVarName); // 获取导出全局变量的文件偏移 void SetOEP(DWORD dwOEP); // 设置新OEP PVOID AddSection(LPCTSTR strName, DWORD dwSize, DWORD dwChara, PIMAGE_SECTION_HEADER pNewSection, PDWORD lpSize); // 添加区段 void SetDLL(); // 去除DLL动态加载标识 private: DWORD m_dwFileDataAddr; // 目标文件所在缓存区的地址 DWORD m_dwFileDataSize; // 目标文件大小 PIMAGE_DOS_HEADER m_pDos_Header; // DOS头指针 PIMAGE_NT_HEADERS m_pNt_Header; // NT头指针 PE_INFO m_stcPeInfo; // PE关键信息 };
ProcessingPE.cpp 实现如下:
#include "stdafx.h" #include "ProcessingPE.h" CProcessingPE::CProcessingPE(void) { ZeroMemory(&m_stcPeInfo, sizeof(PE_INFO)); } CProcessingPE::~CProcessingPE(void) { } /************************************************************************/ /* 方法名称: RVAToOffset /* 方法全称: CProcessingPE::RVAToOffset /* 参数: ULONG uRvaAddr RVA地址值 /* 返回值: DWORD 成功返回Offset,失败则返回0 /* 说明: 相对虚拟地址(RVA)转文件偏移(Offset) /************************************************************************/ DWORD CProcessingPE::RVAToOffset(ULONG uRvaAddr) { //获取区段头表 PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header); //获取区段的数量 --- nt表中的文件头中 DWORD dwSize = m_pNt_Header->FileHeader.NumberOfSections; for (DWORD i = 0; i < dwSize; i++) { if ((pSectionHeader[i].VirtualAddress <= uRvaAddr) && ((pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize) > uRvaAddr)) { return ( uRvaAddr - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData ); } } return 0; } /************************************************************************/ /* 方法名称: OffsetToRVA /* 方法全称: CProcessingPE::OffsetToRVA /* 参数: ULONG uOffsetAddr Offset地址值 /* 返回值: DWORD 成功返回RVA地址,失败则返回0 /* 说明: 文件偏移(Offset)转相对虚拟地址(RVA) /************************************************************************/ DWORD CProcessingPE::OffsetToRVA(ULONG uOffsetAddr) { //获取区段头表 PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header); //获取区段的数量 --- nt表中的文件头中 DWORD dwSize = m_pNt_Header->FileHeader.NumberOfSections; for (DWORD i = 0; i < dwSize; i++) { if ((pSectionHeader[i].PointerToRawData <= uOffsetAddr) && (pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData > uOffsetAddr)) { return (uOffsetAddr - pSectionHeader[i].PointerToRawData + pSectionHeader[i].VirtualAddress ); } } return 0; } /************************************************************************/ /* 方法名称: GetPeInfo /* 方法全称: CProcessingPE::GetPeInfo /* 参数: LPVOID lpImageData 目标文件所在缓存区的指针 /* 参数: DWORD dwImageSize 目标文件的大小 /* 参数: PPE_INFO pPeInfo [OUT]用于传出目标文件的关键PE信息 /* 返回值: BOOL 成功返回true,失败则返回false /* 说明: 获取PE文件信息 /************************************************************************/ BOOL CProcessingPE::GetPeInfo(LPVOID lpImageData, DWORD dwImageSize, PPE_INFO pPeInfo) { // 1、判断映像指针是否有效 if (m_stcPeInfo.dwOEP) { CopyMemory(pPeInfo, &m_stcPeInfo, sizeof(PE_INFO)); return true; } if (!lpImageData) { return false; } // 2、将要获取PE的文件的地址和大小保存起来 m_dwFileDataAddr = (DWORD)lpImageData; m_dwFileDataSize = dwImageSize; // 3. 获取基本信息 // 3.1 获取DOS头、NT头 m_pDos_Header = (PIMAGE_DOS_HEADER)lpImageData; // 检查PE文件是否有效 if (m_pDos_Header->e_magic != IMAGE_DOS_SIGNATURE) { return false; } m_pNt_Header = (PIMAGE_NT_HEADERS)((DWORD)lpImageData + m_pDos_Header->e_lfanew); // 检查PE文件是否有效 if ( m_pNt_Header->Signature != IMAGE_NT_SIGNATURE) { return false; } // 3.2 获取OEP m_stcPeInfo.dwOEP = m_pNt_Header->OptionalHeader.AddressOfEntryPoint; // 3.3 获取映像基址 m_stcPeInfo.dwImageBase = m_pNt_Header->OptionalHeader.ImageBase; // 3.4 获取关键数据目录表的内容 PIMAGE_DATA_DIRECTORY lpDataDir = m_pNt_Header->OptionalHeader.DataDirectory; m_stcPeInfo.pDataDir = lpDataDir; CopyMemory(&m_stcPeInfo.stcExport, lpDataDir + IMAGE_DIRECTORY_ENTRY_EXPORT, sizeof(IMAGE_DATA_DIRECTORY)); // 3.5 获取区段表与其他详细信息 m_stcPeInfo.pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header); // 4. 传出处理结果 CopyMemory(pPeInfo, &m_stcPeInfo, sizeof(PE_INFO)); return true; } /************************************************************************/ /* 方法名称: FixReloc /* 方法全称: CProcessingPE::FixReloc /* 参数: DWORD dwLoadImageAddr 此映像被加载后的预计模块基址 /* 返回值: void /* 说明: 修复重定位项 此函数依赖于RVAToOffset函数。 /* 注意: /* 1. dwLoadImageAddr指的并非是其本身ImageBase的值,而是其被加载后的预 /* 计模块基址。 /* 2. 此重定位函数并未考虑到修复类型问题,如果要提高兼容性,应该分别对 /* 三种重定位类型进行区别对待。 /************************************************************************/ void CProcessingPE::FixReloc(DWORD dwLoadImageAddr) { // 1. 获取映像基址与代码段指针 DWORD dwImageBase; PVOID lpCode; dwImageBase = m_pNt_Header->OptionalHeader.ImageBase; lpCode = (PVOID)(m_dwFileDataAddr + RVAToOffset(m_pNt_Header->OptionalHeader.BaseOfCode)); // 2. 获取重定位表在内存中的地址 PIMAGE_DATA_DIRECTORY pDataDir; PIMAGE_BASE_RELOCATION pReloc; pDataDir =&( m_pNt_Header->OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_BASERELOC]); pReloc = (PIMAGE_BASE_RELOCATION)(m_dwFileDataAddr + RVAToOffset(pDataDir->VirtualAddress)); // 3. 遍历重定位表,并对目标代码进行重定位 while (pReloc->SizeOfBlock ) { // 3.1 取得重定位项TypeOffset与其数量 PWORD pTypeOffset = (PWORD)((DWORD)pReloc + sizeof(IMAGE_BASE_RELOCATION)); DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); // 3.2 循环检查重定位项 for (DWORD i = 0; i < dwCount; i++) { if (!*pTypeOffset) continue; // 3.2.1 获取此重定位项指向的指针 DWORD dwPointToRVA = (*pTypeOffset & 0x0FFF) + pReloc->VirtualAddress; PDWORD pPtr = (PDWORD)(m_dwFileDataAddr + RVAToOffset(dwPointToRVA) ); // 3.2.2 计算重定位增量值 DWORD dwIncrement = dwLoadImageAddr - dwImageBase; // 3.2.3 修复需重定位的地址数据 *((PDWORD)pPtr) += dwIncrement; pTypeOffset++; } // 3.3 指向下一个重定位块,开始另一次循环 pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock); } } /************************************************************************/ /* 方法名称: GetExpVarAddr /* 方法全称: CProcessingPE::GetExpVarAddr /* 参数: LPCTSTR strVarName /* 返回值: PVOID /* 说明: 获取导出全局变量的文件偏移 /************************************************************************/ PVOID CProcessingPE::GetExpVarAddr(LPCTSTR strVarName) { // 1、获取导出表地址,并将参数strVarName转为ASCII形式,方便对比查找 CHAR szVarName[MAX_PATH] = { 0 }; PIMAGE_EXPORT_DIRECTORY lpExport = (PIMAGE_EXPORT_DIRECTORY)(m_dwFileDataAddr + RVAToOffset(m_stcPeInfo.stcExport.VirtualAddress)); WideCharToMultiByte(CP_ACP, NULL, strVarName, -1, szVarName, _countof(szVarName), NULL, FALSE); // 2、循环读取导出表输出项的输出函数,并依次与szVarName做比对,如果相同,则取出相对应的函数地址 for (DWORD i = 0; i < lpExport->NumberOfNames; i++) { PDWORD pNameAddr = (PDWORD)(m_dwFileDataAddr + RVAToOffset((DWORD)lpExport->AddressOfNames + 4 * i)); PCHAR strTempName = (PCHAR)(m_dwFileDataAddr + RVAToOffset(*pNameAddr)); if (!strcmp(szVarName, strTempName)) { PDWORD pFunAddr = (PDWORD)(m_dwFileDataAddr + RVAToOffset((DWORD)lpExport->AddressOfFunctions + 4 * i)); return (PVOID)(m_dwFileDataAddr + RVAToOffset(*pFunAddr)); } } return 0; } /************************************************************************/ /* 方法名称: AddSection /* 方法全称: CProcessingPE::AddSection /* 参数: LPCTSTR strName 新区段的名称 /* 参数: DWORD dwSize 新区段的最小体积 /* 参数: DWORD dwChara 新区段的属性 /* 参数: PIMAGE_SECTION_HEADER pNewSection [OUT]新区段的段结构指针 /* 参数: PDWORD lpSize [OUT]新区段的最终大小 /* 返回值: PVOID 成功返回指向新区段现在所在内存的指针 /* 说明: 添加区段函数 /* 注: /* 此函数并未考虑到目标函数存在附加数据等细节问题。 /************************************************************************/ PVOID CProcessingPE::AddSection(LPCTSTR strName, DWORD dwSize, DWORD dwChara, PIMAGE_SECTION_HEADER pNewSection, PDWORD lpSize) { PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header); // 1. 获取基本信息 DWORD dwDosSize = m_pDos_Header->e_lfanew; DWORD dwPeSize = sizeof(IMAGE_NT_HEADERS32); DWORD dwStnSize = m_pNt_Header->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); DWORD dwHeadSize = dwDosSize + dwPeSize + dwStnSize; // 2. 在区段表中加入新区段的信息 // 2.1 获取基本信息 CHAR szVarName[7] = { 0 }; DWORD dwFileAlign = m_pNt_Header->OptionalHeader.FileAlignment; // 文件粒度 DWORD dwSectAlign = m_pNt_Header->OptionalHeader.SectionAlignment; // 区段粒度 WORD dwNumOfsect = m_pNt_Header->FileHeader.NumberOfSections; // 区段数目 // 2.2 获取最后一个区段的信息 IMAGE_SECTION_HEADER stcLastSect = { 0 }; CopyMemory(&stcLastSect, &pSectionHeader[dwNumOfsect - 1], sizeof(IMAGE_SECTION_HEADER)); // 2.3 根据区段粒度计算相应地址信息 DWORD dwVStart = 0; // 虚拟地址起始位置 DWORD dwFStart = stcLastSect.SizeOfRawData + stcLastSect.PointerToRawData; // 文件地址起始位置 if (stcLastSect.Misc.VirtualSize%dwSectAlign) dwVStart = (stcLastSect.Misc.VirtualSize / dwSectAlign + 1) * dwSectAlign + stcLastSect.VirtualAddress; else dwVStart = (stcLastSect.Misc.VirtualSize / dwSectAlign) * dwSectAlign + stcLastSect.VirtualAddress; DWORD dwVirtualSize = 0; // 区段虚拟大小 DWORD dwSizeOfRawData = 0; // 区段文件大小 if (dwSize%dwSectAlign) dwVirtualSize = (dwSize / dwSectAlign + 1) * dwSectAlign; else dwVirtualSize = (dwSize / dwSectAlign) * dwSectAlign; if (dwSize%dwFileAlign) dwSizeOfRawData = (dwSize / dwFileAlign + 1) * dwFileAlign; else dwSizeOfRawData = (dwSize / dwFileAlign) * dwFileAlign; WideCharToMultiByte(CP_ACP, NULL, strName, -1, szVarName, _countof(szVarName), NULL, FALSE); // 2.4 组装一个新的区段头 IMAGE_SECTION_HEADER stcNewSect = { 0 }; CopyMemory(stcNewSect.Name, szVarName, 7); // 区段名称 stcNewSect.Misc.VirtualSize = dwVirtualSize; // 虚拟大小 stcNewSect.VirtualAddress = dwVStart; // 虚拟地址 stcNewSect.SizeOfRawData = dwSizeOfRawData; // 文件大小 stcNewSect.PointerToRawData = dwFStart; // 文件地址 stcNewSect.Characteristics = dwChara; // 区段属性 // 2.5 写入指定位置 CopyMemory((PVOID)(m_dwFileDataAddr + dwHeadSize), &stcNewSect, sizeof(IMAGE_SECTION_HEADER)); // 3. 修改区段数目字段NumberOfSections m_pNt_Header->FileHeader.NumberOfSections++; // 4. 修改PE文件的景象尺寸字段SizeOfImage m_pNt_Header->OptionalHeader.SizeOfImage += dwVirtualSize; // 5. 返回新区段的详细信息、大小,以及可直接访问的地址 CopyMemory(pNewSection, &stcNewSect, sizeof(IMAGE_SECTION_HEADER)); *lpSize = dwSizeOfRawData; return (PVOID)(m_dwFileDataAddr + dwFStart); } /************************************************************************/ /* 方法名称: SetOEP /* 方法全称: CProcessingPE::SetOEP /* 参数: DWORD dwOEP 新OEP /* 返回值: void /* 说明: 修改目标文件OEP /************************************************************************/ void CProcessingPE::SetOEP(DWORD dwOEP) { m_pNt_Header->OptionalHeader.AddressOfEntryPoint = dwOEP; } /************************************************************************/ /* 方法名称: SetDLL /* 方法全称: CProcessingPE::SetDLL /* 返回值: void /* 说明: 去除DLL动态加载标识 /************************************************************************/ void CProcessingPE::SetDLL() { m_pNt_Header->OptionalHeader.DllCharacteristics = 0x0; }
————————————————
版权声明:本文为CSDN博主「布衣僧」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/oBuYiSeng/article/details/50478832