This commit is contained in:
Shiz 2020-06-01 19:34:15 +02:00
commit 7a46066a9a
8 changed files with 1337 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
*.pyc

0
dotnet/__init__.py Normal file
View File

17
dotnet/__main__.py Normal file
View File

@ -0,0 +1,17 @@
import sys
from dotnet.types import CLRSignature
from dotnet.tables import CLRTableType
from dotnet.file import CLRFile
file = CLRFile(sys.argv[1])
print(f'Entrypoint: {file.entrypoint.name}')
for cls in file.get_table(CLRTableType.TypeDef):
print('---')
print(f'Class: {cls.name}')
print('Fields:')
for f in cls.fields:
print(f'- {f.name}: {f.signature}')
print('Methods:')
for m in cls.methods:
print(f'- {m.name}: {m.signature}')

229
dotnet/common.py Normal file
View File

@ -0,0 +1,229 @@
import enum
import math
import collections
from destruct import Type, Struct
class CLRStreamType(enum.Enum):
Metadata = '#~'
String = '#Strings'
UserString = '#US'
Blob = '#Blob'
GUID = '#GUID'
class CLRTableType(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 CLRElementType(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 CLRHeapFlags(enum.Flag):
BigStringStream = 1
BigGUIDStream = 2
BigBlobStream = 4
class CLRToken(Struct):
row = UInt(24)
table = Enum(CLRTableType, UInt(8))
class CLRCodedToken(Type):
def __init__(self, types):
self.types = types
def parse(self, input, context):
metadata = context.user.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):
size = 2
else:
size = 4
val = int.from_bytes(input.read(size), 'little')
row = val >> table_bits
tag = val & (1 << (table_bits - 1))
return CLRToken(row=row, table=self.types[tag])
class CLRSectionReference(Struct):
rva = UInt(32)
size = UInt(32)
class CLRTableIndex(Type):
def __init__(self, type):
self.type = type
def parse(self, input, context):
metadata = context.user.metadata
row_count = metadata.row_counts.get(self.type, 0)
if row_count < (1 << 16):
size = 2
else:
size = 4
return int.from_bytes(input.read(size), 'little')
class CLRTableRange(Type):
def __init__(self, type):
self.type = type
def parse(self, input, context):
metadata = context.user.metadata
row_count = metadata.row_counts.get(self.type, 0)
if row_count < (1 << 16):
size = 2
else:
size = 4
return int.from_bytes(input.read(size), 'little')
class CLRStreamIndex(Type):
def __init__(self, type, child=None):
self.type = type
self.child = child
def parse(self, input, context):
metadata = context.user.metadata
big_mapping = {
CLRStreamType.String: CLRHeapFlags.BigStringStream,
CLRStreamType.Blob: CLRHeapFlags.BigBlobStream,
CLRStreamType.GUID: CLRHeapFlags.BigGUIDStream,
}
flag = big_mapping.get(self.type, None)
if flag:
if metadata.heap_flags & flag:
size = 4
else:
size = 2
else:
size = 2
return int.from_bytes(input.read(size), 'little')
class MultiEnumMeta(type):
@classmethod
def __prepare__(mcls, name, bases, **kwargs):
return collections.OrderedDict()
def __new__(cls, name, bases, attrs, **kwargs):
parsers = {}
used = 0
for key, value in attrs.copy().items():
if isinstance(value, tuple):
if len(value) == 2:
child, mask = value
elif len(value) == 3:
child, offset, size = value
mask = ((1 << size) - 1) << offset
if issubclass(child, enum.Enum):
if mask is None:
mask = ~used
parsers[key] = (child, mask)
used |= mask
del attrs[key]
attrs['_parsers_'] = parsers
return super().__new__(cls, name, bases, attrs)
class MultiEnum(metaclass=MultiEnumMeta):
def __init__(self, value: int):
for field, (child, mask) in self._parsers_.items():
setattr(self, field, child(value & mask))
def __str__(self):
return '{}({})'.format(self.__class__.__name__,
', '.join('{}: {}'.format(n, getattr(self, n)) for n in self._parsers_)
)
def __repr__(self):
return '<{}({})>'.format(self.__class__.__name__,
', '.join('{}: {!r}'.format(n, getattr(self, n)) for n in self._parsers_)
)

175
dotnet/file.py Normal file
View File

@ -0,0 +1,175 @@
import pefile
import destruct
from destruct import Type, Struct, Arr
from .common import CLRSectionReference, CLRToken, CLRStreamType, CLRStreamIndex, CLRTableIndex, CLRTableRange
from .streams import STREAM_PARSERS
class CLRHeader(Struct):
size = UInt(32)
version_major = UInt(16)
version_minor = UInt(16)
metadata_info = CLRSectionReference
flags = UInt(32)
entrypoint = CLRToken
unk_info = CLRSectionReference
namesig_info = CLRSectionReference
_gap0x28 = Data(32)
class CLRStreamMetadata(Struct):
offset = UInt(32)
size = UInt(32)
name = AlignTo(Str(kind='c'), 4)
class CLRMetadataHeader(Struct):
magic = Sig(b'BSJB')
version_major = UInt(16)
version_minor = UInt(16)
_gap0x8 = Data(4)
version = Str(kind='pascal', length_type=UInt(32))
_gap0x12 = Data(2)
stream_count = UInt(16)
streams = Arr(CLRStreamMetadata)
def on_stream_count(self, spec, context):
spec.streams.count = self.stream_count
class CLRTableWrapper:
__slots__ = ('__file', '__table', '__index', '__child')
def __init__(self, file, table, index, child):
self.__file = file
self.__table = table
self.__index = index
self.__child = child
def __getattr__(self, name):
val = getattr(self.__child, name)
if isinstance(self.__child, destruct.Struct):
type = self.__child._spec[name]
if isinstance(type, CLRStreamIndex):
stream = type.type
if stream == CLRStreamType.String:
val = self.__file.get_string_at(val)
elif stream == CLRStreamType.Blob:
val = self.__file.get_blob_at(val)
elif stream == CLRStreamType.UserString:
val = self.__file.get_user_string_at(val)
elif stream == CLRStreamType.GUID:
val = self.__file.get_guid_at(val)
if type.child:
c = destruct.Context(type.child)
c.user.metadata = self.__file.streams[CLRStreamType.Metadata]
val = destruct.parse(type.child, val, c)
return val
elif isinstance(type, CLRTableIndex):
table = type.type
return self.__file.get_table_entry(table, val)
elif isinstance(type, CLRTableRange):
table = type.type
if self.__index + 1 < self.__file.get_table_size(self.__table):
next = self.__file.get_table_entry(self.__table, self.__index + 1)
end = min(self.__file.get_table_size(table), getattr(next.__child, name))
else:
end = self.__file.get_table_size(table)
return [self.__file.get_table_entry(table, i) for i in range(val, end)]
return val
def __repr__(self):
return '<{}: {!r} in {!r}>'.format(self.__class__.__name__, self.__child, self.__file)
class CLRFile:
def __init__(self, fn):
self.name = fn
self.pe = pefile.PE(fn)
self.header = None
self.metadata = None
self.streams = {}
self.parse()
def __repr__(self):
return '<{}: "{}">'.format(self.__class__.__name__, self.name)
def parse_at(self, c, offset, size=None):
if size is None:
size = destruct.sizeof(c)
buf = self.pe.get_data(rva=offset, length=size)
return destruct.parse(c, buf)
def parse(self):
net_data_entry = pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR']
if net_data_entry < len(self.pe.OPTIONAL_HEADER.DATA_DIRECTORY):
net_data_dir = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[net_data_entry]
net_offset = net_data_dir.VirtualAddress
net_size = net_data_dir.Size
else:
for section in self.pe.sections:
if section.Name.rstrip(b'\x00') != b'.text':
continue
net_offset = section.PointerToRawData + 8
net_size = 72
self.header = self.parse_at(CLRHeader, net_offset, net_size)
self.metadata = self.parse_at(CLRMetadataHeader, self.header.metadata_info.rva, self.header.metadata_info.size)
self.streams = {}
for s in self.metadata.streams:
type = CLRStreamType(s.name)
if type in STREAM_PARSERS:
stream = self.parse_at(STREAM_PARSERS[type], self.header.metadata_info.rva + s.offset, s.size)
else:
stream = None
self.streams[type] = stream
def get_table(self, t):
return (CLRTableWrapper(self, t, i, x) for i, x in enumerate(self.streams[CLRStreamType.Metadata].tables[t]))
def get_table_size(self, t):
return len(self.streams[CLRStreamType.Metadata].tables[t])
def get_table_entry(self, t, i):
return CLRTableWrapper(self, t, i, self.streams[CLRStreamType.Metadata].tables[t][i])
def get_string_at(self, i):
buf = bytearray()
while True:
c = self.streams[CLRStreamType.String].data[i]
if not c:
break
buf.append(c)
i += 1
return buf.decode('utf-8')
def _get_length_prefixed_value(self, stream, i):
length = self.streams[stream].data[i]
if (length >> 5) == 0b110:
length = length & 0b11111
nbytes = 4
elif (length >> 6) == 0b10:
length = length & 0b111111
nbytes = 2
else:
nbytes = 1
for off in range(nbytes - 1):
length = (length << 8) | self.streams[stream].data[i + 1 + off]
return self.streams[stream].data[i + nbytes:i + nbytes + length]
def get_blob_at(self, i):
return self._get_length_prefixed_value(CLRStreamType.Blob, i)
def get_user_string_at(self, i):
return self._get_length_prefixed_value(CLRStreamType.UserString, i)[:-1].decode('utf-16le')
def get_guid_at(self, i):
if i == 0:
return None
return self.streams[CLRStreamType.GUID].guids[i - 1]
def get_by_token(self, t):
if not t.row:
return None
return self.get_table_entry(t.table, t.row - 1)
@property
def entrypoint(self):
return self.get_by_token(self.header.entrypoint)

93
dotnet/streams.py Normal file
View File

@ -0,0 +1,93 @@
import enum
import uuid
from destruct import Type, Struct, Arr
from .common import CLRStreamType, CLRTableType, CLRHeapFlags
from .tables import TABLE_PARSERS
STREAM_PARSERS = {}
def stream_parser(name):
def inner(c):
STREAM_PARSERS[name] = c
return c
return inner
class BitVector(Type):
ORDER_MAP = {
'be': 'big',
'le': 'little',
}
def __init__(self, size, child=None, order='be'):
self.size = size
self.order = order
self.child = child
def parse(self, input, context):
data = int.from_bytes(input.read(self.size // 8), self.ORDER_MAP[self.order])
values = []
for i in range(self.size):
if data & (1 << i):
values.append(self.child(i) if self.child else i)
return values
def emit(self, output, value, context):
data = 0
for i in values:
data |= (1 << i)
output.write(data.to_bytes(self.size // 8, self.ORDER_MAP[self.order]))
def __repr__(self):
return '<BitVector[{}]>'.format(self.size)
@stream_parser(CLRStreamType.Metadata)
class CLRMetadataStream(Struct):
_gap0 = Data(4)
version_major = UInt(8)
version_minor = UInt(8)
heap_flags = Enum(CLRHeapFlags, UInt(8))
_gap7 = Data(1)
present = BitVector(64, CLRTableType, order='le')
sorted = BitVector(64, CLRTableType, order='le')
row_counts = Arr(UInt(32))
tables = Arr([])
def on_present(self, spec, context):
spec.row_counts.count = len(self.present)
spec.tables.count = len(self.present)
def on_row_counts(self, spec, context):
counts = {}
for p, count in zip(self.present, self.row_counts):
spec.tables.child.append(Arr(TABLE_PARSERS[p], count=count))
counts[p] = count
self.row_counts = counts
context.user.metadata = self
def on_tables(self, spec, context):
tables = {}
for p, table in zip(self.present, self.tables):
tables[p] = table
self.tables = tables
class GUID(Type):
def parse(self, input, context):
data = input.read(16)
return uuid.UUID(bytes=data)
def emit(self, value, output, context):
output.write(value.bytes)
def sizeof(self, value, context):
return 16
@stream_parser(CLRStreamType.GUID)
class CLRGUIDStream(Struct):
guids = Arr(GUID)
@stream_parser(CLRStreamType.String)
@stream_parser(CLRStreamType.UserString)
@stream_parser(CLRStreamType.Blob)
class CLRDataStream(Struct):
data = Data(None)

568
dotnet/tables.py Normal file
View File

@ -0,0 +1,568 @@
import enum
from destruct import Struct
from .common import (
MultiEnum,
CLRElementType, CLRStreamType, CLRTableType,
CLRCodedToken, CLRStreamIndex, CLRTableIndex, CLRTableRange
)
from .types import CLRSignature
TABLE_PARSERS = {}
def table_parser(name):
def inner(c):
TABLE_PARSERS[name] = c
return c
return inner
@table_parser(CLRTableType.Module)
class CLRModuleTable(Struct):
generation = UInt(16)
name = CLRStreamIndex(CLRStreamType.String)
mvid = CLRStreamIndex(CLRStreamType.GUID)
encid = CLRStreamIndex(CLRStreamType.GUID)
encbaseid = CLRStreamIndex(CLRStreamType.GUID)
ResolutionScope = CLRCodedToken([
CLRTableType.Module, CLRTableType.ModuleRef,
CLRTableType.AssemblyRef, CLRTableType.TypeRef
])
@table_parser(CLRTableType.TypeRef)
class CLRTypeRefTable(Struct):
scope = ResolutionScope
name = CLRStreamIndex(CLRStreamType.String)
namespace = CLRStreamIndex(CLRStreamType.String)
TypeDefOrRef = CLRCodedToken([
CLRTableType.TypeDef, CLRTableType.TypeRef, CLRTableType.TypeSpec
])
class CLRTypeVisiblity(enum.Enum):
NotPublic = 0x0
Public = 0x1
NestedPublic = 0x2
NestedPrivate = 0x3
NestedFamily = 0x4
NestedAssembly = 0x5
NestedFamANDAssem = 0x6
NestedFamORAssem = 0x7
class CLRTypeLayout(enum.Enum):
AutoLayout = 0x0
SequentialLayout = 0x8
ExplicitLayout = 0x10
class CLRTypeClassSemantics(enum.Enum):
Class = 0x0
Interface = 0x20
class CLRTypeStringFormatting(enum.IntFlag):
ANSI = 0
Unicode = 0x10000
Custom = 0x30000
class CLRTypeFlags(enum.Flag):
Abstract = 0x80
Sealed = 0x100
SpecialName = 0x400
RTSpecialName = 0x800
Import = 0x1000
Serializable = 0x2000
HasSecurity = 0x4000
BeforeFieldInit = 0x100000
IsTypeForwarder = 0x200000
class CLRTypeAttributes(MultiEnum):
visibility = (CLRTypeVisiblity, 0x7)
layout = (CLRTypeLayout, 0x18)
semantics = (CLRTypeClassSemantics, 0x20)
formatting = (CLRTypeStringFormatting, 0xC30000)
flags = (CLRTypeFlags, None)
@table_parser(CLRTableType.TypeDef)
class CLRTypeDefTable(Struct):
flags = Enum(CLRTypeAttributes, UInt(32))
name = CLRStreamIndex(CLRStreamType.String)
namespace = CLRStreamIndex(CLRStreamType.String)
extends = TypeDefOrRef
fields = CLRTableRange(CLRTableType.Field)
methods = CLRTableRange(CLRTableType.MethodDef)
@table_parser(CLRTableType.FieldPointer)
def CLRFieldPointerTable(Struct):
field = CLRTableIndex(CLRTableType.Field)
class CLRAccess(enum.Enum):
CompilerControlled = 0
Private = 1
FamANDAssem = 2
Assembly = 3
Family = 4
FamORAssem = 5
Public = 6
class CLRFieldFlags(enum.Flag):
Static = 0x10
InitOnly = 0x20
Literal = 0x40
NotSerialized = 0x80
HasFieldRVA = 0x100
SpecialName = 0x200
RTSpecialName = 0x400
HasFieldMarshal = 0x1000
PInvokeImpl = 0x2000
HasDefault = 0x8000
class CLRFieldAttributes(MultiEnum):
access = (CLRAccess, 0x7)
flags = (CLRFieldFlags, 0xFFF0)
@table_parser(CLRTableType.Field)
class CLRFieldTable(Struct):
flags = Enum(CLRFieldAttributes, UInt(16))
name = CLRStreamIndex(CLRStreamType.String)
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
@table_parser(CLRTableType.MethodPointer)
def CLRMethodPointerTable(Struct):
method = CLRTableIndex(CLRTableType.Method)
class CLRMemberVtableLayout(enum.Enum):
ReuseSlot = 0
NewSlot = 0x100
class CLRMethodFlags(enum.Flag):
Static = 0x10
Final = 0x20
Virtual = 0x40
HideBySig = 0x80
Strict = 0x200
Abstract = 0x400
SpecialName = 0x800
RTSpecialName = 0x1000
PInvokeImpl = 0x2000
HasSecurity = 0x4000
RequireSecObject = 0x8000
class CLRMethodAttributes(MultiEnum):
access = (CLRAccess, 0x7)
vtable = (CLRMemberVtableLayout, 0x100)
flags = (CLRMethodFlags, 0xFEF0)
class CLRMethodCodeType(enum.Enum):
IL = 0
Native = 1
OPTIL = 2
Runtime = 3
class CLRMethodManaged(enum.Enum):
Managed = 0
Unmanaged = 4
class CLRMethodImplFlags(enum.Flag):
NoInlining = 0x8
ForwardRef = 0x10
Synchronized = 0x20
NoOptimization = 0x40
PreserveSig = 0x80
AggressiveInlining = 0x100
InternalCall = 0x1000
class CLRMethodImplAttributes(MultiEnum):
code_type = (CLRMethodCodeType, 0x3)
managed = (CLRMethodManaged, 0x4)
flags = (CLRMethodImplFlags, 0xFFF8)
@table_parser(CLRTableType.MethodDef)
class CLRMethodDefTable(Struct):
rva = UInt(32)
impl_flags = Enum(CLRMethodImplAttributes, UInt(16))
flags = Enum(CLRMethodAttributes, UInt(16))
name = CLRStreamIndex(CLRStreamType.String)
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
params = CLRTableRange(CLRTableType.Param)
@table_parser(CLRTableType.ParamPointer)
def CLRParamPointerTable(Struct):
param = CLRTableIndex(CLRTableType.Param)
class CLRParamAttributes(enum.Flag):
In = 0x1
Out = 0x2
Optional = 0x10
HasDefault = 0x1000
HasFieldMarshal = 0x2000
@table_parser(CLRTableType.Param)
class CLRParamTable(Struct):
flags = Enum(CLRParamAttributes, UInt(16))
sequence = UInt(16)
name = CLRStreamIndex(CLRStreamType.String)
MemberRefParent = CLRCodedToken([
CLRTableType.MethodDef, CLRTableType.ModuleRef,
CLRTableType.TypeDef, CLRTableType.TypeRef, CLRTableType.TypeSpec
])
@table_parser(CLRTableType.MemberRef)
class CLRMemberRefTable(Struct):
parent = MemberRefParent
name = CLRStreamIndex(CLRStreamType.String)
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
HasCustomAttribute = CLRCodedToken([
CLRTableType.MethodDef, CLRTableType.Field,
CLRTableType.TypeDef, CLRTableType.TypeRef,
CLRTableType.Param, CLRTableType.InterfaceImpl,
CLRTableType.MemberRef, CLRTableType.Module, 'Permission',
CLRTableType.Property, CLRTableType.Event,
CLRTableType.StandAloneSig, CLRTableType.ModuleRef, CLRTableType.TypeSpec,
CLRTableType.Assembly, CLRTableType.AssemblyRef,
CLRTableType.File, CLRTableType.ExportedType, CLRTableType.ManifestResource,
CLRTableType.GenericParam, CLRTableType.GenericParamConstraint,
CLRTableType.MethodSpec
])
CustomAttributeType = CLRCodedToken([
None, None, CLRTableType.MethodDef, CLRTableType.MemberRef, None
])
@table_parser(CLRTableType.CustomAttribute)
class CLRCustomAttributeTable(Struct):
parent = HasCustomAttribute
type = CustomAttributeType
value = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.StandAloneSig)
class CLRStandAloneSigTable(Struct):
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
@table_parser(CLRTableType.TypeSpec)
class CLRTypeSpecTable(Struct):
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
class AssemblyHashAlgorithm(enum.Enum):
Null = 0
MD5 = 0x8003
SHA1 = 0x8004
class AssemblyFlags(enum.Flag):
PublicKey = 0x1
Retargetable = 0x100
DisableJITOptimizing = 0x4000
EnableJITracking = 0x8000
@table_parser(CLRTableType.Assembly)
class CLRAssemblyTable(Struct):
hash_algo = Enum(AssemblyHashAlgorithm, UInt(32))
version_major = UInt(16)
version_minor = UInt(16)
build_number = UInt(16)
rev_number = UInt(16)
flags = Enum(AssemblyFlags, UInt(32))
public_key = CLRStreamIndex(CLRStreamType.Blob)
name = CLRStreamIndex(CLRStreamType.String)
culture = CLRStreamIndex(CLRStreamType.String)
@table_parser(CLRTableType.AssemblyCPU)
class CLRAssemblyCPUTable(Struct):
processor = UInt(32)
@table_parser(CLRTableType.AssemblyOS)
class CLRAssemblyOSTable(Struct):
platform_id = UInt(32)
version_major = UInt(32)
version_minor = UInt(32)
@table_parser(CLRTableType.AssemblyRef)
class CLRAssemblyRefTable(Struct):
version_major = UInt(16)
version_minor = UInt(16)
build_number = UInt(16)
rev_number = UInt(16)
flags = Enum(AssemblyFlags, UInt(32))
public_key = CLRStreamIndex(CLRStreamType.Blob)
name = CLRStreamIndex(CLRStreamType.String)
culture = CLRStreamIndex(CLRStreamType.String)
hash_value = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.AssemblyRefCPU)
class CLRAssemblyRefCPUTable(Struct):
processor = UInt(32)
assembly = CLRTableIndex(CLRTableType.Assembly)
@table_parser(CLRTableType.AssemblyRefOS)
class CLRAssemblyRefOSTable(Struct):
platform_id = UInt(32)
version_major = UInt(32)
version_minor = UInt(32)
assembly = CLRTableIndex(CLRTableType.Assembly)
Implementation = CLRCodedToken([
CLRTableType.File, CLRTableType.AssemblyRef, CLRTableType.ExportedType
])
class ManifestResourceAttributes(enum.Enum):
Public = 1
Private = 2
@table_parser(CLRTableType.ManifestResource)
class CLRManifestResourceTable(Struct):
offset = UInt(32)
flags = Enum(ManifestResourceAttributes, UInt(32))
name = CLRStreamIndex(CLRStreamType.String)
implementation = Implementation
@table_parser(CLRTableType.NestedClass)
class CLRNestedClassTable(Struct):
nested = CLRTableIndex(CLRTableType.TypeDef)
enclosing = CLRTableIndex(CLRTableType.TypeDef)
@table_parser(CLRTableType.InterfaceImpl)
class CLRInterfaceImplTable(Struct):
type = CLRTableIndex(CLRTableType.TypeDef)
interface = TypeDefOrRef
HasConstant = CLRCodedToken([
CLRTableType.Param, CLRTableType.Field, CLRTableType.Property
])
@table_parser(CLRTableType.Constant)
class CLRConstantTable(Struct):
type = UInt(8)
_pad1 = UInt(8)
parent = HasConstant
value = CLRStreamIndex(CLRStreamType.Blob)
HasDeclSecurity = CLRCodedToken([
CLRTableType.TypeDef, CLRTableType.MethodDef, CLRTableType.Assembly
])
class CLRSecurityAction(enum.Enum):
Demand = 2
Assert = 3
Deny = 4
PermitOnly = 5
LinkDemand = 6
InheritanceDemand = 7
RequestMinimum = 8
RequestOptional = 9
RequestRefuse = 10
@table_parser(CLRTableType.DeclSecurity)
class CLRDeclSecurityTable(Struct):
action = Enum(CLRSecurityAction, UInt(16))
parent = HasDeclSecurity
permission_set = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.ClassLayout)
class CLRClassLayoutTable(Struct):
packing_size = UInt(16)
class_size = UInt(32)
parent = CLRTableIndex(CLRTableType.TypeDef)
@table_parser(CLRTableType.FieldLayout)
class CLRFieldLayoutTable(Struct):
offset = UInt(32)
field = CLRTableIndex(CLRTableType.Field)
@table_parser(CLRTableType.EventMap)
class CLREventMapTable(Struct):
parent = CLRTableIndex(CLRTableType.TypeDef)
events = CLRTableRange(CLRTableType.Event)
@table_parser(CLRTableType.EventPointer)
class CLREventPointerTable(Struct):
event = CLRTableIndex(CLRTableType.Event)
class CLREventAttributes(enum.Flag):
SpecialName = 0x200
RTSpecialName = 0x400
@table_parser(CLRTableType.Event)
class CLREventTable(Struct):
flags = Enum(CLREventAttributes, UInt(16))
name = CLRStreamIndex(CLRStreamType.String)
type = TypeDefOrRef
@table_parser(CLRTableType.PropertyMap)
class CLRPropertyMapTable(Struct):
parent = CLRTableIndex(CLRTableType.TypeDef)
properties = CLRTableRange(CLRTableType.Property)
@table_parser(CLRTableType.PropertyPointer)
class CLRPropertyPointerTable(Struct):
property = CLRTableIndex(CLRTableType.Property)
class CLRPropertyAttributes(enum.Flag):
SpecialName = 0x0200
RTSpecialName = 0x0400
HasDefault = 0x1000
@table_parser(CLRTableType.Property)
class CLRPropertyTable(Struct):
flags = Enum(CLRPropertyAttributes, UInt(16))
name = CLRStreamIndex(CLRStreamType.String)
signature = CLRStreamIndex(CLRStreamType.Blob, CLRSignature)
HasSemantics = CLRCodedToken([CLRTableType.Event, CLRTableType.Property])
class CLRMethodSemanticsAttributes(enum.Enum):
Setter = 0x1
Getter = 0x2
Other = 0x4
AddOn = 0x8
RemoveOn = 0x10
Fire = 0x20
@table_parser(CLRTableType.MethodSemantics)
class CLRMethodSemanticsTable(Struct):
semantics = Enum(CLRMethodSemanticsAttributes, UInt(16))
method = CLRTableIndex(CLRTableType.MethodDef)
association = HasSemantics
MethodDefOrRef = CLRCodedToken([
CLRTableType.MethodDef, CLRTableType.MemberRef
])
@table_parser(CLRTableType.MethodImpl)
class CLRMethodImplTable(Struct):
parent = CLRTableIndex(CLRTableType.TypeDef)
body = MethodDefOrRef
declaration = MethodDefOrRef
@table_parser(CLRTableType.ModuleRef)
class CLRModuleRefTable(Struct):
name = CLRStreamIndex(CLRStreamType.String)
MemberForwarded = CLRCodedToken([CLRTableType.Field, CLRTableType.MethodDef])
class CLRVariance(enum.Enum):
No = 0
Covariant = 1
Contravariant = 2
class CLRConstraint(enum.Flag):
ReferenceTypeConstraint = 0x4
NotNullableValueTypeConstraint = 0x8
DefaultConstructorConstraint = 0x10
class CLRPInvokeAttributes(MultiEnum):
variance = (CLRVariance, 0b11)
constraint = (CLRConstraint, 0b11100)
@table_parser(CLRTableType.ImplMap)
class CLRImplMapTable(Struct):
flags = Enum(CLRPInvokeAttributes, UInt(16))
forwarded = MemberForwarded
name = CLRStreamIndex(CLRStreamType.String)
scope = CLRTableIndex(CLRTableType.ModuleRef)
@table_parser(CLRTableType.FieldRVA)
class CLRFieldRVATable(Struct):
rva = UInt(32)
field = CLRTableIndex(CLRTableType.Field)
TypeOrMethodDef = CLRCodedToken([CLRTableType.TypeDef, CLRTableType.MethodDef])
class CLRGenericParamAttributes(MultiEnum):
variance = (CLRVariance, 0b11)
constraint = (CLRConstraint, 0b11100)
@table_parser(CLRTableType.GenericParam)
class CLRGenericParamTable(Struct):
index = UInt(16)
flags = Enum(CLRGenericParamAttributes, UInt(16))
owner = TypeOrMethodDef
name = CLRStreamIndex(CLRStreamType.String)
@table_parser(CLRTableType.MethodSpec)
class CLRMethodSpecTable(Struct):
parent = MethodDefOrRef
instantiation = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.GenericParamConstraint)
class CLRGenericParamConstraintTable(Struct):
owner = CLRTableIndex(CLRTableType.GenericParam)
constraint = TypeDefOrRef
HasFieldMarshal = CLRCodedToken([CLRTableType.Field, CLRTableType.Param])
@table_parser(CLRTableType.FieldMarshal)
class CLRFieldMarshalTable(Struct):
parent = HasFieldMarshal
native_type = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.EncLog)
class CLREncLogTable(Struct):
token = UInt(32)
func_code = UInt(32)
@table_parser(CLRTableType.EncMap)
class CLREncMapTable(Struct):
token = UInt(32)
class CLRFileAttributes(enum.Enum):
ContainsMetadata = 0
ContainsNoMetadata = 1
@table_parser(CLRTableType.File)
class CLRFileTable(Struct):
flags = Enum(CLRFileAttributes, UInt(32))
name = CLRStreamIndex(CLRStreamType.String)
hash = CLRStreamIndex(CLRStreamType.Blob)
@table_parser(CLRTableType.ExportedType)
class CLRExportedTypeTable(Struct):
flags = Enum(CLRTypeAttributes, UInt(32))
type_id = UInt(32)
name = CLRStreamIndex(CLRStreamType.String)
namespace = CLRStreamIndex(CLRStreamType.String)
implementation = Implementation

253
dotnet/types.py Normal file
View File

@ -0,0 +1,253 @@
import enum
from itertools import zip_longest
from destruct import parse, Type, Struct
from .common import CLRCodedToken, CLRTableType, CLRElementType, MultiEnum
class CLRType:
pass
class CLRPrimitiveType(CLRType):
def __init__(self, type: CLRElementType):
self.type = type
def __str__(self):
return self.type.name.lower()
class CLRPointerType(CLRType):
def __init__(self, child: CLRType):
self.child = child
def __str__(self):
return str(self.child) + '*'
class CLRRefType(CLRType):
def __init__(self, child: CLRType):
self.child = child
def __str__(self):
return str(self.child) + '&'
class CLRPinnedType(CLRType):
def __init__(self, child: CLRType):
self.child = child
def __str__(self):
return str(self.child) + ' fixed&'
class CLRUserTypeKind(enum.Enum):
Class = 'class'
ValueType = 'enum'
class CLRUserType(CLRType):
def __init__(self, token, kind):
self.token = token
self.kind = kind
def __str__(self):
if self.token.table == CLRTableType.TypeRef:
prefix = 'ext-'
else:
prefix = ''
return '{}{}#{}'.format(prefix, self.kind.value, self.token.row)
class CLROptionalType(CLRType):
def __init__(self, child):
self.child = child
def __str__(self):
return str(self.child) + '?'
class CLRRequiredType(CLRType):
def __init__(self, child):
self.child = child
def __str__(self):
return str(self.child) + '!'
class CLRGenericParamScope(enum.Enum):
Type = 'type'
Method = 'method'
class CLRGenericParamType(CLRType):
def __init__(self, index, scope):
self.index = index
self.scope = scope
def __str__(self):
return '#' + str(self.index)
class CLRGenericInstantiationType(CLRType):
def __init__(self, child, args):
self.child = child
self.args = args
def __str__(self):
return str(self.child) + '[' + ', '.join(str(a) for a in self.args) + ']'
class CLRArrayType(CLRType):
def __init__(self, child, rank, sizes=None, lowers=None):
self.child = child
self.rank = rank
self.sizes = sizes or []
self.lowers = lowers or []
def __str__(self):
return str(self.child) + ''.join(
'[{}{}]'.format(str(lower) + '...' if lower else '', upper + (lower or 0) if upper else '')
for _, lower, upper in zip_longest(range(self.rank), self.lowers, self.sizes)
)
class CLRElement(Type):
def __init__(self, child):
self.child = child
def parse(self, input, context):
nested_types = {
CLRElementType.Pointer: CLRPointerType,
CLRElementType.ByRef: CLRRefType,
CLRElementType.Pinned: CLRPinnedType,
}
token_types = {
CLRElementType.Class: lambda t: CLRUserType(t, CLRUserTypeKind.Class),
CLRElementType.ValueType: lambda t: CLRUserType(t, CLRUserTypeKind.ValueType),
CLRElementType.OptModifier: CLROptionalType,
CLRElementType.ReqModifier: CLRRequiredType,
}
int_types = {
CLRElementType.Var: lambda i: CLRGenericParamType(i, CLRGenericParamScope.Type),
CLRElementType.MVar: lambda i: CLRGenericParamType(i, CLRGenericParamScope.Method),
}
val = CLRElementType(parse(self.child, input, context))
if val in nested_types:
with context.enter(val.name, self):
val = nested_types[val](self.parse(input, context))
elif val in token_types:
with context.enter(val.name, self.child):
raw = parse(self.child, input, context)
token = parse(CLRCodedToken([CLRTableType.TypeDef, CLRTableType.TypeRef]), raw.to_bytes(4, 'little'), context)
val = token_types[val](token)
elif val in int_types:
with context.enter(val.name, self.child):
val = int_types[val](parse(self.child, input, context))
elif val == CLRElementType.Array:
with context.enter(val.name, self):
type = self.parse(input, context)
rank = parse(self.child, input, context)
nbounds = parse(self.child, input, context)
bounds = [parse(self.child, input, context) for _ in range(nbounds)]
nlo = parse(self.child, input, context)
lo = [parse(self.child, input, context) for _ in range(nlo)]
val = CLRArrayType(type, rank, bounds, lo)
elif val == CLRElementType.GenericInst:
with context.enter(val.name, self):
type = self.parse(input, context)
nargs = parse(self.child, input, context)
args = []
for i in range(nargs):
with context.enter(i, self):
args.append(self.parse(input, context))
val = CLRGenericInstantiationType(type, args)
else:
val = CLRPrimitiveType(val)
return val
class CLRCompressedInt(Type):
def __init__(self, signed=True):
self.signed = signed
def parse(self, input, context):
val = input.read(1)[0]
if (val >> 5) == 0b110:
nbytes = 4
b = bytearray([val & 0b11111])
elif (val >> 6) == 0b10:
nbytes = 2
b = bytearray([val & 0b111111])
else:
nbytes = 1
b = bytearray([val])
b.extend(input.read(nbytes - 1))
return int.from_bytes(b, byteorder='big', signed=self.signed)
class CLRCompressedUInt(Type):
def __new__(self):
return CLRCompressedInt(signed=False)
class CLRSignatureType(enum.Enum):
Default = 0x0
C = 0x1
StdCall = 0x2
ThisCall = 0x3
FastCall = 0x4
VarArg = 0x5
Field = 0x6
LocalVar = 0x7
Property = 0x8
class CLRSignatureFlags(enum.Flag):
Generic = 0x10
HasThis = 0x20
ExplicitThis = 0x40
class CLRSignatureAttributes(MultiEnum):
type = (CLRSignatureType, 0x0F)
flags = (CLRSignatureFlags, 0xF0)
class CLRMethodSignature(Struct):
param_count = CLRCompressedUInt()
ret_type = CLRElement(CLRCompressedUInt())
params = Arr(CLRElement(CLRCompressedUInt()))
def on_param_count(self, spec, context):
spec.params.count = self.param_count
def __str__(self):
return '(' + ', '.join(str(p) for p in self.params) + ') -> ' + str(self.ret_type)
class CLRFieldSignature(Struct):
type = CLRElement(CLRCompressedUInt())
def __str__(self):
return str(self.type)
class CLRPropertySignature(Struct):
param_count = CLRCompressedUInt()
type = CLRElement(CLRCompressedUInt())
params = Arr(CLRElement(CLRCompressedUInt()))
def on_param_count(self, spec, context):
spec.params.count = self.param_count
def __str__(self):
return '(' + ', '.join(str(p) for p in self.params) + ') -> ' + str(self.ret_type)
class CLRLocalVarSignature(Struct):
count = CLRCompressedUInt()
vars = Arr(CLRElement(CLRCompressedUInt()))
def on_count(self, spec, context):
spec.vars.count = self.count
class CLRSignature(Struct):
attribs = Enum(CLRSignatureAttributes, UInt(8))
signature = Switch(options={
CLRSignatureType.Default: CLRMethodSignature,
CLRSignatureType.C: CLRMethodSignature,
CLRSignatureType.StdCall: CLRMethodSignature,
CLRSignatureType.ThisCall: CLRMethodSignature,
CLRSignatureType.FastCall: CLRMethodSignature,
CLRSignatureType.VarArg: CLRMethodSignature,
CLRSignatureType.Field: CLRFieldSignature,
CLRSignatureType.Property: CLRPropertySignature,
CLRSignatureType.LocalVar: CLRLocalVarSignature,
})
def on_attribs(self, spec, context):
spec.signature.selector = self.attribs.type
def __str__(self):
return str(self.signature)