原创:星绽紫辉(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