[普通]c++代码给PE文件添加一个区段

作者(passion) 阅读(1322次) 评论(0) 分类( c++)

为什么要添加区段就不说了, 如果连理由都没有的话吧应该是不需要添加区段的. 怎么添加区段那是必须说的,这也是这个文章出现的原因.

添加区段:                                
首先得了解区段在PE文件中哪一个位置(从哪里来).
其次,要把区段添加到什么位置(到哪里去),
最后还需要清楚要添加的区段的区段名,大小,文件偏移,内存偏移(你是谁).

                                        
第一步 从哪里来                                 
1. 区段一般都是有多个.
2. 区段内容的位置一般是在PE文件头部(对齐后)的末尾, 
3. 每个区段描述表索引一个区段的内容,所以区段描述表是多个的, 
   PE文件的区段描述表之于区段内容就像字典里的拼音之于字(请脑补使用拼音查找字的技术).
                                        
区段描述表(索引):
                                 
区段内容(索引到的内容):
                                
                                        
区段内容是通过区段描述表的DWORD PointerToRawDatz 字段索引到的.
IMAGE_SECTION_HEADER 结构

BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]   //区段名,长度8字节的ASCII字符串
union   Misc :  ;//区段大小,实际被使用的区段大小,即未被对齐之前的大小
                DWORD    PhysicalAddress
                DWORD    VirtualSize
DWORD     VirtualAddress  //此区段加载到内存后的RVA,按照内存页对齐
DWORD   SizeOfRawData   //此区段在磁盘中的体积,按照文件页对齐
DWORD  PointerToRawData    //此区段在文件中的偏移
DWORD  PointerToRelocations    ;//此区段重定位表的偏移(用于OBJ文件)
DWORD  PointerToLinenumbers    //行号表在文件中的偏移(用于调试)
WORD     NumberOfRelocations //重定位表项数量(用于OBJ文件)
WORD     NumberOfLinenumbers ;//行号表项数量
DWORD  Characteristics //区段属性值,具体的值和前面那张文件属性表相同


                                        
第二步 到哪里去                                 
等待解决要到哪里去这个深奥的问题的有两个:
一. 区段描述表                                
二. 区段内容                                 
                                                
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;
}


« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:VS2012编译ACE源代码
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img