为什么要添加区段就不说了, 如果连理由都没有的话吧应该是不需要添加区段的. 怎么添加区段那是必须说的,这也是这个文章出现的原因.
添加区段:
首先得了解区段在PE文件中哪一个位置(从哪里来).
其次,要把区段添加到什么位置(到哪里去),
最后还需要清楚要添加的区段的区段名,大小,文件偏移,内存偏移(你是谁).
第一步 从哪里来
1. 区段一般都是有多个.
2. 区段内容的位置一般是在PE文件头部(对齐后)的末尾,
3. 每个区段描述表索引一个区段的内容,所以区段描述表是多个的,
PE文件的区段描述表之于区段内容就像字典里的拼音之于字(请脑补使用拼音查找字的技术).
区段描述表(索引):
区段内容(索引到的内容):
区段内容是通过区段描述表的DWORD PointerToRawDatz 字段索引到的.
IMAGE_SECTION_HEADER 结构
第二步 到哪里去
等待解决要到哪里去这个深奥的问题的有两个:
一. 区段描述表
二. 区段内容
1. 先说区段描述表, 区段描述表只能在PE扩展头末尾,PE头部的底部之前(文件对齐后的底部).
区段描述表就像上图所示,是一个IMAGE_SECTION_HEADER结构的数组,数组的个数在PE文件头:IMAGE_FILE_HEADER 的NumberOfSections 有给出.数组以全零的结构结尾.
所以区段描述表要到哪里去呢,一般都是在最后一个区段表描述表后,就是那个以全零填充的结构那里.
2. 再说区段内容
PE文件是一个组织好的文件,就像一篇优美的小说,就像正常的作家,继续写作时一般都是在文件的末尾接着写,因为如果在中间插上一段,就意味着要把后续的内容改变.
PE文件是一个组织好的文件 , 如果在中间插上一个区段,想要不覆盖原先的内容,就要把原先的内容后移,那么后移的内容中涉及到地址操作的代码就不能用了,
因为代码操作的地址没有改变,比如 jmp 0x00400000,原先在地址0x00400000处的代码是mov eax,0 ,但是mov eax,0 已经被后移到0x004000001.
所以添加的区段要到哪里去呢,插入区段一般都是插入到文件末尾.
第三步 你是谁
先作一个小小的总结, 经过前两步,我们可以有了几个已知条件
1. 区段描述表和区段内容的关联
2. 添加一个区段描述表必须要在PE扩展头底部的区段描述表数组末尾
3. 区段的内容要添加到文件的末尾.
第三步主要是完善的一步 , 要完善的地方就是区段描述表.
1. 增加了几个区段,需要如实在IMAGE_FILE_HEADER 的 NumberOfSection字段中记录.
2. 增加了多大的区段内容,需要如实在IMAGE_OPTION_HEADER 的SizeofImage 中如实记录.
|---1. SizeOfImage是经过内存粒度对齐的值, 所以如果你的区段的实际大小是200字节.那么你就需要先计算200的内存对齐粒度的值.
3. 区段描述表必须介绍区段内容在文件中的哪个位置,有多大,是什么属性,在内存中的RVA是 多少.
|---1. 在文件中的位置可以从最后一个区段描述表中获取. 新区段的文件偏移就是最后一个区段的文件偏移+区段大小 .
|---2. 新区段的RVA就是最后一个区段的RVA+按照内存粒度对齐后的区段大小
4. 将原来PE文件扩充,长度是原PE文件的字节数 + 新区段文件对齐后的字节数
|---1. 将原来的PE文件数据拷贝到新的内存中,
|---2. 将新内存写会文件.
5. 修改Misc.VirtualSize的大小为新区段实际大小按内存对齐后的值(这个我不是很清楚是否要修改成这样的值, 修改为实际大小时会形成不是有效的PE文件)
值得注意的地方是
1. 要记得改区段计数 IMAGE_FILE_HEADER.NumberOfSection
2. 要记得修改镜像大小,并且记得镜像大小是按内存粒度对齐的,修改前要注意 IMAGE_OPTION_HEADER.SizeofImage
3. 要记得添加区段描述表 IMAGE_SECTION_HEADER
4. 要记得修改区段描述表字段的值
修改区段内容文件偏移PointerToRawData
修改区段内存相对偏移VirtualAddress
修改区段大小 SizeOfRawData
修改Misc Misc.VirtualAddress
下面是代码 , 有一些函数宏和函数是自己封装的,另外删除区段的函数也有写,但是一个帖子放太多的内容不对排版不好,不过我提供了附件,附件中有全部的源码.
PIMAGE_SECTION_HEADER AddSection(_Inout_ LPBYTE& lpFile, // 通过ReadFile()获得的文件在内存指针 _Inout_ DWORD& dwSize, // 通过GetFileSize()获得的文件大小 const TCHAR* pszName, // 新增区段名 DWORD dwNameLen, // 新增区段名长度 DWORD dwSecSize // 新增区段字节数 ) { ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // 获取nt头 // 获取扩展nt头 PIMAGE_FILE_HEADER pFileNt = &(NTHEADER(lpFile))->FileHeader; PIMAGE_OPTIONAL_HEADER pOptNt = NTOPTIONALHEADER(lpFile); // 获取对齐后的区段描述表对齐后的总大小 DWORD dwAligentSectionSize = pOptNt->SizeOfHeaders; // 得到所有区段描述表的实际占用字节数 DWORD dwLengthOfAllSection = pFileNt->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); // 判断是否有空间容纳新的区段描述表 if((dwLengthOfAllSection + sizeof(IMAGE_SECTION_HEADER) <= dwAligentSectionSize)) { // 1. 修改PE标准文件头的Numberofsection +1 // 2. 修改PE扩展头的SizeofImage 增加一个内存对齐粒度 // 3. 找打最后一个区段描述表 // 3.1 修改Misc.Virtualaddress 为一个内存对齐粒度 // 3.2 修改SizeOfRawData 0x200(实际大小文件对齐后的大小) // 3.3 修改PointerToRawData 为上一个PointerToRawData+Pointer +SizeOfRawData的位置 // 3.3 修改VirtualAddress 为上一个VirtualAddress+SizeOfRawData 内存对齐后的位置 // 有空间 //在最后一个全段描述表末尾添加一个新的区段描述表 PIMAGE_SECTION_HEADER pNewSection = &(IMAGE_FIRST_SECTION(NTHEADER(lpFile))[pFileNt->NumberOfSections]); /// 修改新的区段的读数 // 修改区段名 WideCharToMultiByte(CP_ACP,0, pszName,dwNameLen, (char*)pNewSection->Name,dwNameLen, NULL,NULL); pNewSection->Name[dwNameLen] = 0; // 修改大小 pNewSection->Misc.VirtualSize = size2AligentSize(dwSecSize,pOptNt->SectionAlignment); // 修改区段文件对齐后文件大小 pNewSection->SizeOfRawData = size2AligentSize(dwSecSize,pOptNt->FileAlignment); // 修改区段的位置 PIMAGE_SECTION_HEADER pOldSec = &(IMAGE_FIRST_SECTION(NTHEADER(lpFile))[pFileNt->NumberOfSections - 1]); pNewSection->PointerToRawData = pOldSec->PointerToRawData + pOldSec->SizeOfRawData; // 修改区段的RVA pNewSection->VirtualAddress = size2AligentSize(pOldSec->VirtualAddress + pOldSec->SizeOfRawData,pOptNt->SectionAlignment); // 增加一个区段描述表计数 ++pFileNt->NumberOfSections; // 增加映像大小为新增区段大小内存对齐后的大小 pOptNt->SizeOfImage += size2AligentSize(dwSecSize,pOptNt->SectionAlignment); /// 申请空间追加新增的区段 DWORD dwSecAligSize = size2AligentSize(dwSecSize,pOptNt->FileAlignment); LPBYTE lpNewFile = new BYTE[dwSize + dwSecAligSize]; //memset(lpNewFile,0,dwSize + dwSecAligSize); // 将原有内容拷贝回去 memcpy_s(lpNewFile,dwSize + dwSecAligSize,lpFile,dwSize); memset(lpNewFile + dwSize,0,dwSecAligSize); // 以0填充 delete[] lpFile; lpFile = lpNewFile; dwSize += dwSecSize; return pNewSection; } return NULL; }
微信
支付宝