Skip to content

Commit 348b9a8

Browse files
committed
Python APIs for function-level lifting
1 parent f35b9f1 commit 348b9a8

2 files changed

Lines changed: 144 additions & 2 deletions

File tree

python/architecture.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from . import binaryview
4242
from . import variable
4343
from . import basicblock
44+
from . import log
4445

4546
RegisterIndex = NewType('RegisterIndex', int)
4647
RegisterStackIndex = NewType('RegisterStackIndex', int)
@@ -412,6 +413,117 @@ def finalize(self) -> None:
412413
core.BNAnalyzeBasicBlocksContextFinalize(self._handle)
413414

414415

416+
@dataclass
417+
class FunctionLifterContext:
418+
"""Used by ``lift_function`` and contains contextual information for function-level lifting
419+
420+
.. note:: This class is meant to be used by Architecture plugins only
421+
"""
422+
423+
_handle: core.BNFunctionLifterContext
424+
_function: "function.Function"
425+
_platform: "platform.Platform"
426+
_logger: "log.Logger"
427+
_blocks: List["basicblock.BasicBlock"]
428+
_contextual_returns: Dict["function.ArchAndAddr", bool]
429+
_inline_remapping: Dict["function.ArchAndAddr", "function.ArchAndAddr"]
430+
_user_indirect_branches: Dict["function.ArchAndAddr", Set["function.ArchAndAddr"]]
431+
_auto_indirect_branches: Dict["function.ArchAndAddr", Set["function.ArchAndAddr"]]
432+
_inlined_calls: Set[int]
433+
434+
@staticmethod
435+
def from_core_struct(func: core.BNLowLevelILFunction,
436+
bn_fl_context: core.BNFunctionLifterContext) -> "FunctionLifterContext":
437+
"""Create a FunctionLifterContext from a core.BNFunctionLifterContext structure."""
438+
439+
session_id = core.BNLoggerGetSessionId(bn_fl_context.logger)
440+
name = core.BNLoggerGetName(bn_fl_context.logger)
441+
logger = log.Logger(session_id, name, handle=core.BNNewLoggerReference(bn_fl_context.logger))
442+
443+
plat = platform.CorePlatform._from_cache(core.BNNewPlatformReference(bn_fl_context.platform))
444+
blocks = []
445+
for i in range(0, bn_fl_context.basicBlockCount):
446+
blocks.append(
447+
basicblock.BasicBlock(
448+
core.BNNewBasicBlockReference(bn_fl_context.basicBlocks[i])
449+
)
450+
)
451+
452+
contextual_returns = {}
453+
for i in range(0, bn_fl_context.contextualFunctionReturnCount):
454+
loc = function.ArchAndAddr(
455+
CoreArchitecture._from_cache(bn_fl_context.contextualFunctionReturnLocations[i].arch),
456+
bn_fl_context.contextualFunctionReturnLocations[i].address,
457+
)
458+
459+
contextual_returns[loc] = bn_fl_context._contextualFunctionReturnValues[i]
460+
461+
inline_remapping = {}
462+
for i in range(0, bn_fl_context.inlinedRemappingEntryCount):
463+
key = function.ArchAndAddr(
464+
CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingKeys[i].arch),
465+
bn_fl_context.inlinedRemappingKeys[i].address,
466+
)
467+
dest = function.ArchAndAddr(
468+
CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingEntries[i].destination.arch),
469+
bn_fl_context.inlinedRemappingEntries[i].destination.address,
470+
)
471+
inline_remapping[src] = dest
472+
473+
user_indirect_branches = {}
474+
auto_indirect_branches = {}
475+
for i in range(0, bn_fl_context.indirectBranchesCount):
476+
src = function.ArchAndAddr(
477+
CoreArchitecture._from_cache(bn_fl_context.indirectBranches[i].sourceArch),
478+
bn_fl_context.indirectBranches[i].sourceAddr,
479+
)
480+
481+
dest = function.ArchAndAddr(
482+
CoreArchitecture._from_cache(bn_fl_context.indirectBranches[i].destArch),
483+
bn_fl_context.indirectBranches[i].dests[j].destAddr,
484+
)
485+
486+
if bn_fl_context.indirectBranches[i].dests[j].isAutoDefined:
487+
if src not in auto_indirect_branches:
488+
auto_indirect_branches[src] = set()
489+
auto_indirect_branches[src].add(dest)
490+
else:
491+
if src not in user_indirect_branches:
492+
user_indirect_branches[src] = set()
493+
user_indirect_branches[src].add(dest)
494+
495+
496+
inlined_calls = set()
497+
for i in range(0, bn_fl_context.inlinedCallsCount):
498+
inlined_calls.add(bn_fl_context.inlinedCalls[i])
499+
500+
return FunctionLifterContext(
501+
_handle=bn_fl_context,
502+
_function=lowlevelil.LowLevelILFunction(
503+
plat.arch, core.BNNewLowLevelILFunctionReference(func)
504+
),
505+
_platform=plat,
506+
_logger=logger,
507+
_blocks=blocks,
508+
_contextual_returns=contextual_returns,
509+
_inline_remapping=inline_remapping,
510+
_user_indirect_branches=user_indirect_branches,
511+
_auto_indirect_branches=auto_indirect_branches,
512+
_inlined_calls=inlined_calls,
513+
)
514+
515+
def prepare_block_translation(self, function, arch, address):
516+
"""Prepare block for translation"""
517+
518+
core.BNPrepareBlockTranslation(function.handle, arch.handle, address)
519+
520+
@property
521+
def blocks(self) -> List["basicblock.BasicBlock"]:
522+
"""Get the list of basic blocks in this context."""
523+
524+
return self._blocks
525+
526+
415527
@dataclass(frozen=True)
416528
class RegisterInfo:
417529
full_width_reg: RegisterName
@@ -611,6 +723,7 @@ def __init__(self):
611723
self._get_instruction_low_level_il
612724
)
613725
self._cb.analyzeBasicBlocks = self._cb.analyzeBasicBlocks.__class__(self._analyze_basic_blocks)
726+
self._cb.liftFunction = self._cb.liftFunction.__class__(self._lift_function)
614727
self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name)
615728
self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name)
616729
self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name)
@@ -1084,6 +1197,15 @@ def _analyze_basic_blocks(self, ctx, func, ptr_bn_bb_context):
10841197
except:
10851198
log_error_for_exception("Unhandled Python exception in Architecture._analyze_basic_blocks")
10861199

1200+
def _lift_function(self, ctx, func, ptr_bn_fl_context):
1201+
try:
1202+
bn_fl_context = ptr_bn_fl_context.contents
1203+
context = FunctionLifterContext.from_core_struct(func, bn_fl_context)
1204+
return self.lift_function(lowlevelil.LowLevelILFunction(arch=self, handle=core.BNNewLowLevelILFunctionReference(func)), context)
1205+
except:
1206+
log_error_for_exception("Unhandled Python exception in Architecture._lift_function")
1207+
return False
1208+
10871209
def _get_register_name(self, ctxt, reg):
10881210
try:
10891211
if reg in self._regs_by_index:
@@ -1814,6 +1936,23 @@ def analyze_basic_blocks(self, func: 'function.Function', context: BasicBlockAna
18141936
except:
18151937
log_error_for_exception("Unhandled Python exception in Architecture.analyze_basic_blocks")
18161938

1939+
def lift_function(self, func: "lowlevelil.LowLevelILFunction", context: FunctionLifterContext) -> bool:
1940+
"""
1941+
``lift_function`` performs lifting of the function and commits the results to the function analysis
1942+
1943+
.. note:: Architecture subclasses should only implement this method if function-level analysis is required
1944+
1945+
:param LowLevelILFunction func: the function to analyze
1946+
:param FunctionLifterContext context: the lifting context
1947+
:return: True on success, False otherwise
1948+
"""
1949+
1950+
try:
1951+
return core.BNArchitectureDefaultLiftFunction(func.handle, context._handle)
1952+
except:
1953+
log_error_for_exception("Unhandled Python exception in Architecture.lift_function")
1954+
return False
1955+
18171956
def get_low_level_il_from_bytes(self, data: bytes, addr: int) -> 'lowlevelil.LowLevelILInstruction':
18181957
"""
18191958
``get_low_level_il_from_bytes`` converts the instruction in bytes to ``il`` at the given virtual address

python/log.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,10 +365,13 @@ def close_logs():
365365

366366

367367
class Logger:
368-
def __init__(self, session_id: int, logger_name: str):
368+
def __init__(self, session_id: int, logger_name: str, handle=None):
369369
self.session_id = session_id
370370
self.logger_name = logger_name
371-
self.handle = core.BNLogCreateLogger(logger_name, session_id)
371+
if handle:
372+
self.handle = handle
373+
else:
374+
self.handle = core.BNLogCreateLogger(logger_name, session_id)
372375

373376
def log(self, level: LogLevel, message: str) -> None:
374377
log(level, message, self.logger_name, self.session_id)

0 commit comments

Comments
 (0)