有时候为了某些原因,需要在PE末尾添加节,比如加壳,补丁等等,需要对PE文件进行扩展。
在末尾添加节是最简单的方式,只需要做如下修改:
- 修改FILE_HEADER中的节数目字段
- 修改OPTIONAL_HEADER中映像大小字段
- 在节描述符数组中添加新描述符
- 在文件末尾添加新节数据
首先找到文件最后一个节并计算出PE范围内的文件结尾,这里的末尾是指PE描述范围内的文件结尾,即最后一个节的结束:
//找到文件末尾的节,计算出文件结束位置
PIMAGE_SECTION_HEADER last_sec_hdr;
get_section_entry_by_index(ctx, 0, &last_sec_hdr);
for (int i = 1;i < file_hdr->NumberOfSections;i++)
{
PIMAGE_SECTION_HEADER sec_hdr;
get_section_entry_by_index(ctx, i, &sec_hdr);
if (sec_hdr->PointerToRawData > last_sec_hdr->PointerToRawData)
last_sec_hdr = sec_hdr;
}
DWORD file_end = ALIGN_PAGE((last_sec_hdr->PointerToRawData + last_sec_hdr->SizeOfRawData), FileAlignment);
然后去初始化新节描述符:
//创建新节描述并初始化
IMAGE_SECTION_HEADER sec_hdr;
memset(&sec_hdr, 0, sizeof(IMAGE_SECTION_HEADER));
sec_hdr.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
sec_hdr.SizeOfRawData = size;
sec_hdr.PointerToRawData = file_end;
sec_hdr.Misc.VirtualSize = size;
sec_hdr.VirtualAddress = ALIGN_PAGE((last_sec_hdr->VirtualAddress + last_sec_hdr->Misc.VirtualSize), SectionAlignment);
memcpy(sec_hdr.Name, name, 8);
在某些PE文件中,可能有些PE描述范围外的数据,需要先将这些数据备份出来:
//备份pe文件未定义的文件数据
DWORD useless_size = GetFileSize(ctx->file, &ret) - file_end;
PUCHAR useless_buf;
if (useless_size > 0)
{
useless_buf = (PUCHAR)malloc(useless_size);
if (useless_buf == NULL)
return FALSE;
ReadFile(ctx->file, useless_buf, useless_size, &ret, NULL);
if (ret != useless_size)
return FALSE;
}
用0去在文件末尾填充新节:
//在文件中填充新节
DWORD need_size = ALIGN_PAGE(size, FileAlignment);
PVOID new_sec = malloc(need_size);
sec_hdr.SizeOfRawData = need_size;
SetFilePointer(ctx->file, file_end, 0, FILE_BEGIN);
WriteFile(ctx->file, new_sec, need_size, &ret, NULL);
free(new_sec);
if (ret != need_size)
return FALSE;
再将刚刚备份的PE描述范围外的数据拷回去:
//将pe文件定义外的数据拷会
if (useless_size)
{
SetFilePointer(ctx->file, 0, 0, FILE_END);
WriteFile(ctx->file, useless_buf, useless_size, &ret, NULL);
}
最后将PE头的信息进行修正,由于这里用的内存映射文件,所以只要内存操作就能映射到文件中:
//将新节描述写到最后一个节描述符的后面
get_section_entry_by_index(ctx, file_hdr->NumberOfSections - 1, &last_sec_hdr);
memcpy(last_sec_hdr + 1, &sec_hdr, sizeof(IMAGE_SECTION_HEADER));
//修正文件头信息
if (ctx->is_x64)
{
PIMAGE_OPTIONAL_HEADER64 opt_hdr;
get_optional_header64(ctx, &opt_hdr);
opt_hdr->SizeOfImage+= ALIGN_PAGE(size, SectionAlignment);
}
else
{
PIMAGE_OPTIONAL_HEADER32 opt_hdr;
get_optional_header32(ctx, &opt_hdr);
opt_hdr->SizeOfImage += ALIGN_PAGE(size, SectionAlignment);
}
file_hdr->NumberOfSections++;
其中用到对齐操作,即将地址对齐到页表边界:
#define ALIGN_PAGE(_addr_in_page_,_page_align_) \
(((_addr_in_page_)+(_page_align_-1))&~((_page_align_-1))) \