pedump 0.0.1 → 0.1.0
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/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:
|