notded/dotnet/common.py

218 lines
6.1 KiB
Python

import enum
import math
from destruct import Type, Struct
class CLIStreamType(enum.Enum):
Metadata = '#~'
String = '#Strings'
UserString = '#US'
Blob = '#Blob'
GUID = '#GUID'
class CLITableType(enum.Enum):
Assembly = 0x20
AssemblyCPU = 0x21
AssemblyOS = 0x22
AssemblyRef = 0x23
AssemblyRefCPU = 0x24
AssemblyRefOS = 0x25
ClassLayout = 0x0F
Constant = 0x0B
CustomAttribute = 0x0C
DeclSecurity = 0x0E
EncLog = 0x1E
EncMap = 0x1F
EventMap = 0x12
Event = 0x14
EventPointer = 0x13
ExportedType = 0x27
Field = 0x04
FieldLayout = 0x10
FieldMarshal = 0x0D
FieldPointer = 0x03
FieldRVA = 0x1D
File = 0x26
GenericParam = 0x2A
GenericParamConstraint = 0x2C
ImplMap = 0x1C
InterfaceImpl = 0x09
ManifestResource = 0x28
MemberRef = 0x0A
MethodDef = 0x06
MethodImpl = 0x19
MethodPointer = 0x05
MethodSemantics = 0x18
MethodSpec = 0x2B
Module = 0x00
ModuleRef = 0x1A
NestedClass = 0x29
Param = 0x08
ParamPointer = 0x07
Property = 0x17
PropertyMap = 0x15
PropertyPointer = 0x16
StandAloneSig = 0x11
TypeDef = 0x02
TypeRef = 0x01
TypeSpec = 0x1B
Document = 0x30
MethodBody = 0x31
LocalScope = 0x32
LocalVariable = 0x33
LocalConstant = 0x34
ImportScope = 0x35
StateMachineMethod = 0x36
CustomDebugInformation = 0x37
class CLIElementType(enum.Enum):
End = 0x00
Void = 0x01
Boolean = 0x02
Char = 0x03
I1 = 0x04
U1 = 0x05
I2 = 0x06
U2 = 0x07
I4 = 0x08
U4 = 0x09
I8 = 0x0A
U8 = 0x0B
R4 = 0x0C
R8 = 0x0D
String = 0x0E
Pointer = 0x0F
ByRef = 0x10
ValueType = 0x11
Class = 0x12
Var = 0x13
Array = 0x14
GenericInst = 0x15
TypedByRef = 0x16
I = 0x18
U = 0x19
FnPtr = 0x1B
Object = 0x1C
SZArray = 0x1D
MVar = 0x1E
ReqModifier = 0x1F
OptModifier = 0x20
Internal = 0x21
Modifier = 0x40
Sentinel = 0x41
Pinned = 0x45
System = 0x50
Boxed = 0x51
Field = 0x53
Property = 0x54
Enum = 0x55
class CLIHeapFlags(enum.Flag):
BigStringStream = 1
BigGUIDStream = 2
BigBlobStream = 4
class CLIToken(Struct, nocopy=True):
row = UInt(24)
table = Enum(CLITableType, UInt(8))
class CLICodedToken(Type):
def __init__(self, types):
self.types = types
def _get_size(self, metadata):
row_counts = []
for t in self.types:
row_counts.append(metadata.row_counts.get(t, 0))
max_rows = max(row_counts)
table_bits = math.ceil(math.log(len(self.types), 2))
if max_rows < (1 << 16 - table_bits):
return table_bits, 2
else:
return table_bits, 4
def parse(self, input, context):
metadata = context.user.metadata
table_bits, size = self._get_size(metadata)
val = int.from_bytes(input.read(size), 'little')
row = val >> table_bits
tag = val & ((1 << table_bits) - 1)
return CLIToken(row=row, table=self.types[tag])
def sizeof(self, value, context):
metadata = context.user.metadata
_, size = self._get_size(metadata)
return size
class CLISectionReference(Struct, nocopy=True):
rva = UInt(32)
size = UInt(32)
class CLITableIndex(Type):
def __init__(self, type):
self.type = type
def _get_size(self, metadata):
row_count = metadata.row_counts.get(self.type, 0)
if row_count < (1 << 16):
return 2
else:
return 4
def parse(self, input, context):
metadata = context.user.metadata
size = self._get_size(metadata)
return int.from_bytes(input.read(size), 'little')
def sizeof(self, input, context):
metadata = context.user.metadata
return self._get_size(metadata)
class CLITableRange(Type):
def __init__(self, type):
self.type = type
def _get_size(self, metadata):
row_count = metadata.row_counts.get(self.type, 0)
if row_count < (1 << 16):
return 2
else:
return 4
def parse(self, input, context):
metadata = context.user.metadata
size = self._get_size(metadata)
return int.from_bytes(input.read(size), 'little')
def sizeof(self, input, context):
metadata = context.user.metadata
return self._get_size(metadata)
class CLIStreamIndex(Type):
def __init__(self, type, child=None):
self.type = type
self.child = child
def _get_size(self, metadata):
big_mapping = {
CLIStreamType.String: CLIHeapFlags.BigStringStream,
CLIStreamType.Blob: CLIHeapFlags.BigBlobStream,
CLIStreamType.GUID: CLIHeapFlags.BigGUIDStream,
}
flag = big_mapping.get(self.type, None)
if not flag:
return 2
elif metadata.heap_flags & flag:
return 4
else:
return 2
def parse(self, input, context):
metadata = context.user.metadata
size = self._get_size(metadata)
return int.from_bytes(input.read(size), 'little')
def sizeof(self, input, context):
metadata = context.user.metadata
return self._get_size(metadata)