pedump 0.4.5 → 0.4.6
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 +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)
|