pedump 0.3.3 → 0.4.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/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) || '?'
|