pedump 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,14 @@ class PEdump
3
3
  # http://wiki.phoenix.com/wiki/index.php/EFI_TE_IMAGE_HEADER
4
4
  # https://formats.kaitai.io/uefi_te/index.html
5
5
  # http://ho.ax/tag/efi/
6
+ # https://github.com/gdbinit/TELoader
6
7
 
8
+ EFI_IMAGE_DATA_DIRECTORY = IOStruct.new( "VV", :va, :size )
9
+ EFI_IMAGE_DATA_DIRECTORY::TYPES = %w'BASERELOC DEBUG'
10
+ EFI_IMAGE_DATA_DIRECTORY::TYPES.each_with_index do |type,idx|
11
+ EFI_IMAGE_DATA_DIRECTORY.const_set(type,idx)
12
+ end
13
+
7
14
  class EFI_TE_IMAGE_HEADER < IOStruct.new 'vvCCvVVQ',
8
15
  :Signature,
9
16
  :Machine,
@@ -15,6 +22,8 @@ class PEdump
15
22
  :ImageBase,
16
23
  :DataDirectory # readed manually: EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]
17
24
 
25
+ REAL_SIZE = SIZE + EFI_IMAGE_DATA_DIRECTORY::SIZE * 2
26
+
18
27
  attr_accessor :sections
19
28
 
20
29
  def self.read io, args = {}
@@ -28,10 +37,12 @@ class PEdump
28
37
  end
29
38
  TE = EFI_TE_IMAGE_HEADER
30
39
 
31
- EFI_IMAGE_DATA_DIRECTORY = IOStruct.new( "VV", :va, :size )
32
- EFI_IMAGE_DATA_DIRECTORY::TYPES = %w'BASERELOC DEBUG'
33
- EFI_IMAGE_DATA_DIRECTORY::TYPES.each_with_index do |type,idx|
34
- EFI_IMAGE_DATA_DIRECTORY.const_set(type,idx)
40
+ def te_shift
41
+ if @te
42
+ @te.StrippedSize - EFI_TE_IMAGE_HEADER::REAL_SIZE
43
+ else
44
+ 0
45
+ end
35
46
  end
36
47
 
37
48
  def te f=@io
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: pedump 0.6.0 ruby lib
5
+ # stub: pedump 0.6.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "pedump".freeze
9
- s.version = "0.6.0"
9
+ s.version = "0.6.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andrey \"Zed\" Zaikin".freeze]
14
- s.date = "2020-07-27"
14
+ s.date = "2020-07-28"
15
15
  s.description = "dump headers, sections, extract resources of win32 PE exe,dll,etc".freeze
16
16
  s.email = "zed.0xff@gmail.com".freeze
17
17
  s.executables = ["pedump".freeze]
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "Rakefile",
31
31
  "VERSION",
32
32
  "bin/pedump",
33
+ "data/comp_id.txt",
33
34
  "data/fs.txt",
34
35
  "data/jc-userdb.txt",
35
36
  "data/sig.bin",
@@ -50,6 +51,7 @@ Gem::Specification.new do |s|
50
51
  "lib/pedump/packer.rb",
51
52
  "lib/pedump/pe.rb",
52
53
  "lib/pedump/resources.rb",
54
+ "lib/pedump/rich.rb",
53
55
  "lib/pedump/security.rb",
54
56
  "lib/pedump/sig_parser.rb",
55
57
  "lib/pedump/te.rb",
@@ -64,7 +66,8 @@ Gem::Specification.new do |s|
64
66
  "misc/aspack/lzxdec.c",
65
67
  "misc/aspack/lzxdec.h",
66
68
  "misc/nedump.c",
67
- "pedump.gemspec"
69
+ "pedump.gemspec",
70
+ "rich.py"
68
71
  ]
69
72
  s.homepage = "http://github.com/zed-0xff/pedump".freeze
70
73
  s.licenses = ["MIT".freeze]
data/rich.py ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env python
2
+ # based on code from http://trendystephen.blogspot.be/2008/01/rich-header.html
3
+ import sys
4
+ import struct
5
+
6
+ # I'm trying not to bury the magic number...
7
+ CHECKSUM_MASK = 0x536e6144 # DanS (actuall SnaD)
8
+ RICH_TEXT = 'Rich'
9
+ RICH_TEXT_LENGTH = len(RICH_TEXT)
10
+ PE_START = 0x3c
11
+ PE_FIELD_LENGTH = 4
12
+
13
+ # most of values up to AliasObj900 are from old MSVC leak with private PDBs;
14
+ # rest is from guesses/observations
15
+ PRODID_MAP = {
16
+ 0: "Unknown",
17
+ 1: "Import0",
18
+ 2: "Linker510",
19
+ 3: "Cvtomf510",
20
+ 4: "Linker600",
21
+ 5: "Cvtomf600",
22
+ 6: "Cvtres500",
23
+ 7: "Utc11_Basic",
24
+ 8: "Utc11_C",
25
+ 9: "Utc12_Basic",
26
+ 10: "Utc12_C",
27
+ 11: "Utc12_CPP",
28
+ 12: "AliasObj60",
29
+ 13: "VisualBasic60",
30
+ 14: "Masm613",
31
+ 15: "Masm710",
32
+ 16: "Linker511",
33
+ 17: "Cvtomf511",
34
+ 18: "Masm614",
35
+ 19: "Linker512",
36
+ 20: "Cvtomf512",
37
+ 21: "Utc12_C_Std",
38
+ 22: "Utc12_CPP_Std",
39
+ 23: "Utc12_C_Book",
40
+ 24: "Utc12_CPP_Book",
41
+ 25: "Implib700",
42
+ 26: "Cvtomf700",
43
+ 27: "Utc13_Basic",
44
+ 28: "Utc13_C",
45
+ 29: "Utc13_CPP",
46
+ 30: "Linker610",
47
+ 31: "Cvtomf610",
48
+ 32: "Linker601",
49
+ 33: "Cvtomf601",
50
+ 34: "Utc12_1_Basic",
51
+ 35: "Utc12_1_C",
52
+ 36: "Utc12_1_CPP",
53
+ 37: "Linker620",
54
+ 38: "Cvtomf620",
55
+ 39: "AliasObj70",
56
+ 40: "Linker621",
57
+ 41: "Cvtomf621",
58
+ 42: "Masm615",
59
+ 43: "Utc13_LTCG_C",
60
+ 44: "Utc13_LTCG_CPP",
61
+ 45: "Masm620",
62
+ 46: "ILAsm100",
63
+ 47: "Utc12_2_Basic",
64
+ 48: "Utc12_2_C",
65
+ 49: "Utc12_2_CPP",
66
+ 50: "Utc12_2_C_Std",
67
+ 51: "Utc12_2_CPP_Std",
68
+ 52: "Utc12_2_C_Book",
69
+ 53: "Utc12_2_CPP_Book",
70
+ 54: "Implib622",
71
+ 55: "Cvtomf622",
72
+ 56: "Cvtres501",
73
+ 57: "Utc13_C_Std",
74
+ 58: "Utc13_CPP_Std",
75
+ 59: "Cvtpgd1300",
76
+ 60: "Linker622",
77
+ 61: "Linker700",
78
+ 62: "Export622",
79
+ 63: "Export700",
80
+ 64: "Masm700",
81
+ 65: "Utc13_POGO_I_C",
82
+ 66: "Utc13_POGO_I_CPP",
83
+ 67: "Utc13_POGO_O_C",
84
+ 68: "Utc13_POGO_O_CPP",
85
+ 69: "Cvtres700",
86
+ 70: "Cvtres710p",
87
+ 71: "Linker710p",
88
+ 72: "Cvtomf710p",
89
+ 73: "Export710p",
90
+ 74: "Implib710p",
91
+ 75: "Masm710p",
92
+ 76: "Utc1310p_C",
93
+ 77: "Utc1310p_CPP",
94
+ 78: "Utc1310p_C_Std",
95
+ 79: "Utc1310p_CPP_Std",
96
+ 80: "Utc1310p_LTCG_C",
97
+ 81: "Utc1310p_LTCG_CPP",
98
+ 82: "Utc1310p_POGO_I_C",
99
+ 83: "Utc1310p_POGO_I_CPP",
100
+ 84: "Utc1310p_POGO_O_C",
101
+ 85: "Utc1310p_POGO_O_CPP",
102
+ 86: "Linker624",
103
+ 87: "Cvtomf624",
104
+ 88: "Export624",
105
+ 89: "Implib624",
106
+ 90: "Linker710",
107
+ 91: "Cvtomf710",
108
+ 92: "Export710",
109
+ 93: "Implib710",
110
+ 94: "Cvtres710",
111
+ 95: "Utc1310_C",
112
+ 96: "Utc1310_CPP",
113
+ 97: "Utc1310_C_Std",
114
+ 98: "Utc1310_CPP_Std",
115
+ 99: "Utc1310_LTCG_C",
116
+ 100: "Utc1310_LTCG_CPP",
117
+ 101: "Utc1310_POGO_I_C",
118
+ 102: "Utc1310_POGO_I_CPP",
119
+ 103: "Utc1310_POGO_O_C",
120
+ 104: "Utc1310_POGO_O_CPP",
121
+ 105: "AliasObj710",
122
+ 106: "AliasObj710p",
123
+ 107: "Cvtpgd1310",
124
+ 108: "Cvtpgd1310p",
125
+ 109: "Utc1400_C",
126
+ 110: "Utc1400_CPP",
127
+ 111: "Utc1400_C_Std",
128
+ 112: "Utc1400_CPP_Std",
129
+ 113: "Utc1400_LTCG_C",
130
+ 114: "Utc1400_LTCG_CPP",
131
+ 115: "Utc1400_POGO_I_C",
132
+ 116: "Utc1400_POGO_I_CPP",
133
+ 117: "Utc1400_POGO_O_C",
134
+ 118: "Utc1400_POGO_O_CPP",
135
+ 119: "Cvtpgd1400",
136
+ 120: "Linker800",
137
+ 121: "Cvtomf800",
138
+ 122: "Export800",
139
+ 123: "Implib800",
140
+ 124: "Cvtres800",
141
+ 125: "Masm800",
142
+ 126: "AliasObj800",
143
+ 127: "PhoenixPrerelease",
144
+ 128: "Utc1400_CVTCIL_C",
145
+ 129: "Utc1400_CVTCIL_CPP",
146
+ 130: "Utc1400_LTCG_MSIL",
147
+ 131: "Utc1500_C",
148
+ 132: "Utc1500_CPP",
149
+ 133: "Utc1500_C_Std",
150
+ 134: "Utc1500_CPP_Std",
151
+ 135: "Utc1500_CVTCIL_C",
152
+ 136: "Utc1500_CVTCIL_CPP",
153
+ 137: "Utc1500_LTCG_C",
154
+ 138: "Utc1500_LTCG_CPP",
155
+ 139: "Utc1500_LTCG_MSIL",
156
+ 140: "Utc1500_POGO_I_C",
157
+ 141: "Utc1500_POGO_I_CPP",
158
+ 142: "Utc1500_POGO_O_C",
159
+ 143: "Utc1500_POGO_O_CPP",
160
+
161
+ 144: "Cvtpgd1500",
162
+ 145: "Linker900",
163
+ 146: "Export900",
164
+ 147: "Implib900",
165
+ 148: "Cvtres900",
166
+ 149: "Masm900",
167
+ 150: "AliasObj900",
168
+ 151: "Resource900",
169
+
170
+ 152: "AliasObj1000",
171
+ 154: "Cvtres1000",
172
+ 155: "Export1000",
173
+ 156: "Implib1000",
174
+ 157: "Linker1000",
175
+ 158: "Masm1000",
176
+
177
+ 170: "Utc1600_C",
178
+ 171: "Utc1600_CPP",
179
+ 172: "Utc1600_CVTCIL_C",
180
+ 173: "Utc1600_CVTCIL_CPP",
181
+ 174: "Utc1600_LTCG_C ",
182
+ 175: "Utc1600_LTCG_CPP",
183
+ 176: "Utc1600_LTCG_MSIL",
184
+ 177: "Utc1600_POGO_I_C",
185
+ 178: "Utc1600_POGO_I_CPP",
186
+ 179: "Utc1600_POGO_O_C",
187
+ 180: "Utc1600_POGO_O_CPP",
188
+
189
+ # vvv
190
+ 183: "Linker1010",
191
+ 184: "Export1010",
192
+ 185: "Implib1010",
193
+ 186: "Cvtres1010",
194
+ 187: "Masm1010",
195
+ 188: "AliasObj1010",
196
+ # ^^^
197
+
198
+ 199: "AliasObj1100",
199
+ 201: "Cvtres1100",
200
+ 202: "Export1100",
201
+ 203: "Implib1100",
202
+ 204: "Linker1100",
203
+ 205: "Masm1100",
204
+
205
+ 206: "Utc1700_C",
206
+ 207: "Utc1700_CPP",
207
+ 208: "Utc1700_CVTCIL_C",
208
+ 209: "Utc1700_CVTCIL_CPP",
209
+ 210: "Utc1700_LTCG_C ",
210
+ 211: "Utc1700_LTCG_CPP",
211
+ 212: "Utc1700_LTCG_MSIL",
212
+ 213: "Utc1700_POGO_I_C",
213
+ 214: "Utc1700_POGO_I_CPP",
214
+ 215: "Utc1700_POGO_O_C",
215
+ 216: "Utc1700_POGO_O_CPP",
216
+ }
217
+
218
+ ##
219
+ # A convenient exception to raise if the Rich Header doesn't exist.
220
+ class RichHeaderNotFoundException(Exception):
221
+ def __init__(self):
222
+ Exception.__init__(self, "Rich footer does not appear to exist")
223
+
224
+ ##
225
+ # Locate the body of the data that contains the rich header This will be
226
+ # (roughly) between 0x3c and the beginning of the PE header, but the entire
227
+ # thing up to the last checksum will be needed in order to verify the header.
228
+ def get_file_header(file_name):
229
+ f = open(file_name,'rb')
230
+
231
+ #start with 0x3c
232
+ f.seek(PE_START)
233
+ data = f.read(PE_FIELD_LENGTH)
234
+
235
+ if data == '': #File is empty, bail
236
+ raise RichHeaderNotFoundException()
237
+ end = struct.unpack('<L',data)[0] # get the value at 0x3c
238
+
239
+ f.seek(0)
240
+ data = f.read( end ) # read until that value is reached
241
+ f.close()
242
+
243
+ return data
244
+
245
+ ##
246
+ # This class assists in parsing the Rich Header from PE Files.
247
+ # The Rich Header is the section in the PE file following the dos stub but
248
+ # preceding the lfa_new header which is inserted by link.exe when building with
249
+ # the Microsoft Compilers. The Rich Heder contains the following:
250
+ # <pre>
251
+ # marker, checksum, checksum, checksum,
252
+ # R_compid_i, R_occurrence_i,
253
+ # R_compid_i+1, R_occurrence_i+1, ...
254
+ # R_compid_N-1, R_occurrence_N-1, Rich, marker
255
+ #
256
+ # marker = checksum XOR 0x536e6144
257
+ # R_compid_i is the ith compid XORed with the checksum
258
+ # R_occurrence_i is the ith occurrence XORed with the checksum
259
+ # Rich = the text string 'Rich'
260
+ # The checksum is the sum of all the PE Header values rotated by their
261
+ # offset and the sum of all compids rotated by their occurrence counts.
262
+ # </pre>
263
+ # @see _validate_checksum code for checksum calculation
264
+ class ParsedRichHeader:
265
+ ##
266
+ # Creates a ParsedRichHeader from the specified PE File.
267
+ # @throws RichHeaderNotFoundException if the file does not contain a rich header
268
+ # @param file_name The PE File to be parsed
269
+ def __init__(self, file_name):
270
+ ## The file that was parsed
271
+ self.file_name = file_name
272
+ self._parse( file_name )
273
+
274
+ ##
275
+ # Used internally to parse the PE File and extract Rich Header data.
276
+ # Initializes self.compids and self.valid_checksum.
277
+ # @param file_name The PE File to be parsed
278
+ # @throws RichHeaderNotFoundException if the file does not contain a rich header
279
+ def _parse(self,file_name):
280
+ #make sure there is a header:
281
+ data = get_file_header( file_name )
282
+
283
+ compid_end_index = data.find(RICH_TEXT)
284
+ if compid_end_index == -1:
285
+ raise RichHeaderNotFoundException()
286
+
287
+ rich_offset = compid_end_index + RICH_TEXT_LENGTH
288
+
289
+ checksum_text = data[rich_offset:rich_offset+4]
290
+ checksum_value = struct.unpack('<L', checksum_text)[0]
291
+ #start marker denotes the beginning of the rich header
292
+ start_marker = struct.pack('<LLLL',checksum_value ^ CHECKSUM_MASK, checksum_value, checksum_value, checksum_value )[0]
293
+
294
+ rich_header_start = data.find(start_marker)
295
+ if rich_header_start == -1:
296
+ raise RichHeaderNotFoundException()
297
+
298
+ compid_start_index = rich_header_start + 16 # move past the marker and 3 checksums
299
+
300
+ compids = dict()
301
+ for i in range(compid_start_index, compid_end_index, 8):
302
+ compid = struct.unpack('<L',data[i:i+4])[0] ^ checksum_value
303
+ count = struct.unpack('<L',data[i+4:i+8])[0] ^ checksum_value
304
+ compids[compid]=count
305
+
306
+ ## A dictionary of compids and their occurrence counts
307
+ self.compids = compids
308
+ ## A value for later reference to see if the checksum was valid
309
+ self.valid_checksum = self._validate_checksum( data, rich_header_start, checksum_value )
310
+
311
+ ##
312
+ # Compute the checksum value and see if it matches the checksum stored in
313
+ # the Rich Header.
314
+ # The checksum is the sum of all the PE Header values rotated by their
315
+ # offset and the sum of all compids rotated by their occurrence counts
316
+ # @param data A blob of binary data that corresponds to the PE Header data
317
+ # @param rich_header_start The offset to marker, checksum, checksum, checksum
318
+ # @returns True if the checksum is valid, false otherwise
319
+ def _validate_checksum(self, data, rich_header_start, checksum):
320
+
321
+ #initialize the checksum offset at which the rich header is located
322
+ cksum = rich_header_start
323
+
324
+ #add the value from the pe header after rotating the value by its offset in the pe header
325
+ for i in range(0,rich_header_start):
326
+ if PE_START <= i <= PE_START+PE_FIELD_LENGTH-1:
327
+ continue
328
+ temp = ord(data[i])
329
+ cksum+= ((temp << (i%32)) | (temp >> (32-(i%32))) & 0xff)
330
+ cksum &=0xffffffff
331
+
332
+ #add each compid to the checksum after rotating it by its occurrence count
333
+ for k in self.compids.keys():
334
+ cksum += (k << self.compids[k]%32 | k >> ( 32 - (self.compids[k]%32)))
335
+ cksum &=0xffffffff
336
+
337
+ ## A convenient place for storing the checksum that was computing during checksum validation
338
+ self.checksum = cksum
339
+
340
+ return cksum == checksum
341
+
342
+ if __name__ == "__main__":
343
+ ph = ParsedRichHeader(sys.argv[1])
344
+ print ("PRODID name build count")
345
+ for key in ph.compids.keys():
346
+ count = ph.compids[key]
347
+ prodid, build = (key>>16), key&0xFFFF
348
+ prodid_name = PRODID_MAP[prodid] if prodid in PRODID_MAP else "<unknown>"
349
+ print ('%6d %-15s %5d %5d' % (prodid, prodid_name, build, count))
350
+ if ph.valid_checksum:
351
+ print ("Checksum valid")
352
+ else:
353
+ print("Checksum not valid!")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pedump
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey "Zed" Zaikin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-27 00:00:00.000000000 Z
11
+ date: 2020-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -155,6 +155,7 @@ files:
155
155
  - Rakefile
156
156
  - VERSION
157
157
  - bin/pedump
158
+ - data/comp_id.txt
158
159
  - data/fs.txt
159
160
  - data/jc-userdb.txt
160
161
  - data/sig.bin
@@ -175,6 +176,7 @@ files:
175
176
  - lib/pedump/packer.rb
176
177
  - lib/pedump/pe.rb
177
178
  - lib/pedump/resources.rb
179
+ - lib/pedump/rich.rb
178
180
  - lib/pedump/security.rb
179
181
  - lib/pedump/sig_parser.rb
180
182
  - lib/pedump/te.rb
@@ -190,6 +192,7 @@ files:
190
192
  - misc/aspack/lzxdec.h
191
193
  - misc/nedump.c
192
194
  - pedump.gemspec
195
+ - rich.py
193
196
  homepage: http://github.com/zed-0xff/pedump
194
197
  licenses:
195
198
  - MIT