文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

网络安全编程:PE编程实例之添加节区

2024-12-03 05:59

关注

添加节区在很多场合都会用到,比如在加壳中、在免杀中都会经常用到对PE文件添加一个节区。添加一个节区的方法有4步,第1步是在节表的最后面添加一个IMAGE_SECTI ON_HEADER,第2步是更新IMAGE_FILE_HEADER中的NumberOfSections字段,第3步是更新IMAGE_OPTIONAL_HEADER中的SizeOfImage字段,最后一步则是添加文件的数据。当然,前3步是没有先后顺序的,但是最后一步一定要明确如何改变。

某些情况下,在添加新的节区项以后会向新节区项的数据部分添加一些代码,而这些代码可能要求在程序执行之前就被执行,那么这时还需要更新IMAGE_OPTIONAL_HEADER中的AddressOfEntryPoint字段。

1. 手动添加一个节区

先来进行一次手动添加节区的操作,这个过程是个熟悉上述步骤的过程。网上有很多现成的添加节区的工具。这里自己编写工具的目的是掌握和了解其实现方法,锻炼编程能力;手动添加节区是为了巩固所学的知识,熟悉添加节区的步骤。

使用C32Asm用十六进制编辑方式打开测试程序,并定位到其节表处,如图1所示。

图1  节表位置信息

从图1中可以看到,该PE文件有3个节表。直接看十六进制信息可能很不方便,为了直观方便地查看节表中IMAGE_SECTION_HEADER的信息,那么使用LordPE进行查看,如图2所示。

图2  使用LordPE查看该节表信息

用LordPE工具查看的确直观多了。对照LordPE显示的节表信息来添加一个节区。IMAGE_SECTION_HEADER结构体定义如下: 

  1. typedef struct _IMAGE_SECTION_HEADER {  
  2.  BYTE Name[IMAGE_SIZEOF_SHORT_NAME];  
  3.  union {  
  4.  DWORD PhysicalAddress;  
  5.  DWORD VirtualSize;  
  6.  } Misc;  
  7.  DWORD VirtualAddress;  
  8.  DWORD SizeOfRawData;  
  9.  DWORD PointerToRawData;  
  10.  DWORD PointerToRelocations;  
  11.  DWORD PointerToLinenumbers;  
  12.  WORD NumberOfRelocations;  
  13.  WORD NumberOfLinenumbers;  
  14.  DWORD Characteristics;  
  15. } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 

IMAGE_SECTION_HEADER 结构体的成员很多,但是真正要使用的只有 6 个,分别是Name、VirtualSize、VritualAddress、SizeOfRawData、PointerToRawData 和 Characteristics。这 6 项刚好与 LordPE 显示的 6 项相同。其实 IMAGE_SECTION_HEADER 结构体中其余的成员几乎不被使用。下面介绍如何添加这些内容。

IMAGE_SECTION_HEADER 的长度为 40 字节,是十六进制的 0x28,在 C32Asm 中占用 2 行半的内容,这里一次把这两行半的内容手动添加进去。回到 C32Asm 中,在最后一个节表的位置处开始添加内容,首先把光标放到右边的 ASCII 字符中,输入“.test”,如图3所示。

图3  添加“.test”节名

接下来在00000240位置处添加节的大小,该大小直接是对齐后的大小即可。由于文件对齐是0x1000字节,也就是4096字节,那么采用最小值即可,使该值为0x1000。在C32Asm中添加时,正确的添加应当是“00 10 00 00”,以后添加时也要注意字节顺序。在添加后面几个成员时,不再提示注意字节顺序,应时刻清楚这点。在添加该值时,应当将光标定位在十六进制编辑处,而不是刚才所在的ASCII字符处。顺便要把VirutalAddress也添加上,VirtualAddress的值是前一个节区的起始位置加上上一个节对齐后的长度的值,上一个节区的起始位置为0x6000,上一个节区对齐后的长度为0x3000,因此新节区的起始位置为0x9000。添加VirtualSize和VirtualAddress后如图4所示。

图4  添加VirtualSize和VirtualAddress的值

接下来的两个字段分别是SizeOfRawData和PointerToRawData,其添加方法类似前面两个字段的添加方法,这里就不细说了。分别添加“0x9000”和“0x1000”两个值,如图5所示。

图5  添加SizeOfRawData和PointerToRawData

PointerToRawData后面的12字节都可以为0,只要修改最后4字节的内容,也就是Characteristics的值即可。这个值直接使用上一个节区的值即可,实际添加时应根据所要节的属性给值。这里为了省事而直接使用上一个节区的属性,如图6所示。

图6  添加Characteristics属性 

整个节表需要添加的地方就添加完成了,接下来需要修改该PE文件的节区数量。当前节区数量是3,这里要修改为4。虽然可以通过LordPE等修改工具完成,但是这里仍然使用手动修改。对于修改的位置,请大家自行定位找到,修改如图7所示。

图7  修改节区个数为4

除了节区数量以外,还要修改文件映像的大小,也就是SizeOfImage的值。由于新添加了节区,那么应该把该节区的大小加上SizeOfImage的大小,即为新的SizeOfImage的大小。现在的SizeOfImage的大小为0x9000,加上新添加节区的大小为0xa000。SizeOfImage的位置请大家自行查找,修改如图8所示。

图8  修改SizeOfImage的值为0xa000

修改PE结构字段的内容都已经做完了,最后一步就是添加真实的数据。由于这个节区不使用,因此填充0值就可以了,文件的起始位置为0x9000,长度为0x1000。把光标移到文件的末尾,单击“编辑”→“插入数据”命令,在“插入数据大小”文本框中输入十进制的4096,也就是十六进制的0x1000,如图9所示。

图9  “插入数据”对话框的设置

单击“确定”按钮,可以看到在刚才的光标处插入了很多0值,这样工作也完成了。单击“保存”按钮进行保存,提示是否备份,选择“是”。然后用LordPE查看添加节区的情况,如图10所示。

图10  添加新的节区信息

对比前后两个文件的大小,如图11所示。

图11  添加节区前后文件的大小

从图11中可以看出,添加节区后的文件比原来的文件大了4KB,这是由于添加了4096字节的0值。也许大家最关心的不是大小问题,而是软件添加了大小后是否真的可以运行。其实试运行一下,是可以运行的。

上面的整个过程就是手动添加一个新节区的全部过程,除了特有的几个步骤以外,要注意新节区的内存起始位置和文件起始位置的值。相信通过上面手动添加节区,大家对此已经非常熟悉了。下面就开始通过编程来完成添加节区的任务。

在C32Asm软件中可以快速定位PE结构的各个结构体和字段的位置,在菜单栏单击“查看(V)”->“PE信息(P)”即可在C32Asm工作区的左侧打开一个PE结构字段的解析面板,在面板上双击PE结构的每个字段则可在C32Asm工作区中定位到十六进制形式的PE结构字段的数据。

2. 通过编程添加节区

通过编程添加一个新的节区无非就是文件相关的操作,只是多了一个对PE文件的解析和操作而已。添加节区的步骤和手动添加节区的步骤是一样的,只要一步一步按照上面的步骤写代码就可以了。在开始写代码前,首先修改FileCreate()函数中的部分代码,具体如下: 

  1. m_hMap = CreateFileMapping(m_hFile, NULL,  
  2.   PAGE_READWRITE ,0, 0, 0);  
  3. if ( m_hMap == NULL )  
  4.  
  5.   CloseHandle(m_hFile); 
  6.   return bRet;  
  7. }  

这里要把SEC_IMAGE宏注释掉。因为要修改内存文件映射,有这个值会使添加节区失败,因此要将其注释掉或者直接删除掉。

程序的界面如图12所示。

图12  添加节区界面

首先编写“添加”按钮响应事件,代码如下: 

  1. void CPeParseDlg::OnBtnAddSection()  
  2.  
  3.   // 在这里添加驱动程序  
  4.   // 节名  
  5.   char szSecName[8] = { 0 };  
  6.   // 节大小  
  7.   int nSecSize = 0 
  8.   GetDlgItemText(IDC_EDIT_SECNAME, szSecName, 8);  
  9.   nSecSize = GetDlgItemInt(IDC_EDIT_SEC_SIZE, FALSE, TRUE);  
  10.   AddSec(szSecName, nSecSize);  

按钮事件中最关键的地方是AddSec()函数。该函数有两个参数,分别是添加节的名称与添加节的大小。这个大小无论输入多大,最后都会按照对齐方式进行向上对齐。看一下AddSec()函数的代码,具体如下: 

  1. VOID CPeParseDlg::AddSec(char *szSecName, int nSecSize)  
  2.  
  3.   int nSecNum = m_pNtHdr->FileHeader.NumberOfSections;  
  4.   DWORD dwFileAlignment = m_pNtHdr->OptionalHeader.FileAlignment;  
  5.   DWORD dwSecAlignment = m_pNtHdr->OptionalHeader.SectionAlignment;  
  6.   PIMAGE_SECTION_HEADER pTmpSec = m_pSecHdr + nSecNum;  
  7.   // 复制节名  
  8.   strncpy((char *)pTmpSec->Name, szSecName, 7);  
  9.   // 节的内存大小  
  10.   pTmpSec->Misc.VirtualSize = AlignSize(nSecSize, dwSecAlignment);  
  11.   // 节的内存起始位置  
  12.   pTmpSec->VirtualAddress=m_pSecHdr[nSecNum-1].VirtualAddress+AlignSize(m_pSecHdr [nSecNum - 1].Misc.VirtualSize, dwSecAlignment);  
  13.   // 节的文件大小  
  14.   pTmpSec->SizeOfRawData = AlignSize(nSecSize, dwFileAlignment);  
  15.   // 节的文件起始位置 
  16.   pTmpSec->PointerToRawData=m_pSecHdr[nSecNum-1].PointerToRawData+AlignSize(m_pSecHdr[nSecNum - 1].SizeOfRawData, dwSecAlignment);  
  17.   // 修正节数量  
  18.   m_pNtHdr->FileHeader.NumberOfSections ++;  
  19.   // 修正映像大小  
  20.   m_pNtHdr->OptionalHeader.SizeOfImage += pTmpSec->Misc.VirtualSize;  
  21.   FlushViewOfFile(m_lpBase, 0);  
  22.   // 添加节数据  
  23.   AddSecData(pTmpSec->SizeOfRawData);  
  24.   EnumSections();  

代码中每一步都按照相应的步骤来完成,其中用到的两个函数分别是 AlignSize()和AddSecData()。前者是用来进行对齐的,后者是用来在文件中添加实际的数据内容的。这两个函数非常简单,代码如下: 

  1. DWORD CPeParseDlg::AlignSize(int nSecSize, DWORD Alignment)  
  2.  
  3.   int nSize = nSecSize 
  4.   if ( nSize % Alignment != 0 )  
  5.   {  
  6.     nSecSize = (nSize / Alignment + 1) * Alignment;  
  7.   }  
  8.   return nSecSize;  
  9.  
  10. VOID CPeParseDlg::AddSecData(int nSecSize)  
  11.   PBYTE pByte = NULL 
  12.   pByte = (PBYTE)malloc(nSecSize);  
  13.   ZeroMemory(pByte, nSecSize);  
  14.   DWORD dwNum = 0 
  15.   SetFilePointer(m_hFile, 0, 0, FILE_END);  
  16.   WriteFile(m_hFile, pByte, nSecSize, &dwNum, NULL);  
  17.   FlushFileBuffers(m_hFile);  
  18.   free(pByte);  

整个添加节区的代码就完成了,仍然使用最开始的那个简单程序进行测试,看是否可以添加一个节区,如图13所示。

图13  添加节区

从图13中可以看出,添加节区是成功的。试着运行一下添加节区后的文件,可以正常运行,而且添加节区的文件比原文件大了4KB,和前面手动添加的效果是一样的。 

 

来源:计算机与网络安全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯