diff options
| -rw-r--r-- | binaryninjaapi.h | 10 | ||||
| -rw-r--r-- | binaryninjacore.h | 16 | ||||
| -rw-r--r-- | binaryviewtype.cpp | 25 | ||||
| -rw-r--r-- | python/binaryview.py | 46 | ||||
| -rw-r--r-- | suite/testcommon.py | 36 |
5 files changed, 132 insertions, 1 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index fb5e77fc..6f39b8d5 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1803,6 +1803,11 @@ __attribute__ ((format (printf, 1, 2))) class BinaryViewType: public StaticCoreRefCountObject<BNBinaryViewType> { + struct BinaryViewEvent + { + std::function<void(BinaryView*)> action; + }; + protected: std::string m_nameForRegister, m_longNameForRegister; @@ -1841,6 +1846,11 @@ __attribute__ ((format (printf, 1, 2))) virtual BinaryView* Parse(BinaryView* data) = 0; virtual bool IsTypeValidForData(BinaryView* data) = 0; virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) = 0; + + static void RegisterBinaryViewFinalizationEvent(const std::function<void(BinaryView* view)>& callback); + static void RegisterBinaryViewInitialAnalysisCompletionEvent(const std::function<void(BinaryView* view)>& callback); + + static void BinaryViewEventCallback(void* ctxt, BNBinaryView* view); }; class CoreBinaryViewType: public BinaryViewType diff --git a/binaryninjacore.h b/binaryninjacore.h index e878ccb5..6bdc68f4 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -2422,6 +2422,19 @@ extern "C" uint64_t start, end; }; + enum BNBinaryViewEventType + { + BinaryViewFinalizationEvent, + BinaryViewInitialAnalysisCompletionEvent + }; + + struct BNBinaryViewEvent + { + BNBinaryViewEventType type; + void (*callback)(void* ctx, BNBinaryView* view); + void* ctx; + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size); @@ -2790,6 +2803,9 @@ __attribute__ ((format (printf, 1, 2))) BNPlatform* platform); BINARYNINJACOREAPI BNPlatform* BNGetPlatformForViewType(BNBinaryViewType* type, uint32_t id, BNArchitecture* arch); + BINARYNINJACOREAPI void BNRegisterBinaryViewEvent(BNBinaryViewEventType type, + void (*callback)(void* ctx, BNBinaryView* view), void* ctx); + // Stream reader object BINARYNINJACOREAPI BNBinaryReader* BNCreateBinaryReader(BNBinaryView* view); BINARYNINJACOREAPI void BNFreeBinaryReader(BNBinaryReader* stream); diff --git a/binaryviewtype.cpp b/binaryviewtype.cpp index 8b0f8007..6691b820 100644 --- a/binaryviewtype.cpp +++ b/binaryviewtype.cpp @@ -219,6 +219,31 @@ bool BinaryViewType::IsDeprecated() return BNIsBinaryViewTypeDeprecated(m_object); } + +void BinaryViewType::RegisterBinaryViewFinalizationEvent(const function<void(BinaryView* view)>& callback) +{ + BinaryViewEvent* event = new BinaryViewEvent; + event->action = callback; + BNRegisterBinaryViewEvent(BinaryViewFinalizationEvent, BinaryViewEventCallback, event); +} + + +void BinaryViewType::RegisterBinaryViewInitialAnalysisCompletionEvent(const function<void(BinaryView* view)>& callback) +{ + BinaryViewEvent* event = new BinaryViewEvent; + event->action = callback; + BNRegisterBinaryViewEvent(BinaryViewInitialAnalysisCompletionEvent, BinaryViewEventCallback, event); +} + + +void BinaryViewType::BinaryViewEventCallback(void* ctxt, BNBinaryView* view) +{ + BinaryViewEvent* event = (BinaryViewEvent*)ctxt; + Ref<BinaryView> viewObject = new BinaryView(BNNewViewReference(view)); + event->action(viewObject); +} + + CoreBinaryViewType::CoreBinaryViewType(BNBinaryViewType* type): BinaryViewType(type) { } diff --git a/python/binaryview.py b/python/binaryview.py index 16127873..545fcc15 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -32,7 +32,8 @@ from collections import OrderedDict # Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType, - Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, TypeClass, SaveOption) + Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, + TypeClass, SaveOption, BinaryViewEventType) import binaryninja from binaryninja import associateddatastore # required for _BinaryViewAssociatedDataStore from binaryninja import log @@ -252,7 +253,42 @@ class AnalysisCompletionEvent(object): def view(self, value): self._view = value +# This has no functional purposes; +# we just need it to stop Python from prematurely freeing the object +_binaryview_events = {} +class BinaryViewEvent(object): + """ + The ``BinaryViewEvent`` object provides a mechanism for receiving callbacks when a BinaryView + is Finalized or the initial analysis is finished. The BinaryView finalized callbacks run before the + intialanalysis starts. The callbacks run one-after-another in the same order as they get registered. + It is a good place to modify the BinaryView to add extra information to it. + + The callback function receives a BinaryView as its parameter. It is possible to call + BinaryView.add_analysis_completion_event() on it to set up other callbacks for analysis completion. + + :Example: + >>> def callback(bv): + ... print('start: 0x%x' % bv.start) + ... + >>> BinaryViewEvent.add_binaryview_finalized_event(callback) + """ + @classmethod + def register(cls, event_type, callback): + callback_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._notify(view, callback)) + core.BNRegisterBinaryViewEvent(event_type, callback_obj, None) + global _binaryview_events + _binaryview_events[len(_binaryview_events)] = callback_obj + @classmethod + def _notify(cls, view, callback): + try: + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + callback(view_obj) + except: + binaryninja.log.log_error(traceback.format_exc()) + + class ActiveAnalysisInfo(object): def __init__(self, func, analysis_time, update_count, submit_count): self._func = func @@ -932,6 +968,14 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): return None return binaryninja.platform.Platform(handle = plat) + @staticmethod + def add_binaryview_finalized_event(callback): + BinaryViewEvent.register(BinaryViewEventType.BinaryViewFinalizationEvent, callback) + + @staticmethod + def add_binaryview_initial_analysis_completion_event(callback): + BinaryViewEvent.register(BinaryViewEventType.BinaryViewInitialAnalysisCompletionEvent, callback) + class Segment(object): def __init__(self, handle): diff --git a/suite/testcommon.py b/suite/testcommon.py index 34316a53..992a7961 100644 --- a/suite/testcommon.py +++ b/suite/testcommon.py @@ -1505,3 +1505,39 @@ class VerifyBuilder(Builder): assert(test_helper(binja.PossibleValueSet.in_set_of_values([1,2,3,4]))) assert(test_helper(binja.PossibleValueSet.not_in_set_of_values([1,2,3,4]))) return True + + def test_binaryview_callbacks(self): + """BinaryView finalized callback and analysis completion callback""" + file_name = self.unpackage_file("helloworld") + + # Currently, there is no way to unregister a BinaryView event callback. + # This boolean tells the callback function whether it should run or just return + callback_should_run = True + + def bv_finalized_callback(bv): + if callback_should_run: + bv.store_metadata('finalized', 'yes') + + def bv_finalized_callback_2(bv): + if callback_should_run: + bv.store_metadata('finalized_2', 'yes') + + def bv_analysis_completion_callback(bv): + if callback_should_run: + bv.store_metadata('analysis_completion', 'yes') + + BinaryViewType.add_binaryview_finalized_event(bv_finalized_callback) + BinaryViewType.add_binaryview_finalized_event(bv_finalized_callback_2) + BinaryViewType.add_binaryview_initial_analysis_completion_event(bv_analysis_completion_callback) + + try: + with binja.open_view(file_name) as bv: + finalized = bv.query_metadata('finalized') == 'yes' + finalized_2 = bv.query_metadata('finalized_2') == 'yes' + analysis_completion = bv.query_metadata('analysis_completion') == 'yes' + return finalized and finalized_2 and analysis_completion + + finally: + self.delete_package("helloworld") + callback_should_run = False + |
