PE新增节
原理
新增节的原理是在最后一个节表后面新增一个节表,然后在ImageBuffer后面新增一块内存;要注意新增节表后必须留有一个节表空间(40字节)的0,否则会出错。有些程序如notepad,最后一个节表后面是一些数据,删除后程序无法运行,对于这种情况必须特殊处理,比如扩大节后再新增节,或者把NT头往上挪,覆盖掉dos stub垃圾数据,当然,方法是多样的。
除了上述工作,还需要修改PE头中节的数量(加1),修改SizeOfImage的大小,增加的值应该是要新增的节的大小内存对齐后的结果。另外,不要忘记设置新增节的属性。
测试了几个32位程序,有的能正常工作,有的不行。出错的程序有个共同点是他们最后一个节表后面并不是0,而是绑定导入表,以32位notepad为例,最后一个节表后面的数据是这样的:
为了解决这个问题,我添加了移动NT头的代码,也就是刚才说的,把NT头移动到DOS STUB中的方式。
移动NT头+新增节的代码
// 移动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; }完整代码
https://blog.csdn.net/Kwansy/article/details/106234264
总结
- 上一篇: 向代码节添加代码编程实现
- 下一篇: PE扩大最后一个节