继续PE系列笔记的更新
PE其它笔记索引可前往:
PE文件笔记一 PE介绍
前面在PE文件笔记十一 新增节学习了关于节的操作之新增节,接下来继续学习节的操作之合并节
合并节
为什么要合并节
在前面新增节中,要判断最后一个节表后面是否有空间用于新增节表,只有当最后一个节表后40个字节全为0时,才能进行新增节的操作;但当条件不满足时又想要新增节,该如何操作?
答案便是:合并节,合并节就是用一个节表描述多个节,这样省下来的节表空间就可以用于新增节了
于是合并节的目的便是:节省节表空间,这样就能实现新增节
合并节涉及的结构体成员
涉及的节表成员 |
含义 |
Name |
节名称 |
VirtualAddress |
节在内存中的偏移 (RVA) |
Misc |
节的实际大小 |
SizeOfRawData |
节在文件中对齐后的尺寸 |
PointerToRawData |
节区在文件中的偏移 |
Characteristics |
节的属性 |
涉及的标准PE头成员 |
含义 |
NumberOfSections |
节的个数 |
合并节的流程
- 修正内存对齐
- 修改第一个节的大小
- 修改第一个节的权限
- 修改节数量为1
- 清空后面的节(可选)
按流程合并节
修正内存对齐
关于修正内存对齐的内容在上一篇笔记 PE文件笔记十二 修正内存对齐中已经详细说明了
这次就以上一篇修正过内存对齐的结果:EverEdit_修正.exe直接进行合并节,在这里省略修正内存对齐的步骤;不了解如何修正内存对齐的可以回顾上一篇笔记
给出修正完内存对齐后 节的信息

Name |
Misc |
SizeOfRawData |
PointerToRawData |
Characteristics |
.text |
0x19a000 |
0x19a000 |
0x400 |
0x60000020 |
.rdata |
0x38000 |
0x38000 |
0x19a400 |
0x40000040 |
.data |
0x4b000 |
0x4b000 |
0x1d2400 |
0xc0000040 |
.rsrc |
0x63000 |
0x63000 |
0x21d400 |
0x40000040 |
.reloc |
0x17000 |
0x17000 |
0x280400 |
0x42000040 |
修改第一个节的大小
修改第一个节的大小为后面 为所有节内存对齐后的大小的和
即修改第一个节的大小为 0x19a000+0x38000+0x4b000+0x63000+0x17000=0x297000
仍然使用PE工具:DIE进行修改

修改后

修改第一个节的权限
既然要用一个节表概括所有的节,那么该节表就必须具备先前所有节表的权限
也就是第一个节的权限 = 所有节的权限 相或

得到新的权限为0xE2000060
使用PE工具:DIE修改

修改后

修改节数量为1
找到标准PE头中的NumberOfSections成员,将其修改为1

修改后

清空后面的节(可选)
其实做完上面一步就已经完成了合并节,但合并节是为了腾出节表空间,于是这里再将后面无用的节表清空掉
使用WinHex找到节表处

选中要清空的部分,编辑→填充选块(快捷键 Ctrl+L)


清空后保存即可

测试运行
程序仍然可以正常运行

并且此时再用PE工具:DIE查看节的信息,也只有一个节了

代码实现合并节
完整代码
#include <stdio.h>
#include <malloc.h>
#include <windows.h>
#include <winnt.h>
#include <math.h>
#define IMAGE_FILE_MACHINE_AMD64 0x8664
BOOL appendFile(LPCSTR filePath, PVOID writeData, DWORD sizeOfWriteData, DWORD offset, HANDLE* phMap, PVOID* ppFile) {
HANDLE hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
char newPath[100];
strcpy(newPath, filePath);
strcat(newPath, ".exe");
HANDLE hFile2 = CreateFileA(newPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);
DWORD dwWritenSize = 0;
HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);
BYTE* content = (BYTE*)pFile;
content += offset;
DWORD size = SetFilePointer(hFile, NULL, NULL, FILE_END);
BOOL bRet;
bRet = WriteFile(hFile2, pFile, offset, &dwWritenSize, NULL);
if (!bRet)return false;
SetFilePointer(hFile, NULL, NULL, FILE_END);
bRet = WriteFile(hFile2, writeData, sizeOfWriteData, &dwWritenSize, NULL);
if (!bRet)return false;
SetFilePointer(hFile, NULL, NULL, FILE_END);
bRet = WriteFile(hFile2, content, size - offset, &dwWritenSize, NULL);
if (!bRet)return false;
CloseHandle(hFile);
CloseHandle(hMap);
CloseHandle(*phMap);
UnmapViewOfFile(pFile);
UnmapViewOfFile(*ppFile);
bRet = DeleteFileA(filePath);
if (!bRet)return false;
hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);
hMap = CreateFileMappingA(hFile2, NULL, PAGE_READWRITE, 0, 0, 0);
pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);
SetFilePointer(hFile, NULL, NULL, FILE_BEGIN);
bRet = WriteFile(hFile, pFile, sizeOfWriteData + size, &dwWritenSize, NULL);
if (!bRet)return false;
CloseHandle(hFile);
CloseHandle(hFile2);
CloseHandle(hMap);
UnmapViewOfFile(pFile);
bRet = DeleteFileA(newPath);
if (!bRet)return false;
hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);
hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
*ppFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);
*phMap = hMap;
CloseHandle(hFile);
return true;
}
void GetPeStruct32(LPVOID pFile, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) {
dos = (_IMAGE_DOS_HEADER*)pFile;
DWORD* peId;
peId = (DWORD*)((UINT)dos + dos->e_lfanew);
WORD* magic;
magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));
nt = (_IMAGE_NT_HEADERS*)peId;
_IMAGE_SECTION_HEADER* sectionHeader;
sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS));
int cnt = 0;
while (cnt < nt->FileHeader.NumberOfSections) {
_IMAGE_SECTION_HEADER* section;
section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);
sectionArr[cnt++] = section;
}
}
void sectionAlignment(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, LPCSTR filePath, HANDLE* phMap,LPVOID* ppFile, int n) {
DWORD VirtualSize = sectionArr[n]->Misc.VirtualSize;
DWORD SizeOfRawData = sectionArr[n]->SizeOfRawData;
UINT SizeInMemory = (UINT)ceil((double)max(VirtualSize, SizeOfRawData) / (double)nt->OptionalHeader.SectionAlignment) * nt->OptionalHeader.SectionAlignment;
printf("%X\n", SizeInMemory);
UINT offset = SizeInMemory - sectionArr[n]->SizeOfRawData;
printf("%X\n", offset);
UINT end = sectionArr[n]->PointerToRawData + sectionArr[n]->SizeOfRawData;
printf("end:%X\n", end);
INT* content = (INT*)malloc(offset);
ZeroMemory(content, offset);
DWORD dwWritenSize = 0;
BOOL bRet=appendFile(filePath, (PVOID)content, offset, end,phMap,ppFile);
GetPeStruct32(*ppFile, dos, nt, sectionArr);
if (bRet) {
sectionArr[n]->Misc.VirtualSize = SizeInMemory;
sectionArr[n]->SizeOfRawData = SizeInMemory;
int i;
while (n + 1 <= nt->FileHeader.NumberOfSections - 1) {
n++;
sectionArr[n]->PointerToRawData += offset;
}
}
}
void combineSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) {
DWORD allSectionSize = 0;
DWORD allSectionCharacteristics = sectionArr[0]->Characteristics;
int i;
for (i = 0; i < nt->FileHeader.NumberOfSections;i++) {
allSectionSize += sectionArr[i]->SizeOfRawData;
allSectionCharacteristics = allSectionCharacteristics | sectionArr[i]->Characteristics;
}
printf("allSectionSize:%X\n", allSectionSize);
printf("allSectionCharacteristics:%X\n", allSectionCharacteristics);
sectionArr[0]->Misc.VirtualSize = allSectionSize;
sectionArr[0]->SizeOfRawData = allSectionSize;
sectionArr[0]->Characteristics = allSectionCharacteristics;
for (i = 1; i < nt->FileHeader.NumberOfSections; i++) {
ZeroMemory(sectionArr[i], sizeof(_IMAGE_SECTION_HEADER));
}
nt->FileHeader.NumberOfSections = 1;
}
int main(int argc, char* argv[])
{
_IMAGE_DOS_HEADER* dos;
HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit_修正.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);
dos = (_IMAGE_DOS_HEADER*)pFile;
printf("dos->e_magic:%X\n", dos->e_magic);
DWORD* peId;
peId = (DWORD*)((UINT)dos + dos->e_lfanew);
printf("peId:%X\n", *peId);
WORD* magic;
magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));
printf("magic:%X\n", *magic);
switch (*magic) {
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
{
printf("32位程序\n");
_IMAGE_NT_HEADERS* nt;
nt = (_IMAGE_NT_HEADERS*)peId;
printf("Machine:%X\n", nt->FileHeader.Machine);
printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader;
sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS));
int cnt = 0;
while (cnt < nt->FileHeader.NumberOfSections) {
_IMAGE_SECTION_HEADER* section;
section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);
sectionArr[cnt++] = section;
printf("%s\n", section->Name);
}
CloseHandle(hFile);
int i;
for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {
sectionAlignment(dos, nt, sectionArr, "C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit_修正.exe", &hMap, &pFile,i);
}
combineSection(dos, nt, sectionArr);
break;
}
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
{
printf("64位程序\n");
_IMAGE_NT_HEADERS64* nt;
nt = (_IMAGE_NT_HEADERS64*)peId;
printf("Machine:%X\n", nt->FileHeader.Machine);
printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader;
sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64));
int cnt = 0;
while (cnt < nt->FileHeader.NumberOfSections) {
_IMAGE_SECTION_HEADER* section;
section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);
sectionArr[cnt++] = section;
printf("%s\n", section->Name);
}
break;
}
default:
{
printf("error!\n");
break;
}
}
return 0;
}
合并节代码
void combineSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) {
DWORD allSectionSize = 0;
DWORD allSectionCharacteristics = sectionArr[0]->Characteristics;
int i;
for (i = 0; i < nt->FileHeader.NumberOfSections;i++) {
allSectionSize += sectionArr[i]->SizeOfRawData;
allSectionCharacteristics = allSectionCharacteristics | sectionArr[i]->Characteristics;
}
printf("allSectionSize:%X\n", allSectionSize);
printf("allSectionCharacteristics:%X\n", allSectionCharacteristics);
sectionArr[0]->Misc.VirtualSize = allSectionSize;
sectionArr[0]->SizeOfRawData = allSectionSize;
sectionArr[0]->Characteristics = allSectionCharacteristics;
for (i = 1; i < nt->FileHeader.NumberOfSections; i++) {
ZeroMemory(sectionArr[i], sizeof(_IMAGE_SECTION_HEADER));
}
nt->FileHeader.NumberOfSections = 1;
}
运行结果


说明
- 合并节就是用一个节表来包括多个节表的信息
- 可以看到合并节除了修正内存对齐,其余部分都十分简单
- 合并节之后多出了的节表空间可以用来新增节
附件
附上本笔记中分析的EverEdit文件:点我下载
此次附件中添加了合并完节后的exe
