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