fix and enable binary ninja fake string segment support

This commit is contained in:
LukeFZ
2025-04-23 16:52:05 +02:00
parent c12429bf97
commit d70db09901

View File

@@ -1,6 +1,21 @@
from binaryninja import * from binaryninja import (
BinaryView,
Component,
Type,
PointerType,
TypeParser,
Platform,
Endianness,
ArrayType,
BackgroundTaskThread,
demangle_gnu3,
get_qualified_name,
SegmentFlag,
SectionSemantics,
)
from binaryninja.log import log_error
#try: # try:
# from typing import TYPE_CHECKING # from typing import TYPE_CHECKING
# if TYPE_CHECKING: # if TYPE_CHECKING:
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext # from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
@@ -10,16 +25,14 @@ from binaryninja import *
# from datetime import datetime # from datetime import datetime
# from typing import Literal # from typing import Literal
# bv: BinaryView = None # type: ignore # bv: BinaryView = None # type: ignore
#except: # except:
# pass # pass
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface): class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
# this is implemented, supports_fake_string_segment: bool = True
# however the write API does not seem to work properly here (possibly a bug),
# so this is disabled for now
supports_fake_string_segment: bool = False
_status: BaseStatusHandler _status: BaseStatusHandler
@@ -32,20 +45,16 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
_address_size: int _address_size: int
_endianness: Literal["little", "big"] _endianness: Literal["little", "big"]
TYPE_PARSER_OPTIONS = [ TYPE_PARSER_OPTIONS = ["--target=x86_64-pc-linux", "-x", "c++", "-D_BINARYNINJA_=1"]
"--target=x86_64-pc-linux",
"-x", "c++",
"-D_BINARYNINJA_=1"
]
def __init__(self, status: BaseStatusHandler): def __init__(self, status: BaseStatusHandler):
self._status = status self._status = status
def _get_or_create_type(self, type: str) -> Type: def _get_or_create_type(self, type: str) -> Type:
if type.startswith("struct "): if type.startswith("struct "):
type = type[len("struct "):] type = type[len("struct ") :]
elif type.startswith("class "): elif type.startswith("class "):
type = type[len("class "):] type = type[len("class ") :]
if type in self._type_cache: if type in self._type_cache:
return self._type_cache[type] return self._type_cache[type]
@@ -66,9 +75,11 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
parsed_types, errors = TypeParser.default.parse_types_from_source( parsed_types, errors = TypeParser.default.parse_types_from_source(
types, types,
filename if filename else "types.hpp", filename if filename else "types.hpp",
self._view.platform if self._view.platform is not None else Platform["windows-x86_64"], self._view.platform
if self._view.platform is not None
else Platform["windows-x86_64"],
self._view, self._view,
self.TYPE_PARSER_OPTIONS self.TYPE_PARSER_OPTIONS,
) )
if parsed_types is None: if parsed_types is None:
@@ -90,7 +101,9 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
self._function_type_cache = {} self._function_type_cache = {}
self._address_size = self._view.address_size self._address_size = self._view.address_size
self._endianness = "little" if self._view.endianness == Endianness.LittleEndian else "big" self._endianness = (
"little" if self._view.endianness == Endianness.LittleEndian else "big"
)
self._status.update_step("Parsing header") self._status.update_step("Parsing header")
@@ -105,7 +118,9 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
self._status.update_progress(1) self._status.update_progress(1)
return True return True
self._view.define_user_types([(x.name, x.type) for x in parsed_types.types], import_progress_func) self._view.define_user_types(
[(x.name, x.type) for x in parsed_types.types], import_progress_func
)
def on_finish(self): def on_finish(self):
self._view.commit_undo_actions(self._undo_id) self._view.commit_undo_actions(self._undo_id)
@@ -143,8 +158,8 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
if type in self._function_type_cache: if type in self._function_type_cache:
function.type = self._function_type_cache[type] # type: ignore function.type = self._function_type_cache[type] # type: ignore
else: else:
#log_info(f"skipping function type setting for {address}, {type}") # log_info(f"skipping function type setting for {address}, {type}")
#pass # pass
function.type = type.replace("this", "`this`") function.type = type.replace("this", "`this`")
def set_data_comment(self, address: int, cmt: str): def set_data_comment(self, address: int, cmt: str):
@@ -176,7 +191,7 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
if name.startswith("_Z"): if name.startswith("_Z"):
type, demangled = demangle_gnu3(self._view.arch, name, self._view) type, demangled = demangle_gnu3(self._view.arch, name, self._view)
function.name = get_qualified_name(demangled) function.name = get_qualified_name(demangled)
#function.type = type - this does not work due to the generated types not being namespaced. :( # function.type = type - this does not work due to the generated types not being namespaced. :(
else: else:
function.name = name function.name = name
@@ -230,10 +245,19 @@ class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
def create_fake_segment(self, name: str, size: int) -> int: def create_fake_segment(self, name: str, size: int) -> int:
last_end_addr = self._view.mapped_address_ranges[-1].end last_end_addr = self._view.mapped_address_ranges[-1].end
if last_end_addr % 0x1000 != 0: if last_end_addr % 0x1000 != 0:
last_end_addr += (0x1000 - (last_end_addr % 0x1000)) last_end_addr += 0x1000 - (last_end_addr % 0x1000)
self._view.memory_map.add_memory_region(
f"mem_{name}",
last_end_addr,
bytes(size),
SegmentFlag.SegmentContainsData | SegmentFlag.SegmentReadable,
)
self._view.add_user_section(
name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics
)
self._view.add_user_segment(last_end_addr, size, 0, 0, SegmentFlag.SegmentContainsData)
self._view.add_user_section(name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics)
return last_end_addr return last_end_addr
def write_string(self, address: int, value: str) -> int: def write_string(self, address: int, value: str) -> int:
@@ -255,7 +279,8 @@ class BinaryNinjaStatusHandler(BaseStatusHandler):
self.last_updated_time = datetime.min self.last_updated_time = datetime.min
self._thread = thread self._thread = thread
def initialize(self): pass def initialize(self):
pass
def update(self): def update(self):
if self.was_cancelled(): if self.was_cancelled():
@@ -271,7 +296,7 @@ class BinaryNinjaStatusHandler(BaseStatusHandler):
total_time = current_time - self.start_time total_time = current_time - self.start_time
self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})" self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})"
def update_step(self, step, max_items = 0): def update_step(self, step, max_items=0):
self.step = step self.step = step
self.max_items = max_items self.max_items = max_items
self.current_items = 0 self.current_items = 0
@@ -279,15 +304,17 @@ class BinaryNinjaStatusHandler(BaseStatusHandler):
self.last_updated_time = datetime.min self.last_updated_time = datetime.min
self.update() self.update()
def update_progress(self, new_progress = 1): def update_progress(self, new_progress=1):
self.current_items += new_progress self.current_items += new_progress
self.update() self.update()
def was_cancelled(self): return False def was_cancelled(self):
return False
def close(self): def close(self):
pass pass
# Entry point # Entry point
class Il2CppTask(BackgroundTaskThread): class Il2CppTask(BackgroundTaskThread):
def __init__(self): def __init__(self):
@@ -299,4 +326,5 @@ class Il2CppTask(BackgroundTaskThread):
context = ScriptContext(backend, status) context = ScriptContext(backend, status)
context.process() context.process()
Il2CppTask().start() Il2CppTask().start()