继续PE系列笔记的更新
PE其它笔记索引可前往:
PE文件笔记一 PE介绍
在前面PE文件笔记十 扩大节中,需要修正节表成员;修正节表成员时要把Misc(实际大小)和 SizeOfRawData(文件对齐后的大小)修正为内存对齐后的大小
但在扩大节中,只针对最后一个节进行修正,没有影响到其它的成员,接下来学习将所有节表都修正为内存对齐
此篇笔记为学习节操作之修正内存对齐
修正内存对齐
为什么要修正内存对齐
修正内存对齐 使得 节文件对齐后的大小和内存对齐后的大小一致,方便后续合并节
修正内存对齐涉及的结构体成员
涉及的节表成员 |
含义 |
Misc |
节的实际大小 |
SizeOfRawData |
节在文件中对齐后的尺寸 |
PointerToRawData |
节区在文件中的偏移 |
修正内存对齐的流程
- 计算节内存对齐后的大小
- 计算差值 = 节内存对齐后的大小 - 节文件对齐后的大小
- 计算节在文件中的末尾位置 = 节在文件中的偏移 + 节文件对齐后的大小
- 在节的文件中的末尾位置后填充新空间,新空间的大小为 前面计算的差值
- 修正Misc和SizeOfRawData为节内存对齐后的大小
- 在该节后面的节在文件中的偏移增加差值
按流程修正内存对齐
此次依旧以先前的EverEdit.exe为例进行修正内存对齐的演示,这次选择修正倒数第二个节
节信息

这里只关注最后两个节:
节名称 |
Misc |
SizeOfRawData |
PointerToRawData |
.rsrc |
0x62008 |
0x62200 |
0x1dfa00 |
.reloc |
0x1643a |
0x16600 |
0x241c00 |
计算节内存对齐后的大小
计算节内存对齐后的大小在先前的笔记PE文件笔记十 扩大节中已经说明过,这里不再赘述,直接计算
节内存对齐后的大小 = ( max{Misc,SizeOfRawData} ÷ SectionAlignment)向上取整 × SectionAlignment
即节内存对齐后的大小 = ( max{0x62008,0x62200}÷0x1000)向上取整 × 0x1000
即节内存对齐后的大小 = (0x62200 ÷ 0x1000)向上取整 × 0x1000 = 0x63000
计算差值
差值 = 节内存对齐后的大小 - 节文件对齐后的大小 = 节内存对齐后的大小 - SizeOfRawData
即 差值 = 0x63000 - 0x62200 = 0xE00
计算节在文件中的末尾位置
节在文件中的末尾位置 = 节在文件中的偏移 + 节文件对齐后的大小
即节在文件中的末尾位置 = PointerToRawData + SizeOfRawData
即节在文件中的末尾位置 = 0x1dfa00 + 0x62200 = 0x241C00
填充新空间
用WinHex打开EverEdit.exe,找到前面计算出来的节在文件中的末尾位置

选中末尾后 编辑→粘贴0字节


选择插入的大小为:0xE00(对应十进制为3584),即插入前面计算出来的差值
插入后,保存

修正节成员
修正Misc和SizeOfRawData为节内存对齐后的大小:0x63000
这里为了省事,直接使用PE工具:DIE进行修改

修正后:

修正后面的节
在该节后面的节在文件中的偏移增加差值
该节后面只有一个
节名称 |
Misc |
SizeOfRawData |
PointerToRawData |
.reloc |
0x1643a |
0x16600 |
0x241c00 |
修改其PointerToRawData = PointerToRawData + 差值
即 PointerToRawData = 0x241c00 + 0xe00 = 242A00
同样使用PE工具:DIE进行修正

修改后

修正前后对比
执行完上一步,倒数第二个节就已经修正完了
对比一下修改前后节的信息
修改前
节名称 |
Misc |
SizeOfRawData |
PointerToRawData |
.rsrc |
0x62008 |
0x62200 |
0x1dfa00 |
.reloc |
0x1643a |
0x16600 |
0x241c00 |

修改后
节名称 |
Misc |
SizeOfRawData |
PointerToRawData |
.rsrc |
0x63000 |
0x63000 |
0x1dfa00 |
.reloc |
0x1643a |
0x16600 |
0x242a00 |

测试运行
发现仍然能够正常运行

代码实现修正内存对齐
#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;
}
}
}
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);
}
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;
}
运行结果



代码说明
这次的代码比较复杂,因为C语言没有提供对文件指定位置插入数据的函数;只能自己手动实现了一个appendFile
其实就是:
- 把要插入数据之前的数据写出到新文件
- 将要插入数据写入到前面写出到文件末尾
- 把要插入数据之后的数据写出到文件末尾
- 删除原本的文件
- 将新文件复制为原本的文件
- 删除旧的文件
其中要注意,删除文件的时候要将先前获得的句柄和映射,即hFile,hMap,pFile关闭
关闭hFile,hMap,pFile后,需要重新获得新的PE结构,即更新dos、nt、sectionArr
说明
- 修正内存对齐会影响后面的节,要修正后面的节在文件中的偏移
- 对文件指定位置插入数据需要自己实现
- 删除文件前要关闭相关的句柄和映射
- 这次代码实现关于PE部分其实并没有什么难点,主要麻烦在要对文件指定位置插入数据
附件
附上本笔记中分析的EverEdit文件:点我下载
此次附件中添加了 修正完内存对齐后的exe
