pedump 0.5.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +2 -0
- data/.github/dependabot.yml +8 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +10 -9
- data/Gemfile.lock +72 -25
- data/README.md +15 -6
- data/Rakefile +69 -4
- data/VERSION +1 -1
- data/data/comp_id.txt +776 -0
- data/lib/pedump.rb +120 -29
- data/lib/pedump/cli.rb +35 -21
- data/lib/pedump/loader.rb +28 -6
- data/lib/pedump/loader/minidump.rb +130 -15
- data/lib/pedump/loader/section.rb +5 -3
- data/lib/pedump/ne.rb +1 -1
- data/lib/pedump/pe.rb +63 -54
- data/lib/pedump/rich.rb +562 -0
- data/lib/pedump/te.rb +62 -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 +108 -22
- data/rich.py +353 -0
- metadata +107 -15
data/lib/pedump/te.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
class PEdump
|
2
|
+
# https://www.intel.com/content/www/us/en/architecture-and-technology/unified-extensible-firmware-interface/efi-specifications-general-technology.html
|
3
|
+
# http://wiki.phoenix.com/wiki/index.php/EFI_TE_IMAGE_HEADER
|
4
|
+
# https://formats.kaitai.io/uefi_te/index.html
|
5
|
+
# http://ho.ax/tag/efi/
|
6
|
+
# https://github.com/gdbinit/TELoader
|
7
|
+
|
8
|
+
EFI_IMAGE_DATA_DIRECTORY = IOStruct.new( "VV", :va, :size )
|
9
|
+
EFI_IMAGE_DATA_DIRECTORY::TYPES = %w'BASERELOC DEBUG'
|
10
|
+
EFI_IMAGE_DATA_DIRECTORY::TYPES.each_with_index do |type,idx|
|
11
|
+
EFI_IMAGE_DATA_DIRECTORY.const_set(type,idx)
|
12
|
+
end
|
13
|
+
|
14
|
+
class EFI_TE_IMAGE_HEADER < IOStruct.new 'vvCCvVVQ',
|
15
|
+
:Signature,
|
16
|
+
:Machine,
|
17
|
+
:NumberOfSections,
|
18
|
+
:Subsystem,
|
19
|
+
:StrippedSize,
|
20
|
+
:AddressOfEntryPoint,
|
21
|
+
:BaseOfCode,
|
22
|
+
:ImageBase,
|
23
|
+
:DataDirectory # readed manually: EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]
|
24
|
+
|
25
|
+
REAL_SIZE = SIZE + EFI_IMAGE_DATA_DIRECTORY::SIZE * 2
|
26
|
+
|
27
|
+
attr_accessor :sections
|
28
|
+
|
29
|
+
def self.read io, args = {}
|
30
|
+
super(io).tap do |te|
|
31
|
+
te.DataDirectory = 2.times.map do
|
32
|
+
EFI_IMAGE_DATA_DIRECTORY.read(io)
|
33
|
+
end
|
34
|
+
te.sections = PE.read_sections(io, te.NumberOfSections, args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
TE = EFI_TE_IMAGE_HEADER
|
39
|
+
|
40
|
+
def te_shift
|
41
|
+
if @te
|
42
|
+
@te.StrippedSize - EFI_TE_IMAGE_HEADER::REAL_SIZE
|
43
|
+
else
|
44
|
+
0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def te f=@io
|
49
|
+
return @te if defined?(@te)
|
50
|
+
@te ||=
|
51
|
+
begin
|
52
|
+
te_offset = 0
|
53
|
+
f.seek te_offset
|
54
|
+
if f.read(2) == 'VZ'
|
55
|
+
f.seek te_offset
|
56
|
+
EFI_TE_IMAGE_HEADER.read f, :force => @force
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/pedump/version.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
class PEdump
|
2
2
|
module Version
|
3
|
-
|
4
|
-
MINOR =
|
5
|
-
PATCH = 1
|
3
|
+
STRING = File.read(File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'VERSION')).strip
|
4
|
+
MAJOR, MINOR, PATCH = STRING.split('.').map(&:to_i)
|
6
5
|
BUILD = nil
|
7
|
-
|
8
|
-
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
|
9
6
|
end
|
10
7
|
end
|
data/misc/aspack/aspack_unlzx.c
CHANGED
@@ -30,6 +30,7 @@ int unpack(BYTE*packed_data, size_t packed_size, size_t unpacked_size){
|
|
30
30
|
LZX_CONTEXT LZX;
|
31
31
|
BYTE* unpacked_data = NULL;
|
32
32
|
size_t decoded_size;
|
33
|
+
int r;
|
33
34
|
|
34
35
|
bzero(&LZX, sizeof(LZX));
|
35
36
|
|
@@ -38,8 +39,9 @@ int unpack(BYTE*packed_data, size_t packed_size, size_t unpacked_size){
|
|
38
39
|
return(ERR_NO_MEM);
|
39
40
|
}
|
40
41
|
|
41
|
-
|
42
|
-
|
42
|
+
r = DecodeLZX(&LZX, packed_data, unpacked_data, packed_size, unpacked_size);
|
43
|
+
decoded_size = (size_t)r;
|
44
|
+
if ( r < 0 || decoded_size < unpacked_size ) {
|
43
45
|
free(unpacked_data);
|
44
46
|
fprintf(stderr,"ERR_UNPACK\n");
|
45
47
|
return(ERR_UNPACK);
|
@@ -58,7 +60,7 @@ int main(int argc, char*argv[]){
|
|
58
60
|
if(argc != 3){
|
59
61
|
fprintf(stderr, "ASPack unLZX\n");
|
60
62
|
fprintf(stderr, "usage: %s <packed_size> <unpacked_size>\n", argv[0]);
|
61
|
-
fprintf(stderr, "(data is read from stdin and written to stdout)\n"
|
63
|
+
fprintf(stderr, "(data is read from stdin and written to stdout)\n");
|
62
64
|
return 1;
|
63
65
|
}
|
64
66
|
|
data/pedump.gemspec
CHANGED
@@ -1,27 +1,113 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: pedump 0.6.1 ruby lib
|
5
6
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
spec.authors = ["Andrey \"Zed\" Zaikin"]
|
10
|
-
spec.email = ["zed.0xff@gmail.com"]
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "pedump".freeze
|
9
|
+
s.version = "0.6.1"
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib".freeze]
|
13
|
+
s.authors = ["Andrey \"Zed\" Zaikin".freeze]
|
14
|
+
s.date = "2020-07-28"
|
15
|
+
s.description = "dump headers, sections, extract resources of win32 PE exe,dll,etc".freeze
|
16
|
+
s.email = "zed.0xff@gmail.com".freeze
|
17
|
+
s.executables = ["pedump".freeze]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE.txt",
|
20
|
+
"README.md"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".github/FUNDING.yml",
|
24
|
+
".github/dependabot.yml",
|
25
|
+
"CODE_OF_CONDUCT.md",
|
26
|
+
"Gemfile",
|
27
|
+
"Gemfile.lock",
|
28
|
+
"LICENSE.txt",
|
29
|
+
"README.md",
|
30
|
+
"Rakefile",
|
31
|
+
"VERSION",
|
32
|
+
"bin/pedump",
|
33
|
+
"data/comp_id.txt",
|
34
|
+
"data/fs.txt",
|
35
|
+
"data/jc-userdb.txt",
|
36
|
+
"data/sig.bin",
|
37
|
+
"data/signatures.txt",
|
38
|
+
"data/userdb.txt",
|
39
|
+
"lib/pedump.rb",
|
40
|
+
"lib/pedump/cli.rb",
|
41
|
+
"lib/pedump/comparer.rb",
|
42
|
+
"lib/pedump/composite_io.rb",
|
43
|
+
"lib/pedump/core.rb",
|
44
|
+
"lib/pedump/core_ext/try.rb",
|
45
|
+
"lib/pedump/loader.rb",
|
46
|
+
"lib/pedump/loader/minidump.rb",
|
47
|
+
"lib/pedump/loader/section.rb",
|
48
|
+
"lib/pedump/logger.rb",
|
49
|
+
"lib/pedump/ne.rb",
|
50
|
+
"lib/pedump/ne/version_info.rb",
|
51
|
+
"lib/pedump/packer.rb",
|
52
|
+
"lib/pedump/pe.rb",
|
53
|
+
"lib/pedump/resources.rb",
|
54
|
+
"lib/pedump/rich.rb",
|
55
|
+
"lib/pedump/security.rb",
|
56
|
+
"lib/pedump/sig_parser.rb",
|
57
|
+
"lib/pedump/te.rb",
|
58
|
+
"lib/pedump/tls.rb",
|
59
|
+
"lib/pedump/unpacker.rb",
|
60
|
+
"lib/pedump/unpacker/aspack.rb",
|
61
|
+
"lib/pedump/unpacker/upx.rb",
|
62
|
+
"lib/pedump/version.rb",
|
63
|
+
"lib/pedump/version_info.rb",
|
64
|
+
"misc/aspack/Makefile",
|
65
|
+
"misc/aspack/aspack_unlzx.c",
|
66
|
+
"misc/aspack/lzxdec.c",
|
67
|
+
"misc/aspack/lzxdec.h",
|
68
|
+
"misc/nedump.c",
|
69
|
+
"pedump.gemspec",
|
70
|
+
"rich.py"
|
71
|
+
]
|
72
|
+
s.homepage = "http://github.com/zed-0xff/pedump".freeze
|
73
|
+
s.licenses = ["MIT".freeze]
|
74
|
+
s.rubygems_version = "2.7.10".freeze
|
75
|
+
s.summary = "dump win32 PE executable files with a pure ruby".freeze
|
16
76
|
|
17
|
-
|
18
|
-
|
77
|
+
if s.respond_to? :specification_version then
|
78
|
+
s.specification_version = 4
|
19
79
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
80
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
81
|
+
s.add_runtime_dependency(%q<rainbow>.freeze, [">= 0"])
|
82
|
+
s.add_runtime_dependency(%q<awesome_print>.freeze, [">= 0"])
|
83
|
+
s.add_runtime_dependency(%q<iostruct>.freeze, [">= 0.0.4"])
|
84
|
+
s.add_runtime_dependency(%q<multipart-post>.freeze, [">= 2.0.0"])
|
85
|
+
s.add_runtime_dependency(%q<zhexdump>.freeze, [">= 0.0.2"])
|
86
|
+
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.9.0"])
|
87
|
+
s.add_development_dependency(%q<rspec-its>.freeze, ["~> 1.3.0"])
|
88
|
+
s.add_development_dependency(%q<bundler>.freeze, ["~> 2.1.4"])
|
89
|
+
s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.3.9"])
|
90
|
+
else
|
91
|
+
s.add_dependency(%q<rainbow>.freeze, [">= 0"])
|
92
|
+
s.add_dependency(%q<awesome_print>.freeze, [">= 0"])
|
93
|
+
s.add_dependency(%q<iostruct>.freeze, [">= 0.0.4"])
|
94
|
+
s.add_dependency(%q<multipart-post>.freeze, [">= 2.0.0"])
|
95
|
+
s.add_dependency(%q<zhexdump>.freeze, [">= 0.0.2"])
|
96
|
+
s.add_dependency(%q<rspec>.freeze, ["~> 3.9.0"])
|
97
|
+
s.add_dependency(%q<rspec-its>.freeze, ["~> 1.3.0"])
|
98
|
+
s.add_dependency(%q<bundler>.freeze, ["~> 2.1.4"])
|
99
|
+
s.add_dependency(%q<jeweler>.freeze, ["~> 2.3.9"])
|
100
|
+
end
|
101
|
+
else
|
102
|
+
s.add_dependency(%q<rainbow>.freeze, [">= 0"])
|
103
|
+
s.add_dependency(%q<awesome_print>.freeze, [">= 0"])
|
104
|
+
s.add_dependency(%q<iostruct>.freeze, [">= 0.0.4"])
|
105
|
+
s.add_dependency(%q<multipart-post>.freeze, [">= 2.0.0"])
|
106
|
+
s.add_dependency(%q<zhexdump>.freeze, [">= 0.0.2"])
|
107
|
+
s.add_dependency(%q<rspec>.freeze, ["~> 3.9.0"])
|
108
|
+
s.add_dependency(%q<rspec-its>.freeze, ["~> 1.3.0"])
|
109
|
+
s.add_dependency(%q<bundler>.freeze, ["~> 2.1.4"])
|
110
|
+
s.add_dependency(%q<jeweler>.freeze, ["~> 2.3.9"])
|
111
|
+
end
|
27
112
|
end
|
113
|
+
|
data/rich.py
ADDED
@@ -0,0 +1,353 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# based on code from http://trendystephen.blogspot.be/2008/01/rich-header.html
|
3
|
+
import sys
|
4
|
+
import struct
|
5
|
+
|
6
|
+
# I'm trying not to bury the magic number...
|
7
|
+
CHECKSUM_MASK = 0x536e6144 # DanS (actuall SnaD)
|
8
|
+
RICH_TEXT = 'Rich'
|
9
|
+
RICH_TEXT_LENGTH = len(RICH_TEXT)
|
10
|
+
PE_START = 0x3c
|
11
|
+
PE_FIELD_LENGTH = 4
|
12
|
+
|
13
|
+
# most of values up to AliasObj900 are from old MSVC leak with private PDBs;
|
14
|
+
# rest is from guesses/observations
|
15
|
+
PRODID_MAP = {
|
16
|
+
0: "Unknown",
|
17
|
+
1: "Import0",
|
18
|
+
2: "Linker510",
|
19
|
+
3: "Cvtomf510",
|
20
|
+
4: "Linker600",
|
21
|
+
5: "Cvtomf600",
|
22
|
+
6: "Cvtres500",
|
23
|
+
7: "Utc11_Basic",
|
24
|
+
8: "Utc11_C",
|
25
|
+
9: "Utc12_Basic",
|
26
|
+
10: "Utc12_C",
|
27
|
+
11: "Utc12_CPP",
|
28
|
+
12: "AliasObj60",
|
29
|
+
13: "VisualBasic60",
|
30
|
+
14: "Masm613",
|
31
|
+
15: "Masm710",
|
32
|
+
16: "Linker511",
|
33
|
+
17: "Cvtomf511",
|
34
|
+
18: "Masm614",
|
35
|
+
19: "Linker512",
|
36
|
+
20: "Cvtomf512",
|
37
|
+
21: "Utc12_C_Std",
|
38
|
+
22: "Utc12_CPP_Std",
|
39
|
+
23: "Utc12_C_Book",
|
40
|
+
24: "Utc12_CPP_Book",
|
41
|
+
25: "Implib700",
|
42
|
+
26: "Cvtomf700",
|
43
|
+
27: "Utc13_Basic",
|
44
|
+
28: "Utc13_C",
|
45
|
+
29: "Utc13_CPP",
|
46
|
+
30: "Linker610",
|
47
|
+
31: "Cvtomf610",
|
48
|
+
32: "Linker601",
|
49
|
+
33: "Cvtomf601",
|
50
|
+
34: "Utc12_1_Basic",
|
51
|
+
35: "Utc12_1_C",
|
52
|
+
36: "Utc12_1_CPP",
|
53
|
+
37: "Linker620",
|
54
|
+
38: "Cvtomf620",
|
55
|
+
39: "AliasObj70",
|
56
|
+
40: "Linker621",
|
57
|
+
41: "Cvtomf621",
|
58
|
+
42: "Masm615",
|
59
|
+
43: "Utc13_LTCG_C",
|
60
|
+
44: "Utc13_LTCG_CPP",
|
61
|
+
45: "Masm620",
|
62
|
+
46: "ILAsm100",
|
63
|
+
47: "Utc12_2_Basic",
|
64
|
+
48: "Utc12_2_C",
|
65
|
+
49: "Utc12_2_CPP",
|
66
|
+
50: "Utc12_2_C_Std",
|
67
|
+
51: "Utc12_2_CPP_Std",
|
68
|
+
52: "Utc12_2_C_Book",
|
69
|
+
53: "Utc12_2_CPP_Book",
|
70
|
+
54: "Implib622",
|
71
|
+
55: "Cvtomf622",
|
72
|
+
56: "Cvtres501",
|
73
|
+
57: "Utc13_C_Std",
|
74
|
+
58: "Utc13_CPP_Std",
|
75
|
+
59: "Cvtpgd1300",
|
76
|
+
60: "Linker622",
|
77
|
+
61: "Linker700",
|
78
|
+
62: "Export622",
|
79
|
+
63: "Export700",
|
80
|
+
64: "Masm700",
|
81
|
+
65: "Utc13_POGO_I_C",
|
82
|
+
66: "Utc13_POGO_I_CPP",
|
83
|
+
67: "Utc13_POGO_O_C",
|
84
|
+
68: "Utc13_POGO_O_CPP",
|
85
|
+
69: "Cvtres700",
|
86
|
+
70: "Cvtres710p",
|
87
|
+
71: "Linker710p",
|
88
|
+
72: "Cvtomf710p",
|
89
|
+
73: "Export710p",
|
90
|
+
74: "Implib710p",
|
91
|
+
75: "Masm710p",
|
92
|
+
76: "Utc1310p_C",
|
93
|
+
77: "Utc1310p_CPP",
|
94
|
+
78: "Utc1310p_C_Std",
|
95
|
+
79: "Utc1310p_CPP_Std",
|
96
|
+
80: "Utc1310p_LTCG_C",
|
97
|
+
81: "Utc1310p_LTCG_CPP",
|
98
|
+
82: "Utc1310p_POGO_I_C",
|
99
|
+
83: "Utc1310p_POGO_I_CPP",
|
100
|
+
84: "Utc1310p_POGO_O_C",
|
101
|
+
85: "Utc1310p_POGO_O_CPP",
|
102
|
+
86: "Linker624",
|
103
|
+
87: "Cvtomf624",
|
104
|
+
88: "Export624",
|
105
|
+
89: "Implib624",
|
106
|
+
90: "Linker710",
|
107
|
+
91: "Cvtomf710",
|
108
|
+
92: "Export710",
|
109
|
+
93: "Implib710",
|
110
|
+
94: "Cvtres710",
|
111
|
+
95: "Utc1310_C",
|
112
|
+
96: "Utc1310_CPP",
|
113
|
+
97: "Utc1310_C_Std",
|
114
|
+
98: "Utc1310_CPP_Std",
|
115
|
+
99: "Utc1310_LTCG_C",
|
116
|
+
100: "Utc1310_LTCG_CPP",
|
117
|
+
101: "Utc1310_POGO_I_C",
|
118
|
+
102: "Utc1310_POGO_I_CPP",
|
119
|
+
103: "Utc1310_POGO_O_C",
|
120
|
+
104: "Utc1310_POGO_O_CPP",
|
121
|
+
105: "AliasObj710",
|
122
|
+
106: "AliasObj710p",
|
123
|
+
107: "Cvtpgd1310",
|
124
|
+
108: "Cvtpgd1310p",
|
125
|
+
109: "Utc1400_C",
|
126
|
+
110: "Utc1400_CPP",
|
127
|
+
111: "Utc1400_C_Std",
|
128
|
+
112: "Utc1400_CPP_Std",
|
129
|
+
113: "Utc1400_LTCG_C",
|
130
|
+
114: "Utc1400_LTCG_CPP",
|
131
|
+
115: "Utc1400_POGO_I_C",
|
132
|
+
116: "Utc1400_POGO_I_CPP",
|
133
|
+
117: "Utc1400_POGO_O_C",
|
134
|
+
118: "Utc1400_POGO_O_CPP",
|
135
|
+
119: "Cvtpgd1400",
|
136
|
+
120: "Linker800",
|
137
|
+
121: "Cvtomf800",
|
138
|
+
122: "Export800",
|
139
|
+
123: "Implib800",
|
140
|
+
124: "Cvtres800",
|
141
|
+
125: "Masm800",
|
142
|
+
126: "AliasObj800",
|
143
|
+
127: "PhoenixPrerelease",
|
144
|
+
128: "Utc1400_CVTCIL_C",
|
145
|
+
129: "Utc1400_CVTCIL_CPP",
|
146
|
+
130: "Utc1400_LTCG_MSIL",
|
147
|
+
131: "Utc1500_C",
|
148
|
+
132: "Utc1500_CPP",
|
149
|
+
133: "Utc1500_C_Std",
|
150
|
+
134: "Utc1500_CPP_Std",
|
151
|
+
135: "Utc1500_CVTCIL_C",
|
152
|
+
136: "Utc1500_CVTCIL_CPP",
|
153
|
+
137: "Utc1500_LTCG_C",
|
154
|
+
138: "Utc1500_LTCG_CPP",
|
155
|
+
139: "Utc1500_LTCG_MSIL",
|
156
|
+
140: "Utc1500_POGO_I_C",
|
157
|
+
141: "Utc1500_POGO_I_CPP",
|
158
|
+
142: "Utc1500_POGO_O_C",
|
159
|
+
143: "Utc1500_POGO_O_CPP",
|
160
|
+
|
161
|
+
144: "Cvtpgd1500",
|
162
|
+
145: "Linker900",
|
163
|
+
146: "Export900",
|
164
|
+
147: "Implib900",
|
165
|
+
148: "Cvtres900",
|
166
|
+
149: "Masm900",
|
167
|
+
150: "AliasObj900",
|
168
|
+
151: "Resource900",
|
169
|
+
|
170
|
+
152: "AliasObj1000",
|
171
|
+
154: "Cvtres1000",
|
172
|
+
155: "Export1000",
|
173
|
+
156: "Implib1000",
|
174
|
+
157: "Linker1000",
|
175
|
+
158: "Masm1000",
|
176
|
+
|
177
|
+
170: "Utc1600_C",
|
178
|
+
171: "Utc1600_CPP",
|
179
|
+
172: "Utc1600_CVTCIL_C",
|
180
|
+
173: "Utc1600_CVTCIL_CPP",
|
181
|
+
174: "Utc1600_LTCG_C ",
|
182
|
+
175: "Utc1600_LTCG_CPP",
|
183
|
+
176: "Utc1600_LTCG_MSIL",
|
184
|
+
177: "Utc1600_POGO_I_C",
|
185
|
+
178: "Utc1600_POGO_I_CPP",
|
186
|
+
179: "Utc1600_POGO_O_C",
|
187
|
+
180: "Utc1600_POGO_O_CPP",
|
188
|
+
|
189
|
+
# vvv
|
190
|
+
183: "Linker1010",
|
191
|
+
184: "Export1010",
|
192
|
+
185: "Implib1010",
|
193
|
+
186: "Cvtres1010",
|
194
|
+
187: "Masm1010",
|
195
|
+
188: "AliasObj1010",
|
196
|
+
# ^^^
|
197
|
+
|
198
|
+
199: "AliasObj1100",
|
199
|
+
201: "Cvtres1100",
|
200
|
+
202: "Export1100",
|
201
|
+
203: "Implib1100",
|
202
|
+
204: "Linker1100",
|
203
|
+
205: "Masm1100",
|
204
|
+
|
205
|
+
206: "Utc1700_C",
|
206
|
+
207: "Utc1700_CPP",
|
207
|
+
208: "Utc1700_CVTCIL_C",
|
208
|
+
209: "Utc1700_CVTCIL_CPP",
|
209
|
+
210: "Utc1700_LTCG_C ",
|
210
|
+
211: "Utc1700_LTCG_CPP",
|
211
|
+
212: "Utc1700_LTCG_MSIL",
|
212
|
+
213: "Utc1700_POGO_I_C",
|
213
|
+
214: "Utc1700_POGO_I_CPP",
|
214
|
+
215: "Utc1700_POGO_O_C",
|
215
|
+
216: "Utc1700_POGO_O_CPP",
|
216
|
+
}
|
217
|
+
|
218
|
+
##
|
219
|
+
# A convenient exception to raise if the Rich Header doesn't exist.
|
220
|
+
class RichHeaderNotFoundException(Exception):
|
221
|
+
def __init__(self):
|
222
|
+
Exception.__init__(self, "Rich footer does not appear to exist")
|
223
|
+
|
224
|
+
##
|
225
|
+
# Locate the body of the data that contains the rich header This will be
|
226
|
+
# (roughly) between 0x3c and the beginning of the PE header, but the entire
|
227
|
+
# thing up to the last checksum will be needed in order to verify the header.
|
228
|
+
def get_file_header(file_name):
|
229
|
+
f = open(file_name,'rb')
|
230
|
+
|
231
|
+
#start with 0x3c
|
232
|
+
f.seek(PE_START)
|
233
|
+
data = f.read(PE_FIELD_LENGTH)
|
234
|
+
|
235
|
+
if data == '': #File is empty, bail
|
236
|
+
raise RichHeaderNotFoundException()
|
237
|
+
end = struct.unpack('<L',data)[0] # get the value at 0x3c
|
238
|
+
|
239
|
+
f.seek(0)
|
240
|
+
data = f.read( end ) # read until that value is reached
|
241
|
+
f.close()
|
242
|
+
|
243
|
+
return data
|
244
|
+
|
245
|
+
##
|
246
|
+
# This class assists in parsing the Rich Header from PE Files.
|
247
|
+
# The Rich Header is the section in the PE file following the dos stub but
|
248
|
+
# preceding the lfa_new header which is inserted by link.exe when building with
|
249
|
+
# the Microsoft Compilers. The Rich Heder contains the following:
|
250
|
+
# <pre>
|
251
|
+
# marker, checksum, checksum, checksum,
|
252
|
+
# R_compid_i, R_occurrence_i,
|
253
|
+
# R_compid_i+1, R_occurrence_i+1, ...
|
254
|
+
# R_compid_N-1, R_occurrence_N-1, Rich, marker
|
255
|
+
#
|
256
|
+
# marker = checksum XOR 0x536e6144
|
257
|
+
# R_compid_i is the ith compid XORed with the checksum
|
258
|
+
# R_occurrence_i is the ith occurrence XORed with the checksum
|
259
|
+
# Rich = the text string 'Rich'
|
260
|
+
# The checksum is the sum of all the PE Header values rotated by their
|
261
|
+
# offset and the sum of all compids rotated by their occurrence counts.
|
262
|
+
# </pre>
|
263
|
+
# @see _validate_checksum code for checksum calculation
|
264
|
+
class ParsedRichHeader:
|
265
|
+
##
|
266
|
+
# Creates a ParsedRichHeader from the specified PE File.
|
267
|
+
# @throws RichHeaderNotFoundException if the file does not contain a rich header
|
268
|
+
# @param file_name The PE File to be parsed
|
269
|
+
def __init__(self, file_name):
|
270
|
+
## The file that was parsed
|
271
|
+
self.file_name = file_name
|
272
|
+
self._parse( file_name )
|
273
|
+
|
274
|
+
##
|
275
|
+
# Used internally to parse the PE File and extract Rich Header data.
|
276
|
+
# Initializes self.compids and self.valid_checksum.
|
277
|
+
# @param file_name The PE File to be parsed
|
278
|
+
# @throws RichHeaderNotFoundException if the file does not contain a rich header
|
279
|
+
def _parse(self,file_name):
|
280
|
+
#make sure there is a header:
|
281
|
+
data = get_file_header( file_name )
|
282
|
+
|
283
|
+
compid_end_index = data.find(RICH_TEXT)
|
284
|
+
if compid_end_index == -1:
|
285
|
+
raise RichHeaderNotFoundException()
|
286
|
+
|
287
|
+
rich_offset = compid_end_index + RICH_TEXT_LENGTH
|
288
|
+
|
289
|
+
checksum_text = data[rich_offset:rich_offset+4]
|
290
|
+
checksum_value = struct.unpack('<L', checksum_text)[0]
|
291
|
+
#start marker denotes the beginning of the rich header
|
292
|
+
start_marker = struct.pack('<LLLL',checksum_value ^ CHECKSUM_MASK, checksum_value, checksum_value, checksum_value )[0]
|
293
|
+
|
294
|
+
rich_header_start = data.find(start_marker)
|
295
|
+
if rich_header_start == -1:
|
296
|
+
raise RichHeaderNotFoundException()
|
297
|
+
|
298
|
+
compid_start_index = rich_header_start + 16 # move past the marker and 3 checksums
|
299
|
+
|
300
|
+
compids = dict()
|
301
|
+
for i in range(compid_start_index, compid_end_index, 8):
|
302
|
+
compid = struct.unpack('<L',data[i:i+4])[0] ^ checksum_value
|
303
|
+
count = struct.unpack('<L',data[i+4:i+8])[0] ^ checksum_value
|
304
|
+
compids[compid]=count
|
305
|
+
|
306
|
+
## A dictionary of compids and their occurrence counts
|
307
|
+
self.compids = compids
|
308
|
+
## A value for later reference to see if the checksum was valid
|
309
|
+
self.valid_checksum = self._validate_checksum( data, rich_header_start, checksum_value )
|
310
|
+
|
311
|
+
##
|
312
|
+
# Compute the checksum value and see if it matches the checksum stored in
|
313
|
+
# the Rich Header.
|
314
|
+
# The checksum is the sum of all the PE Header values rotated by their
|
315
|
+
# offset and the sum of all compids rotated by their occurrence counts
|
316
|
+
# @param data A blob of binary data that corresponds to the PE Header data
|
317
|
+
# @param rich_header_start The offset to marker, checksum, checksum, checksum
|
318
|
+
# @returns True if the checksum is valid, false otherwise
|
319
|
+
def _validate_checksum(self, data, rich_header_start, checksum):
|
320
|
+
|
321
|
+
#initialize the checksum offset at which the rich header is located
|
322
|
+
cksum = rich_header_start
|
323
|
+
|
324
|
+
#add the value from the pe header after rotating the value by its offset in the pe header
|
325
|
+
for i in range(0,rich_header_start):
|
326
|
+
if PE_START <= i <= PE_START+PE_FIELD_LENGTH-1:
|
327
|
+
continue
|
328
|
+
temp = ord(data[i])
|
329
|
+
cksum+= ((temp << (i%32)) | (temp >> (32-(i%32))) & 0xff)
|
330
|
+
cksum &=0xffffffff
|
331
|
+
|
332
|
+
#add each compid to the checksum after rotating it by its occurrence count
|
333
|
+
for k in self.compids.keys():
|
334
|
+
cksum += (k << self.compids[k]%32 | k >> ( 32 - (self.compids[k]%32)))
|
335
|
+
cksum &=0xffffffff
|
336
|
+
|
337
|
+
## A convenient place for storing the checksum that was computing during checksum validation
|
338
|
+
self.checksum = cksum
|
339
|
+
|
340
|
+
return cksum == checksum
|
341
|
+
|
342
|
+
if __name__ == "__main__":
|
343
|
+
ph = ParsedRichHeader(sys.argv[1])
|
344
|
+
print ("PRODID name build count")
|
345
|
+
for key in ph.compids.keys():
|
346
|
+
count = ph.compids[key]
|
347
|
+
prodid, build = (key>>16), key&0xFFFF
|
348
|
+
prodid_name = PRODID_MAP[prodid] if prodid in PRODID_MAP else "<unknown>"
|
349
|
+
print ('%6d %-15s %5d %5d' % (prodid, prodid_name, build, count))
|
350
|
+
if ph.valid_checksum:
|
351
|
+
print ("Checksum valid")
|
352
|
+
else:
|
353
|
+
print("Checksum not valid!")
|