继续PE系列笔记的更新
PE其它笔记索引可前往:
PE文件笔记一 PE介绍
前面在PE文件笔记十 扩大节学习了关于节的操作之扩大节,接下来继续学习节的操作之新增节
新增节
为什么要新增节
新增节和扩大节一样,都是用来解决空白区不足的问题
但既然扩大节已经能够解决问题了,为什么还要新增节呢?或者说新增节比扩大节好在哪里?
在前面的扩大节中,只是扩大了节,并没有关注扩大出来的空白区的权限问题;每个节都有其对应的权限,由节.Characteristics决定
有关节的权限问题已经在PE文件笔记六 节表和节中说过,这里不再赘述
如果扩大出来的空白区希望能够被用于执行代码,那么被扩大的节就必须具备IMAGE_SCN_CNT_CODE权限(该节包含可执行代码)
如果被扩大的节不具备这个权限,还得为此将整个节的权限修改
除此之外,扩大节还会使得原本的数据和我们扩大的空白区混在一起
相比之下新增节则完全拥有自己的权限,不依附于要扩大的节的权限,可以自己指定想要的权限
扩大节和新增节的差异
扩大节:权限取决于要被扩大的节的原本权限,如果不满足权限还需要去修改;原本数据和扩大的空白区混在一起
新增节:权限由自己来指定;空间独立没有数据混杂
新增节涉及的结构体成员
涉及的节表成员 |
含义 |
Name |
节名称 |
VirtualAddress |
节在内存中的偏移 (RVA) |
Misc |
节的实际大小 |
SizeOfRawData |
节在文件中对齐后的尺寸 |
PointerToRawData |
节区在文件中的偏移 |
Characteristics |
节的属性 |
涉及的标准PE头成员 |
含义 |
NumberOfSections |
节的个数 |
涉及的扩展PE头成员 |
含义 |
SizeOfImage |
Image(PE文件)大小 |
新增节的位置
和扩大节一个道理,为了避免新增节后影响先前的节,选择在原本的最后一个节后面新增节
新增节的流程
- 判断是否有足够空间用于添加节表
- 修改标准PE头中节的数量
- 在节表中新增一个成员
- 修正SizeOfImage的大小
- 分配新空间
按流程新增节
此次依旧以先前的EverEdit.exe为例进行新增节的演示
判断是否有空间能添加节表
用WinHex打开EverEdit.exe,找到最后一个节表,判断最后一个节表后面的40个字节(节表的大小)是否全为0

可以看到最后一个节区后40个字节全为0,因此是可以添加节表的
修改标准PE头中节的数量
之前都是使用WinHex来进行修改演示的,是为了更好地学习本质;到了现在对PE文件比较熟悉的时候,就可以用PE工具:DIE工具来代替WinHex进行修改了(看起来更直观)




在节表中新增一个成员
回到先前PE 基本信息的地方,点击节来查看节表信息




确定要修改的数值
涉及的节表成员 |
值 |
要求 |
含义 |
Name |
.lyl610 |
小于等于8位 |
节名称 |
Misc.VirtualSize |
0x1000 |
要新增的节的大小 |
节的实际大小 |
VirtualAddress |
0x298000 |
上一个节.VirtualAddress+上一个节内存对齐后的大小 |
节在内存中的偏移 (RVA) |
SizeOfRawData |
0x1000 |
要新增的节的大小 |
节在文件中对齐后的尺寸 |
PointerToRawData |
0x258200 |
上一个节.PointerToRawData+上一个节.SizeOfRawData |
节区在文件中的偏移 |
Characteristics |
0xe0000000 |
该节具备的权限,这里指定为节可读、可写、可执行 |
节的属性 |
这里主要讲一下VirtualAddress和PointerToRawData的计算,其余的都是自己指定的
VirtualAddress:
VirtualAddress为该节在内存中的偏移 = 上一个节.VirtualAddress+上一个节内存对齐后的大小 = 0x281000 + 0x17000 = 0x298000
关于节内存对齐后的大小如何计算在PE文件笔记十 扩大节中已经说明,这里也就不再赘述
PointerToRawData:
PointerToRawData为该节在文件中的偏移 = 上一个节在文件中的偏移 + 上一个节文件对齐后的大小
即PointerToRawData = 上一个节.PointerToRawData + 上一个节.SizeOfRawData = 0x241c00+0x16600=0x258200
填充要修改的数值

修正SizeOfImage的大小

这里将SizeOfImage增加0x1000(与前面新增节中的Misc.VirtualSize和SizeOfRawData对应)

分配新空间
分配新空间还是得交给WinHex,步骤和扩大节中的分配新空间没什么不同o( ̄▽ ̄)ブ
使用WinHex打开EverEdit,直接拉到文件的末尾,并选中最后一个字节

然后 编辑→粘贴0字节


按"是"

选择插入的大小为:0x1000(与前面新增节中的Misc.VirtualSize和SizeOfRawData对应),对应十进制为4096
添加完成

保存测试
将文件保存后再打开,发现仍然能够正常运行

代码实现新增节
void addSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, HANDLE hFile, UINT addSize, BYTE Name[IMAGE_SIZEOF_SHORT_NAME], DWORD Characteristics) {
_IMAGE_SECTION_HEADER* newSection = (_IMAGE_SECTION_HEADER*)((UINT)§ionArr[nt->FileHeader.NumberOfSections - 1]->Name + sizeof(_IMAGE_SECTION_HEADER));
UINT* tmp = (UINT*)newSection;
int i;
BOOL flag = false;
for (i = 0; i < sizeof(_IMAGE_SECTION_HEADER) / sizeof(INT); i++) {
if (*tmp != 0) {
flag = true;
}
tmp++;
}
if (flag) {
printf("空间不足,无法新增节\n");
return;
}
for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME; i++) {
newSection->Name[i] = Name[i];
}
newSection->Misc.VirtualSize = addSize;
newSection->SizeOfRawData = addSize;
newSection->Characteristics = Characteristics;
DWORD VirtualSize = sectionArr[nt->FileHeader.NumberOfSections - 1]->Misc.VirtualSize;
DWORD SizeOfRawData = sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;
UINT SizeInMemory = (UINT)ceil((double)max(VirtualSize, SizeOfRawData) / double(nt->OptionalHeader.SectionAlignment)) * nt->OptionalHeader.SectionAlignment;
newSection->VirtualAddress = sectionArr[nt->FileHeader.NumberOfSections - 1]->VirtualAddress + SizeInMemory;
printf("newSection->VirtualAddress:%X\n", newSection->VirtualAddress);
newSection->PointerToRawData = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;
printf("newSection->PointerToRawData:%X\n", newSection->PointerToRawData);
UINT end = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;
printf("end:%X\n", end);
SetFilePointer(hFile, end, NULL, FILE_BEGIN);
INT* content = (INT*)malloc(addSize);
ZeroMemory(content, addSize);
DWORD dwWritenSize = 0;
BOOL bRet = WriteFile(hFile, content, addSize, &dwWritenSize, NULL);
if (bRet)
{
nt->FileHeader.NumberOfSections += 1;
nt->OptionalHeader.SizeOfImage += addSize;
printf("add Section success!\n");
}
else {
printf("分配新空间失败\n");
}
}
int main(int argc, char* argv[])
{
_IMAGE_DOS_HEADER* dos;
HANDLE hFile = CreateFileA("C:\\Users\\sixonezero\\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);
}
BYTE Name[IMAGE_SIZEOF_SHORT_NAME] = ".lyl610";
addSection(dos, nt, sectionArr, hFile, 0x1000, Name, 0xe0000000);
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;
}
运行结果



可以看到代码执行以后,结果和前面手动操作一致,并且程序仍然能够正常运行( •̀ ω •́ )✧
总结
- 新增节的好处就是可以自己指定想要的权限,代码更加具有独立性
- 无论是新增节还是扩大节都要注意文件对齐和内存对齐;分配的新空间大小最好为内存对齐的整数倍
附件
附上本笔记中分析的EverEdit文件:点我下载