pedump 0.5.2 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|