pedump 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/FUNDING.yml +2 -0
- data/.github/dependabot.yml +8 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +11 -16
- data/Gemfile.lock +73 -27
- data/README.md +15 -6
- data/Rakefile +5 -44
- data/VERSION +1 -1
- data/lib/pedump.rb +101 -29
- data/lib/pedump/cli.rb +29 -18
- data/lib/pedump/loader.rb +1 -1
- data/lib/pedump/loader/minidump.rb +195 -31
- data/lib/pedump/ne.rb +1 -1
- data/lib/pedump/pe.rb +63 -54
- data/lib/pedump/te.rb +51 -0
- data/lib/pedump/unpacker/aspack.rb +1 -1
- data/lib/pedump/version.rb +2 -5
- data/misc/aspack/aspack_unlzx.c +5 -3
- data/pedump.gemspec +47 -74
- metadata +50 -101
- data/.document +0 -5
- data/.rspec +0 -1
- data/.travis.yml +0 -4
- data/samples/bad/68.exe +0 -0
- data/samples/bad/data_dir_15_entries.exe +0 -0
- data/spec/65535sects_spec.rb +0 -8
- data/spec/bad_imports_spec.rb +0 -20
- data/spec/bad_samples_spec.rb +0 -13
- data/spec/composite_io_spec.rb +0 -122
- data/spec/data/calc.exe_sections.yml +0 -49
- data/spec/data/data_dir_15_entries.exe_sections.yml +0 -95
- data/spec/dllord_spec.rb +0 -21
- data/spec/foldedhdr_spec.rb +0 -28
- data/spec/imports_badterm_spec.rb +0 -52
- data/spec/imports_vterm_spec.rb +0 -52
- data/spec/loader/names_spec.rb +0 -24
- data/spec/loader/va_spec.rb +0 -44
- data/spec/manyimportsW7_spec.rb +0 -22
- data/spec/ne_spec.rb +0 -125
- data/spec/packer_spec.rb +0 -17
- data/spec/pe_spec.rb +0 -67
- data/spec/pedump_spec.rb +0 -19
- data/spec/resource_spec.rb +0 -13
- data/spec/sections_spec.rb +0 -11
- data/spec/sig_all_packers_spec.rb +0 -24
- data/spec/sig_spec.rb +0 -68
- data/spec/spec_helper.rb +0 -24
- data/spec/support/samples.rb +0 -24
- data/spec/unpackers/aspack_spec.rb +0 -69
- data/spec/unpackers/find_spec.rb +0 -21
- data/spec/virtsectblXP_spec.rb +0 -12
- data/tmp/.keep +0 -0
data/lib/pedump/cli.rb
CHANGED
@@ -33,7 +33,7 @@ class PEdump::CLI
|
|
33
33
|
attr_accessor :data, :argv
|
34
34
|
|
35
35
|
KNOWN_ACTIONS = (
|
36
|
-
%w'mz dos_stub rich pe ne data_directory sections tls security' +
|
36
|
+
%w'mz dos_stub rich pe ne te data_directory sections tls security' +
|
37
37
|
%w'strings resources resource_directory imports exports version_info packer web console packer_only'
|
38
38
|
).map(&:to_sym)
|
39
39
|
|
@@ -65,8 +65,8 @@ class PEdump::CLI
|
|
65
65
|
@options[:force] ||= 0
|
66
66
|
@options[:force] += 1
|
67
67
|
end
|
68
|
-
opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :table, :yaml],
|
69
|
-
"Output format: bin,c,dump,hex,inspect,table,yaml","(default: table)" do |v|
|
68
|
+
opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :json, :table, :yaml],
|
69
|
+
"Output format: bin,c,dump,hex,inspect,json,table,yaml","(default: table)" do |v|
|
70
70
|
@options[:format] = v
|
71
71
|
end
|
72
72
|
KNOWN_ACTIONS.each do |t|
|
@@ -135,7 +135,7 @@ class PEdump::CLI
|
|
135
135
|
File.open(fname,'rb') do |f|
|
136
136
|
@pedump = create_pedump fname
|
137
137
|
|
138
|
-
next if !@options[:force] && !@pedump.
|
138
|
+
next if !@options[:force] && !@pedump.supported_file?(f)
|
139
139
|
|
140
140
|
@actions.each do |action|
|
141
141
|
case action
|
@@ -194,16 +194,14 @@ class PEdump::CLI
|
|
194
194
|
end
|
195
195
|
|
196
196
|
class ProgressProxy
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
@
|
201
|
-
@pbar = ProgressBar.new("[.] uploading", file.size, STDOUT)
|
202
|
-
@pbar.try(:file_transfer_mode)
|
203
|
-
@pbar.bar_mark = '='
|
197
|
+
def initialize file, prefix = "[.] uploading: ", io = STDOUT
|
198
|
+
@file = file
|
199
|
+
@io = io
|
200
|
+
@prefix = prefix
|
204
201
|
end
|
205
202
|
def read *args
|
206
|
-
@
|
203
|
+
@io.write("\r#{@prefix}#{@file.tell}/#{@file.size} ")
|
204
|
+
@io.flush
|
207
205
|
@file.read *args
|
208
206
|
end
|
209
207
|
def method_missing *args
|
@@ -212,6 +210,10 @@ class PEdump::CLI
|
|
212
210
|
def respond_to? *args
|
213
211
|
@file.respond_to?(args.first) || super(*args)
|
214
212
|
end
|
213
|
+
|
214
|
+
def finish!
|
215
|
+
@io.write("\r#{@prefix}#{@file.size}/#{@file.size} \n")
|
216
|
+
end
|
215
217
|
end
|
216
218
|
|
217
219
|
def upload f
|
@@ -224,7 +226,6 @@ class PEdump::CLI
|
|
224
226
|
require 'open-uri'
|
225
227
|
require 'net/http'
|
226
228
|
require 'net/http/post/multipart'
|
227
|
-
require 'progressbar'
|
228
229
|
|
229
230
|
stdout_sync = STDOUT.sync
|
230
231
|
STDOUT.sync = true
|
@@ -250,15 +251,15 @@ class PEdump::CLI
|
|
250
251
|
|
251
252
|
f.rewind
|
252
253
|
|
253
|
-
# upload with
|
254
|
+
# upload with progress
|
254
255
|
post_url = URI.parse(URL_BASE+'/')
|
256
|
+
# UploadIO is from multipart-post
|
255
257
|
uio = UploadIO.new(f, "application/octet-stream", File.basename(f.path))
|
256
258
|
ppx = ProgressProxy.new(uio)
|
257
259
|
req = Net::HTTP::Post::Multipart.new post_url.path, "file" => ppx
|
258
260
|
res = Net::HTTP.start(post_url.host, post_url.port){ |http| http.request(req) }
|
259
|
-
ppx.
|
261
|
+
ppx.finish!
|
260
262
|
|
261
|
-
puts
|
262
263
|
puts "[.] analyzing..."
|
263
264
|
|
264
265
|
if (r=open(File.join(URL_BASE,md5,'analyze')).read) != "OK"
|
@@ -326,7 +327,7 @@ class PEdump::CLI
|
|
326
327
|
|
327
328
|
puts action_title(action) unless @options[:format] == :binary
|
328
329
|
|
329
|
-
return dump(data) if [:inspect, :table, :yaml].include?(@options[:format])
|
330
|
+
return dump(data) if [:inspect, :table, :json, :yaml].include?(@options[:format])
|
330
331
|
|
331
332
|
dump_opts = {:name => action}
|
332
333
|
case action
|
@@ -376,6 +377,9 @@ class PEdump::CLI
|
|
376
377
|
when :yaml
|
377
378
|
require 'yaml'
|
378
379
|
puts data.to_yaml
|
380
|
+
when :json
|
381
|
+
require 'json'
|
382
|
+
puts data.to_json
|
379
383
|
end
|
380
384
|
end
|
381
385
|
|
@@ -454,6 +458,8 @@ class PEdump::CLI
|
|
454
458
|
case data.first
|
455
459
|
when PEdump::IMAGE_DATA_DIRECTORY
|
456
460
|
dump_data_dir data
|
461
|
+
when PEdump::EFI_IMAGE_DATA_DIRECTORY
|
462
|
+
dump_efi_data_dir data
|
457
463
|
when PEdump::IMAGE_SECTION_HEADER
|
458
464
|
dump_sections data
|
459
465
|
when PEdump::Resource
|
@@ -778,13 +784,18 @@ class PEdump::CLI
|
|
778
784
|
end
|
779
785
|
end
|
780
786
|
|
781
|
-
|
782
787
|
def dump_data_dir data
|
783
788
|
data.each do |row|
|
784
789
|
printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va.to_i, row.size.to_i
|
785
790
|
end
|
786
791
|
end
|
787
792
|
|
793
|
+
def dump_efi_data_dir data
|
794
|
+
data.each_with_index do |row, idx|
|
795
|
+
printf " %-12s rva:0x%8x size:0x %8x\n", PEdump::EFI_IMAGE_DATA_DIRECTORY::TYPES[idx], row.va.to_i, row.size.to_i
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
788
799
|
def dump_rich_hdr data
|
789
800
|
if decoded = data.decode
|
790
801
|
puts " LIB_ID VERSION TIMES_USED "
|
data/lib/pedump/loader.rb
CHANGED
@@ -263,7 +263,7 @@ class PEdump::Loader
|
|
263
263
|
@pedump.imports.each do |iid| # Image Import Descriptor
|
264
264
|
va = iid.FirstThunk + @image_base
|
265
265
|
(Array(iid.original_first_thunk) + Array(iid.first_thunk)).uniq.each do |func|
|
266
|
-
name = func.name || "
|
266
|
+
name = "__imp_" + (func.name || "#{func.ordinal}")
|
267
267
|
@names[va] = name
|
268
268
|
va += 4
|
269
269
|
end
|
@@ -53,6 +53,24 @@ class PEdump
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
MINIDUMP_MEMORY_DESCRIPTOR = IOStruct.new 'QLL',
|
57
|
+
:StartOfMemoryRange,
|
58
|
+
:DataSize,
|
59
|
+
:Rva
|
60
|
+
|
61
|
+
class MINIDUMP_MEMORY_LIST < IOStruct.new 'L',
|
62
|
+
:NumberOfMemoryRanges,
|
63
|
+
:MemoryRanges
|
64
|
+
|
65
|
+
def self.read io
|
66
|
+
r = super
|
67
|
+
r.MemoryRanges = r.NumberOfMemoryRanges.times.map{ MINIDUMP_MEMORY_DESCRIPTOR.read(io) }
|
68
|
+
r
|
69
|
+
end
|
70
|
+
|
71
|
+
def entries; self.MemoryRanges; end
|
72
|
+
end
|
73
|
+
|
56
74
|
MINIDUMP_MEMORY_DESCRIPTOR64 = IOStruct.new 'QQ',
|
57
75
|
:StartOfMemoryRange,
|
58
76
|
:DataSize
|
@@ -78,7 +96,7 @@ class PEdump
|
|
78
96
|
2 => :ReservedStream1,
|
79
97
|
3 => :ThreadListStream,
|
80
98
|
4 => :ModuleListStream,
|
81
|
-
5 => :MemoryListStream,
|
99
|
+
5 => :MemoryListStream, # MINIDUMP_MEMORY_LIST
|
82
100
|
6 => :ExceptionStream,
|
83
101
|
7 => :SystemInfoStream,
|
84
102
|
8 => :ThreadExListStream,
|
@@ -92,7 +110,34 @@ class PEdump
|
|
92
110
|
16 => :MemoryInfoListStream, # MINIDUMP_MEMORY_INFO_LIST
|
93
111
|
17 => :ThreadInfoListStream,
|
94
112
|
18 => :HandleOperationListStream,
|
95
|
-
0xffff => :LastReservedStream
|
113
|
+
0xffff => :LastReservedStream,
|
114
|
+
|
115
|
+
# Special types saved by google breakpad
|
116
|
+
# https://chromium.googlesource.com/breakpad/breakpad/+/846b6335c5b0ba46dfa2ed96fccfa3f7a02fa2f1/src/google_breakpad/common/minidump_format.h#311
|
117
|
+
0x47670001 => :BreakpadInfoStream,
|
118
|
+
0x47670002 => :BreakpadAssertionInfoStream,
|
119
|
+
0x47670003 => :BreakpadLinuxCpuInfo,
|
120
|
+
0x47670004 => :BreakpadLinuxProcStatus,
|
121
|
+
0x47670005 => :BreakpadLinuxLsbRelease,
|
122
|
+
0x47670006 => :BreakpadLinuxCmdLine,
|
123
|
+
0x47670007 => :BreakpadLinuxEnviron,
|
124
|
+
0x47670008 => :BreakpadLinuxAuxv,
|
125
|
+
0x47670009 => :BreakpadLinuxMaps,
|
126
|
+
0x4767000A => :BreakpadLinuxDsoDebug,
|
127
|
+
|
128
|
+
# Saved by crashpad
|
129
|
+
# https://chromium.googlesource.com/crashpad/crashpad/+/doc/minidump/minidump_extensions.h#95
|
130
|
+
0x43500001 => :CrashpadInfo,
|
131
|
+
|
132
|
+
# Saved by Syzyasan
|
133
|
+
# https://github.com/google/syzygy/blob/c8bb4927f07fec0de8834c4774ddaafef0bc099f/syzygy/kasko/api/client.h#L28
|
134
|
+
# https://github.com/google/syzygy/blob/master/syzygy/crashdata/crashdata.proto
|
135
|
+
0x4B6B0001 => :SyzyasanCrashdata,
|
136
|
+
|
137
|
+
# Saved by Chromium
|
138
|
+
0x4B6B0002 => :ChromiumStabilityReport,
|
139
|
+
0x4B6B0003 => :ChromiumSystemProfile,
|
140
|
+
0x4B6B0004 => :ChromiumGwpAsanData,
|
96
141
|
}
|
97
142
|
|
98
143
|
class Loader
|
@@ -116,17 +161,32 @@ class PEdump
|
|
116
161
|
end
|
117
162
|
end
|
118
163
|
|
164
|
+
def stream_by_name(name)
|
165
|
+
type = MINIDUMP_STREAM_TYPE.invert[name]
|
166
|
+
raise "Unknown type symbol #{name}!" if !type
|
167
|
+
|
168
|
+
streams.find { |s| s.StreamType == type }
|
169
|
+
end
|
170
|
+
|
119
171
|
def memory_info_list
|
120
172
|
# MINIDUMP_MEMORY_INFO_LIST
|
121
|
-
stream =
|
173
|
+
stream = stream_by_name(:MemoryInfoListStream)
|
122
174
|
return nil unless stream
|
123
175
|
io.seek stream.Location.Rva
|
124
176
|
MINIDUMP_MEMORY_INFO_LIST.read io
|
125
177
|
end
|
126
178
|
|
127
179
|
def memory_list
|
180
|
+
# MINIDUMP_MEMORY_LIST
|
181
|
+
stream = stream_by_name(:MemoryListStream)
|
182
|
+
return nil unless stream
|
183
|
+
io.seek stream.Location.Rva
|
184
|
+
MINIDUMP_MEMORY_LIST.read io
|
185
|
+
end
|
186
|
+
|
187
|
+
def memory64_list
|
128
188
|
# MINIDUMP_MEMORY64_LIST
|
129
|
-
stream =
|
189
|
+
stream = stream_by_name(:Memory64ListStream)
|
130
190
|
return nil unless stream
|
131
191
|
io.seek stream.Location.Rva
|
132
192
|
MINIDUMP_MEMORY64_LIST.read io
|
@@ -136,27 +196,50 @@ class PEdump
|
|
136
196
|
|
137
197
|
# set options[:merge] = true to merge adjacent memory ranges
|
138
198
|
def memory_ranges options = {}
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
199
|
+
if memory64_list
|
200
|
+
ml = memory64_list
|
201
|
+
file_offset = ml.BaseRva
|
202
|
+
r = []
|
203
|
+
if options[:merge]
|
204
|
+
ml.entries.each do |x|
|
205
|
+
if r.last && r.last.va + r.last.size == x.StartOfMemoryRange
|
206
|
+
# if section VA == prev_section.VA + prev_section.SIZE
|
207
|
+
# then just increase the size of previous section
|
208
|
+
r.last.size += x.DataSize
|
209
|
+
else
|
210
|
+
r << MemoryRange.new( file_offset, x.StartOfMemoryRange, x.DataSize )
|
211
|
+
end
|
212
|
+
file_offset += x.DataSize
|
213
|
+
end
|
214
|
+
else
|
215
|
+
ml.entries.each do |x|
|
149
216
|
r << MemoryRange.new( file_offset, x.StartOfMemoryRange, x.DataSize )
|
217
|
+
file_offset += x.DataSize
|
150
218
|
end
|
151
|
-
file_offset += x.DataSize
|
152
219
|
end
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
220
|
+
return r
|
221
|
+
elsif memory_list
|
222
|
+
ml = memory_list
|
223
|
+
r = []
|
224
|
+
if options[:merge]
|
225
|
+
ml.entries.each do |x|
|
226
|
+
if r.last && r.last.va + r.last.size == x.StartOfMemoryRange
|
227
|
+
# if section VA == prev_section.VA + prev_section.SIZE
|
228
|
+
# then just increase the size of previous section
|
229
|
+
r.last.size += x.DataSize
|
230
|
+
else
|
231
|
+
r << MemoryRange.new( x.Rva, x.StartOfMemoryRange, x.DataSize )
|
232
|
+
end
|
233
|
+
end
|
234
|
+
else
|
235
|
+
ml.entries.each do |x|
|
236
|
+
r << MemoryRange.new( x.Rva, x.StartOfMemoryRange, x.DataSize )
|
237
|
+
end
|
157
238
|
end
|
239
|
+
return r
|
240
|
+
else
|
241
|
+
raise "Could not find memory ranges"
|
158
242
|
end
|
159
|
-
r
|
160
243
|
end
|
161
244
|
|
162
245
|
end # class Minidump
|
@@ -167,21 +250,102 @@ end # module PEdump
|
|
167
250
|
|
168
251
|
if $0 == __FILE__
|
169
252
|
require 'pp'
|
253
|
+
require 'optparse'
|
170
254
|
|
171
|
-
|
172
|
-
|
255
|
+
options = {}
|
256
|
+
opt_parse = OptionParser.new do |opts|
|
257
|
+
opts.banner = "Usage: #{$0} [options] <minidump>"
|
173
258
|
|
259
|
+
opts.on("--all", "Print all of the following sections") do
|
260
|
+
options[:all] = true
|
261
|
+
end
|
262
|
+
opts.on("--header", "Print minidump header") do
|
263
|
+
options[:header] = true
|
264
|
+
end
|
265
|
+
opts.on("--streams", "Print out the streams present") do
|
266
|
+
options[:streams] = true
|
267
|
+
end
|
268
|
+
opts.on("--memory-ranges", "Print out memory ranges included in the minidump") do
|
269
|
+
options[:memory_ranges] = true
|
270
|
+
end
|
271
|
+
opts.on("--breakpad", "Print out breakpad text sections if present") do
|
272
|
+
options[:breakpad] = true
|
273
|
+
end
|
274
|
+
opts.separator ''
|
275
|
+
|
276
|
+
opts.on("--memory <address>", "Print the memory range beginning at address") do |m|
|
277
|
+
options[:memory] = m.hex
|
278
|
+
end
|
279
|
+
opts.separator ''
|
280
|
+
|
281
|
+
opts.on("-h", "--help", "Help") do
|
282
|
+
puts opts
|
283
|
+
exit 0
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
opt_parse.parse!
|
288
|
+
|
289
|
+
if ARGV.empty?
|
290
|
+
$stderr.puts opt_parse.help
|
291
|
+
exit 1
|
292
|
+
end
|
293
|
+
|
294
|
+
io = open(ARGV.first, "rb")
|
174
295
|
md = PEdump::Loader::Minidump.new io
|
175
|
-
pp md.hdr
|
176
|
-
puts
|
177
|
-
puts "[.] #{md.memory_ranges.size} memory ranges"
|
178
|
-
puts "[.] #{md.memory_ranges(:merge => true).size} merged memory ranges"
|
179
|
-
puts
|
180
296
|
|
181
|
-
|
182
|
-
|
297
|
+
if options[:all] || options[:header]
|
298
|
+
pp md.hdr
|
299
|
+
puts
|
300
|
+
end
|
301
|
+
|
302
|
+
if options[:all] || options[:streams]
|
303
|
+
puts "[.] Streams present in the minidump:"
|
304
|
+
md.streams.each do |s|
|
305
|
+
if PEdump::MINIDUMP_STREAM_TYPE[s.StreamType]
|
306
|
+
puts "[.] #{PEdump::MINIDUMP_STREAM_TYPE[s.StreamType]}"
|
307
|
+
else
|
308
|
+
puts "[.] Unknown stream type #{s.StreamType}"
|
309
|
+
end
|
310
|
+
end
|
311
|
+
puts
|
312
|
+
end
|
313
|
+
|
314
|
+
if options[:all] || options[:breakpad]
|
315
|
+
[ :BreakpadLinuxCpuInfo, :BreakpadLinuxProcStatus, :BreakpadLinuxMaps,
|
316
|
+
:BreakpadLinuxCmdLine, :BreakpadLinuxEnviron ].each { |name|
|
317
|
+
stream = md.stream_by_name(name)
|
318
|
+
next if !stream
|
319
|
+
|
320
|
+
io.seek stream.Location.Rva
|
321
|
+
contents = io.read(stream.Location.DataSize)
|
322
|
+
|
323
|
+
if contents !~ /[^[:print:][:space:]]/
|
324
|
+
puts "[.] Section #{name}:"
|
325
|
+
puts contents
|
326
|
+
else
|
327
|
+
puts "[.] Section #{name}: #{contents.inspect}"
|
328
|
+
end
|
329
|
+
puts
|
330
|
+
}
|
331
|
+
end
|
332
|
+
|
333
|
+
if options[:all] || options[:memory_ranges]
|
334
|
+
puts "[.] #{md.memory_ranges.size} memory ranges"
|
335
|
+
puts "[.] #{md.memory_ranges(:merge => true).size} merged memory ranges"
|
336
|
+
puts
|
337
|
+
|
338
|
+
printf "[.] %16s %8s\n", "addr", "size"
|
339
|
+
md.memory_ranges(:merge => true).sort_by { |mr| mr.va }.each do |mr|
|
340
|
+
printf "[.] %16x %8x\n", mr.va, mr.size
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
if options[:memory]
|
345
|
+
mr = md.memory_ranges(:merge => true).find { |r| r.va == options[:memory] }
|
346
|
+
raise "Could not find the specified region" if !mr
|
183
347
|
|
184
|
-
|
185
|
-
|
348
|
+
io.seek(mr.file_offset)
|
349
|
+
print io.read(mr.size)
|
186
350
|
end
|
187
351
|
end
|
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."
|