pedump 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.md +352 -10
- data/README.md.tpl +57 -2
- data/Rakefile +54 -27
- data/VERSION +1 -1
- data/data/fs.txt +1595 -0
- data/data/sig.bin +0 -0
- data/data/signatures.txt +678 -0
- data/data/{sig.txt → userdb.txt} +0 -0
- data/lib/pedump.rb +125 -64
- data/lib/pedump/cli.rb +135 -25
- data/lib/pedump/packer.rb +66 -65
- data/lib/pedump/sig_parser.rb +386 -0
- data/lib/pedump/version.rb +2 -2
- data/lib/pedump/version_info.rb +166 -0
- data/pedump.gemspec +14 -3
- data/samples/zlib.dll +0 -0
- data/spec/pedump_spec.rb +3 -3
- data/spec/resource_spec.rb +13 -0
- data/spec/sig_all_packers_spec.rb +14 -0
- data/spec/sig_spec.rb +63 -0
- metadata +35 -16
data/data/{sig.txt → userdb.txt}
RENAMED
File without changes
|
data/lib/pedump.rb
CHANGED
@@ -40,6 +40,8 @@ class PEdump
|
|
40
40
|
|
41
41
|
VERSION = Version::STRING
|
42
42
|
|
43
|
+
@@logger = nil
|
44
|
+
|
43
45
|
def initialize fname, params = {}
|
44
46
|
@fname = fname
|
45
47
|
@force = params[:force]
|
@@ -62,6 +64,17 @@ class PEdump
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
67
|
+
module Readable
|
68
|
+
def read file, size = nil
|
69
|
+
size ||= const_get 'SIZE'
|
70
|
+
data = file.read(size).to_s
|
71
|
+
if data.size < size && PEdump.logger
|
72
|
+
PEdump.logger.error "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
|
73
|
+
end
|
74
|
+
new(*data.unpack(const_get('FORMAT')))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
65
78
|
class << self
|
66
79
|
def logger; @@logger; end
|
67
80
|
def logger= l; @@logger=l; end
|
@@ -89,14 +102,7 @@ class PEdump
|
|
89
102
|
to_a.all?{ |t| t == 0 || t.nil? || t.to_s.tr("\x00","").empty? }
|
90
103
|
end
|
91
104
|
end
|
92
|
-
|
93
|
-
size ||= const_get 'SIZE'
|
94
|
-
data = file.read(size).to_s
|
95
|
-
if data.size < size && PEdump.logger
|
96
|
-
PEdump.logger.error "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
|
97
|
-
end
|
98
|
-
new(*data.unpack(const_get('FORMAT')))
|
99
|
-
end
|
105
|
+
x.extend Readable
|
100
106
|
end
|
101
107
|
end
|
102
108
|
end
|
@@ -140,10 +146,13 @@ class PEdump
|
|
140
146
|
def x64?
|
141
147
|
ifh && ifh.Machine == 0x8664
|
142
148
|
end
|
149
|
+
def dll?
|
150
|
+
ifh && ifh.flags.include?('DLL')
|
151
|
+
end
|
143
152
|
end
|
144
153
|
|
145
154
|
# http://msdn.microsoft.com/en-us/library/ms809762.aspx
|
146
|
-
IMAGE_FILE_HEADER
|
155
|
+
class IMAGE_FILE_HEADER < create_struct( 'v2V3v2',
|
147
156
|
:Machine, # w
|
148
157
|
:NumberOfSections, # w
|
149
158
|
:TimeDateStamp, # dw
|
@@ -152,19 +161,97 @@ class PEdump
|
|
152
161
|
:SizeOfOptionalHeader, # w
|
153
162
|
:Characteristics # w
|
154
163
|
)
|
155
|
-
|
164
|
+
# Characteristics, http://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=VS.85).aspx)
|
165
|
+
FLAGS = {
|
166
|
+
0x0001 => 'RELOCS_STRIPPED', # Relocation information was stripped from the file.
|
167
|
+
# The file must be loaded at its preferred base address.
|
168
|
+
# If the base address is not available, the loader reports an error.
|
169
|
+
0x0002 => 'EXECUTABLE_IMAGE',
|
170
|
+
0x0004 => 'LINE_NUMS_STRIPPED',
|
171
|
+
0x0008 => 'LOCAL_SYMS_STRIPPED',
|
172
|
+
0x0010 => 'AGGRESIVE_WS_TRIM', # Aggressively trim the working set. This value is obsolete as of Windows 2000.
|
173
|
+
0x0020 => 'LARGE_ADDRESS_AWARE', # The application can handle addresses larger than 2 GB.
|
174
|
+
0x0040 => '16BIT_MACHINE',
|
175
|
+
0x0080 => 'BYTES_REVERSED_LO', # The bytes of the word are reversed. This flag is obsolete.
|
176
|
+
0x0100 => '32BIT_MACHINE',
|
177
|
+
0x0200 => 'DEBUG_STRIPPED',
|
178
|
+
0x0400 => 'REMOVABLE_RUN_FROM_SWAP',
|
179
|
+
0x0800 => 'NET_RUN_FROM_SWAP',
|
180
|
+
0x1000 => 'SYSTEM',
|
181
|
+
0x2000 => 'DLL',
|
182
|
+
0x4000 => 'UP_SYSTEM_ONLY', # The file should be run only on a uniprocessor computer.
|
183
|
+
0x8000 => 'BYTES_REVERSED_HI' # The bytes of the word are reversed. This flag is obsolete.
|
184
|
+
}
|
185
|
+
|
156
186
|
def initialize *args
|
157
187
|
super
|
158
188
|
self.TimeDateStamp = Time.at(self.TimeDateStamp)
|
159
189
|
end
|
160
|
-
def
|
161
|
-
|
162
|
-
|
190
|
+
def flags
|
191
|
+
FLAGS.find_all{ |k,v| (self.Characteristics & k) != 0 }.map(&:last)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module IMAGE_OPTIONAL_HEADER
|
196
|
+
# DllCharacteristics, http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx)
|
197
|
+
FLAGS = {
|
198
|
+
0x0001 => '0x01', # reserved
|
199
|
+
0x0002 => '0x02', # reserved
|
200
|
+
0x0004 => '0x04', # reserved
|
201
|
+
0x0008 => '0x08', # reserved
|
202
|
+
0x0010 => '0x10', # ?
|
203
|
+
0x0020 => '0x20', # ?
|
204
|
+
0x0040 => 'DYNAMIC_BASE',
|
205
|
+
0x0080 => 'FORCE_INTEGRITY',
|
206
|
+
0x0100 => 'NX_COMPAT',
|
207
|
+
0x0200 => 'NO_ISOLATION',
|
208
|
+
0x0400 => 'NO_SEH',
|
209
|
+
0x0800 => 'NO_BIND',
|
210
|
+
0x1000 => '0x1000', # ?
|
211
|
+
0x2000 => 'WDM_DRIVER',
|
212
|
+
0x4000 => '0x4000', # ?
|
213
|
+
0x8000 => 'TERMINAL_SERVER_AWARE'
|
214
|
+
}
|
215
|
+
def initialize *args
|
216
|
+
super
|
217
|
+
self.extend InstanceMethods
|
218
|
+
end
|
219
|
+
def self.included base
|
220
|
+
base.extend ClassMethods
|
221
|
+
end
|
222
|
+
module ClassMethods
|
223
|
+
def read file, size = nil
|
224
|
+
usual_size = self.const_get('USUAL_SIZE')
|
225
|
+
cSIZE = self.const_get 'SIZE'
|
226
|
+
cFORMAT = self.const_get 'FORMAT'
|
227
|
+
size ||= cSIZE
|
228
|
+
PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
|
229
|
+
new(*file.read([size,cSIZE].min).to_s.unpack(cFORMAT)).tap do |ioh|
|
230
|
+
ioh.DataDirectory = []
|
231
|
+
|
232
|
+
# check if "...this address is outside the memory mapped file and is zeroed by the OS"
|
233
|
+
# see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
|
234
|
+
ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
|
235
|
+
|
236
|
+
# http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
|
237
|
+
# maximum of 0x10 entries, even if bigger
|
238
|
+
[0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
|
239
|
+
ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
|
240
|
+
ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
|
241
|
+
end
|
242
|
+
#ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
module InstanceMethods
|
247
|
+
def flags
|
248
|
+
FLAGS.find_all{ |k,v| (self.DllCharacteristics & k) != 0 }.map(&:last)
|
249
|
+
end
|
163
250
|
end
|
164
251
|
end
|
165
252
|
|
166
253
|
# http://msdn.microsoft.com/en-us/library/ms809762.aspx
|
167
|
-
|
254
|
+
class IMAGE_OPTIONAL_HEADER32 < create_struct( 'vC2V9v6V4v2V6',
|
168
255
|
:Magic, # w
|
169
256
|
:MajorLinkerVersion, :MinorLinkerVersion, # 2b
|
170
257
|
:SizeOfCode, :SizeOfInitializedData, :SizeOfUninitializedData, :AddressOfEntryPoint, # 9dw
|
@@ -177,9 +264,12 @@ class PEdump
|
|
177
264
|
:LoaderFlags, :NumberOfRvaAndSizes,
|
178
265
|
:DataDirectory # readed manually
|
179
266
|
)
|
267
|
+
USUAL_SIZE = 224
|
268
|
+
include IMAGE_OPTIONAL_HEADER
|
269
|
+
end
|
180
270
|
|
181
271
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=VS.85).aspx)
|
182
|
-
IMAGE_OPTIONAL_HEADER64
|
272
|
+
class IMAGE_OPTIONAL_HEADER64 < create_struct( 'vC2V5QV2v6V4v2Q4V2',
|
183
273
|
:Magic, # w
|
184
274
|
:MajorLinkerVersion, :MinorLinkerVersion, # 2b
|
185
275
|
:SizeOfCode, :SizeOfInitializedData, :SizeOfUninitializedData, :AddressOfEntryPoint, :BaseOfCode, # 5dw
|
@@ -193,49 +283,8 @@ class PEdump
|
|
193
283
|
:LoaderFlags, :NumberOfRvaAndSizes, #2dw
|
194
284
|
:DataDirectory # readed manually
|
195
285
|
)
|
196
|
-
|
197
|
-
|
198
|
-
def self.read file, size = SIZE
|
199
|
-
usual_size = 224
|
200
|
-
PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
|
201
|
-
new(*file.read([size,SIZE].min).to_s.unpack(FORMAT)).tap do |ioh|
|
202
|
-
ioh.DataDirectory = []
|
203
|
-
|
204
|
-
# check if "...this address is outside the memory mapped file and is zeroed by the OS"
|
205
|
-
# see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
|
206
|
-
ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
|
207
|
-
|
208
|
-
# http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
|
209
|
-
# maximum of 0x10 entries, even if bigger
|
210
|
-
[0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
|
211
|
-
ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
|
212
|
-
ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
|
213
|
-
end
|
214
|
-
#ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
class IMAGE_OPTIONAL_HEADER64
|
220
|
-
def self.read file, size = SIZE
|
221
|
-
usual_size = 240
|
222
|
-
PEdump.logger.warn "[?] unusual size of IMAGE_OPTIONAL_HEADER = #{size} (must be #{usual_size})" if size != usual_size
|
223
|
-
new(*file.read([size,SIZE].min).unpack(FORMAT)).tap do |ioh|
|
224
|
-
ioh.DataDirectory = []
|
225
|
-
|
226
|
-
# check if "...this address is outside the memory mapped file and is zeroed by the OS"
|
227
|
-
# see http://www.phreedom.org/solar/code/tinype/, section "Removing the data directories"
|
228
|
-
ioh.each_pair{ |k,v| ioh[k] = 0 if v.nil? }
|
229
|
-
|
230
|
-
# http://opcode0x90.wordpress.com/2007/04/22/windows-loader-does-it-differently/
|
231
|
-
# maximum of 0x10 entries, even if bigger
|
232
|
-
[0x10,ioh.NumberOfRvaAndSizes].min.times do |idx|
|
233
|
-
ioh.DataDirectory << IMAGE_DATA_DIRECTORY.read(file)
|
234
|
-
ioh.DataDirectory.last.type = IMAGE_DATA_DIRECTORY::TYPES[idx]
|
235
|
-
end
|
236
|
-
#ioh.DataDirectory.pop while ioh.DataDirectory.last.empty?
|
237
|
-
end
|
238
|
-
end
|
286
|
+
USUAL_SIZE = 240
|
287
|
+
include IMAGE_OPTIONAL_HEADER
|
239
288
|
end
|
240
289
|
|
241
290
|
IMAGE_DATA_DIRECTORY = create_struct( "VV", :va, :size, :type )
|
@@ -418,7 +467,7 @@ class PEdump
|
|
418
467
|
if pe.x64?
|
419
468
|
pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
|
420
469
|
else
|
421
|
-
pe.image_optional_header =
|
470
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER32.read(f, pe.ifh.SizeOfOptionalHeader)
|
422
471
|
end
|
423
472
|
end
|
424
473
|
|
@@ -444,7 +493,7 @@ class PEdump
|
|
444
493
|
|
445
494
|
# OPTIONAL: assigns @mz, @rich_hdr, @pe, etc
|
446
495
|
def dump f=nil
|
447
|
-
f ? _dump_handle(f) : File.open(@fname){ |f| _dump_handle(f) }
|
496
|
+
f ? _dump_handle(f) : File.open(@fname,'rb'){ |f| _dump_handle(f) }
|
448
497
|
self
|
449
498
|
end
|
450
499
|
|
@@ -453,6 +502,7 @@ class PEdump
|
|
453
502
|
resources(h) # includes pe(h)
|
454
503
|
imports h
|
455
504
|
exports h
|
505
|
+
packer h
|
456
506
|
end
|
457
507
|
|
458
508
|
def data_directory f=nil
|
@@ -795,7 +845,7 @@ class PEdump
|
|
795
845
|
f.seek file_offset
|
796
846
|
data << CUR_ICO_HEADER.read(f)
|
797
847
|
nRead = CUR_ICO_HEADER::SIZE
|
798
|
-
data.last.wNumImages.times do
|
848
|
+
data.last.wNumImages.to_i.times do
|
799
849
|
if nRead >= self.size
|
800
850
|
PEdump.logger.error "[!] refusing to read CURDIRENTRY beyond resource size"
|
801
851
|
break
|
@@ -807,7 +857,7 @@ class PEdump
|
|
807
857
|
f.seek file_offset
|
808
858
|
data << CUR_ICO_HEADER.read(f)
|
809
859
|
nRead = CUR_ICO_HEADER::SIZE
|
810
|
-
data.last.wNumImages.times do
|
860
|
+
data.last.wNumImages.to_i.times do
|
811
861
|
if nRead >= self.size
|
812
862
|
PEdump.logger.error "[!] refusing to read ICODIRENTRY beyond resource size"
|
813
863
|
break
|
@@ -839,6 +889,10 @@ class PEdump
|
|
839
889
|
end
|
840
890
|
end
|
841
891
|
# XXX: check if readed strings summary length is less than resource data length
|
892
|
+
when 'VERSION'
|
893
|
+
require 'pedump/version_info'
|
894
|
+
f.seek file_offset
|
895
|
+
data << PEdump::VS_VERSIONINFO.read(f)
|
842
896
|
end
|
843
897
|
|
844
898
|
data.delete_if do |x|
|
@@ -854,7 +908,7 @@ class PEdump
|
|
854
908
|
self.valid =
|
855
909
|
case type
|
856
910
|
when 'BITMAP','ICON','CURSOR'
|
857
|
-
data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? }
|
911
|
+
data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? } || data.first == 'PNG'
|
858
912
|
else
|
859
913
|
true
|
860
914
|
end
|
@@ -929,6 +983,10 @@ class PEdump
|
|
929
983
|
@resources ||= _scan_resources(f)
|
930
984
|
end
|
931
985
|
|
986
|
+
def version_info f=nil
|
987
|
+
resources(f) && resources(f).find_all{ |res| res.type == 'VERSION' }.map(&:data).flatten
|
988
|
+
end
|
989
|
+
|
932
990
|
def _scan_resources f=nil, dir=nil
|
933
991
|
dir ||= resource_directory(f)
|
934
992
|
return nil unless dir
|
@@ -978,6 +1036,9 @@ class PEdump
|
|
978
1036
|
if !(va=@pe.ioh.AddressOfEntryPoint)
|
979
1037
|
logger.error "[?] can't find EntryPoint RVA"
|
980
1038
|
nil
|
1039
|
+
elsif va == 0 && @pe.dll?
|
1040
|
+
logger.debug "[.] it's a DLL with no EntryPoint"
|
1041
|
+
nil
|
981
1042
|
elsif !(ofs = va2file(va))
|
982
1043
|
logger.error "[?] can't find EntryPoint RVA (0x#{va.to_s(16)}) file offset"
|
983
1044
|
nil
|
@@ -987,7 +1048,7 @@ class PEdump
|
|
987
1048
|
logger.error "[?] no packer definitions found"
|
988
1049
|
nil
|
989
1050
|
else
|
990
|
-
Packer.of
|
1051
|
+
Packer.of f, :ep_offset => ofs
|
991
1052
|
end
|
992
1053
|
end
|
993
1054
|
end
|
data/lib/pedump/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pedump'
|
2
|
+
require 'pedump/packer'
|
2
3
|
require 'optparse'
|
3
4
|
|
4
5
|
unless Object.instance_methods.include?(:try)
|
@@ -14,7 +15,7 @@ class PEdump::CLI
|
|
14
15
|
|
15
16
|
KNOWN_ACTIONS = (
|
16
17
|
%w'mz dos_stub rich pe data_directory sections' +
|
17
|
-
%w'strings resources resource_directory imports exports packer web packer_only'
|
18
|
+
%w'strings resources resource_directory imports exports version_info packer web packer_only'
|
18
19
|
).map(&:to_sym)
|
19
20
|
|
20
21
|
DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only'.map(&:to_sym)
|
@@ -27,18 +28,20 @@ class PEdump::CLI
|
|
27
28
|
|
28
29
|
def run
|
29
30
|
@actions = []
|
30
|
-
@options = { :format => :table }
|
31
|
+
@options = { :format => :table, :verbose => 0 }
|
31
32
|
optparser = OptionParser.new do |opts|
|
32
33
|
opts.banner = "Usage: pedump [options]"
|
33
34
|
|
34
|
-
opts.on "
|
35
|
+
opts.on "--version", "Print version information and exit" do
|
35
36
|
puts PEdump::VERSION
|
36
37
|
exit
|
37
38
|
end
|
38
|
-
opts.on "-v", "--
|
39
|
-
@options[:verbose] ||= 0
|
39
|
+
opts.on "-v", "--verbose", "Run verbosely","(can be used multiple times)" do |v|
|
40
40
|
@options[:verbose] += 1
|
41
41
|
end
|
42
|
+
opts.on "-q", "--quiet", "Silent any warnings","(can be used multiple times)" do |v|
|
43
|
+
@options[:verbose] -= 1
|
44
|
+
end
|
42
45
|
opts.on "-F", "--force", "Try to dump by all means","(can cause exceptions & heavy wounds)" do |v|
|
43
46
|
@options[:force] ||= 0
|
44
47
|
@options[:force] += 1
|
@@ -48,11 +51,25 @@ class PEdump::CLI
|
|
48
51
|
@options[:format] = v
|
49
52
|
end
|
50
53
|
KNOWN_ACTIONS.each do |t|
|
51
|
-
|
54
|
+
a = [
|
55
|
+
"--#{t.to_s.tr('_','-')}",
|
56
|
+
eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }")
|
57
|
+
]
|
58
|
+
a.unshift(a[0][1,2].upcase) if a[0] =~ /--(((ex|im)port|section|resource)s|version-info)/
|
59
|
+
a.unshift(a[0][1,2]) if a[0] =~ /--strings/
|
60
|
+
opts.on *a
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on "--deep", "packer deep scan, significantly slower" do
|
64
|
+
@options[:deep] ||= 0
|
65
|
+
@options[:deep] += 1
|
66
|
+
PEdump::Packer.default_deep = @options[:deep]
|
52
67
|
end
|
68
|
+
|
53
69
|
opts.on '-P', "--packer-only", "packer/compiler detect only,","mimics 'file' command output" do
|
54
70
|
@actions << :packer_only
|
55
71
|
end
|
72
|
+
|
56
73
|
opts.on "--all", "Dump all but resource-directory (default)" do
|
57
74
|
@actions = DEFAULT_ALL_ACTIONS
|
58
75
|
end
|
@@ -87,11 +104,7 @@ class PEdump::CLI
|
|
87
104
|
@file_name = fname
|
88
105
|
|
89
106
|
File.open(fname,'rb') do |f|
|
90
|
-
@pedump =
|
91
|
-
if @options[:verbose]
|
92
|
-
x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
|
93
|
-
end
|
94
|
-
end
|
107
|
+
@pedump = create_pedump fname
|
95
108
|
|
96
109
|
next if !@options[:force] && !@pedump.mz(f)
|
97
110
|
|
@@ -109,18 +122,34 @@ class PEdump::CLI
|
|
109
122
|
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
110
123
|
end
|
111
124
|
|
125
|
+
def create_pedump fname
|
126
|
+
PEdump.new(fname, :force => @options[:force]).tap do |x|
|
127
|
+
x.logger.level =
|
128
|
+
case @options[:verbose]
|
129
|
+
when -100..-3
|
130
|
+
Logger::FATAL + 1
|
131
|
+
when -2
|
132
|
+
Logger::FATAL
|
133
|
+
when -1
|
134
|
+
Logger::ERROR
|
135
|
+
when 0
|
136
|
+
Logger::WARN # default
|
137
|
+
when 1
|
138
|
+
Logger::INFO
|
139
|
+
when 2..100
|
140
|
+
Logger::DEBUG
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
112
145
|
def dump_packer_only fnames
|
113
146
|
max_fname_len = fnames.map(&:size).max
|
114
147
|
fnames.each do |fname|
|
115
148
|
File.open(fname,'rb') do |f|
|
116
|
-
@pedump =
|
117
|
-
if @options[:verbose]
|
118
|
-
x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
|
119
|
-
end
|
120
|
-
end
|
149
|
+
@pedump = create_pedump fname
|
121
150
|
packers = @pedump.packers(f)
|
122
151
|
pname = Array(packers).first.try(:packer).try(:name)
|
123
|
-
pname ||= "unknown" if @options[:verbose]
|
152
|
+
pname ||= "unknown" if @options[:verbose] > 0
|
124
153
|
printf("%-*s %s\n", max_fname_len+1, "#{fname}:", pname) if pname
|
125
154
|
end
|
126
155
|
end
|
@@ -248,6 +277,8 @@ class PEdump::CLI
|
|
248
277
|
return dump_imports(data)
|
249
278
|
when :exports
|
250
279
|
return dump_exports(data)
|
280
|
+
when :version_info
|
281
|
+
return dump_version_info(data)
|
251
282
|
else
|
252
283
|
if data.is_a?(Struct) && data.respond_to?(:pack)
|
253
284
|
data = data.pack
|
@@ -298,8 +329,22 @@ class PEdump::CLI
|
|
298
329
|
:Subsystem => PEdump::IMAGE_SUBSYSTEMS
|
299
330
|
}
|
300
331
|
|
332
|
+
def _flags2string flags
|
333
|
+
return '' if !flags || flags.empty?
|
334
|
+
a = [flags.shift.dup]
|
335
|
+
flags.each do |f|
|
336
|
+
if (a.last.size + f.size) < 40
|
337
|
+
a.last << ", " << f
|
338
|
+
else
|
339
|
+
a << f.dup
|
340
|
+
end
|
341
|
+
end
|
342
|
+
a.join("\n"+ ' '*58)
|
343
|
+
end
|
344
|
+
|
301
345
|
def dump_generic_table data
|
302
346
|
data.each_pair do |k,v|
|
347
|
+
next if [:DataDirectory, :section_table].include?(k)
|
303
348
|
case v
|
304
349
|
when Numeric
|
305
350
|
case k
|
@@ -309,20 +354,25 @@ class PEdump::CLI
|
|
309
354
|
when /TimeDateStamp/
|
310
355
|
printf "%30s: %24s\n", k, Time.at(v).strftime('"%Y-%m-%d %H:%M:%S"')
|
311
356
|
else
|
357
|
+
comment = ''
|
312
358
|
if COMMENTS[k]
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
359
|
+
comment = COMMENTS[k][v] || (COMMENTS[k].is_a?(Hash) ? COMMENTS[k]['default'] : '') || ''
|
360
|
+
elsif data.is_a?(PEdump::IMAGE_FILE_HEADER) && k == :Characteristics
|
361
|
+
comment = _flags2string(data.flags)
|
362
|
+
elsif k == :DllCharacteristics
|
363
|
+
comment = _flags2string(data.flags)
|
317
364
|
end
|
365
|
+
comment.strip!
|
366
|
+
comment = " #{comment}" unless comment.empty?
|
367
|
+
printf "%30s: %10d %12s%s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)), comment
|
318
368
|
end
|
319
369
|
when Struct
|
370
|
+
# IMAGE_FILE_HEADER:
|
371
|
+
# IMAGE_OPTIONAL_HEADER:
|
320
372
|
printf "\n# %s:\n", v.class.to_s.split('::').last
|
321
373
|
dump_table v
|
322
374
|
when Time
|
323
375
|
printf "%30s: %24s\n", k, v.strftime('"%Y-%m-%d %H:%M:%S"')
|
324
|
-
when Array
|
325
|
-
next if %w'DataDirectory section_table'.include?(k)
|
326
376
|
else
|
327
377
|
printf "%30s: %24s\n", k, v.to_s.inspect
|
328
378
|
end
|
@@ -348,6 +398,8 @@ class PEdump::CLI
|
|
348
398
|
dump_imports data
|
349
399
|
when PEdump::Packer::Match
|
350
400
|
dump_packers data
|
401
|
+
when PEdump::VS_VERSIONINFO
|
402
|
+
dump_version_info data
|
351
403
|
else
|
352
404
|
puts "[?] don't know how to dump: #{data.inspect[0,50]}" unless data.empty?
|
353
405
|
end
|
@@ -360,8 +412,66 @@ class PEdump::CLI
|
|
360
412
|
end
|
361
413
|
end
|
362
414
|
|
415
|
+
def dump_version_info data
|
416
|
+
if @options[:format] != :table
|
417
|
+
File.open(@file_name,'rb') do |f|
|
418
|
+
@pedump.resources.find_all{ |r| r.type == 'VERSION'}.each do |res|
|
419
|
+
f.seek res.file_offset
|
420
|
+
data = f.read(res.size)
|
421
|
+
dump data
|
422
|
+
end
|
423
|
+
end
|
424
|
+
return
|
425
|
+
end
|
426
|
+
|
427
|
+
fmt = " %-20s: %s\n"
|
428
|
+
data.each do |vi|
|
429
|
+
puts "# VS_FIXEDFILEINFO:"
|
430
|
+
|
431
|
+
if @options[:verbose] > 0 || vi.Value.dwSignature != 0xfeef04bd
|
432
|
+
printf(fmt, "Signature", "0x#{vi.Value.dwSignature.to_s(16)}")
|
433
|
+
end
|
434
|
+
|
435
|
+
printf fmt, 'FileVersion', [
|
436
|
+
vi.Value.dwFileVersionMS >> 16,
|
437
|
+
vi.Value.dwFileVersionMS & 0xffff,
|
438
|
+
vi.Value.dwFileVersionLS >> 16,
|
439
|
+
vi.Value.dwFileVersionLS & 0xffff
|
440
|
+
].join('.')
|
441
|
+
|
442
|
+
printf fmt, 'ProductVersion', [
|
443
|
+
vi.Value.dwProductVersionMS >> 16,
|
444
|
+
vi.Value.dwProductVersionMS & 0xffff,
|
445
|
+
vi.Value.dwProductVersionLS >> 16,
|
446
|
+
vi.Value.dwProductVersionLS & 0xffff
|
447
|
+
].join('.')
|
448
|
+
|
449
|
+
vi.Value.each_pair do |k,v|
|
450
|
+
next if k[/[ML]S$/] || k == :valid || k == :dwSignature
|
451
|
+
printf fmt, k.to_s.sub(/^dw/,''), v > 9 ? "0x#{v.to_s(16)}" : v
|
452
|
+
end
|
453
|
+
|
454
|
+
vi.Children.each do |file_info|
|
455
|
+
case file_info
|
456
|
+
when PEdump::StringFileInfo
|
457
|
+
file_info.Children.each do |string_table|
|
458
|
+
puts "\n# StringTable #{string_table.szKey}:"
|
459
|
+
string_table.Children.each do |string|
|
460
|
+
printf fmt, string.szKey, string.Value.inspect
|
461
|
+
end
|
462
|
+
end
|
463
|
+
when PEdump::VarFileInfo
|
464
|
+
puts
|
465
|
+
printf fmt, "VarFileInfo", '[ 0x' + file_info.Children.Value.map{|v| v.to_s(16)}.join(", 0x") + ' ]'
|
466
|
+
else
|
467
|
+
puts "[?] unknown child type: #{file_info.inspect}, use -fi to inspect"
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
363
473
|
def dump_packers data
|
364
|
-
if @options[:verbose]
|
474
|
+
if @options[:verbose] > 0
|
365
475
|
data.each do |p|
|
366
476
|
printf "%8x %4d %s\n", p.offset, p.packer.size, p.packer.name
|
367
477
|
end
|
@@ -379,7 +489,7 @@ class PEdump::CLI
|
|
379
489
|
data.MajorVersion, data.MinorVersion,
|
380
490
|
data.Base
|
381
491
|
|
382
|
-
if @options[:verbose]
|
492
|
+
if @options[:verbose] > 0
|
383
493
|
[%w'Names', %w'EntryPoints Functions', %w'Ordinals NameOrdinals'].each do |x|
|
384
494
|
va = data["AddressOf"+x.last]
|
385
495
|
ofs = @pedump.va2file(va) || '?'
|