pedump 0.3.3 → 0.4.0

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.
File without changes
@@ -40,6 +40,8 @@ class PEdump
40
40
 
41
41
  VERSION = Version::STRING
42
42
 
43
+ @@logger = nil
44
+
43
45
  def initialize fname, params = {}
44
46
  @fname = fname
45
47
  @force = params[:force]
@@ -62,6 +64,17 @@ class PEdump
62
64
  end
63
65
  end
64
66
 
67
+ module Readable
68
+ def read file, size = nil
69
+ size ||= const_get 'SIZE'
70
+ data = file.read(size).to_s
71
+ if data.size < size && PEdump.logger
72
+ PEdump.logger.error "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
73
+ end
74
+ new(*data.unpack(const_get('FORMAT')))
75
+ end
76
+ end
77
+
65
78
  class << self
66
79
  def logger; @@logger; end
67
80
  def logger= l; @@logger=l; end
@@ -89,14 +102,7 @@ class PEdump
89
102
  to_a.all?{ |t| t == 0 || t.nil? || t.to_s.tr("\x00","").empty? }
90
103
  end
91
104
  end
92
- def x.read file, size = nil
93
- size ||= const_get 'SIZE'
94
- data = file.read(size).to_s
95
- if data.size < size && PEdump.logger
96
- PEdump.logger.error "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
97
- end
98
- new(*data.unpack(const_get('FORMAT')))
99
- end
105
+ x.extend Readable
100
106
  end
101
107
  end
102
108
  end
@@ -140,10 +146,13 @@ class PEdump
140
146
  def x64?
141
147
  ifh && ifh.Machine == 0x8664
142
148
  end
149
+ def dll?
150
+ ifh && ifh.flags.include?('DLL')
151
+ end
143
152
  end
144
153
 
145
154
  # http://msdn.microsoft.com/en-us/library/ms809762.aspx
146
- IMAGE_FILE_HEADER = create_struct( 'v2V3v2',
155
+ class IMAGE_FILE_HEADER < create_struct( 'v2V3v2',
147
156
  :Machine, # w
148
157
  :NumberOfSections, # w
149
158
  :TimeDateStamp, # dw
@@ -152,19 +161,97 @@ class PEdump
152
161
  :SizeOfOptionalHeader, # w
153
162
  :Characteristics # w
154
163
  )
155
- class IMAGE_FILE_HEADER
164
+ # Characteristics, http://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=VS.85).aspx)
165
+ FLAGS = {
166
+ 0x0001 => 'RELOCS_STRIPPED', # Relocation information was stripped from the file.
167
+ # The file must be loaded at its preferred base address.
168
+ # If the base address is not available, the loader reports an error.
169
+ 0x0002 => 'EXECUTABLE_IMAGE',
170
+ 0x0004 => 'LINE_NUMS_STRIPPED',
171
+ 0x0008 => 'LOCAL_SYMS_STRIPPED',
172
+ 0x0010 => 'AGGRESIVE_WS_TRIM', # Aggressively trim the working set. This value is obsolete as of Windows 2000.
173
+ 0x0020 => 'LARGE_ADDRESS_AWARE', # The application can handle addresses larger than 2 GB.
174
+ 0x0040 => '16BIT_MACHINE',
175
+ 0x0080 => 'BYTES_REVERSED_LO', # The bytes of the word are reversed. This flag is obsolete.
176
+ 0x0100 => '32BIT_MACHINE',
177
+ 0x0200 => 'DEBUG_STRIPPED',
178
+ 0x0400 => 'REMOVABLE_RUN_FROM_SWAP',
179
+ 0x0800 => 'NET_RUN_FROM_SWAP',
180
+ 0x1000 => 'SYSTEM',
181
+ 0x2000 => 'DLL',
182
+ 0x4000 => 'UP_SYSTEM_ONLY', # The file should be run only on a uniprocessor computer.
183
+ 0x8000 => 'BYTES_REVERSED_HI' # The bytes of the word are reversed. This flag is obsolete.
184
+ }
185
+
156
186
  def initialize *args
157
187
  super
158
188
  self.TimeDateStamp = Time.at(self.TimeDateStamp)
159
189
  end
160
- def method_missing mname
161
- mname = mname.to_s.capitalize
162
- self.send(mname) if self.respond_to?(mname)
190
+ def flags
191
+ FLAGS.find_all{ |k,v| (self.Characteristics & k) != 0 }.map(&:last)
192
+ end
193
+ end
194
+
195
+ module IMAGE_OPTIONAL_HEADER
196
+ # DllCharacteristics, http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx)
197
+ FLAGS = {
198
+ 0x0001 => '0x01', # reserved
199
+ 0x0002 => '0x02', # reserved
200
+ 0x0004 => '0x04', # reserved
201
+ 0x0008 => '0x08', # reserved
202
+ 0x0010 => '0x10', # ?
203
+ 0x0020 => '0x20', # ?
204
+ 0x0040 => 'DYNAMIC_BASE',
205
+ 0x0080 => 'FORCE_INTEGRITY',
206
+ 0x0100 => 'NX_COMPAT',
207
+ 0x0200 => 'NO_ISOLATION',
208
+ 0x0400 => 'NO_SEH',
209
+ 0x0800 => 'NO_BIND',
210
+ 0x1000 => '0x1000', # ?
211
+ 0x2000 => 'WDM_DRIVER',
212
+ 0x4000 => '0x4000', # ?
213
+ 0x8000 => 'TERMINAL_SERVER_AWARE'
214
+ }
215
+ def initialize *args
216
+ super
217
+ self.extend InstanceMethods
218
+ end
219
+ def self.included base
220
+ base.extend ClassMethods
221
+ end
222
+ module ClassMethods
223
+ def read file, size = nil
224
+ usual_size = self.const_get('USUAL_SIZE')
225
+ cSIZE = self.const_get 'SIZE'
226
+ cFORMAT = self.const_get 'FORMAT'
227
+ size ||= cSIZE
228
+ PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
229
+ new(*file.read([size,cSIZE].min).to_s.unpack(cFORMAT)).tap do |ioh|
230
+ ioh.DataDirectory = []
231
+
232
+ # check if "...this address is outside the memory mapped file and is zeroed by the OS"
233
+ # see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
234
+ ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
235
+
236
+ # http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
237
+ # maximum of 0x10 entries, even if bigger
238
+ [0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
239
+ ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
240
+ ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
241
+ end
242
+ #ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
243
+ end
244
+ end
245
+ end
246
+ module InstanceMethods
247
+ def flags
248
+ FLAGS.find_all{ |k,v| (self.DllCharacteristics & k) != 0 }.map(&:last)
249
+ end
163
250
  end
164
251
  end
165
252
 
166
253
  # http://msdn.microsoft.com/en-us/library/ms809762.aspx
167
- IMAGE_OPTIONAL_HEADER = create_struct( 'vC2V9v6V4v2V6',
254
+ class IMAGE_OPTIONAL_HEADER32 < create_struct( 'vC2V9v6V4v2V6',
168
255
  :Magic, # w
169
256
  :MajorLinkerVersion, :MinorLinkerVersion, # 2b
170
257
  :SizeOfCode, :SizeOfInitializedData, :SizeOfUninitializedData, :AddressOfEntryPoint, # 9dw
@@ -177,9 +264,12 @@ class PEdump
177
264
  :LoaderFlags, :NumberOfRvaAndSizes,
178
265
  :DataDirectory # readed manually
179
266
  )
267
+ USUAL_SIZE = 224
268
+ include IMAGE_OPTIONAL_HEADER
269
+ end
180
270
 
181
271
  # http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=VS.85).aspx)
182
- IMAGE_OPTIONAL_HEADER64 = create_struct( 'vC2V5QV2v6V4v2Q4V2',
272
+ class IMAGE_OPTIONAL_HEADER64 < create_struct( 'vC2V5QV2v6V4v2Q4V2',
183
273
  :Magic, # w
184
274
  :MajorLinkerVersion, :MinorLinkerVersion, # 2b
185
275
  :SizeOfCode, :SizeOfInitializedData, :SizeOfUninitializedData, :AddressOfEntryPoint, :BaseOfCode, # 5dw
@@ -193,49 +283,8 @@ class PEdump
193
283
  :LoaderFlags, :NumberOfRvaAndSizes, #2dw
194
284
  :DataDirectory # readed manually
195
285
  )
196
-
197
- class IMAGE_OPTIONAL_HEADER
198
- def self.read file, size = SIZE
199
- usual_size = 224
200
- PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
201
- new(*file.read([size,SIZE].min).to_s.unpack(FORMAT)).tap do |ioh|
202
- ioh.DataDirectory = []
203
-
204
- # check if "...this address is outside the memory mapped file and is zeroed by the OS"
205
- # see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
206
- ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
207
-
208
- # http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
209
- # maximum of 0x10 entries, even if bigger
210
- [0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
211
- ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
212
- ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
213
- end
214
- #ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
215
- end
216
- end
217
- end
218
-
219
- class IMAGE_OPTIONAL_HEADER64
220
- def self.read file, size = SIZE
221
- usual_size = 240
222
- PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
223
- new(*file.read([size,SIZE].min).unpack(FORMAT)).tap do |ioh|
224
- ioh.DataDirectory = []
225
-
226
- # check if "...this address is outside the memory mapped file and is zeroed by the OS"
227
- # see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
228
- ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
229
-
230
- # http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
231
- # maximum of 0x10 entries, even if bigger
232
- [0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
233
- ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
234
- ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
235
- end
236
- #ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
237
- end
238
- end
286
+ USUAL_SIZE = 240
287
+ include IMAGE_OPTIONAL_HEADER
239
288
  end
240
289
 
241
290
  IMAGE_DATA_DIRECTORY = create_struct( "VV", :va, :size, :type )
@@ -418,7 +467,7 @@ class PEdump
418
467
  if pe.x64?
419
468
  pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
420
469
  else
421
- pe.image_optional_header = IMAGE_OPTIONAL_HEADER.read(f, pe.ifh.SizeOfOptionalHeader)
470
+ pe.image_optional_header = IMAGE_OPTIONAL_HEADER32.read(f, pe.ifh.SizeOfOptionalHeader)
422
471
  end
423
472
  end
424
473
 
@@ -444,7 +493,7 @@ class PEdump
444
493
 
445
494
  # OPTIONAL: assigns @mz, @rich_hdr, @pe, etc
446
495
  def dump f=nil
447
- f ? _dump_handle(f) : File.open(@fname){ |f| _dump_handle(f) }
496
+ f ? _dump_handle(f) : File.open(@fname,'rb'){ |f| _dump_handle(f) }
448
497
  self
449
498
  end
450
499
 
@@ -453,6 +502,7 @@ class PEdump
453
502
  resources(h) # includes pe(h)
454
503
  imports h
455
504
  exports h
505
+ packer h
456
506
  end
457
507
 
458
508
  def data_directory f=nil
@@ -795,7 +845,7 @@ class PEdump
795
845
  f.seek file_offset
796
846
  data << CUR_ICO_HEADER.read(f)
797
847
  nRead = CUR_ICO_HEADER::SIZE
798
- data.last.wNumImages.times do
848
+ data.last.wNumImages.to_i.times do
799
849
  if nRead >= self.size
800
850
  PEdump.logger.error "[!] refusing to read CURDIRENTRY beyond resource size"
801
851
  break
@@ -807,7 +857,7 @@ class PEdump
807
857
  f.seek file_offset
808
858
  data << CUR_ICO_HEADER.read(f)
809
859
  nRead = CUR_ICO_HEADER::SIZE
810
- data.last.wNumImages.times do
860
+ data.last.wNumImages.to_i.times do
811
861
  if nRead >= self.size
812
862
  PEdump.logger.error "[!] refusing to read ICODIRENTRY beyond resource size"
813
863
  break
@@ -839,6 +889,10 @@ class PEdump
839
889
  end
840
890
  end
841
891
  # XXX: check if readed strings summary length is less than resource data length
892
+ when 'VERSION'
893
+ require 'pedump/version_info'
894
+ f.seek file_offset
895
+ data << PEdump::VS_VERSIONINFO.read(f)
842
896
  end
843
897
 
844
898
  data.delete_if do |x|
@@ -854,7 +908,7 @@ class PEdump
854
908
  self.valid =
855
909
  case type
856
910
  when 'BITMAP','ICON','CURSOR'
857
- data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? }
911
+ data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? } || data.first == 'PNG'
858
912
  else
859
913
  true
860
914
  end
@@ -929,6 +983,10 @@ class PEdump
929
983
  @resources ||= _scan_resources(f)
930
984
  end
931
985
 
986
+ def version_info f=nil
987
+ resources(f) && resources(f).find_all{ |res| res.type == 'VERSION' }.map(&:data).flatten
988
+ end
989
+
932
990
  def _scan_resources f=nil, dir=nil
933
991
  dir ||= resource_directory(f)
934
992
  return nil unless dir
@@ -978,6 +1036,9 @@ class PEdump
978
1036
  if !(va=@pe.ioh.AddressOfEntryPoint)
979
1037
  logger.error "[?] can't find EntryPoint RVA"
980
1038
  nil
1039
+ elsif va == 0 && @pe.dll?
1040
+ logger.debug "[.] it's a DLL with no EntryPoint"
1041
+ nil
981
1042
  elsif !(ofs = va2file(va))
982
1043
  logger.error "[?] can't find EntryPoint RVA (0x#{va.to_s(16)}) file offset"
983
1044
  nil
@@ -987,7 +1048,7 @@ class PEdump
987
1048
  logger.error "[?] no packer definitions found"
988
1049
  nil
989
1050
  else
990
- Packer.of(f, ofs)
1051
+ Packer.of f, :ep_offset => ofs
991
1052
  end
992
1053
  end
993
1054
  end
@@ -1,4 +1,5 @@
1
1
  require 'pedump'
2
+ require 'pedump/packer'
2
3
  require 'optparse'
3
4
 
4
5
  unless Object.instance_methods.include?(:try)
@@ -14,7 +15,7 @@ class PEdump::CLI
14
15
 
15
16
  KNOWN_ACTIONS = (
16
17
  %w'mz dos_stub rich pe data_directory sections' +
17
- %w'strings resources resource_directory imports exports packer web packer_only'
18
+ %w'strings resources resource_directory imports exports version_info packer web packer_only'
18
19
  ).map(&:to_sym)
19
20
 
20
21
  DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only'.map(&:to_sym)
@@ -27,18 +28,20 @@ class PEdump::CLI
27
28
 
28
29
  def run
29
30
  @actions = []
30
- @options = { :format => :table }
31
+ @options = { :format => :table, :verbose => 0 }
31
32
  optparser = OptionParser.new do |opts|
32
33
  opts.banner = "Usage: pedump [options]"
33
34
 
34
- opts.on "-V", "--version", "Print version information and exit" do
35
+ opts.on "--version", "Print version information and exit" do
35
36
  puts PEdump::VERSION
36
37
  exit
37
38
  end
38
- opts.on "-v", "--[no-]verbose", "Run verbosely" do |v|
39
- @options[:verbose] ||= 0
39
+ opts.on "-v", "--verbose", "Run verbosely","(can be used multiple times)" do |v|
40
40
  @options[:verbose] += 1
41
41
  end
42
+ opts.on "-q", "--quiet", "Silent any warnings","(can be used multiple times)" do |v|
43
+ @options[:verbose] -= 1
44
+ end
42
45
  opts.on "-F", "--force", "Try to dump by all means","(can cause exceptions & heavy wounds)" do |v|
43
46
  @options[:force] ||= 0
44
47
  @options[:force] += 1
@@ -48,11 +51,25 @@ class PEdump::CLI
48
51
  @options[:format] = v
49
52
  end
50
53
  KNOWN_ACTIONS.each do |t|
51
- opts.on "--#{t.to_s.tr('_','-')}", eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }")
54
+ a = [
55
+ "--#{t.to_s.tr('_','-')}",
56
+ eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }")
57
+ ]
58
+ a.unshift(a[0][1,2].upcase) if a[0] =~ /--(((ex|im)port|section|resource)s|version-info)/
59
+ a.unshift(a[0][1,2]) if a[0] =~ /--strings/
60
+ opts.on *a
61
+ end
62
+
63
+ opts.on "--deep", "packer deep scan, significantly slower" do
64
+ @options[:deep] ||= 0
65
+ @options[:deep] += 1
66
+ PEdump::Packer.default_deep = @options[:deep]
52
67
  end
68
+
53
69
  opts.on '-P', "--packer-only", "packer/compiler detect only,","mimics 'file' command output" do
54
70
  @actions << :packer_only
55
71
  end
72
+
56
73
  opts.on "--all", "Dump all but resource-directory (default)" do
57
74
  @actions = DEFAULT_ALL_ACTIONS
58
75
  end
@@ -87,11 +104,7 @@ class PEdump::CLI
87
104
  @file_name = fname
88
105
 
89
106
  File.open(fname,'rb') do |f|
90
- @pedump = PEdump.new(fname, :force => @options[:force]).tap do |x|
91
- if @options[:verbose]
92
- x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
93
- end
94
- end
107
+ @pedump = create_pedump fname
95
108
 
96
109
  next if !@options[:force] && !@pedump.mz(f)
97
110
 
@@ -109,18 +122,34 @@ class PEdump::CLI
109
122
  # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
110
123
  end
111
124
 
125
+ def create_pedump fname
126
+ PEdump.new(fname, :force => @options[:force]).tap do |x|
127
+ x.logger.level =
128
+ case @options[:verbose]
129
+ when -100..-3
130
+ Logger::FATAL + 1
131
+ when -2
132
+ Logger::FATAL
133
+ when -1
134
+ Logger::ERROR
135
+ when 0
136
+ Logger::WARN # default
137
+ when 1
138
+ Logger::INFO
139
+ when 2..100
140
+ Logger::DEBUG
141
+ end
142
+ end
143
+ end
144
+
112
145
  def dump_packer_only fnames
113
146
  max_fname_len = fnames.map(&:size).max
114
147
  fnames.each do |fname|
115
148
  File.open(fname,'rb') do |f|
116
- @pedump = PEdump.new(fname, :force => @options[:force]).tap do |x|
117
- if @options[:verbose]
118
- x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
119
- end
120
- end
149
+ @pedump = create_pedump fname
121
150
  packers = @pedump.packers(f)
122
151
  pname = Array(packers).first.try(:packer).try(:name)
123
- pname ||= "unknown" if @options[:verbose]
152
+ pname ||= "unknown" if @options[:verbose] > 0
124
153
  printf("%-*s %s\n", max_fname_len+1, "#{fname}:", pname) if pname
125
154
  end
126
155
  end
@@ -248,6 +277,8 @@ class PEdump::CLI
248
277
  return dump_imports(data)
249
278
  when :exports
250
279
  return dump_exports(data)
280
+ when :version_info
281
+ return dump_version_info(data)
251
282
  else
252
283
  if data.is_a?(Struct) && data.respond_to?(:pack)
253
284
  data = data.pack
@@ -298,8 +329,22 @@ class PEdump::CLI
298
329
  :Subsystem => PEdump::IMAGE_SUBSYSTEMS
299
330
  }
300
331
 
332
+ def _flags2string flags
333
+ return '' if !flags || flags.empty?
334
+ a = [flags.shift.dup]
335
+ flags.each do |f|
336
+ if (a.last.size + f.size) < 40
337
+ a.last << ", " << f
338
+ else
339
+ a << f.dup
340
+ end
341
+ end
342
+ a.join("\n"+ ' '*58)
343
+ end
344
+
301
345
  def dump_generic_table data
302
346
  data.each_pair do |k,v|
347
+ next if [:DataDirectory, :section_table].include?(k)
303
348
  case v
304
349
  when Numeric
305
350
  case k
@@ -309,20 +354,25 @@ class PEdump::CLI
309
354
  when /TimeDateStamp/
310
355
  printf "%30s: %24s\n", k, Time.at(v).strftime('"%Y-%m-%d %H:%M:%S"')
311
356
  else
357
+ comment = ''
312
358
  if COMMENTS[k]
313
- printf "%30s: %10d %12s %s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)),
314
- COMMENTS[k][v] || (COMMENTS[k].is_a?(Hash) ? COMMENTS[k]['default'] : '') || ''
315
- else
316
- printf "%30s: %10d %12s\n", k, v, v<10 ? v : ("0x"+v.to_s(16))
359
+ comment = COMMENTS[k][v] || (COMMENTS[k].is_a?(Hash) ? COMMENTS[k]['default'] : '') || ''
360
+ elsif data.is_a?(PEdump::IMAGE_FILE_HEADER) && k == :Characteristics
361
+ comment = _flags2string(data.flags)
362
+ elsif k == :DllCharacteristics
363
+ comment = _flags2string(data.flags)
317
364
  end
365
+ comment.strip!
366
+ comment = " #{comment}" unless comment.empty?
367
+ printf "%30s: %10d %12s%s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)), comment
318
368
  end
319
369
  when Struct
370
+ # IMAGE_FILE_HEADER:
371
+ # IMAGE_OPTIONAL_HEADER:
320
372
  printf "\n# %s:\n", v.class.to_s.split('::').last
321
373
  dump_table v
322
374
  when Time
323
375
  printf "%30s: %24s\n", k, v.strftime('"%Y-%m-%d %H:%M:%S"')
324
- when Array
325
- next if %w'DataDirectory section_table'.include?(k)
326
376
  else
327
377
  printf "%30s: %24s\n", k, v.to_s.inspect
328
378
  end
@@ -348,6 +398,8 @@ class PEdump::CLI
348
398
  dump_imports data
349
399
  when PEdump::Packer::Match
350
400
  dump_packers data
401
+ when PEdump::VS_VERSIONINFO
402
+ dump_version_info data
351
403
  else
352
404
  puts "[?] don't know how to dump: #{data.inspect[0,50]}" unless data.empty?
353
405
  end
@@ -360,8 +412,66 @@ class PEdump::CLI
360
412
  end
361
413
  end
362
414
 
415
+ def dump_version_info data
416
+ if @options[:format] != :table
417
+ File.open(@file_name,'rb') do |f|
418
+ @pedump.resources.find_all{ |r| r.type == 'VERSION'}.each do |res|
419
+ f.seek res.file_offset
420
+ data = f.read(res.size)
421
+ dump data
422
+ end
423
+ end
424
+ return
425
+ end
426
+
427
+ fmt = " %-20s: %s\n"
428
+ data.each do |vi|
429
+ puts "# VS_FIXEDFILEINFO:"
430
+
431
+ if @options[:verbose] > 0 || vi.Value.dwSignature != 0xfeef04bd
432
+ printf(fmt, "Signature", "0x#{vi.Value.dwSignature.to_s(16)}")
433
+ end
434
+
435
+ printf fmt, 'FileVersion', [
436
+ vi.Value.dwFileVersionMS >> 16,
437
+ vi.Value.dwFileVersionMS & 0xffff,
438
+ vi.Value.dwFileVersionLS >> 16,
439
+ vi.Value.dwFileVersionLS & 0xffff
440
+ ].join('.')
441
+
442
+ printf fmt, 'ProductVersion', [
443
+ vi.Value.dwProductVersionMS >> 16,
444
+ vi.Value.dwProductVersionMS & 0xffff,
445
+ vi.Value.dwProductVersionLS >> 16,
446
+ vi.Value.dwProductVersionLS & 0xffff
447
+ ].join('.')
448
+
449
+ vi.Value.each_pair do |k,v|
450
+ next if k[/[ML]S$/] || k == :valid || k == :dwSignature
451
+ printf fmt, k.to_s.sub(/^dw/,''), v > 9 ? "0x#{v.to_s(16)}" : v
452
+ end
453
+
454
+ vi.Children.each do |file_info|
455
+ case file_info
456
+ when PEdump::StringFileInfo
457
+ file_info.Children.each do |string_table|
458
+ puts "\n# StringTable #{string_table.szKey}:"
459
+ string_table.Children.each do |string|
460
+ printf fmt, string.szKey, string.Value.inspect
461
+ end
462
+ end
463
+ when PEdump::VarFileInfo
464
+ puts
465
+ printf fmt, "VarFileInfo", '[ 0x' + file_info.Children.Value.map{|v| v.to_s(16)}.join(", 0x") + ' ]'
466
+ else
467
+ puts "[?] unknown child type: #{file_info.inspect}, use -fi to inspect"
468
+ end
469
+ end
470
+ end
471
+ end
472
+
363
473
  def dump_packers data
364
- if @options[:verbose]
474
+ if @options[:verbose] > 0
365
475
  data.each do |p|
366
476
  printf "%8x %4d %s\n", p.offset, p.packer.size, p.packer.name
367
477
  end
@@ -379,7 +489,7 @@ class PEdump::CLI
379
489
  data.MajorVersion, data.MinorVersion,
380
490
  data.Base
381
491
 
382
- if @options[:verbose]
492
+ if @options[:verbose] > 0
383
493
  [%w'Names', %w'EntryPoints Functions', %w'Ordinals NameOrdinals'].each do |x|
384
494
  va = data["AddressOf"+x.last]
385
495
  ofs = @pedump.va2file(va) || '?'