pedump 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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