pedump 0.6.10 → 0.7.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 +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +85 -34
- data/README.md +89 -72
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/pedump +1 -1
- data/data/jc-userdb.txt +0 -8
- data/data/signatures.txt +1 -2
- data/data/userdb.txt +0 -8
- data/lib/pedump/cli.rb +305 -48
- data/lib/pedump/clr/readytorun.rb +115 -0
- data/lib/pedump/clr/signature.rb +318 -0
- data/lib/pedump/clr.rb +709 -0
- data/lib/pedump/logger.rb +1 -1
- data/lib/pedump.rb +41 -5
- data/pedump.gemspec +8 -5
- metadata +8 -8
data/lib/pedump/clr.rb
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
#coding: binary
|
|
2
|
+
require 'iostruct'
|
|
3
|
+
|
|
4
|
+
require_relative 'clr/signature'
|
|
5
|
+
require_relative 'clr/readytorun'
|
|
6
|
+
|
|
7
|
+
class PEdump
|
|
8
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.25.3.3-cli-header.md
|
|
9
|
+
class IMAGE_COR20_HEADER < IOStruct.new(
|
|
10
|
+
'V S2 Q VV Q6',
|
|
11
|
+
:cb,
|
|
12
|
+
:MajorRuntimeVersion,
|
|
13
|
+
:MinorRuntimeVersion,
|
|
14
|
+
:MetaData, # IMAGE_DATA_DIRECTORY
|
|
15
|
+
:Flags,
|
|
16
|
+
:EntryPointToken,
|
|
17
|
+
# IMAGE_DATA_DIRECTORies:
|
|
18
|
+
:Resources,
|
|
19
|
+
:StrongNameSignature,
|
|
20
|
+
:CodeManagerTable,
|
|
21
|
+
:VTableFixups,
|
|
22
|
+
:ExportAddressTableJumps,
|
|
23
|
+
:ManagedNativeHeader
|
|
24
|
+
)
|
|
25
|
+
FLAGS = {
|
|
26
|
+
0x00001 => 'ILONLY',
|
|
27
|
+
0x00002 => '32BITREQUIRED',
|
|
28
|
+
0x00004 => 'IL_LIBRARY',
|
|
29
|
+
0x00008 => 'STRONGNAMESIGNED',
|
|
30
|
+
0x00010 => 'NATIVE_ENTRYPOINT',
|
|
31
|
+
0x10000 => 'TRACKDEBUGDATA',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
FLAGS.each{ |k,v| const_set("COMIMAGE_FLAGS_#{v}", k) }
|
|
35
|
+
|
|
36
|
+
def flags
|
|
37
|
+
FLAGS.find_all{ |k,v| (self.Flags & k) != 0 }.map(&:last)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.read io
|
|
41
|
+
super.tap do |r|
|
|
42
|
+
%i'MetaData Resources StrongNameSignature CodeManagerTable VTableFixups ExportAddressTableJumps ManagedNativeHeader'.each do |field|
|
|
43
|
+
next unless r[field] # broken CLR header
|
|
44
|
+
|
|
45
|
+
r[field] = IMAGE_DATA_DIRECTORY.read([r[field]].pack('Q'))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
module CLR
|
|
52
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.24.2.1-metadata-root.md
|
|
53
|
+
class MetadataHeader < IOStruct.new(
|
|
54
|
+
'V S2 VV',
|
|
55
|
+
:Magic,
|
|
56
|
+
:MajorVersion,
|
|
57
|
+
:MinorVersion,
|
|
58
|
+
:ExtraData,
|
|
59
|
+
:VersionLength, # aligned to 4 bytes
|
|
60
|
+
:Version,
|
|
61
|
+
:Flags, # maybe 1 byte of flags + 1 byte of padding
|
|
62
|
+
:NumberOfStreams
|
|
63
|
+
)
|
|
64
|
+
MAGIC = 0x424A5342 # 'BSJB'
|
|
65
|
+
|
|
66
|
+
def valid?
|
|
67
|
+
Magic == MAGIC
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.read io
|
|
71
|
+
super.tap do |r|
|
|
72
|
+
r.Version = io.read(r.VersionLength).unpack1('A*')
|
|
73
|
+
r.Flags = io.read(2).unpack1('S')
|
|
74
|
+
r.NumberOfStreams = io.read(2).unpack1('v')
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class MetadataStreamHeader < IOStruct.new(
|
|
80
|
+
'VV',
|
|
81
|
+
:offset,
|
|
82
|
+
:size,
|
|
83
|
+
:name # a zero-terminated ASCII string no longer than 31 characters. The name might be shorter, case the size of the stream header is correspondingly
|
|
84
|
+
# reduced, padded to the 4-byte boundary.
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def self.read io
|
|
88
|
+
super.tap do |r|
|
|
89
|
+
r.name = ''
|
|
90
|
+
nread = 0
|
|
91
|
+
while !io.eof? && r.name.size < 32
|
|
92
|
+
c = io.read(1)
|
|
93
|
+
nread += 1
|
|
94
|
+
break if c == "\0"
|
|
95
|
+
|
|
96
|
+
r.name << c
|
|
97
|
+
end
|
|
98
|
+
if !io.eof? && nread % 4 != 0
|
|
99
|
+
io.read(4 - nread % 4) # padding
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.24.2.6-metadata-stream.md
|
|
106
|
+
class MetadataTableStreamHeader < IOStruct.new('V CC CCQQ', :Reserved, :MajorVersion, :MinorVersion, :HeapSizes, :Reserved2, :Valid, :Sorted, :Rows)
|
|
107
|
+
attr_accessor :sizes_hash
|
|
108
|
+
|
|
109
|
+
HEAP_SIZES_MANY_STRINGS = 1 # Size of "#Strings" stream ≥ 216
|
|
110
|
+
HEAP_SIZES_MANY_GUIDS = 2 # Size of "#GUID" stream ≥ 216
|
|
111
|
+
HEAP_SIZES_MANY_BLOBS = 4 # Size of "#Blob" stream ≥ 216
|
|
112
|
+
|
|
113
|
+
FLAGS = {
|
|
114
|
+
Module: 0x1,
|
|
115
|
+
TypeRef: 0x2,
|
|
116
|
+
TypeDef: 0x4,
|
|
117
|
+
FiedPtr: 0x8,
|
|
118
|
+
Field: 0x10,
|
|
119
|
+
MethodPtr: 0x20,
|
|
120
|
+
MethodDef: 0x40,
|
|
121
|
+
ParamPtr: 0x80,
|
|
122
|
+
Param: 0x100,
|
|
123
|
+
InterfaceImpl: 0x200,
|
|
124
|
+
MemberRef: 0x400,
|
|
125
|
+
Constant: 0x800,
|
|
126
|
+
CustomAttribute: 0x1000,
|
|
127
|
+
FieldMarshal: 0x2000,
|
|
128
|
+
DeclSecurity: 0x4000,
|
|
129
|
+
ClassLayout: 0x8000,
|
|
130
|
+
FieldLayout: 0x10000,
|
|
131
|
+
StandAloneSig: 0x20000,
|
|
132
|
+
EventMap: 0x40000,
|
|
133
|
+
EventPtr: 0x80000,
|
|
134
|
+
Event: 0x100000,
|
|
135
|
+
PropertyMap: 0x200000,
|
|
136
|
+
PropertyPtr: 0x400000,
|
|
137
|
+
Property: 0x800000,
|
|
138
|
+
MethodSemantics: 0x1000000,
|
|
139
|
+
MethodImpl: 0x2000000,
|
|
140
|
+
ModuleRef: 0x4000000,
|
|
141
|
+
TypeSpec: 0x8000000,
|
|
142
|
+
ImplMap: 0x10000000,
|
|
143
|
+
FieldRVA: 0x20000000,
|
|
144
|
+
EnCLog: 0x40000000,
|
|
145
|
+
EnCMap: 0x80000000,
|
|
146
|
+
Assembly: 0x100000000,
|
|
147
|
+
AssemblyProcessor: 0x200000000,
|
|
148
|
+
AssemblyOS: 0x400000000,
|
|
149
|
+
AssemblyRef: 0x800000000,
|
|
150
|
+
AssemblyRefProcessor: 0x1000000000,
|
|
151
|
+
AssemblyRefOS: 0x2000000000,
|
|
152
|
+
File: 0x4000000000,
|
|
153
|
+
ExportedType: 0x8000000000,
|
|
154
|
+
ManifestResource: 0x10000000000,
|
|
155
|
+
NestedClass: 0x20000000000,
|
|
156
|
+
GenericParam: 0x40000000000,
|
|
157
|
+
MethodSpec: 0x80000000000,
|
|
158
|
+
GenericParamConstraint: 0x100000000000,
|
|
159
|
+
Document: 0x1000000000000,
|
|
160
|
+
MethodDebugInformation: 0x2000000000000,
|
|
161
|
+
LocalScope: 0x4000000000000,
|
|
162
|
+
LocalVariable: 0x8000000000000,
|
|
163
|
+
LocalConstant: 0x10000000000000,
|
|
164
|
+
ImportScope: 0x20000000000000,
|
|
165
|
+
StateMachineMethod: 0x40000000000000,
|
|
166
|
+
CustomDebugInformation: 0x80000000000000,
|
|
167
|
+
}
|
|
168
|
+
INVERSE_FLAGS = FLAGS.invert
|
|
169
|
+
|
|
170
|
+
def known_table? key
|
|
171
|
+
FLAGS.key?(key)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def heap_sizes
|
|
175
|
+
sizes = []
|
|
176
|
+
sizes << 'MANY_STRINGS' if self.HeapSizes & HEAP_SIZES_MANY_STRINGS != 0
|
|
177
|
+
sizes << 'MANY_GUIDS' if self.HeapSizes & HEAP_SIZES_MANY_GUIDS != 0
|
|
178
|
+
sizes << 'MANY_BLOBS' if self.HeapSizes & HEAP_SIZES_MANY_BLOBS != 0
|
|
179
|
+
sizes
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def valid_flags
|
|
183
|
+
FLAGS.find_all{ |k,v| (self.Valid & v) != 0 }.map(&:first)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def sorted_flags
|
|
187
|
+
FLAGS.find_all{ |k,v| (self.Sorted & v) != 0 }.map(&:first)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def valid?
|
|
191
|
+
self.Reserved == 0 && self.MajorVersion == 2 && self.MinorVersion == 0
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.read io
|
|
195
|
+
super.tap do |r|
|
|
196
|
+
idx = 1
|
|
197
|
+
r.sizes_hash = {}
|
|
198
|
+
n = r.Valid
|
|
199
|
+
while n > 0
|
|
200
|
+
if n & 1 != 0
|
|
201
|
+
key = INVERSE_FLAGS[idx].to_sym
|
|
202
|
+
r.sizes_hash[key] = io.read(4).unpack1('V')
|
|
203
|
+
end
|
|
204
|
+
n >>= 1
|
|
205
|
+
idx <<= 1
|
|
206
|
+
end
|
|
207
|
+
r.Rows = r.sizes_hash.values
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# reference types
|
|
213
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.24.2.6-metadata-stream.md
|
|
214
|
+
CustomAttributeType = [:not_used, :not_used, :MethodDef, :MemberRef, :not_used] # see spec. last :not used is important for it to be 3 bits in size
|
|
215
|
+
HasConstant = [:Field, :Param, :Property]
|
|
216
|
+
HasCustomAttribute = [:MethodDef, :Field, :TypeRef, :TypeDef, :Param, :InterfaceImpl, :MemberRef, :Module, :DeclSecurity, :Property, :Event, :StandAloneSig, :ModuleRef, :TypeSpec, :Assembly, :AssemblyRef, :File, :ExportedType, :ManifestResource, :GenericParam, :GenericParamConstraint, :MethodSpec] # XXX not checked thoroughly
|
|
217
|
+
HasDeclSecurity = [:TypeDef, :MethodDef, :Assembly]
|
|
218
|
+
HasFieldMarshal = [:Field, :Param]
|
|
219
|
+
HasSemantics = [:Event, :Property]
|
|
220
|
+
Implementation = [:File, :AssemblyRef, :ExportedType]
|
|
221
|
+
MemberForwarded = [:Field, :MethodDef]
|
|
222
|
+
MemberRefParent = [:TypeDef, :TypeRef, :ModuleRef, :MethodDef, :TypeSpec]
|
|
223
|
+
MethodDefOrRef = [:MethodDef, :MemberRef]
|
|
224
|
+
ResolutionScope = [:Module, :ModuleRef, :AssemblyRef, :TypeRef]
|
|
225
|
+
TypeDefOrRef = [:TypeDef, :TypeRef, :TypeSpec]
|
|
226
|
+
TypeOrMethodDef = [:TypeDef, :MethodDef]
|
|
227
|
+
|
|
228
|
+
module DynTableMethods
|
|
229
|
+
def get_name(strings)
|
|
230
|
+
h = self.to_h
|
|
231
|
+
if h[:TypeNamespace] || h[:TypeName]
|
|
232
|
+
if h[:TypeNamespace] != 0
|
|
233
|
+
"#{strings[h[:TypeNamespace]]}.#{strings[h[:TypeName]]}"
|
|
234
|
+
else
|
|
235
|
+
strings[h[:TypeName]]
|
|
236
|
+
end
|
|
237
|
+
elsif h[:Name]
|
|
238
|
+
strings[h[:Name]]
|
|
239
|
+
elsif h[:ImportName]
|
|
240
|
+
strings[h[:ImportName]]
|
|
241
|
+
else
|
|
242
|
+
nil
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
TableDefs = {
|
|
248
|
+
# undocumented?
|
|
249
|
+
FieldPtr: { Field: :Field },
|
|
250
|
+
MethodPtr: { Method: :MethodDef },
|
|
251
|
+
ParamPtr: { Param: :Param },
|
|
252
|
+
EventPtr: { Event: :Event },
|
|
253
|
+
PropertyPtr: { Property: :Property },
|
|
254
|
+
|
|
255
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.30-module_0x00.md
|
|
256
|
+
Module: {
|
|
257
|
+
Generation: 2, # a 2-byte value, reserved, shall be zero)
|
|
258
|
+
Name: :string, # an index into the String heap
|
|
259
|
+
Mvid: :guid, # an index into the Guid heap
|
|
260
|
+
EncId: :guid, # an index into the Guid heap; reserved, shall be zero
|
|
261
|
+
EncBaseId: :guid, # an index into the Guid heap; reserved, shall be zero
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.38-typeref-0x01.md
|
|
265
|
+
TypeRef: {
|
|
266
|
+
ResolutionScope: ResolutionScope,
|
|
267
|
+
TypeName: :string,
|
|
268
|
+
TypeNamespace: :string,
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.37-typedef-0x02.md
|
|
272
|
+
TypeDef: {
|
|
273
|
+
Flags: 4, # a 4-byte bitmask of TypeAttributes
|
|
274
|
+
TypeName: :string,
|
|
275
|
+
TypeNamespace: :string,
|
|
276
|
+
Extends: TypeDefOrRef,
|
|
277
|
+
FieldList: :Field,
|
|
278
|
+
MethodList: :MethodDef,
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.15-field-0x04.md
|
|
282
|
+
Field: {
|
|
283
|
+
Flags: 2, # a 2-byte bitmask of FieldAttributes
|
|
284
|
+
Name: :string,
|
|
285
|
+
Signature: :blob,
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.26-methoddef-0x06.md
|
|
289
|
+
MethodDef: {
|
|
290
|
+
RVA: 4, # a 4-byte constant
|
|
291
|
+
ImplFlags: 2, # a 2-byte bitmask of MethodImplAttributes
|
|
292
|
+
Flags: 2, # a 2-byte bitmask of MethodAttributes
|
|
293
|
+
Name: :string,
|
|
294
|
+
Signature: :blob,
|
|
295
|
+
ParamList: :Param,
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.33-param-0x08.md
|
|
299
|
+
Param: {
|
|
300
|
+
Flags: 2, # a 2-byte bitmask of ParamAttributes
|
|
301
|
+
Sequence: 2, # a 2-byte constant
|
|
302
|
+
Name: :string,
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.23-interfaceimpl-0x09.md
|
|
306
|
+
InterfaceImpl: {
|
|
307
|
+
Class: :TypeDef,
|
|
308
|
+
Interface: TypeDefOrRef,
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.25-memberref-0x0a.md
|
|
312
|
+
MemberRef: {
|
|
313
|
+
Class: MemberRefParent,
|
|
314
|
+
Name: :string,
|
|
315
|
+
Signature: :blob,
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.9-constant-0x0b.md
|
|
319
|
+
Constant: {
|
|
320
|
+
Type: 2, # a 1-byte constant, followed by a 1-byte padding zero
|
|
321
|
+
Parent: HasConstant,
|
|
322
|
+
Value: :blob,
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.10-customattribute-0x0c.md
|
|
326
|
+
CustomAttribute: {
|
|
327
|
+
Parent: HasCustomAttribute,
|
|
328
|
+
Type: CustomAttributeType,
|
|
329
|
+
Value: :blob,
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.17-fieldmarshal-0x0d.md
|
|
333
|
+
FieldMarshal: {
|
|
334
|
+
Parent: HasFieldMarshal,
|
|
335
|
+
NativeType: :blob,
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.11-declsecurity-0x0e.md
|
|
339
|
+
DeclSecurity: {
|
|
340
|
+
Action: 2, # a 2-byte constant
|
|
341
|
+
Parent: HasDeclSecurity,
|
|
342
|
+
PermissionSet: :blob,
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.8-classlayout-0x0f.md
|
|
346
|
+
ClassLayout: {
|
|
347
|
+
PackingSize: 2, # a 2-byte constant
|
|
348
|
+
ClassSize: 4, # a 4-byte constant
|
|
349
|
+
Parent: :TypeDef,
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.16-fieldlayout-0x10.md
|
|
353
|
+
FieldLayout: {
|
|
354
|
+
Offset: 4, # a 4-byte constant
|
|
355
|
+
Field: :Field,
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.36-standalonesig-0x11.md
|
|
359
|
+
StandAloneSig: {
|
|
360
|
+
Signature: :blob,
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.12-eventmap-0x12.md
|
|
364
|
+
EventMap: {
|
|
365
|
+
Parent: :TypeDef,
|
|
366
|
+
EventList: :Event,
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.13-event-0x14.md
|
|
370
|
+
Event: {
|
|
371
|
+
EventFlags: 2, # a 2-byte bitmask of EventAttributes
|
|
372
|
+
Name: :string,
|
|
373
|
+
EventType: TypeDefOrRef,
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.35-propertymap-0x15.md
|
|
377
|
+
PropertyMap: {
|
|
378
|
+
Parent: :TypeDef,
|
|
379
|
+
PropertyList: :Property,
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.34-property-0x17.md
|
|
383
|
+
Property: {
|
|
384
|
+
Flags: 2, # a 2-byte bitmask of PropertyAttributes
|
|
385
|
+
Name: :string,
|
|
386
|
+
Type: :blob,
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.28-methodsemantics-0x18.md
|
|
390
|
+
MethodSemantics: {
|
|
391
|
+
Semantics: 2, # a 2-byte bitmask of MethodSemanticsAttributes
|
|
392
|
+
Method: :MethodDef,
|
|
393
|
+
Association: HasSemantics,
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.27-methodimpl-0x19.md
|
|
397
|
+
MethodImpl: {
|
|
398
|
+
Class: :TypeDef,
|
|
399
|
+
MethodBody: MethodDefOrRef,
|
|
400
|
+
MethodDeclaration: MethodDefOrRef,
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.31-moduleref-0x1a.md
|
|
404
|
+
ModuleRef: {
|
|
405
|
+
Name: :string,
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.39-typespec-0x1b.md
|
|
409
|
+
TypeSpec: {
|
|
410
|
+
Signature: :blob,
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.22-implmap-0x1c.md
|
|
414
|
+
ImplMap: {
|
|
415
|
+
MappingFlags: 2, # a 2-byte bitmask of PInvokeAttributes
|
|
416
|
+
MemberForwarded: MemberForwarded,
|
|
417
|
+
ImportName: :string,
|
|
418
|
+
ImportScope: :ModuleRef,
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.18-fieldrva-0x1d.md
|
|
422
|
+
FieldRVA: {
|
|
423
|
+
RVA: 4, # a 4-byte constant
|
|
424
|
+
Field: :Field,
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.2-assembly-0x20.md
|
|
428
|
+
Assembly: {
|
|
429
|
+
HashAlgId: 4, # a 4-byte constant
|
|
430
|
+
MajorVersion: 2, # a 2-byte constant
|
|
431
|
+
MinorVersion: 2, # a 2-byte constant
|
|
432
|
+
BuildNumber: 2, # a 2-byte constant
|
|
433
|
+
RevisionNumber: 2, # a 2-byte constant
|
|
434
|
+
Flags: 4, # a 4-byte bitmask of AssemblyFlags
|
|
435
|
+
PublicKey: :blob,
|
|
436
|
+
Name: :string,
|
|
437
|
+
Culture: :string,
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.4-assemblyprocessor-0x21.md
|
|
441
|
+
AssemblyProcessor: {
|
|
442
|
+
Processor: 4, # a 4-byte constant
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.3-assemblyos-0x22.md
|
|
446
|
+
AssemblyOS: {
|
|
447
|
+
OSPlatformId: 4, # a 4-byte constant
|
|
448
|
+
OSMajorVersion: 4, # a 4-byte constant
|
|
449
|
+
OSMinorVersion: 4, # a 4-byte constant
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.5-assemblyref-0x23.md
|
|
453
|
+
AssemblyRef: {
|
|
454
|
+
MajorVersion: 2, # a 2-byte constant
|
|
455
|
+
MinorVersion: 2, # a 2-byte constant
|
|
456
|
+
BuildNumber: 2, # a 2-byte constant
|
|
457
|
+
RevisionNumber: 2, # a 2-byte constant
|
|
458
|
+
Flags: 4, # a 4-byte bitmask of AssemblyFlags
|
|
459
|
+
PublicKeyOrToken: :blob,
|
|
460
|
+
Name: :string,
|
|
461
|
+
Culture: :string,
|
|
462
|
+
HashValue: :blob,
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.7-assemblyrefprocessor-0x24.md
|
|
466
|
+
AssemblyRefProcessor: {
|
|
467
|
+
Processor: 4, # a 4-byte constant
|
|
468
|
+
AssemblyRef: :AssemblyRef,
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.6-assemblyrefos-0x25.md
|
|
472
|
+
AssemblyRefOS: {
|
|
473
|
+
OSPlatformId: 4, # a 4-byte constant
|
|
474
|
+
OSMajorVersion: 4, # a 4-byte constant
|
|
475
|
+
OSMinorVersion: 4, # a 4-byte constant
|
|
476
|
+
AssemblyRef: :AssemblyRef,
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.19-file-0x26.md
|
|
480
|
+
File: {
|
|
481
|
+
Flags: 4, # a 4-byte bitmask of FileAttributes
|
|
482
|
+
Name: :string,
|
|
483
|
+
HashValue: :blob,
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.24-manifestresource-0x28.md
|
|
487
|
+
ManifestResource: {
|
|
488
|
+
Offset: 4, # a 4-byte constant
|
|
489
|
+
Flags: 4, # a 4-byte bitmask of ManifestResourceAttributes
|
|
490
|
+
Name: :string,
|
|
491
|
+
Implementation: Implementation,
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.32-nestedclass-0x29.md
|
|
495
|
+
NestedClass: {
|
|
496
|
+
NestedClass: :TypeDef,
|
|
497
|
+
EnclosingClass: :TypeDef,
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.20-genericparam-0x2a.md
|
|
501
|
+
GenericParam: {
|
|
502
|
+
Number: 2, # a 2-byte constant
|
|
503
|
+
Flags: 2, # a 2-byte bitmask of GenericParamAttributes
|
|
504
|
+
Owner: TypeOrMethodDef,
|
|
505
|
+
Name: :string,
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.29-methodspec-0x2b.md
|
|
509
|
+
MethodSpec: {
|
|
510
|
+
Method: MethodDefOrRef,
|
|
511
|
+
Instantiation: :blob,
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
# https://github.com/stakx/ecma-335/blob/master/docs/ii.22.21-genericparamconstraint-0x2c.md
|
|
515
|
+
GenericParamConstraint: {
|
|
516
|
+
Owner: :GenericParam,
|
|
517
|
+
Constraint: TypeDefOrRef,
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
# needed only for pedump CLI format guessing
|
|
522
|
+
class TablesHash < Hash; end
|
|
523
|
+
class StringsHash < Hash; end
|
|
524
|
+
|
|
525
|
+
def self._create_dynamic_class fields, hdr, name: nil
|
|
526
|
+
table_idx_bits = {}
|
|
527
|
+
string_keys = []
|
|
528
|
+
|
|
529
|
+
decl = fields.map do |k,v|
|
|
530
|
+
case v
|
|
531
|
+
when 2
|
|
532
|
+
'S'
|
|
533
|
+
when 4
|
|
534
|
+
'V'
|
|
535
|
+
when :blob
|
|
536
|
+
hdr.HeapSizes & MetadataTableStreamHeader::HEAP_SIZES_MANY_BLOBS != 0 ? 'V' : 'S'
|
|
537
|
+
when :guid
|
|
538
|
+
hdr.HeapSizes & MetadataTableStreamHeader::HEAP_SIZES_MANY_GUIDS != 0 ? 'V' : 'S'
|
|
539
|
+
when :string
|
|
540
|
+
string_keys << k
|
|
541
|
+
hdr.HeapSizes & MetadataTableStreamHeader::HEAP_SIZES_MANY_STRINGS != 0 ? 'V' : 'S'
|
|
542
|
+
when Array
|
|
543
|
+
# pointer to table i out of n possible tables
|
|
544
|
+
n = v.size
|
|
545
|
+
table_idx_bits[k] = bits_for_table_idx = Math.log2(n).ceil
|
|
546
|
+
max_rows = 2**(16 - bits_for_table_idx)
|
|
547
|
+
v.each{ |table_id| raise "Unknown table: #{table_id}" unless table_id == :not_used || hdr.known_table?(table_id) }
|
|
548
|
+
v.any?{ |table_id| hdr.sizes_hash[table_id].to_i >= max_rows } ? 'V' : 'S'
|
|
549
|
+
when Symbol
|
|
550
|
+
raise "Unknown table: #{v}" unless hdr.known_table?(v)
|
|
551
|
+
hdr.sizes_hash[v].to_i < 2**16 ? 'S' : 'V'
|
|
552
|
+
else
|
|
553
|
+
raise "Unknown field type #{v.inspect}"
|
|
554
|
+
end
|
|
555
|
+
end.join
|
|
556
|
+
|
|
557
|
+
IOStruct.new(decl, *fields.keys, inspect_name_override: name).tap do |klass|
|
|
558
|
+
klass.const_set(:STRING_KEYS, string_keys)
|
|
559
|
+
klass.include(DynTableMethods)
|
|
560
|
+
fields.each do |k,v|
|
|
561
|
+
case v
|
|
562
|
+
when Array
|
|
563
|
+
# define 'decode_...' method
|
|
564
|
+
idx_bits = table_idx_bits[k]
|
|
565
|
+
table_id_mask = (1 << idx_bits) - 1
|
|
566
|
+
klass.instance_eval do
|
|
567
|
+
define_method("decode_#{k}") do
|
|
568
|
+
val = self[k]
|
|
569
|
+
table_id = val & table_id_mask
|
|
570
|
+
table_key = v[table_id]
|
|
571
|
+
val >>= idx_bits
|
|
572
|
+
[table_key, val]
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
end # module CLR
|
|
580
|
+
|
|
581
|
+
def clr_header f=@io
|
|
582
|
+
return nil unless pe(f) && pe(f).ioh && f
|
|
583
|
+
|
|
584
|
+
dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::CLR_Header]
|
|
585
|
+
return nil if !dir || (dir.va == 0 && dir.size == 0)
|
|
586
|
+
|
|
587
|
+
file_offset = va2file(dir.va)
|
|
588
|
+
return nil unless file_offset
|
|
589
|
+
|
|
590
|
+
if f.checked_seek(file_offset)
|
|
591
|
+
IMAGE_COR20_HEADER.read(f)
|
|
592
|
+
else
|
|
593
|
+
logger.warn "[?] CLR header beyond EOF"
|
|
594
|
+
nil
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def clr_metadata f=@io
|
|
599
|
+
return nil unless hdr = clr_header(f)
|
|
600
|
+
|
|
601
|
+
dir = hdr&.MetaData
|
|
602
|
+
return nil if !dir || (dir.va.to_i == 0 || dir.size.to_i == 0)
|
|
603
|
+
|
|
604
|
+
file_offset = va2file(dir.va)
|
|
605
|
+
return nil unless file_offset
|
|
606
|
+
|
|
607
|
+
if f.checked_seek(file_offset)
|
|
608
|
+
CLR::MetadataHeader.read(f)
|
|
609
|
+
else
|
|
610
|
+
logger.warn "[?] CLR metadata header beyond EOF"
|
|
611
|
+
nil
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def clr_streams f=@io
|
|
616
|
+
return nil unless metadata = clr_metadata(f)
|
|
617
|
+
|
|
618
|
+
streams = []
|
|
619
|
+
metadata.NumberOfStreams.times do
|
|
620
|
+
if stream = CLR::MetadataStreamHeader.read(f)
|
|
621
|
+
streams << stream
|
|
622
|
+
else
|
|
623
|
+
logger.warn "[?] Error reading CLR stream header"
|
|
624
|
+
break
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
streams
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
def clr_strings f=@io
|
|
631
|
+
return nil unless dir = clr_header(f)&.MetaData
|
|
632
|
+
return nil unless streams = clr_streams(f)
|
|
633
|
+
|
|
634
|
+
strings = CLR::StringsHash.new
|
|
635
|
+
streams.each do |stream|
|
|
636
|
+
next unless stream.name == '#Strings'
|
|
637
|
+
|
|
638
|
+
unless f.checked_seek(va2file(dir.va) + stream.offset)
|
|
639
|
+
logger.warn "[?] Error seeking to CLR strings stream"
|
|
640
|
+
return nil
|
|
641
|
+
end
|
|
642
|
+
pos = 0
|
|
643
|
+
while pos < stream.size && !f.eof?
|
|
644
|
+
s = f.gets("\0")
|
|
645
|
+
break unless s
|
|
646
|
+
|
|
647
|
+
ssize = s.bytesize
|
|
648
|
+
s.chomp!("\0")
|
|
649
|
+
s.force_encoding('utf-8')
|
|
650
|
+
strings[pos] = s
|
|
651
|
+
pos += ssize
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
break
|
|
655
|
+
end
|
|
656
|
+
strings
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def clr_tables table_ids_or_f=nil
|
|
660
|
+
f = @io
|
|
661
|
+
table_ids = nil
|
|
662
|
+
|
|
663
|
+
case table_ids_or_f
|
|
664
|
+
when IO
|
|
665
|
+
f = table_ids_or_f
|
|
666
|
+
when String
|
|
667
|
+
table_ids = table_ids_or_f.split(/\W/).map(&:to_sym)
|
|
668
|
+
when Array
|
|
669
|
+
table_ids = table_ids_or_f
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
return nil unless dir = clr_header(f)&.MetaData
|
|
673
|
+
return nil unless streams = clr_streams(f)
|
|
674
|
+
|
|
675
|
+
@dynamic_classes ||= {}
|
|
676
|
+
|
|
677
|
+
tables = CLR::TablesHash.new
|
|
678
|
+
streams.each do |stream|
|
|
679
|
+
next if stream.name != '#~' && stream.name != '#-' # Metadata Table Stream
|
|
680
|
+
|
|
681
|
+
unless f.checked_seek(va2file(dir.va) + stream.offset)
|
|
682
|
+
logger.warn "[?] Error seeking to CLR table stream"
|
|
683
|
+
return nil
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
if hdr = CLR::MetadataTableStreamHeader.read(f)
|
|
687
|
+
hdr.sizes_hash.each do |key, nrows|
|
|
688
|
+
raise "Unknown table: #{key}" unless hdr.known_table?(key)
|
|
689
|
+
|
|
690
|
+
if fields = CLR::TableDefs[key]
|
|
691
|
+
klass = @dynamic_classes[key] ||= CLR::_create_dynamic_class(fields, hdr, name: key)
|
|
692
|
+
tables[key] = [nil] # 1-based index, 0-th element is NULL
|
|
693
|
+
nrows.times do
|
|
694
|
+
tables[key] << klass.read(f)
|
|
695
|
+
end
|
|
696
|
+
else
|
|
697
|
+
logger.warn "[?] Unknown CLR table: #{key}"
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
else
|
|
701
|
+
logger.warn "[?] Error reading CLR table stream header"
|
|
702
|
+
break
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
# tables are layed out sequentially in the file, so ALL of them should be read first, even if only some are requested
|
|
706
|
+
tables.delete_if{ |k,v| !table_ids.include?(k) } if table_ids
|
|
707
|
+
tables
|
|
708
|
+
end
|
|
709
|
+
end
|