由于项目须要,须要读取PE文件的代码段信息和进程内的代码段信息,用于防破解的数据准备。从安全防御的角度上看,能防一点是一点。下面分别从读取PE文件和进程空间的代码段信息来小结。windows
准备知识参见 windows PE文件结构及其加载机制,这篇文章讲解的很全面,颇有参考价值。完成此项功能的思路是,将PE文件读取到内存中,定位到.text
段在PE文件中的偏移和代码段长度,将结果写入本地文件便可。api
大体的代码流程以下:安全
// 得到PE文件代码段信息 // 思路:读取PE文件头部的代码段偏移和代码段大小 bool GetPECodeSegInfo(const char* pFilePath) { string strContent = GetFileContent(pFilePath); if (0 == strContent.length()) return false; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)strContent.c_str(); if (pDosHeader && pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return false; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pDosHeader + pDosHeader->e_lfanew); if (pNtHeaders && pNtHeaders->Signature != IMAGE_NT_SIGNATURE) return false; PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(pNtHeaders); const int nSectionCnts = pNtHeaders->FileHeader.NumberOfSections; for (int i = 0; i < nSectionCnts; i++) { if (0 == strcmp((char*)pSecHeader->Name, ".text")) { const char* pCodeEntry = (const char*)((BYTE*)pDosHeader + pSecHeader->PointerToRawData); SaveFile("CodeSegInfo.txt", pCodeEntry, pSecHeader->SizeOfRawData); return true; } pSecHeader++; } return false; }
在取偏移地址时,注意不要使用 IMAGE_NT_HEADERS32_OptionalHeader_BaseOfCode 代码段基偏移地址。参见MSDN中的解释IMAGE_OPTIONAL_HEADER32 structureless
BaseOfCode A pointer to the beginning of the code section, relative to the image base.
该字段表示代码段相对虚拟地址(RVA),它是相对于镜像载入地址的偏移。镜像载入地址是加载器加载PE文件时设置的地址。在上述实例代码中,使用的偏移地址是 PointerToRawData
,参考MSDN中的解释IMAGE_SECTION_HEADER structurethis
PointerToRawData A file pointer to the first page within the COFF file. This value must be a multiple of the FileAlignment member of the IMAGE_OPTIONAL_HEADER structure. If a section contains only uninitialized data, set this member is zero. SizeOfRawData The size of the initialized data on disk, in bytes. This value must be a multiple of the FileAlignment member of the IMAGE_OPTIONAL_HEADER structure. If this value is less than the VirtualSize member, the remainder of the section is filled with zeroes. If the section contains only uninitialized data, the member is zero.
它是对应节信息原始数据相对于PE文件首部的偏移,SizeOfRawData
是原始数据的长度,Misc.VirtualSize
是实际加载到内存中的大小,当Misc.VirtualSize
大于SizeOfRawData
时,该节剩余填充0..net
读取进程空间代码段信息,大体的思路以下:code
bool GetProcessCodeSegInfoTesx(const char* pEXENAME) { bool bRet = false; int nPid = GetProcessId(pEXENAME); MODULEENTRY32 moduleInfo = { 0 }; if (!GetProcessModule(nPid, pEXENAME, &moduleInfo, sizeof(moduleInfo))) { printf("can't find %s dll in %s", pEXENAME, pEXENAME); return false; } HANDLE hHandle = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, false, nPid); char* pBuffer = new char[moduleInfo.modBaseSize + 1]; memset(pBuffer, 0, moduleInfo.modBaseSize + 1); DWORD dwByteOfRead = 0; ReadProcessMemory(hHandle, (LPCVOID)moduleInfo.modBaseAddr, pBuffer, moduleInfo.modBaseSize, &dwByteOfRead); if (moduleInfo.modBaseSize == dwByteOfRead) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuffer; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pDosHeader + pDosHeader->e_lfanew); if (pDosHeader && pDosHeader->e_magic == IMAGE_DOS_SIGNATURE && pNtHeaders && pNtHeaders->Signature == IMAGE_NT_SIGNATURE) { DWORD dwBaseOfCode = pNtHeaders->OptionalHeader.BaseOfCode; // 代码段偏移地址 DWORD dwSizeOfCode = pNtHeaders->OptionalHeader.SizeOfCode; // 代码段大小 SaveFile("ProcessCodeInfo.txt", pBuffer + dwBaseOfCode, dwSizeOfCode); bRet = true; } } delete[]pBuffer; pBuffer = NULL; return bRet; }
读取进程空间的代码段信息须要注意,若是是获取当前进程的模块信息,不须要提权,若是是获取其余进程的模块信息,则须要提权,不然会没有权限访问。blog