-
-
[原创]用Python构建一个PE文件
-
发表于: 2021-5-8 20:23 8334
-
用Python生成一个PE文件,主要是为了学习PE格式,因为看很多红队工具的原理都要掌握这个,这篇文章主要是记录调试代码的过程。
主要使用的是Python3。
有关PE的文件结构描述,之前写过一个简短的对每个字段的描述
- 描述pe格式的主要地方是
winnt.h
,其中有一节叫做Image Format
,该节给出了DOS MZ格式和Windows 3.1 NE格式的文件头,之后就是PE文件的内容。在这个文件中几乎能找到所有关于PE文件的数据结构定义、枚举类型、常量定义。 - EXE文件和Dll文件是语义上的,它们使用完全的相同的PE格式,唯一的区别就是用一个字段标识这个是EXE还是DLL。
第一版 PE+ShellCode
照着PE结构的描述,用Python实现了,PE格式每个字段都有对应的大小,用到了struct
库。
一个简要的例子,因为Windows一般字符存储都是小端模式,所以用<
标明,后面的字母代表将数值转换的大小
-
H
unsigned short 占2byte -
L
unsigned long 占 4byte -
Q
unsigned long long 占8byte
然后用msf生成一个shellcode,到时候直接填入代码段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
$ msfvenom - a x86 - p windows / exec CMD = "calc" - f python
[ - ] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder specified, outputting raw payload Payload size: 189 bytes
Final size of python file : 932 bytes
buf = b""
buf + = b "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf + = b "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf + = b "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf + = b "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf + = b "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf + = b "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf + = b "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf + = b "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf + = b "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf + = b "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf + = b "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
buf + = b "\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
buf + = b "\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
buf + = b "\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
buf + = b "\xff\xd5\x63\x61\x6c\x63\x00"
|
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
|
# 学习pe的最好方法,就是自己写一个PE文件。这个例子展示了用python生成一个pe文件 import struct
import time
MZ_MAGIC = 0x5A4D
PE_MAGIC = 0x4550
IMAGE_FILE_MACHINE_I386 = 0x014c
IMAGE_SCN_MEM_EXECUTE = 0x20000000 # Section is executable.
IMAGE_SCN_MEM_READ = 0x40000000 # Section is readable.
IMAGE_SCN_MEM_WRITE = 0x80000000 # Section is writeable.
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
class DOS_HEADER_32( object ):
'''
DOS头只关心 magic 和 e_lfanew 位置就行
'''
e_magic = MZ_MAGIC
e_cblp, e_cp, e_crlc, e_cparhdr, e_minalloc, e_maxalloc, e_ss, e_sp, \
e_csum, e_ip, e_cs, e_lfarlc, e_ovno, e_res, e_oemid, \
e_oeminfo, e_res2, e_lfanew = [ 0 ] * 18
def __init__( self ):
self .fmt = "<30HL"
# 小端模式 30个H(unsigned short 占2byte) 后一个是L(unsigned long 占 4byte)
self .e_res = [ 0 , 0 , 0 , 0 ]
self .e_res2 = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
def raw( self ):
return struct.pack( self .fmt, self .e_magic, self .e_cblp, self .e_cp,
self .e_crlc, self .e_cparhdr, self .e_minalloc,
self .e_maxalloc, self .e_ss, self .e_sp, self .e_csum,
self .e_ip, self .e_cs, self .e_lfarlc, self .e_ovno,
self .e_res[ 0 ], self .e_res[ 1 ], self .e_res[ 2 ], self .e_res[ 3 ],
self .e_oemid, self .e_oeminfo,
self .e_res2[ 0 ], self .e_res2[ 1 ], self .e_res2[ 2 ], self .e_res2[ 3 ],
self .e_res2[ 4 ], self .e_res2[ 5 ], self .e_res2[ 6 ], self .e_res2[ 7 ],
self .e_res2[ 8 ], self .e_res2[ 9 ], self .e_lfanew)
# e_lfanew是文件偏移
def getPEOffset( self ):
return self .e_lfanew
def getSize( self ):
'''
DOS头,SIZE:30*2+1*4=64
:return:
'''
return struct.calcsize( self .fmt)
class IMAGE_NT_HEADER_32( object ):
def __init__( self ):
self .Signature = PE_MAGIC
self .file_header = self .IMAGE_FILE_HEADER()
self .optional_header = self .IMAGE_OPTIONAL_HEADER32()
def getSize( self ):
'''
PE文件头,SIZE:4+20+224=248
:return:
'''
return 4 + self .file_header.getSize() + self .optional_header.getSize()
def raw( self ):
return struct.pack( "
|
这个是第一版本的代码,详细描述了PE文件的组装流程
- 先初始化
DOS_HEADER_32()
- 再
IMAGE_NT_HEADER_32()
- 再定义
section字段
- 再根据
文件对齐
字段补充0对齐 - 再填写section字段的具体代码
我是直接对text字段填入了shellcode(32位),这样一个PE文件就组装好了。
但是生成出来的文件有几kb太大了。原因是文件的section需要字节对齐
1 |
text.SizeOfRawData = align(text.VirtualSize, pe.optional_header.SectionAlignment)
|
SizeOfRawData 要和SectionAlignment对齐才行,就导致了体积膨胀,修改SectionAlignment的大小竟然就无法运行了。但是看到有其他的程序SectionAlignment是可以设置得很小的
后面无意间用lordPE进行修复PE,发现它自动PE大小缩小并且能够运行了。于是我对比了两个文件找到了原因。
-
text.SizeOfRawData
是根据文件对齐
的字段来对齐的,我用节对齐
的字段对齐了。 -
第一个
sizeofimage
字段我设置的太小了(代码问题,我是根据文件的长度对齐section的) - 导入表段不用对齐文件长度,这个很神奇,text段对齐就好了,导入表字段看lordPE是直接将后面填充
\x00
的去掉了 (这个后面有加导入表的PE格式)
修改SectionAlignment的大小竟然就无法运行了
这个的主要原因是sizeofimage设置得太小了
最后sizeOfImage修改为
1 |
pe.optional_header.SizeOfImage = pe.optional_header.SizeOfHeaders + align(text.SizeOfRawData,pe.optional_header.SectionAlignment) + align(rdata.SizeOfRawData, pe.optional_header.SectionAlignment)
|
第二版 x86 PE + 导入表
上一个版本使用了shellcode执行命令,这个版本直接通过导入表来调用API
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]用Python构建一个PE文件 8335