原創(chuàng):星綻紫輝(rawdata) http://www.shnenglu.com/rawdata 2009-1-20
轉(zhuǎn)載請注明出處
關(guān)鍵字: PE 增加 區(qū)段 section 文件格式
現(xiàn)在我要給一PE文件增加區(qū)段(section),但是增加區(qū)段后我不希望影響PE文件的正常使用,那么應(yīng)該怎么
做呢?我寫這個教程的目的,希望能幫學(xué)習(xí)PE格式,當(dāng)然也作為我以后參考的筆記。
簡單地說:PE文件和普通文件沒有什么區(qū)別,只是存在格式上的差異。另外,當(dāng)你雙擊某個.exe文件
時,Windows Shell 程序?qū)L試解析文件并運(yùn)行它的PE代碼。所以第一步,你必須對PE格式比較熟悉。現(xiàn)在
網(wǎng)上的PE教程我認(rèn)為最好的就是羅云斌主頁上的匯編教程了,我在這里長話短說,只是討論和我們要解決的
問題相關(guān)的方面。
一般來講,PE由以下幾個部分依次排列下來:
1. Dos 頭
2. Dos stub (通常你不必關(guān)心它的內(nèi)容,重建PE只需完全拷貝即可)
3. NT 頭
4. 節(jié)表
5. 文件對齊間隙
6. 第一節(jié)
7. 第二節(jié)
8. ...
9. 第 n 節(jié) (文件結(jié)尾)
對于PE文件,如果沒有文件對齊和內(nèi)存對齊,那將是非常簡單了。(但是,太簡單了就不安全了,不是
嗎?對于這種格式,當(dāng)初的設(shè)計者還是動了不少腦筋的,呵呵~)。我們可以用非常簡單的讀文件函數(shù),讀取
各部分結(jié)構(gòu),然后把它重組還原PE。如果你是初次接觸,建議你這樣實(shí)踐一下。
然后我們討論一下如何增加區(qū)段,我們將盡量保證維持原來的PE結(jié)構(gòu)的數(shù)據(jù),然后在此基礎(chǔ)上增加我們
的數(shù)據(jù)。現(xiàn)在我把這個過程步驟化:
一、讀取Dos頭
二、讀取Dos stub
三、讀取NT頭,根據(jù)Dos頭定位到此
四、讀取NT頭
五、讀取節(jié)表
六、遍歷讀取所有節(jié)塊
現(xiàn)在,我們把一個PE文件讀到緩沖區(qū)了。然后我們進(jìn)行修改:試驗(yàn)證明,只需要某些特征項(xiàng)即可,而不
必對所有參數(shù)進(jìn)行修改,這樣PE文件(如.exe)還是能正常運(yùn)行。對于具體的節(jié)塊,我們必須給某些關(guān)鍵的
參數(shù)賦值,否則將破壞PE結(jié)構(gòu),導(dǎo)致不能運(yùn)行。
在開始修改前,有一些非常關(guān)鍵的東西我們必須知道:文件對齊和內(nèi)存對齊。實(shí)際上,所有的section區(qū)
段都是文件對齊的(你把每一節(jié)當(dāng)成一個塊,具有起始文件位置和塊大小),比如:第一節(jié)的文件偏移為
1024KB,節(jié)塊大小為1000KB,那么第二節(jié)的文件偏移將是2048KB,而不是2024KB(文件偏移即是節(jié)塊的開始
位置)。但這其實(shí)不是一成不變的。你可以修改它,只要滿足文件對齊,但是帶來的麻煩是,你必須同時也
修改它的定位目錄----->節(jié)表里的PointerToRawData文件指針,否則將由于找不到對應(yīng)的節(jié)塊而產(chǎn)生錯誤。
我常常思考這樣一個問題,到底要不要把PE裝載到內(nèi)存,然后在內(nèi)存中增加區(qū)段,然后把PE內(nèi)存dump成
新的PE文件達(dá)到增加區(qū)段的目的。實(shí)際上,這是完全可行的。但是,直接修改PE文件也是可以的,可以不把
PE裝載到內(nèi)存。因?yàn)槲覀儍H僅是增加區(qū)段,不需要修改引入表之類的東西。在我增加區(qū)段成功后,對比發(fā)
現(xiàn),節(jié)表里面的Virtual Address 實(shí)際上等于PointerToRawData,節(jié)表里面的Virtual Size 實(shí)際上等于SizeofRawData
(呵呵,我的網(wǎng)名就是rawdata)。于是,我想,這樣的設(shè)計實(shí)際上是為了簡化PE程序的設(shè)計的復(fù)雜性。內(nèi)存對
齊轉(zhuǎn)化為文件對齊,一旦設(shè)計好文件對齊,那么內(nèi)存對齊就設(shè)計好了。但是,你千萬不要認(rèn)為兩者一定總是相
等的,實(shí)際它有很大的靈活性,你可以隨意設(shè)計,只要滿足NT頭里面的指定的內(nèi)存對齊值參數(shù)。
還有一個關(guān)鍵的要注意的地方:必須重新修改NT頭里面的pINH->OptionalHeader.SizeOfImage值。即整個PE
在內(nèi)存的全部的映像的大小。我們可以這樣給它賦予新的值:增加1個新的區(qū)段,就在原來的SizeOfImage值基
礎(chǔ)上再加上該節(jié)塊大小的文件對齊值,增加了幾個區(qū)段,就累加幾次,你應(yīng)該明白了吧?
如果你感覺我的語言表達(dá)很糟糕,請你諒解。不過,請你放心,我后面會給出源代碼。其實(shí)主要做的工
作就是在文件尾加上一些數(shù)據(jù),然后修改文件頭的一些參數(shù),僅此而已,沒有什么神奇的地方,還等什么?
趕快去寫一個PE加區(qū)工具吧~~~
代碼清單如下: (平臺: console)
把其中的PE文件名該為你想加區(qū)的PE文件名就可以了。
后記:
原來的程序是只使用于raw和rva相等的情況,正確的修改如下:
把第295行修改為:
//Rva與raw不匹配的情況。。。
ppISH[i]->VirtualAddress = ppISH[i-1]->VirtualAddress +\
((ppISH[i-1]->SizeOfRawData/pINH->OptionalHeader.SectionAlignment)+1)*\
(pINH->OptionalHeader.SectionAlignment);
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>
12
using namespace std;
13
14
#pragma warning(disable:4312)
15
#pragma warning(disable:4311)
16
#pragma warning(disable:4244)
17
18
int main(int argc,char**argv)
19

{
20
//------------------- 主要緩沖區(qū)定義 ----------------------
21
22
BYTE* pDos = NULL; //Dos頭和Stub區(qū)
23
DWORD dwSizeDos = 0; //Dos頭 大小
24
DWORD dwSizeStub = 0; //Dos Stub區(qū)大小
25
26
BYTE* pNT = NULL; //NT頭
27
DWORD dwSizeNT = 0; //該區(qū)大小
28
29
BYTE* pSecH = NULL; //節(jié)表
30
DWORD dwSizeSecH = 0; //該區(qū)大小
31
WORD dwSizeAdd = 3; //增加的節(jié)的個數(shù)
32
33
BYTE* pDelta = NULL; //文件對齊填充數(shù)據(jù)
34
DWORD dwSizeDelta = 0; //該區(qū)大小
35
36
BYTE* pPrevSec = NULL; //節(jié)塊
37
DWORD dwSizePrevSec = 0;//該區(qū)大小
38
39
BYTE* pAddSec = NULL; //新增的節(jié)塊
40
DWORD dwSizeAddSec = 0; //該區(qū)大小
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; //節(jié)表
50
51
DWORD dwRealRead = 0; //實(shí)際每次文件讀取的字節(jié)數(shù)
52
DWORD dwMiniPointer = 0; //第一個section的位置
53
BOOL bNeedModify = FALSE; //是否有必要需要修改節(jié)塊的文件指針
54
55
DWORD dwRawDataSize = 10*1024; //增加區(qū)段的數(shù)據(jù)大小
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指針,效驗(yàn)
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
//節(jié)表
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
//節(jié)塊
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
//數(shù)據(jù)填補(bǔ)、修改部分
194
//================================================================================
195
196
//第一個節(jié)塊的位置
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
//空白填充區(qū)大小 ,先計算好
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
//修改原來節(jié)表頭的文件指針
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
//填充增加的節(jié)表頭
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
//增加的文件區(qū)段
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頭,可選,不設(shè)置也能正常運(yùn)行,但是為保證數(shù)據(jù)準(zhǔn)確,還是寫上
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
//節(jié)表 (包括新加的)
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
//填充空白數(shù)據(jù)
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
//原來的節(jié)塊
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
//增加的節(jié)塊
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
369
Error:
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