关于PE文件新区段的创建

时间:2021-02-08 11:44:22

   对于为已有的EXE文件加壳,一般我们会创建一个新的区段,在该区段中写入的是外壳代码。

  新加一个区段,通常会有三个步骤

  1.首先修改PE文件头部信息,NT头部的区段数目,修改OPTIONAL头部的镜像大小

  2.向内存中申请一个IMAGE_SECTION_HEADER的内存模型,并修改其各个变量(区块的偏移位置和大小,文件相对虚拟地址和大小)

  3.写入文件

  现在说下IMAGE_SECTION_HEADER的几个重要变量

   VirtualSize指的是实际的,被使用的大小是区块未对齐之前的大小

   VirtualAddress指的是区块内容按照内存页对齐后的地址,即RVA

   SizeOfRawData指的是该区段按照文件对齐值对齐后的大小

   PointerToRawData指的是该块在磁盘文件中的偏移位置,和内存无关

void CPEPackerDlg::EditHeader()
{
 DWORD dwNumOfSections; //区块个数
 DWORD dwFileAlign; //文件粒度(文件对齐大小)
 DWORD dwSectionAlign; //块粒度(内存对齐大小)
 DWORD dwAlignLastSection; //最后一个区段按内存对齐后的大小
 LPVOID lpShellSecTab;  //壳区段表指针
 IMAGE_SECTION_HEADER SectionHeaderOfShell; //壳代码段的区块表信息
 
 ///////以下为修改区段表信息功能,主要是增加壳区段表////////////////////////
 //初始化区段表结构
 memset(&SectionHeaderOfShell,0,sizeof(SectionHeaderOfShell));
 //获取对齐大小数据
 dwFileAlign=pOptionalHeader->FileAlignment;
 dwSectionAlign=pOptionalHeader->SectionAlignment;
 //获取区块表个数
 dwNumOfSections=pNtHeader->FileHeader.NumberOfSections;
 //设定壳的区块表信息
 SectionHeaderOfShell.Misc.PhysicalAddress=SizeOfShell; //原始大小
 //在文件中对齐后的大小,除以文件粒度,如果余数为零,就直接使用;否则,就扩充对齐。
 SectionHeaderOfShell.SizeOfRawData=(SizeOfShell%dwFileAlign)?(dwFileAlign*(SizeOfShell/dwFileAlign+1)):SizeOfShell; 
 //区块特征
 SectionHeaderOfShell.Characteristics=IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE;
 memcpy(&SectionHeaderOfShell.Name,".bugsky",7);  //区块名称
 SectionHeaderOfShell.PointerToRelocations=0; //重定位偏移
 SectionHeaderOfShell.NumberOfRelocations=0;  //重定位表数目
 SectionHeaderOfShell.PointerToLinenumbers=0; //行号表偏移
 SectionHeaderOfShell.NumberOfLinenumbers=0;  //行号表中行号数目
 
 
 //对齐最后一个区段后的块大小,来计算壳区段的虚拟地址重新计算dwAlignLastSection(最后一个区段的大小)。
 if ((pSectionHeader+dwNumOfSections-1)->SizeOfRawData % dwSectionAlign)
 {
  dwAlignLastSection=dwSectionAlign*((pSectionHeader+dwNumOfSections-1)->SizeOfRawData / dwSectionAlign+1);
 }
 else
 {
  dwAlignLastSection=(pSectionHeader+dwNumOfSections-1)->SizeOfRawData;
 }
 //获取最后一个区段的相对虚拟地址和虚拟大小
 SectionHeaderOfShell.VirtualAddress=(pSectionHeader+dwNumOfSections-1)->VirtualAddress+dwAlignLastSection;
 SectionHeaderOfShell.PointerToRawData=(pSectionHeader+dwNumOfSections-1)->PointerToRawData+(pSectionHeader+dwNumOfSections-1)->SizeOfRawData;
 
 //计算壳区段表在PE头中的位置
 lpShellSecTab=(LPVOID)((DWORD)pSectionHeader+sizeof(IMAGE_SECTION_HEADER)*dwNumOfSections);
 //将壳区段信息拷贝到文件头中
 //此方法并不严密,因为没有考虑到PE文件头中是否还有多余的空间,为简化,暂如此操作。
 memcpy(lpShellSecTab,&SectionHeaderOfShell,sizeof(SectionHeaderOfShell));
 
 //////////区段表修改完毕,下面修改PE头/////////////////////////////////////////////
 //区段表个数加1。
 pNtHeader->FileHeader.NumberOfSections++;
 //文件镜像增加
 pOptionalHeader->SizeOfImage=SizeOfImage+((SizeOfShell % dwSectionAlign)?(dwSectionAlign*(SizeOfShell/dwSectionAlign+1)):SizeOfShell);
 
}