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.
- checksums.yaml +5 -5
- data/.github/workflows/rubocop-analysis.yml +39 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +14 -1
- data/Gemfile.lock +78 -31
- data/README.md +111 -16
- data/Rakefile +71 -6
- data/VERSION +1 -1
- data/data/comp_id.txt +776 -0
- data/lib/pedump.rb +120 -29
- data/lib/pedump/cli.rb +150 -37
- data/lib/pedump/loader.rb +28 -6
- data/lib/pedump/loader/minidump.rb +130 -15
- data/lib/pedump/loader/section.rb +5 -3
- data/lib/pedump/ne.rb +1 -1
- data/lib/pedump/pe.rb +63 -54
- data/lib/pedump/rich.rb +562 -0
- data/lib/pedump/te.rb +62 -0
- data/lib/pedump/unpacker/aspack.rb +1 -1
- data/lib/pedump/version.rb +2 -5
- data/misc/aspack/aspack_unlzx.c +5 -3
- data/pedump.gemspec +96 -28
- metadata +49 -30
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[
|
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#{
|
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#{
|
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
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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)
|
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
|
452
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
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 >
|
724
|
-
logger.warn "[?] NumberOfNames too big (#{x.NumberOfNames}), limiting to
|
725
|
-
n =
|
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 = "
|
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
|
154
|
+
@pedump = create_pedump f
|
137
155
|
|
138
|
-
next if !@options[:force] && !@pedump.
|
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
|
156
|
-
PEdump.new(
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
@
|
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
|
-
@
|
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 =
|
230
|
-
|
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
|
-
|
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
|
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)
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
265
|
-
|
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
|
-
|
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 "
|
826
|
+
puts " ID VER COUNT DESCRIPTION"
|
791
827
|
decoded.each do |row|
|
792
|
-
printf " %5d %
|
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
|