对于为已有的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);

}