mirror of
https://github.com/dobin/avred
synced 2026-06-08 13:54:13 +00:00
179 lines
6.1 KiB
Python
179 lines
6.1 KiB
Python
from intervaltree import Interval, IntervalTree
|
|
import logging
|
|
from typing import List, Tuple
|
|
from model.model import Match, FileInfo, UiDisasmLine
|
|
from model.extensions import Scanner
|
|
from plugins.file_pe import FilePe, Section
|
|
from utils import *
|
|
from dotnetfile import DotNetPE
|
|
from plugins.dncilparser import DncilParser
|
|
|
|
|
|
def augmentFileDotnet(filePe: FilePe, matches: List[Match]) -> str:
|
|
"""Correlates file offsets in matches with the disassembles filePe methods"""
|
|
dotnetSections = getDotNetSections(filePe)
|
|
if dotnetSections is None:
|
|
logging.warn("No dotNet sections")
|
|
dncilParser = DncilParser(filePe.filepath)
|
|
|
|
for match in matches:
|
|
uiDisasmLines = []
|
|
data = filePe.data[match.start():match.end()]
|
|
dataHexdump = hexdmp(data, offset=match.start())
|
|
sectionName = filePe.findSectionNameFor(match.fileOffset)
|
|
|
|
# set info: PE section name first
|
|
info = sectionName + " "
|
|
|
|
if dotnetSections is not None:
|
|
# set info: .NET sections/streams name next if found
|
|
sections = list(filter(lambda x: match.start() >= x.addr and match.start() <= x.addr + x.size, dotnetSections))
|
|
if len(sections) > 0:
|
|
info += ' '.join(s.name for s in sections)
|
|
|
|
if sectionName == ".text": # only disassemble in .text
|
|
# set info: precise disassembly info (e.g. function name)
|
|
uiDisasmLines, info2 = getDotNetDisassembly(match.start(), match.size, dncilParser)
|
|
info += " " + info2
|
|
|
|
match.setData(data)
|
|
match.setDataHexdump(dataHexdump)
|
|
match.setSectionInfo(info)
|
|
match.setDisasmLines(uiDisasmLines)
|
|
|
|
return ""
|
|
|
|
|
|
def getDotNetDisassembly(offset, size, dncilParser) -> Tuple[List[UiDisasmLine], str]:
|
|
"""Get section-info & disassembly with dncilParser for range offset/+size"""
|
|
uiDisasmLines = [] # all diasassmbled IL
|
|
methodNames = set() # a set with unique function names
|
|
|
|
ilMethods = dncilParser.query(offset, offset+size)
|
|
if ilMethods is None or len(ilMethods) == 0:
|
|
logging.debug("No disassembly found for {:X}", offset)
|
|
return uiDisasmLines, ''
|
|
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
|
|
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)
|
|
|
|
uiDisasmLine = UiDisasmLine(
|
|
ilMethod.getOffset(),
|
|
ilMethod.getRva(),
|
|
isPart,
|
|
"Header size: {}".format(ilMethod.getHeaderSize()),
|
|
"Header size: {}".format(ilMethod.getHeaderSize())
|
|
)
|
|
uiDisasmLines.append(uiDisasmLine)
|
|
|
|
# find all instructions of method which are part of the match
|
|
for ilInstruction in ilMethod.instructions:
|
|
addrOff = ilInstruction.fileOffset
|
|
|
|
if addrOff > addrWideStart and addrOff < addrWideEnd:
|
|
isPart = False
|
|
if addrOff >= addrTightStart and addrOff <= addrTightEnd:
|
|
isPart = True
|
|
|
|
uiDisasmLine = UiDisasmLine(
|
|
ilInstruction.fileOffset,
|
|
ilInstruction.rva,
|
|
isPart,
|
|
ilInstruction.text,
|
|
ilInstruction.text
|
|
)
|
|
uiDisasmLines.append(uiDisasmLine)
|
|
|
|
methodNames.add(ilMethod.getName())
|
|
|
|
info = str(methodNames)
|
|
return uiDisasmLines, info
|
|
|
|
|
|
def getDotNetSections(filePe):
|
|
# Get more details about .net executable (e.g. streams)
|
|
# as most of it is just in PE .text
|
|
sections = []
|
|
|
|
dotnet_file = DotNetPE(filePe.filepath)
|
|
|
|
textSection = filePe.getSectionByName('.text')
|
|
addrOffset = textSection.virtaddr - textSection.addr
|
|
|
|
cli_header_addr = textSection.addr
|
|
cli_header_size = dotnet_file.clr_header.HeaderSize.value
|
|
|
|
metadata_header_addr = dotnet_file.clr_header.MetaDataDirectoryAddress.value
|
|
metadata_header_addr -= addrOffset
|
|
metadata_header_size = dotnet_file.clr_header.MetaDataDirectorySize.value
|
|
|
|
methods_addr = cli_header_addr + cli_header_size
|
|
methods_size = metadata_header_addr - methods_addr
|
|
|
|
signature_addr = dotnet_file.clr_header.StrongNameSignatureAddress.value
|
|
signature_addr -= addrOffset
|
|
signature_size = dotnet_file.clr_header.StrongNameSignatureSize.value
|
|
|
|
s = Section('DotNet Header',
|
|
cli_header_addr,
|
|
cli_header_size,
|
|
0)
|
|
sections.append(s)
|
|
|
|
s = Section('methods',
|
|
methods_addr,
|
|
methods_size,
|
|
0)
|
|
sections.append(s)
|
|
|
|
s = Section('Metadata Header',
|
|
metadata_header_addr,
|
|
metadata_header_size,
|
|
0)
|
|
sections.append(s)
|
|
|
|
for stream in dotnet_file.dotnet_streams:
|
|
s = Section('Stream: ' + stream.string_representation,
|
|
stream.address - addrOffset,
|
|
stream.size,
|
|
0)
|
|
sections.append(s)
|
|
|
|
s = Section('Signature',
|
|
signature_addr,
|
|
signature_size,
|
|
0)
|
|
sections.append(s)
|
|
|
|
return sections
|