pedump 0.5.1 → 0.6.1
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.
- 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!")
|