angr.simos.snimmuc_nxp 源代码
from __future__ import annotations
from typing import TYPE_CHECKING
from io import BytesIO
from cle.backends import Blob
from angr.knowledge_base import KnowledgeBase
from .simos import SimOS
if TYPE_CHECKING:
from angr import Project
[文档]
class SimSnimmucNxp(SimOS):
"""
This class implements the "OS" for a bare-metal firmware used at an imaginary company.
"""
[文档]
def __init__(self, project: Project, name=None, **kwargs): # pylint:disable=unused-argument
super().__init__(project, name=name)
[文档]
def configure_project(self):
# pattern match the entry point to figure out if we support parsing this binary
entry_bytes = self.project.loader.memory.load(self.project.entry, 3 * 4)
if entry_bytes != b"\x94!\xff\xf0" b"|\x08\x02\xa6" b"\x90\x01\x00\x14":
return
entry_block = self.project.factory.block(self.project.entry)
try:
first_sync = next(
iter(
[
idx
for idx, insn in enumerate(entry_block.disassembly.insns)
if insn.mnemonic in {"sync", "isync"}
]
)
)
except StopIteration:
return
# run this block and acquire initial registers for each function
state = self.project.factory.blank_state(addr=self.project.entry)
# set garbage value to key registers
key_registers = ["r13", "r2", "r14", "r15", "r16"]
GARBAGE = 0xDEADBEEF
for key_reg in key_registers:
setattr(state.regs, "_" + key_reg, GARBAGE)
simgr = self.project.factory.simgr(state)
simgr.step(num_inst=first_sync)
if simgr.active and len(simgr.active) == 1:
stepped_state = simgr.one_active
else:
return
reg_values = {}
for key_reg in key_registers:
reg_values[key_reg] = getattr(stepped_state.regs, "_" + key_reg).concrete_value
if reg_values[key_reg] in {None, GARBAGE}:
# umm the register is not initialized. unsupported?
return
# TODO: Make them part of the ABI
self.function_initial_registers = reg_values
# load SDATA, SDATA2, and a few other regions
mappings = {}
# this is just CRAZY...
# TODO: Better resilience
tmp_kb = KnowledgeBase(self.project)
self.project.analyses.CFG(
regions=[(self.project.entry, self.project.entry + 180)], data_references=False, kb=tmp_kb
)
# take the last function
func = tmp_kb.functions[self.project.entry]
second_to_last_block = sorted(func.blocks, key=lambda x: x.addr)[-2]
if second_to_last_block.vex.jumpkind != "Ijk_Call":
return
init_func_addr = second_to_last_block.vex.next
if not isinstance(init_func_addr, int):
return
# lift one block
init_func_block = self.project.factory.block(init_func_addr)
if init_func_block.vex.jumpkind != "Ijk_Call":
return
section_init_func_addr = init_func_block.vex.next
if not isinstance(section_init_func_addr, int):
return
self.project.analyses.CFG(
regions=[(section_init_func_addr, section_init_func_addr + 0x324)], data_references=False, kb=tmp_kb
)
section_init_func = tmp_kb.functions[section_init_func_addr]
sorted_blocks = sorted(section_init_func.blocks, key=lambda x: x.addr)
sdata_section_init_call = sorted_blocks[25]
if sdata_section_init_call.vex.jumpkind != "Ijk_Call":
return
sdata_section_init_func = sdata_section_init_call.vex.next
if not isinstance(sdata_section_init_func, int):
return
# more pattern matching
state = self.project.factory.blank_state(addr=sdata_section_init_func)
for key_reg in ["r28", "r29", "r30"]:
setattr(state.regs, "_" + key_reg, GARBAGE)
simgr = self.project.factory.simgr(state)
simgr.step()
if simgr.active and len(simgr.active) == 1:
stepped_state = simgr.one_active
else:
return
sdata_reg_values = {}
for key_reg in ["r28", "r29", "r30"]:
sdata_reg_values[key_reg] = getattr(stepped_state.regs, "_" + key_reg).concrete_value
if sdata_reg_values[key_reg] in {None, GARBAGE}:
# umm the register is not initialized. unsupported?
return
mappings[sdata_reg_values["r30"]] = (sdata_reg_values["r29"], sdata_reg_values["r28"] - sdata_reg_values["r29"])
# TODO: Implement support for SDATA2 and other sections
# mappings = {
# 0x60005850: (0x30A734, 0x60008D30 - 0x60005850),
# 0x60011DA0: (0x3165EC, 0x60014580 - 0x60011DA0),
# 0x60014580: (0x32AC48, 0x60061638 - 0x60014580),
# }
for mem_base, (source_addr, size) in mappings.items():
backing = BytesIO()
backing.write(self.project.loader.memory.load(source_addr, size))
backing.seek(0)
blob = Blob(
binary=None,
binary_stream=backing,
base_addr=mem_base,
offset=0,
arch=self.project.arch,
)
self.project.loader.dynamic_load(blob)
# FIXME: Use ret_offset from the calling convention
self.project.arch.ret_offset = self.project.arch.registers["r3"][0]