S.l.e!ep.¢%

像打了激速一样,以四倍的速度运转,开心的工作
简单、开放、平等的公司文化;尊重个性、自由与个人价值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
        
       原创:星绽紫辉(rawdata)
http://www.cppblog.com/rawdata  2009-1-20
       转载请注明出处

       关键字: PE   增加  区段  section 文件格式

       现在我要给一PE文件增加区段(section),但是增加区段后我不希望影响PE文件的正常使用,那么应该怎么

做呢?我写这个教程的目的,希望能帮学习PE格式,当然也作为我以后参考的笔记。

        简单地说:PE文件和普通文件没有什么区别,只是存在格式上的差异。另外,当你双击某个.exe文件

时,Windows Shell 程序将会尝试解析文件并运行它的PE代码。所以第一步,你必须对PE格式比较熟悉。现在

网上的PE教程我认为最好的就是罗云斌主页上的汇编教程了,我在这里长话短说,只是讨论和我们要解决的

问题相关的方面。

         一般来讲,PE由以下几个部分依次排列下来:

          1.   Dos 头

          2.   Dos stub    (通常你不必关心它的内容,重建PE只需完全拷贝即可) 
 
          3.   NT 头
    
          4.    节表

          5.    文件对齐间隙

          6.    第一节

          7.    第二节

          8.    ...

          9.    第 n  节 (文件结尾)


          对于PE文件,如果没有文件对齐和内存对齐,那将是非常简单了。(但是,太简单了就不安全了,不是

吗?对于这种格式,当初的设计者还是动了不少脑筋的,呵呵~)。我们可以用非常简单的读文件函数,读取

各部分结构,然后把它重组还原PE。如果你是初次接触,建议你这样实践一下。

        
         然后我们讨论一下如何增加区段,我们将尽量保证维持原来的PE结构的数据,然后在此基础上增加我们

的数据。现在我把这个过程步骤化:

        一、读取Dos头

        二、读取Dos stub

        三、读取NT头,根据Dos头定位到此

        四、读取NT头

        五、读取节表

        六、遍历读取所有节块


      现在,我们把一个PE文件读到缓冲区了。然后我们进行修改:试验证明,只需要某些特征项即可,而不

必对所有参数进行修改,这样PE文件(如.exe)还是能正常运行。对于具体的节块,我们必须给某些关键的

参数赋值,否则将破坏PE结构,导致不能运行。


      在开始修改前,有一些非常关键的东西我们必须知道:文件对齐和内存对齐。实际上,所有的section区

段都是文件对齐的(你把每一节当成一个块,具有起始文件位置和块大小),比如:第一节的文件偏移为

1024KB,节块大小为1000KB,那么第二节的文件偏移将是2048KB,而不是2024KB(文件偏移即是节块的开始

位置)。但这其实不是一成不变的。你可以修改它,只要满足文件对齐,但是带来的麻烦是,你必须同时也

修改它的定位目录----->节表里的PointerToRawData文件指针,否则将由于找不到对应的节块而产生错误。

       我常常思考这样一个问题,到底要不要把PE装载到内存,然后在内存中增加区段,然后把PE内存dump成

新的PE文件达到增加区段的目的。实际上,这是完全可行的。但是,直接修改PE文件也是可以的,可以不把

PE装载到内存。因为我们仅仅是增加区段,不需要修改引入表之类的东西。在我增加区段成功后,对比发

现,节表里面的Virtual Address 实际上等于PointerToRawData,节表里面的Virtual Size 实际上等于SizeofRawData

(呵呵,我的网名就是rawdata)。于是,我想,这样的设计实际上是为了简化PE程序的设计的复杂性。内存对

齐转化为文件对齐,一旦设计好文件对齐,那么内存对齐就设计好了。但是,你千万不要认为两者一定总是相

等的,实际它有很大的灵活性,你可以随意设计,只要满足NT头里面的指定的内存对齐值参数。

      还有一个关键的要注意的地方:必须重新修改NT头里面的pINH->OptionalHeader.SizeOfImage值。即整个PE

在内存的全部的映像的大小。我们可以这样给它赋予新的值:增加1个新的区段,就在原来的SizeOfImage值基

础上再加上该节块大小的文件对齐值,增加了几个区段,就累加几次,你应该明白了吧?

    
      如果你感觉我的语言表达很糟糕,请你谅解。不过,请你放心,我后面会给出源代码。其实主要做的工

作就是在文件尾加上一些数据,然后修改文件头的一些参数,仅此而已,没有什么神奇的地方,还等什么?

赶快去写一个PE加区工具吧~~~ 

     代码清单如下:    (平台: window console)
     把其中的PE文件名该为你想加区的PE文件名就可以了。

  1/**************************************************************************
  2*    文件名:        Main.cpp
  3*    日  期:        2009年1月13日
  4*    作  者:        rawdata
  5*    描  述:        
  6***************************************************************************/

  7
  8#include <windows.h>
  9#include <windowsx.h>
 10#include <winnt.h>
 11#include <iostream>
 12using namespace std;
 13
 14#pragma warning(disable:4312)
 15#pragma warning(disable:4311)
 16#pragma warning(disable:4244)
 17
 18int main(int argc,char**argv)
 19{
 20    //------------------- 主要缓冲区定义 ----------------------
 21    
 22    BYTE* pDos = NULL;        //Dos头和Stub区
 23    DWORD dwSizeDos = 0;    //Dos头 大小
 24    DWORD dwSizeStub = 0;    //Dos Stub区大小
 25
 26    BYTE* pNT = NULL;        //NT头
 27    DWORD dwSizeNT = 0;        //该区大小
 28
 29    BYTE* pSecH = NULL;        //节表
 30    DWORD dwSizeSecH = 0;    //该区大小
 31    WORD dwSizeAdd = 3;        //增加的节的个数
 32
 33    BYTE* pDelta = NULL;    //文件对齐填充数据
 34    DWORD dwSizeDelta = 0;    //该区大小
 35
 36    BYTE* pPrevSec = NULL;    //节块
 37    DWORD dwSizePrevSec = 0;//该区大小
 38
 39    BYTE* pAddSec = NULL;    //新增的节块
 40    DWORD dwSizeAddSec = 0;    //该区大小
 41
 42    //---------------------- 其他临时定义 --------------------
 43    
 44    const char* pszFilePath = NULL;            //文件路径
 45    HANDLE hFile = NULL;                    //文件句柄
 46
 47    PIMAGE_DOS_HEADER        pIDH = NULL;    //Dos头
 48    PIMAGE_NT_HEADERS        pINH = NULL;    //NT 头
 49    PIMAGE_SECTION_HEADER*    ppISH = NULL;    //节表
 50
 51    DWORD dwRealRead = 0;                    //实际每次文件读取的字节数
 52    DWORD dwMiniPointer = 0;                //第一个section的位置
 53    BOOL bNeedModify = FALSE;                //是否有必要需要修改节块的文件指针
 54
 55    DWORD dwRawDataSize = 10*1024;                //增加区段的数据大小
 56
 57    DWORD dwTmp=0,dwTmp2=0,dwTmp3 = 0;
 58    
 59    //Dos 头
 60    //===============================================================================
 61    //打开文件、读取Dos头
 62
 63    pszFilePath = argv[0];
 64    pszFilePath = "BeingInjued.exe";//Test
 65
 66    cout<<"PE FileName:"<<pszFilePath<<endl;
 67
 68    if(0 == lstrcmp(pszFilePath,""))
 69    {
 70        cout<<"文件路径不能为空!"<<endl;
 71        return -1;
 72    }

 73
 74    hFile = CreateFile(pszFilePath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
 75        FILE_ATTRIBUTE_NORMAL,NULL);
 76
 77    if(INVALID_HANDLE_VALUE == hFile)
 78    {
 79        cout<<"打开文件失败!"<<endl;
 80        hFile = NULL;
 81        goto Error;
 82    }

 83
 84    dwSizeDos = sizeof(IMAGE_DOS_HEADER);
 85    pDos = new BYTE[dwSizeDos];
 86    memset(pDos,0,dwSizeDos);
 87
 88    if(!ReadFile(hFile,pDos,dwSizeDos,&dwRealRead,NULL))
 89    {
 90        cout<<"读取Dos头失败!"<<endl;
 91        goto Error;
 92    }

 93    cout<<"Dos Header: "<<dwRealRead<<"bytes have read."<<endl;
 94
 95    //获得IMAGE_DOS_HEADER指针,效验
 96    pIDH = (PIMAGE_DOS_HEADER)pDos;
 97    if(memcmp(&(pIDH->e_magic),"MZ",2)!=0)
 98    {
 99        cout<<"不是有效的PE文件格式!"<<endl;
100        goto Error;
101    }

102
103    //DOS Stub
104    //================================================================================
105    
106    dwSizeStub = pIDH->e_lfanew - dwSizeDos;
107    pDos = (BYTE*)realloc(pDos,dwSizeDos + dwSizeStub);
108    memset(pDos+dwSizeDos,0,dwSizeStub);
109    
110    if(!ReadFile(hFile,pDos+dwSizeDos,dwSizeStub,&dwRealRead,NULL))
111    {
112        cout<<"读取DosStub失败!"<<endl;
113        goto Error;
114    }

115    cout<<"Dos Stub: "<<dwRealRead<<"bytes have read."<<endl;
116
117    //NT Header
118    //================================================================================
119
120    dwSizeNT = sizeof(IMAGE_NT_HEADERS);
121    pNT = new BYTE[dwSizeNT];
122    memset(pNT,0,dwSizeNT);
123    
124    if(!ReadFile(hFile,pNT,dwSizeNT,&dwRealRead,NULL))
125    {
126        cout<<"读取NT Headers失败!"<<endl;
127        goto Error;
128    }

129    
130    //获得NT指针
131    pINH = (PIMAGE_NT_HEADERS)pNT;
132    if(memcmp(&(pINH->Signature),"PE",2)!=0)
133    {
134        cout<<"错误的PE文件格式!"<<endl;
135        goto Error;
136    }

137
138    //节表
139    //================================================================================
140    
141    dwSizeSecH = (pINH->FileHeader.NumberOfSections + dwSizeAdd) * sizeof(IMAGE_SECTION_HEADER);
142    pSecH = new BYTE[dwSizeSecH];
143    memset(pSecH,0,pINH->FileHeader.NumberOfSections + dwSizeAdd);
144
145    ppISH = (PIMAGE_SECTION_HEADER*)new DWORD[pINH->FileHeader.NumberOfSections + dwSizeAdd];
146    memset(ppISH,0,sizeof(DWORD)*(pINH->FileHeader.NumberOfSections + dwSizeAdd));
147
148    for(UINT i=0;i<pINH->FileHeader.NumberOfSections;i++)
149    {
150        if(!ReadFile(hFile,pSecH+i*sizeof(IMAGE_SECTION_HEADER),
151            sizeof(IMAGE_SECTION_HEADER),&dwRealRead,NULL))
152        {
153            cout<<"读取Section Headers失败!"<<endl;
154            goto Error;
155        }

156        
157        ppISH[i] = (PIMAGE_SECTION_HEADER)(pSecH+i*sizeof(IMAGE_SECTION_HEADER));
158        
159        cout<<"Name: "<<ppISH[i]->Name<<endl;
160        cout<<"Size: "<<ppISH[i]->SizeOfRawData<<endl;
161        cout<<"Virtual Adress: "<<ppISH[i]->VirtualAddress<<endl;
162        cout<<"PointerToRawData: "<<ppISH[i]->PointerToRawData<<endl;
163    }

164    cout<<endl;
165
166
167    //节块 
168    //================================================================================
169
170    pPrevSec = (BYTE*)malloc(0);
171    for(i=0;i<pINH->FileHeader.NumberOfSections;i++)
172    {
173        dwTmp = ppISH[i]->SizeOfRawData;
174        pPrevSec = (BYTE*)realloc(pPrevSec,dwTmp2 + dwTmp);
175        memset(pPrevSec+dwTmp2,0,dwTmp);
176        
177        SetFilePointer(hFile,ppISH[i]->PointerToRawData,NULL,FILE_BEGIN);
178
179        cout<<i<<":PointerToRawData:"<<ppISH[i]->PointerToRawData<<endl;
180        cout<<"BlockSize:"<<ppISH[i]->SizeOfRawData<<endl;
181        cout<<"Virtual Address:"<<ppISH[i]->VirtualAddress<<endl;
182
183        if(!ReadFile(hFile,pPrevSec+dwTmp2,dwTmp,&dwRealRead,NULL)){
184            cout<<"ReadFile Faield!"<<endl;
185            goto Error;
186        }

187
188        dwTmp2 += dwTmp;
189    }

190    dwSizePrevSec = dwTmp2;
191
192
193    //数据填补、修改部分
194    //================================================================================
195
196    //第一个节块的位置
197    for(i=0;i<pINH->FileHeader.NumberOfSections;i++)
198    {
199        if(ppISH[i])
200        {
201            if(0 == dwMiniPointer)
202                dwMiniPointer = (DWORD)ppISH[i]->PointerToRawData;
203            
204            if((ppISH[i]->PointerToRawData)<dwMiniPointer)
205                dwMiniPointer = (DWORD)ppISH[i]->PointerToRawData;
206        }

207    }

208    cout<<"Prev First Section Pos:"<<dwMiniPointer<<endl;
209
210    //空白填充区大小    ,先计算好
211    dwTmp = (dwSizeDos+dwSizeStub+dwSizeNT+dwSizeSecH);
212    if(dwMiniPointer >= dwTmp)
213        dwSizeDelta = dwMiniPointer - dwTmp;
214    else
215    {
216        dwSizeDelta = dwTmp % (pINH->OptionalHeader.FileAlignment);
217        
218        if(dwSizeDelta != 0)
219            dwSizeDelta = pINH->OptionalHeader.FileAlignment - dwSizeDelta;
220        else dwSizeDelta = 0;
221
222        bNeedModify = TRUE;
223    }

224    cout<<"Delta:"<<dwSizeDelta<<endl;
225    pDelta = new BYTE[dwSizeDelta];
226    memset(pDelta,0,dwSizeDelta);
227
228    dwMiniPointer = dwTmp;
229    dwMiniPointer += dwSizeDelta;
230
231    //修改 NT 头,必须修改
232    pINH->FileHeader.NumberOfSections += dwSizeAdd;
233
234    
235    //修改原来节表头的文件指针
236    cout<<endl;
237    if(bNeedModify)
238    {
239        for(i=0;i<(UINT)(pINH->FileHeader.NumberOfSections - dwSizeAdd);i++)
240        {
241            if(0 != i)
242                ppISH[i]->PointerToRawData =
243                ppISH[i-1]->PointerToRawData +
244                ppISH[i-1]->SizeOfRawData;
245            else
246               ppISH[i]->PointerToRawData = dwMiniPointer;
247            cout<<"New Entry:"<<i<<""<<ppISH[i]->PointerToRawData<<endl;
248        }

249    }

250
251    //填充增加的节表头
252    cout<<endl;
253    char szName[8= ".";
254    char szNum[7= {0};
255    szName[5= 0;
256    dwTmp = ppISH[0]->VirtualAddress;
257    dwTmp2 = pINH->OptionalHeader.SectionAlignment;
258    dwTmp3 = pINH->OptionalHeader.FileAlignment;
259    int nCount = 1;
260    DWORD dwNewAllocSize = 0;
261
262    for(i=(pINH->FileHeader.NumberOfSections - dwSizeAdd);
263        i<pINH->FileHeader.NumberOfSections;i++)
264    {                                                   
265        ppISH[i] = (PIMAGE_SECTION_HEADER)(pSecH+i*sizeof(IMAGE_SECTION_HEADER));
266        memset(ppISH[i],0,sizeof(IMAGE_SECTION_HEADER));
267        
268        ppISH[i]->Characteristics = ppISH[0]->Characteristics;
269        
270        sprintf(szNum,"%d",nCount);
271        memcpy(szName+1,szNum,7);
272        memcpy(ppISH[i]->Name,szName,8);
273
274        dwNewAllocSize = dwRawDataSize % dwTmp3;
275        if(dwNewAllocSize != 0)
276           dwNewAllocSize = dwRawDataSize + (dwTmp3 - dwNewAllocSize);
277        else dwNewAllocSize = dwRawDataSize;
278        ppISH[i]->SizeOfRawData = dwNewAllocSize;
279
280        //增加的文件区段
281        dwSizeAddSec += ppISH[i]->SizeOfRawData;
282        
283        dwNewAllocSize = dwRawDataSize % dwTmp2;
284        if(dwNewAllocSize != 0)
285           dwNewAllocSize = dwRawDataSize + (dwTmp2 - dwNewAllocSize);
286        else dwNewAllocSize = dwRawDataSize;
287
288        ppISH[i]->Misc.VirtualSize = dwNewAllocSize;
289
290        //修改NT头,必须修改
291        pINH->OptionalHeader.SizeOfImage += dwNewAllocSize;
292
293        ppISH[i]->PointerToRawData = (ppISH[i-1]->PointerToRawData+ppISH[i-1]->SizeOfRawData);         
294
295        ppISH[i]->VirtualAddress = ppISH[i]->PointerToRawData;
296        if(ppISH[i]->VirtualAddress>=(0x7FFFFFFF-dwNewAllocSize))
297        {
298            cout<<"Error!Virtual address overflow!"<<endl;
299            goto Error;
300        }

301        cout<<"New Entry:"<<i<<""<<ppISH[i]->PointerToRawData<<endl;
302        nCount++;
303    }

304
305    //修改NT头,可选,不设置也能正常运行,但是为保证数据准确,还是写上
306    pINH->OptionalHeader.SizeOfHeaders = dwMiniPointer;
307
308
309
310    //新的Sections,必须满足文件对齐
311    pAddSec = new BYTE[dwSizeAddSec];
312    memset(pAddSec,0,dwSizeAddSec);
313
314    //重建文件: dumpOK.exe
315    //================================================================================
316
317    CloseHandle(hFile);
318    hFile = CreateFile("dumpOK.exe",GENERIC_READ|GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,
319        FILE_ATTRIBUTE_NORMAL,NULL);
320    
321    //Dos Data
322    if(!WriteFile(hFile,pDos,dwSizeDos+dwSizeStub,&dwRealRead,NULL))
323    {
324        cout<<"WriteFile Faield!"<<endl;
325        goto Error;
326    }

327    cout<<"Dos Data:"<<dwRealRead<<" bytes write."<<endl;
328
329
330    //NT 头
331    if(!WriteFile(hFile,pNT,dwSizeNT,&dwRealRead,NULL))
332    {
333        cout<<"WriteFile Faield!"<<endl;
334        goto Error;
335    }

336    cout<<"NT Data:"<<dwRealRead<<" bytes write."<<endl;
337
338    //节表  (包括新加的)
339    if(!WriteFile(hFile,pSecH,dwSizeSecH,&dwRealRead,NULL))
340    {
341        cout<<"WriteFile Faield!"<<endl;
342        goto Error;
343    }

344    cout<<"Section Headers:"<<dwRealRead<<" bytes write."<<endl;
345
346    //填充空白数据
347    if(!WriteFile(hFile,pDelta,dwSizeDelta,&dwRealRead,NULL)){
348        cout<<"WriteFile Faield!"<<endl;
349        goto Error;
350    }

351    cout<<"Delta Data:"<<dwRealRead<<" bytes write."<<endl;
352
353
354    //原来的节块
355    if(!WriteFile(hFile,pPrevSec,dwSizePrevSec,&dwRealRead,NULL)){
356        cout<<"WriteFile Faield!"<<endl;
357        goto Error;
358    }

359    cout<<"Section Blocks:"<<dwRealRead<<" bytes write."<<endl;
360
361    //增加的节块
362    if(!WriteFile(hFile,pAddSec,dwSizeAddSec,&dwRealRead,NULL)){
363        cout<<"WriteFile Faield!"<<endl;
364        goto Error;
365    }

366    cout<<"Section Blocks:"<<dwRealRead<<" bytes write."<<endl;
367    cout<<"Requeried Works Success Completed."<<endl;
368
369Error:
370    CloseHandle(hFile);
371    delete[]ppISH;        
372    delete[]pDos;
373    delete[]pNT;
374    delete[]pSecH;
375    delete[]pDelta;
376    delete[]pPrevSec;
377    delete[]pAddSec;
378    return 0;
379}

380
381
382
383
384


      如果代码有什么谬误或你有更好的解决方案,请留言或者EmailToMe:xiaolu69soft@yahoo.com.cn
希望我的文章对你有所帮助。
      rawdata

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理