Skip to content

Commit

Permalink
add bin_arm_bare target
Browse files Browse the repository at this point in the history
  • Loading branch information
DennyDai committed Dec 17, 2024
1 parent 6038c10 commit 53824e5
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/patcherex2/components/binary_analyzers/angr.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def mem_addr_to_file_offset(self, addr: int) -> int:

def get_basic_block(self, addr: int) -> dict[str, int | list[int]]:
# NOTE: angr splits basic blocks at call instructions, so we need to handle this
if self.is_thumb(addr):
if self.is_thumb(addr) and addr % 2 == 0:
addr += 1
addr = self.denormalize_addr(addr)

Expand Down
70 changes: 70 additions & 0 deletions src/patcherex2/components/binfmt_tools/binary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from __future__ import annotations

import io
import logging

from .binfmt_tool import BinFmtTool

logger = logging.getLogger(__name__)


class Binary(BinFmtTool):
def __init__(self, p, binary_path: str) -> None:
super().__init__(p, binary_path)
self._file = open(binary_path, "rb")
self.file_size = self._file.seek(0, io.SEEK_END)
self._file.seek(0)
self.file_updates = []

def __del__(self) -> None:
self._file.close()

def _init_memory_analysis(self) -> None:
pass

def finalize(self) -> None:
pass

def save_binary(self, filename: str | None = None) -> None:
if filename is None:
filename = f"{self.binary_path}.patched"
with open(filename, "wb") as f:
self._file.seek(0)
f.write(self._file.read())
# apply the updates
for update in self.file_updates:
f.seek(update["offset"])
f.write(update["content"])

def update_binary_content(self, offset: int, new_content: bytes) -> None:
logger.debug(
f"Updating offset {hex(offset)} with content ({len(new_content)} bytes) {new_content}"
)
for update in self.file_updates:
if offset >= update["offset"] and offset < update["offset"] + len(
update["content"]
):
raise ValueError(
f"Cannot update offset {hex(offset)} with content {new_content}, it overlaps with a previous update"
)
self.file_updates.append({"offset": offset, "content": new_content})
if offset + len(new_content) > self.file_size:
self.file_size = offset + len(new_content)

def get_binary_content(self, offset: int, size: int) -> bytes:
# FIXME: content partially in the file and partially in the updates (check other binfmt tools as well)
# check if it's in the file updates
for update in self.file_updates:
if offset >= update["offset"] and offset + size <= update["offset"] + len(
update["content"]
):
return update["content"][
offset - update["offset"] : offset - update["offset"] + size
]
# otherwise read from the file
self._file.seek(offset)
return self._file.read(size)

def append_to_binary_content(self, new_content: bytes) -> None:
self.file_updates.append({"offset": self.file_size, "content": new_content})
self.file_size += len(new_content)
1 change: 1 addition & 0 deletions src/patcherex2/targets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .bin_arm_bare import BinArmBare
from .elf_aarch64_linux import ElfAArch64Linux
from .elf_amd64_linux import ElfAmd64Linux
from .elf_amd64_linux_recomp import ElfAmd64LinuxRecomp
Expand Down
83 changes: 83 additions & 0 deletions src/patcherex2/targets/bin_arm_bare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import logging

from ..components.allocation_managers.allocation_manager import AllocationManager
from ..components.archinfo.arm import ArmInfo
from ..components.assemblers.keystone_arm import KeystoneArm
from ..components.binary_analyzers.angr import Angr
from ..components.binfmt_tools.binary import Binary
from ..components.compilers.clang_arm import ClangArm
from ..components.disassemblers.capstone_arm import CapstoneArm
from ..components.utils.utils import Utils
from .target import Target

logger = logging.getLogger(__name__)


class BinArmBare(Target):
@staticmethod
def detect_target(binary_path):
return False

def get_assembler(self, assembler):
assembler = assembler or "keystone"
if assembler == "keystone":
return KeystoneArm(self.p)
raise NotImplementedError()

def get_allocation_manager(self, allocation_manager):
allocation_manager = allocation_manager or "default"
if allocation_manager == "default":
return AllocationManager(self.p)
raise NotImplementedError()

def get_compiler(self, compiler):
compiler = compiler or "clang"
if compiler == "clang":
return ClangArm(self.p, compiler_flags=["-target", "arm-linux-gnueabihf"])
elif compiler == "clang19":
return ClangArm(
self.p,
compiler_flags=["-target", "arm-linux-gnueabihf"],
clang_version=19,
)
raise NotImplementedError()

def get_disassembler(self, disassembler):
disassembler = disassembler or "capstone"
if disassembler == "capstone":
return CapstoneArm(self.p)
raise NotImplementedError()

def get_binfmt_tool(self, binfmt_tool):
binfmt_tool = binfmt_tool or "default"
if binfmt_tool == "default":
return Binary(self.p, self.binary_path)
raise NotImplementedError()

def get_binary_analyzer(self, binary_analyzer):
binary_analyzer = binary_analyzer or "angr"
if binary_analyzer == "angr":
return Angr(
self.binary_path,
angr_kwargs={
"arch": "ARMEL",
"auto_load_libs": False,
},
angr_cfg_kwargs={
"normalize": True,
"data_references": True,
},
)
raise NotImplementedError()

def get_utils(self, utils):
utils = utils or "default"
if utils == "default":
return Utils(self.p, self.binary_path)
raise NotImplementedError()

def get_archinfo(self, archinfo):
archinfo = archinfo or "default"
if archinfo == "default":
return ArmInfo()
raise NotImplementedError()

0 comments on commit 53824e5

Please sign in to comment.