向代码节添加代码编程实现
生活随笔
收集整理的这篇文章主要介绍了
向代码节添加代码编程实现
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
原理
分析PE结构,找到第一个节,假设它是代码节(后面的实验都是基于这个前提),获取其内存偏移 VirtualAddress,计算它最后一条指令的偏移的下一个字节,作为代码插入点。插入调用MessageBoxA的硬编码,和jmp到原入口点的代码。跳转地址计算公式是:X = 要跳转的地址 - 下一条指令的地址。要注意,公式中涉及的“地址”指的都是运行时的地址,即通过 [ImageBase + 偏移] 得到。
修改程序入口点,使运行时先执行我插入的代码,然后跳转到原来的入口点。
代码使用vc6开发,如果要迁移到其他环境如vs,则需要注意字符串参数的类型,要做相应的调整。另外,只适用于32位程序。
向代码节添加代码的函数
// 向代码节添加MessageBox代码 // 向代码节添加代码不需要担心内存对齐后大小发生变化 // 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节 BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile) {BYTE shellcode[] ={0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 00xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBoxA0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP};DWORD dwShellCodeSize = 18;DWORD dwCodeRva = 0; // 插入的位置RVALPVOID pFileBuffer = NULL;LPVOID pImageBuffer = NULL;LPVOID pNewBuffer = NULL;DWORD dwFileBufferSize = 0;DWORD dwImageBufferSize = 0;DWORD dwNewBufferSize = 0;// 读取PE到内存中if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0){printf("读取失败\n");return FALSE;} // 拉伸成内存映像 dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer); if (0 == dwImageBufferSize) {free(pFileBuffer);return FALSE; } PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionHeader = \ (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwCodeSecIndex = -1; // 遍历节表,找到代码节 for (int i = 0; i < pPEHeader->NumberOfSections; i++) {if ((pSectionHeader[i].Characteristics & 0x60000020) == 0x60000020){dwCodeSecIndex = i;break;} } if (dwCodeSecIndex == -1) {printf("找不到代码节\n");free(pFileBuffer);free(pImageBuffer);return FALSE; }// 计算插入点RVA dwCodeRva = pSectionHeader[dwCodeSecIndex].VirtualAddress + pSectionHeader[dwCodeSecIndex].Misc.VirtualSize; // 是否有足够的空间插入代码,要考虑到代码节是最后一个节的情况 if (dwCodeSecIndex + 1 == pPEHeader->NumberOfSections) {if (dwCodeRva + dwShellCodeSize > pOptionHeader->SizeOfImage){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;} } else {DWORD dwUnuseSize = pSectionHeader[dwCodeSecIndex + 1].VirtualAddress - \pSectionHeader[dwCodeSecIndex].VirtualAddress - pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;if (dwUnuseSize < dwShellCodeSize){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;} }// 代码插入点偏移 = VA + VSIZE dwCodeRva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize; // 代码插入 memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva), shellcode, dwShellCodeSize); // 修正地址 DWORD MsgBoxAddr = (DWORD)&MessageBoxA; // 获取MessageBox的地址 DWORD hardCodeAddr = MsgBoxAddr - (pOptionHeader->ImageBase + dwCodeRva + 13); memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 9), &hardCodeAddr, 4); hardCodeAddr = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint \ - (pOptionHeader->ImageBase + dwCodeRva + 18); memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 14), &hardCodeAddr, 4); // 修改入口点 pOptionHeader->AddressOfEntryPoint = dwCodeRva; // 转成文件对齐 dwNewBufferSize = CopyImageBufferToFileBuffer(pImageBuffer, &pNewBuffer); if (dwNewBufferSize != dwFileBufferSize) {printf("可能丢失数据\n"); } MemoryToFile(pNewBuffer, dwNewBufferSize, lpszOutFile); free(pFileBuffer); free(pImageBuffer); free(pNewBuffer); printf("插入代码成功\n"); return TRUE; }完整代码
#include "headers.h"// 读取PE文件到内存中,返回读取的字节数;读取失败返回0 DWORD ReadPEFile(LPCSTR lpszFile, LPVOID *pFileBuffer) {FILE *pFile = NULL;DWORD dwFileSize = 0;pFile = fopen(lpszFile, "rb");if (pFile == NULL) {printf("打开文件失败\n");return 0;}fseek(pFile, 0, SEEK_END);dwFileSize = ftell(pFile);fseek(pFile, 0, SEEK_SET);*pFileBuffer = malloc(dwFileSize);if (*pFileBuffer == NULL){printf("分配内存失败\n");fclose(pFile);return 0;} DWORD dwRead = fread(*pFileBuffer, 1, dwFileSize, pFile);fclose(pFile);if (dwRead != dwFileSize){printf("文件大小 = %d\t实际写入内存字节 = %d\t写入失败\n", dwFileSize, dwRead);return 0;}if (!IsPEFile(*pFileBuffer, dwRead)){printf("不是有效的PE文件\n");return 0;}return dwRead; }// 验证是否PE文件 BOOL IsPEFile(LPVOID pFileBuffer, DWORD dwSize) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE){printf("不是有效的MZ标志\n");return FALSE;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标记\n");return FALSE;}return TRUE; }// 打印PE头信息 VOID PrintNTHeaders(LPCSTR lpszFile) {LPVOID pFileBuffer = NULL;DWORD dwFileSize = ReadPEFile(lpszFile, &pFileBuffer);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);if (dwFileSize == 0){printf("读取文件失败\n");return;}//打印DOS头puts("----DOS HEADER----");printf("e_magic = %x\n", pDosHeader->e_magic);printf("e_lfanew = %x\n", pDosHeader->e_lfanew); //打印NT头 printf("----标准PE头----\n");printf("Machine = %x\n", pPEHeader->Machine);printf("NumberOfSections = %x\n", pPEHeader->NumberOfSections);printf("TimeDateStamp = %x\n", pPEHeader->TimeDateStamp);printf("PointerToSymbolTable = %x\n", pPEHeader->PointerToSymbolTable);printf("NumberOfSymbols = %x\n", pPEHeader->NumberOfSymbols);printf("SizeOfOptionalHeader = %x\n", pPEHeader->SizeOfOptionalHeader);printf("Characteristics = %x\n", pPEHeader->Characteristics);//可选PE头 printf("----可选PE头----\n");printf("Magic = %x\n", pOptionHeader->Magic);printf("MajorLinkerVersion = %x\n", pOptionHeader->MajorLinkerVersion);printf("MinorLinkerVersion = %x\n", pOptionHeader->MinorLinkerVersion);printf("SizeOfCode = %x\n", pOptionHeader->SizeOfCode);printf("SizeOfInitializedData = %x\n", pOptionHeader->SizeOfInitializedData);printf("SizeOfUninitializedData = %x\n", pOptionHeader->SizeOfUninitializedData);printf("AddressOfEntryPoint = %x\n", pOptionHeader->AddressOfEntryPoint);printf("BaseOfCode = %x\n", pOptionHeader->BaseOfCode);printf("BaseOfData = %x\n", pOptionHeader->BaseOfData);printf("ImageBase = %x\n", pOptionHeader->ImageBase);printf("SectionAlignment = %x\n", pOptionHeader->SectionAlignment);printf("FileAlignment = %x\n", pOptionHeader->FileAlignment);printf("MajorOperatingSystemVersion = %x\n", pOptionHeader->MajorOperatingSystemVersion);printf("MinorOperatingSystemVersion = %x\n", pOptionHeader->MinorOperatingSystemVersion);printf("MajorImageVersion = %x\n", pOptionHeader->MajorImageVersion);printf("MinorImageVersion = %x\n", pOptionHeader->MinorImageVersion);printf("MajorSubsystemVersion = %x\n", pOptionHeader->MajorSubsystemVersion);printf("MinorSubsystemVersion = %x\n", pOptionHeader->MinorSubsystemVersion);printf("Win32VersionValue = %x\n", pOptionHeader->Win32VersionValue);printf("SizeOfImage = %x\n", pOptionHeader->SizeOfImage);printf("SizeOfHeaders = %x\n", pOptionHeader->SizeOfHeaders);printf("CheckSum = %x\n", pOptionHeader->CheckSum);printf("Subsystem = %x\n", pOptionHeader->Subsystem);printf("DllCharacteristics = %x\n", pOptionHeader->DllCharacteristics);printf("SizeOfStackReserve = %x\n", pOptionHeader->SizeOfStackReserve);printf("SizeOfStackCommit = %x\n", pOptionHeader->SizeOfStackCommit);printf("SizeOfHeapReserve = %x\n", pOptionHeader->SizeOfHeapReserve);printf("SizeOfHeapCommit = %x\n", pOptionHeader->SizeOfHeapCommit);printf("LoaderFlags = %x\n", pOptionHeader->LoaderFlags);printf("NumberOfRvaAndSizes = %x\n", pOptionHeader->NumberOfRvaAndSizes);//打印节表 char sectionName[9];for (int i = 0; i < pPEHeader->NumberOfSections; i++){memset(sectionName, 0, 9);memcpy(sectionName, pSectionHeader->Name, 8);printf("----节表: [%s]----\n", sectionName);printf("VirtualSize = %x\n", pSectionHeader->Misc.VirtualSize);printf("VirtualAddress = %x\n", pSectionHeader->VirtualAddress);printf("SizeOfRawData = %x\n", pSectionHeader->SizeOfRawData);printf("PointerToRawData = %x\n", pSectionHeader->PointerToRawData);printf("Characteristics = %x\n", pSectionHeader->Characteristics);pSectionHeader++;}//释放内存free(pFileBuffer); }// 将 FileBuffer 拉伸成 ImageBuffer 并写入到新的缓冲区 // 返回 ImageBuffer 的大小;失败返回0 DWORD CopyFileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);*pImageBuffer = malloc(pOptionHeader->SizeOfImage);if (*pImageBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pImageBuffer, 0, pOptionHeader->SizeOfImage);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制所有节 for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pImageBuffer) + pSectionHeader[i].VirtualAddress), \(LPVOID)((DWORD)pFileBuffer + pSectionHeader[i].PointerToRawData), \pSectionHeader[i].SizeOfRawData);}return pOptionHeader->SizeOfImage; }// 将 ImageBuffer 变成文件对齐的 FileBuffer 写入新的缓冲区 // 返回复制的大小,失败返回0 DWORD CopyImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pNewBuffer) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// 最后一个节表PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;// 计算要复制的字节// 这一步有BUG,当最后一个节后面还有数据时(多见于控制台程序),丢失数据DWORD dwFileBufferSize = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;*pNewBuffer = malloc(dwFileBufferSize); if (*pNewBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pNewBuffer, 0, dwFileBufferSize);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制文件对齐后的节 for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pNewBuffer) + pSectionHeader[i].PointerToRawData), \(LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), \pSectionHeader[i].SizeOfRawData);}return dwFileBufferSize; }// 内存数据写入文件 BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile) {FILE *fp = NULL;fp = fopen(lpszFile, "wb+");if (fp == NULL){printf("打开文件失败\n");return FALSE;}DWORD dwWritten = fwrite(pMemBuffer, 1, dwSize, fp);if (dwWritten != dwSize){printf("写入了 %d 字节,不等于 %d\n", dwWritten, dwSize);fclose(fp);return FALSE;}fclose(fp);return TRUE; }// 向代码节添加MessageBox代码 // 向代码节添加代码不需要担心内存对齐后大小发生变化 // 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节 BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile) {BYTE shellcode[] ={0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 00xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBoxA0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP};DWORD dwShellCodeSize = 18;DWORD dwCodeRva = 0; // 插入的位置RVALPVOID pFileBuffer = NULL;LPVOID pImageBuffer = NULL;LPVOID pNewBuffer = NULL;DWORD dwFileBufferSize = 0;DWORD dwImageBufferSize = 0;DWORD dwNewBufferSize = 0;// 读取PE到内存中if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0){printf("读取失败\n");return FALSE;} // 拉伸成内存映像 dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer); if (0 == dwImageBufferSize) {free(pFileBuffer);return FALSE; } PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionHeader = \ (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwCodeSecIndex = -1; // 遍历节表,找到代码节 for (int i = 0; i < pPEHeader->NumberOfSections; i++) {if ((pSectionHeader[i].Characteristics & 0x60000020) == 0x60000020){dwCodeSecIndex = i;break;} } if (dwCodeSecIndex == -1) {printf("找不到代码节\n");free(pFileBuffer);free(pImageBuffer);return FALSE; }// 计算插入点RVA dwCodeRva = pSectionHeader[dwCodeSecIndex].VirtualAddress + pSectionHeader[dwCodeSecIndex].Misc.VirtualSize; // 是否有足够的空间插入代码,要考虑到代码节是最后一个节的情况 if (dwCodeSecIndex + 1 == pPEHeader->NumberOfSections) {if (dwCodeRva + dwShellCodeSize > pOptionHeader->SizeOfImage){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;} } else {DWORD dwUnuseSize = pSectionHeader[dwCodeSecIndex + 1].VirtualAddress - \pSectionHeader[dwCodeSecIndex].VirtualAddress - pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;if (dwUnuseSize < dwShellCodeSize){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;} }// 代码插入点偏移 = VA + VSIZE dwCodeRva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize; // 代码插入 memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva), shellcode, dwShellCodeSize); // 修正地址 DWORD MsgBoxAddr = (DWORD)&MessageBoxA; // 获取MessageBox的地址 DWORD hardCodeAddr = MsgBoxAddr - (pOptionHeader->ImageBase + dwCodeRva + 13); memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 9), &hardCodeAddr, 4); hardCodeAddr = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint \ - (pOptionHeader->ImageBase + dwCodeRva + 18); memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 14), &hardCodeAddr, 4); // 修改入口点 pOptionHeader->AddressOfEntryPoint = dwCodeRva; // 转成文件对齐 dwNewBufferSize = CopyImageBufferToFileBuffer(pImageBuffer, &pNewBuffer); if (dwNewBufferSize != dwFileBufferSize) {printf("可能丢失数据\n"); } MemoryToFile(pNewBuffer, dwNewBufferSize, lpszOutFile); free(pFileBuffer); free(pImageBuffer); free(pNewBuffer); printf("插入代码成功\n"); return TRUE; }// 计算对齐的函数,如偏移为900,对齐为1000h,返回1000h DWORD Align(DWORD dwOffset, DWORD dwAlign) {// 如果偏移小于对齐,向上取整if (dwOffset <= dwAlign) return dwAlign;// 如果偏移大于对齐且不能除尽,向上取整if (dwOffset % dwAlign){return (dwOffset / dwAlign + 1) * dwAlign;}// 如果能除尽,直接返回offsetreturn dwOffset; }// RVA 转 FOA DWORD RvaToFoa(LPVOID pFileBuffer, DWORD dwRva) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pFileBuffer + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// RVA在文件头中或者文件对齐==内存对齐时,RVA==FOA 错!第一句是对的,第二句是错的if (dwRva < pOptionHeader->SizeOfHeaders){return dwRva;}// 遍历节表,确定偏移属于哪一个节 for (int i = 0; i < pPEHeader->NumberOfSections; i++){if (dwRva >= pSectionHeader[i].VirtualAddress && \dwRva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize){int offset = dwRva - pSectionHeader[i].VirtualAddress;return pSectionHeader[i].PointerToRawData + offset;}}printf("找不到RVA %x 对应的 FOA,转换失败\n", dwRva);return 0; }// 移动NT头和节表到DOS STUB,该函数在新增节时节表空间不足的情况下调用;返回地址减小值 DWORD MoveNTHeaderAndSectionHeadersToDosStub(LPVOID pFileBuffer) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);LPVOID pDst = (LPVOID)((DWORD)pDosHeader + sizeof(IMAGE_DOS_HEADER)); // NT头插入点DWORD dwRet = (DWORD)pNTHeader - (DWORD)pDst; // 返回地址减小的值DWORD dwSize = 4 + sizeof(IMAGE_FILE_HEADER) + pPEHeader->SizeOfOptionalHeader +\sizeof(IMAGE_SECTION_HEADER) * pPEHeader->NumberOfSections; // 移动的字节数LPVOID pSrc = malloc(dwSize);if (pSrc == NULL){printf("分配内存失败\n");return 0;}memcpy(pSrc, (LPVOID)pNTHeader, dwSize); // 保存要复制的数据memset((LPVOID)pNTHeader, 0, dwSize); // 清空原数据memcpy(pDst, pSrc, dwSize); // 移动数据free(pSrc);pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER); // 更新 e_lfanewreturn dwRet; }// 新增一个大小为 newSectionSize 的代码节 // dwFileBufferSize 是原来的文件大小 // 返回新缓冲区的大小,失败返回0 DWORD AddSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize) {// 复制一份 pFileBuffer,不要修改原来的数据LPVOID pFileBuffer2 = malloc(dwFileBufferSize);memcpy(pFileBuffer2, pFileBuffer, dwFileBufferSize);pFileBuffer = pFileBuffer2;PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PWORD pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点DWORD newFileBufferSize = 0; // 新文件的大小// 判断最后一个节表后面是否有空闲的80字节if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader){printf("没有足够的80字节插入新节表\n");free(pFileBuffer2);return 0;}// 判断空闲的80字节是否全为0,如果不是,则把整个NT头往上挪覆盖dos stub以空出空间插入节表for (int i = 0; i < 80; i++){if (((PBYTE)pNewSectionHeader)[i] != 0){ DWORD dwRet = MoveNTHeaderAndSectionHeadersToDosStub(pFileBuffer);printf("节表空间不足,NT头和节表向低地址移动了 %d 字节\n", dwRet);if (dwRet < 80){printf("移动后仍没有足够的80字节空间插入新节表\n");free(pFileBuffer2);return 0;}// 更新指针pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点break;}}// 定义一个 IMAGE_SECTION_HEADER 结构,计算里面的属性IMAGE_SECTION_HEADER newSectionHeader;memcpy(newSectionHeader.Name, ".newsec", 8);newSectionHeader.Misc.VirtualSize = Align(dwNewSectionSize, pOptionHeader->SectionAlignment);newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + \Align(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment); newSectionHeader.SizeOfRawData = Align(dwNewSectionSize, pOptionHeader->FileAlignment);newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;newSectionHeader.PointerToRelocations = 0;newSectionHeader.PointerToLinenumbers = 0;newSectionHeader.NumberOfRelocations = 0;newSectionHeader.NumberOfLinenumbers = 0;newSectionHeader.Characteristics = 0x60000020;// pNewFileBuffer 分配内存,把 pFileBuffer 复制过去,后面的修改都在 pNewFileBuffer 进行*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData); // 新增节数据清0// 更新指针,指向新内存 pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections);pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1;pNewSectionHeader = pSectionHeader + *pNumberOfSections;// 节的数量+1,SizeOfImage是内存中拉伸后的大小*pNumberOfSections += 1; pOptionHeader->SizeOfImage += Align(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);// 拷贝 newSectionHeadermemcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));printf("插入成功\n");free(pFileBuffer2);return dwFileBufferSize + newSectionHeader.SizeOfRawData; }// 扩大最后一个节 // 返回新文件的大小,失败返回0 DWORD ExpandLastSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwOldSize, DWORD dwExpandSize) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwVirtualSizeExpand = Align(dwExpandSize, pOptionHeader->SectionAlignment);DWORD dwRawDataExpand = Align(dwExpandSize, pOptionHeader->FileAlignment);*pNewFileBuffer = malloc(dwOldSize + dwRawDataExpand);memcpy(*pNewFileBuffer, pFileBuffer, dwOldSize);memset((LPVOID)((DWORD)(*pNewFileBuffer) + dwOldSize), 0, dwRawDataExpand);pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (DWORD)*pNewFileBuffer);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pNTHeader + 0x18);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// 修改新内存的属性pSectionHeader[pPEHeader->NumberOfSections - 1].Misc.VirtualSize += dwVirtualSizeExpand;pSectionHeader[pPEHeader->NumberOfSections - 1].SizeOfRawData += dwRawDataExpand;pOptionHeader->SizeOfImage += dwVirtualSizeExpand;return dwOldSize + dwRawDataExpand; }// 合并所有节 BOOL MergeSection(LPVOID pImageBuffer, LPVOID *pNewImageBuffer, DWORD dwImageSize) {*pNewImageBuffer = malloc(dwImageSize);if (*pNewImageBuffer == NULL){printf("分配内存失败\n");return FALSE;}memcpy(*pNewImageBuffer, pImageBuffer, dwImageSize);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)*pNewImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;// 修改第一个节的范围以覆盖其他所有节pSectionHeader->SizeOfRawData = pSectionHeader->Misc.VirtualSize = \pOptionHeader->SizeOfImage - pSectionHeader->VirtualAddress;pSectionHeader->SizeOfRawData = Align(pSectionHeader->SizeOfRawData, pOptionHeader->FileAlignment);// 属性包含所有节的属性for (int i = 1; i < pPEHeader->NumberOfSections; i++){pSectionHeader[0].Characteristics |= pSectionHeader[i].Characteristics;}// 清空其他节表的数据,这步是为了合并节后新增节方便memset(pSectionHeader + 1, 0, sizeof(IMAGE_SECTION_HEADER) * (pPEHeader->NumberOfSections - 1));// 节的数量 = 1pPEHeader->NumberOfSections = 1;return TRUE; }// 打印数据目录 DWORD TestPrintDataDirectory(LPCSTR lpszFile) {LPVOID pFileBuffer = NULL;ReadPEFile(lpszFile, &pFileBuffer);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);LPCSTR data[16] ={"导出表","导入表","资源表","异常信息表","安全证书表","重定位表","调试信息表","版权表","全局指针表","TLS表","加载配置表","绑定导入表","IAT表","延迟导入表","COM信息表","保留"};for (int i = 0; i < 16; i++){puts(data[i]);printf("VirtualAddress = %x\n", (pOptionHeader->DataDirectory)[i].VirtualAddress);printf("Size = %x\n", (pOptionHeader->DataDirectory)[i].Size);puts("-------------------------");}free(pFileBuffer);return 0; }// 打印导出表 VOID PrintExportTable(LPVOID pFileBuffer) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));printf("Base = %x\n", pExportDirectory->Base);printf("NumberOfFunctions = %x\n", pExportDirectory->NumberOfFunctions);printf("NumberOfNames = %x\n", pExportDirectory->NumberOfNames);printf("----AddressOfFunctions----\n");PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));for (int i = 0; i < (int)pExportDirectory->NumberOfFunctions; i++){printf("AddressOfFunctions[%d] = %x\n", i, AddressOfFunctions[i]);// 测试:调用导出序号为12的Plus函数,VC6可以这样调用,VS不行,要VirtualAlloc一块可执行内存//if (i + pExportDirectory->Base == 12)//{// int (*Plus)(int x, int y) = (int (*)(int x, int y))((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfFunctions[i]));// printf("2+3=%d\n", Plus(2, 3));//}}printf("----AddressOfNames & AddressOfNameOridinals----\n");PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));for (int i = 0; i < (int)pExportDirectory->NumberOfNames; i++){printf("AddressOfNames[%d] = %s, AddressOfOrdinals[%d] = %d\n",i, (char *)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfNames[i])), i, AddressOfNameOridinals[i]);} }// 根据函数名返回函数入口点FOA DWORD GetFunctionAddrByName(LPVOID pFileBuffer, LPCSTR lpszFunc) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));for (int i = 0; i < (int)pExportDirectory->NumberOfNames; i++){if (!strcmp((char *)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfNames[i])), lpszFunc)){return AddressOfFunctions[AddressOfNameOridinals[i]];}}return NULL; }// 根据导出序号返回函数入口点FOA DWORD GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD dwOrdinal) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));return AddressOfFunctions[dwOrdinal - pExportDirectory->Base]; }// 打印重定位表 VOID PrintRelocationTable(LPVOID pFileBuffer) {PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_BASE_RELOCATION pBaseRelocation = \(PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[5].VirtualAddress));while (pBaseRelocation->VirtualAddress || pBaseRelocation->SizeOfBlock){puts("-------------------------------------");printf("VirtualAddress = %08x\n", pBaseRelocation->VirtualAddress);printf("SizeOfBlock = %08x\n", pBaseRelocation->SizeOfBlock);PWORD pwAddr = (PWORD)((DWORD)pBaseRelocation + 8);int n = (pBaseRelocation->SizeOfBlock - 8) / 2;printf("要修改的地址个数 = %d\n", n);for (int i = 0; i < n; i++){WORD wProp = (0xF000 & pwAddr[i]) >> 12;WORD wAddr = 0x0FFF & pwAddr[i];printf("[%d]:RVA = %08x\t属性 = %d\n", i + 1, pBaseRelocation->VirtualAddress + wAddr, wProp);}pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);} }总结
以上是生活随笔为你收集整理的向代码节添加代码编程实现的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: RvaToFileOffset 内存偏移
- 下一篇: PE新增节