Files
Dobin Rutishauser f24a8091da fix: bug
2023-09-30 12:19:30 +02:00

238 lines
9.7 KiB
Python

from intervaltree import Interval, IntervalTree
import logging
from typing import List, Tuple, Set
from plugins.dotnet.file_dotnet import FilePeDotnet
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(filePeDotnet: FilePeDotnet, matches: List[Match]) -> str:
"""Correlates file offsets in matches with the disassembles filePe methods"""
dncilParser = DncilParser(filePeDotnet.filepath)
dotNetData = DotnetData(filePeDotnet.filepath)
dotNetData.init()
for match in matches:
matchDisasmLines: List[UiDisasmLine] = []
matchAsmInstructions: List[AsmInstruction] = []
matchBytes: bytes = filePeDotnet.Data().getBytesRange(start=match.start(), end=match.end())
matchHexdump = hexdmp(matchBytes, offset=match.start())
detail = ''
info = filePeDotnet.peSectionsBag.getSectionByPhysAddr(match.start()).name # should always work
# May have more information in dotnet sections
dotnetSection = filePeDotnet.dotnetSectionsBag.getSectionByPhysAddr(match.start())
if dotnetSection is not None:
info += " " + dotnetSection.name
# Even more info with regions
regions = filePeDotnet.regionsBag.getSectionsForPhysRange(match.start(), match.end())
info += ' '.join(s.name for s in regions)
matchPeSection = filePeDotnet.peSectionsBag.getSectionByPhysAddr(match.fileOffset)
if matchPeSection is not None and matchPeSection.name == ".text":
# .text has most of DotNet, check if its methods
matchDotnetSection = filePeDotnet.dotnetSectionsBag.getSectionByPhysAddr(match.fileOffset)
if matchDotnetSection is not None and matchDotnetSection.name == "methods":
match.sectionType = SectionType.CODE
if match.size < MAX_DISASM_SIZE:
# only disassemble if the match is reasonably small. same for function names
matchAsmInstructions, matchDisasmLines, methodNames = disassembleDotNet(match.start(), match.size, dncilParser)
detail += " " + " ".join(methodNames)
else:
match.sectionType = SectionType.DATA
results: List[DotnetDataEntry] = dotNetData.findBy(match.start(), match.end())
infoAdd: Set[str] = set()
uidl: List[UiDisasmLine] = []
for res in results:
u = UiDisasmLine(
res.offset,
0,
True,
res.tableName + res.data,
res.tableName + res.data)
uidl.append(u)
infoAdd.add(res.tableName)
matchDisasmLines += uidl
detail += " ".join(list(infoAdd))
else:
match.sectionType = SectionType.DATA
# take dotnet section
relevantSection = filePeDotnet.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 filePeDotnet.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(filePeDotnet: FilePeDotnet, offset: int, size: int,) -> List[UiDisasmLine]:
uiDisasmLines: List[UiDisasmLine] = [] # all diasassmbled IL
dotnet_file = DotNetPE(filePeDotnet.filepath)
textSection = filePeDotnet.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(filePeDotnet.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(filePeDotnet.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(filePeDotnet.DataAsBytes(), entryFileOffset, entrySize),
entry.display_name,
entry.value)
uiDisasmLine = UiDisasmLine(
hdrFileOffset,
entry.address,
True,
text,
text
)
uiDisasmLines.append(uiDisasmLine)
return uiDisasmLines