pedump 0.4.0 → 0.5.0

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.
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