原創:星綻紫輝(rawdata) http://www.shnenglu.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加區工具吧~~~
代碼清單如下: (平臺: console)
把其中的PE文件名該為你想加區的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
//------------------- 主要緩沖區定義 ----------------------
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
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