pedump 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.travis.yml +4 -0
  2. data/Gemfile +10 -6
  3. data/Gemfile.lock +27 -19
  4. data/README.md +37 -25
  5. data/Rakefile +45 -6
  6. data/VERSION +1 -1
  7. data/data/fs.txt +37 -1408
  8. data/data/jc-userdb.txt +14371 -0
  9. data/data/sig.bin +0 -0
  10. data/lib/pedump.rb +355 -618
  11. data/lib/pedump/cli.rb +214 -113
  12. data/lib/pedump/comparer.rb +147 -0
  13. data/lib/pedump/composite_io.rb +56 -0
  14. data/lib/pedump/core.rb +38 -0
  15. data/lib/pedump/core_ext/try.rb +57 -0
  16. data/lib/pedump/loader.rb +393 -0
  17. data/lib/pedump/loader/minidump.rb +187 -0
  18. data/lib/pedump/loader/section.rb +57 -0
  19. data/lib/pedump/logger.rb +67 -0
  20. data/lib/pedump/ne.rb +425 -0
  21. data/lib/pedump/ne/version_info.rb +171 -0
  22. data/lib/pedump/packer.rb +50 -2
  23. data/lib/pedump/pe.rb +121 -0
  24. data/lib/pedump/resources.rb +436 -0
  25. data/lib/pedump/security.rb +58 -0
  26. data/lib/pedump/sig_parser.rb +145 -24
  27. data/lib/pedump/tls.rb +17 -0
  28. data/lib/pedump/unpacker.rb +26 -0
  29. data/lib/pedump/unpacker/aspack.rb +858 -0
  30. data/lib/pedump/unpacker/upx.rb +13 -0
  31. data/lib/pedump/version.rb +1 -1
  32. data/lib/pedump/version_info.rb +15 -10
  33. data/misc/aspack/Makefile +3 -0
  34. data/misc/aspack/aspack_unlzx.c +92 -0
  35. data/misc/aspack/lzxdec.c +479 -0
  36. data/misc/aspack/lzxdec.h +56 -0
  37. data/misc/nedump.c +751 -0
  38. data/pedump.gemspec +75 -25
  39. data/samples/bad/68.exe +0 -0
  40. data/samples/bad/data_dir_15_entries.exe +0 -0
  41. data/spec/65535sects_spec.rb +8 -0
  42. data/spec/bad_imports_spec.rb +20 -0
  43. data/spec/bad_samples_spec.rb +13 -0
  44. data/spec/composite_io_spec.rb +122 -0
  45. data/spec/data/calc.exe_sections.yml +49 -0
  46. data/spec/data/data_dir_15_entries.exe_sections.yml +95 -0
  47. data/spec/dllord_spec.rb +21 -0
  48. data/spec/foldedhdr_spec.rb +28 -0
  49. data/spec/imports_badterm_spec.rb +52 -0
  50. data/spec/imports_vterm_spec.rb +52 -0
  51. data/spec/loader/names_spec.rb +24 -0
  52. data/spec/loader/va_spec.rb +44 -0
  53. data/spec/manyimportsW7_spec.rb +22 -0
  54. data/spec/ne_spec.rb +125 -0
  55. data/spec/packer_spec.rb +17 -0
  56. data/spec/pe_spec.rb +67 -0
  57. data/spec/pedump_spec.rb +16 -4
  58. data/spec/sections_spec.rb +11 -0
  59. data/spec/sig_all_packers_spec.rb +15 -5
  60. data/spec/sig_spec.rb +6 -1
  61. data/spec/spec_helper.rb +15 -3
  62. data/spec/support/samples.rb +24 -0
  63. data/spec/unpackers/aspack_spec.rb +69 -0
  64. data/spec/unpackers/find_spec.rb +21 -0
  65. data/spec/virtsectblXP_spec.rb +12 -0
  66. data/tmp/.keep +0 -0
  67. metadata +146 -35
  68. data/README.md.tpl +0 -90
  69. data/samples/calc.7z +0 -0
  70. data/samples/zlib.dll +0 -0
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ describe 'corkami/dllord.dll' do
5
+ it "should have 1 import" do
6
+ sample.imports.size.should == 1
7
+ sample.imports.map(&:module_name).should == %w'msvcrt.dll'
8
+ sample.imports.map do |iid|
9
+ (iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
10
+ end.flatten.should == ["printf"]
11
+ end
12
+
13
+ it "exports at least 2 entries" do
14
+ sample.exports.Base.should == 0x313
15
+ sample.exports.name.should be_nil
16
+ sample.exports.names.should be_empty
17
+ sample.exports.name_ordinals.should be_empty
18
+ sample.exports.entry_points[0].should == 0xffff_ffff
19
+ sample.exports.entry_points[1].should == 0x1008
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ [ 'corkami/foldedhdr.exe', 'corkami/foldedhdrW7.exe' ].each do |fname|
5
+ describe fname do
6
+ before :all do
7
+ @sample = sample
8
+ end
9
+
10
+ it "should have 2 imports" do
11
+ @sample.imports.size.should == 2
12
+ @sample.imports.map(&:module_name).should == %w'kernel32.dll msvcrt.dll'
13
+ @sample.imports.map do |iid|
14
+ (iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
15
+ end.flatten.should == ["ExitProcess", "printf"]
16
+ end
17
+
18
+ it "should have 1 section" do
19
+ @sample.sections.size.should == 1
20
+ s = @sample.sections.first
21
+ s.VirtualSize.should == 0x1000
22
+ s.VirtualAddress.should == 0x1000
23
+ s.SizeOfRawData.should == 0x200
24
+ s.PointerToRawData.should == 0x200
25
+ s.flags.should == 0xa0000000
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ describe 'corkami/imports_badterm.exe' do
5
+ # PE with a 'bad' imports terminator, just the dll name is empty
6
+ # http://code.google.com/p/corkami/source/browse/trunk/asm/PE/imports_badterm.asm
7
+ before :all do
8
+ @imports = sample.imports
9
+ end
10
+
11
+ it "should have 2 IMAGE_IMPORT_DESCRIPTORs" do
12
+ @imports.size.should == 2
13
+ end
14
+
15
+ it "should have only IMAGE_IMPORT_DESCRIPTORs" do
16
+ @imports.map(&:class).uniq.should == [PEdump::IMAGE_IMPORT_DESCRIPTOR]
17
+ end
18
+
19
+ # it "should have all entries thunks equal" do
20
+ # @imports.each do |iid|
21
+ # iid.first_thunk.should == iid.original_first_thunk
22
+ # end
23
+ # end
24
+
25
+ describe "1st image_import_descriptor" do
26
+ it "should be from kernel32.dll" do
27
+ @imports[0].module_name.should == "kernel32.dll"
28
+ end
29
+ it "should have 1 function" do
30
+ @imports[0].first_thunk.size.should == 1
31
+ end
32
+ it "should have ExitProcess" do
33
+ @imports[0].first_thunk.first.name.should == "ExitProcess"
34
+ @imports[0].first_thunk.first.hint.should == 0
35
+ @imports[0].first_thunk.first.ordinal.should be_nil
36
+ end
37
+ end
38
+
39
+ describe "2nd image_import_descriptor" do
40
+ it "should be from msvcrt.dll" do
41
+ @imports[1].module_name.should == "msvcrt.dll"
42
+ end
43
+ it "should have 1 function" do
44
+ @imports[1].first_thunk.size.should == 1
45
+ end
46
+ it "should have printf" do
47
+ @imports[1].first_thunk.first.name.should == "printf"
48
+ @imports[1].first_thunk.first.hint.should == 0
49
+ @imports[1].first_thunk.first.ordinal.should be_nil
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ describe 'corkami/imports_vterm.exe' do
5
+ # http://code.google.com/p/corkami/source/browse/trunk/asm/PE/imports_vterm.asm
6
+ #describe "import terminator in virtual space" do
7
+ before :all do
8
+ @imports = sample.imports
9
+ end
10
+
11
+ it "should have 2 IMAGE_IMPORT_DESCRIPTORs" do
12
+ @imports.size.should == 2
13
+ end
14
+
15
+ it "should have only IMAGE_IMPORT_DESCRIPTORs" do
16
+ @imports.map(&:class).uniq.should == [PEdump::IMAGE_IMPORT_DESCRIPTOR]
17
+ end
18
+
19
+ # it "should have all entries thunks equal" do
20
+ # @imports.each do |iid|
21
+ # iid.first_thunk.should == iid.original_first_thunk
22
+ # end
23
+ # end
24
+
25
+ describe "1st image_import_descriptor" do
26
+ it "should be from kernel32.dll" do
27
+ @imports[0].module_name.should == "kernel32.dll"
28
+ end
29
+ it "should have 1 function" do
30
+ @imports[0].first_thunk.size.should == 1
31
+ end
32
+ it "should have ExitProcess" do
33
+ @imports[0].first_thunk.first.name.should == "ExitProcess"
34
+ @imports[0].first_thunk.first.hint.should == 0
35
+ @imports[0].first_thunk.first.ordinal.should be_nil
36
+ end
37
+ end
38
+
39
+ describe "2nd image_import_descriptor" do
40
+ it "should be from msvcrt.dll" do
41
+ @imports[1].module_name.should == "msvcrt.dll"
42
+ end
43
+ it "should have 1 function" do
44
+ @imports[1].first_thunk.size.should == 1
45
+ end
46
+ it "should have printf" do
47
+ @imports[1].first_thunk.first.name.should == "printf"
48
+ @imports[1].first_thunk.first.hint.should == 0
49
+ @imports[1].first_thunk.first.ordinal.should be_nil
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'pedump/loader'
3
+
4
+ describe PEdump::Loader do
5
+ it "should read names from imports" do
6
+ io = open("samples/calc.exe","rb")
7
+ @ldr = PEdump::Loader.new io
8
+
9
+ @ldr.names.should_not be_nil
10
+ @ldr.names.should_not be_empty
11
+ @ldr.names.size.should >= 343
12
+ @ldr.names[0x10010d0].should == 'GetStartupInfoA'
13
+ end
14
+
15
+ it "should read names from exports" do
16
+ io = open("samples/zlib.dll","rb")
17
+ @ldr = PEdump::Loader.new io
18
+
19
+ @ldr.names.should_not be_nil
20
+ @ldr.names.should_not be_empty
21
+ @ldr.names.size.should >= 69
22
+ @ldr.names[0x1000e340].should == 'zlib_version'
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'pedump/loader'
3
+
4
+ describe PEdump::Loader do
5
+ describe "#valid_va?" do
6
+ describe "samples/calc.exe" do
7
+ before do
8
+ io = open("samples/calc.exe","rb")
9
+ @ldr = PEdump::Loader.new io
10
+ end
11
+
12
+ %w'1001000 1010000 104b999 104c000 1051000 109c000 10a01f5'.each do |x|
13
+ it "returns true for 0x#{x}" do
14
+ @ldr.valid_va?(x.to_i(16)).should be_true
15
+ end
16
+ end
17
+
18
+ %w'0 1 1000 1000fff 104b99a 104bfff 1050fff 109bfff 10a01f6'.each do |x|
19
+ it "returns false for 0x#{x}" do
20
+ @ldr.valid_va?(x.to_i(16)).should be_false
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "samples/upx.exe" do
26
+ before do
27
+ io = open("samples/upx.exe","rb")
28
+ @ldr = PEdump::Loader.new io
29
+ end
30
+
31
+ %w'401000 541000 589000 589fff'.each do |x|
32
+ it "returns true for 0x#{x}" do
33
+ @ldr.valid_va?(x.to_i(16)).should be_true
34
+ end
35
+ end
36
+
37
+ %w'0 1 1000 400000 58a000'.each do |x|
38
+ it "returns false for 0x#{x}" do
39
+ @ldr.valid_va?(x.to_i(16)).should be_false
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ describe "corkami/manyimportsW7.exe" do
5
+ before :all do
6
+ @sample = sample
7
+ end
8
+
9
+ it "should have 2 imports" do
10
+ @sample.imports.size.should == 2
11
+ @sample.imports.map(&:module_name).should == %w'kernel32.dll msvcrt.dll'
12
+ @sample.imports.map do |iid|
13
+ (iid.original_first_thunk + iid.first_thunk).uniq.map(&:name)
14
+ end.flatten.should == ["ExitProcess", "printf"]
15
+ end
16
+
17
+ it "should have 1 TLS" do
18
+ @sample.tls.size.should == 1
19
+ @sample.tls.first.AddressOfIndex.should == 0x401148
20
+ @sample.tls.first.AddressOfCallBacks.should == 0x401100
21
+ end
22
+ end
@@ -0,0 +1,125 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ 5.times do |idx|
5
+ fname = "ne#{idx}." + (idx==4 ? "dll" : "exe")
6
+
7
+ modulenames = %w"_DELIS VISTA21P ISSET_SE HORSNCF MAPI"
8
+ exports = []
9
+
10
+ # ne0.exe
11
+ exports << [
12
+ PEdump::ExportedFunction.new("WNDPROC", 1, 0x10258)
13
+ ]
14
+
15
+ # ne1.exe
16
+ exports << []
17
+
18
+ # ne2.exe
19
+ exports << [
20
+ PEdump::ExportedFunction.new("LOGODLGPROC", 1, 0x13ACA),
21
+ PEdump::ExportedFunction.new("BARWNDPROC", 2, 0x15FF0),
22
+ PEdump::ExportedFunction.new("SETUPWNDPROC",3, 0x100B2),
23
+ PEdump::ExportedFunction.new("LOGOBWNDPROC",4, 0x147B4),
24
+ ]
25
+
26
+ # ne3.exe
27
+ exports << [
28
+ PEdump::ExportedFunction.new("___EXPORTEDSTUB", 1, 0x63cf4),
29
+ PEdump::ExportedFunction.new("_AFX_VERSION", 2, 0x4272c),
30
+ ]
31
+
32
+ # ne4.dll
33
+ exports << [
34
+ PEdump::ExportedFunction.new("WEP", 1, 0x10000),
35
+ PEdump::ExportedFunction.new("BMAPIGETREADMAIL", 33, 0x7020A),
36
+ PEdump::ExportedFunction.new("BMAPIRESOLVENAME", 38, 0x7077C),
37
+ PEdump::ExportedFunction.new("BMAPIGETADDRESS", 36, 0x70692),
38
+ PEdump::ExportedFunction.new("BMAPIFINDNEXT", 34, 0x70074),
39
+ PEdump::ExportedFunction.new("BMAPIDETAILS", 37, 0x706F1),
40
+ PEdump::ExportedFunction.new("MAPIFREEBUFFER", 18, 0xb0A71),
41
+ PEdump::ExportedFunction.new("MAPIFINDNEXT", 16, 0xa0000),
42
+ PEdump::ExportedFunction.new("MAPIDELETEMAIL", 17, 0x90000),
43
+ PEdump::ExportedFunction.new("MAPIREADMAIL", 15, 0x100000),
44
+ PEdump::ExportedFunction.new("BMAPIADDRESS", 35, 0x7051D),
45
+ PEdump::ExportedFunction.new("MAPIADDRESS", 19, 0x60139),
46
+ PEdump::ExportedFunction.new("MAPILOGON", 11, 0xc0000),
47
+ PEdump::ExportedFunction.new("MAPISENDMAIL", 13, 0x130000),
48
+ PEdump::ExportedFunction.new("MAPIRESOLVENAME", 21, 0x60A3F),
49
+ PEdump::ExportedFunction.new("MAPIDETAILS", 20, 0x60752),
50
+ PEdump::ExportedFunction.new("BMAPISAVEMAIL", 31, 0x70455),
51
+ PEdump::ExportedFunction.new("MAPISAVEMAIL", 14, 0x1302BE),
52
+ PEdump::ExportedFunction.new("BMAPIREADMAIL", 32, 0x70141),
53
+ PEdump::ExportedFunction.new("MAPISENDDOCUMENTS", 10, 0x120703),
54
+ PEdump::ExportedFunction.new("MAPILOGOFF", 12, 0xc00D2),
55
+ PEdump::ExportedFunction.new("BMAPISENDMAIL", 30, 0x70000),
56
+ ]
57
+
58
+ imports = [
59
+ ['KERNEL', 0x80],
60
+ ['VBRUN300', 0x64],
61
+ ['GDI', 0x15f],
62
+ ['FINSTDLL', nil, 'FILECOPY'],
63
+ ['DEMILAYR', 0x6f]
64
+ ]
65
+
66
+ versions = %w'2.20.900.0 - 3.0.111.0 1.0.0.1 3.2.0.4057'
67
+
68
+ describe fname do
69
+ it "should have NE header" do
70
+ sample do |f|
71
+ f.ne.should_not be_nil
72
+ end
73
+ end
74
+
75
+ it "should not have PE header" do
76
+ sample do |f|
77
+ f.pe.should be_nil
78
+ end
79
+ end
80
+
81
+ it "should have NE segments" do
82
+ sample do |f|
83
+ f.ne.segments.size.should == f.ne.ne_cseg
84
+ end
85
+ end
86
+
87
+ it "should have NE resources" do
88
+ sample do |f|
89
+ f.ne.resources.should_not be_nil
90
+ ver = f.ne.resources.find{ |res| res.type == 'VERSION' }
91
+ expected = versions[idx]
92
+ if expected == '-'
93
+ ver.should be_nil
94
+ else
95
+ vi = ver.data.first
96
+ [
97
+ vi.Value.dwFileVersionMS.to_i >> 16,
98
+ vi.Value.dwFileVersionMS.to_i & 0xffff,
99
+ vi.Value.dwFileVersionLS.to_i >> 16,
100
+ vi.Value.dwFileVersionLS.to_i & 0xffff
101
+ ].join('.').should == expected
102
+ end
103
+ end
104
+ end
105
+
106
+ it "should have imports" do
107
+ sample do |f|
108
+ f.ne.imports.should_not be_nil
109
+ func = PEdump::ImportedFunction.new
110
+ func.module_name = imports[idx][0]
111
+ func.ordinal = imports[idx][1]
112
+ func.name = imports[idx][2]
113
+
114
+ f.ne.imports.should include(func)
115
+ end
116
+ end
117
+ it "should have exports" do
118
+ sample do |f|
119
+ f.ne.exports.should_not be_nil
120
+ f.ne.exports.name.should == modulenames[idx]
121
+ f.ne.exports.functions.should == exports[idx]
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ %w'calc_upx.exe arm_upx.exe'.each do |fname|
5
+ describe fname do
6
+ before :all do
7
+ File.open(File.join("samples",fname),"rb") do |f|
8
+ @packer = PEdump.new(f).packer.first
9
+ end
10
+ end
11
+
12
+ it "should detect UPX" do
13
+ @packer.should_not be_nil
14
+ @packer.name.should include 'UPX'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/pedump')
3
+
4
+ describe 'PE' do
5
+ it "should assume TimeDateStamp is in UTC"
6
+
7
+ KLASS = PEdump::ImportedFunction
8
+
9
+ describe KLASS do
10
+ it "should be equal" do
11
+ pending "necessary?"
12
+ a = []
13
+ KLASS.new(*a).should == KLASS.new(*a)
14
+ a = ['a']
15
+ KLASS.new(*a).should == KLASS.new(*a)
16
+ a = ['a','b']
17
+ KLASS.new(*a).should == KLASS.new(*a)
18
+ a = ['a','b','c']
19
+ KLASS.new(*a).should == KLASS.new(*a)
20
+ a = ['a','b','c','d']
21
+ KLASS.new(*a).should == KLASS.new(*a)
22
+ end
23
+
24
+ it "should not be equal" do
25
+ a = ['a']
26
+ b = []
27
+ KLASS.new(*a).should_not == KLASS.new(*b)
28
+ a = ['a']
29
+ b = ['b']
30
+ KLASS.new(*a).should_not == KLASS.new(*b)
31
+ a = ['a','B']
32
+ b = ['a','b']
33
+ KLASS.new(*a).should_not == KLASS.new(*b)
34
+ a = ['a','b','c']
35
+ b = ['a','b']
36
+ KLASS.new(*a).should_not == KLASS.new(*b)
37
+ a = ['a','b','c']
38
+ b = ['a','b','X']
39
+ KLASS.new(*a).should_not == KLASS.new(*b)
40
+ end
41
+
42
+ it "should be equal with different VA's" do
43
+ pending "necessary?"
44
+ a = ['a','b','c',nil]
45
+ b = ['a','b','c','d']
46
+ KLASS.new(*a).should == KLASS.new(*b)
47
+ a = ['a','b','c',0x1000]
48
+ b = ['a','b','c',0x2000]
49
+ KLASS.new(*a).should == KLASS.new(*b)
50
+ a = ['a','b','c',0x1000]
51
+ b = ['a','b','c',0x1000]
52
+ KLASS.new(*a).should == KLASS.new(*b)
53
+ end
54
+
55
+ it "should be equal in uniq() with different VA's" do
56
+ a = ['a','b','c',nil]
57
+ b = ['a','b','c','d']
58
+ [KLASS.new(*a), KLASS.new(*b)].uniq.size.should == 1
59
+ a = ['a','b','c',0x1000]
60
+ b = ['a','b','c',0x2000]
61
+ [KLASS.new(*a), KLASS.new(*b)].uniq.size.should == 1
62
+ a = ['a','b','c',0x1000]
63
+ b = ['a','b','c',0x1000]
64
+ [KLASS.new(*a), KLASS.new(*b)].uniq.size.should == 1
65
+ end
66
+ end
67
+ end