pedump 0.4.5 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -1
- data/Gemfile.lock +5 -1
- data/README.md +22 -20
- data/Rakefile +25 -0
- data/VERSION +1 -1
- data/lib/pedump.rb +92 -45
- data/lib/pedump/cli.rb +56 -16
- data/lib/pedump/comparer.rb +147 -0
- data/lib/pedump/core.rb +12 -18
- data/lib/pedump/loader.rb +131 -0
- data/lib/pedump/loader/section.rb +51 -0
- data/lib/pedump/logger.rb +67 -0
- data/lib/pedump/pe.rb +3 -0
- data/lib/pedump/resources.rb +3 -3
- data/lib/pedump/unpacker.rb +26 -0
- data/lib/pedump/unpacker/aspack.rb +853 -0
- data/lib/pedump/unpacker/upx.rb +13 -0
- data/lib/pedump/version.rb +1 -1
- data/lib/pedump/version_info.rb +8 -3
- data/misc/aspack/Makefile +3 -0
- data/misc/aspack/aspack_unlzx.c +92 -0
- data/misc/aspack/lzxdec.c +479 -0
- data/misc/aspack/lzxdec.h +56 -0
- data/pedump.gemspec +24 -5
- data/spec/pe_spec.rb +61 -0
- data/spec/unpackers/aspack_spec.rb +69 -0
- data/spec/unpackers/find_spec.rb +17 -0
- metadata +53 -18
data/Gemfile
CHANGED
@@ -4,6 +4,7 @@ source "http://rubygems.org"
|
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
5
|
gem "multipart-post", "~> 1.1.4"
|
6
6
|
gem "progressbar", "~> 0.9.2"
|
7
|
+
gem "awesome_print"
|
7
8
|
|
8
9
|
# Add dependencies to develop your gem here.
|
9
10
|
# Include everything needed to run rake, tests, features, etc.
|
@@ -12,5 +13,6 @@ group :development do
|
|
12
13
|
gem "bundler", "~> 1.0.0"
|
13
14
|
gem "jeweler", "~> 1.6.4"
|
14
15
|
gem "rcov", ">= 0"
|
15
|
-
gem "
|
16
|
+
gem "what_methods"
|
17
|
+
gem "looksee"
|
16
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
awesome_print (0.
|
4
|
+
awesome_print (1.0.2)
|
5
5
|
diff-lcs (1.1.3)
|
6
6
|
git (1.2.5)
|
7
7
|
jeweler (1.6.4)
|
8
8
|
bundler (~> 1.0)
|
9
9
|
git (>= 1.2.5)
|
10
10
|
rake
|
11
|
+
looksee (1.0.3)
|
11
12
|
multipart-post (1.1.4)
|
12
13
|
progressbar (0.9.2)
|
13
14
|
rake (0.9.2.2)
|
@@ -20,6 +21,7 @@ GEM
|
|
20
21
|
rspec-expectations (2.3.0)
|
21
22
|
diff-lcs (~> 1.1.2)
|
22
23
|
rspec-mocks (2.3.0)
|
24
|
+
what_methods (1.0.1)
|
23
25
|
|
24
26
|
PLATFORMS
|
25
27
|
ruby
|
@@ -28,7 +30,9 @@ DEPENDENCIES
|
|
28
30
|
awesome_print
|
29
31
|
bundler (~> 1.0.0)
|
30
32
|
jeweler (~> 1.6.4)
|
33
|
+
looksee
|
31
34
|
multipart-post (~> 1.1.4)
|
32
35
|
progressbar (~> 0.9.2)
|
33
36
|
rcov
|
34
37
|
rspec (~> 2.3.0)
|
38
|
+
what_methods
|
data/README.md
CHANGED
@@ -43,6 +43,7 @@ Usage
|
|
43
43
|
--pe
|
44
44
|
--data-directory
|
45
45
|
-S, --sections
|
46
|
+
--tls
|
46
47
|
-s, --strings
|
47
48
|
-R, --resources
|
48
49
|
--resource-directory
|
@@ -53,6 +54,7 @@ Usage
|
|
53
54
|
--deep packer deep scan, significantly slower
|
54
55
|
-P, --packer-only packer/compiler detect only,
|
55
56
|
mimics 'file' command output
|
57
|
+
-r, --recursive recurse dirs in packer detect
|
56
58
|
--all Dump all but resource-directory (default)
|
57
59
|
--va2file VA Convert RVA to file offset
|
58
60
|
-W, --web Uploads files to a http://pedump.me
|
@@ -126,7 +128,7 @@ Usage
|
|
126
128
|
# IMAGE_FILE_HEADER:
|
127
129
|
Machine: 332 0x14c x86
|
128
130
|
NumberOfSections: 4 4
|
129
|
-
TimeDateStamp: "2008-09-14
|
131
|
+
TimeDateStamp: "2008-09-14 07:28:52"
|
130
132
|
PointerToSymbolTable: 0 0
|
131
133
|
NumberOfSymbols: 0 0
|
132
134
|
SizeOfOptionalHeader: 224 0xe0
|
@@ -315,7 +317,7 @@ Usage
|
|
315
317
|
=== EXPORTS ===
|
316
318
|
|
317
319
|
# module "zlib.dll"
|
318
|
-
# flags=0x0 ts="1996-05-07
|
320
|
+
# flags=0x0 ts="1996-05-07 08:46:46" version=0.0 ord_base=1
|
319
321
|
# nFuncs=27 nNames=27
|
320
322
|
|
321
323
|
ORD ENTRY_VA NAME
|
@@ -328,24 +330,24 @@ Usage
|
|
328
330
|
7 37f0 deflateInit2_
|
329
331
|
8 37c0 deflateInit_
|
330
332
|
9 3bc0 deflateParams
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
333
|
+
a 3b40 deflateReset
|
334
|
+
b 3a40 deflateSetDictionary
|
335
|
+
c 7510 gzclose
|
336
|
+
d 6f00 gzdopen
|
337
|
+
e 75a0 gzerror
|
338
|
+
f 73f0 gzflush
|
339
|
+
10 6c50 gzopen
|
340
|
+
11 7190 gzread
|
341
|
+
12 7350 gzwrite
|
342
|
+
13 4e50 inflate
|
343
|
+
14 4cc0 inflateEnd
|
344
|
+
15 4d20 inflateInit2_
|
345
|
+
16 4e30 inflateInit_
|
346
|
+
17 4c70 inflateReset
|
347
|
+
18 5260 inflateSetDictionary
|
348
|
+
19 52f0 inflateSync
|
349
|
+
1a 4bd0 uncompress
|
350
|
+
1b e340 zlib_version
|
349
351
|
|
350
352
|
### VS_VERSIONINFO parsing
|
351
353
|
|
data/Rakefile
CHANGED
@@ -190,3 +190,28 @@ task :readme do
|
|
190
190
|
Dir.chdir '..'
|
191
191
|
File.open('README.md','w'){ |f| f << result }
|
192
192
|
end
|
193
|
+
|
194
|
+
namespace :console do
|
195
|
+
desc "start console with PEdump::Loader with loaded file"
|
196
|
+
task :load do
|
197
|
+
raise "gimme a fname" unless fname = ENV['fname']
|
198
|
+
require './lib/pedump'
|
199
|
+
require './lib/pedump/loader'
|
200
|
+
require 'pp'
|
201
|
+
File.open(fname,"rb") do |f|
|
202
|
+
@ldr = PEdump::Loader.new f
|
203
|
+
puts "[.] loader is at @ldr"
|
204
|
+
pp @ldr.sections
|
205
|
+
Rake::Task["console"].execute
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
desc "compare two PE files"
|
211
|
+
task :cmp do
|
212
|
+
raise "gimme a f1" unless f1 = ENV['f1']
|
213
|
+
raise "gimme a f2" unless f2 = ENV['f2']
|
214
|
+
require './lib/pedump'
|
215
|
+
require './lib/pedump/comparer'
|
216
|
+
PEdump::Comparer.cmp(f1,f2)
|
217
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.6
|
data/lib/pedump.rb
CHANGED
@@ -12,16 +12,20 @@ require 'pedump/tls'
|
|
12
12
|
# http://github.com/zed-0xff
|
13
13
|
|
14
14
|
class PEdump
|
15
|
-
attr_accessor :fname, :logger, :force
|
15
|
+
attr_accessor :fname, :logger, :force, :io
|
16
16
|
|
17
17
|
VERSION = Version::STRING
|
18
18
|
|
19
19
|
@@logger = nil
|
20
20
|
|
21
|
-
def initialize
|
22
|
-
|
21
|
+
def initialize io = nil, params = {}
|
22
|
+
if io.is_a?(Hash)
|
23
|
+
@io, params = nil, io
|
24
|
+
else
|
25
|
+
@io = io
|
26
|
+
end
|
23
27
|
@force = params[:force]
|
24
|
-
@logger = @@logger =
|
28
|
+
@logger = @@logger = Logger.create(params)
|
25
29
|
end
|
26
30
|
|
27
31
|
# http://www.delorie.com/djgpp/doc/exe/
|
@@ -83,10 +87,10 @@ class PEdump
|
|
83
87
|
0x8000 => 'BYTES_REVERSED_HI' # The bytes of the word are reversed. This flag is obsolete.
|
84
88
|
}
|
85
89
|
|
86
|
-
def initialize *args
|
87
|
-
super
|
88
|
-
self.TimeDateStamp = Time.at(self.TimeDateStamp).utc
|
89
|
-
end
|
90
|
+
# def initialize *args
|
91
|
+
# super
|
92
|
+
# self.TimeDateStamp = Time.at(self.TimeDateStamp).utc
|
93
|
+
# end
|
90
94
|
def flags
|
91
95
|
FLAGS.find_all{ |k,v| (self.Characteristics & k) != 0 }.map(&:last)
|
92
96
|
end
|
@@ -226,6 +230,10 @@ class PEdump
|
|
226
230
|
r << ' SHARED' if f & 0x10000000 > 0
|
227
231
|
r
|
228
232
|
end
|
233
|
+
|
234
|
+
def pack
|
235
|
+
to_a.pack FORMAT.tr('A','a') # pad names with NULL bytes on pack()
|
236
|
+
end
|
229
237
|
end
|
230
238
|
|
231
239
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=VS.85).aspx
|
@@ -286,7 +294,15 @@ class PEdump
|
|
286
294
|
new(fname, params).dump
|
287
295
|
end
|
288
296
|
|
289
|
-
def
|
297
|
+
def self.quiet
|
298
|
+
oldlevel = @@logger.level
|
299
|
+
@@logger.level = ::Logger::FATAL
|
300
|
+
yield
|
301
|
+
ensure
|
302
|
+
@@logger.level = oldlevel
|
303
|
+
end
|
304
|
+
|
305
|
+
def mz f=@io
|
290
306
|
@mz ||= f && MZ.read(f).tap do |mz|
|
291
307
|
if mz.signature != 'MZ' && mz.signature != 'ZM'
|
292
308
|
if @force
|
@@ -299,7 +315,7 @@ class PEdump
|
|
299
315
|
end
|
300
316
|
end
|
301
317
|
|
302
|
-
def dos_stub f
|
318
|
+
def dos_stub f=@io
|
303
319
|
@dos_stub ||=
|
304
320
|
begin
|
305
321
|
return nil unless mz = mz(f)
|
@@ -339,13 +355,13 @@ class PEdump
|
|
339
355
|
end
|
340
356
|
end
|
341
357
|
|
342
|
-
def rich_hdr f
|
358
|
+
def rich_hdr f=@io
|
343
359
|
dos_stub(f) && @rich_hdr
|
344
360
|
end
|
345
361
|
alias :rich_header :rich_hdr
|
346
362
|
alias :rich :rich_hdr
|
347
363
|
|
348
|
-
def pe f
|
364
|
+
def pe f=@io
|
349
365
|
@pe ||=
|
350
366
|
begin
|
351
367
|
pe_offset = mz(f) && mz(f).lfanew
|
@@ -396,8 +412,14 @@ class PEdump
|
|
396
412
|
end
|
397
413
|
|
398
414
|
# OPTIONAL: assigns @mz, @rich_hdr, @pe, etc
|
399
|
-
def dump f
|
400
|
-
|
415
|
+
def dump f=@io
|
416
|
+
if f.is_a?(String)
|
417
|
+
File.open(f,'rb'){ |f| _dump_handle(f) }
|
418
|
+
elsif f.is_a?(::IO)
|
419
|
+
_dump_handle f
|
420
|
+
elsif @io
|
421
|
+
_dump_handle @io
|
422
|
+
end
|
401
423
|
self
|
402
424
|
end
|
403
425
|
|
@@ -410,11 +432,11 @@ class PEdump
|
|
410
432
|
packer h
|
411
433
|
end
|
412
434
|
|
413
|
-
def data_directory f
|
435
|
+
def data_directory f=@io
|
414
436
|
pe(f) && pe.ioh && pe.ioh.DataDirectory
|
415
437
|
end
|
416
438
|
|
417
|
-
def sections f
|
439
|
+
def sections f=@io
|
418
440
|
pe(f) && pe.section_table
|
419
441
|
end
|
420
442
|
alias :section_table :sections
|
@@ -436,9 +458,25 @@ class PEdump
|
|
436
458
|
:original_first_thunk,
|
437
459
|
:first_thunk
|
438
460
|
|
439
|
-
ImportedFunction
|
461
|
+
class ImportedFunction < Struct.new(:hint, :name, :ordinal, :va)
|
462
|
+
# def == x
|
463
|
+
# self.hint == x.hint && self.name == x.name && self.ordinal == x.ordinal
|
464
|
+
# end
|
465
|
+
# def <=> x
|
466
|
+
# self.to_a[0..-2] <=> x.to_a[0..-2]
|
467
|
+
# end
|
468
|
+
|
469
|
+
# magic to be able to easy merge :first_thunk & :original_first_thunk arrays
|
470
|
+
# (keeping va different)
|
471
|
+
def hash
|
472
|
+
self.to_a[0..-2].hash
|
473
|
+
end
|
474
|
+
def eql? x
|
475
|
+
self.hint == x.hint && self.name == x.name && self.ordinal == x.ordinal
|
476
|
+
end
|
477
|
+
end
|
440
478
|
|
441
|
-
def imports f
|
479
|
+
def imports f=@io
|
442
480
|
return @imports if @imports
|
443
481
|
return nil unless pe(f) && pe(f).ioh && f
|
444
482
|
dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::IMPORT]
|
@@ -472,14 +510,14 @@ class PEdump
|
|
472
510
|
|
473
511
|
logger.warn "[?] non-empty last IMAGE_IMPORT_DESCRIPTOR: #{t.inspect}" unless t.empty?
|
474
512
|
@imports = r.each do |x|
|
475
|
-
if x.Name.to_i != 0 && (
|
476
|
-
f.seek
|
477
|
-
x.module_name = f.gets("\x00").chomp("\x00")
|
513
|
+
if x.Name.to_i != 0 && (ofs = va2file(x.Name))
|
514
|
+
f.seek ofs
|
515
|
+
x.module_name = f.gets("\x00").to_s.chomp("\x00")
|
478
516
|
end
|
479
517
|
[:original_first_thunk, :first_thunk].each do |tbl|
|
480
518
|
camel = tbl.capitalize.to_s.gsub(/_./){ |char| char[1..-1].upcase}
|
481
|
-
if x[camel].to_i != 0 && (
|
482
|
-
f.seek
|
519
|
+
if x[camel].to_i != 0 && (ofs = va2file(x[camel]))
|
520
|
+
f.seek ofs
|
483
521
|
x[tbl] ||= []
|
484
522
|
if pe.x64?
|
485
523
|
x[tbl] << t while (t = f.read(8).unpack('Q').first) != 0
|
@@ -492,16 +530,22 @@ class PEdump
|
|
492
530
|
idx = -1
|
493
531
|
x[tbl] && x[tbl].map! do |t|
|
494
532
|
idx += 1
|
533
|
+
va = x[camel].to_i + idx*4
|
495
534
|
cache[t] ||=
|
496
|
-
if t & (2**(bits-1)) > 0
|
497
|
-
ImportedFunction.new(nil,nil,t & (2**(bits-1)-1)) # 0x7fff_ffff(_ffff_ffff)
|
498
|
-
elsif
|
499
|
-
f.seek
|
535
|
+
if t & (2**(bits-1)) > 0 # 0x8000_0000(_0000_0000)
|
536
|
+
ImportedFunction.new(nil,nil,t & (2**(bits-1)-1),va) # 0x7fff_ffff(_ffff_ffff)
|
537
|
+
elsif ofs=va2file(t, :quiet => true)
|
538
|
+
f.seek ofs
|
500
539
|
if f.eof?
|
501
|
-
logger.warn "[?] import
|
540
|
+
logger.warn "[?] import ofs 0x#{ofs.to_s(16)} beyond EOF"
|
502
541
|
nil
|
503
542
|
else
|
504
|
-
ImportedFunction.new(
|
543
|
+
ImportedFunction.new(
|
544
|
+
f.read(2).unpack('v').first,
|
545
|
+
f.gets("\x00").chomp("\x00"),
|
546
|
+
nil,
|
547
|
+
va
|
548
|
+
)
|
505
549
|
end
|
506
550
|
elsif tbl == :original_first_thunk
|
507
551
|
# OriginalFirstThunk entries can not be invalid, show a warning msg
|
@@ -521,8 +565,11 @@ class PEdump
|
|
521
565
|
logger.warn "[?] import table: empty FirstThunk for #{x.module_name}"
|
522
566
|
elsif !x.original_first_thunk && x.first_thunk
|
523
567
|
logger.info "[?] import table: empty OriginalFirstThunk for #{x.module_name}"
|
524
|
-
elsif
|
525
|
-
|
568
|
+
elsif logger.debug?
|
569
|
+
# compare all but VAs
|
570
|
+
if x.original_first_thunk != x.first_thunk
|
571
|
+
logger.debug "[?] import table: OriginalFirstThunk != FirstThunk for #{x.module_name}"
|
572
|
+
end
|
526
573
|
end
|
527
574
|
end
|
528
575
|
end
|
@@ -547,7 +594,7 @@ class PEdump
|
|
547
594
|
# manual:
|
548
595
|
:name, :entry_points, :names, :name_ordinals
|
549
596
|
|
550
|
-
def exports f
|
597
|
+
def exports f=@io
|
551
598
|
return @exports if @exports
|
552
599
|
return nil unless pe(f) && pe(f).ioh && f
|
553
600
|
dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::EXPORT]
|
@@ -564,18 +611,18 @@ class PEdump
|
|
564
611
|
x.entry_points = []
|
565
612
|
x.name_ordinals = []
|
566
613
|
x.names = []
|
567
|
-
if x.Name.to_i != 0 && (
|
568
|
-
f.seek
|
614
|
+
if x.Name.to_i != 0 && (ofs = va2file(x.Name))
|
615
|
+
f.seek ofs
|
569
616
|
if f.eof?
|
570
|
-
logger.warn "[?] export
|
617
|
+
logger.warn "[?] export ofs 0x#{ofs.to_s(16)} beyond EOF"
|
571
618
|
nil
|
572
619
|
else
|
573
620
|
x.name = f.gets("\x00").chomp("\x00")
|
574
621
|
end
|
575
622
|
end
|
576
623
|
if x.NumberOfFunctions.to_i != 0
|
577
|
-
if x.AddressOfFunctions.to_i !=0 && (
|
578
|
-
f.seek
|
624
|
+
if x.AddressOfFunctions.to_i !=0 && (ofs = va2file(x.AddressOfFunctions))
|
625
|
+
f.seek ofs
|
579
626
|
x.entry_points = []
|
580
627
|
x.NumberOfFunctions.times do
|
581
628
|
if f.eof?
|
@@ -585,8 +632,8 @@ class PEdump
|
|
585
632
|
x.entry_points << f.read(4).unpack('V').first
|
586
633
|
end
|
587
634
|
end
|
588
|
-
if x.AddressOfNameOrdinals.to_i !=0 && (
|
589
|
-
f.seek
|
635
|
+
if x.AddressOfNameOrdinals.to_i !=0 && (ofs = va2file(x.AddressOfNameOrdinals))
|
636
|
+
f.seek ofs
|
590
637
|
x.name_ordinals = []
|
591
638
|
x.NumberOfNames.times do
|
592
639
|
if f.eof?
|
@@ -597,8 +644,8 @@ class PEdump
|
|
597
644
|
end
|
598
645
|
end
|
599
646
|
end
|
600
|
-
if x.NumberOfNames.to_i != 0 && x.AddressOfNames.to_i !=0 && (
|
601
|
-
f.seek
|
647
|
+
if x.NumberOfNames.to_i != 0 && x.AddressOfNames.to_i !=0 && (ofs = va2file(x.AddressOfNames))
|
648
|
+
f.seek ofs
|
602
649
|
x.names = []
|
603
650
|
x.NumberOfNames.times do
|
604
651
|
if f.eof?
|
@@ -619,7 +666,7 @@ class PEdump
|
|
619
666
|
# TLS
|
620
667
|
##############################################################################
|
621
668
|
|
622
|
-
def tls f
|
669
|
+
def tls f=@io
|
623
670
|
@tls ||= pe(f) && pe(f).ioh && f &&
|
624
671
|
begin
|
625
672
|
dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::TLS]
|
@@ -646,11 +693,11 @@ class PEdump
|
|
646
693
|
# resources
|
647
694
|
##############################################################################
|
648
695
|
|
649
|
-
def resources f
|
696
|
+
def resources f=@io
|
650
697
|
@resources ||= _scan_resources(f)
|
651
698
|
end
|
652
699
|
|
653
|
-
def version_info f
|
700
|
+
def version_info f=@io
|
654
701
|
resources(f) && resources(f).find_all{ |res| res.type == 'VERSION' }.map(&:data).flatten
|
655
702
|
end
|
656
703
|
|
@@ -658,7 +705,7 @@ class PEdump
|
|
658
705
|
# packer / compiler detection
|
659
706
|
##############################################################################
|
660
707
|
|
661
|
-
def packer f
|
708
|
+
def packer f=@io
|
662
709
|
@packer ||= pe(f) && @pe.ioh &&
|
663
710
|
begin
|
664
711
|
if !(va=@pe.ioh.AddressOfEntryPoint)
|