pedump 0.5.4 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +25 -23
- data/README.md +104 -11
- data/Rakefile +41 -6
- data/VERSION +1 -1
- data/data/comp_id.txt +776 -0
- data/lib/pedump/cli.rb +144 -34
- data/lib/pedump/loader/section.rb +5 -3
- data/lib/pedump/loader.rb +28 -6
- data/lib/pedump/ne.rb +1 -1
- data/lib/pedump/pe.rb +63 -54
- data/lib/pedump/rich.rb +562 -0
- data/lib/pedump/te.rb +62 -0
- data/lib/pedump.rb +86 -18
- data/misc/aspack/aspack_unlzx.c +5 -3
- data/pedump.gemspec +20 -29
- metadata +12 -23
data/lib/pedump/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pedump'
|
2
2
|
require 'pedump/packer'
|
3
|
+
require 'pedump/rich'
|
3
4
|
require 'pedump/version_info'
|
4
5
|
require 'optparse'
|
5
6
|
|
@@ -33,13 +34,14 @@ class PEdump::CLI
|
|
33
34
|
attr_accessor :data, :argv
|
34
35
|
|
35
36
|
KNOWN_ACTIONS = (
|
36
|
-
%w'mz dos_stub rich pe ne data_directory sections tls security' +
|
37
|
-
%w'strings resources resource_directory imports exports version_info packer web console packer_only'
|
37
|
+
%w'mz dos_stub rich pe ne te data_directory sections tls security' +
|
38
|
+
%w'strings resources resource_directory imports exports version_info packer web console packer_only' +
|
39
|
+
%w'extract' # 'disasm'
|
38
40
|
).map(&:to_sym)
|
39
41
|
|
40
|
-
DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console'.map(&:to_sym)
|
42
|
+
DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console extract disasm'.map(&:to_sym)
|
41
43
|
|
42
|
-
URL_BASE = "
|
44
|
+
URL_BASE = "https://pedump.me"
|
43
45
|
|
44
46
|
def initialize argv = ARGV
|
45
47
|
@argv = argv
|
@@ -96,8 +98,24 @@ class PEdump::CLI
|
|
96
98
|
opts.on "--all", "Dump all but resource-directory (default)" do
|
97
99
|
@actions = DEFAULT_ALL_ACTIONS
|
98
100
|
end
|
101
|
+
|
102
|
+
opts.separator ''
|
103
|
+
|
104
|
+
# opts.on "--disasm [X]", "Disassemble a symbol/VA" do |v|
|
105
|
+
# @actions << [:disasm, v]
|
106
|
+
# end
|
107
|
+
opts.on("--extract ID", "Extract a resource/section/data_dir",
|
108
|
+
"ID: datadir:EXPORT - datadir by type",
|
109
|
+
"ID: resource:0x98478 - resource by offset",
|
110
|
+
"ID: resource:ICON/#1 - resource by type & name",
|
111
|
+
"ID: section:.text - section by name",
|
112
|
+
"ID: section:rva/0x1000 - section by RVA",
|
113
|
+
"ID: section:raw/0x400 - section by RAW_PTR",
|
114
|
+
) do |v|
|
115
|
+
@actions << [:extract, v]
|
116
|
+
end
|
99
117
|
opts.on "--va2file VA", "Convert RVA to file offset" do |va|
|
100
|
-
@actions << [:va2file,va]
|
118
|
+
@actions << [:va2file, va]
|
101
119
|
end
|
102
120
|
|
103
121
|
opts.separator ''
|
@@ -133,9 +151,9 @@ class PEdump::CLI
|
|
133
151
|
@file_name = fname
|
134
152
|
|
135
153
|
File.open(fname,'rb') do |f|
|
136
|
-
@pedump = create_pedump
|
154
|
+
@pedump = create_pedump f
|
137
155
|
|
138
|
-
next if !@options[:force] && !@pedump.
|
156
|
+
next if !@options[:force] && !@pedump.supported_file?(f)
|
139
157
|
|
140
158
|
@actions.each do |action|
|
141
159
|
case action
|
@@ -152,8 +170,8 @@ class PEdump::CLI
|
|
152
170
|
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
153
171
|
end
|
154
172
|
|
155
|
-
def create_pedump
|
156
|
-
PEdump.new(
|
173
|
+
def create_pedump io
|
174
|
+
PEdump.new(io, :force => @options[:force]).tap do |x|
|
157
175
|
x.logger.level =
|
158
176
|
case @options[:verbose]
|
159
177
|
when -100..-3
|
@@ -194,16 +212,14 @@ class PEdump::CLI
|
|
194
212
|
end
|
195
213
|
|
196
214
|
class ProgressProxy
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
@
|
201
|
-
@pbar = ProgressBar.new("[.] uploading", file.size, STDOUT)
|
202
|
-
@pbar.try(:file_transfer_mode)
|
203
|
-
@pbar.bar_mark = '='
|
215
|
+
def initialize file, prefix = "[.] uploading: ", io = $stdout
|
216
|
+
@file = file
|
217
|
+
@io = io
|
218
|
+
@prefix = prefix
|
204
219
|
end
|
205
220
|
def read *args
|
206
|
-
@
|
221
|
+
@io.write("\r#{@prefix}#{@file.tell}/#{@file.size} ")
|
222
|
+
@io.flush
|
207
223
|
@file.read *args
|
208
224
|
end
|
209
225
|
def method_missing *args
|
@@ -212,6 +228,10 @@ class PEdump::CLI
|
|
212
228
|
def respond_to? *args
|
213
229
|
@file.respond_to?(args.first) || super(*args)
|
214
230
|
end
|
231
|
+
|
232
|
+
def finish!
|
233
|
+
@io.write("\r#{@prefix}#{@file.size}/#{@file.size} \n")
|
234
|
+
end
|
215
235
|
end
|
216
236
|
|
217
237
|
def upload f
|
@@ -224,17 +244,17 @@ class PEdump::CLI
|
|
224
244
|
require 'open-uri'
|
225
245
|
require 'net/http'
|
226
246
|
require 'net/http/post/multipart'
|
227
|
-
require 'progressbar'
|
228
247
|
|
229
|
-
stdout_sync =
|
230
|
-
|
248
|
+
stdout_sync = $stdout.sync
|
249
|
+
$stdout.sync = true
|
231
250
|
|
232
251
|
md5 = Digest::MD5.file(f.path).hexdigest
|
233
252
|
@pedump.logger.info "[.] md5: #{md5}"
|
234
253
|
file_url = "#{URL_BASE}/#{md5}/"
|
235
254
|
|
236
255
|
@pedump.logger.warn "[.] checking if file already uploaded.."
|
237
|
-
|
256
|
+
uri = URI.parse URL_BASE
|
257
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
|
238
258
|
http.open_timeout = 10
|
239
259
|
http.read_timeout = 10
|
240
260
|
# doing HTTP HEAD is a lot faster than open-uri
|
@@ -250,24 +270,26 @@ class PEdump::CLI
|
|
250
270
|
|
251
271
|
f.rewind
|
252
272
|
|
253
|
-
# upload with
|
273
|
+
# upload with progress
|
254
274
|
post_url = URI.parse(URL_BASE+'/')
|
275
|
+
# UploadIO is from multipart-post
|
255
276
|
uio = UploadIO.new(f, "application/octet-stream", File.basename(f.path))
|
256
277
|
ppx = ProgressProxy.new(uio)
|
257
278
|
req = Net::HTTP::Post::Multipart.new post_url.path, "file" => ppx
|
258
|
-
res = Net::HTTP.start(post_url.host, post_url.port)
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
puts "[.] analyzing..."
|
279
|
+
res = Net::HTTP.start(post_url.host, post_url.port, use_ssl: (post_url.scheme == 'https')) do |http|
|
280
|
+
http.request(req)
|
281
|
+
end
|
282
|
+
ppx.finish!
|
263
283
|
|
264
|
-
|
265
|
-
|
284
|
+
unless [200, 302, 303].include?(res.code.to_i)
|
285
|
+
@pedump.logger.fatal "[!] invalid server response: #{res.code} #{res.msg}"
|
286
|
+
@pedump.logger.fatal res.body unless res.body.empty?
|
287
|
+
exit(1)
|
266
288
|
end
|
267
289
|
|
268
290
|
puts "[.] uploaded: #{file_url}"
|
269
291
|
ensure
|
270
|
-
|
292
|
+
$stdout.sync = stdout_sync
|
271
293
|
end
|
272
294
|
|
273
295
|
def console f
|
@@ -311,6 +333,10 @@ class PEdump::CLI
|
|
311
333
|
def dump_action action, f
|
312
334
|
if action.is_a?(Array)
|
313
335
|
case action[0]
|
336
|
+
when :disasm
|
337
|
+
return
|
338
|
+
when :extract
|
339
|
+
return extract action[1]
|
314
340
|
when :va2file
|
315
341
|
@pedump.sections(f)
|
316
342
|
va = action[1] =~ /(^0x)|(h$)/i ? action[1].to_i(16) : action[1].to_i
|
@@ -457,6 +483,8 @@ class PEdump::CLI
|
|
457
483
|
case data.first
|
458
484
|
when PEdump::IMAGE_DATA_DIRECTORY
|
459
485
|
dump_data_dir data
|
486
|
+
when PEdump::EFI_IMAGE_DATA_DIRECTORY
|
487
|
+
dump_efi_data_dir data
|
460
488
|
when PEdump::IMAGE_SECTION_HEADER
|
461
489
|
dump_sections data
|
462
490
|
when PEdump::Resource
|
@@ -781,19 +809,26 @@ class PEdump::CLI
|
|
781
809
|
end
|
782
810
|
end
|
783
811
|
|
784
|
-
|
785
812
|
def dump_data_dir data
|
786
813
|
data.each do |row|
|
787
814
|
printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va.to_i, row.size.to_i
|
788
815
|
end
|
789
816
|
end
|
790
817
|
|
818
|
+
def dump_efi_data_dir data
|
819
|
+
data.each_with_index do |row, idx|
|
820
|
+
printf " %-12s rva:0x%8x size:0x %8x\n", PEdump::EFI_IMAGE_DATA_DIRECTORY::TYPES[idx], row.va.to_i, row.size.to_i
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
791
824
|
def dump_rich_hdr data
|
792
825
|
if decoded = data.decode
|
793
|
-
puts "
|
826
|
+
puts " ID VER COUNT DESCRIPTION"
|
794
827
|
decoded.each do |row|
|
795
|
-
printf " %5d %
|
796
|
-
row.id, row.id, row.version, row.version, row.times, row.times
|
828
|
+
# printf " %5d %4x %7d %4x %12d %8x\n",
|
829
|
+
# row.id, row.id, row.version, row.version, row.times, row.times
|
830
|
+
printf " %4x %4x %12d %s\n",
|
831
|
+
row.id, row.version, row.times, ::PEdump::RICH_IDS[(row.id<<16) + row.version]
|
797
832
|
end
|
798
833
|
else
|
799
834
|
puts "# raw:"
|
@@ -804,4 +839,79 @@ class PEdump::CLI
|
|
804
839
|
end
|
805
840
|
end
|
806
841
|
|
842
|
+
def disasm x
|
843
|
+
puts "TODO"
|
844
|
+
end
|
845
|
+
|
846
|
+
def extract x
|
847
|
+
a = x.split(':',2)
|
848
|
+
case a[0]
|
849
|
+
when 'datadir'
|
850
|
+
extract_datadir a[1]
|
851
|
+
when 'resource'
|
852
|
+
extract_resource a[1]
|
853
|
+
when 'section'
|
854
|
+
extract_section a[1]
|
855
|
+
else
|
856
|
+
raise "invalid #{x.inspect}"
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
def extract_datadir id
|
861
|
+
entry = @pedump.data_directory.find{ |x| x.type == id }
|
862
|
+
unless entry
|
863
|
+
@pedump.logger.fatal "[!] entry #{id.inspect} not found"
|
864
|
+
exit(1)
|
865
|
+
end
|
866
|
+
if entry.size != 0
|
867
|
+
IO::copy_stream @pedump.io, $stdout, entry.size, @pedump.va2file(entry.va)
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
def extract_resource id
|
872
|
+
res = nil
|
873
|
+
if id =~ /\A0x\h+\Z/
|
874
|
+
needle = id.to_i(16)
|
875
|
+
res = @pedump.resources.find{ |r| r.file_offset == needle }
|
876
|
+
elsif id =~ /\A\d+\Z/
|
877
|
+
needle = id.to_i(10)
|
878
|
+
res = @pedump.resources.find{ |r| r.file_offset == needle }
|
879
|
+
elsif id['/']
|
880
|
+
type, name = id.split('/', 2)
|
881
|
+
res = @pedump.resources.find{ |r| r.type == type && r.name == name }
|
882
|
+
else
|
883
|
+
@pedump.logger.fatal "[!] invalid resource id #{id.inspect}"
|
884
|
+
exit(1)
|
885
|
+
end
|
886
|
+
unless res
|
887
|
+
@pedump.logger.fatal "[!] resource #{id.inspect} not found"
|
888
|
+
exit(1)
|
889
|
+
end
|
890
|
+
IO::copy_stream @pedump.io, $stdout, res.size, res.file_offset
|
891
|
+
end
|
892
|
+
|
893
|
+
def extract_section id
|
894
|
+
section = nil
|
895
|
+
if id['/']
|
896
|
+
a = id.split('/',2)
|
897
|
+
if a[0] == 'rva'
|
898
|
+
value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
|
899
|
+
section = @pedump.sections.find{ |s| s.VirtualAddress == value }
|
900
|
+
elsif a[0] == 'raw'
|
901
|
+
value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
|
902
|
+
section = @pedump.sections.find{ |s| s.PointerToRawData == value }
|
903
|
+
else
|
904
|
+
@pedump.logger.fatal "[!] invalid section id #{id.inspect}"
|
905
|
+
exit(1)
|
906
|
+
end
|
907
|
+
else
|
908
|
+
section = @pedump.sections.find{ |s| s.Name == id }
|
909
|
+
end
|
910
|
+
unless section
|
911
|
+
@pedump.logger.fatal "[!] section #{id.inspect} not found"
|
912
|
+
exit(1)
|
913
|
+
end
|
914
|
+
IO::copy_stream @pedump.io, $stdout, section.SizeOfRawData, section.PointerToRawData
|
915
|
+
end
|
916
|
+
|
807
917
|
end # class PEdump::CLI
|
@@ -10,15 +10,16 @@ class PEdump::Loader
|
|
10
10
|
@hdr = x.dup
|
11
11
|
end
|
12
12
|
@data = EMPTY_DATA.dup
|
13
|
+
@delta = args[:delta] || 0
|
13
14
|
@deferred_load_io = args[:deferred_load_io]
|
14
|
-
@deferred_load_pos = args[:deferred_load_pos] || (@hdr && @hdr.PointerToRawData)
|
15
|
+
@deferred_load_pos = args[:deferred_load_pos] || (@hdr && (@hdr.PointerToRawData - @delta))
|
15
16
|
@deferred_load_size = args[:deferred_load_size] || (@hdr && @hdr.SizeOfRawData)
|
16
17
|
@image_base = args[:image_base] || 0
|
17
18
|
end
|
18
19
|
|
19
20
|
def name; @hdr.Name; end
|
20
|
-
def va ; @hdr.VirtualAddress + @image_base; end
|
21
|
-
def rva ; @hdr.VirtualAddress; end
|
21
|
+
def va ; @hdr.VirtualAddress + @image_base - @delta; end
|
22
|
+
def rva ; @hdr.VirtualAddress - @delta; end
|
22
23
|
def vsize; @hdr.VirtualSize; end
|
23
24
|
def flags; @hdr.Characteristics; end
|
24
25
|
def flags= f; @hdr.Characteristics= f; end
|
@@ -51,6 +52,7 @@ class PEdump::Loader
|
|
51
52
|
@data.size > 0 ? @data.size.to_s(16) : (@deferred_load_io ? "<defer>" : 0)
|
52
53
|
]
|
53
54
|
r << (" dlpos=%8x" % @deferred_load_pos) if @deferred_load_pos
|
55
|
+
r << (" delta=%3x" % @delta) if @delta != 0
|
54
56
|
r << ">"
|
55
57
|
end
|
56
58
|
end
|
data/lib/pedump/loader.rb
CHANGED
@@ -15,7 +15,15 @@ class PEdump::Loader
|
|
15
15
|
|
16
16
|
# shortcuts
|
17
17
|
alias :pe :pe_hdr
|
18
|
-
|
18
|
+
|
19
|
+
def ep
|
20
|
+
if @pe_hdr
|
21
|
+
@pe_hdr.try(:ioh).try(:AddressOfEntryPoint)
|
22
|
+
elsif @te_hdr
|
23
|
+
@te_hdr.AddressOfEntryPoint - @delta
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
19
27
|
def ep= v; @pe_hdr.ioh.AddressOfEntryPoint=v; end
|
20
28
|
|
21
29
|
########################################################################
|
@@ -23,12 +31,24 @@ class PEdump::Loader
|
|
23
31
|
########################################################################
|
24
32
|
|
25
33
|
def initialize io = nil, params = {}
|
34
|
+
# @delta is for EFI TE loading, based on https://github.com/gdbinit/TELoader/blob/master/teloader.cpp
|
35
|
+
@delta = 0
|
26
36
|
@pedump = PEdump.new(io, params)
|
27
37
|
if io
|
28
38
|
@mz_hdr = @pedump.mz
|
29
39
|
@dos_stub = @pedump.dos_stub
|
30
40
|
@pe_hdr = @pedump.pe
|
31
|
-
@
|
41
|
+
@te_hdr = @pedump.te
|
42
|
+
|
43
|
+
@image_base = params[:image_base]
|
44
|
+
if @pe_hdr
|
45
|
+
@image_base ||= @pe_hdr.try(:ioh).try(:ImageBase)
|
46
|
+
elsif @te_hdr
|
47
|
+
@image_base ||= @te_hdr.ImageBase
|
48
|
+
@delta = @pedump.te_shift
|
49
|
+
end
|
50
|
+
@image_base ||= 0
|
51
|
+
|
32
52
|
load_sections @pedump.sections, io
|
33
53
|
end
|
34
54
|
@find_limit = params[:find_limit] || DEFAULT_FIND_LIMIT
|
@@ -38,7 +58,7 @@ class PEdump::Loader
|
|
38
58
|
if section_hdrs.is_a?(Array)
|
39
59
|
@sections = section_hdrs.map do |x|
|
40
60
|
raise "unknown section hdr: #{x.inspect}" unless x.is_a?(PEdump::IMAGE_SECTION_HEADER)
|
41
|
-
Section.new(x, :deferred_load_io => f, :image_base => @image_base )
|
61
|
+
Section.new(x, :deferred_load_io => f, :image_base => @image_base, :delta => @delta )
|
42
62
|
end
|
43
63
|
if f.respond_to?(:seek) && f.respond_to?(:read)
|
44
64
|
#
|
@@ -249,10 +269,12 @@ class PEdump::Loader
|
|
249
269
|
def names
|
250
270
|
return @names if @names
|
251
271
|
@names = {}
|
252
|
-
|
253
|
-
|
254
|
-
|
272
|
+
|
273
|
+
oep = ep()
|
274
|
+
if oep
|
275
|
+
@names[oep + @image_base] = 'start'
|
255
276
|
end
|
277
|
+
|
256
278
|
_parse_imports
|
257
279
|
_parse_exports
|
258
280
|
#TODO: debug info
|
data/lib/pedump/ne.rb
CHANGED
@@ -405,7 +405,7 @@ class PEdump
|
|
405
405
|
begin
|
406
406
|
ne_offset = mz(f) && mz(f).lfanew
|
407
407
|
if ne_offset.nil?
|
408
|
-
logger.
|
408
|
+
logger.debug "[!] NULL NE offset (e_lfanew)."
|
409
409
|
nil
|
410
410
|
elsif ne_offset > f.size
|
411
411
|
logger.fatal "[!] NE offset beyond EOF."
|
data/lib/pedump/pe.rb
CHANGED
@@ -24,78 +24,87 @@ class PEdump
|
|
24
24
|
signature + ifh.pack + ioh.pack
|
25
25
|
end
|
26
26
|
|
27
|
-
def self.
|
27
|
+
def self.read_sections f, nToRead, args = {}
|
28
28
|
force = args[:force]
|
29
29
|
|
30
|
+
if nToRead > 0xffff
|
31
|
+
if force.is_a?(Numeric) && force > 1
|
32
|
+
PEdump.logger.warn "[!] too many sections (#{nToRead}). forced. reading all"
|
33
|
+
else
|
34
|
+
PEdump.logger.warn "[!] too many sections (#{nToRead}). not forced, reading first 65535"
|
35
|
+
nToRead = 65535
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
sections = []
|
40
|
+
nToRead.times do
|
41
|
+
break if f.eof?
|
42
|
+
sections << IMAGE_SECTION_HEADER.read(f)
|
43
|
+
end
|
44
|
+
|
45
|
+
if sections.any?
|
46
|
+
# zero all missing values of last section
|
47
|
+
sections.last.tap do |last_section|
|
48
|
+
last_section.each_pair do |k,v|
|
49
|
+
last_section[k] = 0 if v.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
sections
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.read f, args = {}
|
30
58
|
pe_offset = f.tell
|
31
59
|
pe_sig = f.read 4
|
32
60
|
#logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
|
33
61
|
if pe_sig != "PE\x00\x00"
|
34
|
-
if force
|
62
|
+
if args[:force]
|
35
63
|
logger.warn "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect})"
|
36
64
|
else
|
37
65
|
logger.debug "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect}). (not forced)"
|
38
66
|
return nil
|
39
67
|
end
|
40
68
|
end
|
41
|
-
PE.new(pe_sig)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
69
|
+
pe = PE.new(pe_sig)
|
70
|
+
pe.image_file_header = IMAGE_FILE_HEADER.read(f)
|
71
|
+
ioh_offset = f.tell # offset to IMAGE_OPTIONAL_HEADER
|
72
|
+
if pe.ifh.SizeOfOptionalHeader.to_i > 0
|
73
|
+
if pe.x64?
|
74
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER64.read(f, pe.ifh.SizeOfOptionalHeader)
|
75
|
+
else
|
76
|
+
pe.image_optional_header = IMAGE_OPTIONAL_HEADER32.read(f, pe.ifh.SizeOfOptionalHeader)
|
50
77
|
end
|
78
|
+
end
|
51
79
|
|
52
|
-
|
53
|
-
if force.is_a?(Numeric) && force > 1
|
54
|
-
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). forced. reading all"
|
55
|
-
else
|
56
|
-
logger.warn "[!] too many sections (#{pe.ifh.NumberOfSections}). not forced, reading first 65535"
|
57
|
-
nToRead = 65535
|
58
|
-
end
|
59
|
-
end
|
80
|
+
nToRead=pe.ifh.NumberOfSections.to_i
|
60
81
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
nToRead.times do
|
66
|
-
break if f.eof?
|
67
|
-
pe.sections << IMAGE_SECTION_HEADER.read(f)
|
68
|
-
end
|
82
|
+
# The Windows loader expects to find the PE section headers after the optional header. It calculates the address of the first section header by adding SizeOfOptionalHeader to the beginning of the optional header.
|
83
|
+
# // http://www.phreedom.org/research/tinype/
|
84
|
+
f.seek( ioh_offset + pe.ifh.SizeOfOptionalHeader.to_i )
|
85
|
+
pe.sections = read_sections(f, nToRead, args)
|
69
86
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
87
|
+
pe_end = f.tell
|
88
|
+
if s=pe.sections.find{ |s| (pe_offset...pe_end).include?(s.va) }
|
89
|
+
if args[:pass2]
|
90
|
+
# already called with CompositeIO ?
|
91
|
+
PEdump.logger.error "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! 2nd time?!"
|
78
92
|
|
79
|
-
pe_end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
io = CompositeIO.new(StringIO.new(data), f)
|
91
|
-
args1 = args.dup
|
92
|
-
args1[:pass2] = true
|
93
|
-
return PE.read(io, args1)
|
94
|
-
else
|
95
|
-
logger.error "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! too big to rebuild!"
|
96
|
-
end
|
93
|
+
elsif pe_end-pe_offset < 0x100_000
|
94
|
+
PEdump.logger.warn "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! trying to rebuild..."
|
95
|
+
f.seek pe_offset
|
96
|
+
data = f.read(s.va-pe_offset)
|
97
|
+
f.seek s.PointerToRawData
|
98
|
+
io = CompositeIO.new(StringIO.new(data), f)
|
99
|
+
args1 = args.dup
|
100
|
+
args1[:pass2] = true
|
101
|
+
return PE.read(io, args1)
|
102
|
+
else
|
103
|
+
PEdump.logger.error "[!] section with va=0x#{s.va.to_s(16)} overwrites PE header! too big to rebuild!"
|
97
104
|
end
|
98
105
|
end
|
106
|
+
|
107
|
+
pe
|
99
108
|
end
|
100
109
|
|
101
110
|
def self.logger; PEdump.logger; end
|
@@ -106,7 +115,7 @@ class PEdump
|
|
106
115
|
begin
|
107
116
|
pe_offset = mz(f) && mz(f).lfanew
|
108
117
|
if pe_offset.nil?
|
109
|
-
logger.
|
118
|
+
logger.debug "[!] NULL PE offset (e_lfanew). cannot continue."
|
110
119
|
nil
|
111
120
|
elsif pe_offset > f.size
|
112
121
|
logger.fatal "[!] PE offset beyond EOF. cannot continue."
|