add constant, layout, implements, properties and marshal field wrappers and Assembly table wrapper

This commit is contained in:
Shiz 2020-06-02 04:28:31 +02:00
parent ce9a1aefa2
commit c823b0fc41
4 changed files with 183 additions and 29 deletions

View File

@ -142,6 +142,17 @@ class CLIFile:
def entrypoint(self):
return self.get_by_token(self.header.entrypoint)
@property
def assembly(self):
try:
return self.get_table_entry(CLITableType.Assembly, 1)
except IndexError:
return None
@property
def referenced_assemblies(self):
return self.get_table(CLITableType.AssemblyRef)
@property
def classes(self):
return (

View File

@ -6,7 +6,7 @@ from .common import (
CLIElementType, CLIStreamType, CLITableType,
CLICodedToken, CLIStreamIndex, CLITableIndex, CLITableRange
)
from .types import CLISignature
from .types import CLIElementType, CLISignature, CLINativeSignature
TABLE_PARSERS = {}
@ -240,11 +240,20 @@ CustomAttributeType = CLICodedToken([
None, None, CLITableType.MethodDef, CLITableType.MemberRef, None
])
class CLICustomAttribute(Struct, nocopy=True):
prolog = Sig(b'\x01\x00')
fixed = Arr([])
named_count = UInt(16)
named = Arr([])
def on_named_count(self, spec, context):
spec.named.count = self.named_count
@table_parser(CLITableType.CustomAttribute)
class CLICustomAttributeTable(Struct, nocopy=True):
parent = HasCustomAttribute
type = CustomAttributeType
value = CLIStreamIndex(CLIStreamType.Blob)
value = CLIStreamIndex(CLIStreamType.Blob, CLICustomAttribute)
@table_parser(CLITableType.StandAloneSig)
@ -354,7 +363,7 @@ HasConstant = CLICodedToken([
@table_parser(CLITableType.Constant)
class CLIConstantTable(Struct, nocopy=True):
type = UInt(8)
type = Enum(CLIElementType, UInt(8))
_pad1 = UInt(8)
parent = HasConstant
value = CLIStreamIndex(CLIStreamType.Blob)
@ -534,7 +543,7 @@ HasFieldMarshal = CLICodedToken([CLITableType.Field, CLITableType.Param])
@table_parser(CLITableType.FieldMarshal)
class CLIFieldMarshalTable(Struct, nocopy=True):
parent = HasFieldMarshal
native_type = CLIStreamIndex(CLIStreamType.Blob)
native_type = CLIStreamIndex(CLIStreamType.Blob, CLINativeSignature)
@table_parser(CLITableType.EncLog)

View File

@ -6,6 +6,88 @@ from .util import MultiEnum
from .common import CLICodedToken, CLITableType, CLIElementType
class CLICompressedInt(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 CLICompressedUInt(Type):
def __new__(self):
return CLICompressedInt(signed=False)
class CLINativeElementType(enum.Enum):
Boolean = 0x2
I1 = 0x3
U1 = 0x4
I2 = 0x5
U2 = 0x6
I4 = 0x7
U4 = 0x8
I8 = 0x9
U8 = 0xA
R4 = 0xB
R8 = 0xC
LPStr = 0x14
LPWStr = 0x15
Int = 0x1F
UInt = 0x20
Function = 0x26
Array = 0x2A
class CLINativeType:
pass
class CLINativeIntrinsicType(CLINativeType):
def __init__(self, type: CLIElementType):
self.type = type
def __str__(self):
return self.type.name.lower()
def __repr__(self):
return '<{}: {}>'.format(self.__class__.__name__, self.type.name)
class CLINativeArrayType(CLINativeType):
def __init__(self, type, count, count_param):
self.type = type
self.count = count
self.count_param = count_param
def __str__(self):
return str(self.type) + '[{}]'.format(self.count if self.count is not None else '')
def __repr__(self):
return '<{}: {}[{}] @ {}>'.format(
self.__class__.__name__, self.type.name,
self.count if self.count is not None else '', self.count_param
)
class CLINativeSignature(Struct, nocopy=True):
type = Enum(CLINativeElementType, CLICompressedUInt())
elem_type = Maybe(Enum(CLINativeElementType, CLICompressedUInt()))
count_param = Maybe(CLICompressedUInt())
count = Maybe(CLICompressedUInt())
def parse(self, input, context):
val = super().parse(input, context)
if val.type == CLINativeElementType.Array:
return CLINativeArrayType(val.elem_type, val.count, val.count_param)
return CLINativeIntrinsicType(val.type)
class CLIType:
pass
@ -18,7 +100,7 @@ class CLIPrimitiveType(CLIType):
return self.type.name.lower()
def __repr__(self):
return '<{}: {}>'.format(self.__class__.__name__, self.type.value)
return '<{}: {}>'.format(self.__class__.__name__, self.type.name)
class CLIPointerType(CLIType):
def __init__(self, child: CLIType):
@ -189,28 +271,6 @@ class CLIElement(Type):
return val
class CLICompressedInt(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 CLICompressedUInt(Type):
def __new__(self):
return CLICompressedInt(signed=False)
class CLISignatureType(enum.Enum):
Default = 0x0
C = 0x1
@ -257,7 +317,7 @@ class CLIPropertySignature(Struct, nocopy=True):
spec.params.count = self.param_count
def __str__(self):
return '(' + ', '.join(str(p) for p in self.params) + ') -> ' + str(self.ret_type)
return '(' + ', '.join(str(p) for p in self.params) + ') -> ' + str(self.type)
class CLILocalVarSignature(Struct, nocopy=True):
count = CLICompressedUInt()

View File

@ -132,8 +132,34 @@ class CLITypeRef(CLITableWrapper):
namespace = self.namespace
return '<{}: {}{} (from {})>'.format(self.__class__.__name__, namespace + '.' if namespace else '', self.name, self.scope.name)
@register_wrapper(CLITableType.Property)
class CLIProperty(CLITableWrapper):
@cached_property
def constant(self):
try:
return next(c for c in self._file.get_table(CLITableType.Constant) if c.parent == self)
except StopIteration:
return None
def __repr__(self):
return '<{}: {} {}>'.format(self.__class__.__name__, self.signature, self.name)
@register_wrapper(CLITableType.Param)
class CLIParam(CLITableWrapper):
@cached_property
def marshal(self):
try:
return next(l for l in self._file.get_table(CLITableType.FieldMarshal) if l.parent == self)
except StopIteration:
return None
@cached_property
def constant(self):
try:
return next(c for c in self._file.get_table(CLITableType.Constant) if c.parent == self)
except StopIteration:
return None
def __repr__(self):
return '<{} ({}): {}>'.format(self.__class__.__name__, self.sequence, self.name)
@ -143,6 +169,27 @@ class CLIField(CLITableWrapper):
def parent(self):
return next(c for c in self._file.classes if self in c.fields)
@cached_property
def layout(self):
try:
return next(l for l in self._file.get_table(CLITableType.FieldLayout) if l.field == self)
except StopIteration:
return None
@cached_property
def marshal(self):
try:
return next(l for l in self._file.get_table(CLITableType.FieldMarshal) if l.parent == self)
except StopIteration:
return None
@cached_property
def constant(self):
try:
return next(c for c in self._file.get_table(CLITableType.Constant) if c.parent == self)
except StopIteration:
return None
def __repr__(self):
return '<{}: {} {}>'.format(self.__class__.__name__, self.signature, self.name)
@ -172,8 +219,35 @@ class CLIClass(CLITableWrapper):
@cached_property
def children(self):
return [c.nested for c in self._file.get_table(CLITableType.NestedClass) if c.enclosing == self]
return (c.nested for c in self._file.get_table(CLITableType.NestedClass) if c.enclosing == self)
@cached_property
def layout(self):
try:
return next(l for l in self._file.get_table(CLITableType.ClassLayout) if l.parent == self)
except StopIteration:
return None
@property
def implements(self):
return (i.interface for i in self._file.get_table(CLITableType.InterfaceImpl) if i.type == self)
@property
def properties(self):
try:
return next(p.properties for p in self._file.get_table(CLITableType.PropertyMap) if p.parent == self)
except StopIteration:
return []
def __repr__(self):
namespace = self.namespace
return '<{}: {}{}>'.format(self.__class__.__name__, namespace + '.' if namespace else '', self.name)
@register_wrapper(CLITableType.Assembly)
@register_wrapper(CLITableType.AssemblyRef)
class CLIAssembly(CLITableWrapper):
def __repr__(self):
return '<{}: {} (v{}.{}.{}.{})>'.format(
self.__class__.__name__, self.name,
self.version_major, self.version_minor, self.build_number, self.rev_number
)