pedump 0.5.2 → 0.6.2

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.
data/lib/pedump.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  require 'stringio'
3
3
  require 'iostruct'
4
4
  require 'zhexdump'
5
+ require 'set'
5
6
 
6
7
  unless Object.new.respond_to?(:try) && nil.respond_to?(:try)
7
8
  require 'pedump/core_ext/try'
@@ -16,6 +17,7 @@ require 'pedump/security'
16
17
  require 'pedump/packer'
17
18
  require 'pedump/ne'
18
19
  require 'pedump/ne/version_info'
20
+ require 'pedump/te'
19
21
 
20
22
  # pedump.rb by zed_0xff
21
23
  #
@@ -27,6 +29,10 @@ class PEdump
27
29
 
28
30
  VERSION = Version::STRING
29
31
  MAX_ERRORS = 100
32
+ MAX_IMAGE_IMPORT_DESCRIPTORS = 1000
33
+ MAX_EXPORT_NUMBER_OF_NAMES = 16384 # got 7977 in https://pedump.me/03ad7400080678c6b1984f995d36fd04
34
+ GOOD_FUNCTION_NAME_RE = /\A[\x21-\x7f]+\Z/
35
+ SUPPORTED_SIGNATURES = ['MZ', 'ZM', 'VZ']
30
36
 
31
37
  @@logger = nil
32
38
 
@@ -255,7 +261,7 @@ class PEdump
255
261
 
256
262
  # http://ntcore.com/files/richsign.htm
257
263
  class RichHdr < String
258
- attr_accessor :offset, :key # xor key
264
+ attr_accessor :offset, :skip, :key # xor key
259
265
 
260
266
  class Entry < Struct.new(:version,:id,:times)
261
267
  def inspect
@@ -264,8 +270,21 @@ class PEdump
264
270
  end
265
271
 
266
272
  def self.from_dos_stub stub
273
+ #stub.hexdump
267
274
  key = stub[stub.index('Rich')+4,4]
268
275
  start_idx = stub.index(key.xor('DanS'))
276
+ skip = 0
277
+ if start_idx
278
+ skip = 4
279
+ else
280
+ PEdump.logger.warn "[?] cannot find rich_hdr start_idx, using heuristics"
281
+ start_idx = stub.index("$\x00\x00\x00\x00\x00\x00\x00")
282
+ unless start_idx
283
+ PEdump.logger.warn "[?] heuristics failed :("
284
+ return nil
285
+ end
286
+ start_idx += 8
287
+ end
269
288
  end_idx = stub.index('Rich')+8
270
289
  if stub[end_idx..-1].tr("\x00",'') != ''
271
290
  t = stub[end_idx..-1]
@@ -273,14 +292,16 @@ class PEdump
273
292
  PEdump.logger.error "[!] non-zero dos stub after rich_hdr: #{t.inspect}"
274
293
  return nil
275
294
  end
295
+ #stub[start_idx, end_idx-start_idx].hexdump
276
296
  RichHdr.new(stub[start_idx, end_idx-start_idx]).tap do |x|
277
297
  x.key = key
278
298
  x.offset = stub.offset + start_idx
299
+ x.skip = skip
279
300
  end
280
301
  end
281
302
 
282
303
  def dexor
283
- self[4..-9].sub(/\A(#{Regexp::escape(key)}){3}/,'').xor(key)
304
+ self[skip..-9].sub(/\A(#{Regexp::escape(key)}){3}/,'').xor(key)
284
305
  end
285
306
 
286
307
  def decode
@@ -318,9 +339,9 @@ class PEdump
318
339
  @mz ||= f && MZ.read(f).tap do |mz|
319
340
  if mz.signature != 'MZ' && mz.signature != 'ZM'
320
341
  if @force
321
- logger.warn "[?] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}"
342
+ #logger.warn "[?] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}"
322
343
  else
323
- logger.error "[!] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}. (not forced)"
344
+ #logger.error "[!] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}. (not forced)"
324
345
  return nil
325
346
  end
326
347
  end
@@ -376,6 +397,13 @@ class PEdump
376
397
  def va2file va, h={}
377
398
  return nil if va.nil?
378
399
 
400
+ va0 = va # save for log output of original addr
401
+ if pe?
402
+ # most common case, do nothing
403
+ elsif te?
404
+ va = va - te_shift()
405
+ end
406
+
379
407
  sections.each do |s|
380
408
  if (s.VirtualAddress...(s.VirtualAddress+s.VirtualSize)).include?(va)
381
409
  offset = va - s.VirtualAddress
@@ -407,9 +435,9 @@ class PEdump
407
435
  # TODO: not all VirtualAdresses == 0 case
408
436
 
409
437
  if h[:quiet]
410
- logger.debug "[?] can't find file_offset of VA 0x#{va.to_i.to_s(16)} (quiet=true)"
438
+ logger.debug "[?] can't find file_offset of VA 0x#{va0.to_i.to_s(16)} (quiet=true)"
411
439
  else
412
- logger.error "[?] can't find file_offset of VA 0x#{va.to_i.to_s(16)}"
440
+ logger.error "[?] can't find file_offset of VA 0x#{va0.to_i.to_s(16)}"
413
441
  end
414
442
  nil
415
443
  end
@@ -427,16 +455,22 @@ class PEdump
427
455
  end
428
456
 
429
457
  def _dump_handle h
430
- return unless pe(h) # also calls mz(h)
431
- rich_hdr h
432
- resources h
433
- imports h # also calls tls(h)
434
- exports h
435
- packer h
458
+ if pe(h) # also calls mz(h)
459
+ rich_hdr h
460
+ resources h
461
+ imports h # also calls tls(h)
462
+ exports h
463
+ packer h
464
+ elsif te(h)
465
+ end
436
466
  end
437
467
 
438
468
  def data_directory f=@io
439
- pe(f) && pe.ioh && pe.ioh.DataDirectory
469
+ if pe(f)
470
+ pe.ioh && pe.ioh.DataDirectory
471
+ elsif te(f)
472
+ te.DataDirectory
473
+ end
440
474
  end
441
475
 
442
476
  def sections f=@io
@@ -444,16 +478,52 @@ class PEdump
444
478
  pe.section_table
445
479
  elsif ne(f)
446
480
  ne.segments
481
+ elsif te(f)
482
+ te.sections
447
483
  end
448
484
  end
449
485
  alias :section_table :sections
450
486
 
451
- def ne?
452
- @pe ? false : (@ne ? true : (pe ? false : (ne ? true : false)))
487
+ def supported_file? f=@io
488
+ pos = f.tell
489
+ sig = f.read(2)
490
+ f.seek(pos)
491
+ if SUPPORTED_SIGNATURES.include?(sig)
492
+ true
493
+ else
494
+ unless @not_supported_sig_warned
495
+ msg = "no supported signature. want: #{SUPPORTED_SIGNATURES.join("/")}, got: #{sig.inspect}"
496
+ if @force
497
+ logger.warn "[?] #{msg}"
498
+ else
499
+ logger.error "[!] #{msg}. (not forced)"
500
+ end
501
+ @not_supported_sig_warned = true
502
+ end
503
+ false
504
+ end
505
+ end
506
+
507
+ def _detect_format
508
+ return :pe if @pe
509
+ return :ne if @ne
510
+ return :te if @te
511
+ return :pe if pe()
512
+ return :ne if ne()
513
+ return :te if te()
514
+ nil
453
515
  end
454
516
 
455
517
  def pe?
456
- @pe ? true : (@ne ? false : (pe ? true : false ))
518
+ _detect_format() == :pe
519
+ end
520
+
521
+ def ne?
522
+ _detect_format() == :ne
523
+ end
524
+
525
+ def te?
526
+ _detect_format() == :te
457
527
  end
458
528
 
459
529
  ##############################################################################
@@ -498,6 +568,8 @@ class PEdump
498
568
  pe_imports(f)
499
569
  elsif ne(f)
500
570
  ne(f).imports
571
+ else
572
+ []
501
573
  end
502
574
  end
503
575
 
@@ -527,7 +599,11 @@ class PEdump
527
599
  # http://code.google.com/p/corkami/source/browse/trunk/asm/PE/manyimportsW7.asm
528
600
  break
529
601
  end
530
- t=IMAGE_IMPORT_DESCRIPTOR.read(f)
602
+ if r.size >= MAX_IMAGE_IMPORT_DESCRIPTORS
603
+ logger.warn "[!] too many IMAGE_IMPORT_DESCRIPTORs, not reading more than #{r.size}"
604
+ break
605
+ end
606
+ t = IMAGE_IMPORT_DESCRIPTOR.read(f)
531
607
  break if t.Name.to_i == 0 # also catches EOF
532
608
  r << t
533
609
  file_offset += IMAGE_IMPORT_DESCRIPTOR::SIZE
@@ -536,8 +612,16 @@ class PEdump
536
612
  logger.warn "[?] imports info beyond EOF"
537
613
  end
538
614
 
615
+ n_bad_names = 0
539
616
  logger.warn "[?] non-empty last IMAGE_IMPORT_DESCRIPTOR: #{t.inspect}" if t && !t.empty?
540
- @imports = r.each do |x|
617
+ @imports = r
618
+ r = nil
619
+ @imports.each_with_index do |x, iidx|
620
+ if n_bad_names > MAX_ERRORS
621
+ logger.warn "[!] too many bad imported function names. skipping further imports parsing"
622
+ @imports = @imports[0,iidx]
623
+ break
624
+ end
541
625
  if x.Name.to_i != 0 && (ofs = va2file(x.Name))
542
626
  begin
543
627
  f.seek ofs
@@ -572,12 +656,18 @@ class PEdump
572
656
  logger.warn "[?] import ofs 0x#{ofs.to_s(16)} VA=0x#{t.to_s(16)} beyond EOF"
573
657
  nil
574
658
  else
575
- ImportedFunction.new(
576
- f.read(2).unpack('v').first,
577
- f.gets("\x00").chomp("\x00"),
578
- nil,
579
- va
580
- )
659
+ hint = f.read(2).unpack('v').first
660
+ name = f.gets("\x00").to_s.chomp("\x00")
661
+ if !name.empty? && name !~ GOOD_FUNCTION_NAME_RE
662
+ n_bad_names += 1
663
+ if n_bad_names > MAX_ERRORS
664
+ nil
665
+ else
666
+ ImportedFunction.new(hint, name, nil, va)
667
+ end
668
+ else
669
+ ImportedFunction.new(hint, name, nil, va)
670
+ end
581
671
  end
582
672
  elsif tbl == :original_first_thunk
583
673
  # OriginalFirstThunk entries can not be invalid, show a warning msg
@@ -592,7 +682,7 @@ class PEdump
592
682
  end
593
683
  end
594
684
  x[tbl] && x[tbl].compact!
595
- end
685
+ end # [:original_first_thunk, :first_thunk].each
596
686
  if x.original_first_thunk && !x.first_thunk
597
687
  logger.warn "[?] import table: empty FirstThunk for #{x.module_name}"
598
688
  elsif !x.original_first_thunk && x.first_thunk
@@ -603,7 +693,8 @@ class PEdump
603
693
  logger.debug "[?] import table: OriginalFirstThunk != FirstThunk for #{x.module_name}"
604
694
  end
605
695
  end
606
- end
696
+ end # r.each
697
+ @imports
607
698
  end
608
699
 
609
700
  ##############################################################################
@@ -720,9 +811,9 @@ class PEdump
720
811
  ord2name = {}
721
812
  if x.names && x.names.any?
722
813
  n = x.NumberOfNames
723
- if n > 2048
724
- logger.warn "[?] NumberOfNames too big (#{x.NumberOfNames}), limiting to 2048"
725
- n = 2048
814
+ if n > MAX_EXPORT_NUMBER_OF_NAMES
815
+ logger.warn "[?] NumberOfNames too big (#{x.NumberOfNames}), limiting to #{MAX_EXPORT_NUMBER_OF_NAMES}"
816
+ n = MAX_EXPORT_NUMBER_OF_NAMES
726
817
  end
727
818
  n.times do |i|
728
819
  ord2name[x.name_ordinals[i]] ||= []
data/lib/pedump/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'pedump'
2
2
  require 'pedump/packer'
3
+ require 'pedump/rich'
3
4
  require 'pedump/version_info'
4
5
  require 'optparse'
5
6
 
@@ -33,13 +34,14 @@ class PEdump::CLI
33
34
  attr_accessor :data, :argv
34
35
 
35
36
  KNOWN_ACTIONS = (
36
- %w'mz dos_stub rich pe ne data_directory sections tls security' +
37
- %w'strings resources resource_directory imports exports version_info packer web console packer_only'
37
+ %w'mz dos_stub rich pe ne te data_directory sections tls security' +
38
+ %w'strings resources resource_directory imports exports version_info packer web console packer_only' +
39
+ %w'extract' # 'disasm'
38
40
  ).map(&:to_sym)
39
41
 
40
- DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console'.map(&:to_sym)
42
+ DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console extract disasm'.map(&:to_sym)
41
43
 
42
- URL_BASE = "http://pedump.me"
44
+ URL_BASE = "https://pedump.me"
43
45
 
44
46
  def initialize argv = ARGV
45
47
  @argv = argv
@@ -65,8 +67,8 @@ class PEdump::CLI
65
67
  @options[:force] ||= 0
66
68
  @options[:force] += 1
67
69
  end
68
- opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :table, :yaml],
69
- "Output format: bin,c,dump,hex,inspect,table,yaml","(default: table)" do |v|
70
+ opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :json, :table, :yaml],
71
+ "Output format: bin,c,dump,hex,inspect,json,table,yaml","(default: table)" do |v|
70
72
  @options[:format] = v
71
73
  end
72
74
  KNOWN_ACTIONS.each do |t|
@@ -96,8 +98,24 @@ class PEdump::CLI
96
98
  opts.on "--all", "Dump all but resource-directory (default)" do
97
99
  @actions = DEFAULT_ALL_ACTIONS
98
100
  end
101
+
102
+ opts.separator ''
103
+
104
+ # opts.on "--disasm [X]", "Disassemble a symbol/VA" do |v|
105
+ # @actions << [:disasm, v]
106
+ # end
107
+ opts.on("--extract ID", "Extract a resource/section/data_dir",
108
+ "ID: datadir:EXPORT - datadir by type",
109
+ "ID: resource:0x98478 - resource by offset",
110
+ "ID: resource:ICON/#1 - resource by type & name",
111
+ "ID: section:.text - section by name",
112
+ "ID: section:rva/0x1000 - section by RVA",
113
+ "ID: section:raw/0x400 - section by RAW_PTR",
114
+ ) do |v|
115
+ @actions << [:extract, v]
116
+ end
99
117
  opts.on "--va2file VA", "Convert RVA to file offset" do |va|
100
- @actions << [:va2file,va]
118
+ @actions << [:va2file, va]
101
119
  end
102
120
 
103
121
  opts.separator ''
@@ -133,9 +151,9 @@ class PEdump::CLI
133
151
  @file_name = fname
134
152
 
135
153
  File.open(fname,'rb') do |f|
136
- @pedump = create_pedump fname
154
+ @pedump = create_pedump f
137
155
 
138
- next if !@options[:force] && !@pedump.mz(f)
156
+ next if !@options[:force] && !@pedump.supported_file?(f)
139
157
 
140
158
  @actions.each do |action|
141
159
  case action
@@ -152,8 +170,8 @@ class PEdump::CLI
152
170
  # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
153
171
  end
154
172
 
155
- def create_pedump fname
156
- PEdump.new(fname, :force => @options[:force]).tap do |x|
173
+ def create_pedump io
174
+ PEdump.new(io, :force => @options[:force]).tap do |x|
157
175
  x.logger.level =
158
176
  case @options[:verbose]
159
177
  when -100..-3
@@ -194,16 +212,14 @@ class PEdump::CLI
194
212
  end
195
213
 
196
214
  class ProgressProxy
197
- attr_reader :pbar
198
-
199
- def initialize file
200
- @file = file
201
- @pbar = ProgressBar.new("[.] uploading", file.size, STDOUT)
202
- @pbar.try(:file_transfer_mode)
203
- @pbar.bar_mark = '='
215
+ def initialize file, prefix = "[.] uploading: ", io = $stdout
216
+ @file = file
217
+ @io = io
218
+ @prefix = prefix
204
219
  end
205
220
  def read *args
206
- @pbar.inc args.first
221
+ @io.write("\r#{@prefix}#{@file.tell}/#{@file.size} ")
222
+ @io.flush
207
223
  @file.read *args
208
224
  end
209
225
  def method_missing *args
@@ -212,6 +228,10 @@ class PEdump::CLI
212
228
  def respond_to? *args
213
229
  @file.respond_to?(args.first) || super(*args)
214
230
  end
231
+
232
+ def finish!
233
+ @io.write("\r#{@prefix}#{@file.size}/#{@file.size} \n")
234
+ end
215
235
  end
216
236
 
217
237
  def upload f
@@ -224,17 +244,17 @@ class PEdump::CLI
224
244
  require 'open-uri'
225
245
  require 'net/http'
226
246
  require 'net/http/post/multipart'
227
- require 'progressbar'
228
247
 
229
- stdout_sync = STDOUT.sync
230
- STDOUT.sync = true
248
+ stdout_sync = $stdout.sync
249
+ $stdout.sync = true
231
250
 
232
251
  md5 = Digest::MD5.file(f.path).hexdigest
233
252
  @pedump.logger.info "[.] md5: #{md5}"
234
253
  file_url = "#{URL_BASE}/#{md5}/"
235
254
 
236
255
  @pedump.logger.warn "[.] checking if file already uploaded.."
237
- Net::HTTP.start('pedump.me') do |http|
256
+ uri = URI.parse URL_BASE
257
+ Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
238
258
  http.open_timeout = 10
239
259
  http.read_timeout = 10
240
260
  # doing HTTP HEAD is a lot faster than open-uri
@@ -250,24 +270,26 @@ class PEdump::CLI
250
270
 
251
271
  f.rewind
252
272
 
253
- # upload with progressbar
273
+ # upload with progress
254
274
  post_url = URI.parse(URL_BASE+'/')
275
+ # UploadIO is from multipart-post
255
276
  uio = UploadIO.new(f, "application/octet-stream", File.basename(f.path))
256
277
  ppx = ProgressProxy.new(uio)
257
278
  req = Net::HTTP::Post::Multipart.new post_url.path, "file" => ppx
258
- res = Net::HTTP.start(post_url.host, post_url.port){ |http| http.request(req) }
259
- ppx.pbar.finish
260
-
261
- puts
262
- puts "[.] analyzing..."
279
+ res = Net::HTTP.start(post_url.host, post_url.port, use_ssl: (post_url.scheme == 'https')) do |http|
280
+ http.request(req)
281
+ end
282
+ ppx.finish!
263
283
 
264
- if (r=open(File.join(URL_BASE,md5,'analyze')).read) != "OK"
265
- raise "invalid server response: #{r}"
284
+ unless [200, 302, 303].include?(res.code.to_i)
285
+ @pedump.logger.fatal "[!] invalid server response: #{res.code} #{res.msg}"
286
+ @pedump.logger.fatal res.body unless res.body.empty?
287
+ exit(1)
266
288
  end
267
289
 
268
290
  puts "[.] uploaded: #{file_url}"
269
291
  ensure
270
- STDOUT.sync = stdout_sync
292
+ $stdout.sync = stdout_sync
271
293
  end
272
294
 
273
295
  def console f
@@ -311,6 +333,10 @@ class PEdump::CLI
311
333
  def dump_action action, f
312
334
  if action.is_a?(Array)
313
335
  case action[0]
336
+ when :disasm
337
+ return
338
+ when :extract
339
+ return extract action[1]
314
340
  when :va2file
315
341
  @pedump.sections(f)
316
342
  va = action[1] =~ /(^0x)|(h$)/i ? action[1].to_i(16) : action[1].to_i
@@ -326,7 +352,7 @@ class PEdump::CLI
326
352
 
327
353
  puts action_title(action) unless @options[:format] == :binary
328
354
 
329
- return dump(data) if [:inspect, :table, :yaml].include?(@options[:format])
355
+ return dump(data) if [:inspect, :table, :json, :yaml].include?(@options[:format])
330
356
 
331
357
  dump_opts = {:name => action}
332
358
  case action
@@ -376,6 +402,9 @@ class PEdump::CLI
376
402
  when :yaml
377
403
  require 'yaml'
378
404
  puts data.to_yaml
405
+ when :json
406
+ require 'json'
407
+ puts data.to_json
379
408
  end
380
409
  end
381
410
 
@@ -454,6 +483,8 @@ class PEdump::CLI
454
483
  case data.first
455
484
  when PEdump::IMAGE_DATA_DIRECTORY
456
485
  dump_data_dir data
486
+ when PEdump::EFI_IMAGE_DATA_DIRECTORY
487
+ dump_efi_data_dir data
457
488
  when PEdump::IMAGE_SECTION_HEADER
458
489
  dump_sections data
459
490
  when PEdump::Resource
@@ -778,19 +809,26 @@ class PEdump::CLI
778
809
  end
779
810
  end
780
811
 
781
-
782
812
  def dump_data_dir data
783
813
  data.each do |row|
784
814
  printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va.to_i, row.size.to_i
785
815
  end
786
816
  end
787
817
 
818
+ def dump_efi_data_dir data
819
+ data.each_with_index do |row, idx|
820
+ printf " %-12s rva:0x%8x size:0x %8x\n", PEdump::EFI_IMAGE_DATA_DIRECTORY::TYPES[idx], row.va.to_i, row.size.to_i
821
+ end
822
+ end
823
+
788
824
  def dump_rich_hdr data
789
825
  if decoded = data.decode
790
- puts " LIB_ID VERSION TIMES_USED "
826
+ puts " ID VER COUNT DESCRIPTION"
791
827
  decoded.each do |row|
792
- printf " %5d %2x %7d %4x %7d %3x\n",
793
- row.id, row.id, row.version, row.version, row.times, row.times
828
+ # printf " %5d %4x %7d %4x %12d %8x\n",
829
+ # row.id, row.id, row.version, row.version, row.times, row.times
830
+ printf " %4x %4x %12d %s\n",
831
+ row.id, row.version, row.times, ::PEdump::RICH_IDS[(row.id<<16) + row.version]
794
832
  end
795
833
  else
796
834
  puts "# raw:"
@@ -801,4 +839,79 @@ class PEdump::CLI
801
839
  end
802
840
  end
803
841
 
842
+ def disasm x
843
+ puts "TODO"
844
+ end
845
+
846
+ def extract x
847
+ a = x.split(':',2)
848
+ case a[0]
849
+ when 'datadir'
850
+ extract_datadir a[1]
851
+ when 'resource'
852
+ extract_resource a[1]
853
+ when 'section'
854
+ extract_section a[1]
855
+ else
856
+ raise "invalid #{x.inspect}"
857
+ end
858
+ end
859
+
860
+ def extract_datadir id
861
+ entry = @pedump.data_directory.find{ |x| x.type == id }
862
+ unless entry
863
+ @pedump.logger.fatal "[!] entry #{id.inspect} not found"
864
+ exit(1)
865
+ end
866
+ if entry.size != 0
867
+ IO::copy_stream @pedump.io, $stdout, entry.size, @pedump.va2file(entry.va)
868
+ end
869
+ end
870
+
871
+ def extract_resource id
872
+ res = nil
873
+ if id =~ /\A0x\h+\Z/
874
+ needle = id.to_i(16)
875
+ res = @pedump.resources.find{ |r| r.file_offset == needle }
876
+ elsif id =~ /\A\d+\Z/
877
+ needle = id.to_i(10)
878
+ res = @pedump.resources.find{ |r| r.file_offset == needle }
879
+ elsif id['/']
880
+ type, name = id.split('/', 2)
881
+ res = @pedump.resources.find{ |r| r.type == type && r.name == name }
882
+ else
883
+ @pedump.logger.fatal "[!] invalid resource id #{id.inspect}"
884
+ exit(1)
885
+ end
886
+ unless res
887
+ @pedump.logger.fatal "[!] resource #{id.inspect} not found"
888
+ exit(1)
889
+ end
890
+ IO::copy_stream @pedump.io, $stdout, res.size, res.file_offset
891
+ end
892
+
893
+ def extract_section id
894
+ section = nil
895
+ if id['/']
896
+ a = id.split('/',2)
897
+ if a[0] == 'rva'
898
+ value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
899
+ section = @pedump.sections.find{ |s| s.VirtualAddress == value }
900
+ elsif a[0] == 'raw'
901
+ value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
902
+ section = @pedump.sections.find{ |s| s.PointerToRawData == value }
903
+ else
904
+ @pedump.logger.fatal "[!] invalid section id #{id.inspect}"
905
+ exit(1)
906
+ end
907
+ else
908
+ section = @pedump.sections.find{ |s| s.Name == id }
909
+ end
910
+ unless section
911
+ @pedump.logger.fatal "[!] section #{id.inspect} not found"
912
+ exit(1)
913
+ end
914
+ IO::copy_stream @pedump.io, $stdout, section.SizeOfRawData, section.PointerToRawData
915
+ end
916
+
804
917
  end # class PEdump::CLI