pedump 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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) || '?'