from typing import TYPE_CHECKING, Any, Union, Optional from dnfile import dnPE from dnfile.mdtable import MethodDefRow import dnfile from dnfile.enums import MetadataTables from dncil.cil.body import CilMethodBody from dncil.cil.error import MethodBodyFormatError from dncil.clr.token import Token, StringToken, InvalidToken from dncil.cil.body.reader import CilMethodBodyReaderBase import struct from intervaltree import Interval, IntervalTree from typing import List import logging import struct from bitstring import Bits, BitArray, BitStream, pack from model.model import AsmInstruction # key token indexes to dotnet meta tables DOTNET_META_TABLES_BY_INDEX = {table.value: table.name for table in MetadataTables} class DnfileMethodBodyReader(CilMethodBodyReaderBase): def __init__(self, pe: dnPE, row: MethodDefRow): """ """ self.pe: dnPE = pe self.offset: int = self.pe.get_offset_from_rva(row.Rva) def read(self, n: int) -> bytes: """ """ data: bytes = self.pe.get_data(self.pe.get_rva_from_offset(self.offset), n) self.offset += n return data def tell(self) -> int: """ """ return self.offset def seek(self, offset: int) -> int: """ """ self.offset = offset return self.offset def read_dotnet_user_string(pe: dnfile.dnPE, token: StringToken) -> Union[str, InvalidToken]: """read user string from #US stream""" try: user_string: Optional[dnfile.stream.UserString] = pe.net.user_strings.get_us(token.rid) except UnicodeDecodeError as e: return InvalidToken(token.value) if user_string is None: return InvalidToken(token.value) return user_string.value def resolve_token(pe: dnPE, token: Token) -> Any: """ """ if isinstance(token, StringToken): return read_dotnet_user_string(pe, token) table_name: str = DOTNET_META_TABLES_BY_INDEX.get(token.table, "") if not table_name: # table_index is not valid return InvalidToken(token.value) table: Any = getattr(pe.net.mdtables, table_name, None) if table is None: # table index is valid but table is not present return InvalidToken(token.value) try: return table.rows[token.rid - 1] except IndexError: # table index is valid but row index is not valid return InvalidToken(token.value) def read_method_body(pe: dnPE, row: MethodDefRow) -> CilMethodBody: """ """ return CilMethodBody(DnfileMethodBodyReader(pe, row)) def format_operand(pe: dnPE, operand: Any) -> str: """ """ if isinstance(operand, Token): operand = resolve_token(pe, operand) if isinstance(operand, str): return f'"{operand}"' elif isinstance(operand, int): return hex(operand) elif isinstance(operand, list): return f"[{', '.join(['({:04X})'.format(x) for x in operand])}]" elif isinstance(operand, dnfile.mdtable.MemberRefRow): if isinstance(operand.Class.row, (dnfile.mdtable.TypeRefRow,)): return f"{str(operand.Class.row.TypeNamespace)}.{operand.Class.row.TypeName}::{operand.Name}" elif isinstance(operand, dnfile.mdtable.TypeRefRow): return f"{str(operand.TypeNamespace)}.{operand.TypeName}" elif isinstance(operand, (dnfile.mdtable.FieldRow, dnfile.mdtable.MethodDefRow)): return f"{operand.Name}" elif operand is None: return "" return str(operand) class IlMethodHeaderFat(): def __init__(self, headerBytes: bytes, methodOffset: int): self.headerBytes = headerBytes self.offset = methodOffset self.hdrOther = None self.size = None self.flags = None self.type = None self.maxStack = None self.codeSize = None self.localVarSigTok = None self.parse(headerBytes) def parse(self, headerBytes: bytes): # swap first two bytes #c = struct.unpack(' List[IlMethod]: res = self.methodsIt.overlap(begin, end) if len(res) == 0: return None res = [r[2] for r in res] return res def parseDotNetHeader(self, headerBytes: bytes, methodOffset:int): b = BitStream(headerBytes) hdrOther = b.read('uint6') hdrType = b.read('uint2') if hdrType == 0x2: #print("Tiny header, size: {} bytes".format(hdrOther)) self.ilMethodHeaderFat = None return None if hdrType == 0x3: return IlMethodHeaderFat(headerBytes, methodOffset)