Files
dobin-avred/plugins/dotnet/augment_dotnet.py
T
2023-09-01 08:52:22 +02:00

238 lines
9.5 KiB
Python

from intervaltree import Interval, IntervalTree
import logging
from typing import List, Tuple, Set
from plugins.pe.file_pe import FilePe, getDotNetSections
from plugins.dotnet.dncilparser import DncilParser, IlMethod
from plugins.dotnet.dotnet_data import DotnetData, DotnetDataEntry
from myutils import *
from config import MAX_DISASM_SIZE
from dotnetfile import DotNetPE
from dotnetfile.structures import DOTNET_CLR_HEADER
from dotnetfile.parser import DOTNET_STREAM_HEADER
from dotnetfile.util import BinaryStructureField, FileLocation
from model.model_data import Match
from model.model_code import AsmInstruction, UiDisasmLine, SectionType
def augmentFileDotnet(filePe: FilePe, matches: List[Match]) -> str:
"""Correlates file offsets in matches with the disassembles filePe methods"""
dotnetSectionsBag = getDotNetSections(filePe)
if dotnetSectionsBag is None:
logging.warning("No dotNet sections")
dncilParser = DncilParser(filePe.filepath)
dotNetData = DotnetData(filePe.filepath)
dotNetData.init()
for match in matches:
matchDisasmLines: List[UiDisasmLine] = []
matchAsmInstructions: List[AsmInstruction] = []
matchBytes: bytes = filePe.Data().getBytesRange(start=match.start(), end=match.end())
matchHexdump = hexdmp(matchBytes, offset=match.start())
matchSection = filePe.peSectionsBag.getSectionByPhysAddr(match.fileOffset) # note: we take the PE section, not DotNet
matchSectionName = filePe.peSectionsBag.getSectionNameByPhysAddr(match.fileOffset)
# set info: PE section name first
info = matchSectionName + " "
detail = ''
if dotnetSectionsBag is not None:
# set info: .NET sections/streams name next if found
sections = dotnetSectionsBag.getSectionsForPhysRange(match.start(), match.end())
info += ','.join(s.name for s in sections)
if matchSectionName == ".text":
# only disassemble if the match is reasonably small. same for function names
if match.size < MAX_DISASM_SIZE:
matchAsmInstructions, matchDisasmLines, methodNames = disassembleDotNet(match.start(), match.size, dncilParser)
detail += " " + " ".join(methodNames)
# .text has most of DotNet, check if its methods
if dotnetSectionsBag.getSectionNameByPhysAddr(match.start()) == "methods":
match.sectionType = SectionType.CODE
else:
match.sectionType = SectionType.DATA
results: List[DotnetDataEntry] = dotNetData.findBy(match.start(), match.end())
uidl: List[UiDisasmLine] = []
for res in results:
u = UiDisasmLine(
res.offset,
0,
True,
res.tableName + res.data,
res.tableName + res.data)
uidl.append(u)
matchDisasmLines = uidl
else:
match.sectionType = SectionType.DATA
# take dotnet section
relevantSection = dotnetSectionsBag.getSectionByPhysAddr(match.fileOffset)
if relevantSection is None:
# or pe section if not in dotnet section
relevantSection = filePe.peSectionsBag.getSectionByPhysAddr(match.fileOffset)
if relevantSection is None:
# should never be reached
relevantSection = "unknown?"
match.setSection(relevantSection)
match.setData(matchBytes)
match.setDataHexdump(matchHexdump)
match.setSectionInfo(info)
match.setSectionDetail(detail)
match.setDisasmLines(matchDisasmLines)
match.setAsmInstructions(matchAsmInstructions)
s = ''
for section in filePe.peSectionsBag.sections:
s += "{0:<24}: File Offset: {1:<7} Virtual Addr: {2:<6} size {3:<6} scan:{4}\n".format(
section.name, section.physaddr, section.virtaddr, section.size, section.scan)
return s
def disassembleDotNet(offset: int, size: int, dncilParser: DncilParser) -> Tuple[List[AsmInstruction], List[UiDisasmLine], Set[str]]:
"""Get section-info & disassembly as UiDisasmLine's with dncilParser for range offset/+size"""
uiDisasmLines: List[UiDisasmLine] = []
asmInstructions: List[AsmInstruction] = []
methodNames: Set[str] = set()
ilMethods = dncilParser.getMethods(offset, offset+size)
if ilMethods is None or len(ilMethods) == 0:
#logging.debug("No disassembly found for {:X}", offset)
return [], [], ''
logging.info("Match physical {}/0x{:X}, method disassemblies found: {}".format(
offset, offset, len(ilMethods)))
# all relevant instructions
addrTightStart = offset
addrTightEnd = offset + size
# provide some more context
addrWideStart = addrTightStart - 16
addrWideEnd = addrTightEnd + 16
intervalMatch = Interval(offset, offset+size)
# check each disassembled function if it contains instructions for our offset
ilMethod: IlMethod
for ilMethod in sorted(ilMethods):
intervalMethod = Interval(ilMethod.getOffset(), ilMethod.getOffset() + ilMethod.getSize())
# check if this method contains part of the data
if not intervalMatch.overlaps(intervalMethod):
continue
# the method contains some of the data.
# * add method metadata
# * add the relevant instructions
isPart = False
if ilMethod.getOffset() >= addrTightStart and ilMethod.getOffset() <= addrTightEnd:
isPart = True
uiDisasmLine = UiDisasmLine(
ilMethod.getOffset(),
ilMethod.getRva(),
isPart,
"Function: {}".format(ilMethod.getName()),
"Function: {}".format(ilMethod.getName())
)
uiDisasmLines.append(uiDisasmLine)
# find all instructions of method which are part of the match
for asmInstruction in ilMethod.instructions:
addrOff = asmInstruction.offset
if addrOff > addrWideStart and addrOff < addrWideEnd:
isPart = False
if addrOff >= addrTightStart and addrOff <= addrTightEnd:
asmInstructions.append(asmInstruction)
isPart = True
text = ' '
text += f"{' '.join('{:02x}'.format(b) for b in asmInstruction.rawBytes) : <20}"
text += asmInstruction.disasm
uiDisasmLine = UiDisasmLine(
asmInstruction.offset,
asmInstruction.rva,
isPart,
text,
text
)
uiDisasmLines.append(uiDisasmLine)
methodNames.add(ilMethod.getName())
return asmInstructions, uiDisasmLines, methodNames
def getDotNetDisassemblyHeader(filePe: FilePe, offset: int, size: int,) -> List[UiDisasmLine]:
uiDisasmLines: List[UiDisasmLine] = [] # all diasassmbled IL
dotnet_file = DotNetPE(filePe.filepath)
textSection = filePe.peSectionsBag.getSectionByName('.text')
addrOffset = textSection.virtaddr - textSection.physaddr
# DotNet header / CLI header / CLR header
clrHeader: DOTNET_CLR_HEADER = dotnet_file.clr_header
for entry in clrHeader.structure_fields:
hdrFileOffset = entry.address
hdrSize = entry.size
if hdrFileOffset >= offset and hdrFileOffset + hdrSize <= offset + size:
text = "{:18} CLR Header: {}: {}".format(
hexstr(filePe.DataAsBytes(), hdrFileOffset, hdrSize),
entry.display_name,
entry.value)
uiDisasmLine = UiDisasmLine(
hdrFileOffset,
entry.address,
True,
text,
text
)
uiDisasmLines.append(uiDisasmLine)
# metadata header
entry: BinaryStructureField
for entry in dotnet_file.dotnet_metadata_header.structure_fields:
hdrFileOffset = entry.address - addrOffset
hdrSize = entry.size
if hdrFileOffset >= offset and hdrFileOffset + hdrSize <= offset + size:
text = "{:18} Metadata Header: {}: {}".format(
hexstr(filePe.DataAsBytes(), hdrFileOffset, hdrSize),
entry.display_name,
entry.value)
uiDisasmLine = UiDisasmLine(
hdrFileOffset,
entry.address,
True,
text,
text
)
uiDisasmLines.append(uiDisasmLine)
# all 5 stream headers
streamHeader: DOTNET_STREAM_HEADER
for streamHeader in dotnet_file.dotnet_stream_headers:
hdrFileOffset = streamHeader.address - addrOffset
hdrSize = streamHeader.size
for entry in streamHeader.structure_fields:
entryFileOffset = entry.address - addrOffset
entrySize = entry.size
if entryFileOffset >= offset and entryFileOffset + entrySize <= offset + size:
text = "{:18} Stream Header: {}: {}".format(
hexstr(filePe.DataAsBytes(), entryFileOffset, entrySize),
entry.display_name,
entry.value)
uiDisasmLine = UiDisasmLine(
hdrFileOffset,
entry.address,
True,
text,
text
)
uiDisasmLines.append(uiDisasmLine)
return uiDisasmLines