pedump 0.4.2 → 0.4.3
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/Rakefile +11 -0
- data/VERSION +1 -1
- data/lib/pedump.rb +50 -71
- data/lib/pedump/cli.rb +11 -10
- data/lib/pedump/composite_io.rb +33 -0
- data/lib/pedump/pe.rb +89 -0
- data/lib/pedump/version.rb +1 -1
- data/pedump.gemspec +10 -6
- data/samples/corkami.7z +0 -0
- data/spec/65535sects_spec.rb +2 -10
- data/spec/composite_io_spec.rb +72 -0
- data/spec/dllord_spec.rb +21 -0
- data/spec/foldedhdr_spec.rb +28 -0
- data/spec/imports_badterm_spec.rb +37 -43
- data/spec/imports_vterm_spec.rb +37 -43
- data/spec/sig_all_packers_spec.rb +15 -5
- data/spec/spec_helper.rb +18 -1
- data/spec/virtsectblXP_spec.rb +12 -0
- metadata +24 -20
- data/samples/65535sects.7z +0 -0
- data/samples/imports_badterm.exe +0 -0
- data/samples/imports_vterm.exe +0 -0
data/Rakefile
CHANGED
@@ -98,6 +98,17 @@ namespace :test do
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
desc "test on corkami binaries"
|
103
|
+
task :corkami do
|
104
|
+
require './lib/pedump'
|
105
|
+
require './lib/pedump/cli'
|
106
|
+
path = "samples/corkami"
|
107
|
+
`find #{path} -type f`.split("\n").each do |fname|
|
108
|
+
STDERR.puts "\n### #{fname}\n"
|
109
|
+
PEdump::CLI.new(fname).run
|
110
|
+
end
|
111
|
+
end
|
101
112
|
end
|
102
113
|
|
103
114
|
def check_file url
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/lib/pedump.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'logger'
|
3
|
+
require 'stringio'
|
4
|
+
require 'pedump/composite_io'
|
5
|
+
require 'pedump/pe'
|
3
6
|
require 'pedump/version'
|
4
7
|
|
5
8
|
# pedump.rb by zed_0xff
|
@@ -135,22 +138,6 @@ class PEdump
|
|
135
138
|
:lfanew
|
136
139
|
)
|
137
140
|
|
138
|
-
class PE < Struct.new(
|
139
|
-
:signature, # "PE\x00\x00"
|
140
|
-
:image_file_header,
|
141
|
-
:image_optional_header,
|
142
|
-
:section_table
|
143
|
-
)
|
144
|
-
alias :ifh :image_file_header
|
145
|
-
alias :ioh :image_optional_header
|
146
|
-
def x64?
|
147
|
-
ifh && ifh.Machine == 0x8664
|
148
|
-
end
|
149
|
-
def dll?
|
150
|
-
ifh && ifh.flags.include?('DLL')
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
141
|
# http://msdn.microsoft.com/en-us/library/ms809762.aspx
|
155
142
|
class IMAGE_FILE_HEADER < create_struct( 'v2V3v2',
|
156
143
|
:Machine, # w
|
@@ -307,6 +294,7 @@ class PEdump
|
|
307
294
|
)
|
308
295
|
class IMAGE_SECTION_HEADER
|
309
296
|
alias :flags :Characteristics
|
297
|
+
alias :va :VirtualAddress
|
310
298
|
def flags_desc
|
311
299
|
r = ''
|
312
300
|
f = self.flags.to_i
|
@@ -455,54 +443,7 @@ class PEdump
|
|
455
443
|
nil
|
456
444
|
else
|
457
445
|
f.seek pe_offset
|
458
|
-
|
459
|
-
logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
|
460
|
-
if pe_sig != "PE\x00\x00"
|
461
|
-
if @force
|
462
|
-
logger.warn "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect})"
|
463
|
-
else
|
464
|
-
logger.error "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect}). (not forced)"
|
465
|
-
return nil
|
466
|
-
end
|
467
|
-
end
|
468
|
-
PE.new(pe_sig).tap do |pe|
|
469
|
-
pe.image_file_header = IMAGE_FILE_HEADER.read(f)
|
470
|
-
if pe.ifh.SizeOfOptionalHeader > 0
|
471
|
-
if pe.x64?
|
472
|
-
pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
|
473
|
-
else
|
474
|
-
pe.image_optional_header = IMAGE_OPTIONAL_HEADER32.read(f, pe.ifh.SizeOfOptionalHeader)
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
if (nToRead=pe.ifh.NumberOfSections) > 0xffff
|
479
|
-
if @force.is_a?(Numeric) && @force > 1
|
480
|
-
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). forced. reading all"
|
481
|
-
else
|
482
|
-
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). not forced, reading first 65535"
|
483
|
-
nToRead = 65535
|
484
|
-
end
|
485
|
-
end
|
486
|
-
pe.section_table = []
|
487
|
-
nToRead.times do
|
488
|
-
break if f.eof?
|
489
|
-
pe.section_table << IMAGE_SECTION_HEADER.read(f)
|
490
|
-
end
|
491
|
-
|
492
|
-
# if pe.section_table.empty?
|
493
|
-
# pe.section_table << IMAGE_SECTION_HEADER.new("")
|
494
|
-
# logger.warn "[?] no sections, appending empty one"
|
495
|
-
# end
|
496
|
-
|
497
|
-
if pe.section_table.any?
|
498
|
-
# zero all missing values of last section
|
499
|
-
pe.section_table.last.tap do |last_section|
|
500
|
-
last_section.each_pair do |k,v|
|
501
|
-
last_section[k] = 0 if v.nil?
|
502
|
-
end
|
503
|
-
end
|
504
|
-
end
|
505
|
-
end
|
446
|
+
PE.read f, :force => @force
|
506
447
|
end
|
507
448
|
end
|
508
449
|
end
|
@@ -591,7 +532,12 @@ class PEdump
|
|
591
532
|
ImportedFunction.new(nil,nil,t & (2**(bits-1)-1)) # 0x7fff_ffff(_ffff_ffff)
|
592
533
|
elsif va=va2file(t)
|
593
534
|
f.seek va
|
594
|
-
|
535
|
+
if f.eof?
|
536
|
+
logger.warn "[?] import va 0x#{va.to_s(16)} beyond EOF"
|
537
|
+
nil
|
538
|
+
else
|
539
|
+
ImportedFunction.new(f.read(2).unpack('v').first, f.gets("\x00").chop)
|
540
|
+
end
|
595
541
|
else
|
596
542
|
nil
|
597
543
|
end
|
@@ -632,34 +578,65 @@ class PEdump
|
|
632
578
|
return @exports if @exports
|
633
579
|
return nil unless pe(f) && pe(f).ioh && f
|
634
580
|
dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::EXPORT]
|
635
|
-
return
|
581
|
+
return nil if !dir || (dir.va == 0 && dir.size == 0)
|
636
582
|
va = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::EXPORT].va
|
637
583
|
file_offset = va2file(va)
|
638
584
|
return nil unless file_offset
|
639
585
|
f.seek file_offset
|
586
|
+
if f.eof?
|
587
|
+
logger.info "[?] exports info beyond EOF"
|
588
|
+
return nil
|
589
|
+
end
|
640
590
|
@exports = IMAGE_EXPORT_DIRECTORY.read(f).tap do |x|
|
641
591
|
x.entry_points = []
|
642
592
|
x.name_ordinals = []
|
643
593
|
x.names = []
|
644
594
|
if x.Name.to_i != 0 && (va = va2file(x.Name))
|
645
595
|
f.seek va
|
646
|
-
|
596
|
+
if f.eof?
|
597
|
+
logger.warn "[?] export va 0x#{va.to_s(16)} beyond EOF"
|
598
|
+
nil
|
599
|
+
else
|
600
|
+
x.name = f.gets("\x00").chomp("\x00")
|
601
|
+
end
|
647
602
|
end
|
648
603
|
if x.NumberOfFunctions.to_i != 0
|
649
604
|
if x.AddressOfFunctions.to_i !=0 && (va = va2file(x.AddressOfFunctions))
|
650
605
|
f.seek va
|
651
|
-
x.entry_points =
|
606
|
+
x.entry_points = []
|
607
|
+
x.NumberOfFunctions.times do
|
608
|
+
if f.eof?
|
609
|
+
logger.warn "[?] got EOF while reading exports entry_points"
|
610
|
+
break
|
611
|
+
end
|
612
|
+
x.entry_points << f.read(4).unpack('V').first
|
613
|
+
end
|
652
614
|
end
|
653
615
|
if x.AddressOfNameOrdinals.to_i !=0 && (va = va2file(x.AddressOfNameOrdinals))
|
654
616
|
f.seek va
|
655
|
-
x.name_ordinals =
|
617
|
+
x.name_ordinals = []
|
618
|
+
x.NumberOfNames.times do
|
619
|
+
if f.eof?
|
620
|
+
logger.warn "[?] got EOF while reading exports name_ordinals"
|
621
|
+
break
|
622
|
+
end
|
623
|
+
x.name_ordinals << f.read(2).unpack('v').first + x.Base
|
624
|
+
end
|
656
625
|
end
|
657
626
|
end
|
658
627
|
if x.NumberOfNames.to_i != 0 && x.AddressOfNames.to_i !=0 && (va = va2file(x.AddressOfNames))
|
659
628
|
f.seek va
|
660
|
-
x.names =
|
629
|
+
x.names = []
|
630
|
+
x.NumberOfNames.times do
|
631
|
+
if f.eof?
|
632
|
+
logger.warn "[?] got EOF while reading exports names"
|
633
|
+
break
|
634
|
+
end
|
635
|
+
x.names << f.read(4).unpack('V').first
|
636
|
+
end
|
637
|
+
x.names.map! do |va|
|
661
638
|
f.seek va2file(va)
|
662
|
-
f.gets("\x00").
|
639
|
+
f.gets("\x00").to_s.chomp("\x00")
|
663
640
|
end
|
664
641
|
end
|
665
642
|
end
|
@@ -746,6 +723,8 @@ class PEdump
|
|
746
723
|
:OffsetToData, :Size, :CodePage, :Reserved
|
747
724
|
|
748
725
|
def va2file va
|
726
|
+
return nil if va.nil?
|
727
|
+
|
749
728
|
sections.each do |s|
|
750
729
|
if (s.VirtualAddress...(s.VirtualAddress+s.VirtualSize)).include?(va)
|
751
730
|
return va - s.VirtualAddress + s.PointerToRawData
|
data/lib/pedump/cli.rb
CHANGED
@@ -486,8 +486,8 @@ class PEdump::CLI
|
|
486
486
|
data.name.inspect,
|
487
487
|
data.Characteristics.to_i,
|
488
488
|
Time.at(data.TimeDateStamp.to_i).utc.strftime('"%Y-%m-%d %H:%M:%S"'),
|
489
|
-
data.MajorVersion, data.MinorVersion,
|
490
|
-
data.Base
|
489
|
+
data.MajorVersion.to_i, data.MinorVersion.to_i,
|
490
|
+
data.Base.to_i
|
491
491
|
|
492
492
|
if @options[:verbose] > 0
|
493
493
|
[%w'Names', %w'EntryPoints Functions', %w'Ordinals NameOrdinals'].each do |x|
|
@@ -498,25 +498,26 @@ class PEdump::CLI
|
|
498
498
|
end
|
499
499
|
|
500
500
|
printf "# nFuncs=%d nNames=%d\n",
|
501
|
-
data.NumberOfFunctions,
|
502
|
-
data.NumberOfNames
|
501
|
+
data.NumberOfFunctions.to_i,
|
502
|
+
data.NumberOfNames.to_i
|
503
503
|
|
504
504
|
return unless data.name_ordinals.any? || data.entry_points.any? || data.names.any?
|
505
505
|
|
506
506
|
puts
|
507
507
|
|
508
508
|
ord2name = {}
|
509
|
-
data.
|
510
|
-
|
511
|
-
|
509
|
+
if data.names && data.names.any?
|
510
|
+
data.NumberOfNames.times do |i|
|
511
|
+
ord2name[data.name_ordinals[i]] ||= []
|
512
|
+
ord2name[data.name_ordinals[i]] << data.names[i]
|
513
|
+
end
|
512
514
|
end
|
513
515
|
|
514
516
|
printf "%5s %8s %s\n", "ORD", "ENTRY_VA", "NAME"
|
515
|
-
data.
|
516
|
-
ep = data.entry_points[i]
|
517
|
+
data.entry_points.each_with_index do |ep,i|
|
517
518
|
names = ord2name[i+data.Base].try(:join,', ')
|
518
519
|
next if ep.to_i == 0 && names.nil?
|
519
|
-
printf "%
|
520
|
+
printf "%5x %8x %s\n", i + data.Base, ep, names
|
520
521
|
end
|
521
522
|
end
|
522
523
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class PEdump
|
2
|
+
class CompositeIO
|
3
|
+
def initialize(*ios)
|
4
|
+
@ios = ios.flatten
|
5
|
+
@pos = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def read(amount = nil, buf = nil)
|
9
|
+
buf ||= ''; buf1 = ''
|
10
|
+
|
11
|
+
# truncates buffer to zero length if nothing read
|
12
|
+
@ios.first.read(amount,buf)
|
13
|
+
|
14
|
+
@ios[1..-1].each do |io|
|
15
|
+
break if amount && buf.size >= amount
|
16
|
+
io.read(amount ? (amount-buf.size) : nil, buf1)
|
17
|
+
buf << buf1
|
18
|
+
end
|
19
|
+
|
20
|
+
@pos += buf.size
|
21
|
+
|
22
|
+
buf.size > 0 ? buf : (amount ? nil : buf )
|
23
|
+
end
|
24
|
+
|
25
|
+
def tell
|
26
|
+
@pos
|
27
|
+
end
|
28
|
+
|
29
|
+
def eof?
|
30
|
+
@ios.all?(&:eof?)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/pedump/pe.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
class PEdump
|
2
|
+
class PE < Struct.new(
|
3
|
+
:signature, # "PE\x00\x00"
|
4
|
+
:image_file_header,
|
5
|
+
:image_optional_header, # includes data directory
|
6
|
+
:section_table
|
7
|
+
)
|
8
|
+
alias :ifh :image_file_header
|
9
|
+
alias :ioh :image_optional_header
|
10
|
+
alias :sections :section_table
|
11
|
+
alias :sections= :section_table=
|
12
|
+
def x64?
|
13
|
+
ifh && ifh.Machine == 0x8664
|
14
|
+
end
|
15
|
+
def dll?
|
16
|
+
ifh && ifh.flags.include?('DLL')
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def self.read f, args = nil
|
21
|
+
force = args.is_a?(Hash) && args[:force]
|
22
|
+
|
23
|
+
pe_offset = f.tell
|
24
|
+
pe_sig = f.read 4
|
25
|
+
logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
|
26
|
+
if pe_sig != "PE\x00\x00"
|
27
|
+
if force
|
28
|
+
logger.warn "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect})"
|
29
|
+
else
|
30
|
+
logger.error "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect}). (not forced)"
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
PE.new(pe_sig).tap do |pe|
|
35
|
+
pe.image_file_header = IMAGE_FILE_HEADER.read(f)
|
36
|
+
if pe.ifh.SizeOfOptionalHeader > 0
|
37
|
+
if pe.x64?
|
38
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
|
39
|
+
else
|
40
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER32.read(f, pe.ifh.SizeOfOptionalHeader)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if (nToRead=pe.ifh.NumberOfSections) > 0xffff
|
45
|
+
if force.is_a?(Numeric) && force > 1
|
46
|
+
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). forced. reading all"
|
47
|
+
else
|
48
|
+
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). not forced, reading first 65535"
|
49
|
+
nToRead = 65535
|
50
|
+
end
|
51
|
+
end
|
52
|
+
pe.sections = []
|
53
|
+
nToRead.times do
|
54
|
+
break if f.eof?
|
55
|
+
pe.sections << IMAGE_SECTION_HEADER.read(f)
|
56
|
+
end
|
57
|
+
|
58
|
+
if pe.sections.any?
|
59
|
+
# zero all missing values of last section
|
60
|
+
pe.sections.last.tap do |last_section|
|
61
|
+
last_section.each_pair do |k,v|
|
62
|
+
last_section[k] = 0 if v.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
pe_end = f.tell
|
68
|
+
if s=pe.sections.find{ |s| (pe_offset...pe_end).include?(s.va) }
|
69
|
+
if !f.respond_to?(:seek)
|
70
|
+
# already called with CompositeIO ?
|
71
|
+
logger.error "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! 2nd time?!"
|
72
|
+
|
73
|
+
elsif pe_end-pe_offset < 0x100_000
|
74
|
+
logger.warn "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! trying to rebuild..."
|
75
|
+
f.seek pe_offset
|
76
|
+
data = f.read(s.va-pe_offset)
|
77
|
+
f.seek s.PointerToRawData
|
78
|
+
io = CompositeIO.new(StringIO.new(data), f)
|
79
|
+
return PE.read(io, args)
|
80
|
+
else
|
81
|
+
logger.error "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! too big to rebuild!"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.logger; PEdump.logger; end
|
88
|
+
end
|
89
|
+
end
|
data/lib/pedump/version.rb
CHANGED
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.4.
|
8
|
+
s.version = "0.4.3"
|
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-20"
|
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"]
|
@@ -35,17 +35,20 @@ Gem::Specification.new do |s|
|
|
35
35
|
"data/userdb.txt",
|
36
36
|
"lib/pedump.rb",
|
37
37
|
"lib/pedump/cli.rb",
|
38
|
+
"lib/pedump/composite_io.rb",
|
38
39
|
"lib/pedump/packer.rb",
|
40
|
+
"lib/pedump/pe.rb",
|
39
41
|
"lib/pedump/sig_parser.rb",
|
40
42
|
"lib/pedump/version.rb",
|
41
43
|
"lib/pedump/version_info.rb",
|
42
44
|
"pedump.gemspec",
|
43
|
-
"samples/65535sects.7z",
|
44
45
|
"samples/calc.7z",
|
45
|
-
"samples/
|
46
|
-
"samples/imports_vterm.exe",
|
46
|
+
"samples/corkami.7z",
|
47
47
|
"samples/zlib.dll",
|
48
48
|
"spec/65535sects_spec.rb",
|
49
|
+
"spec/composite_io_spec.rb",
|
50
|
+
"spec/dllord_spec.rb",
|
51
|
+
"spec/foldedhdr_spec.rb",
|
49
52
|
"spec/imports_badterm_spec.rb",
|
50
53
|
"spec/imports_vterm_spec.rb",
|
51
54
|
"spec/pe_spec.rb",
|
@@ -53,7 +56,8 @@ Gem::Specification.new do |s|
|
|
53
56
|
"spec/resource_spec.rb",
|
54
57
|
"spec/sig_all_packers_spec.rb",
|
55
58
|
"spec/sig_spec.rb",
|
56
|
-
"spec/spec_helper.rb"
|
59
|
+
"spec/spec_helper.rb",
|
60
|
+
"spec/virtsectblXP_spec.rb"
|
57
61
|
]
|
58
62
|
s.homepage = "http://github.com/zed-0xff/pedump"
|
59
63
|
s.licenses = ["MIT"]
|
data/samples/corkami.7z
ADDED
Binary file
|
data/spec/65535sects_spec.rb
CHANGED
@@ -1,16 +1,8 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
3
|
|
4
|
-
describe '
|
5
|
-
before :all do
|
6
|
-
fname = File.expand_path(File.dirname(__FILE__) + '/../samples/65535sects.exe')
|
7
|
-
File.open(fname,"rb") do |f|
|
8
|
-
@pedump = PEdump.new(fname)
|
9
|
-
@sections = @pedump.sections(f)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
4
|
+
describe 'corkami/65535sects.exe' do
|
13
5
|
it "should have 65535 sections" do
|
14
|
-
|
6
|
+
sample.sections.size.should == 65535
|
15
7
|
end
|
16
8
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump/composite_io')
|
3
|
+
|
4
|
+
describe PEdump::CompositeIO do
|
5
|
+
it "concatenates" do
|
6
|
+
io = PEdump::CompositeIO.new(
|
7
|
+
StringIO.new('foo'),
|
8
|
+
StringIO.new('bar'),
|
9
|
+
StringIO.new('baz')
|
10
|
+
)
|
11
|
+
io.read.should == 'foobarbaz'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reads sequentally" do
|
15
|
+
io = PEdump::CompositeIO.new(
|
16
|
+
StringIO.new('foo1'),
|
17
|
+
StringIO.new('bar2'),
|
18
|
+
StringIO.new('baz')
|
19
|
+
)
|
20
|
+
io.read(3).should == 'foo'
|
21
|
+
io.read(3).should == '1ba'
|
22
|
+
io.read(3).should == 'r2b'
|
23
|
+
io.read(3).should == 'az'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "behaves like StringIO" do
|
27
|
+
io1 = StringIO.new('foo')
|
28
|
+
io2 = PEdump::CompositeIO.new(StringIO.new('foo'))
|
29
|
+
|
30
|
+
io1.read.should == io2.read # 'foo'
|
31
|
+
io1.read.should == io2.read # ''
|
32
|
+
io1.read(3).should == io2.read(3) # nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "tracks number of bytes read" do
|
36
|
+
io = PEdump::CompositeIO.new(
|
37
|
+
StringIO.new('foo1'),
|
38
|
+
StringIO.new('bar2'),
|
39
|
+
StringIO.new('baz')
|
40
|
+
)
|
41
|
+
io.tell.should == 0
|
42
|
+
io.read(3)
|
43
|
+
io.tell.should == 3
|
44
|
+
io.read(4)
|
45
|
+
io.tell.should == 7
|
46
|
+
io.read
|
47
|
+
io.tell.should == 11
|
48
|
+
io.read
|
49
|
+
io.tell.should == 11
|
50
|
+
io.read 10
|
51
|
+
io.tell.should == 11
|
52
|
+
end
|
53
|
+
|
54
|
+
it "chains eof? call" do
|
55
|
+
io = PEdump::CompositeIO.new(
|
56
|
+
StringIO.new('foo1'),
|
57
|
+
StringIO.new('bar2'),
|
58
|
+
StringIO.new('baz')
|
59
|
+
)
|
60
|
+
io.eof?.should be_false
|
61
|
+
io.read(3)
|
62
|
+
io.eof?.should be_false
|
63
|
+
io.read(4)
|
64
|
+
io.eof?.should be_false
|
65
|
+
io.read
|
66
|
+
io.eof?.should be_true
|
67
|
+
io.read
|
68
|
+
io.eof?.should be_true
|
69
|
+
io.read 10
|
70
|
+
io.eof?.should be_true
|
71
|
+
end
|
72
|
+
end
|
data/spec/dllord_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
|
+
|
4
|
+
describe 'corkami/dllord.dll' do
|
5
|
+
it "should have 1 import" do
|
6
|
+
sample.imports.size.should == 1
|
7
|
+
sample.imports.map(&:module_name).should == %w'msvcrt.dll'
|
8
|
+
sample.imports.map do |iid|
|
9
|
+
(iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
|
10
|
+
end.flatten.should == ["printf"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "exports at least 2 entries" do
|
14
|
+
sample.exports.Base.should == 0x313
|
15
|
+
sample.exports.name.should be_nil
|
16
|
+
sample.exports.names.should be_empty
|
17
|
+
sample.exports.name_ordinals.should be_empty
|
18
|
+
sample.exports.entry_points[0].should == 0xffff_ffff
|
19
|
+
sample.exports.entry_points[1].should == 0x1008
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
|
+
|
4
|
+
[ 'corkami/foldedhdr.exe', 'corkami/foldedhdrW7.exe' ].each do |fname|
|
5
|
+
describe fname do
|
6
|
+
before :all do
|
7
|
+
@sample = sample
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have 2 imports" do
|
11
|
+
@sample.imports.size.should == 2
|
12
|
+
@sample.imports.map(&:module_name).should == %w'kernel32.dll msvcrt.dll'
|
13
|
+
@sample.imports.map do |iid|
|
14
|
+
(iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
|
15
|
+
end.flatten.should == ["ExitProcess", "printf"]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have 1 section" do
|
19
|
+
@sample.sections.size.should == 1
|
20
|
+
s = @sample.sections.first
|
21
|
+
s.VirtualSize.should == 0x1000
|
22
|
+
s.VirtualAddress.should == 0x1000
|
23
|
+
s.SizeOfRawData.should == 0x200
|
24
|
+
s.PointerToRawData.should == 0x200
|
25
|
+
s.flags.should == 0xa0000000
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,58 +1,52 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
3
|
|
4
|
-
describe '
|
4
|
+
describe 'corkami/imports_badterm.exe' do
|
5
5
|
# PE with a 'bad' imports terminator, just the dll name is empty
|
6
6
|
# http://code.google.com/p/corkami/source/browse/trunk/asm/PE/imports_badterm.asm
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
File.open(fname,"rb") do |f|
|
11
|
-
@pedump = PEdump.new(fname)
|
12
|
-
@imports = @pedump.imports(f)
|
13
|
-
end
|
14
|
-
end
|
7
|
+
before :all do
|
8
|
+
@imports = sample.imports
|
9
|
+
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
it "should have 2 IMAGE_IMPORT_DESCRIPTORs" do
|
12
|
+
@imports.size.should == 2
|
13
|
+
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
it "should have only IMAGE_IMPORT_DESCRIPTORs" do
|
16
|
+
@imports.map(&:class).uniq.should == [PEdump::IMAGE_IMPORT_DESCRIPTOR]
|
17
|
+
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
19
|
+
it "should have all entries thunks equal" do
|
20
|
+
@imports.each do |iid|
|
21
|
+
iid.first_thunk.should == iid.original_first_thunk
|
28
22
|
end
|
23
|
+
end
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
25
|
+
describe "1st image_import_descriptor" do
|
26
|
+
it "should be from kernel32.dll" do
|
27
|
+
@imports[0].module_name.should == "kernel32.dll"
|
28
|
+
end
|
29
|
+
it "should have 1 function" do
|
30
|
+
@imports[0].first_thunk.size.should == 1
|
31
|
+
end
|
32
|
+
it "should have ExitProcess" do
|
33
|
+
@imports[0].first_thunk.first.name.should == "ExitProcess"
|
34
|
+
@imports[0].first_thunk.first.hint.should == 0
|
35
|
+
@imports[0].first_thunk.first.ordinal.should be_nil
|
42
36
|
end
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
39
|
+
describe "2nd image_import_descriptor" do
|
40
|
+
it "should be from msvcrt.dll" do
|
41
|
+
@imports[1].module_name.should == "msvcrt.dll"
|
42
|
+
end
|
43
|
+
it "should have 1 function" do
|
44
|
+
@imports[1].first_thunk.size.should == 1
|
45
|
+
end
|
46
|
+
it "should have printf" do
|
47
|
+
@imports[1].first_thunk.first.name.should == "printf"
|
48
|
+
@imports[1].first_thunk.first.hint.should == 0
|
49
|
+
@imports[1].first_thunk.first.ordinal.should be_nil
|
56
50
|
end
|
57
51
|
end
|
58
52
|
end
|
data/spec/imports_vterm_spec.rb
CHANGED
@@ -1,58 +1,52 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
3
|
|
4
|
-
describe '
|
4
|
+
describe 'corkami/imports_vterm.exe' do
|
5
5
|
# http://code.google.com/p/corkami/source/browse/trunk/asm/PE/imports_vterm.asm
|
6
6
|
#describe "import terminator in virtual space" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
File.open(fname,"rb") do |f|
|
11
|
-
@pedump = PEdump.new(fname)
|
12
|
-
@imports = @pedump.imports(f)
|
13
|
-
end
|
14
|
-
end
|
7
|
+
before :all do
|
8
|
+
@imports = sample.imports
|
9
|
+
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
it "should have 2 IMAGE_IMPORT_DESCRIPTORs" do
|
12
|
+
@imports.size.should == 2
|
13
|
+
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
it "should have only IMAGE_IMPORT_DESCRIPTORs" do
|
16
|
+
@imports.map(&:class).uniq.should == [PEdump::IMAGE_IMPORT_DESCRIPTOR]
|
17
|
+
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
19
|
+
it "should have all entries thunks equal" do
|
20
|
+
@imports.each do |iid|
|
21
|
+
iid.first_thunk.should == iid.original_first_thunk
|
28
22
|
end
|
23
|
+
end
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
25
|
+
describe "1st image_import_descriptor" do
|
26
|
+
it "should be from kernel32.dll" do
|
27
|
+
@imports[0].module_name.should == "kernel32.dll"
|
28
|
+
end
|
29
|
+
it "should have 1 function" do
|
30
|
+
@imports[0].first_thunk.size.should == 1
|
31
|
+
end
|
32
|
+
it "should have ExitProcess" do
|
33
|
+
@imports[0].first_thunk.first.name.should == "ExitProcess"
|
34
|
+
@imports[0].first_thunk.first.hint.should == 0
|
35
|
+
@imports[0].first_thunk.first.ordinal.should be_nil
|
42
36
|
end
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
39
|
+
describe "2nd image_import_descriptor" do
|
40
|
+
it "should be from msvcrt.dll" do
|
41
|
+
@imports[1].module_name.should == "msvcrt.dll"
|
42
|
+
end
|
43
|
+
it "should have 1 function" do
|
44
|
+
@imports[1].first_thunk.size.should == 1
|
45
|
+
end
|
46
|
+
it "should have printf" do
|
47
|
+
@imports[1].first_thunk.first.name.should == "printf"
|
48
|
+
@imports[1].first_thunk.first.hint.should == 0
|
49
|
+
@imports[1].first_thunk.first.ordinal.should be_nil
|
56
50
|
end
|
57
51
|
end
|
58
52
|
end
|
@@ -3,12 +3,22 @@ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump/packer')
|
|
3
3
|
|
4
4
|
describe "PEdump::Packer" do
|
5
5
|
describe "matchers" do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
if ENV['SLOW']
|
7
|
+
PEdump::SigParser.parse(:raw => true).each do |sig|
|
8
|
+
data = sig.re.join
|
9
|
+
next if data == "This program cannot be run in DOS mo"
|
10
|
+
it "should find #{sig.name}" do
|
11
|
+
a = PEdump::Packer.of(data).map(&:name)
|
12
|
+
a.size.should > 0
|
13
|
+
|
14
|
+
a = sig.name.split - a.join(' ').split - ['Exe','PE']
|
15
|
+
a.delete_if{ |x| x[/[vV\.\/()\[\]]/] }
|
16
|
+
p a if a.size > 1
|
17
|
+
a.size.should < 2
|
18
|
+
end
|
11
19
|
end
|
20
|
+
else
|
21
|
+
pending "SLOW"
|
12
22
|
end
|
13
23
|
end
|
14
24
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,5 +8,22 @@ require 'pedump'
|
|
8
8
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
9
|
|
10
10
|
RSpec.configure do |config|
|
11
|
-
|
11
|
+
end
|
12
|
+
|
13
|
+
def sample
|
14
|
+
@pedump ||=
|
15
|
+
begin
|
16
|
+
fname =
|
17
|
+
if self.example
|
18
|
+
# called from it(...)
|
19
|
+
self.example.full_description.split.first
|
20
|
+
else
|
21
|
+
# called from before(:all)
|
22
|
+
self.class.metadata[:example_group][:description_args].first
|
23
|
+
end
|
24
|
+
fname = File.expand_path(File.dirname(__FILE__) + '/../samples/' + fname)
|
25
|
+
File.open(fname,"rb") do |f|
|
26
|
+
PEdump.new(fname).dump
|
27
|
+
end
|
28
|
+
end
|
12
29
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
|
3
|
+
|
4
|
+
describe 'corkami/virtsectblXP.exe' do
|
5
|
+
it "should have 2 imports" do
|
6
|
+
sample.imports.size.should == 2
|
7
|
+
sample.imports.map(&:module_name).should == %w'kernel32.dll msvcrt.dll'
|
8
|
+
sample.imports.map do |iid|
|
9
|
+
(iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
|
10
|
+
end.flatten.should == ["ExitProcess", "printf"]
|
11
|
+
end
|
12
|
+
end
|
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.4.
|
4
|
+
version: 0.4.3
|
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-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multipart-post
|
16
|
-
requirement: &
|
16
|
+
requirement: &70155235875660 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.1.4
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70155235875660
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: progressbar
|
27
|
-
requirement: &
|
27
|
+
requirement: &70155235891480 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.9.2
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70155235891480
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70155235890940 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.3.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70155235890940
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
|
-
requirement: &
|
49
|
+
requirement: &70155235890060 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.0.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70155235890060
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: jeweler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70155235889180 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.6.4
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70155235889180
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rcov
|
71
|
-
requirement: &
|
71
|
+
requirement: &70155235888640 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70155235888640
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: awesome_print
|
82
|
-
requirement: &
|
82
|
+
requirement: &70155235888120 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70155235888120
|
91
91
|
description: dump headers, sections, extract resources of win32 PE exe,dll,etc
|
92
92
|
email: zed.0xff@gmail.com
|
93
93
|
executables:
|
@@ -114,17 +114,20 @@ files:
|
|
114
114
|
- data/userdb.txt
|
115
115
|
- lib/pedump.rb
|
116
116
|
- lib/pedump/cli.rb
|
117
|
+
- lib/pedump/composite_io.rb
|
117
118
|
- lib/pedump/packer.rb
|
119
|
+
- lib/pedump/pe.rb
|
118
120
|
- lib/pedump/sig_parser.rb
|
119
121
|
- lib/pedump/version.rb
|
120
122
|
- lib/pedump/version_info.rb
|
121
123
|
- pedump.gemspec
|
122
|
-
- samples/65535sects.7z
|
123
124
|
- samples/calc.7z
|
124
|
-
- samples/
|
125
|
-
- samples/imports_vterm.exe
|
125
|
+
- samples/corkami.7z
|
126
126
|
- samples/zlib.dll
|
127
127
|
- spec/65535sects_spec.rb
|
128
|
+
- spec/composite_io_spec.rb
|
129
|
+
- spec/dllord_spec.rb
|
130
|
+
- spec/foldedhdr_spec.rb
|
128
131
|
- spec/imports_badterm_spec.rb
|
129
132
|
- spec/imports_vterm_spec.rb
|
130
133
|
- spec/pe_spec.rb
|
@@ -133,6 +136,7 @@ files:
|
|
133
136
|
- spec/sig_all_packers_spec.rb
|
134
137
|
- spec/sig_spec.rb
|
135
138
|
- spec/spec_helper.rb
|
139
|
+
- spec/virtsectblXP_spec.rb
|
136
140
|
homepage: http://github.com/zed-0xff/pedump
|
137
141
|
licenses:
|
138
142
|
- MIT
|
@@ -148,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
152
|
version: '0'
|
149
153
|
segments:
|
150
154
|
- 0
|
151
|
-
hash:
|
155
|
+
hash: 2848967461238967885
|
152
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
157
|
none: false
|
154
158
|
requirements:
|
data/samples/65535sects.7z
DELETED
Binary file
|
data/samples/imports_badterm.exe
DELETED
Binary file
|
data/samples/imports_vterm.exe
DELETED
Binary file
|