pedump 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/pedump.rb +215 -94
- data/lib/pedump/cli.rb +27 -22
- data/pedump.gemspec +2 -2
- metadata +11 -11
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/pedump.rb
CHANGED
@@ -27,12 +27,37 @@ class String
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
class File
|
31
|
+
def checked_seek newpos
|
32
|
+
@file_range ||= (0..size)
|
33
|
+
@file_range.include?(newpos) && (seek(newpos) || true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
30
37
|
class PEdump
|
31
|
-
attr_accessor :fname, :logger
|
32
|
-
attr_reader :mz, :dos_stub, :rich_hdr, :pe, :resources, :resource_directory
|
38
|
+
attr_accessor :fname, :logger, :force
|
33
39
|
|
34
|
-
|
35
|
-
|
40
|
+
def initialize fname, params = {}
|
41
|
+
@fname = fname
|
42
|
+
@force = params[:force]
|
43
|
+
@logger = @@logger = params[:logger] || PEdump::Logger.new(STDERR)
|
44
|
+
end
|
45
|
+
|
46
|
+
class Logger < ::Logger
|
47
|
+
def initialize *args
|
48
|
+
super
|
49
|
+
@formatter = proc do |severity,_,_,msg|
|
50
|
+
# quick and dirty way to remove duplicate messages
|
51
|
+
if @prevmsg == msg && severity != 'DEBUG' && severity != 'INFO'
|
52
|
+
''
|
53
|
+
else
|
54
|
+
@prevmsg = msg
|
55
|
+
"#{msg}\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@level = Logger::WARN
|
59
|
+
end
|
60
|
+
end
|
36
61
|
|
37
62
|
class << self
|
38
63
|
def logger; @@logger; end
|
@@ -63,14 +88,19 @@ class PEdump
|
|
63
88
|
end
|
64
89
|
def x.read file, size = nil
|
65
90
|
size ||= const_get 'SIZE'
|
66
|
-
|
91
|
+
data = file.read(size).to_s
|
92
|
+
if data.size < size && PEdump.logger
|
93
|
+
PEdump.logger.error "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
|
94
|
+
end
|
95
|
+
new(*data.unpack(const_get('FORMAT')))
|
67
96
|
end
|
68
97
|
end
|
69
98
|
end
|
70
99
|
end
|
71
100
|
|
101
|
+
|
72
102
|
# http://www.delorie.com/djgpp/doc/exe/
|
73
|
-
MZ = create_struct( "
|
103
|
+
MZ = create_struct( "a2v13Qv2V6",
|
74
104
|
:signature,
|
75
105
|
:bytes_in_last_block,
|
76
106
|
:blocks_in_file,
|
@@ -84,7 +114,16 @@ class PEdump
|
|
84
114
|
:ip,
|
85
115
|
:cs,
|
86
116
|
:reloc_table_offset,
|
87
|
-
:overlay_number
|
117
|
+
:overlay_number,
|
118
|
+
:reserved0, # 8 reserved bytes
|
119
|
+
:oem_id,
|
120
|
+
:oem_info,
|
121
|
+
:reserved2, # 20 reserved bytes
|
122
|
+
:reserved3,
|
123
|
+
:reserved4,
|
124
|
+
:reserved5,
|
125
|
+
:reserved6,
|
126
|
+
:lfanew
|
88
127
|
)
|
89
128
|
|
90
129
|
PE = Struct.new(
|
@@ -152,7 +191,7 @@ class PEdump
|
|
152
191
|
def self.read file, size = SIZE
|
153
192
|
usual_size = 224
|
154
193
|
PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
|
155
|
-
new(*file.read([size,SIZE].min).unpack(FORMAT)).tap do |ioh|
|
194
|
+
new(*file.read([size,SIZE].min).to_s.unpack(FORMAT)).tap do |ioh|
|
156
195
|
ioh.DataDirectory = []
|
157
196
|
|
158
197
|
# check if "...this address is outside the memory mapped file and is zeroed by the OS"
|
@@ -237,8 +276,7 @@ class PEdump
|
|
237
276
|
class RichHdr < String
|
238
277
|
attr_accessor :offset, :key # xor key
|
239
278
|
|
240
|
-
Entry
|
241
|
-
class Entry
|
279
|
+
class Entry < Struct.new(:version,:id,:times)
|
242
280
|
def inspect
|
243
281
|
"<id=#{id}, version=#{version}, times=#{times}>"
|
244
282
|
end
|
@@ -249,7 +287,10 @@ class PEdump
|
|
249
287
|
start_idx = stub.index(key.xor('DanS'))
|
250
288
|
end_idx = stub.index('Rich')+8
|
251
289
|
if stub[end_idx..-1].tr("\x00",'') != ''
|
252
|
-
|
290
|
+
t = stub[end_idx..-1]
|
291
|
+
t = "#{t[0,0x100]}..." if t.size > 0x100
|
292
|
+
PEdump.logger.error "[!] non-zero dos stub after rich_hdr: #{t.inspect}"
|
293
|
+
return nil
|
253
294
|
end
|
254
295
|
RichHdr.new(stub[start_idx, end_idx-start_idx]).tap do |x|
|
255
296
|
x.key = key
|
@@ -272,18 +313,6 @@ class PEdump
|
|
272
313
|
attr_accessor :offset
|
273
314
|
end
|
274
315
|
|
275
|
-
def initialize fname, params = {}
|
276
|
-
@fname = fname
|
277
|
-
@logger = params[:logger] ||
|
278
|
-
begin
|
279
|
-
Logger.new(STDERR).tap do |l|
|
280
|
-
l.formatter = proc{ |_,_,_,msg| "#{msg}\n" }
|
281
|
-
l.level = Logger::WARN
|
282
|
-
end
|
283
|
-
end
|
284
|
-
@@logger = @logger
|
285
|
-
end
|
286
|
-
|
287
316
|
def logger= l
|
288
317
|
@logger = @@logger = l
|
289
318
|
end
|
@@ -292,57 +321,114 @@ class PEdump
|
|
292
321
|
new(fname).dump
|
293
322
|
end
|
294
323
|
|
295
|
-
def
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
if @mz.signature != 'MZ' && @mz.signature != 'ZM'
|
300
|
-
logger.warn "[?] no MZ header (want: 'MZ' or 'ZM', got: #{@mz.signature.inspect}"
|
324
|
+
def mz f=nil
|
325
|
+
@mz ||= MZ.read(f).tap do |mz|
|
326
|
+
if mz.signature != 'MZ' && mz.signature != 'ZM'
|
327
|
+
logger.warn "[?] no MZ signature (want: 'MZ' or 'ZM', got: #{mz.signature.inspect}"
|
301
328
|
end
|
302
|
-
|
303
|
-
|
329
|
+
end
|
330
|
+
end
|
304
331
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
332
|
+
def dos_stub f=nil
|
333
|
+
@dos_stub ||=
|
334
|
+
begin
|
335
|
+
mz = mz(f)
|
336
|
+
dos_stub_offset = mz.header_paragraphs.to_i * 0x10
|
337
|
+
dos_stub_size = mz.lfanew.to_i - dos_stub_offset
|
338
|
+
if dos_stub_offset <= 0
|
339
|
+
logger.warn "[?] invalid DOS stub offset #{dos_stub_offset}"
|
340
|
+
nil
|
341
|
+
elsif dos_stub_offset > f.size
|
342
|
+
logger.warn "[?] DOS stub offset beyond EOF: #{dos_stub_offset}"
|
343
|
+
nil
|
344
|
+
elsif dos_stub_size < 0
|
345
|
+
logger.warn "[?] invalid DOS stub size #{dos_stub_size}"
|
346
|
+
nil
|
347
|
+
elsif dos_stub_size == 0
|
348
|
+
# no DOS stub, it's ok
|
349
|
+
nil
|
350
|
+
else
|
351
|
+
if dos_stub_size > 0x1000
|
352
|
+
logger.warn "[?] DOS stub size too big (#{dos_stub_size}), limiting to 0x1000"
|
353
|
+
dos_stub_size = 0x1000
|
354
|
+
end
|
311
355
|
f.seek dos_stub_offset
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
356
|
+
DOSStub.new(f.read(dos_stub_size)).tap do |dos_stub|
|
357
|
+
dos_stub.offset = dos_stub_offset
|
358
|
+
if dos_stub['Rich']
|
359
|
+
if @rich_hdr = RichHdr.from_dos_stub(dos_stub)
|
360
|
+
dos_stub[dos_stub.index(@rich_hdr)..-1] = ''
|
361
|
+
end
|
362
|
+
end
|
317
363
|
end
|
318
|
-
else
|
319
|
-
logger.info "[.] uncommon DOS stub size = #{dos_stub_size}"
|
320
364
|
end
|
321
365
|
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def rich_hdr f=nil
|
369
|
+
dos_stub(f) && @rich_hdr
|
370
|
+
end
|
371
|
+
alias :rich_header :rich_hdr
|
372
|
+
alias :rich :rich_hdr
|
322
373
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
374
|
+
def pe f=nil
|
375
|
+
@pe ||=
|
376
|
+
begin
|
377
|
+
pe_offset = mz(f).try(:lfanew)
|
378
|
+
if pe_offset.nil?
|
379
|
+
logger.fatal "[!] NULL PE offset (e_lfanew). cannot continue."
|
380
|
+
nil
|
381
|
+
elsif pe_offset > f.size
|
382
|
+
logger.fatal "[!] PE offset beyond EOF. cannot continue."
|
383
|
+
nil
|
331
384
|
else
|
332
|
-
|
385
|
+
f.seek pe_offset
|
386
|
+
pe_sig = f.read 4
|
387
|
+
logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
|
388
|
+
logger.warn "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect})" if pe_sig != "PE\x00\x00"
|
389
|
+
PE.new(pe_sig).tap do |pe|
|
390
|
+
pe.image_file_header = IMAGE_FILE_HEADER.read(f)
|
391
|
+
if pe.ifh.SizeOfOptionalHeader > 0
|
392
|
+
if pe.ifh.Machine == 0x8664
|
393
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
|
394
|
+
else
|
395
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER.read(f, pe.ifh.SizeOfOptionalHeader)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
if (nToRead=pe.ifh.NumberOfSections) > 32
|
400
|
+
if @force
|
401
|
+
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). forced. reading all"
|
402
|
+
else
|
403
|
+
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). not forced, reading first 32"
|
404
|
+
nToRead = 32
|
405
|
+
end
|
406
|
+
end
|
407
|
+
pe.section_table = nToRead.times.map do
|
408
|
+
IMAGE_SECTION_HEADER.read(f)
|
409
|
+
end
|
410
|
+
end
|
333
411
|
end
|
334
412
|
end
|
335
|
-
|
336
|
-
IMAGE_SECTION_HEADER.read(f)
|
337
|
-
end
|
413
|
+
end
|
338
414
|
|
339
|
-
|
340
|
-
|
415
|
+
def resource_directory f=nil
|
416
|
+
@resource_directory ||= _read_resource_directory_tree(f)
|
417
|
+
end
|
418
|
+
|
419
|
+
# OPTIONAL: assigns @mz, @rich_hdr, @pe, etc
|
420
|
+
def dump f=nil
|
421
|
+
f ? pe(f) : File.open(@fname){ |f| pe(f) }
|
341
422
|
self
|
342
423
|
end
|
343
424
|
|
344
|
-
def data_directory
|
345
|
-
|
425
|
+
def data_directory f=nil
|
426
|
+
pe(f) && pe.ioh && pe.ioh.DataDirectory
|
427
|
+
end
|
428
|
+
|
429
|
+
def sections f=nil
|
430
|
+
pe(f) && pe.section_table
|
431
|
+
end
|
346
432
|
alias :section_table :sections
|
347
433
|
|
348
434
|
IMAGE_RESOURCE_DIRECTORY = create_struct 'V2v4',
|
@@ -353,31 +439,62 @@ class PEdump
|
|
353
439
|
class << self
|
354
440
|
attr_accessor :base
|
355
441
|
alias :read_without_children :read
|
356
|
-
def read f
|
442
|
+
def read f, root=true
|
443
|
+
if root
|
444
|
+
@@loopchk1 = Hash.new(0)
|
445
|
+
@@loopchk2 = Hash.new(0)
|
446
|
+
@@loopchk3 = Hash.new(0)
|
447
|
+
elsif (@@loopchk1[f.tell] += 1) > 1
|
448
|
+
PEdump.logger.error "[!] #{self}: loop1 detected at file pos #{f.tell}" if @@loopchk1[f.tell] < 2
|
449
|
+
return nil
|
450
|
+
end
|
357
451
|
read_without_children(f).tap do |r|
|
358
|
-
|
359
|
-
|
360
|
-
|
452
|
+
nToRead = r.NumberOfNamedEntries.to_i + r.NumberOfIdEntries.to_i
|
453
|
+
r.entries = []
|
454
|
+
nToRead.times do |i|
|
455
|
+
if f.eof?
|
456
|
+
PEdump.logger.error "[!] #{self}: #{nToRead} entries in directory, but got EOF on #{i}-th."
|
457
|
+
break
|
458
|
+
end
|
459
|
+
if (@@loopchk2[f.tell] += 1) > 1
|
460
|
+
PEdump.logger.error "[!] #{self}: loop2 detected at file pos #{f.tell}" if @@loopchk2[f.tell] < 2
|
461
|
+
next
|
462
|
+
end
|
463
|
+
r.entries << IMAGE_RESOURCE_DIRECTORY_ENTRY.read(f)
|
464
|
+
end
|
465
|
+
#r.entries.uniq!
|
466
|
+
r.entries.each do |entry|
|
361
467
|
entry.name =
|
362
|
-
if entry.Name & 0x8000_0000 > 0
|
468
|
+
if entry.Name.to_i & 0x8000_0000 > 0
|
363
469
|
# Name is an address of unicode string
|
364
470
|
f.seek base + entry.Name & 0x7fff_ffff
|
365
|
-
nChars = f.read(2).unpack("v").first
|
366
|
-
|
471
|
+
nChars = f.read(2).to_s.unpack("v").first.to_i
|
472
|
+
begin
|
473
|
+
f.read(nChars*2).force_encoding('UTF-16LE').encode!('UTF-8')
|
474
|
+
rescue
|
475
|
+
PEdump.logger.error "[!] #{self} failed to read entry name: #{$!}"
|
476
|
+
"???"
|
477
|
+
end
|
367
478
|
else
|
368
479
|
# Name is a numeric id
|
369
480
|
"##{entry.Name}"
|
370
481
|
end
|
371
|
-
f.
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
IMAGE_RESOURCE_DIRECTORY.read(f)
|
376
|
-
else
|
377
|
-
# child is a resource
|
378
|
-
IMAGE_RESOURCE_DATA_ENTRY.read(f)
|
482
|
+
if entry.OffsetToData && f.checked_seek(base + entry.OffsetToData & 0x7fff_ffff)
|
483
|
+
if (@@loopchk3[f.tell] += 1) > 1
|
484
|
+
PEdump.logger.error "[!] #{self}: loop3 detected at file pos #{f.tell}" if @@loopchk3[f.tell] < 2
|
485
|
+
next
|
379
486
|
end
|
487
|
+
entry.data =
|
488
|
+
if entry.OffsetToData & 0x8000_0000 > 0
|
489
|
+
# child is a directory
|
490
|
+
IMAGE_RESOURCE_DIRECTORY.read(f,false)
|
491
|
+
else
|
492
|
+
# child is a resource
|
493
|
+
IMAGE_RESOURCE_DATA_ENTRY.read(f)
|
494
|
+
end
|
495
|
+
end
|
380
496
|
end
|
497
|
+
@@loopchk1 = @@loopchk2 = @@loopchk3 = nil if root # save some memory
|
381
498
|
end
|
382
499
|
end
|
383
500
|
end
|
@@ -400,7 +517,7 @@ class PEdump
|
|
400
517
|
end
|
401
518
|
|
402
519
|
def _read_resource_directory_tree f
|
403
|
-
return nil unless
|
520
|
+
return nil unless pe(f).try(:ioh)
|
404
521
|
res_dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::RESOURCE]
|
405
522
|
return [] if !res_dir || (res_dir.va == 0 && res_dir.size == 0)
|
406
523
|
res_va = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::RESOURCE].va
|
@@ -451,14 +568,14 @@ class PEdump
|
|
451
568
|
end
|
452
569
|
end
|
453
570
|
|
454
|
-
def
|
571
|
+
def bitmap_mask src_fname
|
455
572
|
File.open(src_fname, "rb") do |f|
|
456
573
|
parse f
|
457
574
|
bmp_info_hdr = bitmap_hdr
|
458
575
|
bitmap_size = BITMAPINFOHEADER::SIZE + @palette_size + @imgdata_size
|
459
576
|
return nil if bitmap_size >= self.size
|
460
577
|
|
461
|
-
|
578
|
+
mask_size = self.size - bitmap_size
|
462
579
|
f.seek file_offset + bitmap_size
|
463
580
|
|
464
581
|
bmp_info_hdr = BITMAPINFOHEADER.new(*bmp_info_hdr[14..-1].unpack(BITMAPINFOHEADER::FORMAT))
|
@@ -470,10 +587,10 @@ class PEdump
|
|
470
587
|
@palette_size = palette.size
|
471
588
|
|
472
589
|
"BM" + [
|
473
|
-
BITMAPINFOHEADER::SIZE + 14 + @palette_size +
|
590
|
+
BITMAPINFOHEADER::SIZE + 14 + @palette_size + mask_size,
|
474
591
|
0,
|
475
592
|
BITMAPINFOHEADER::SIZE + 14 + @palette_size
|
476
|
-
].pack("V3") + bmp_info_hdr.pack + palette + f.read(
|
593
|
+
].pack("V3") + bmp_info_hdr.pack + palette + f.read(mask_size)
|
477
594
|
end
|
478
595
|
end
|
479
596
|
|
@@ -516,9 +633,9 @@ class PEdump
|
|
516
633
|
|
517
634
|
STRING = Struct.new(:id, :lang, :value)
|
518
635
|
|
519
|
-
def strings
|
636
|
+
def strings f=nil
|
520
637
|
r = []
|
521
|
-
Array(resources).find_all{ |x| x.type == 'STRING'}.each do |res|
|
638
|
+
Array(resources(f)).find_all{ |x| x.type == 'STRING'}.each do |res|
|
522
639
|
res.data.each_with_index do |string,idx|
|
523
640
|
r << STRING.new( ((res.id-1)<<4) + idx, res.lang, string ) unless string.empty?
|
524
641
|
end
|
@@ -573,7 +690,12 @@ class PEdump
|
|
573
690
|
%w'MESSAGETABLE GROUP_CURSOR' + [nil] + %w'GROUP_ICON' + [nil] +
|
574
691
|
%w'VERSION DLGINCLUDE' + [nil] + %w'PLUGPLAY VXD ANICURSOR ANIICON HTML MANIFEST'
|
575
692
|
|
576
|
-
def resources
|
693
|
+
def resources f=nil
|
694
|
+
@resources ||= _scan_resources(f)
|
695
|
+
end
|
696
|
+
|
697
|
+
def _scan_resources f=nil, dir=nil
|
698
|
+
dir ||= resource_directory(f)
|
577
699
|
return nil unless dir
|
578
700
|
dir.entries.map do |entry|
|
579
701
|
case entry.data
|
@@ -586,14 +708,12 @@ class PEdump
|
|
586
708
|
else
|
587
709
|
entry.name
|
588
710
|
end
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
res.parse f
|
593
|
-
end
|
711
|
+
_scan_resources(f,entry.data).each do |res|
|
712
|
+
res.type = entry_type
|
713
|
+
res.parse f
|
594
714
|
end
|
595
715
|
else
|
596
|
-
|
716
|
+
_scan_resources(f,entry.data).each do |res|
|
597
717
|
res.name = res.name == "##{res.lang}" ? entry.name : "#{entry.name} / #{res.name}"
|
598
718
|
res.id ||= entry.Name if entry.Name.is_a?(Numeric) && entry.Name < 0x8000_0000
|
599
719
|
end
|
@@ -610,9 +730,10 @@ class PEdump
|
|
610
730
|
entry.data.Reserved
|
611
731
|
)
|
612
732
|
else
|
613
|
-
|
733
|
+
logger.error "[!] invalid resource entry: #{entry.data.inspect}"
|
734
|
+
nil
|
614
735
|
end
|
615
|
-
end.flatten
|
736
|
+
end.flatten.compact
|
616
737
|
end
|
617
738
|
end
|
618
739
|
|
@@ -633,10 +754,10 @@ if $0 == __FILE__
|
|
633
754
|
fname = r.name.tr("/# ",'_')+".bmp"
|
634
755
|
puts "[.] #{fname}"
|
635
756
|
File.open(fname,"wb"){ |fo| fo << r.restore_bitmap(fi) }
|
636
|
-
if
|
637
|
-
fname.sub! '.bmp', '.
|
757
|
+
if mask = r.bitmap_mask(fi)
|
758
|
+
fname.sub! '.bmp', '.mask.bmp'
|
638
759
|
puts "[.] #{fname}"
|
639
|
-
File.open(fname,"wb"){ |fo| fo << r.
|
760
|
+
File.open(fname,"wb"){ |fo| fo << r.bitmap_mask(fi) }
|
640
761
|
end
|
641
762
|
end
|
642
763
|
end
|
data/lib/pedump/cli.rb
CHANGED
@@ -29,6 +29,9 @@ class PEdump::CLI
|
|
29
29
|
@options[:verbose] ||= 0
|
30
30
|
@options[:verbose] += 1
|
31
31
|
end
|
32
|
+
opts.on "-F", "--force", "Try to dump by all means (can cause exceptions & heavy wounds)" do |v|
|
33
|
+
@options[:force] = true
|
34
|
+
end
|
32
35
|
opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :table],
|
33
36
|
"Output format: bin,c,dump,hex,inspect,table (default)" do |v|
|
34
37
|
@options[:format] = v
|
@@ -59,16 +62,18 @@ class PEdump::CLI
|
|
59
62
|
puts "# -----------------------------------------------"
|
60
63
|
puts "# #{fname}"
|
61
64
|
puts "# -----------------------------------------------"
|
62
|
-
puts
|
63
|
-
end
|
64
|
-
@pedump = PEdump.new fname
|
65
|
-
if @options[:verbose]
|
66
|
-
@pedump.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
|
67
65
|
end
|
68
|
-
@pedump = @pedump.dump
|
69
66
|
|
70
|
-
|
71
|
-
|
67
|
+
File.open(fname,'rb') do |f|
|
68
|
+
@pedump = PEdump.new(fname, :force => @options[:force]).tap do |x|
|
69
|
+
if @options[:verbose]
|
70
|
+
x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@actions.each do |action|
|
75
|
+
dump_action action,f
|
76
|
+
end
|
72
77
|
end
|
73
78
|
end
|
74
79
|
rescue Errno::EPIPE
|
@@ -79,11 +84,11 @@ class PEdump::CLI
|
|
79
84
|
def action_title action
|
80
85
|
s = action.to_s.upcase.tr('_',' ')
|
81
86
|
s += " Header" if [:mz, :pe, :rich].include?(action)
|
82
|
-
"=== %s ===\n\n" % s
|
87
|
+
"\n=== %s ===\n\n" % s
|
83
88
|
end
|
84
89
|
|
85
|
-
def dump_action action
|
86
|
-
data = @pedump.send(action)
|
90
|
+
def dump_action action, f
|
91
|
+
data = @pedump.send(action, f)
|
87
92
|
return if !data || (data.respond_to?(:empty?) && data.empty?)
|
88
93
|
|
89
94
|
puts action_title(action)
|
@@ -108,8 +113,6 @@ class PEdump::CLI
|
|
108
113
|
end
|
109
114
|
end
|
110
115
|
dump data, dump_opts
|
111
|
-
ensure
|
112
|
-
puts
|
113
116
|
end
|
114
117
|
|
115
118
|
def dump data, opts = {}
|
@@ -165,7 +168,7 @@ class PEdump::CLI
|
|
165
168
|
else
|
166
169
|
if COMMENTS[k]
|
167
170
|
printf "%30s: %10d %12s %s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)),
|
168
|
-
COMMENTS[k][v] || COMMENTS[k]['default'] || ''
|
171
|
+
COMMENTS[k][v] || (COMMENTS[k].is_a?(Hash) ? COMMENTS[k]['default'] : '') || ''
|
169
172
|
else
|
170
173
|
printf "%30s: %10d %12s\n", k, v, v<10 ? v : ("0x"+v.to_s(16))
|
171
174
|
end
|
@@ -257,8 +260,10 @@ class PEdump::CLI
|
|
257
260
|
dir.entries.each do |child|
|
258
261
|
dump_res_dir child, level+1
|
259
262
|
end
|
260
|
-
|
263
|
+
elsif dir
|
261
264
|
printf "DATA: %8x %8x %5s %8x\n", dir.OffsetToData, dir.Size, dir.CodePage, dir.Reserved
|
265
|
+
else
|
266
|
+
puts # null dir
|
262
267
|
end
|
263
268
|
end
|
264
269
|
|
@@ -309,18 +314,18 @@ class PEdump::CLI
|
|
309
314
|
data.each do |s|
|
310
315
|
name = s.Name[/[^a-z0-9_.]/i] ? s.Name.inspect : s.Name
|
311
316
|
name = "#{name}\n " if name.size > 8
|
312
|
-
printf " %-8s %8x %8x %8x %8x %5x %8x %5x %8x %8x %s\n", name,
|
313
|
-
s.VirtualAddress, s.VirtualSize,
|
314
|
-
s.SizeOfRawData, s.PointerToRawData,
|
315
|
-
s.NumberOfRelocations, s.PointerToRelocations,
|
316
|
-
s.NumberOfLinenumbers, s.PointerToLinenumbers,
|
317
|
-
s.flags, s.flags_desc
|
317
|
+
printf " %-8s %8x %8x %8x %8x %5x %8x %5x %8x %8x %s\n", name.to_s,
|
318
|
+
s.VirtualAddress.to_i, s.VirtualSize.to_i,
|
319
|
+
s.SizeOfRawData.to_i, s.PointerToRawData.to_i,
|
320
|
+
s.NumberOfRelocations.to_i, s.PointerToRelocations.to_i,
|
321
|
+
s.NumberOfLinenumbers.to_i, s.PointerToLinenumbers.to_i,
|
322
|
+
s.flags.to_i, s.flags_desc
|
318
323
|
end
|
319
324
|
end
|
320
325
|
|
321
326
|
def dump_data_dir data
|
322
327
|
data.each do |row|
|
323
|
-
printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va, row.size
|
328
|
+
printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va.to_i, row.size.to_i
|
324
329
|
end
|
325
330
|
end
|
326
331
|
|
data/pedump.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "pedump"
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andrey \"Zed\" Zaikin"]
|
12
|
-
s.date = "2011-12-
|
12
|
+
s.date = "2011-12-09"
|
13
13
|
s.description = "dump headers, sections, extract resources of win32 PE exe,dll,etc"
|
14
14
|
s.email = "zed.0xff@gmail.com"
|
15
15
|
s.executables = ["pedump"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pedump
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-09 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70102209592580 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 2.3.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70102209592580
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bundler
|
27
|
-
requirement: &
|
27
|
+
requirement: &70102209591820 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70102209591820
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: jeweler
|
38
|
-
requirement: &
|
38
|
+
requirement: &70102209590740 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.6.4
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70102209590740
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rcov
|
49
|
-
requirement: &
|
49
|
+
requirement: &70102209585380 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70102209585380
|
58
58
|
description: dump headers, sections, extract resources of win32 PE exe,dll,etc
|
59
59
|
email: zed.0xff@gmail.com
|
60
60
|
executables:
|
@@ -93,7 +93,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
93
|
version: '0'
|
94
94
|
segments:
|
95
95
|
- 0
|
96
|
-
hash:
|
96
|
+
hash: 3334158789950577908
|
97
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
98
|
none: false
|
99
99
|
requirements:
|