加段用在很多场合,比如在加壳、反杀中给PE文件加段。添加分区区域有4个步骤。第一步是在节表的末尾添加一个IMAGE_SECTION_HEADER。第二步是更新IMAGE_FILE_HEADER中的NumberOfSections字段。第三步是更新IMAGE_OPTIONAL_HEADER中的SizeOfImage字段。最后一步是添加文件的数据。当然前三步不分先后,但是最后一步一定要清楚怎么改。在某些情况下,添加新的节项后,会在新节项的数据部分添加一些代码,而这些代码可能需要在程序执行前执行,这时也需要更新AddressOfEntryPointIMAGE_OPTIONAL_HEADER中的字段。1.手动添加一个section首先,让我们手动添加一个section。这个过程就是熟悉上面几个步骤的过程。在Internet上有许多现成的用于添加部分的工具。这里写工具的目的是为了掌握和理解它的实现方法,锻炼编程能力;手动添加section是为了巩固所学的知识,熟悉添加section的步骤。使用C32Asm以16进制编辑方式打开测试程序,定位到其节表,如图1所示。图1节表位置信息从图1可以看出,PE文件有3个节表。直接查看十六进制信息可能不方便。为了直观方便的查看节表中IMAGE_SECTION_HEADER的信息,使用LordPE查看,如图2所示。图2使用LordPE查看节表信息使用LordPE工具确实直观多了看法。根据LordPE显示的节表信息添加节。IMAGE_SECTION_HEADER结构体定义如下:typedefstruct_IMAGE_SECTION_HEADER{BYTEName[IMAGE_SIZEOF_SHORT_NAME];union{DWORDPhysicalAddress;DWORDVirtualSize;}Misc;DWORDVirtualAddress;DWORDSizeOfRawData;DWORDPointerToRawData;DWORDPointerToRelocations;DWORDPointerToLinenumbers;WORDNumberOfRelocations;WORDNumberOfLinenumbers;DWORDCharacteristics;}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;IMAGE_SECTION_HEADER结构体的成员很多,但实际用到的只有6个,分别是Name、VirtualSize、VirtualAddress、SizeOfRawData、PointerToRawData和Characteristics。这6个项目刚好和LordPE展示的6个项目是一样的。事实上,IMAGE_SECTION_HEADER结构的其余成员几乎没有用到。下面是如何添加这些。IMAGE_SECTION_HEADER的长度为40字节,十六进制为0x28,在C32Asm中占2行半内容。这里,手动一次添加这两行半的内容。回到C32Asm,在最后一个段表的位置开始添加内容,先把光标放在右边的ASCII字符处,输入“.test”,如图3所示。图3添加“.test”sectionname然后在00000240的位置加上sectionsize,也就是对齐后的大小。由于文件对齐是0x1000字节,也就是4096字节,所以可以用最小值使值0x1000。在C32Asm中添加时,正确的添加应该是“00100000”,后面添加的时候要注意字节顺序。添加后面几个成员时,将不再提示你注意字节顺序,这一点你要时刻注意。添加此值时,光标应位于十六进制编辑所在的位置,而不是ASCII字符所在的位置。顺便说一下,还应该添加VirutalAddress。VirtualAddress的值为上一节的起始位置加上上一节对齐长度的值。上一节起始位置为0x6000,上一节对齐。长度为0x3000,所以新区段的起始位置为0x9000。添加VirtualSize和VirtualAddress后如图4所示。图4添加VirtualSize和VirtualAddress的值接下来的两个字段分别是SizeOfRawData和PointerToRawData。添加方法与前两个字段类似,这里不再赘述。分别添加“0x9000”和“0x1000”两个值,如图5所示。图5添加SizeOfRawData和PointerToRawDataPointerToRawData后面的12个字节可以为0,只需修改最后4个字节的内容即可,即特征值。这个值可以直接使用上一个section的值,需要根据要添加的section的属性来添加。这里为了省事,直接使用了之前section的属性,如图6所示。图6添加Characteristics属性整个sectiontable需要添加的地方添加,然后是section的个数需要修改PE文件。当前节数为3,这里需要改为4。虽然可以通过LordPE等修改工具来完成,但是这里还是采用手动修改的方式。修改位置请自行定位查找。修改如图7所示。图7修改sections个数为4除了sections个数,文件图片的大小也必须修改,即SizeOfImage的值。由于增加了一个新的section,所以section的大小应该加上SizeOfImage的大小,也就是新的SizeOfImage的大小。当前SizeOfImage的大小为0x9000,加上新添加的section的大小为0xa000。请自行找到SizeOfImage所在位置修改,如图8所示。图8将SizeOfImage的值修改为0xa000,修改PE结构域的内容。最后一步是添加真实数据。由于该段区域没有被使用,所以填0值即可,文件起始位置为0x9000,长度为0x1000。将光标移至文件末尾,单击“编辑”→“插入数据”命令,在“插入数据大小”文本框中输入十进制4096,十六进制为0x1000,如图9所示。图9“插入数据”对话框的设置点击“确定”按钮,可以看到刚才光标处插入了很多0值,工作完成。点击“保存”按钮保存,提示是否备份,选择“是”。然后使用LordPE查看添加节点区域的情况,如图10。图10添加新的section信息,对比前后两个文件的大小,如图11。图11文件大小添加节前后从图11可以看出,添加节后的文件比原文件大了4KB,这是由于增加了4096字节的0值所致。或许大家最关心的不是尺寸问题,而是软件加尺寸后是否真的能跑起来。事实上,尝试运行它,它可以工作。上面的整个过程就是手动添加一个新section的整个过程。除了一些独特的步骤外,您应该注意新节的内存起始位置和文件起始位置的值。通过上面的手动添加section相信大家已经很熟悉了。下面开始编程,完成添加节的任务。在C32Asm软件中,可以快速定位到PE结构体各个结构体和字段的位置。点击菜单栏“查看(V)”->“PE信息(P)”,在C32Asm工作区左侧打开一个PE结构字段的分析面板,双击上的PE结构的各个字段面板,十六进制形式的PE结构字段的数据可以位于C32Asm工作区。2.通过编程增加一个section通过编程增加一个新的section无非是一个文件相关的操作,除了对PE文件的额外分析和操作。添加section的步骤和手动添加section的步骤是一样的,按照上面的步骤一步步写代码即可。在开始写代码之前,先修改FileCreate()函数中的部分代码,如下:m_hMap=CreateFileMapping(m_hFile,NULL,PAGE_READWRITE/*|SEC_IMAGE*/,0,0,0);if(m_hMap==NULL){CloseHandle(m_hFile);returnbRet;}SEC_IMAGE宏应该在这里被注释掉。因为需要修改内存文件映射,这个值会添加section失败,所以应该注释掉或者直接删除。程序界面如图12所示。图12添加section界面首先编写“Add”按钮响应事件,代码如下:voidCPeParseDlg::OnBtnAddSection(){//adddriverhere//sectionnamecharszSecName[8]={0};//sectionsizeintnSecSize=0;GetDlgItemText(IDC_EDIT_SECNAME,szSecName,8);nSecSize=GetDlgItemInt(IDC_EDIT_SEC_SIZE,FALSE,TRUE);AddSec(szSecName,nSecSize);}最关键的部分按钮事件的核心是AddSec()函数。该函数有两个参数,分别是添加的节的名称和添加的节的大小。无论输入的大小如何,最后都会按照对齐方式向上对齐。看一下AddSec()函数的代码,如下:;PIMAGE_SECTION_HEADERpTmpSec=m_pSecHdr+nSecNum;//复制段名strncpy((char*)pTmpSec->Name,szSecName,7);//段内存大小pTmpSec->Misc.VirtualSize=AlignSize(nSecSize,dwSecAlignment);//pTmpSec->VirtualAddress=m_pSecHdr[nSecNum-1].VirtualAddress+AlignSize(m_pSecHdr[nSecNum-1].Misc.VirtualSize,dwSecAlignment);//该段的文件大小pTmpSec->SizeOfRawData=AlignSize(nSecSize,dwFileAlignment);//节文件起始位置pTmpSec->PointerToRawData=m_pSecHdr[nSecNum-1].PointerToRawData+AlignSize(m_pSecHdr[nSecNum-1].SizeOfRawData,dwSecAlignment);//调整节数m_pNtHdr->文件头。NumberOfSections++;//修正图像大小m_pNtHdr->OptionalHeader.SizeOfImage+=pTmpSec->Misc.VirtualSize;FlushViewOfFile(m_lpBase,0);//添加截面数据AddSecData(pTmpSec->SizeOfRawData);EnumSections();}代码中的每一步都是按照相应的步骤完成的,用到的两个函数是AlignSize()和AddSecData()。前者用于对齐,后者用于将实际数据内容添加到文件中。这两个函数很简单,代码如下:DWORDCPeParseDlg::AlignSize(intnSecSize,DWORDAligment){intnSize=nSecSize;if(nSize%Alignment!=0){nSecSize=(nSize/Alignment+1)*Alignment;}returnSecSize;}VOIDCPeParseDlg::AddSecData(intnSecSize){PBYTEpByte=NULL;pByte=(PBYTE)malloc(nSecSize);ZeroMemory(pByte,nSecSize);DWORDdwNum=0;SetFilePointer(m_hFile,0,0,FILE_END);WriteFile(m_hFile,pByte,nSecSize,&dwNum,NULL);FlushFileBuffers(m_hFile);free(pByte);}整个添加分区的代码就完成了,还是用一开始的简单程序来测试看是否分区可以添加,如图13所示。图13添加一个section从图13可以看出,添加一个section是成功的。尝试运行添加节区后的文件,可以正常运行,添加节区的文件比原文件大4KB,和之前手动添加的效果一样。
