From d15d5acf01ded806bea77963c816587e9fca5ed8 Mon Sep 17 00:00:00 2001 From: Bambu Date: Sat, 20 Aug 2016 09:46:46 -0400 Subject: Fixed small python bugs Found a couple of simple bugs with the __init__.py * mangledName is returned when it should be mangled_name * perform_decode() should return self.perform_encode() * indirect_branches() is redefined with the exact same code --- python/__init__.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index b88c5beb..0f57f651 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4342,16 +4342,6 @@ class Function(object): core.BNFreeAddressList(addrs) return result - @property - def indirect_branches(self): - count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranches(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) - core.BNFreeIndirectBranchList(branches) - return result - @property def low_level_il(self): """Function low level IL (read-only)""" @@ -8201,7 +8191,7 @@ class Transform: def perform_decode(self, data, params): if self.type == "InvertingTransform": - return perform_encode(data, params) + return self.perform_encode(data, params) return None def perform_encode(self, data, params): @@ -9852,7 +9842,7 @@ def demangle_ms(arch, mangled_name): for i in xrange(outSize.value): names.append(outName[i]) return (Type(handle), names) - return (None, mangledName) + return (None, mangled_name) _output_to_log = False def redirect_output_to_log(): -- cgit v1.3.1 From 923b0b1aa9df85844c6002e1ffbbea3ba47e2b04 Mon Sep 17 00:00:00 2001 From: plafosse Date: Mon, 22 Aug 2016 01:26:07 +0100 Subject: Updating documentation for _user_ functions --- python/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index 0f57f651..69858d2e 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -889,9 +889,11 @@ class BinaryView(object): externally. Additionanlly, methods which begin with ``perform_`` should not be called either and are used explicitly for subclassing the BinaryView. - Another important note is the ``*_user_*()`` methods. These methods are reserved for `undo-able` actions, and thus - under most circumstances shouldn't be called by plugins, rather methods with the same name without ``_user_`` should - be used (e.g. ``remove_function()`` rather than ``remove_user_function()``) + .. note:: An important note on the ``*_user_*()`` methods. Binary Ninja makes a distinction between edits \ + performed by the user and actions performed by auto analysis. Auto analysis actions that can quickly be recalculated \ + are not saved to the database. Auto analysis actions that take a long time and all user edits are stored in the \ + database (e.g. ``remove_user_function()`` rather than ``remove_function()``). Thus use ``_user_`` methods if saving \ + to the database is desired. """ name = None long_name = None -- cgit v1.3.1 From 90bfcd4d2ea8b03887975da798915638162f13cb Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 22 Aug 2016 23:50:16 -0400 Subject: Allow setting here or current_address to navigate in the current view --- python/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index 69858d2e..027c2a1f 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -9476,6 +9476,15 @@ class PythonScriptingInstance(ScriptingInstance): self.locals["current_selection"] = (self.active_selection_begin, self.active_selection_end) self.interpreter.runsource(code) + + if self.locals["here"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"]) + elif self.locals["current_address"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"]) + except: + traceback.print_exc() finally: PythonScriptingInstance._interpreter.value = None self.instance.input_ready_state = core.ReadyForScriptExecution -- cgit v1.3.1 From b3e607f82b97bf8fe5718e7634c0a037a8f103ac Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 23 Aug 2016 15:55:41 -0400 Subject: no longer use the beta channel name --- python/examples/version-switcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python') diff --git a/python/examples/version-switcher.py b/python/examples/version-switcher.py index a1936a52..087bf7f9 100644 --- a/python/examples/version-switcher.py +++ b/python/examples/version-switcher.py @@ -3,7 +3,7 @@ import sys import binaryninja import datetime -chandefault="private-beta" +chandefault="release" channel=0 versions=0 -- cgit v1.3.1 From 3c19753e3ec474d4c84110b9e6ab8ef1f1971fe2 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Wed, 24 Aug 2016 16:43:33 -0400 Subject: fix export to work on windows --- python/examples/export-svg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python') diff --git a/python/examples/export-svg.py b/python/examples/export-svg.py index 3800edf7..02a6d57e 100755 --- a/python/examples/export-svg.py +++ b/python/examples/export-svg.py @@ -14,7 +14,7 @@ def escape(string): return ''.join(escape_table.get(i,i) for i in string) #still escape the basics def save_svg(bv,function): - filename = bv.file.filename.split(os.sep)[-1] + filename = os.path.basename(bv.file.filename) address = hex(function.start).replace('L','') outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) content = render_svg(function) -- cgit v1.3.1 From e878199be4a74c880acd5d38c33965b47d7630d0 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Thu, 25 Aug 2016 20:42:33 -0400 Subject: Adding APIs for database save/load progress and constant references --- binaryninjaapi.h | 10 ++++++++++ binaryninjacore.h | 15 +++++++++++++++ binaryview.cpp | 13 +++++++++++++ filemetadata.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++ function.cpp | 13 +++++++++++++ python/__init__.py | 55 ++++++++++++++++++++++++++++++++++++++++++++---------- 6 files changed, 139 insertions(+), 10 deletions(-) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index f79e9d75..7c951dee 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -511,8 +511,14 @@ namespace BinaryNinja bool IsBackedByDatabase() const; bool CreateDatabase(const std::string& name, BinaryView* data); + bool CreateDatabase(const std::string& name, BinaryView* data, + const std::function& progressCallback); Ref OpenExistingDatabase(const std::string& path); + Ref OpenExistingDatabase(const std::string& path, + const std::function& progressCallback); bool SaveAutoSnapshot(BinaryView* data); + bool SaveAutoSnapshot(BinaryView* data, + const std::function& progressCallback); void BeginUndoActions(); void CommitUndoActions(); @@ -766,7 +772,10 @@ namespace BinaryNinja bool IsAnalysisChanged() const; bool IsBackedByDatabase() const; bool CreateDatabase(const std::string& path); + bool CreateDatabase(const std::string& path, + const std::function& progressCallback); bool SaveAutoSnapshot(); + bool SaveAutoSnapshot(const std::function& progressCallback); void BeginUndoActions(); void AddUndoAction(UndoAction* action); @@ -1626,6 +1635,7 @@ namespace BinaryNinja std::vector GetRegistersReadByInstruction(Architecture* arch, uint64_t addr); std::vector GetRegistersWrittenByInstruction(Architecture* arch, uint64_t addr); std::vector GetStackVariablesReferencedByInstruction(Architecture* arch, uint64_t addr); + std::vector GetConstantsReferencedByInstruction(Architecture* arch, uint64_t addr); Ref GetLiftedIL() const; size_t GetLiftedILForInstruction(Architecture* arch, uint64_t addr); diff --git a/binaryninjacore.h b/binaryninjacore.h index 2ba6f7e6..4d8c31d9 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1013,6 +1013,12 @@ extern "C" void (*addAction)(void* ctxt, BNMainThreadAction* action); }; + struct BNConstantReference + { + int64_t value; + size_t size; + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -1105,8 +1111,14 @@ extern "C" BINARYNINJACOREAPI bool BNIsBackedByDatabase(BNFileMetadata* file); BINARYNINJACOREAPI bool BNCreateDatabase(BNBinaryView* data, const char* path); + BINARYNINJACOREAPI bool BNCreateDatabaseWithProgress(BNBinaryView* data, const char* path, + void* ctxt, void (*progress)(void* ctxt, size_t progress, size_t total)); BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabase(BNFileMetadata* file, const char* path); + BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabaseWithProgress(BNFileMetadata* file, const char* path, + void* ctxt, void (*progress)(void* ctxt, size_t progress, size_t total)); BINARYNINJACOREAPI bool BNSaveAutoSnapshot(BNBinaryView* data); + BINARYNINJACOREAPI bool BNSaveAutoSnapshotWithProgress(BNBinaryView* data, void* ctxt, + void (*progress)(void* ctxt, size_t progress, size_t total)); BINARYNINJACOREAPI char* BNGetFilename(BNFileMetadata* file); BINARYNINJACOREAPI void BNSetFilename(BNFileMetadata* file, const char* name); @@ -1433,6 +1445,9 @@ extern "C" BINARYNINJACOREAPI BNStackVariableReference* BNGetStackVariablesReferencedByInstruction(BNFunction* func, BNArchitecture* arch, uint64_t addr, size_t* count); BINARYNINJACOREAPI void BNFreeStackVariableReferenceList(BNStackVariableReference* refs, size_t count); + BINARYNINJACOREAPI BNConstantReference* BNGetConstantsReferencedByInstruction(BNFunction* func, + BNArchitecture* arch, uint64_t addr, size_t* count); + BINARYNINJACOREAPI void BNFreeConstantReferenceList(BNConstantReference* refs); BINARYNINJACOREAPI BNLowLevelILFunction* BNGetFunctionLiftedIL(BNFunction* func); BINARYNINJACOREAPI size_t BNGetLiftedILForInstruction(BNFunction* func, BNArchitecture* arch, uint64_t addr); diff --git a/binaryview.cpp b/binaryview.cpp index cff3ed10..14393930 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -518,12 +518,25 @@ bool BinaryView::CreateDatabase(const string& path) } +bool BinaryView::CreateDatabase(const string& path, + const function& progressCallback) +{ + return m_file->CreateDatabase(path, this, progressCallback); +} + + bool BinaryView::SaveAutoSnapshot() { return m_file->SaveAutoSnapshot(this); } +bool BinaryView::SaveAutoSnapshot(const function& progressCallback) +{ + return m_file->SaveAutoSnapshot(this, progressCallback); +} + + void BinaryView::BeginUndoActions() { m_file->BeginUndoActions(); diff --git a/filemetadata.cpp b/filemetadata.cpp index 51e152bf..b3764167 100644 --- a/filemetadata.cpp +++ b/filemetadata.cpp @@ -25,6 +25,19 @@ using namespace Json; using namespace std; +struct DatabaseProgressCallbackContext +{ + std::function func; +}; + + +static void DatabaseProgressCallback(void* ctxt, size_t progress, size_t total) +{ + DatabaseProgressCallbackContext* cb = (DatabaseProgressCallbackContext*)ctxt; + cb->func(progress, total); +} + + char* NavigationHandler::GetCurrentViewCallback(void* ctxt) { NavigationHandler* handler = (NavigationHandler*)ctxt; @@ -246,6 +259,15 @@ bool FileMetadata::CreateDatabase(const string& name, BinaryView* data) } +bool FileMetadata::CreateDatabase(const string& name, BinaryView* data, + const function& progressCallback) +{ + DatabaseProgressCallbackContext cb; + cb.func = progressCallback; + return BNCreateDatabaseWithProgress(data->GetObject(), name.c_str(), &cb, DatabaseProgressCallback); +} + + Ref FileMetadata::OpenExistingDatabase(const string& path) { BNBinaryView* data = BNOpenExistingDatabase(m_object, path.c_str()); @@ -255,12 +277,33 @@ Ref FileMetadata::OpenExistingDatabase(const string& path) } +Ref FileMetadata::OpenExistingDatabase(const string& path, + const function& progressCallback) +{ + DatabaseProgressCallbackContext cb; + cb.func = progressCallback; + BNBinaryView* data = BNOpenExistingDatabaseWithProgress(m_object, path.c_str(), &cb, DatabaseProgressCallback); + if (!data) + return nullptr; + return new BinaryView(data); +} + + bool FileMetadata::SaveAutoSnapshot(BinaryView* data) { return BNSaveAutoSnapshot(data->GetObject()); } +bool FileMetadata::SaveAutoSnapshot(BinaryView* data, + const function& progressCallback) +{ + DatabaseProgressCallbackContext cb; + cb.func = progressCallback; + return BNSaveAutoSnapshotWithProgress(data->GetObject(), &cb, DatabaseProgressCallback); +} + + void FileMetadata::BeginUndoActions() { BNBeginUndoActions(m_object); diff --git a/function.cpp b/function.cpp index c5033276..27193992 100644 --- a/function.cpp +++ b/function.cpp @@ -288,6 +288,19 @@ vector Function::GetStackVariablesReferencedByInstructio } +vector Function::GetConstantsReferencedByInstruction(Architecture* arch, uint64_t addr) +{ + size_t count; + BNConstantReference* refs = BNGetConstantsReferencedByInstruction(m_object, arch->GetObject(), addr, &count); + + vector result; + result.insert(result.end(), &refs[0], &refs[count]); + + BNFreeConstantReferenceList(refs); + return result; +} + + Ref Function::GetLiftedIL() const { return new LowLevelILFunction(BNGetFunctionLiftedIL(m_object)); diff --git a/python/__init__.py b/python/__init__.py index 027c2a1f..092e07c0 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -372,18 +372,32 @@ class FileMetadata(object): def navigate(self, view, offset): return core.BNNavigate(self.handle, str(view), offset) - def create_database(self, filename): - - return core.BNCreateDatabase(self.raw.handle, str(filename)) + def create_database(self, filename, progress_func = None): + if progress_func is None: + return core.BNCreateDatabase(self.raw.handle, str(filename)) + else: + return core.BNCreateDatabaseWithProgress(self.raw.handle, str(filename), None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) - def open_existing_database(self, filename): - view = core.BNOpenExistingDatabase(self.handle, str(filename)) + def open_existing_database(self, filename, progress_func = None): + if progress_func is None: + view = core.BNOpenExistingDatabase(self.handle, str(filename)) + else: + view = core.BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) if view is None: return None return BinaryView(self, handle = view) - def save_auto_snapshot(self): - return core.BNSaveAutoSnapshot(self.raw.handle) + def save_auto_snapshot(self, progress_func = None): + if progress_func is None: + return core.BNSaveAutoSnapshot(self.raw.handle) + else: + return core.BNSaveAutoSnapshotWithProgress(self.raw.handle, None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) def get_view_of_type(self, name): view = core.BNGetFileViewOfType(self.handle, str(name)) @@ -1683,26 +1697,28 @@ class BinaryView(object): """ return core.LittleEndian - def create_database(self, filename): + def create_database(self, filename, progress_func = None): """ ``perform_get_database`` writes the current database (.bndb) file out to the specified file. :param str filename: path and filename to write the bndb to, this string `should` have ".bndb" appended to it. + :param callable() progress_func: optional function to be called with the current progress and total count. :return: true on success, false on failure :rtype: bool """ return self.file.create_database(filename) - def save_auto_snapshot(self): + def save_auto_snapshot(self, progress_func = None): """ ``save_auto_snapshot`` saves the current database to the already created file. .. note:: :py:method:`create_database` should have been called prior to executing this method + :param callable() progress_func: optional function to be called with the current progress and total count. :return: True if it successfully saved the snapshot, False otherwise :rtype: bool """ - return self.file.save_auto_snapshot() + return self.file.save_auto_snapshot(progress_func) def get_view_of_type(self, name): """ @@ -4248,6 +4264,16 @@ class StackVariableReference: return "" % (self.source_operand, self.name, self.referenced_offset) return "" % (self.source_operand, self.name) +class ConstantReference: + def __init__(self, val, size): + self.value = val + self.size = size + + def __repr__(self): + if self.size == 0: + return "" % self.value + return "" % (self.value, self.size) + class IndirectBranchInfo: def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): self.source_arch = source_arch @@ -4531,6 +4557,15 @@ class Function(object): core.BNFreeStackVariableReferenceList(refs, count.value) return result + def get_constants_referenced_by(self, arch, addr): + count = ctypes.c_ulonglong() + refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(ConstantReference(refs[i].value, refs[i].size)) + core.BNFreeConstantReferenceList(refs) + return result + def get_lifted_il_at(self, arch, addr): return core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) -- cgit v1.3.1 From 89eb0f36c13428cba18a5e40600aa2e1975e90d5 Mon Sep 17 00:00:00 2001 From: Bambu Date: Thu, 25 Aug 2016 22:54:38 -0400 Subject: Updated arm-syscall plugin to work with current api --- python/examples/README.md | 2 +- python/examples/arm-syscall.py | 18 -------------- python/examples/print_syscalls.py | 50 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 19 deletions(-) delete mode 100644 python/examples/arm-syscall.py create mode 100644 python/examples/print_syscalls.py (limited to 'python') diff --git a/python/examples/README.md b/python/examples/README.md index 7fd3ab6b..5f89496f 100644 --- a/python/examples/README.md +++ b/python/examples/README.md @@ -5,7 +5,7 @@ The following examples demonstrate some of the Binary Ninja API. They include bo ## Stand-alone * bin-info.py - general binary information -* arm-syscall.py - extract syscall numbers from IL for arm Mach-O files +* print_syscalls.py - extract syscall numbers from IL on specified file. Can be run both headless and in Binary Ninja * version-switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, like: diff --git a/python/examples/arm-syscall.py b/python/examples/arm-syscall.py deleted file mode 100644 index f94b8531..00000000 --- a/python/examples/arm-syscall.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -""" - Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: - http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ -""" -import sys, binaryninja, time -if len(sys.argv) > 1: - target = sys.argv[1] -else: - raise ValueError("Missing argument to binary.") - -bv = binaryninja.BinaryViewType["Mach-O"].open(target) -bv.update_analysis_and_wait() - -for func in bv.functions: - for il in func.low_level_il: - if il.operation == core.LLIL_SYSCALL: - print "System call address: %x - %d" % (il.address, func.get_reg_value_at_low_level_il_instruction(il.address, bv.platform.system_call_convention.int_arg_regs[0]).value) diff --git a/python/examples/print_syscalls.py b/python/examples/print_syscalls.py new file mode 100644 index 00000000..7e93356c --- /dev/null +++ b/python/examples/print_syscalls.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" + Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: + http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ +""" +import sys +from itertools import chain + +from binaryninja import BinaryView, core + + +def print_syscalls(bv): + """ Print Syscall numbers for a provided binaryview """ + + calling_convention = bv.platform.system_call_convention + if not calling_convention: + print('Error: No syscall convention available for {:s}'.format(bv.platform)) + return + + register = calling_convention.int_arg_regs[0] + + for func in bv.functions: + syscalls = (il for il in chain.from_iterable(func.low_level_il) + if il.operation == core.LLIL_SYSCALL) + for il in syscalls: + value = func.get_reg_value_at(bv.arch, il.address, register).value + print("System call address: {:#x} - {:d}".format(il.address, value)) + + +def main(): + if len(sys.argv) != 2: + print('Usage: {} '.format(sys.argv[0])) + return -1 + + target = sys.argv[1] + + bv = BinaryView.open(target) + view_type = next(bvt for bvt in bv.available_view_types if bvt.name != 'Raw') + if not view_type: + print('Error: Unable to get any other view type besides Raw') + return -1 + + bv = bv.file.get_view_of_type(view_type.name) + bv.update_analysis_and_wait() + + print_syscalls(bv) + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.3.1 From 827662fbb6caa9d0aa3089cafb20a578a68eaee7 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 30 Aug 2016 19:30:43 -0400 Subject: Add API to determine if a function still has analysis updates to complete --- binaryninjaapi.h | 1 + binaryninjacore.h | 1 + function.cpp | 6 ++++++ python/__init__.py | 5 +++++ 4 files changed, 13 insertions(+) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 7c951dee..f60ee159 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1611,6 +1611,7 @@ namespace BinaryNinja bool WasAutomaticallyDiscovered() const; bool CanReturn() const; bool HasExplicitlyDefinedType() const; + bool NeedsUpdate() const; std::vector> GetBasicBlocks() const; void MarkRecentUse(); diff --git a/binaryninjacore.h b/binaryninjacore.h index 4d8c31d9..c293f940 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1381,6 +1381,7 @@ extern "C" BINARYNINJACOREAPI void BNRemoveUserFunction(BNBinaryView* view, BNFunction* func); BINARYNINJACOREAPI void BNUpdateAnalysis(BNBinaryView* view); BINARYNINJACOREAPI void BNAbortAnalysis(BNBinaryView* view); + BINARYNINJACOREAPI bool BNIsFunctionUpdateNeeded(BNFunction* func); BINARYNINJACOREAPI BNFunction* BNNewFunctionReference(BNFunction* func); BINARYNINJACOREAPI void BNFreeFunction(BNFunction* func); diff --git a/function.cpp b/function.cpp index 27193992..78a33bda 100644 --- a/function.cpp +++ b/function.cpp @@ -72,6 +72,12 @@ bool Function::HasExplicitlyDefinedType() const } +bool Function::NeedsUpdate() const +{ + return BNIsFunctionUpdateNeeded(m_object); +} + + vector> Function::GetBasicBlocks() const { size_t count; diff --git a/python/__init__.py b/python/__init__.py index 092e07c0..e9f036ae 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4348,6 +4348,11 @@ class Function(object): """Whether function has explicitly defined types (read-only)""" return core.BNHasExplicitlyDefinedType(self.handle) + @property + def needs_update(self): + """Whether the function has analysis that needs to be updated (read-only)""" + return core.BNIsFunctionUpdateNeeded(self.handle) + @property def basic_blocks(self): """List of basic blocks (read-only)""" -- cgit v1.3.1 From 3978e18b09ca745fd08defb8f00fbece267beaf2 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 30 Aug 2016 21:59:40 -0400 Subject: Add API to reanalyze --- binaryninjaapi.h | 4 ++++ binaryninjacore.h | 3 +++ binaryview.cpp | 6 ++++++ function.cpp | 6 ++++++ python/__init__.py | 16 ++++++++++++++++ 5 files changed, 35 insertions(+) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index f60ee159..7c417623 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -919,6 +919,8 @@ namespace BinaryNinja void UndefineUserType(const std::string& name); bool FindNextData(uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = NoFindFlags); + + void Reanalyze(); }; class BinaryData: public BinaryView @@ -1672,6 +1674,8 @@ namespace BinaryNinja size_t operand); void SetIntegerConstantDisplayType(Architecture* arch, uint64_t instrAddr, uint64_t value, size_t operand, BNIntegerDisplayType type); + + void Reanalyze(); }; struct FunctionGraphEdge diff --git a/binaryninjacore.h b/binaryninjacore.h index c293f940..3aa0a1e6 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1565,6 +1565,9 @@ extern "C" BINARYNINJACOREAPI void BNUndefineAnalysisType(BNBinaryView* view, const char* name); BINARYNINJACOREAPI void BNUndefineUserAnalysisType(BNBinaryView* view, const char* name); + BINARYNINJACOREAPI void BNReanalyzeAllFunctions(BNBinaryView* view); + BINARYNINJACOREAPI void BNReanalyzeFunction(BNFunction* func); + // Disassembly settings BINARYNINJACOREAPI BNDisassemblySettings* BNCreateDisassemblySettings(void); BINARYNINJACOREAPI BNDisassemblySettings* BNNewDisassemblySettingsReference(BNDisassemblySettings* settings); diff --git a/binaryview.cpp b/binaryview.cpp index 14393930..6631f3d6 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -1440,6 +1440,12 @@ bool BinaryView::FindNextData(uint64_t start, const DataBuffer& data, uint64_t& } +void BinaryView::Reanalyze() +{ + BNReanalyzeAllFunctions(m_object); +} + + BinaryData::BinaryData(FileMetadata* file): BinaryView(BNCreateBinaryDataView(file->GetObject())) { } diff --git a/function.cpp b/function.cpp index 78a33bda..cbd9ec40 100644 --- a/function.cpp +++ b/function.cpp @@ -572,3 +572,9 @@ void Function::SetIntegerConstantDisplayType(Architecture* arch, uint64_t instrA { BNSetIntegerConstantDisplayType(m_object, arch->GetObject(), instrAddr, value, operand, type); } + + +void Function::Reanalyze() +{ + BNReanalyzeFunction(m_object); +} diff --git a/python/__init__.py b/python/__init__.py index e9f036ae..82aaba32 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -3231,6 +3231,14 @@ class BinaryView(object): return None return result.value + def reanalyze(self): + """ + ``reanalyze`` causes all functions to be reanalyzed. This function does not wait for the analysis to finish. + + :rtype: None + """ + core.BNReanalyzeAllFunctions(self.handle) + def __setattr__(self, name, value): try: object.__setattr__(self,name,value) @@ -4677,6 +4685,14 @@ class Function(object): display_type = core.BNIntegerDisplayType_by_name[display_type] core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) + def reanalyze(self): + """ + ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. + + :rtype: None + """ + core.BNReanalyzeFunction(self.handle) + class BasicBlockEdge: def __init__(self, branch_type, target, arch): self.type = core.BNBranchType_names[branch_type] -- cgit v1.3.1 From b84ab8966522eb5063e6a213ca1e48d32e0fd090 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Wed, 31 Aug 2016 00:13:54 -0400 Subject: Add API to enqueue work on the worker thread(s) --- binaryninjaapi.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ binaryninjaapi.h | 5 +++++ binaryninjacore.h | 4 ++++ python/__init__.py | 14 +++++++++--- 4 files changed, 82 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index f98dfef7..71a5a8f7 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -24,6 +24,12 @@ using namespace BinaryNinja; using namespace std; +struct WorkerThreadActionContext +{ + std::function action; +}; + + void BinaryNinja::InitCorePlugins() { BNInitCorePlugins(); @@ -138,3 +144,59 @@ void BinaryNinja::AddOptionalPluginDependency(const string& name) { BNAddOptionalPluginDependency(name.c_str()); } + + +static void WorkerActionCallback(void* ctxt) +{ + WorkerThreadActionContext* action = (WorkerThreadActionContext*)ctxt; + action->action(); + delete action; +} + + +void BinaryNinja::WorkerEnqueue(const function& action) +{ + WorkerThreadActionContext* ctxt = new WorkerThreadActionContext; + ctxt->action = action; + BNWorkerEnqueue(ctxt, WorkerActionCallback); +} + + +void BinaryNinja::WorkerEnqueue(RefCountObject* owner, const function& action) +{ + struct + { + Ref owner; + function func; + } context; + context.owner = owner; + context.func = action; + + WorkerEnqueue([=]() { + context.func(); + }); +} + + +void BinaryNinja::WorkerPriorityEnqueue(const function& action) +{ + WorkerThreadActionContext* ctxt = new WorkerThreadActionContext; + ctxt->action = action; + BNWorkerPriorityEnqueue(ctxt, WorkerActionCallback); +} + + +void BinaryNinja::WorkerPriorityEnqueue(RefCountObject* owner, const function& action) +{ + struct + { + Ref owner; + function func; + } context; + context.owner = owner; + context.func = action; + + WorkerPriorityEnqueue([=]() { + context.func(); + }); +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 7c417623..6902c1a3 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -371,6 +371,11 @@ namespace BinaryNinja Ref ExecuteOnMainThread(const std::function& action); void ExecuteOnMainThreadAndWait(const std::function& action); + void WorkerEnqueue(const std::function& action); + void WorkerEnqueue(RefCountObject* owner, const std::function& action); + void WorkerPriorityEnqueue(const std::function& action); + void WorkerPriorityEnqueue(RefCountObject* owner, const std::function& action); + class DataBuffer { BNDataBuffer* m_buffer; diff --git a/binaryninjacore.h b/binaryninjacore.h index 3aa0a1e6..a24ce9fa 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1959,6 +1959,10 @@ extern "C" BINARYNINJACOREAPI BNMainThreadAction* BNExecuteOnMainThread(void* ctxt, void (*func)(void* ctxt)); BINARYNINJACOREAPI void BNExecuteOnMainThreadAndWait(void* ctxt, void (*func)(void* ctxt)); + // Worker thread queue management + BINARYNINJACOREAPI void BNWorkerEnqueue(void* ctxt, void (*action)(void* ctxt)); + BINARYNINJACOREAPI void BNWorkerPriorityEnqueue(void* ctxt, void (*action)(void* ctxt)); + #ifdef __cplusplus } #endif diff --git a/python/__init__.py b/python/__init__.py index 82aaba32..fa8ff02c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -9916,7 +9916,7 @@ def redirect_output_to_log(): global _output_to_log _output_to_log = True -class _MainThreadActionContext: +class _ThreadActionContext: _actions = [] def __init__(self, func): @@ -9939,16 +9939,24 @@ class _MainThreadActionContext: self.__class__._actions.remove(self) def execute_on_main_thread(func): - action = _MainThreadActionContext(func) + action = _ThreadActionContext(func) obj = core.BNExecuteOnMainThread(0, action.callback) if obj: return MainThreadAction(obj) return None def execute_on_main_thread_and_wait(func): - action = _MainThreadActionContext(func) + action = _ThreadActionContext(func) core.BNExecuteOnMainThreadAndWait(0, action.callback) +def worker_enqueue(func): + action = _ThreadActionContext(func) + core.BNWorkerEnqueue(0, action.callback) + +def worker_priority_enqueue(func): + action = _ThreadActionContext(func) + core.BNWorkerEnqueue(0, action.callback) + bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() -- cgit v1.3.1 From 548b906badf83e500551522a87242fefbc582d2c Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Wed, 31 Aug 2016 02:33:48 -0400 Subject: fix table of branch types --- python/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index fa8ff02c..b8a87aff 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -6046,9 +6046,9 @@ class Architecture(object): :py:Class:`InstructionInfo` object. The InstructionInfo object should have the length of the current instruction. If the instruction is a branch instruction the method should add a branch of the proper type: - ===================== ================================================= + ===================== =================================================== BranchType Description - ===================== ================================================= + ===================== =================================================== UnconditionalBranch Branch will alwasy be taken FalseBranch False branch condition TrueBranch True branch condition @@ -6057,7 +6057,7 @@ class Architecture(object): SystemCall System call instruction IndirectBranch Branch destination is a memory address or register UnresolvedBranch Call instruction that isn't - ===================== ================================================== + ===================== =================================================== :param str data: bytes to decode :param int addr: virtual address of the byte to be decoded -- cgit v1.3.1 From 75f597bb8fcd58315537f1dcd9802bbaadcdbd36 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Wed, 31 Aug 2016 18:06:31 -0400 Subject: Add API to determine if an address is backed by the file --- binaryninjaapi.h | 3 +++ binaryninjacore.h | 2 ++ binaryview.cpp | 20 ++++++++++++++++++++ python/__init__.py | 39 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 61 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index fed1bbd9..342dcc48 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -730,6 +730,7 @@ namespace BinaryNinja virtual bool PerformIsOffsetReadable(uint64_t offset); virtual bool PerformIsOffsetWritable(uint64_t offset); virtual bool PerformIsOffsetExecutable(uint64_t offset); + virtual bool PerformIsOffsetBackedByFile(uint64_t offset); virtual uint64_t PerformGetNextValidOffset(uint64_t offset); virtual uint64_t PerformGetStart() const { return 0; } virtual uint64_t PerformGetLength() const { return 0; } @@ -756,6 +757,7 @@ namespace BinaryNinja static bool IsOffsetReadableCallback(void* ctxt, uint64_t offset); static bool IsOffsetWritableCallback(void* ctxt, uint64_t offset); static bool IsOffsetExecutableCallback(void* ctxt, uint64_t offset); + static bool IsOffsetBackedByFileCallback(void* ctxt, uint64_t offset); static uint64_t GetNextValidOffsetCallback(void* ctxt, uint64_t offset); static uint64_t GetStartCallback(void* ctxt); static uint64_t GetLengthCallback(void* ctxt); @@ -811,6 +813,7 @@ namespace BinaryNinja bool IsOffsetReadable(uint64_t offset) const; bool IsOffsetWritable(uint64_t offset) const; bool IsOffsetExecutable(uint64_t offset) const; + bool IsOffsetBackedByFile(uint64_t offset) const; uint64_t GetNextValidOffset(uint64_t offset) const; uint64_t GetStart() const; diff --git a/binaryninjacore.h b/binaryninjacore.h index a24ce9fa..6eff4d46 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -637,6 +637,7 @@ extern "C" bool (*isOffsetReadable)(void* ctxt, uint64_t offset); bool (*isOffsetWritable)(void* ctxt, uint64_t offset); bool (*isOffsetExecutable)(void* ctxt, uint64_t offset); + bool (*isOffsetBackedByFile)(void* ctxt, uint64_t offset); uint64_t (*getNextValidOffset)(void* ctxt, uint64_t offset); uint64_t (*getStart)(void* ctxt); uint64_t (*getLength)(void* ctxt); @@ -1164,6 +1165,7 @@ extern "C" BINARYNINJACOREAPI bool BNIsOffsetReadable(BNBinaryView* view, uint64_t offset); BINARYNINJACOREAPI bool BNIsOffsetWritable(BNBinaryView* view, uint64_t offset); BINARYNINJACOREAPI bool BNIsOffsetExecutable(BNBinaryView* view, uint64_t offset); + BINARYNINJACOREAPI bool BNIsOffsetBackedByFile(BNBinaryView* view, uint64_t offset); BINARYNINJACOREAPI uint64_t BNGetNextValidOffset(BNBinaryView* view, uint64_t offset); BINARYNINJACOREAPI uint64_t BNGetStartOffset(BNBinaryView* view); BINARYNINJACOREAPI uint64_t BNGetEndOffset(BNBinaryView* view); diff --git a/binaryview.cpp b/binaryview.cpp index 6631f3d6..1c03eca5 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -258,6 +258,7 @@ BinaryView::BinaryView(const std::string& typeName, FileMetadata* file) view.isOffsetReadable = IsOffsetReadableCallback; view.isOffsetWritable = IsOffsetWritableCallback; view.isOffsetExecutable = IsOffsetExecutableCallback; + view.isOffsetBackedByFile = IsOffsetBackedByFileCallback; view.getNextValidOffset = GetNextValidOffsetCallback; view.getStart = GetStartCallback; view.getLength = GetLengthCallback; @@ -357,6 +358,13 @@ bool BinaryView::IsOffsetExecutableCallback(void* ctxt, uint64_t offset) } +bool BinaryView::IsOffsetBackedByFileCallback(void* ctxt, uint64_t offset) +{ + BinaryView* view = (BinaryView*)ctxt; + return view->PerformIsOffsetBackedByFile(offset); +} + + uint64_t BinaryView::GetNextValidOffsetCallback(void* ctxt, uint64_t offset) { BinaryView* view = (BinaryView*)ctxt; @@ -439,6 +447,12 @@ bool BinaryView::PerformIsOffsetExecutable(uint64_t offset) } +bool BinaryView::PerformIsOffsetBackedByFile(uint64_t offset) +{ + return PerformIsValidOffset(offset); +} + + uint64_t BinaryView::PerformGetNextValidOffset(uint64_t offset) { if (offset < PerformGetStart()) @@ -696,6 +710,12 @@ bool BinaryView::IsOffsetExecutable(uint64_t offset) const } +bool BinaryView::IsOffsetBackedByFile(uint64_t offset) const +{ + return BNIsOffsetBackedByFile(m_object, offset); +} + + uint64_t BinaryView::GetNextValidOffset(uint64_t offset) const { return BNGetNextValidOffset(m_object, offset); diff --git a/python/__init__.py b/python/__init__.py index b8a87aff..d199385a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -945,6 +945,7 @@ class BinaryView(object): self._cb.isOffsetReadable = self._cb.isOffsetReadable.__class__(self._is_offset_readable) self._cb.isOffsetWritable = self._cb.isOffsetWritable.__class__(self._is_offset_writable) self._cb.isOffsetExecutable = self._cb.isOffsetExecutable.__class__(self._is_offset_executable) + self._cb.isOffsetBackedByFile = self._cb.isOffsetExecutable.__class__(self._is_offset_backed_by_file) self._cb.getNextValidOffset = self._cb.getNextValidOffset.__class__(self._get_next_valid_offset) self._cb.getStart = self._cb.getStart.__class__(self._get_start) self._cb.getLength = self._cb.getLength.__class__(self._get_length) @@ -1389,6 +1390,13 @@ class BinaryView(object): log_error(traceback.format_exc()) return False + def _is_offset_backed_by_file(self, ctxt, offset): + try: + return self.perform_is_offset_backed_by_file(offset) + except: + log_error(traceback.format_exc()) + return False + def _get_next_valid_offset(self, ctxt, offset): try: return self.perform_get_next_valid_offset(offset) @@ -1632,6 +1640,20 @@ class BinaryView(object): """ return self.is_valid_offset(addr) + def perform_is_offset_backed_by_file(self, addr): + """ + ``perform_is_offset_backed_by_file`` implements a check if a virtual address ``addr`` is backed by the file + contents. + + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is backed by the file, false if the virtual address is initialized by \ + the loader + :rtype: int + """ + return self.is_valid_offset(addr) + def perform_get_next_valid_offset(self, addr): """ ``perform_get_next_valid_offset`` implements a query for the next valid readable, writable, or executable virtual @@ -1933,7 +1955,7 @@ class BinaryView(object): def is_offset_readable(self, addr): """ - ``is_valid_offset`` checks if an virtual address ``addr`` is valid for reading. + ``is_offset_readable`` checks if an virtual address ``addr`` is valid for reading. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for reading, false if the virtual address is invalid or error @@ -1943,7 +1965,7 @@ class BinaryView(object): def is_offset_writable(self, addr): """ - ``is_valid_offset`` checks if an virtual address ``addr`` is valid for writing. + ``is_offset_writable`` checks if an virtual address ``addr`` is valid for writing. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error @@ -1953,7 +1975,7 @@ class BinaryView(object): def is_offset_executable(self, addr): """ - ``is_valid_offset`` checks if an virtual address ``addr`` is valid for executing. + ``is_offset_executable`` checks if an virtual address ``addr`` is valid for executing. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for executing, false if the virtual address is invalid or error @@ -1961,6 +1983,17 @@ class BinaryView(object): """ return core.BNIsOffsetExecutable(self.handle, addr) + def is_offset_backed_by_file(self, addr): + """ + ``is_offset_backed_by_file`` checks if an virtual address ``addr`` is backed by the file contents. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is backed by the file, false if the virtual address is initialized by \ + the loader + :rtype: bool + """ + return core.BNIsOffsetBackedByFile(self.handle, addr) + def save(self, dest): """ ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. -- cgit v1.3.1 From 50cafb19ae59ba48fea2e7bd0c79a2e9473c2c86 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Sat, 3 Sep 2016 01:32:35 -0400 Subject: Adding APIs to ensure IL is available for upcoming caching methods --- binaryninjaapi.h | 21 ++++++++++++++++ binaryninjacore.h | 3 +++ function.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ python/__init__.py | 38 ++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 342dcc48..72e13567 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1611,8 +1611,11 @@ namespace BinaryNinja class Function: public CoreRefCountObject { + int m_advancedAnalysisRequests; + public: Function(BNFunction* func); + virtual ~Function(); Ref GetArchitecture() const; Ref GetPlatform() const; @@ -1684,6 +1687,24 @@ namespace BinaryNinja BNIntegerDisplayType type); void Reanalyze(); + + void RequestAdvancedAnalysisData(); + void ReleaseAdvancedAnalysisData(); + void ReleaseAdvancedAnalysisData(size_t count); + }; + + class AdvancedFunctionAnalysisDataRequestor + { + Ref m_func; + + public: + AdvancedFunctionAnalysisDataRequestor(Function* func = nullptr); + AdvancedFunctionAnalysisDataRequestor(const AdvancedFunctionAnalysisDataRequestor& req); + ~AdvancedFunctionAnalysisDataRequestor(); + AdvancedFunctionAnalysisDataRequestor& operator=(const AdvancedFunctionAnalysisDataRequestor& req); + + Ref GetFunction() { return m_func; } + void SetFunction(Function* func); }; struct FunctionGraphEdge diff --git a/binaryninjacore.h b/binaryninjacore.h index 6eff4d46..0c2738cb 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1384,6 +1384,9 @@ extern "C" BINARYNINJACOREAPI void BNUpdateAnalysis(BNBinaryView* view); BINARYNINJACOREAPI void BNAbortAnalysis(BNBinaryView* view); BINARYNINJACOREAPI bool BNIsFunctionUpdateNeeded(BNFunction* func); + BINARYNINJACOREAPI void BNRequestAdvancedFunctionAnalysisData(BNFunction* func); + BINARYNINJACOREAPI void BNReleaseAdvancedFunctionAnalysisData(BNFunction* func); + BINARYNINJACOREAPI void BNReleaseAdvancedFunctionAnalysisDataMultiple(BNFunction* func, size_t count); BINARYNINJACOREAPI BNFunction* BNNewFunctionReference(BNFunction* func); BINARYNINJACOREAPI void BNFreeFunction(BNFunction* func); diff --git a/function.cpp b/function.cpp index cbd9ec40..de12918f 100644 --- a/function.cpp +++ b/function.cpp @@ -27,6 +27,14 @@ using namespace std; Function::Function(BNFunction* func) { m_object = func; + m_advancedAnalysisRequests = 0; +} + + +Function::~Function() +{ + if (m_advancedAnalysisRequests > 0) + BNReleaseAdvancedFunctionAnalysisDataMultiple(m_object, (size_t)m_advancedAnalysisRequests); } @@ -578,3 +586,67 @@ void Function::Reanalyze() { BNReanalyzeFunction(m_object); } + + +void Function::RequestAdvancedAnalysisData() +{ + BNRequestAdvancedFunctionAnalysisData(m_object); +#ifdef WIN32 + InterlockedIncrement((LONG*)&m_advancedAnalysisRequests); +#else + __sync_fetch_and_add(&m_advancedAnalysisRequests, 1); +#endif +} + + +void Function::ReleaseAdvancedAnalysisData() +{ + BNReleaseAdvancedFunctionAnalysisData(m_object); +#ifdef WIN32 + InterlockedDecrement((LONG*)&m_advancedAnalysisRequests); +#else + __sync_fetch_and_add(&m_advancedAnalysisRequests, -1); +#endif +} + + +AdvancedFunctionAnalysisDataRequestor::AdvancedFunctionAnalysisDataRequestor(Function* func): m_func(func) +{ + if (m_func) + m_func->RequestAdvancedAnalysisData(); +} + + +AdvancedFunctionAnalysisDataRequestor::AdvancedFunctionAnalysisDataRequestor(const AdvancedFunctionAnalysisDataRequestor& req) +{ + m_func = req.m_func; + if (m_func) + m_func->RequestAdvancedAnalysisData(); +} + + +AdvancedFunctionAnalysisDataRequestor::~AdvancedFunctionAnalysisDataRequestor() +{ + if (m_func) + m_func->ReleaseAdvancedAnalysisData(); +} + + +AdvancedFunctionAnalysisDataRequestor& AdvancedFunctionAnalysisDataRequestor::operator=( + const AdvancedFunctionAnalysisDataRequestor& req) +{ + SetFunction(req.m_func); + return *this; +} + + +void AdvancedFunctionAnalysisDataRequestor::SetFunction(Function* func) +{ + if (m_func) + m_func->ReleaseAdvancedAnalysisData(); + + m_func = func; + + if (m_func) + m_func->RequestAdvancedAnalysisData(); +} diff --git a/python/__init__.py b/python/__init__.py index d199385a..cd559fe5 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4330,8 +4330,11 @@ class Function(object): def __init__(self, view, handle): self._view = view self.handle = core.handle_of_type(handle, core.BNFunction) + self._advanced_analysis_requests = 0 def __del__(self): + if self._advanced_analysis_requests > 0: + core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) core.BNFreeFunction(self.handle) @property @@ -4726,6 +4729,41 @@ class Function(object): """ core.BNReanalyzeFunction(self.handle) + def request_advanced_analysis_data(self): + core.BNRequestAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests += 1 + + def release_advanced_analysis_data(self): + core.BNReleaseAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests -= 1 + +class AdvancedFunctionAnalysisDataRequestor(object): + def __init__(self, func = None): + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() + + def __del__(self): + if self._function is not None: + self._function.release_advanced_analysis_data() + + @property + def function(self): + return self._function + + @function.setter + def function(self, func): + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() + + def close(self): + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = None + class BasicBlockEdge: def __init__(self, branch_type, target, arch): self.type = core.BNBranchType_names[branch_type] -- cgit v1.3.1 From bd104cc1d88cc1cd56dc71e54032fe34168b4cf0 Mon Sep 17 00:00:00 2001 From: plafosse Date: Mon, 5 Sep 2016 17:15:15 +0100 Subject: Adding a bunch of fixes found with linter --- python/__init__.py | 501 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 286 insertions(+), 215 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index cd559fe5..f32fa612 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,7 +19,14 @@ # IN THE SOFTWARE. import _binaryninjacore as core -import ctypes, traceback, json, struct, threading, code, sys +import abc +import ctypes +import traceback +import json +import struct +import threading +import code +import sys _plugin_init = False def _init_plugins(): @@ -31,11 +38,11 @@ def _init_plugins(): if not core.BNIsLicenseValidated(): raise RuntimeError, "License is not valid. Please supply a valid license." -class DataBuffer: - def __init__(self, contents = "", handle = None): +class DataBuffer(object): + def __init__(self, contents="", handle=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNDataBuffer) - elif (type(contents) is int) or (type(contents) is long): + elif isinstance(contents, int) or isinstance(contents, long): self.handle = core.BNCreateDataBuffer(None, contents) elif isinstance(contents, DataBuffer): self.handle = core.BNDuplicateDataBuffer(contents.handle) @@ -123,7 +130,7 @@ class DataBuffer: return core.BNDataBufferToEscapedString(self.handle) def unescape(self): - return DataBuffer(handle = core.BNDecodeEscapedString(str(self))) + return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) def base64_encode(self): return core.BNDataBufferToBase64(self.handle) @@ -193,7 +200,7 @@ class FileMetadata(object): self.handle = core.BNCreateFileMetadata() if filename is not None: core.BNSetFilename(self.handle, str(filename)) - self.__dict__['navigation'] = None + self.nav = None def __del__(self): if self.navigation is not None: @@ -268,6 +275,15 @@ class FileMetadata(object): else: core.BNMarkFileModified(self.handle) + @property + def navigation(self): + return self.nav + + @navigation.setter + def navigation(self, value): + value._register(self.handle) + self.nav = value + def close(self): """ Closes the underlying file handle. It is recommended that this is done in a @@ -411,14 +427,10 @@ class FileMetadata(object): return BinaryView(self, handle = view) def __setattr__(self, name, value): - if name == "navigation": - value._register(self.handle) - self.__dict__["navigation"] = value - else: - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name class FileAccessor: def __init__(self): @@ -526,7 +538,7 @@ class UndoAction: raise TypeError, "undo action type not registered" action_type = self.__class__.action_type if isinstance(action_type, str): - self._cb.type = BNActionType_by_name[action_type] + self._cb.type = core.BNActionType_by_name[action_type] else: self._cb.type = action_type self._cb.context = 0 @@ -583,7 +595,7 @@ class UndoAction: log_error(traceback.format_exc()) return "null" -class StringReference: +class StringReference(object): def __init__(self, string_type, start, length): self.type = string_type self.start = start @@ -592,7 +604,7 @@ class StringReference: def __repr__(self): return "<%s: %#x, len %#x>" % (self.type, self.start, self.length) -class BinaryDataNotificationCallbacks: +class BinaryDataNotificationCallbacks(object): def __init__(self, view, notify): self.view = view self.notify = notify @@ -619,7 +631,7 @@ class BinaryDataNotificationCallbacks: def _data_written(self, ctxt, view, offset, length): try: self.notify.data_written(self.view, offset, length) - except: + except OSError: log_error(traceback.format_exc()) def _data_inserted(self, ctxt, view, offset, length): @@ -723,7 +735,7 @@ class _BinaryViewTypeMetaclass(type): def __setattr__(self, name, value): try: - type.__setattr__(self,name,value) + type.__setattr__(self, name, value) except AttributeError: raise AttributeError, "attribute '%s' is read only" % name @@ -784,7 +796,7 @@ class BinaryViewType(object): def __setattr__(self, name, value): try: - object.__setattr__(self,name,value) + object.__setattr__(self, name, value) except AttributeError: raise AttributeError, "attribute '%s' is read only" % name @@ -831,8 +843,8 @@ class LinearDisassemblyPosition(object): """ ``class LinearDisassemblyPosition`` is a helper object containing the position of the current Linear Disassembly. - .. note:: This object should not be instantiated directly. Rather call :py:method:`get_linear_disassembly_position_at` \ - which instantiates this object. + .. note:: This object should not be instantiated directly. Rather call \ + :py:method:`get_linear_disassembly_position_at` which instantiates this object. """ def __init__(self, func, block, addr): self.function = func @@ -945,7 +957,6 @@ class BinaryView(object): self._cb.isOffsetReadable = self._cb.isOffsetReadable.__class__(self._is_offset_readable) self._cb.isOffsetWritable = self._cb.isOffsetWritable.__class__(self._is_offset_writable) self._cb.isOffsetExecutable = self._cb.isOffsetExecutable.__class__(self._is_offset_executable) - self._cb.isOffsetBackedByFile = self._cb.isOffsetExecutable.__class__(self._is_offset_backed_by_file) self._cb.getNextValidOffset = self._cb.getNextValidOffset.__class__(self._get_next_valid_offset) self._cb.getStart = self._cb.getStart.__class__(self._get_start) self._cb.getLength = self._cb.getLength.__class__(self._get_length) @@ -1390,13 +1401,6 @@ class BinaryView(object): log_error(traceback.format_exc()) return False - def _is_offset_backed_by_file(self, ctxt, offset): - try: - return self.perform_is_offset_backed_by_file(offset) - except: - log_error(traceback.format_exc()) - return False - def _get_next_valid_offset(self, ctxt, offset): try: return self.perform_get_next_valid_offset(offset) @@ -1506,6 +1510,19 @@ class BinaryView(object): return None return ''.join(str(a) for a in txt).strip() + @abc.abstractmethod + def perform_save(self, accessor): + raise NotImplementedError + + @abc.abstractmethod + def perform_get_address_size(self): + raise NotImplementedError + + @abc.abstractmethod + def perform_get_length(self): + raise NotImplementedError + + @abc.abstractmethod def perform_read(self, addr, length): """ ``perform_read`` implements a mapping between a virtual address and an absolute file offset, reading @@ -1520,8 +1537,9 @@ class BinaryView(object): :return: length bytes read from addr, should return empty string on error :rtype: int """ - return "" + raise NotImplementedError + @abc.abstractmethod def perform_write(self, addr, data): """ ``perform_write`` implements a mapping between a virtual address and an absolute file offset, writing @@ -1536,8 +1554,9 @@ class BinaryView(object): :return: length of data written, should return 0 on error :rtype: int """ - return 0 + raise NotImplementedError + @abc.abstractmethod def perform_insert(self, addr, data): """ ``perform_insert`` implements a mapping between a virtual address and an absolute file offset, inserting @@ -1552,8 +1571,9 @@ class BinaryView(object): :return: length of data inserted, should return 0 on error :rtype: int """ - return 0 + raise NotImplementedError + @abc.abstractmethod def perform_remove(self, addr, length): """ ``perform_remove`` implements a mapping between a virtual address and an absolute file offset, removing @@ -1568,8 +1588,9 @@ class BinaryView(object): :return: length of data removed, should return 0 on error :rtype: int """ - return 0 + raise NotImplementedError + @abc.abstractmethod def perform_get_modification(self, addr): """ ``perform_get_modification`` implements query to the whether the virtual address ``addr`` is modified. @@ -1583,6 +1604,7 @@ class BinaryView(object): """ return core.Original + @abc.abstractmethod def perform_is_valid_offset(self, addr): """ ``perform_is_valid_offset`` implements a check if an virtual address ``addr`` is valid. @@ -1598,6 +1620,7 @@ class BinaryView(object): data = self.read(addr, 1) return (data is not None) and (len(data) == 1) + @abc.abstractmethod def perform_is_offset_readable(self, offset): """ ``perform_is_offset_readable`` implements a check if an virtual address is readable. @@ -1612,6 +1635,7 @@ class BinaryView(object): """ return self.is_valid_offset(offset) + @abc.abstractmethod def perform_is_offset_writable(self, addr): """ ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is writable. @@ -1626,6 +1650,7 @@ class BinaryView(object): """ return self.is_valid_offset(addr) + @abc.abstractmethod def perform_is_offset_executable(self, addr): """ ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is executable. @@ -1640,20 +1665,7 @@ class BinaryView(object): """ return self.is_valid_offset(addr) - def perform_is_offset_backed_by_file(self, addr): - """ - ``perform_is_offset_backed_by_file`` implements a check if a virtual address ``addr`` is backed by the file - contents. - - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to be checked - :return: true if the virtual address is backed by the file, false if the virtual address is initialized by \ - the loader - :rtype: int - """ - return self.is_valid_offset(addr) - + @abc.abstractmethod def perform_get_next_valid_offset(self, addr): """ ``perform_get_next_valid_offset`` implements a query for the next valid readable, writable, or executable virtual @@ -1670,6 +1682,7 @@ class BinaryView(object): return self.perform_get_start() return addr + @abc.abstractmethod def perform_get_start(self): """ ``perform_get_start`` implements a query for the first readable, writable, or executable virtual address in @@ -1683,6 +1696,7 @@ class BinaryView(object): """ return 0 + @abc.abstractmethod def perform_get_entry_point(self): """ ``perform_get_entry_point`` implements a query for the initial entry point for code execution. @@ -1695,6 +1709,7 @@ class BinaryView(object): """ return 0 + @abc.abstractmethod def perform_is_executable(self): """ ``perform_is_executable`` implements a check which returns true if the BinaryView is executable. @@ -1705,13 +1720,14 @@ class BinaryView(object): :return: true if the current BinaryView is executable, false if it is not executable or on error :rtype: bool """ - return False + raise NotImplementedError + @abc.abstractmethod def perform_get_default_endianness(self): """ ``perform_get_default_endianness`` implements a check which returns true if the BinaryView is executable. - .. note:: This method **must** be implemented for custom BinaryViews that are not LittleEndian. + .. note:: This method **may** be implemented for custom BinaryViews that are not LittleEndian. .. warning:: This method **must not** be called directly. :return: either ``core.LittleEndian`` or ``core.BigEndian`` @@ -1728,7 +1744,7 @@ class BinaryView(object): :return: true on success, false on failure :rtype: bool """ - return self.file.create_database(filename) + return self.file.create_database(filename, progress_func) def save_auto_snapshot(self, progress_func = None): """ @@ -1940,7 +1956,7 @@ class BinaryView(object): if length is None: return core.BNGetModification(self.handle, addr) data = (core.BNModificationStatus * length)() - length = core.BNGetModificationArray(self.handle, addr, data, length); + length = core.BNGetModificationArray(self.handle, addr, data, length) return data[0:length] def is_valid_offset(self, addr): @@ -1983,17 +1999,6 @@ class BinaryView(object): """ return core.BNIsOffsetExecutable(self.handle, addr) - def is_offset_backed_by_file(self, addr): - """ - ``is_offset_backed_by_file`` checks if an virtual address ``addr`` is backed by the file contents. - - :param int addr: a virtual address to be checked - :return: true if the virtual address is backed by the file, false if the virtual address is initialized by \ - the loader - :rtype: bool - """ - return core.BNIsOffsetBackedByFile(self.handle, addr) - def save(self, dest): """ ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. @@ -3898,7 +3903,7 @@ class Type(object): @property def type_class(self): """Type class (read-only)""" - return BNTypeClass_names[core.BNGetTypeClass(self.handle)] + return core.BNTypeClass_names[core.BNGetTypeClass(self.handle)] @property def width(self): @@ -4028,8 +4033,12 @@ class Type(object): return Type(core.BNCreateFloatType(width)) @classmethod - def structure_type(self, s): - return Type(core.BNCreateStructureType(s.handle)) + def structure_type(self, structure_type): + return Type(core.BNCreateStructureType(structure_type.handle)) + + @classmethod + def unknown_type(self, unknown_type): + return Type(core.BNCreateUnknownType(unknown_type.handle)) @classmethod def enumeration_type(self, arch, e, width = None): @@ -4050,7 +4059,7 @@ class Type(object): param_buf = (core.BNNameAndType * len(params))() for i in xrange(0, len(params)): if isinstance(params[i], Type): - param_buf[i].name = ""; + param_buf[i].name = "" param_buf[i].type = params[i].handle else: param_buf[i].name = params[i][1] @@ -4066,7 +4075,32 @@ class Type(object): except AttributeError: raise AttributeError, "attribute '%s' is read only" % name -class StructureMember: + +class UnknownType(object): + def __init__(self, handle = None): + if handle is None: + self.handle = core.BNCreateUnknownType() + else: + self.handle = handle + + def __del__(self): + core.BNFreeUnknownType(self.handle) + + @property + def name(self): + count = ctypes.c_ulonglong() + nameList = core.BNGetUnknownTypeName(self.handle, count) + result = [] + for i in xrange(count.value): + result.append(nameList[i]) + return get_qualified_name(result) + + @name.setter + def name(self, value): + core.BNSetUnknownTypeName(self.handle, value) + + +class StructureMember(object): def __init__(self, t, name, offset): self.type = t self.name = name @@ -4090,7 +4124,12 @@ class Structure(object): @property def name(self): - return core.BNGetStructureName(self.handle) + count = ctypes.c_ulonglong() + nameList = core.BNGetStructureName(self.handle, count) + result = [] + for i in xrange(count.value): + result.append(nameList[i]) + return get_qualified_name(result) @name.setter def name(self, value): @@ -4154,7 +4193,7 @@ class Structure(object): def remove(self, i): core.BNRemoveStructureMember(self.handle, i) -class EnumerationMember: +class EnumerationMember(object): def __init__(self, name, value, default): self.name = name self.value = value @@ -4209,7 +4248,7 @@ class Enumeration(object): else: core.BNAddEnumerationMemberWithValue(self.handle, name, value) -class LookupTableEntry: +class LookupTableEntry(object): def __init__(self, from_values, to_value): self.from_values = from_values self.to_value = to_value @@ -4217,19 +4256,19 @@ class LookupTableEntry: def __repr__(self): return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) -class RegisterValue: +class RegisterValue(object): def __init__(self, arch, value): self.type = value.state - if value.state == EntryValue: + if value.state == core.EntryValue: self.reg = arch.get_reg_name(value.reg) - elif value.state == OffsetFromEntryValue: + elif value.state == core.OffsetFromEntryValue: self.reg = arch.get_reg_name(value.reg) self.offset = value.value - elif value.state == ConstantValue: + elif value.state == core.ConstantValue: self.value = value.value - elif value.state == StackFrameOffset: + elif value.state == core.StackFrameOffset: self.offset = value.value - elif value.state == SignedRangeValue: + elif value.state == core.SignedRangeValue: self.offset = value.value self.start = value.rangeStart self.end = value.rangeEnd @@ -4238,12 +4277,12 @@ class RegisterValue: self.start |= ~((1 << 63) - 1) if self.end & (1 << 63): self.end |= ~((1 << 63) - 1) - elif value.state == UnsignedRangeValue: + elif value.state == core.UnsignedRangeValue: self.offset = value.value self.start = value.rangeStart self.end = value.rangeEnd self.step = value.rangeStep - elif value.state == LookupTableValue: + elif value.state == core.LookupTableValue: self.table = [] self.mapping = {} for i in xrange(0, value.rangeEnd): @@ -4252,29 +4291,29 @@ class RegisterValue: from_list.append(value.table[i].fromValues[j]) self.mapping[value.table[i].fromValues[j]] = value.table[i].toValue self.table.append(LookupTableEntry(from_list, value.table[i].toValue)) - elif value.state == OffsetFromUndeterminedValue: + elif value.state == core.OffsetFromUndeterminedValue: self.offset = value.value def __repr__(self): - if self.type == EntryValue: + if self.type == core.EntryValue: return "" % self.reg - if self.type == OffsetFromEntryValue: + if self.type == core.OffsetFromEntryValue: return "" % (self.reg, self.offset) - if self.type == ConstantValue: + if self.type == core.ConstantValue: return "" % self.value - if self.type == StackFrameOffset: + if self.type == core.StackFrameOffset: return "" % self.offset - if (self.type == SignedRangeValue) or (self.type == UnsignedRangeValue): + if (self.type == core.SignedRangeValue) or (self.type == core.UnsignedRangeValue): if self.step == 1: return "" % (self.start, self.end) return "" % (self.start, self.end, self.step) - if self.type == LookupTableValue: + if self.type == core.LookupTableValue: return "" % ', '.join([repr(i) for i in self.table]) - if self.type == OffsetFromUndeterminedValue: + if self.type == core.OffsetFromUndeterminedValue: return "" % self.offset return "" -class StackVariable: +class StackVariable(object): def __init__(self, ofs, name, t): self.offset = ofs self.name = name @@ -4348,7 +4387,7 @@ class Function(object): if self.symbol is not None: self.view.undefine_user_symbol(self.symbol) else: - symbol = Symbol(FunctionSymbol,self.start,value) + symbol = Symbol(core.FunctionSymbol,self.start,value) self.view.define_user_symbol(symbol) @property @@ -4764,7 +4803,7 @@ class AdvancedFunctionAnalysisDataRequestor(object): self._function.release_advanced_analysis_data() self._function = None -class BasicBlockEdge: +class BasicBlockEdge(object): def __init__(self, branch_type, target, arch): self.type = core.BNBranchType_names[branch_type] if self.type != "UnresolvedBranch": @@ -4912,7 +4951,7 @@ class LowLevelILBasicBlock(BasicBlock): for idx in xrange(self.start, self.end): yield self.il_function[idx] -class DisassemblyTextLine: +class DisassemblyTextLine(object): def __init__(self, addr, tokens): self.address = addr self.tokens = tokens @@ -5081,12 +5120,12 @@ class DisassemblySettings(object): def is_option_set(self, option): if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) + option = core.BNDisassemblyOption_by_name[option] return core.BNIsDisassemblySettingsOptionSet(self.handle, option) def set_option(self, option, state = True): if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) + option = core.BNDisassemblyOption_by_name[option] core.BNSetDisassemblySettingsOption(self.handle, option, state) class FunctionGraph(object): @@ -5230,7 +5269,7 @@ class FunctionGraph(object): option = core.BNDisassemblyOption_by_name(option) core.BNSetFunctionGraphOption(self.handle, option, state) -class RegisterInfo: +class RegisterInfo(object): def __init__(self, full_width_reg, size, offset = 0, extend = core.NoExtend, index = None): self.full_width_reg = full_width_reg self.offset = offset @@ -5247,7 +5286,7 @@ class RegisterInfo: extend = "" return "" % (self.size, self.offset, self.full_width_reg, extend) -class InstructionBranch: +class InstructionBranch(object): def __init__(self, branch_type, target = 0, arch = None): self.type = branch_type self.target = target @@ -5261,7 +5300,7 @@ class InstructionBranch: return "<%s: %s@%#x>" % (branch_type, self.arch.name, self.target) return "<%s: %#x>" % (branch_type, self.target) -class InstructionInfo: +class InstructionInfo(object): def __init__(self): self.length = 0 self.branch_delay = False @@ -5276,7 +5315,7 @@ class InstructionInfo: branch_delay = ", delay slot" return "" % (self.length, branch_delay, repr(self.branches)) -class InstructionTextToken: +class InstructionTextToken(object): """ ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. @@ -5473,7 +5512,7 @@ class Architecture(object): self._flags_required_for_flag_condition = {} self.__dict__["flags_required_for_flag_condition"] = {} - for cond in core.BNLowLevelILFlagCondition_names.keys(): + for cond in core.BNLowLevelILFlagCondition_names: count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) flag_indexes = [] @@ -5560,7 +5599,7 @@ class Architecture(object): self._regs_by_index = {} self.__dict__["regs"] = self.__class__.regs reg_index = 0 - for reg in self.regs.keys(): + for reg in self.regs: info = self.regs[reg] if reg not in self._all_regs: self._all_regs[reg] = reg_index @@ -5597,7 +5636,7 @@ class Architecture(object): self._flag_roles = {} self.__dict__["flag_roles"] = self.__class__.flag_roles - for flag in self.__class__.flag_roles.keys(): + for flag in self.__class__.flag_roles: role = self.__class__.flag_roles[flag] if isinstance(role, str): role = core.BNFlagRole_by_name[role] @@ -5605,7 +5644,7 @@ class Architecture(object): self._flags_required_for_flag_condition = {} self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition - for cond in self.__class__.flags_required_for_flag_condition.keys(): + for cond in self.__class__.flags_required_for_flag_condition: flags = [] for flag in self.__class__.flags_required_for_flag_condition[cond]: flags.append(self._flags[flag]) @@ -5613,7 +5652,7 @@ class Architecture(object): self._flags_written_by_flag_write_type = {} self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type - for write_type in self.__class__.flags_written_by_flag_write_type.keys(): + for write_type in self.__class__.flags_written_by_flag_write_type: flags = [] for flag in self.__class__.flags_written_by_flag_write_type[write_type]: flags.append(self._flags[flag]) @@ -5723,7 +5762,7 @@ class Architecture(object): else: result[0].branchArch[i] = info.branches[i].arch.handle return True - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return False @@ -5740,7 +5779,7 @@ class Architecture(object): token_buf = (core.BNInstructionTextToken * len(tokens))() for i in xrange(0, len(tokens)): if isinstance(tokens[i].type, str): - token_buf[i].type = BNInstructionTextTokenType_by_name[tokens[i].type] + token_buf[i].type = core.BNInstructionTextTokenType_by_name[tokens[i].type] else: token_buf[i].type = tokens[i].type token_buf[i].text = tokens[i].text @@ -5751,7 +5790,7 @@ class Architecture(object): ptr = ctypes.cast(token_buf, ctypes.c_void_p) self._pending_token_lists[ptr.value] = (ptr.value, token_buf) return True - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return False @@ -5761,7 +5800,7 @@ class Architecture(object): if buf.value not in self._pending_token_lists: raise ValueError, "freeing token list that wasn't allocated" del self._pending_token_lists[buf.value] - except: + except KeyError: log_error(traceback.format_exc()) def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): @@ -5774,7 +5813,7 @@ class Architecture(object): return False length[0] = result return True - except: + except OSError: log_error(traceback.format_exc()) return False @@ -5783,7 +5822,7 @@ class Architecture(object): if reg in self._regs_by_index: return core.BNAllocString(self._regs_by_index[reg]) return core.BNAllocString("") - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return core.BNAllocString("") @@ -5792,7 +5831,7 @@ class Architecture(object): if flag in self._flags_by_index: return core.BNAllocString(self._flags_by_index[flag]) return core.BNAllocString("") - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return core.BNAllocString("") @@ -5801,7 +5840,7 @@ class Architecture(object): if write_type in self._flag_write_types_by_index: return core.BNAllocString(self._flag_write_types_by_index[write_type]) return core.BNAllocString("") - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return core.BNAllocString("") @@ -5815,7 +5854,7 @@ class Architecture(object): result = ctypes.cast(reg_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, reg_buf) return result.value - except: + except KeyError: log_error(traceback.format_exc()) count[0] = 0 return None @@ -5830,7 +5869,7 @@ class Architecture(object): result = ctypes.cast(reg_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, reg_buf) return result.value - except: + except KeyError: log_error(traceback.format_exc()) count[0] = 0 return None @@ -5845,7 +5884,7 @@ class Architecture(object): result = ctypes.cast(flag_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, flag_buf) return result.value - except: + except KeyError: log_error(traceback.format_exc()) count[0] = 0 return None @@ -5860,7 +5899,7 @@ class Architecture(object): result = ctypes.cast(type_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, type_buf) return result.value - except: + except KeyError: log_error(traceback.format_exc()) count[0] = 0 return None @@ -5870,7 +5909,7 @@ class Architecture(object): if flag in self._flag_roles: return self._flag_roles[flag] return core.SpecialFlagRole - except: + except KeyError: log_error(traceback.format_exc()) return None @@ -5887,7 +5926,7 @@ class Architecture(object): result = ctypes.cast(flag_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, flag_buf) return result.value - except: + except KeyError: log_error(traceback.format_exc()) count[0] = 0 return None @@ -5905,7 +5944,7 @@ class Architecture(object): result = ctypes.cast(flag_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, flag_buf) return result.value - except: + except (KeyError, OSError): log_error(traceback.format_exc()) count[0] = 0 return None @@ -5926,7 +5965,7 @@ class Architecture(object): operand_list.append(("reg", self._regs_by_index[operands[i].reg])) return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index - except: + except (KeyError, OSError): log_error(traceback.format_exc()) return False @@ -5934,7 +5973,7 @@ class Architecture(object): try: return self.perform_get_flag_condition_low_level_il(cond, LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index - except: + except OSError: log_error(traceback.format_exc()) return 0 @@ -5944,7 +5983,7 @@ class Architecture(object): if buf.value not in self._pending_reg_lists: raise ValueError, "freeing register list that wasn't allocated" del self._pending_reg_lists[buf.value] - except: + except (ValueError, KeyError): log_error(traceback.format_exc()) def _get_register_info(self, ctxt, reg, result): @@ -5963,7 +6002,7 @@ class Architecture(object): result[0].extend = core.BNImplicitRegisterExtend_by_name[info.extend] else: result[0].extend = info.extend - except: + except KeyError: log_error(traceback.format_exc()) result[0].fullWidthRegister = 0 result[0].offset = 0 @@ -5973,7 +6012,7 @@ class Architecture(object): def _get_stack_pointer_register(self, ctxt): try: return self._all_regs[self.__class__.stack_pointer] - except: + except KeyError: log_error(traceback.format_exc()) return 0 @@ -5982,7 +6021,7 @@ class Architecture(object): if self.__class__.link_reg is None: return 0xffffffff return self._all_regs[self.__class__.link_reg] - except: + except KeyError: log_error(traceback.format_exc()) return 0 @@ -6111,6 +6150,7 @@ class Architecture(object): log_error(traceback.format_exc()) return False + @abc.abstractmethod def perform_get_instruction_info(self, data, addr): """ ``perform_get_instruction_info`` implements a method which interpretes the bytes passed in ``data`` as an @@ -6120,7 +6160,7 @@ class Architecture(object): ===================== =================================================== BranchType Description ===================== =================================================== - UnconditionalBranch Branch will alwasy be taken + UnconditionalBranch Branch will always be taken FalseBranch False branch condition TrueBranch True branch condition CallDestination Branch is a call instruction (Branch with Link) @@ -6135,8 +6175,9 @@ class Architecture(object): :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction :rtype: InstructionInfo """ - return None + raise NotImplementedError + @abc.abstractmethod def perform_get_instruction_text(self, data, addr): """ ``perform_get_instruction_text`` implements a method which interpretes the bytes passed in ``data`` as a @@ -6147,8 +6188,9 @@ class Architecture(object): :return: a tuple of list(InstructionTextToken) and length of instruction decoded :rtype: tuple(list(InstructionTextToken), int) """ - return None, None + raise NotImplementedError + @abc.abstractmethod def perform_get_instruction_low_level_il(self, data, addr, il): """ ``perform_get_instruction_low_level_il`` implements a method to interpret the bytes passed in ``data`` to @@ -6161,8 +6203,9 @@ class Architecture(object): :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to :rtype: None """ - return None + raise NotImplementedError + @abc.abstractmethod def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): """ .. note:: Architecture subclasses should implement this method. @@ -6178,6 +6221,7 @@ class Architecture(object): """ return il.unimplemented() + @abc.abstractmethod def perform_get_flag_condition_low_level_il(self, cond, il): """ .. note:: Architecture subclasses should implement this method. @@ -6189,6 +6233,7 @@ class Architecture(object): """ return il.unimplemented() + @abc.abstractmethod def perform_assemble(self, code, addr): """ ``perform_assemble`` implements a method to convert the string of assembly instructions ``code`` loaded at @@ -6207,6 +6252,7 @@ class Architecture(object): """ return None, "Architecture does not implement an assembler.\n" + @abc.abstractmethod def perform_is_never_branch_patch_available(self, data, addr): """ ``perform_is_never_branch_patch_available`` implements a check to determine if the instruction represented by @@ -6222,6 +6268,7 @@ class Architecture(object): """ return False + @abc.abstractmethod def perform_is_always_branch_patch_available(self, data, addr): """ ``perform_is_always_branch_patch_available`` implements a check to determine if the instruction represented by @@ -6237,6 +6284,7 @@ class Architecture(object): """ return False + @abc.abstractmethod def perform_is_invert_branch_patch_available(self, data, addr): """ ``perform_is_invert_branch_patch_available`` implements a check to determine if the instruction represented by @@ -6251,6 +6299,7 @@ class Architecture(object): """ return False + @abc.abstractmethod def perform_is_skip_and_return_zero_patch_available(self, data, addr): """ ``perform_is_skip_and_return_zero_patch_available`` implements a check to determine if the instruction represented by @@ -6268,6 +6317,7 @@ class Architecture(object): """ return False + @abc.abstractmethod def perform_is_skip_and_return_value_patch_available(self, data, addr): """ ``perform_is_skip_and_return_value_patch_available`` implements a check to determine if the instruction represented by @@ -6285,6 +6335,7 @@ class Architecture(object): """ return False + @abc.abstractmethod def perform_convert_to_nop(self, data, addr): """ ``perform_convert_to_nop`` implements a method which returns a nop sequence of len(data) bytes long. @@ -6299,6 +6350,7 @@ class Architecture(object): """ return None + @abc.abstractmethod def perform_always_branch(self, data, addr): """ ``perform_always_branch`` implements a method which converts the branch represented by the bytes in ``data`` to @@ -6314,6 +6366,7 @@ class Architecture(object): """ return None + @abc.abstractmethod def perform_invert_branch(self, data, addr): """ ``perform_invert_branch`` implements a method which inverts the branch represented by the bytes in ``data`` to @@ -6329,6 +6382,7 @@ class Architecture(object): """ return None + @abc.abstractmethod def perform_skip_and_return_value(self, data, addr, value): """ ``perform_skip_and_return_value`` implements a method which converts a *call-like* instruction represented by @@ -6921,7 +6975,7 @@ class Architecture(object): """ core.BNRegisterCallingConvention(self.handle, cc.handle) -class ReferenceSource: +class ReferenceSource(object): def __init__(self, func, arch, addr): self.function = func self.arch = arch @@ -6933,7 +6987,7 @@ class ReferenceSource: else: return "" % self.address -class LowLevelILLabel: +class LowLevelILLabel(object): def __init__(self, handle = None): if handle is None: self.handle = (core.BNLowLevelILLabel * 1)() @@ -6949,73 +7003,73 @@ class LowLevelILInstruction(object): """ ILOperations = { - core.LLIL_NOP: [], - core.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], + core.LLIL_NOP: [], + core.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], core.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), ("src", "expr")], - core.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], - core.LLIL_LOAD: [("src", "expr")], - core.LLIL_STORE: [("dest", "expr"), ("src", "expr")], - core.LLIL_PUSH: [("src", "expr")], - core.LLIL_POP: [], - core.LLIL_REG: [("src", "reg")], - core.LLIL_CONST: [("value", "int")], - core.LLIL_FLAG: [("src", "flag")], - core.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], - core.LLIL_ADD: [("left", "expr"), ("right", "expr")], - core.LLIL_ADC: [("left", "expr"), ("right", "expr")], - core.LLIL_SUB: [("left", "expr"), ("right", "expr")], - core.LLIL_SBB: [("left", "expr"), ("right", "expr")], - core.LLIL_AND: [("left", "expr"), ("right", "expr")], - core.LLIL_OR: [("left", "expr"), ("right", "expr")], - core.LLIL_XOR: [("left", "expr"), ("right", "expr")], - core.LLIL_LSL: [("left", "expr"), ("right", "expr")], - core.LLIL_LSR: [("left", "expr"), ("right", "expr")], - core.LLIL_ASR: [("left", "expr"), ("right", "expr")], - core.LLIL_ROL: [("left", "expr"), ("right", "expr")], - core.LLIL_RLC: [("left", "expr"), ("right", "expr")], - core.LLIL_ROR: [("left", "expr"), ("right", "expr")], - core.LLIL_RRC: [("left", "expr"), ("right", "expr")], - core.LLIL_MUL: [("left", "expr"), ("right", "expr")], - core.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], - core.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVU: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_DIVS: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_MODU: [("left", "expr"), ("right", "expr")], - core.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_MODS: [("left", "expr"), ("right", "expr")], - core.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_NEG: [("src", "expr")], - core.LLIL_NOT: [("src", "expr")], - core.LLIL_SX: [("src", "expr")], - core.LLIL_ZX: [("src", "expr")], - core.LLIL_JUMP: [("dest", "expr")], - core.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], - core.LLIL_CALL: [("dest", "expr")], - core.LLIL_RET: [("dest", "expr")], - core.LLIL_NORET: [], - core.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], - core.LLIL_GOTO: [("dest", "int")], - core.LLIL_FLAG_COND: [("condition", "cond")], - core.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], - core.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], - core.LLIL_BOOL_TO_INT: [("src", "expr")], - core.LLIL_SYSCALL: [], - core.LLIL_BP: [], - core.LLIL_TRAP: [("value", "int")], - core.LLIL_UNDEF: [], - core.LLIL_UNIMPL: [], - core.LLIL_UNIMPL_MEM: [("src", "expr")] + core.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], + core.LLIL_LOAD: [("src", "expr")], + core.LLIL_STORE: [("dest", "expr"), ("src", "expr")], + core.LLIL_PUSH: [("src", "expr")], + core.LLIL_POP: [], + core.LLIL_REG: [("src", "reg")], + core.LLIL_CONST: [("value", "int")], + core.LLIL_FLAG: [("src", "flag")], + core.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], + core.LLIL_ADD: [("left", "expr"), ("right", "expr")], + core.LLIL_ADC: [("left", "expr"), ("right", "expr")], + core.LLIL_SUB: [("left", "expr"), ("right", "expr")], + core.LLIL_SBB: [("left", "expr"), ("right", "expr")], + core.LLIL_AND: [("left", "expr"), ("right", "expr")], + core.LLIL_OR: [("left", "expr"), ("right", "expr")], + core.LLIL_XOR: [("left", "expr"), ("right", "expr")], + core.LLIL_LSL: [("left", "expr"), ("right", "expr")], + core.LLIL_LSR: [("left", "expr"), ("right", "expr")], + core.LLIL_ASR: [("left", "expr"), ("right", "expr")], + core.LLIL_ROL: [("left", "expr"), ("right", "expr")], + core.LLIL_RLC: [("left", "expr"), ("right", "expr")], + core.LLIL_ROR: [("left", "expr"), ("right", "expr")], + core.LLIL_RRC: [("left", "expr"), ("right", "expr")], + core.LLIL_MUL: [("left", "expr"), ("right", "expr")], + core.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], + core.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVU: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_DIVS: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_MODU: [("left", "expr"), ("right", "expr")], + core.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_MODS: [("left", "expr"), ("right", "expr")], + core.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_NEG: [("src", "expr")], + core.LLIL_NOT: [("src", "expr")], + core.LLIL_SX: [("src", "expr")], + core.LLIL_ZX: [("src", "expr")], + core.LLIL_JUMP: [("dest", "expr")], + core.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], + core.LLIL_CALL: [("dest", "expr")], + core.LLIL_RET: [("dest", "expr")], + core.LLIL_NORET: [], + core.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], + core.LLIL_GOTO: [("dest", "int")], + core.LLIL_FLAG_COND: [("condition", "cond")], + core.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], + core.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], + core.LLIL_BOOL_TO_INT: [("src", "expr")], + core.LLIL_SYSCALL: [], + core.LLIL_BP: [], + core.LLIL_TRAP: [("value", "int")], + core.LLIL_UNDEF: [], + core.LLIL_UNIMPL: [], + core.LLIL_UNIMPL_MEM: [("src", "expr")] } def __init__(self, func, expr_index, instr_index = None): @@ -7103,7 +7157,7 @@ class LowLevelILInstruction(object): except AttributeError: raise AttributeError, "attribute '%s' is read only" % name -class LowLevelILExpr: +class LowLevelILExpr(object): """ ``class LowLevelILExpr`` hold the index of IL Expressions. @@ -7203,13 +7257,12 @@ class LowLevelILFunction(object): raise IndexError, "index out of range" return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i), i) - def __setitem__(self, i): + def __setitem__(self, i, j): raise IndexError, "instruction modification not implemented" def __iter__(self): count = ctypes.c_ulonglong() blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) - result = [] view = None if self.source_function is not None: view = self.source_function.view @@ -7231,7 +7284,7 @@ class LowLevelILFunction(object): def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): if isinstance(operation, str): - operation = BNLowLevelILOperation_by_name[operation] + operation = core.BNLowLevelILOperation_by_name[operation] if isinstance(flags, str): flags = self.arch.get_flag_write_type_by_name(flags) elif flags is None: @@ -7846,7 +7899,7 @@ class LowLevelILFunction(object): :rtype: LowLevelILExpr """ if isinstance(cond, str): - cond = BNLowLevelILFlagCondition_by_name[cond] + cond = core.BNLowLevelILFlagCondition_by_name[cond] return self.expr(core.LLIL_FLAG_COND, cond) def compare_equal(self, size, a, b): @@ -8081,7 +8134,7 @@ class LowLevelILFunction(object): :return: the label list expression :rtype: LowLevelILExpr """ - label_list = (ctypes.POINTER(BNLowLevelILLabel) * len(labels))() + label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() for i in xrange(len(labels)): label_list[i] = labels[i].handle return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) @@ -8151,7 +8204,7 @@ class LowLevelILFunction(object): return None return LowLevelILLabel(label) -class TypeParserResult: +class TypeParserResult(object): def __init__(self, types, variables, functions): self.types = types self.variables = variables @@ -8209,7 +8262,7 @@ class _TransformMetaClass(type): cls._registered_cb = xform._cb xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) -class TransformParameter: +class TransformParameter(object): def __init__(self, name, long_name = None, fixed_length = 0): self.name = name if long_name is None: @@ -8318,11 +8371,13 @@ class Transform: log_error(traceback.format_exc()) return False + @abc.abstractmethod def perform_decode(self, data, params): if self.type == "InvertingTransform": return self.perform_encode(data, params) return None + @abc.abstractmethod def perform_encode(self, data, params): return None @@ -8354,7 +8409,7 @@ class Transform: return None return str(output_buf) -class FunctionRecognizer: +class FunctionRecognizer(object): _instance = None def __init__(self): @@ -8453,7 +8508,7 @@ class _UpdateChannelMetaClass(type): raise KeyError, "'%s' is not a valid channel" % str(name) return result -class UpdateProgressCallback: +class UpdateProgressCallback(object): def __init__(self, func): self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) self.func = func @@ -8541,7 +8596,7 @@ class UpdateChannel(object): raise IOError, error_str return core.BNUpdateResult_names[result] -class UpdateVersion: +class UpdateVersion(object): def __init__(self, channel, ver, notes, t): self.channel = channel self.version = ver @@ -8564,7 +8619,7 @@ class UpdateVersion: raise IOError, error_str return core.BNUpdateResult_names[result] -class PluginCommandContext: +class PluginCommandContext(object): def __init__(self, view): self.view = view self.address = 0 @@ -8777,7 +8832,7 @@ class PluginCommand: def __repr__(self): return "" % self.name -class CallingConvention: +class CallingConvention(object): name = None caller_saved_regs = [] int_arg_regs = [] @@ -9297,26 +9352,33 @@ class ScriptingInstance(object): except: log_error(traceback.format_exc()) + @abc.abstractmethod def perform_destroy_instance(self): - pass + raise NotImplementedError + @abc.abstractmethod def perform_execute_script_input(self, text): return core.InvalidScriptInput + @abc.abstractmethod def perform_set_current_binary_view(self, view): - pass + raise NotImplementedError + @abc.abstractmethod def perform_set_current_function(self, func): - pass + raise NotImplementedError + @abc.abstractmethod def perform_set_current_basic_block(self, block): - pass + raise NotImplementedError + @abc.abstractmethod def perform_set_current_address(self, addr): - pass + raise NotImplementedError + @abc.abstractmethod def perform_set_current_selection(self, begin, end): - pass + raise NotImplementedError @property def input_ready_state(self): @@ -9640,9 +9702,11 @@ class PythonScriptingInstance(ScriptingInstance): self.queued_input = "" self.input_ready_state = core.ReadyForScriptExecution + @abc.abstractmethod def perform_destroy_instance(self): self.interpreter.end() + @abc.abstractmethod def perform_execute_script_input(self, text): if self.input_ready_state == core.NotReadyForInput: return core.InvalidScriptInput @@ -9667,18 +9731,23 @@ class PythonScriptingInstance(ScriptingInstance): self.interpreter.execute(text) return core.SuccessfulScriptExecution + @abc.abstractmethod def perform_set_current_binary_view(self, view): self.interpreter.current_view = view + @abc.abstractmethod def perform_set_current_function(self, func): self.interpreter.current_func = func + @abc.abstractmethod def perform_set_current_basic_block(self, block): self.interpreter.current_block = block + @abc.abstractmethod def perform_set_current_address(self, addr): self.interpreter.current_addr = addr + @abc.abstractmethod def perform_set_current_selection(self, begin, end): self.interpreter.current_selection_begin = begin self.interpreter.current_selection_end = end @@ -9979,15 +10048,17 @@ def demangle_ms(arch, mangled_name): if core.BNDemangleMS(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): for i in xrange(outSize.value): names.append(outName[i]) + #core.BNFreeDemangledName(outName.value, outSize.value) return (Type(handle), names) return (None, mangled_name) + _output_to_log = False def redirect_output_to_log(): global _output_to_log _output_to_log = True -class _ThreadActionContext: +class _ThreadActionContext(object): _actions = [] def __init__(self, func): -- cgit v1.3.1 From 89c2a2faabb7344c4bd540c7f14420a34326cfaf Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 6 Sep 2016 00:18:29 -0400 Subject: Fix some memory leaks in the Python API --- python/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index f32fa612..d5eca84a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4461,14 +4461,12 @@ class Function(object): @property def low_level_il(self): """Function low level IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNNewLowLevelILFunctionReference( - core.BNGetFunctionLowLevelIL(self.handle)), self) + return LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) @property def lifted_il(self): """Function lifted IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNNewLowLevelILFunctionReference( - core.BNGetFunctionLiftedIL(self.handle)), self) + return LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) @property def function_type(self): @@ -6924,6 +6922,7 @@ class Architecture(object): variables[parse.variables[i].name] = Type(core.BNNewTypeReference(parse.variables[i].type)) for i in xrange(0, parse.functionCount): functions[parse.functions[i].name] = Type(core.BNNewTypeReference(parse.functions[i].type)) + BNFreeTypeParserResult(parse) return (TypeParserResult(types, variables, functions), error_str) def parse_types_from_source_file(self, filename, include_dirs = []): @@ -6964,6 +6963,7 @@ class Architecture(object): variables[parse.variables[i].name] = Type(core.BNNewTypeReference(parse.variables[i].type)) for i in xrange(0, parse.functionCount): functions[parse.functions[i].name] = Type(core.BNNewTypeReference(parse.functions[i].type)) + BNFreeTypeParserResult(parse) return (TypeParserResult(types, variables, functions), error_str) def register_calling_convention(self, cc): -- cgit v1.3.1 From f5e59b13b726a0d154dbe9351010ed3bd4b2b9e6 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 6 Sep 2016 21:15:32 -0400 Subject: Add APIs for interactive work queue and worker thread count --- binaryninjaapi.cpp | 36 ++++++++++++++++++++++++++++++++++++ binaryninjaapi.h | 5 +++++ binaryninjacore.h | 4 ++++ python/__init__.py | 12 +++++++++++- 4 files changed, 56 insertions(+), 1 deletion(-) (limited to 'python') diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index 1a9f5713..e1dab528 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -201,3 +201,39 @@ void BinaryNinja::WorkerPriorityEnqueue(RefCountObject* owner, const function& action) +{ + WorkerThreadActionContext* ctxt = new WorkerThreadActionContext; + ctxt->action = action; + BNWorkerInteractiveEnqueue(ctxt, WorkerActionCallback); +} + + +void BinaryNinja::WorkerInteractiveEnqueue(RefCountObject* owner, const function& action) +{ + struct + { + Ref owner; + function func; + } context; + context.owner = owner; + context.func = action; + + WorkerInteractiveEnqueue([=]() { + context.func(); + }); +} + + +size_t BinaryNinja::GetWorkerThreadCount() +{ + return BNGetWorkerThreadCount(); +} + + +void BinaryNinja::SetWorkerThreadCount(size_t count) +{ + BNSetWorkerThreadCount(count); +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index ff505527..a86f1067 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -375,6 +375,11 @@ namespace BinaryNinja void WorkerEnqueue(RefCountObject* owner, const std::function& action); void WorkerPriorityEnqueue(const std::function& action); void WorkerPriorityEnqueue(RefCountObject* owner, const std::function& action); + void WorkerInteractiveEnqueue(const std::function& action); + void WorkerInteractiveEnqueue(RefCountObject* owner, const std::function& action); + + size_t GetWorkerThreadCount(); + void SetWorkerThreadCount(size_t count); class DataBuffer { diff --git a/binaryninjacore.h b/binaryninjacore.h index 1fe0ad5a..960390fa 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1968,6 +1968,10 @@ extern "C" // Worker thread queue management BINARYNINJACOREAPI void BNWorkerEnqueue(void* ctxt, void (*action)(void* ctxt)); BINARYNINJACOREAPI void BNWorkerPriorityEnqueue(void* ctxt, void (*action)(void* ctxt)); + BINARYNINJACOREAPI void BNWorkerInteractiveEnqueue(void* ctxt, void (*action)(void* ctxt)); + + BINARYNINJACOREAPI size_t BNGetWorkerThreadCount(void); + BINARYNINJACOREAPI void BNSetWorkerThreadCount(size_t count); #ifdef __cplusplus } diff --git a/python/__init__.py b/python/__init__.py index d5eca84a..e36670cc 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -10097,7 +10097,17 @@ def worker_enqueue(func): def worker_priority_enqueue(func): action = _ThreadActionContext(func) - core.BNWorkerEnqueue(0, action.callback) + core.BNWorkerPriorityEnqueue(0, action.callback) + +def worker_interactive_enqueue(func): + action = _ThreadActionContext(func) + core.BNWorkerInteractiveEnqueue(0, action.callback) + +def get_worker_thread_count(): + return core.BNGetWorkerThreadCount() + +def set_worker_thread_count(count): + core.BNSetWorkerThreadCount(count) bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() -- cgit v1.3.1 From 6be6ee30e5973fa4d5f29cebdeafdf05c3f4251e Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Fri, 9 Sep 2016 03:29:14 -0400 Subject: Minor changes to API for creation of IL functions --- binaryninjaapi.h | 4 ++-- binaryninjacore.h | 4 ++-- lowlevelil.cpp | 8 ++++---- python/__init__.py | 15 +++++++-------- 4 files changed, 15 insertions(+), 16 deletions(-) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index a86f1067..9c7ff4f1 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1788,7 +1788,7 @@ namespace BinaryNinja BNNewLowLevelILFunctionReference, BNFreeLowLevelILFunction> { public: - LowLevelILFunction(Architecture* arch); + LowLevelILFunction(Architecture* arch, Function* func = nullptr); LowLevelILFunction(BNLowLevelILFunction* func); uint64_t GetCurrentAddress() const; @@ -1883,7 +1883,7 @@ namespace BinaryNinja void AddLabelForAddress(Architecture* arch, ExprId addr); BNLowLevelILLabel* GetLabelForAddress(Architecture* arch, ExprId addr); - void Finalize(Function* func = nullptr); + void Finalize(); bool GetExprText(Architecture* arch, ExprId expr, std::vector& tokens); bool GetInstructionText(Function* func, Architecture* arch, size_t i, diff --git a/binaryninjacore.h b/binaryninjacore.h index 960390fa..f917c2fa 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1665,7 +1665,7 @@ extern "C" BINARYNINJACOREAPI BNSymbol* BNImportedFunctionFromImportAddressSymbol(BNSymbol* sym, uint64_t addr); // Low-level IL - BINARYNINJACOREAPI BNLowLevelILFunction* BNCreateLowLevelILFunction(BNArchitecture* arch); + BINARYNINJACOREAPI BNLowLevelILFunction* BNCreateLowLevelILFunction(BNArchitecture* arch, BNFunction* func); BINARYNINJACOREAPI BNLowLevelILFunction* BNNewLowLevelILFunctionReference(BNLowLevelILFunction* func); BINARYNINJACOREAPI void BNFreeLowLevelILFunction(BNLowLevelILFunction* func); BINARYNINJACOREAPI uint64_t BNLowLevelILGetCurrentAddress(BNLowLevelILFunction* func); @@ -1681,7 +1681,7 @@ extern "C" BINARYNINJACOREAPI size_t BNLowLevelILIf(BNLowLevelILFunction* func, uint64_t op, BNLowLevelILLabel* t, BNLowLevelILLabel* f); BINARYNINJACOREAPI void BNLowLevelILInitLabel(BNLowLevelILLabel* label); BINARYNINJACOREAPI void BNLowLevelILMarkLabel(BNLowLevelILFunction* func, BNLowLevelILLabel* label); - BINARYNINJACOREAPI void BNFinalizeLowLevelILFunction(BNLowLevelILFunction* func, BNFunction* sourceFunc); + BINARYNINJACOREAPI void BNFinalizeLowLevelILFunction(BNLowLevelILFunction* func); BINARYNINJACOREAPI size_t BNLowLevelILAddLabelList(BNLowLevelILFunction* func, BNLowLevelILLabel** labels, size_t count); BINARYNINJACOREAPI size_t BNLowLevelILAddOperandList(BNLowLevelILFunction* func, uint64_t* operands, size_t count); diff --git a/lowlevelil.cpp b/lowlevelil.cpp index 550accc2..a312bbd6 100644 --- a/lowlevelil.cpp +++ b/lowlevelil.cpp @@ -30,9 +30,9 @@ LowLevelILLabel::LowLevelILLabel() } -LowLevelILFunction::LowLevelILFunction(Architecture* arch) +LowLevelILFunction::LowLevelILFunction(Architecture* arch, Function* func) { - m_object = BNCreateLowLevelILFunction(arch->GetObject()); + m_object = BNCreateLowLevelILFunction(arch->GetObject(), func ? func->GetObject() : nullptr); } @@ -559,9 +559,9 @@ BNLowLevelILLabel* LowLevelILFunction::GetLabelForAddress(Architecture* arch, Ex } -void LowLevelILFunction::Finalize(Function* func) +void LowLevelILFunction::Finalize() { - BNFinalizeLowLevelILFunction(m_object, func ? func->GetObject() : nullptr); + BNFinalizeLowLevelILFunction(m_object); } diff --git a/python/__init__.py b/python/__init__.py index e36670cc..7978fd2c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -7201,7 +7201,10 @@ class LowLevelILFunction(object): if handle is not None: self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) else: - self.handle = core.BNCreateLowLevelILFunction(arch.handle) + func_handle = None + if self.source_function is not None: + func_handle = self.source_function.handle + self.handle = core.BNCreateLowLevelILFunction(arch.handle, func_handle) def __del__(self): core.BNFreeLowLevelILFunction(self.handle) @@ -8164,17 +8167,13 @@ class LowLevelILFunction(object): core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) return expr - def finalize(self, func = None): + def finalize(self): """ - ``finalize`` ends the provided LowLevelILLabel. + ``finalize`` ends the function and computes the list of basic blocks. - :param LowLevelILFunction func: optional function to end :rtype: None """ - if func is None: - core.BNFinalizeLowLevelILFunction(self.handle, None) - else: - core.BNFinalizeLowLevelILFunction(self.handle, func.handle) + core.BNFinalizeLowLevelILFunction(self.handle) def add_label_for_address(self, arch, addr): """ -- cgit v1.3.1 From 6d3fe290eb20e31a33e929933c01d2ca3eb43da1 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Sat, 10 Sep 2016 00:36:27 -0400 Subject: Add API for list of basic blocks starting at an address --- binaryninjaapi.h | 1 + binaryninjacore.h | 1 + binaryview.cpp | 14 ++++++++++++++ python/__init__.py | 16 ++++++++++++++++ 4 files changed, 32 insertions(+) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 9c7ff4f1..d4df479b 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -867,6 +867,7 @@ namespace BinaryNinja Ref GetRecentBasicBlockForAddress(uint64_t addr); std::vector> GetBasicBlocksForAddress(uint64_t addr); + std::vector> GetBasicBlocksStartingAtAddress(uint64_t addr); std::vector GetCodeReferences(uint64_t addr); std::vector GetCodeReferences(uint64_t addr, uint64_t len); diff --git a/binaryninjacore.h b/binaryninjacore.h index f917c2fa..51bf3391 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1420,6 +1420,7 @@ extern "C" BINARYNINJACOREAPI void BNFreeBasicBlockList(BNBasicBlock** blocks, size_t count); BINARYNINJACOREAPI BNBasicBlock* BNGetRecentBasicBlockForAddress(BNBinaryView* view, uint64_t addr); BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlocksForAddress(BNBinaryView* view, uint64_t addr, size_t* count); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlocksStartingAtAddress(BNBinaryView* view, uint64_t addr, size_t* count); BINARYNINJACOREAPI BNLowLevelILFunction* BNGetFunctionLowLevelIL(BNFunction* func); BINARYNINJACOREAPI size_t BNGetLowLevelILForInstruction(BNFunction* func, BNArchitecture* arch, uint64_t addr); diff --git a/binaryview.cpp b/binaryview.cpp index 1c03eca5..0a7f23ce 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -987,6 +987,20 @@ vector> BinaryView::GetBasicBlocksForAddress(uint64_t addr) } +vector> BinaryView::GetBasicBlocksStartingAtAddress(uint64_t addr) +{ + size_t count; + BNBasicBlock** blocks = BNGetBasicBlocksStartingAtAddress(m_object, addr, &count); + + vector> result; + for (size_t i = 0; i < count; i++) + result.push_back(new BasicBlock(BNNewBasicBlockReference(blocks[i]))); + + BNFreeBasicBlockList(blocks, count); + return result; +} + + vector BinaryView::GetCodeReferences(uint64_t addr) { size_t count; diff --git a/python/__init__.py b/python/__init__.py index 7978fd2c..33202a2c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -2288,6 +2288,22 @@ class BinaryView(object): core.BNFreeBasicBlockList(blocks, count.value) return result + def get_basic_blocks_starting_at(self, addr): + """ + ``get_basic_blocks_at`` get a list of :py:Class:`BasicBlock` objects which start at the provided virtual address. + + :param int addr: virtual address of BasicBlock desired + :return: a list of :py:Class:`BasicBlock` objects + :rtype: list(BasicBlock) + """ + count = ctypes.c_ulonglong(0) + blocks = core.BNGetBasicBlocksStartingAtAddress(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + def get_recent_basic_block_at(self, addr): block = core.BNGetRecentBasicBlockForAddress(self.handle, addr) if block is None: -- cgit v1.3.1 From b398b6a0a8683ae844e6b61389d02317f53a252e Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Tue, 13 Sep 2016 18:22:57 -0400 Subject: better defaults for version switcher --- python/examples/version-switcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'python') diff --git a/python/examples/version-switcher.py b/python/examples/version-switcher.py index 087bf7f9..6199c578 100644 --- a/python/examples/version-switcher.py +++ b/python/examples/version-switcher.py @@ -3,14 +3,14 @@ import sys import binaryninja import datetime -chandefault="release" -channel=0 -versions=0 +chandefault = binaryninja.UpdateChannel.list[0].name +channel = None +versions = [] def load_channel(newchannel): global channel global versions - if (channel != 0 and newchannel == channel.name): + if (channel != None and newchannel == channel.name): print "Same channel, not updating." else: try: -- cgit v1.3.1 From 11d6f78e43e0f305be43aeb8fc75d9d6733e508a Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Tue, 13 Sep 2016 18:24:02 -0400 Subject: rename files to allow them to be imported --- python/examples/bin-info.py | 34 ------- python/examples/bin_info.py | 34 +++++++ python/examples/export-svg.py | 166 -------------------------------- python/examples/export_svg.py | 166 ++++++++++++++++++++++++++++++++ python/examples/instruction-iterator.py | 49 ---------- python/examples/instruction_iterator.py | 49 ++++++++++ python/examples/jump-table.py | 63 ------------ python/examples/jump_table.py | 63 ++++++++++++ python/examples/version-switcher.py | 119 ----------------------- python/examples/version_switcher.py | 119 +++++++++++++++++++++++ 10 files changed, 431 insertions(+), 431 deletions(-) delete mode 100644 python/examples/bin-info.py create mode 100644 python/examples/bin_info.py delete mode 100755 python/examples/export-svg.py create mode 100755 python/examples/export_svg.py delete mode 100644 python/examples/instruction-iterator.py create mode 100644 python/examples/instruction_iterator.py delete mode 100644 python/examples/jump-table.py create mode 100644 python/examples/jump_table.py delete mode 100644 python/examples/version-switcher.py create mode 100644 python/examples/version_switcher.py (limited to 'python') diff --git a/python/examples/bin-info.py b/python/examples/bin-info.py deleted file mode 100644 index 48073894..00000000 --- a/python/examples/bin-info.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -import sys, binaryninja, time -if sys.platform.lower().startswith("linux"): - bintype="ELF" -elif sys.platform.lower() == "darwin": - bintype="Mach-O" -else: - raise Exception, "%s is not supported on this plugin" % sys.platform - -if len(sys.argv) > 1: - target = sys.argv[1] -else: - target = "/bin/ls" - -bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis_and_wait() - -print "-------- %s --------" % target -print "START: 0x%x" % bv.start -print "ENTRY: 0x%x" % bv.entry_point -print "ARCH: %s" % bv.arch.name -print "\n-------- Function List --------" - -for func in bv.functions: - print func.symbol.name - - -print "\n-------- First 10 strings --------" - -for i in xrange(10): - start = bv.strings[i].start - length = bv.strings[i].length - string = bv.read(start,length) - print "0x%x (%d):\t%s" % (start, length, string) diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py new file mode 100644 index 00000000..48073894 --- /dev/null +++ b/python/examples/bin_info.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +import sys, binaryninja, time +if sys.platform.lower().startswith("linux"): + bintype="ELF" +elif sys.platform.lower() == "darwin": + bintype="Mach-O" +else: + raise Exception, "%s is not supported on this plugin" % sys.platform + +if len(sys.argv) > 1: + target = sys.argv[1] +else: + target = "/bin/ls" + +bv = binaryninja.BinaryViewType[bintype].open(target) +bv.update_analysis_and_wait() + +print "-------- %s --------" % target +print "START: 0x%x" % bv.start +print "ENTRY: 0x%x" % bv.entry_point +print "ARCH: %s" % bv.arch.name +print "\n-------- Function List --------" + +for func in bv.functions: + print func.symbol.name + + +print "\n-------- First 10 strings --------" + +for i in xrange(10): + start = bv.strings[i].start + length = bv.strings[i].length + string = bv.read(start,length) + print "0x%x (%d):\t%s" % (start, length, string) diff --git a/python/examples/export-svg.py b/python/examples/export-svg.py deleted file mode 100755 index 02a6d57e..00000000 --- a/python/examples/export-svg.py +++ /dev/null @@ -1,166 +0,0 @@ -from binaryninja import * -import os,sys - -escape_table = { - "'": "'", - ">": ">", - "<": "<", - '"': """, - ' ': " " -} - -def escape(string): - string=string.decode('utf-8').encode('ascii','xmlcharrefreplace') #handle extended unicode - return ''.join(escape_table.get(i,i) for i in string) #still escape the basics - -def save_svg(bv,function): - filename = os.path.basename(bv.file.filename) - address = hex(function.start).replace('L','') - outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) - content = render_svg(function) - output = open(outputfile,'w') - output.write(content) - output.close() - #os.system('open %s' % outputfile) - -def instruction_data_flow(function,address): - ''' TODO: Extract data flow information ''' - length = function.view.get_instruction_length(function.arch,address) - bytes = function.view.read(address, length) - hex = bytes.encode('hex') - padded = ' '.join([hex[i:i+2] for i in range(0, len(hex), 2)]) - return 'Opcode: {bytes}'.format(bytes=padded) - -def render_svg(function): - graph = function.create_graph() - graph.layout_and_wait() - heightconst = 15 - ratio = 0.48 - widthconst = heightconst*ratio - - output = ''' - - - - -''' - output += ''' - - - - - - - - - - - - - - - '''.format(width=graph.width*widthconst, height=graph.height*heightconst) - output += ''' - Function Graph 0 - ''' - edges = '' - for i,block in enumerate(graph.blocks): - - #Calculate basic block location and coordinates - x = ((block.x) * widthconst) - y = ((block.y) * heightconst) - width = ((block.width) * widthconst) - height = ((block.height) * heightconst) - - #Render block - output += ' \n'.format(i=i) - output += ' Basic Block {i}\n'.format(i=i) - output += ' \n'.format(x=x,y=y,width=width,height=height) - - #Render instructions, unfortunately tspans don't allow copying/pasting more - #than one line at a time, need SVG 1.2 textarea tags for that it looks like - - output += ' \n'.format(x=x,y=y + (i + 1) * heightconst) - for i,line in enumerate(block.lines): - output += ' '.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) - hover = instruction_data_flow(function, line.address) - output += '{hover}'.format(hover=hover) - for token in line.tokens: - # TODO: add hover for hex, function, and reg tokens - output+='{text}'.format(text=escape(token.text),tokentype=token.type) - output += '\n' - output += ' \n' - output += ' \n' - - #Edges are rendered in a seperate chunk so they have priority over the - #basic blocks or else they'd render below them - - for edge in block.outgoing_edges: - points = "" - for x,y in edge.points: - points += str(x*widthconst)+","+str(y*heightconst) + " " - edges += ' \n'.format(type=edge.type,points=points) - output += ' ' + edges + '\n' - output += ' \n' - output += '' - return output - -PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py new file mode 100755 index 00000000..02a6d57e --- /dev/null +++ b/python/examples/export_svg.py @@ -0,0 +1,166 @@ +from binaryninja import * +import os,sys + +escape_table = { + "'": "'", + ">": ">", + "<": "<", + '"': """, + ' ': " " +} + +def escape(string): + string=string.decode('utf-8').encode('ascii','xmlcharrefreplace') #handle extended unicode + return ''.join(escape_table.get(i,i) for i in string) #still escape the basics + +def save_svg(bv,function): + filename = os.path.basename(bv.file.filename) + address = hex(function.start).replace('L','') + outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) + content = render_svg(function) + output = open(outputfile,'w') + output.write(content) + output.close() + #os.system('open %s' % outputfile) + +def instruction_data_flow(function,address): + ''' TODO: Extract data flow information ''' + length = function.view.get_instruction_length(function.arch,address) + bytes = function.view.read(address, length) + hex = bytes.encode('hex') + padded = ' '.join([hex[i:i+2] for i in range(0, len(hex), 2)]) + return 'Opcode: {bytes}'.format(bytes=padded) + +def render_svg(function): + graph = function.create_graph() + graph.layout_and_wait() + heightconst = 15 + ratio = 0.48 + widthconst = heightconst*ratio + + output = ''' + + + + +''' + output += ''' + + + + + + + + + + + + + + + '''.format(width=graph.width*widthconst, height=graph.height*heightconst) + output += ''' + Function Graph 0 + ''' + edges = '' + for i,block in enumerate(graph.blocks): + + #Calculate basic block location and coordinates + x = ((block.x) * widthconst) + y = ((block.y) * heightconst) + width = ((block.width) * widthconst) + height = ((block.height) * heightconst) + + #Render block + output += ' \n'.format(i=i) + output += ' Basic Block {i}\n'.format(i=i) + output += ' \n'.format(x=x,y=y,width=width,height=height) + + #Render instructions, unfortunately tspans don't allow copying/pasting more + #than one line at a time, need SVG 1.2 textarea tags for that it looks like + + output += ' \n'.format(x=x,y=y + (i + 1) * heightconst) + for i,line in enumerate(block.lines): + output += ' '.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) + hover = instruction_data_flow(function, line.address) + output += '{hover}'.format(hover=hover) + for token in line.tokens: + # TODO: add hover for hex, function, and reg tokens + output+='{text}'.format(text=escape(token.text),tokentype=token.type) + output += '\n' + output += ' \n' + output += ' \n' + + #Edges are rendered in a seperate chunk so they have priority over the + #basic blocks or else they'd render below them + + for edge in block.outgoing_edges: + points = "" + for x,y in edge.points: + points += str(x*widthconst)+","+str(y*heightconst) + " " + edges += ' \n'.format(type=edge.type,points=points) + output += ' ' + edges + '\n' + output += ' \n' + output += '' + return output + +PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) diff --git a/python/examples/instruction-iterator.py b/python/examples/instruction-iterator.py deleted file mode 100644 index 43bc000e..00000000 --- a/python/examples/instruction-iterator.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -import sys -try: - import binaryninja -except ImportError: - sys.path.append("/Applications/Binary Ninja.app/Contents/Resources/python/") - import binaryninja -import time - -if sys.platform.lower().startswith("linux"): - bintype="ELF" -elif sys.platform.lower() == "darwin": - bintype="Mach-O" -else: - raise Exception, "%s is not supported on this plugin" % sys.platform - -if len(sys.argv) > 1: - target = sys.argv[1] -else: - target = "/bin/ls" - -bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis_and_wait() - -print "-------- %s --------" % target -print "START: 0x%x" % bv.start -print "ENTRY: 0x%x" % bv.entry_point -print "ARCH: %s" % bv.arch.name -print "\n-------- Function List --------" - -""" print all the functions, their basic blocks, and their il instructions """ -for func in bv.functions: - print repr(func) - for block in func.low_level_il: - print "\t{0}".format(block) - - for insn in block: - print "\t\t{0}".format(insn) - - -""" print all the functions, their basic blocks, and their mc instructions """ -for func in bv.functions: - print repr(func) - for block in func: - print "\t{0}".format(block) - - for insn in block: - print "\t\t{0}".format(insn) diff --git a/python/examples/instruction_iterator.py b/python/examples/instruction_iterator.py new file mode 100644 index 00000000..43bc000e --- /dev/null +++ b/python/examples/instruction_iterator.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +import sys +try: + import binaryninja +except ImportError: + sys.path.append("/Applications/Binary Ninja.app/Contents/Resources/python/") + import binaryninja +import time + +if sys.platform.lower().startswith("linux"): + bintype="ELF" +elif sys.platform.lower() == "darwin": + bintype="Mach-O" +else: + raise Exception, "%s is not supported on this plugin" % sys.platform + +if len(sys.argv) > 1: + target = sys.argv[1] +else: + target = "/bin/ls" + +bv = binaryninja.BinaryViewType[bintype].open(target) +bv.update_analysis_and_wait() + +print "-------- %s --------" % target +print "START: 0x%x" % bv.start +print "ENTRY: 0x%x" % bv.entry_point +print "ARCH: %s" % bv.arch.name +print "\n-------- Function List --------" + +""" print all the functions, their basic blocks, and their il instructions """ +for func in bv.functions: + print repr(func) + for block in func.low_level_il: + print "\t{0}".format(block) + + for insn in block: + print "\t\t{0}".format(insn) + + +""" print all the functions, their basic blocks, and their mc instructions """ +for func in bv.functions: + print repr(func) + for block in func: + print "\t{0}".format(block) + + for insn in block: + print "\t\t{0}".format(insn) diff --git a/python/examples/jump-table.py b/python/examples/jump-table.py deleted file mode 100644 index 39fed1a5..00000000 --- a/python/examples/jump-table.py +++ /dev/null @@ -1,63 +0,0 @@ -# This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the destinations -# as indirect branch targets so that the flow graph reflects the jump table's control flow. -from binaryninja import * -import struct - -def find_jump_table(bv, addr): - for block in bv.get_basic_blocks_at(addr): - func = block.function - arch = func.arch - addrsize = arch.address_size - - # Grab the instruction tokens so that we can look for the table's starting address - tokens, length = arch.get_instruction_text(bv.read(addr, 16), addr) - - # Look for the next jump instruction, which may be the current instruction. Some jump tables will - # compute the address first then jump to the computed address as a separate instruction. - jump_addr = addr - while jump_addr < block.end: - info = arch.get_instruction_info(bv.read(jump_addr, 16), jump_addr) - if len(info.branches) != 0: - break - jump_addr += info.length - if jump_addr >= block.end: - print "Unable to find jump after instruction 0x%x" % addr - continue - print "Jump at 0x%x" % jump_addr - - # Collect the branch targets for any tables referenced by the clicked instruction - branches = [] - for token in tokens: - if token.type == "PossibleAddressToken": # Table addresses will be a "possible address" token - tbl = token.value - print "Found possible table at 0x%x" % tbl - i = 0 - while True: - # Read the next pointer from the table - data = bv.read(tbl + (i * addrsize), addrsize) - if len(data) == addrsize: - if addrsize == 4: - ptr = struct.unpack("= bv.start) and (ptr < bv.end): - print "Found destination 0x%x" % ptr - branches.append((arch, ptr)) - else: - # Once a value that is not a pointer is encountered, the jump table is ended - break - else: - # Reading invalid memory - break - - i += 1 - - # Set the indirect branch targets on the jump instruction to be the list of targets discovered - func.set_user_indirect_branches(arch, jump_addr, branches) - -# Create a plugin command so that the user can right click on an instruction referencing a jump table and -# invoke the command -PluginCommand.register_for_address("Process jump table", "Look for jump table destinations", find_jump_table) diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py new file mode 100644 index 00000000..39fed1a5 --- /dev/null +++ b/python/examples/jump_table.py @@ -0,0 +1,63 @@ +# This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the destinations +# as indirect branch targets so that the flow graph reflects the jump table's control flow. +from binaryninja import * +import struct + +def find_jump_table(bv, addr): + for block in bv.get_basic_blocks_at(addr): + func = block.function + arch = func.arch + addrsize = arch.address_size + + # Grab the instruction tokens so that we can look for the table's starting address + tokens, length = arch.get_instruction_text(bv.read(addr, 16), addr) + + # Look for the next jump instruction, which may be the current instruction. Some jump tables will + # compute the address first then jump to the computed address as a separate instruction. + jump_addr = addr + while jump_addr < block.end: + info = arch.get_instruction_info(bv.read(jump_addr, 16), jump_addr) + if len(info.branches) != 0: + break + jump_addr += info.length + if jump_addr >= block.end: + print "Unable to find jump after instruction 0x%x" % addr + continue + print "Jump at 0x%x" % jump_addr + + # Collect the branch targets for any tables referenced by the clicked instruction + branches = [] + for token in tokens: + if token.type == "PossibleAddressToken": # Table addresses will be a "possible address" token + tbl = token.value + print "Found possible table at 0x%x" % tbl + i = 0 + while True: + # Read the next pointer from the table + data = bv.read(tbl + (i * addrsize), addrsize) + if len(data) == addrsize: + if addrsize == 4: + ptr = struct.unpack("= bv.start) and (ptr < bv.end): + print "Found destination 0x%x" % ptr + branches.append((arch, ptr)) + else: + # Once a value that is not a pointer is encountered, the jump table is ended + break + else: + # Reading invalid memory + break + + i += 1 + + # Set the indirect branch targets on the jump instruction to be the list of targets discovered + func.set_user_indirect_branches(arch, jump_addr, branches) + +# Create a plugin command so that the user can right click on an instruction referencing a jump table and +# invoke the command +PluginCommand.register_for_address("Process jump table", "Look for jump table destinations", find_jump_table) diff --git a/python/examples/version-switcher.py b/python/examples/version-switcher.py deleted file mode 100644 index 6199c578..00000000 --- a/python/examples/version-switcher.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -import sys -import binaryninja -import datetime - -chandefault = binaryninja.UpdateChannel.list[0].name -channel = None -versions = [] - -def load_channel(newchannel): - global channel - global versions - if (channel != None and newchannel == channel.name): - print "Same channel, not updating." - else: - try: - print "Loading channel %s" % newchannel - channel = binaryninja.UpdateChannel[newchannel] - print "Loading versions..." - versions = channel.versions - except Exception: - print "%s is not a valid channel name. Defaulting to " % chandefault - channel = binaryninja.UpdateChannel[chandefault] - -def select(version): - done = False - date = datetime.datetime.fromtimestamp(version.time).strftime('%c') - while not done: - print "Version:\t%s" % version.version - print "Updated:\t%s" % date - print "Notes:\n\n-----\n%s" % version.notes - print "-----" - print "\t1)\tSwitch to version" - print "\t2)\tMain Menu" - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection == 2): - done = True - elif (selection == 1): - if (version.version == channel.latest_version.version): - print "Requesting update to latest version." - else: - print "Requesting update to prior version." - if binaryninja.are_auto_updates_enabled(): - print "Disabling automatic updates." - binaryninja.set_auto_updates_enabled(False) - if (version.version == binaryninja.core_version): - print "Already running %s" % version.version - else: - print "version.version %s" % version.version - print "binaryninja.core_version %s" % binaryninja.core_version - print "Updating..." - print version.update() - #forward updating won't work without reloading - sys.exit() - else: - print "Invalid selection" - -def list_channels(): - done = False - print "\tSelect channel:\n" - while not done: - channel_list = binaryninja.UpdateChannel.list - for index, item in enumerate(channel_list): - print "\t%d)\t%s" % (index+1, item.name) - print "\t%d)\t%s" % (len(channel_list)+1, "Main Menu") - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection <= 0 or selection > len(channel_list)+1): - print "%s is an invalid choice." % selection - else: - done = True - if (selection != len(channel_list) + 1): - load_channel(channel_list[selection - 1].name) - -def toggle_updates(): - binaryninja.set_auto_updates_enabled(not binaryninja.are_auto_updates_enabled()) - -def main(): - global channel - done = False - load_channel(chandefault) - while not done: - print "\n\tBinary Ninja Version Switcher" - print "\t\tCurrent Channel:\t%s" % channel.name - print "\t\tCurrent Version:\t%s" % binaryninja.core_version - print "\t\tAuto-Updates On:\t%s\n" % binaryninja.are_auto_updates_enabled() - for index, version in enumerate(versions): - date = datetime.datetime.fromtimestamp(version.time).strftime('%c') - print "\t%d)\t%s (%s)" % (index + 1, version.version, date) - print "\t%d)\t%s" % (len(versions) + 1, "Switch Channel") - print "\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates") - print "\t%d)\t%s" % (len(versions) + 3, "Exit") - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection <= 0 or selection > len(versions) + 3): - print "%d is an invalid choice.\n\n" % selection - else: - if (selection == len(versions) + 3): - done = True - elif (selection == len(versions) + 2): - toggle_updates() - elif (selection == len(versions) + 1): - list_channels() - else: - select(versions[selection - 1]) - - -if __name__ == "__main__": - main() diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py new file mode 100644 index 00000000..6199c578 --- /dev/null +++ b/python/examples/version_switcher.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +import sys +import binaryninja +import datetime + +chandefault = binaryninja.UpdateChannel.list[0].name +channel = None +versions = [] + +def load_channel(newchannel): + global channel + global versions + if (channel != None and newchannel == channel.name): + print "Same channel, not updating." + else: + try: + print "Loading channel %s" % newchannel + channel = binaryninja.UpdateChannel[newchannel] + print "Loading versions..." + versions = channel.versions + except Exception: + print "%s is not a valid channel name. Defaulting to " % chandefault + channel = binaryninja.UpdateChannel[chandefault] + +def select(version): + done = False + date = datetime.datetime.fromtimestamp(version.time).strftime('%c') + while not done: + print "Version:\t%s" % version.version + print "Updated:\t%s" % date + print "Notes:\n\n-----\n%s" % version.notes + print "-----" + print "\t1)\tSwitch to version" + print "\t2)\tMain Menu" + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection == 2): + done = True + elif (selection == 1): + if (version.version == channel.latest_version.version): + print "Requesting update to latest version." + else: + print "Requesting update to prior version." + if binaryninja.are_auto_updates_enabled(): + print "Disabling automatic updates." + binaryninja.set_auto_updates_enabled(False) + if (version.version == binaryninja.core_version): + print "Already running %s" % version.version + else: + print "version.version %s" % version.version + print "binaryninja.core_version %s" % binaryninja.core_version + print "Updating..." + print version.update() + #forward updating won't work without reloading + sys.exit() + else: + print "Invalid selection" + +def list_channels(): + done = False + print "\tSelect channel:\n" + while not done: + channel_list = binaryninja.UpdateChannel.list + for index, item in enumerate(channel_list): + print "\t%d)\t%s" % (index+1, item.name) + print "\t%d)\t%s" % (len(channel_list)+1, "Main Menu") + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection <= 0 or selection > len(channel_list)+1): + print "%s is an invalid choice." % selection + else: + done = True + if (selection != len(channel_list) + 1): + load_channel(channel_list[selection - 1].name) + +def toggle_updates(): + binaryninja.set_auto_updates_enabled(not binaryninja.are_auto_updates_enabled()) + +def main(): + global channel + done = False + load_channel(chandefault) + while not done: + print "\n\tBinary Ninja Version Switcher" + print "\t\tCurrent Channel:\t%s" % channel.name + print "\t\tCurrent Version:\t%s" % binaryninja.core_version + print "\t\tAuto-Updates On:\t%s\n" % binaryninja.are_auto_updates_enabled() + for index, version in enumerate(versions): + date = datetime.datetime.fromtimestamp(version.time).strftime('%c') + print "\t%d)\t%s (%s)" % (index + 1, version.version, date) + print "\t%d)\t%s" % (len(versions) + 1, "Switch Channel") + print "\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates") + print "\t%d)\t%s" % (len(versions) + 3, "Exit") + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection <= 0 or selection > len(versions) + 3): + print "%d is an invalid choice.\n\n" % selection + else: + if (selection == len(versions) + 3): + done = True + elif (selection == len(versions) + 2): + toggle_updates() + elif (selection == len(versions) + 1): + list_channels() + else: + select(versions[selection - 1]) + + +if __name__ == "__main__": + main() -- cgit v1.3.1 From fb0d896cd5cc3197a8dd259a9ca53f2788ef5fdb Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Wed, 14 Sep 2016 00:28:27 -0400 Subject: Add APIs for instruction and block highlighting --- basicblock.cpp | 110 +++++++++++++++++++++++++++++++++++ binaryninjaapi.h | 30 ++++++++++ binaryninjacore.h | 40 +++++++++++++ function.cpp | 123 +++++++++++++++++++++++++++++++++++++++ functiongraphblock.cpp | 6 ++ python/__init__.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 461 insertions(+) (limited to 'python') diff --git a/basicblock.cpp b/basicblock.cpp index 377c5704..93a279a1 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -172,3 +172,113 @@ vector BasicBlock::GetDisassemblyText(DisassemblySettings* BNFreeDisassemblyTextLines(lines, count); return result; } + + +BNHighlightColor BasicBlock::GetBasicBlockHighlight() +{ + return BNGetBasicBlockHighlight(m_object); +} + + +void BasicBlock::SetAutoBasicBlockHighlight(BNHighlightColor color) +{ + BNSetAutoBasicBlockHighlight(m_object, color); +} + + +void BasicBlock::SetAutoBasicBlockHighlight(BNHighlightStandardColor color, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = StandardHighlightColor; + hc.color = color; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetAutoBasicBlockHighlight(hc); +} + + +void BasicBlock::SetAutoBasicBlockHighlight(BNHighlightStandardColor color, BNHighlightStandardColor mixColor, + uint8_t mix, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = MixedHighlightColor; + hc.color = color; + hc.mixColor = mixColor; + hc.mix = mix; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetAutoBasicBlockHighlight(hc); +} + + +void BasicBlock::SetAutoBasicBlockHighlight(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = CustomHighlightColor; + hc.color = NoHighlightColor; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = r; + hc.g = g; + hc.b = b; + hc.alpha = alpha; + SetAutoBasicBlockHighlight(hc); +} + + +void BasicBlock::SetUserBasicBlockHighlight(BNHighlightColor color) +{ + BNSetUserBasicBlockHighlight(m_object, color); +} + + +void BasicBlock::SetUserBasicBlockHighlight(BNHighlightStandardColor color, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = StandardHighlightColor; + hc.color = color; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetUserBasicBlockHighlight(hc); +} + + +void BasicBlock::SetUserBasicBlockHighlight(BNHighlightStandardColor color, BNHighlightStandardColor mixColor, + uint8_t mix, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = MixedHighlightColor; + hc.color = color; + hc.mixColor = mixColor; + hc.mix = mix; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetUserBasicBlockHighlight(hc); +} + + +void BasicBlock::SetUserBasicBlockHighlight(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = CustomHighlightColor; + hc.color = NoHighlightColor; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = r; + hc.g = g; + hc.b = b; + hc.alpha = alpha; + SetUserBasicBlockHighlight(hc); +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d4df479b..30285380 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1561,6 +1561,18 @@ namespace BinaryNinja std::vector> GetAnnotations(); std::vector GetDisassemblyText(DisassemblySettings* settings); + + BNHighlightColor GetBasicBlockHighlight(); + void SetAutoBasicBlockHighlight(BNHighlightColor color); + void SetAutoBasicBlockHighlight(BNHighlightStandardColor color, uint8_t alpha = 255); + void SetAutoBasicBlockHighlight(BNHighlightStandardColor color, BNHighlightStandardColor mixColor, + uint8_t mix, uint8_t alpha = 255); + void SetAutoBasicBlockHighlight(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha = 255); + void SetUserBasicBlockHighlight(BNHighlightColor color); + void SetUserBasicBlockHighlight(BNHighlightStandardColor color, uint8_t alpha = 255); + void SetUserBasicBlockHighlight(BNHighlightStandardColor color, BNHighlightStandardColor mixColor, + uint8_t mix, uint8_t alpha = 255); + void SetUserBasicBlockHighlight(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha = 255); }; struct StackVariable @@ -1633,6 +1645,7 @@ namespace BinaryNinja bool NeedsUpdate() const; std::vector> GetBasicBlocks() const; + Ref GetBasicBlockAtAddress(Architecture* arch, uint64_t addr) const; void MarkRecentUse(); std::string GetCommentForAddress(uint64_t addr) const; @@ -1692,6 +1705,22 @@ namespace BinaryNinja void SetIntegerConstantDisplayType(Architecture* arch, uint64_t instrAddr, uint64_t value, size_t operand, BNIntegerDisplayType type); + BNHighlightColor GetInstructionHighlight(Architecture* arch, uint64_t addr); + void SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightColor color); + void SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + uint8_t alpha = 255); + void SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + BNHighlightStandardColor mixColor, uint8_t mix, uint8_t alpha = 255); + void SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, uint8_t r, uint8_t g, uint8_t b, + uint8_t alpha = 255); + void SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightColor color); + void SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + uint8_t alpha = 255); + void SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + BNHighlightStandardColor mixColor, uint8_t mix, uint8_t alpha = 255); + void SetUserInstructionHighlight(Architecture* arch, uint64_t addr, uint8_t r, uint8_t g, uint8_t b, + uint8_t alpha = 255); + void Reanalyze(); void RequestAdvancedAnalysisData(); @@ -1731,6 +1760,7 @@ namespace BinaryNinja public: FunctionGraphBlock(BNFunctionGraphBlock* block); + Ref GetBasicBlock() const; Ref GetArchitecture() const; uint64_t GetStart() const; uint64_t GetEnd() const; diff --git a/binaryninjacore.h b/binaryninjacore.h index 51bf3391..de7b77fd 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1020,6 +1020,35 @@ extern "C" size_t size; }; + enum BNHighlightColorStyle + { + StandardHighlightColor = 0, + MixedHighlightColor = 1, + CustomHighlightColor = 2 + }; + + enum BNHighlightStandardColor + { + NoHighlightColor = 0, + BlueHighlightColor = 1, + GreenHighlightColor = 2, + CyanHighlightColor = 3, + RedHighlightColor = 4, + MagentaHighlightColor = 5, + YellowHighlightColor = 6, + OrangeHighlightColor = 7, + WhiteHighlightColor = 8, + BlackHighlightColor = 9 + }; + + struct BNHighlightColor + { + BNHighlightColorStyle style; + BNHighlightStandardColor color; + BNHighlightStandardColor mixColor; + uint8_t mix, r, g, b, alpha; + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -1418,6 +1447,7 @@ extern "C" BINARYNINJACOREAPI void BNFreeBasicBlock(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlock** BNGetFunctionBasicBlockList(BNFunction* func, size_t* count); BINARYNINJACOREAPI void BNFreeBasicBlockList(BNBasicBlock** blocks, size_t count); + BINARYNINJACOREAPI BNBasicBlock* BNGetFunctionBasicBlockAtAddress(BNFunction* func, BNArchitecture* arch, uint64_t addr); BINARYNINJACOREAPI BNBasicBlock* BNGetRecentBasicBlockForAddress(BNBinaryView* view, uint64_t addr); BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlocksForAddress(BNBinaryView* view, uint64_t addr, size_t* count); BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlocksStartingAtAddress(BNBinaryView* view, uint64_t addr, size_t* count); @@ -1575,6 +1605,15 @@ extern "C" BINARYNINJACOREAPI void BNReanalyzeAllFunctions(BNBinaryView* view); BINARYNINJACOREAPI void BNReanalyzeFunction(BNFunction* func); + BINARYNINJACOREAPI BNHighlightColor BNGetInstructionHighlight(BNFunction* func, BNArchitecture* arch, uint64_t addr); + BINARYNINJACOREAPI void BNSetAutoInstructionHighlight(BNFunction* func, BNArchitecture* arch, uint64_t addr, + BNHighlightColor color); + BINARYNINJACOREAPI void BNSetUserInstructionHighlight(BNFunction* func, BNArchitecture* arch, uint64_t addr, + BNHighlightColor color); + BINARYNINJACOREAPI BNHighlightColor BNGetBasicBlockHighlight(BNBasicBlock* block); + BINARYNINJACOREAPI void BNSetAutoBasicBlockHighlight(BNBasicBlock* block, BNHighlightColor color); + BINARYNINJACOREAPI void BNSetUserBasicBlockHighlight(BNBasicBlock* block, BNHighlightColor color); + // Disassembly settings BINARYNINJACOREAPI BNDisassemblySettings* BNCreateDisassemblySettings(void); BINARYNINJACOREAPI BNDisassemblySettings* BNNewDisassemblySettingsReference(BNDisassemblySettings* settings); @@ -1622,6 +1661,7 @@ extern "C" BINARYNINJACOREAPI BNFunctionGraphBlock* BNNewFunctionGraphBlockReference(BNFunctionGraphBlock* block); BINARYNINJACOREAPI void BNFreeFunctionGraphBlock(BNFunctionGraphBlock* block); + BINARYNINJACOREAPI BNBasicBlock* BNGetFunctionGraphBasicBlock(BNFunctionGraphBlock* block); BINARYNINJACOREAPI BNArchitecture* BNGetFunctionGraphBlockArchitecture(BNFunctionGraphBlock* block); BINARYNINJACOREAPI uint64_t BNGetFunctionGraphBlockStart(BNFunctionGraphBlock* block); BINARYNINJACOREAPI uint64_t BNGetFunctionGraphBlockEnd(BNFunctionGraphBlock* block); diff --git a/function.cpp b/function.cpp index de12918f..109b6f49 100644 --- a/function.cpp +++ b/function.cpp @@ -100,6 +100,15 @@ vector> Function::GetBasicBlocks() const } +Ref Function::GetBasicBlockAtAddress(Architecture* arch, uint64_t addr) const +{ + BNBasicBlock* block = BNGetFunctionBasicBlockAtAddress(m_object, arch->GetObject(), addr); + if (!block) + return nullptr; + return new BasicBlock(block); +} + + void Function::MarkRecentUse() { BNMarkFunctionAsRecentlyUsed(m_object); @@ -582,6 +591,120 @@ void Function::SetIntegerConstantDisplayType(Architecture* arch, uint64_t instrA } +BNHighlightColor Function::GetInstructionHighlight(Architecture* arch, uint64_t addr) +{ + return BNGetInstructionHighlight(m_object, arch->GetObject(), addr); +} + + +void Function::SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightColor color) +{ + BNSetAutoInstructionHighlight(m_object, arch->GetObject(), addr, color); +} + + +void Function::SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = StandardHighlightColor; + hc.color = color; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetAutoInstructionHighlight(arch, addr, hc); +} + + +void Function::SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + BNHighlightStandardColor mixColor, uint8_t mix, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = MixedHighlightColor; + hc.color = color; + hc.mixColor = mixColor; + hc.mix = mix; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetAutoInstructionHighlight(arch, addr, hc); +} + + +void Function::SetAutoInstructionHighlight(Architecture* arch, uint64_t addr, uint8_t r, uint8_t g, uint8_t b, + uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = CustomHighlightColor; + hc.color = NoHighlightColor; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = r; + hc.g = g; + hc.b = b; + hc.alpha = alpha; + SetAutoInstructionHighlight(arch, addr, hc); +} + + +void Function::SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightColor color) +{ + BNSetUserInstructionHighlight(m_object, arch->GetObject(), addr, color); +} + + +void Function::SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = StandardHighlightColor; + hc.color = color; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetUserInstructionHighlight(arch, addr, hc); +} + + +void Function::SetUserInstructionHighlight(Architecture* arch, uint64_t addr, BNHighlightStandardColor color, + BNHighlightStandardColor mixColor, uint8_t mix, uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = MixedHighlightColor; + hc.color = color; + hc.mixColor = mixColor; + hc.mix = mix; + hc.r = 0; + hc.g = 0; + hc.b = 0; + hc.alpha = alpha; + SetUserInstructionHighlight(arch, addr, hc); +} + + +void Function::SetUserInstructionHighlight(Architecture* arch, uint64_t addr, uint8_t r, uint8_t g, uint8_t b, + uint8_t alpha) +{ + BNHighlightColor hc; + hc.style = CustomHighlightColor; + hc.color = NoHighlightColor; + hc.mixColor = NoHighlightColor; + hc.mix = 0; + hc.r = r; + hc.g = g; + hc.b = b; + hc.alpha = alpha; + SetUserInstructionHighlight(arch, addr, hc); +} + + void Function::Reanalyze() { BNReanalyzeFunction(m_object); diff --git a/functiongraphblock.cpp b/functiongraphblock.cpp index 436339f6..47261453 100644 --- a/functiongraphblock.cpp +++ b/functiongraphblock.cpp @@ -32,6 +32,12 @@ FunctionGraphBlock::FunctionGraphBlock(BNFunctionGraphBlock* block) } +Ref FunctionGraphBlock::GetBasicBlock() const +{ + return new BasicBlock(BNGetFunctionGraphBasicBlock(m_object)); +} + + Ref FunctionGraphBlock::GetArchitecture() const { return new CoreArchitecture(BNGetFunctionGraphBlockArchitecture(m_object)); diff --git a/python/__init__.py b/python/__init__.py index 33202a2c..b29c9f80 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4381,6 +4381,93 @@ class IndirectBranchInfo: def __repr__(self): return " %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) +class HighlightColor(object): + def __init__(self, color = None, mix_color = None, mix = None, red = None, green = None, blue = None, alpha = 255): + if (red is not None) and (green is not None) and (blue is not None): + self.style = core.CustomHighlightColor + self.red = red + self.green = green + self.blue = blue + elif (mix_color is not None) and (mix is not None): + self.style = core.MixedHighlightColor + if color is None: + self.color = core.NoHighlightColor + else: + self.color = color + self.mix_color = mix_color + self.mix = mix + else: + self.style = core.StandardHighlightColor + if color is None: + self.color = core.NoHighlightColor + else: + self.color = color + self.alpha = alpha + + def _standard_color_to_str(self, color): + if color == core.NoHighlightColor: + return "none" + if color == core.BlueHighlightColor: + return "blue" + if color == core.GreenHighlightColor: + return "green" + if color == core.CyanHighlightColor: + return "cyan" + if color == core.RedHighlightColor: + return "red" + if color == core.MagentaHighlightColor: + return "magenta" + if color == core.YellowHighlightColor: + return "yellow" + if color == core.OrangeHighlightColor: + return "orange" + if color == core.WhiteHighlightColor: + return "white" + if color == core.BlackHighlightColor: + return "black" + return "%d" % color + + def __repr__(self): + if self.style == core.StandardHighlightColor: + if self.alpha == 255: + return "" % self._standard_color_to_str(self.color) + return "" % (self._standard_color_to_str(self.color), self.alpha) + if self.style == core.MixedHighlightColor: + if self.alpha == 255: + return "" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix) + return "" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix, self.alpha) + if self.style == core.CustomHighlightColor: + if self.alpha == 255: + return "" % (self.red, self.green, self.blue) + return "" % (self.red, self.green, self.blue, self.alpha) + return "" + + def _get_core_struct(self): + result = core.BNHighlightColor() + result.style = self.style + result.color = core.NoHighlightColor + result.mix_color = core.NoHighlightColor + result.mix = 0 + result.r = 0 + result.g = 0 + result.b = 0 + result.alpha = self.alpha + + if self.style == core.StandardHighlightColor: + result.color = self.color + elif self.style == core.MixedHighlightColor: + result.color = self.color + result.mixColor = self.mix_color + result.mix = self.mix + elif self.style == core.CustomHighlightColor: + result.r = self.red + result.g = self.green + result.b = self.blue + + return result + class Function(object): def __init__(self, view, handle): self._view = view @@ -4790,6 +4877,32 @@ class Function(object): core.BNReleaseAdvancedFunctionAnalysisData(self.handle) self._advanced_analysis_requests -= 1 + def get_basic_block_at(self, arch, addr): + block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) + if not block: + return None + return BasicBlock(self._view, handle = block) + + def get_instr_highlight(self, arch, addr): + color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) + if color.style == core.StandardHighlightColor: + return HighlightColor(color = color.color, alpha = color.alpha) + elif color.style == core.MixedHighlightColor: + return HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) + elif color.style == core.CustomHighlightColor: + return HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) + return HighlightColor(color = core.NoHighlightColor) + + def set_auto_instr_highlight(self, arch, addr, color): + if not isinstance(color, HighlightColor): + color = HighlightColor(color = color) + core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + + def set_user_instr_highlight(self, arch, addr, color): + if not isinstance(color, HighlightColor): + color = HighlightColor(color = color) + core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + class AdvancedFunctionAnalysisDataRequestor(object): def __init__(self, func = None): self._function = func @@ -4902,6 +5015,22 @@ class BasicBlock(object): def disassembly_text(self): return self.get_disassembly_text() + @property + def highlight(self): + """Highlight color for basic block""" + color = core.BNGetBasicBlockHighlight(self.handle) + if color.style == core.StandardHighlightColor: + return HighlightColor(color = color.color, alpha = color.alpha) + elif color.style == core.MixedHighlightColor: + return HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) + elif color.style == core.CustomHighlightColor: + return HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) + return HighlightColor(color = core.NoHighlightColor) + + @highlight.setter + def highlight(self, value): + self.set_user_highlight(value) + def __setattr__(self, name, value): try: object.__setattr__(self,name,value) @@ -4956,6 +5085,16 @@ class BasicBlock(object): core.BNFreeDisassemblyTextLines(lines, count.value) return result + def set_auto_highlight(self, color): + if not isinstance(color, HighlightColor): + color = HighlightColor(color = color) + core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) + + def set_user_highlight(self, color): + if not isinstance(color, HighlightColor): + color = HighlightColor(color = color) + core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) + class LowLevelILBasicBlock(BasicBlock): def __init__(self, view, handle, owner): super(LowLevelILBasicBlock, self).__init__(view, handle) @@ -4998,6 +5137,19 @@ class FunctionGraphBlock(object): def __del__(self): core.BNFreeFunctionGraphBlock(self.handle) + @property + def basic_block(self): + """Basic block associated with this part of the funciton graph (read-only)""" + block = core.BNGetFunctionGraphBasicBlock(self.handle) + func = core.BNGetBasicBlockFunction(block) + if func is None: + core.BNFreeBasicBlock(block) + block = None + else: + block = BasicBlock(BinaryView(None, handle = core.BNGetFunctionData(func)), block) + core.BNFreeFunction(func) + return block + @property def arch(self): """Function graph block architecture (read-only)""" -- cgit v1.3.1 From 124cf1bc4f465a915621b7ac81af94b9a69a937a Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Thu, 15 Sep 2016 01:55:28 -0400 Subject: Add APIs to display busy indicator for long running plugin tasks --- backgroundtask.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ binaryninjaapi.h | 19 +++++++++++ binaryninjacore.h | 16 +++++++++ python/__init__.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 backgroundtask.cpp (limited to 'python') diff --git a/backgroundtask.cpp b/backgroundtask.cpp new file mode 100644 index 00000000..aebf980c --- /dev/null +++ b/backgroundtask.cpp @@ -0,0 +1,75 @@ +#include "binaryninjaapi.h" + +using namespace std; +using namespace BinaryNinja; + + +BackgroundTask::BackgroundTask(BNBackgroundTask* task) +{ + m_object = task; +} + + +BackgroundTask::BackgroundTask(const string& initialText, bool canCancel) +{ + m_object = BNBeginBackgroundTask(initialText.c_str(), canCancel); +} + + +bool BackgroundTask::CanCancel() const +{ + return BNCanCancelBackgroundTask(m_object); +} + + +bool BackgroundTask::IsCancelled() const +{ + return BNIsBackgroundTaskCancelled(m_object); +} + + +bool BackgroundTask::IsFinished() const +{ + return BNIsBackgroundTaskFinished(m_object); +} + + +string BackgroundTask::GetProgressText() const +{ + char* text = BNGetBackgroundTaskProgressText(m_object); + string result = text; + BNFreeString(text); + return result; +} + + +void BackgroundTask::Cancel() +{ + BNCancelBackgroundTask(m_object); +} + + +void BackgroundTask::Finish() +{ + BNFinishBackgroundTask(m_object); +} + + +void BackgroundTask::SetProgressText(const string& text) +{ + BNSetBackgroundTaskProgressText(m_object, text.c_str()); +} + + +vector> BackgroundTask::GetRunningTasks() +{ + size_t count; + BNBackgroundTask** tasks = BNGetRunningBackgroundTasks(&count); + + vector> result; + for (size_t i = 0; i < count; i++) + result.push_back(new BackgroundTask(BNNewBackgroundTaskReference(tasks[i]))); + + BNFreeBackgroundTaskList(tasks, count); + return result; +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 30285380..3400cca7 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2260,4 +2260,23 @@ namespace BinaryNinja public: virtual void AddMainThreadAction(MainThreadAction* action) = 0; }; + + class BackgroundTask: public CoreRefCountObject + { + public: + BackgroundTask(BNBackgroundTask* task); + BackgroundTask(const std::string& initialText, bool canCancel); + + bool CanCancel() const; + bool IsCancelled() const; + bool IsFinished() const; + std::string GetProgressText() const; + + void Cancel(); + void Finish(); + void SetProgressText(const std::string& text); + + static std::vector> GetRunningTasks(); + }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index de7b77fd..89dceba1 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -102,6 +102,7 @@ extern "C" struct BNScriptingProvider; struct BNScriptingInstance; struct BNMainThreadAction; + struct BNBackgroundTask; //! Console log levels enum BNLogLevel @@ -2014,6 +2015,21 @@ extern "C" BINARYNINJACOREAPI size_t BNGetWorkerThreadCount(void); BINARYNINJACOREAPI void BNSetWorkerThreadCount(size_t count); + // Background task progress reporting + BINARYNINJACOREAPI BNBackgroundTask* BNBeginBackgroundTask(const char* initialText, bool canCancel); + BINARYNINJACOREAPI void BNFinishBackgroundTask(BNBackgroundTask* task); + BINARYNINJACOREAPI void BNSetBackgroundTaskProgressText(BNBackgroundTask* task, const char* text); + BINARYNINJACOREAPI bool BNIsBackgroundTaskCancelled(BNBackgroundTask* task); + + BINARYNINJACOREAPI BNBackgroundTask** BNGetRunningBackgroundTasks(size_t* count); + BINARYNINJACOREAPI BNBackgroundTask* BNNewBackgroundTaskReference(BNBackgroundTask* task); + BINARYNINJACOREAPI void BNFreeBackgroundTask(BNBackgroundTask* task); + BINARYNINJACOREAPI void BNFreeBackgroundTaskList(BNBackgroundTask** tasks, size_t count); + BINARYNINJACOREAPI char* BNGetBackgroundTaskProgressText(BNBackgroundTask* task); + BINARYNINJACOREAPI bool BNCanCancelBackgroundTask(BNBackgroundTask* task); + BINARYNINJACOREAPI void BNCancelBackgroundTask(BNBackgroundTask* task); + BINARYNINJACOREAPI bool BNIsBackgroundTaskFinished(BNBackgroundTask* task); + #ifdef __cplusplus } #endif diff --git a/python/__init__.py b/python/__init__.py index b29c9f80..6abbdfa6 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -9961,6 +9961,104 @@ class MainThreadActionHandler(object): def add_action(self, action): pass +class _BackgroundTaskMetaclass(type): + @property + def list(self): + """List all running background tasks (read-only)""" + count = ctypes.c_ulonglong() + tasks = core.BNGetRunningBackgroundTasks(count) + result = [] + for i in xrange(0, count.value): + result.append(BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i]))) + core.BNFreeBackgroundTaskList(tasks) + return result + + def __iter__(self): + _init_plugins() + count = ctypes.c_ulonglong() + tasks = core.BNGetRunningBackgroundTasks(count) + try: + for i in xrange(0, count.value): + yield BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i])) + finally: + core.BNFreeBackgroundTaskList(tasks) + +class BackgroundTask(object): + __metaclass__ = _BackgroundTaskMetaclass + + def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): + if handle is None: + self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) + else: + self.handle = handle + + def __del__(self): + core.BNFreeBackgroundTask(self.handle) + + @property + def progress(self): + """Text description of the progress of the background task (displayed in status bar of the UI)""" + return core.BNGetBackgroundTaskProgressText(self.handle) + + @progress.setter + def progress(self, value): + core.BNSetBackgroundTaskProgressText(self.handle, str(value)) + + @property + def can_cancel(self): + """Whether the task can be cancelled (read-only)""" + return core.BNCanCancelBackgroundTask(self.handle) + + @property + def finished(self): + """Whether the task has finished""" + return core.BNIsBackgroundTaskFinished(self.handle) + + @finished.setter + def finished(self, value): + if value: + self.finish() + + def finish(self): + core.BNFinishBackgroundTask(self.handle) + + @property + def cancelled(self): + """Whether the task has been cancelled""" + return core.BNIsBackgroundTaskCancelled(self.handle) + + @cancelled.setter + def cancelled(self, value): + if value: + self.cancel() + + def cancel(self): + core.BNCancelBackgroundTask(self.handle) + +class BackgroundTaskThread(BackgroundTask): + def __init__(self, initial_progress_text = "", can_cancel = False): + class _Thread(threading.Thread): + def __init__(self, task): + threading.Thread.__init__(self) + self.task = task + + def run(self): + self.task.run() + self.task.finish() + self.task = None + + BackgroundTask.__init__(self, initial_progress_text, can_cancel) + self.thread = _Thread(self) + + def run(self): + pass + + def start(self): + self.thread.start() + + def join(self): + self.thread.join() + def LLIL_TEMP(n): return n | 0x80000000 -- cgit v1.3.1 From 9c94170020ec15eb2e6ac621915257ef2e270c7a Mon Sep 17 00:00:00 2001 From: rootbsd Date: Fri, 16 Sep 2016 10:19:42 +0200 Subject: Add the color support to export_svg.py --- python/examples/export_svg.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index 02a6d57e..ebf3e2d1 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -1,6 +1,8 @@ from binaryninja import * import os,sys +colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} + escape_table = { "'": "'", ">": ">", @@ -21,7 +23,7 @@ def save_svg(bv,function): output = open(outputfile,'w') output.write(content) output.close() - #os.system('open %s' % outputfile) + os.system('%s' % outputfile) def instruction_data_flow(function,address): ''' TODO: Extract data flow information ''' @@ -46,7 +48,6 @@ def render_svg(function): background-color: rgb(42, 42, 42); } .basicblock { - fill: rgb(74, 74, 74); stroke: rgb(224, 224, 224); } .edge { @@ -133,7 +134,16 @@ def render_svg(function): #Render block output += ' \n'.format(i=i) output += ' Basic Block {i}\n'.format(i=i) - output += ' \n'.format(x=x,y=y,width=width,height=height) + rgb=colors['none'] + try: + bb = block.basic_block + color_code = bb.highlight._get_core_struct().color + color_str = bb.highlight._standard_color_to_str(color_code) + if color_str in colors: + rgb=colors[color_str] + except: + pass + output += ' \n'.format(x=x,y=y,width=width,height=height,r=rgb[0],g=rgb[1],b=rgb[2]) #Render instructions, unfortunately tspans don't allow copying/pasting more #than one line at a time, need SVG 1.2 textarea tags for that it looks like @@ -164,3 +174,4 @@ def render_svg(function): return output PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) + -- cgit v1.3.1 From 2ec606fab8754dad2135deec2e72e87d39382978 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Fri, 16 Sep 2016 19:09:01 -0400 Subject: Add APIs for user interaction from plugins --- binaryninjaapi.h | 50 ++++++++++ binaryninjacore.h | 39 ++++++++ binaryview.cpp | 33 +++++++ interaction.cpp | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++++ python/__init__.py | 248 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 656 insertions(+) create mode 100644 interaction.cpp (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 3400cca7..720a8eb6 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -275,6 +275,7 @@ namespace BinaryNinja class DataBuffer; class MainThreadAction; class MainThreadActionHandler; + class InteractionHandler; /*! Logs to the error console with the given BNLogLevel. @@ -381,6 +382,26 @@ namespace BinaryNinja size_t GetWorkerThreadCount(); void SetWorkerThreadCount(size_t count); + std::string MarkdownToHTML(const std::string& contents); + + void RegisterInteractionHandler(InteractionHandler* handler); + + void ShowPlainTextReport(const std::string& title, const std::string& contents); + void ShowMarkdownReport(const std::string& title, const std::string& contents, + const std::string& plainText = ""); + void ShowHTMLReport(const std::string& title, const std::string& contents, + const std::string& plainText = ""); + + bool GetTextLineInput(std::string& result, const std::string& prompt, const std::string& title); + bool GetIntegerInput(int64_t& result, const std::string& prompt, const std::string& title); + bool GetAddressInput(uint64_t& result, const std::string& prompt, const std::string& title); + bool GetChoiceInput(size_t& idx, const std::string& prompt, const std::string& title, + const std::vector& choices); + bool GetOpenFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = ""); + bool GetSaveFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = "", + const std::string& defaultName = ""); + bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + class DataBuffer { BNDataBuffer* m_buffer; @@ -935,6 +956,13 @@ namespace BinaryNinja bool FindNextData(uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = NoFindFlags); void Reanalyze(); + + void ShowPlainTextReport(const std::string& title, const std::string& contents); + void ShowMarkdownReport(const std::string& title, const std::string& contents, const std::string& plainText); + void ShowHTMLReport(const std::string& title, const std::string& contents, const std::string& plainText); + bool GetAddressInput(uint64_t& result, const std::string& prompt, const std::string& title); + bool GetAddressInput(uint64_t& result, const std::string& prompt, const std::string& title, + uint64_t currentAddress); }; class BinaryData: public BinaryView @@ -2279,4 +2307,26 @@ namespace BinaryNinja static std::vector> GetRunningTasks(); }; + + class InteractionHandler + { + public: + virtual void ShowPlainTextReport(Ref view, const std::string& title, const std::string& contents) = 0; + virtual void ShowMarkdownReport(Ref view, const std::string& title, const std::string& contents, + const std::string& plainText); + virtual void ShowHTMLReport(Ref view, const std::string& title, const std::string& contents, + const std::string& plainText); + + virtual bool GetTextLineInput(std::string& result, const std::string& prompt, const std::string& title) = 0; + virtual bool GetIntegerInput(int64_t& result, const std::string& prompt, const std::string& title); + virtual bool GetAddressInput(uint64_t& result, const std::string& prompt, const std::string& title, + Ref view, uint64_t currentAddr); + virtual bool GetChoiceInput(size_t& idx, const std::string& prompt, const std::string& title, + const std::vector& choices) = 0; + virtual bool GetOpenFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = ""); + virtual bool GetSaveFileNameInput(std::string& result, const std::string& prompt, + const std::string& ext = "", const std::string& defaultName = ""); + virtual bool GetDirectoryNameInput(std::string& result, const std::string& prompt, + const std::string& defaultName = ""); + }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index 89dceba1..34806b6c 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1050,6 +1050,26 @@ extern "C" uint8_t mix, r, g, b, alpha; }; + struct BNInteractionHandlerCallbacks + { + void* context; + void (*showPlainTextReport)(void* ctxt, BNBinaryView* view, const char* title, const char* contents); + void (*showMarkdownReport)(void* ctxt, BNBinaryView* view, const char* title, const char* contents, + const char* plaintext); + void (*showHTMLReport)(void* ctxt, BNBinaryView* view, const char* title, const char* contents, + const char* plaintext); + bool (*getTextLineInput)(void* ctxt, char** result, const char* prompt, const char* title); + bool (*getIntegerInput)(void* ctxt, int64_t* result, const char* prompt, const char* title); + bool (*getAddressInput)(void* ctxt, uint64_t* result, const char* prompt, const char* title, + BNBinaryView* view, uint64_t currentAddr); + bool (*getChoiceInput)(void* ctxt, size_t* result, const char* prompt, const char* title, + const char** choices, size_t count); + bool (*getOpenFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext); + bool (*getSaveFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext, + const char* defaultName); + bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName); + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -2030,6 +2050,25 @@ extern "C" BINARYNINJACOREAPI void BNCancelBackgroundTask(BNBackgroundTask* task); BINARYNINJACOREAPI bool BNIsBackgroundTaskFinished(BNBackgroundTask* task); + // Interaction APIs + BINARYNINJACOREAPI void BNRegisterInteractionHandler(BNInteractionHandlerCallbacks* callbacks); + BINARYNINJACOREAPI char* BNMarkdownToHTML(const char* contents); + BINARYNINJACOREAPI void BNShowPlainTextReport(BNBinaryView* view, const char* title, const char* contents); + BINARYNINJACOREAPI void BNShowMarkdownReport(BNBinaryView* view, const char* title, const char* contents, + const char* plaintext); + BINARYNINJACOREAPI void BNShowHTMLReport(BNBinaryView* view, const char* title, const char* contents, + const char* plaintext); + BINARYNINJACOREAPI bool BNGetTextLineInput(char** result, const char* prompt, const char* title); + BINARYNINJACOREAPI bool BNGetIntegerInput(int64_t* result, const char* prompt, const char* title); + BINARYNINJACOREAPI bool BNGetAddressInput(uint64_t* result, const char* prompt, const char* title, + BNBinaryView* view, uint64_t currentAddr); + BINARYNINJACOREAPI bool BNGetChoiceInput(size_t* result, const char* prompt, const char* title, + const char** choices, size_t count); + BINARYNINJACOREAPI bool BNGetOpenFileNameInput(char** result, const char* prompt, const char* ext); + BINARYNINJACOREAPI bool BNGetSaveFileNameInput(char** result, const char* prompt, const char* ext, + const char* defaultName); + BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 0a7f23ce..863d57b2 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -1480,6 +1480,39 @@ void BinaryView::Reanalyze() } +void BinaryView::ShowPlainTextReport(const string& title, const string& contents) +{ + BNShowPlainTextReport(m_object, title.c_str(), contents.c_str()); +} + + +void BinaryView::ShowMarkdownReport(const string& title, const string& contents, const string& plainText) +{ + BNShowMarkdownReport(m_object, title.c_str(), contents.c_str(), plainText.c_str()); +} + + +void BinaryView::ShowHTMLReport(const string& title, const string& contents, const string& plainText) +{ + BNShowHTMLReport(m_object, title.c_str(), contents.c_str(), plainText.c_str()); +} + + +bool BinaryView::GetAddressInput(uint64_t& result, const string& prompt, const string& title) +{ + uint64_t currentAddress = 0; + if (m_file) + currentAddress = m_file->GetCurrentOffset(); + return BNGetAddressInput(&result, prompt.c_str(), title.c_str(), m_object, currentAddress); +} + + +bool BinaryView::GetAddressInput(uint64_t& result, const string& prompt, const string& title, uint64_t currentAddress) +{ + return BNGetAddressInput(&result, prompt.c_str(), title.c_str(), m_object, currentAddress); +} + + BinaryData::BinaryData(FileMetadata* file): BinaryView(BNCreateBinaryDataView(file->GetObject())) { } diff --git a/interaction.cpp b/interaction.cpp new file mode 100644 index 00000000..f7763679 --- /dev/null +++ b/interaction.cpp @@ -0,0 +1,286 @@ +#include +#include +#include "binaryninjaapi.h" + +using namespace std; +using namespace BinaryNinja; + + +void InteractionHandler::ShowMarkdownReport(Ref view, const string& title, const string& contents, + const string& plainText) +{ + ShowHTMLReport(view, title, MarkdownToHTML(contents), plainText); +} + + +void InteractionHandler::ShowHTMLReport(Ref view, const string& title, const string&, + const string& plainText) +{ + if (plainText.size() != 0) + ShowPlainTextReport(view, title, plainText); +} + + +bool InteractionHandler::GetIntegerInput(int64_t& result, const string& prompt, const string& title) +{ + string input; + + while (true) + { + string input; + if (!GetTextLineInput(input, prompt, title)) + return false; + if (input.size() == 0) + return false; + + errno = 0; + result = strtoll(input.c_str(), nullptr, 0); + if (errno != 0) + { + errno = 0; + result = strtoull(input.c_str(), nullptr, 0); + if (errno != 0) + continue; + } + + return true; + } +} + + +bool InteractionHandler::GetAddressInput(uint64_t& result, const string& prompt, const string& title, + Ref, uint64_t) +{ + int64_t value; + if (!GetIntegerInput(value, prompt, title)) + return false; + result = (uint64_t)value; + return true; +} + + +bool InteractionHandler::GetOpenFileNameInput(string& result, const string& prompt, const string&) +{ + return GetTextLineInput(result, prompt, "Open File"); +} + + +bool InteractionHandler::GetSaveFileNameInput(string& result, const string& prompt, const string&, const string&) +{ + return GetTextLineInput(result, prompt, "Save File"); +} + + +bool InteractionHandler::GetDirectoryNameInput(string& result, const string& prompt, const string&) +{ + return GetTextLineInput(result, prompt, "Select Directory"); +} + + +static void ShowPlainTextReportCallback(void* ctxt, BNBinaryView* view, const char* title, const char* contents) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + handler->ShowPlainTextReport(view ? new BinaryView(BNNewViewReference(view)) : nullptr, title, contents); +} + + +static void ShowMarkdownReportCallback(void* ctxt, BNBinaryView* view, const char* title, const char* contents, + const char* plaintext) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + handler->ShowMarkdownReport(view ? new BinaryView(BNNewViewReference(view)) : nullptr, title, contents, plaintext); +} + + +static void ShowHTMLReportCallback(void* ctxt, BNBinaryView* view, const char* title, const char* contents, + const char* plaintext) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + handler->ShowHTMLReport(view ? new BinaryView(BNNewViewReference(view)) : nullptr, title, contents, plaintext); +} + + +static bool GetTextLineInputCallback(void* ctxt, char** result, const char* prompt, const char* title) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + string value; + if (!handler->GetTextLineInput(value, prompt, title)) + return false; + *result = BNAllocString(value.c_str()); + return true; +} + + +static bool GetIntegerInputCallback(void* ctxt, int64_t* result, const char* prompt, const char* title) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + return handler->GetIntegerInput(*result, prompt, title); +} + + +static bool GetAddressInputCallback(void* ctxt, uint64_t* result, const char* prompt, const char* title, + BNBinaryView* view, uint64_t currentAddr) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + return handler->GetAddressInput(*result, prompt, title, view ? new BinaryView(BNNewViewReference(view)) : nullptr, + currentAddr); +} + + +static bool GetChoiceInputCallback(void* ctxt, size_t* result, const char* prompt, const char* title, + const char** choices, size_t count) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + vector choiceStrs; + for (size_t i = 0; i < count; i++) + choiceStrs.push_back(choices[i]); + return handler->GetChoiceInput(*result, prompt, title, choiceStrs); +} + + +static bool GetOpenFileNameInputCallback(void* ctxt, char** result, const char* prompt, const char* ext) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + string value; + if (!handler->GetOpenFileNameInput(value, prompt, ext)) + return false; + *result = BNAllocString(value.c_str()); + return true; +} + + +static bool GetSaveFileNameInputCallback(void* ctxt, char** result, const char* prompt, const char* ext, + const char* defaultName) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + string value; + if (!handler->GetSaveFileNameInput(value, prompt, ext, defaultName)) + return false; + *result = BNAllocString(value.c_str()); + return true; +} + + +static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char* prompt, const char* defaultName) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + string value; + if (!handler->GetDirectoryNameInput(value, prompt, defaultName)) + return false; + *result = BNAllocString(value.c_str()); + return true; +} + + +void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler) +{ + BNInteractionHandlerCallbacks cb; + cb.context = handler; + cb.showPlainTextReport = ShowPlainTextReportCallback; + cb.showMarkdownReport = ShowMarkdownReportCallback; + cb.showHTMLReport = ShowHTMLReportCallback; + cb.getTextLineInput = GetTextLineInputCallback; + cb.getIntegerInput = GetIntegerInputCallback; + cb.getAddressInput = GetAddressInputCallback; + cb.getChoiceInput = GetChoiceInputCallback; + cb.getOpenFileNameInput = GetOpenFileNameInputCallback; + cb.getSaveFileNameInput = GetSaveFileNameInputCallback; + cb.getDirectoryNameInput = GetDirectoryNameInputCallback; + BNRegisterInteractionHandler(&cb); +} + + +string BinaryNinja::MarkdownToHTML(const string& contents) +{ + char* str = BNMarkdownToHTML(contents.c_str()); + string result = str; + BNFreeString(str); + return result; +} + + +void BinaryNinja::ShowPlainTextReport(const string& title, const string& contents) +{ + BNShowPlainTextReport(nullptr, title.c_str(), contents.c_str()); +} + + +void BinaryNinja::ShowMarkdownReport(const string& title, const string& contents, const string& plainText) +{ + BNShowMarkdownReport(nullptr, title.c_str(), contents.c_str(), plainText.c_str()); +} + + +void BinaryNinja::ShowHTMLReport(const string& title, const string& contents, const string& plainText) +{ + BNShowHTMLReport(nullptr, title.c_str(), contents.c_str(), plainText.c_str()); +} + + +bool BinaryNinja::GetTextLineInput(string& result, const string& prompt, const string& title) +{ + char* value = nullptr; + if (!BNGetTextLineInput(&value, prompt.c_str(), title.c_str())) + return false; + result = value; + BNFreeString(value); + return true; +} + + +bool BinaryNinja::GetIntegerInput(int64_t& result, const string& prompt, const string& title) +{ + return BNGetIntegerInput(&result, prompt.c_str(), title.c_str()); +} + + +bool BinaryNinja::GetAddressInput(uint64_t& result, const string& prompt, const string& title) +{ + return BNGetAddressInput(&result, prompt.c_str(), title.c_str(), nullptr, 0); +} + + +bool BinaryNinja::GetChoiceInput(size_t& idx, const string& prompt, const string& title, + const vector& choices) +{ + const char** choiceStrs = new const char*[choices.size()]; + for (size_t i = 0; i < choices.size(); i++) + choiceStrs[i] = choices[i].c_str(); + bool ok = BNGetChoiceInput(&idx, prompt.c_str(), title.c_str(), choiceStrs, choices.size()); + delete[] choiceStrs; + return ok; +} + + +bool BinaryNinja::GetOpenFileNameInput(string& result, const string& prompt, const string& ext) +{ + char* value = nullptr; + if (!BNGetOpenFileNameInput(&value, prompt.c_str(), ext.c_str())) + return false; + result = value; + BNFreeString(value); + return true; +} + + +bool BinaryNinja::GetSaveFileNameInput(string& result, const string& prompt, const string& ext, + const string& defaultName) +{ + char* value = nullptr; + if (!BNGetSaveFileNameInput(&value, prompt.c_str(), ext.c_str(), defaultName.c_str())) + return false; + result = value; + BNFreeString(value); + return true; +} + + +bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, const string& defaultName) +{ + char* value = nullptr; + if (!BNGetDirectoryNameInput(&value, prompt.c_str(), defaultName.c_str())) + return false; + result = value; + BNFreeString(value); + return true; +} diff --git a/python/__init__.py b/python/__init__.py index 6abbdfa6..854de5c7 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -3293,6 +3293,23 @@ class BinaryView(object): """ core.BNReanalyzeAllFunctions(self.handle) + def show_plain_text_report(self, title, contents): + core.BNShowPlainTextReport(self.handle, title, contents) + + def show_markdown_report(self, title, contents, plaintext = ""): + core.BNShowMarkdownReport(self.handle, title, contents, plaintext) + + def show_html_report(self, title, contents, plaintext = ""): + core.BNShowHTMLReport(self.handle, title, contents, plaintext) + + def get_address_input(self, prompt, title, current_address = None): + if current_address is None: + current_address = self.file.offset + value = ctypes.c_ulonglong() + if not core.BNGetAddressInput(value, prompt, title, self.handle, current_address): + return None + return value.value + def __setattr__(self, name, value): try: object.__setattr__(self,name,value) @@ -10059,6 +10076,172 @@ class BackgroundTaskThread(BackgroundTask): def join(self): self.thread.join() +class InteractionHandler(object): + _interaction_handler = None + + def __init__(self): + self._cb = core.BNInteractionHandlerCallbacks() + self._cb.context = 0 + self._cb.showPlainTextReport = self._cb.showPlainTextReport.__class__(self._show_plain_text_report) + self._cb.showMarkdownReport = self._cb.showMarkdownReport.__class__(self._show_markdown_report) + self._cb.showHTMLReport = self._cb.showHTMLReport.__class__(self._show_html_report) + self._cb.getTextLineInput = self._cb.getTextLineInput.__class__(self._get_text_line_input) + self._cb.getIntegerInput = self._cb.getIntegerInput.__class__(self._get_int_input) + self._cb.getAddressInput = self._cb.getAddressInput.__class__(self._get_address_input) + self._cb.getChoiceInput = self._cb.getChoiceInput.__class__(self._get_choice_input) + self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input) + self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input) + self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input) + + def register(self): + self.__class__._interaction_handler = self + core.BNRegisterInteractionHandler(self._cb) + + def _show_plain_text_report(self, ctxt, view, title, contents): + try: + if view: + view = BinaryView(None, handle = core.BNNewViewReference(view)) + else: + view = None + self.show_plain_text_report(view, title, contents) + except: + log_error(traceback.format_exc()) + + def _show_markdown_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = BinaryView(None, handle = core.BNNewViewReference(view)) + else: + view = None + self.show_markdown_report(view, title, contents, plaintext) + except: + log_error(traceback.format_exc()) + + def _show_html_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = BinaryView(None, handle = core.BNNewViewReference(view)) + else: + view = None + self.show_html_report(view, title, contents, plaintext) + except: + log_error(traceback.format_exc()) + + def _get_text_line_input(self, ctxt, result, prompt, title): + try: + value = self.get_text_line_input(prompt, title) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log_error(traceback.format_exc()) + + def _get_int_input(self, ctxt, result, prompt, title): + try: + value = self.get_int_input(prompt, title) + if value is None: + return False + result[0] = value + return True + except: + log_error(traceback.format_exc()) + + def _get_address_input(self, ctxt, result, prompt, title, view, current_address): + try: + if view: + view = BinaryView(None, handle = core.BNNewViewReference(view)) + else: + view = None + value = self.get_address_input(prompt, title, view, current_address) + if value is None: + return False + result[0] = value + return True + except: + log_error(traceback.format_exc()) + + def _get_choice_input(self, ctxt, result, prompt, title, choice_buf, count): + try: + choices = [] + for i in xrange(0, count): + choices.append(choice_buf[i]) + value = self.get_choice_input(prompt, title, choices) + if value is None: + return False + result[0] = value + return True + except: + log_error(traceback.format_exc()) + + def _get_open_filename_input(self, ctxt, result, prompt, ext): + try: + value = self.get_open_filename_input(prompt, ext) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log_error(traceback.format_exc()) + + def _get_save_filename_input(self, ctxt, result, prompt, ext, default_name): + try: + value = self.get_save_filename_input(prompt, ext, default_name) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log_error(traceback.format_exc()) + + def _get_directory_name_input(self, ctxt, result, prompt, default_name): + try: + value = self.get_directory_name_input(prompt, default_name) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log_error(traceback.format_exc()) + + def show_plain_text_report(self, view, title, contents): + pass + + def show_markdown_report(self, view, title, contents, plaintext): + self.show_html_report(view, title, markdown_to_html(contents), plaintext) + + def show_html_report(self, view, title, contents, plaintext): + if len(plaintext) != 0: + self.show_plain_text_report(view, title, plaintext) + + def get_text_line_input(self, prompt, title): + return None + + def get_int_input(self, prompt, title): + while True: + text = self.get_text_line_input(prompt, title) + if len(text) == 0: + return False + try: + return int(text) + except: + continue + + def get_address_input(self, prompt, title, view, current_address): + return get_int_input(prompt, title) + + def get_choice_input(self, prompt, title, choices): + return None + + def get_open_filename_input(self, prompt, ext): + return get_text_line_input(prompt, "Open File") + + def get_save_filename_input(self, prompt, ext, default_name): + return get_text_line_input(title, "Save File") + + def get_directory_name_input(self, prompt, default_name): + return get_text_line_input(title, "Select Directory") + def LLIL_TEMP(n): return n | 0x80000000 @@ -10374,6 +10557,71 @@ def get_worker_thread_count(): def set_worker_thread_count(count): core.BNSetWorkerThreadCount(count) +def markdown_to_html(contents): + return core.BNMarkdownToHTML(contents) + +def show_plain_text_report(title, contents): + core.BNShowPlainTextReport(None, title, contents) + +def show_markdown_report(title, contents, plaintext = ""): + core.BNShowMarkdownReport(None, title, contents, plaintext) + +def show_html_report(title, contents, plaintext = ""): + core.BNShowHTMLReport(None, title, contents, plaintext) + +def get_text_line_input(prompt, title): + value = ctypes.c_char_p() + if not core.BNGetTextLineInput(value, prompt, title): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + +def get_int_input(prompt, title): + value = ctypes.c_longlong() + if not core.BNGetIntegerInput(value, prompt, title): + return None + return value.value + +def get_address_input(prompt, title): + value = ctypes.c_ulonglong() + if not core.BNGetAddressInput(value, prompt, title, None, 0): + return None + return value.value + +def get_choice_input(prompt, title, choices): + choice_buf = (ctypes.c_char_p * len(choices))() + for i in xrange(0, len(choices)): + choice_buf[i] = str(choices[i]) + value = ctypes.c_ulonglong() + if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): + return None + return value.value + +def get_open_filename_input(prompt, ext = ""): + value = ctypes.c_char_p() + if not core.BNGetOpenFileNameInput(value, prompt, ext): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + +def get_save_filename_input(prompt, ext = "", default_name = ""): + value = ctypes.c_char_p() + if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + +def get_directory_name_input(prompt, default_name = ""): + value = ctypes.c_char_p() + if not core.BNGetDirectoryNameInput(value, prompt, default_name): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() -- cgit v1.3.1 From 1364a9bc7b3887441540cd27685c1addb028afb3 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 19 Sep 2016 22:26:56 -0400 Subject: Adding APIs to store arbitrary data on a per-view, per-file, or per-function basis --- binaryninjacore.h | 14 +++++++ python/__init__.py | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) (limited to 'python') diff --git a/binaryninjacore.h b/binaryninjacore.h index 34806b6c..084fd34d 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1070,6 +1070,17 @@ extern "C" bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName); }; + struct BNObjectDestructionCallbacks + { + void* context; + // The provided pointers have a reference count of zero. Do not add additional references, doing so + // can lead to a double free. These are provided only for freeing additional state related to the + // objects passed. + void (*destructBinaryView)(void* ctxt, BNBinaryView* view); + void (*destructFileMetadata)(void* ctxt, BNFileMetadata* file); + void (*destructFunction)(void* ctxt, BNFunction* func); + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -1080,6 +1091,9 @@ extern "C" BINARYNINJACOREAPI bool BNIsLicenseValidated(void); + BINARYNINJACOREAPI void BNRegisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); + BINARYNINJACOREAPI void BNUnregisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); + // Plugin initialization BINARYNINJACOREAPI void BNInitCorePlugins(void); BINARYNINJACOREAPI void BNInitUserPlugins(void); diff --git a/python/__init__.py b/python/__init__.py index 854de5c7..23059068 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -27,6 +27,7 @@ import struct import threading import code import sys +import copy _plugin_init = False def _init_plugins(): @@ -181,7 +182,35 @@ class NavigationHandler(object): log_error(traceback.format_exc()) return False +class _AssociatedDataStore(dict): + _defaults = {} + + @classmethod + def set_default(cls, name, value): + cls._defaults[name] = value + + def __getattr__(self, name): + if name in self.__dict__: + return self.__dict__[name] + if name not in self: + if name in self.__class__._defaults: + result = copy.copy(self.__class__._defaults[name]) + self[name] = result + return result + return self.__getitem__(name) + + def __setattr__(self, name, value): + self.__setitem__(name, value) + + def __delattr__(self, name): + self.__delitem__(name) + +class _FileMetadataAssociatedDataStore(_AssociatedDataStore): + _defaults = {} + class FileMetadata(object): + _associated_data = {} + """ ``class FileMetadata`` represents the file being analyzed by Binary Ninja. It is responsible for opening, closing, creating the database (.bndb) files, and is used to keep track of undoable actions. @@ -207,6 +236,16 @@ class FileMetadata(object): core.BNSetFileMetadataNavigationHandler(self.handle, None) core.BNFreeFileMetadata(self.handle) + @classmethod + def _unregister(cls, f): + handle = ctypes.cast(f, ctypes.c_void_p) + if handle.value in cls._associated_data: + del cls._associated_data[handle.value] + + @classmethod + def set_default_data(cls, name, value): + _FileMetadataAssociatedDataStore.set_default(name, value) + @property def filename(self): """The name of the file (read/write)""" @@ -284,6 +323,17 @@ class FileMetadata(object): value._register(self.handle) self.nav = value + @property + def data(self): + """Dictionary object where plugins can store arbitrary data associated with the file""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in FileMetadata._associated_data: + obj = _FileMetadataAssociatedDataStore() + FileMetadata._associated_data[handle.value] = obj + return obj + else: + return FileMetadata._associated_data[handle.value] + def close(self): """ Closes the underlying file handle. It is recommended that this is done in a @@ -874,6 +924,9 @@ class DataVariable(object): def __repr__(self): return "" % (self.address, str(self.type)) +class _BinaryViewAssociatedDataStore(_AssociatedDataStore): + _defaults = {} + class BinaryView(object): """ ``class BinaryView`` implements a view on binary data, and presents a queryable interface of a binary file. One key @@ -927,6 +980,7 @@ class BinaryView(object): _registered_cb = None registered_view_type = None next_address = 0 + _associated_data = {} def __init__(self, file_metadata = None, handle = None): if handle is not None: @@ -1035,6 +1089,16 @@ class BinaryView(object): result = BinaryView(file_metadata, handle = view) return result + @classmethod + def _unregister(cls, view): + handle = ctypes.cast(view, ctypes.c_void_p) + if handle.value in cls._associated_data: + del cls._associated_data[handle.value] + + @classmethod + def set_default_data(cls, name, value): + _BinaryViewAssociatedDataStore.set_default(name, value) + def __del__(self): for i in self.notifications.values(): i._unregister() @@ -1246,6 +1310,17 @@ class BinaryView(object): core.BNFreeTypeList(type_list, count.value) return result + @property + def data(self): + """Dictionary object where plugins can store arbitrary data associated with the view""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in BinaryView._associated_data: + obj = _BinaryViewAssociatedDataStore() + BinaryView._associated_data[handle.value] = obj + return obj + else: + return BinaryView._associated_data[handle.value] + def __len__(self): return int(core.BNGetViewLength(self.handle)) @@ -4485,7 +4560,12 @@ class HighlightColor(object): return result +class _FunctionAssociatedDataStore(_AssociatedDataStore): + _defaults = {} + class Function(object): + _associated_data = {} + def __init__(self, view, handle): self._view = view self.handle = core.handle_of_type(handle, core.BNFunction) @@ -4496,6 +4576,16 @@ class Function(object): core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) core.BNFreeFunction(self.handle) + @classmethod + def _unregister(cls, func): + handle = ctypes.cast(func, ctypes.c_void_p) + if handle.value in cls._associated_data: + del cls._associated_data[handle.value] + + @classmethod + def set_default_data(cls, name, value): + _FunctionAssociatedDataStore.set_default(name, value) + @property def name(self): """Symbol name for the function""" @@ -4620,6 +4710,17 @@ class Function(object): core.BNFreeIndirectBranchList(branches) return result + @property + def data(self): + """Dictionary object where plugins can store arbitrary data associated with the function""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in Function._associated_data: + obj = _FunctionAssociatedDataStore() + Function._associated_data[handle.value] = obj + return obj + else: + return Function._associated_data[handle.value] + def __iter__(self): count = ctypes.c_ulonglong() blocks = core.BNGetFunctionBasicBlockList(self.handle, count) @@ -10242,6 +10343,26 @@ class InteractionHandler(object): def get_directory_name_input(self, prompt, default_name): return get_text_line_input(title, "Select Directory") +class _DestructionCallbackHandler: + def __init__(self): + self._cb = core.BNObjectDestructionCallbacks() + self._cb.context = 0 + self._cb.destructBinaryView = self._cb.destructBinaryView.__class__(self.destruct_binary_view) + self._cb.destructFileMetadata = self._cb.destructFileMetadata.__class__(self.destruct_file_metadata) + self._cb.destructFunction = self._cb.destructFunction.__class__(self.destruct_function) + core.BNRegisterObjectDestructionCallbacks(self._cb) + + def destruct_binary_view(self, ctxt, view): + BinaryView._unregister(view) + + def destruct_file_metadata(self, ctxt, f): + FileMetadata._unregister(f) + + def destruct_function(self, ctxt, func): + Function._unregister(func) + +_destruct_callbacks = _DestructionCallbackHandler() + def LLIL_TEMP(n): return n | 0x80000000 -- cgit v1.3.1 From 91c9990b0f51864f60e71d4004cfb2eb75154eb8 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 19 Sep 2016 23:57:39 -0400 Subject: Add API for showing a message box in the UI --- binaryninjaapi.h | 6 ++++++ binaryninjacore.h | 27 +++++++++++++++++++++++++++ interaction.cpp | 16 ++++++++++++++++ python/__init__.py | 13 +++++++++++++ 4 files changed, 62 insertions(+) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 720a8eb6..7e496591 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -402,6 +402,9 @@ namespace BinaryNinja const std::string& defaultName = ""); bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text, + BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon); + class DataBuffer { BNDataBuffer* m_buffer; @@ -2328,5 +2331,8 @@ namespace BinaryNinja const std::string& ext = "", const std::string& defaultName = ""); virtual bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + + virtual BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text, + BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon) = 0; }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index 084fd34d..6339b1c3 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1050,6 +1050,29 @@ extern "C" uint8_t mix, r, g, b, alpha; }; + enum BNMessageBoxIcon + { + InformationIcon, + QuestionIcon, + WarningIcon, + ErrorIcon + }; + + enum BNMessageBoxButtonSet + { + OKButtonSet, + YesNoButtonSet, + YesNoCancelButtonSet + }; + + enum BNMessageBoxButtonResult + { + NoButton = 0, + YesButton = 1, + OKButton = 2, + CancelButton = 3 + }; + struct BNInteractionHandlerCallbacks { void* context; @@ -1068,6 +1091,8 @@ extern "C" bool (*getSaveFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName); bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName); + BNMessageBoxButtonResult (*showMessageBox)(void* ctxt, const char* title, const char* text, + BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon); }; struct BNObjectDestructionCallbacks @@ -2082,6 +2107,8 @@ extern "C" BINARYNINJACOREAPI bool BNGetSaveFileNameInput(char** result, const char* prompt, const char* ext, const char* defaultName); BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName); + BINARYNINJACOREAPI BNMessageBoxButtonResult BNShowMessageBox(const char* title, const char* text, + BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon); #ifdef __cplusplus } diff --git a/interaction.cpp b/interaction.cpp index f7763679..432859d9 100644 --- a/interaction.cpp +++ b/interaction.cpp @@ -172,6 +172,14 @@ static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char* } +static BNMessageBoxButtonResult ShowMessageBoxCallback(void* ctxt, const char* title, const char* text, + BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + return handler->ShowMessageBox(title, text, buttons, icon); +} + + void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler) { BNInteractionHandlerCallbacks cb; @@ -186,6 +194,7 @@ void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler) cb.getOpenFileNameInput = GetOpenFileNameInputCallback; cb.getSaveFileNameInput = GetSaveFileNameInputCallback; cb.getDirectoryNameInput = GetDirectoryNameInputCallback; + cb.showMessageBox = ShowMessageBoxCallback; BNRegisterInteractionHandler(&cb); } @@ -284,3 +293,10 @@ bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, co BNFreeString(value); return true; } + + +BNMessageBoxButtonResult BinaryNinja::ShowMessageBox(const string& title, const string& text, + BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon) +{ + return BNShowMessageBox(title.c_str(), text.c_str(), buttons, icon); +} diff --git a/python/__init__.py b/python/__init__.py index 23059068..c8880648 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -10193,6 +10193,7 @@ class InteractionHandler(object): self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input) self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input) self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input) + self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box) def register(self): self.__class__._interaction_handler = self @@ -10305,6 +10306,12 @@ class InteractionHandler(object): except: log_error(traceback.format_exc()) + def _show_message_box(self, ctxt, title, text, buttons, icon): + try: + return self.show_message_box(title, text, buttons, icon) + except: + log_error(traceback.format_exc()) + def show_plain_text_report(self, view, title, contents): pass @@ -10343,6 +10350,9 @@ class InteractionHandler(object): def get_directory_name_input(self, prompt, default_name): return get_text_line_input(title, "Select Directory") + def show_message_box(self, title, text, buttons, icon): + return CancelButton + class _DestructionCallbackHandler: def __init__(self): self._cb = core.BNObjectDestructionCallbacks() @@ -10743,6 +10753,9 @@ def get_directory_name_input(prompt, default_name = ""): core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) return result +def show_message_box(title, text, buttons = core.OKButtonSet, icon = core.InformationIcon): + return core.BNShowMessageBox(title, text, buttons, icon) + bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() -- cgit v1.3.1 From 9ab0ead47220d704a3c80cae25ddd05afac55271 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 19 Sep 2016 23:57:57 -0400 Subject: Add example plugin to solve with angr --- python/examples/angr_plugin.py | 120 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 python/examples/angr_plugin.py (limited to 'python') diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py new file mode 100644 index 00000000..5d567511 --- /dev/null +++ b/python/examples/angr_plugin.py @@ -0,0 +1,120 @@ +# This plugin assumes angr is already installed and available on the system. See the angr documentation +# for information about installing angr. It should be installed using the virtualenv method. +# +# This plugin is currently only known to work on Linux using virtualenv. Switch to the virtual environment +# (using a command such as "workon angr"), then run the Binary Ninja UI from the command line. +# +# This method is known to fail on Mac OS X as the virtualenv used by angr does not appear to provide a +# way to automatically link to the correct version of Python, even when running the UI from within the +# virtual environment. A later update may allow for a manual override to link to the required version +# of Python. + +__name__ = "__console__" # angr looks for this, it won't load from within a UI without it +import angr +from binaryninja import * +import tempfile +import threading +import logging +import os + +# Disable warning logs as they show up as errors in the UI +logging.disable(logging.WARNING) + +# Create sets in the BinaryView's data field to store the desired path for each view +BinaryView.set_default_data("angr_find", set()) +BinaryView.set_default_data("angr_avoid", set()) + +def escaped_output(str): + return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) + +# Define a background thread object for solving in the background +class Solver(BackgroundTaskThread): + def __init__(self, find, avoid, view): + BackgroundTaskThread.__init__(self, "Solving with angr...", True) + self.find = tuple(find) + self.avoid = tuple(avoid) + self.view = view + + # Write the binary to disk so that the angr API can read it + self.binary = tempfile.NamedTemporaryFile() + self.binary.write(view.file.raw.read(0, len(view.file.raw))) + self.binary.flush() + + def run(self): + # Create an angr project and an explorer with the user's settings + p = angr.Project(self.binary.name) + e = p.surveyors.Explorer(find = self.find, avoid = self.avoid) + + # Solve loop + while not e.done: + if self.cancelled: + # Solve cancelled, show results if there were any + if len(e.found) > 0: + break + return + + # Perform the next step in the solve + e.step() + + # Update status + active_count = len(e.active) + found_count = len(e.found) + + progress = "Solving with angr (%d active path%s" % (active_count, "s" if active_count != 1 else "") + if found_count > 0: + progress += ", %d path%s found" % (found_count, "s" if found_count != 1 else "") + self.progress = progress + ")..." + + # Solve complete, show report + text_report = "Found %d path%s.\n\n" % (len(e.found), "s" if len(e.found) != 1 else "") + i = 1 + for f in e.found: + text_report += "Path %d\n" % i + "=" * 10 + "\n" + text_report += "stdin:\n" + escaped_output(f.state.posix.dumps(0)) + "\n\n" + text_report += "stdout:\n" + escaped_output(f.state.posix.dumps(1)) + "\n\n" + text_report += "stderr:\n" + escaped_output(f.state.posix.dumps(2)) + "\n\n" + i += 1 + + name = self.view.file.filename + if len(name) > 0: + show_plain_text_report("Results from angr - " + os.path.basename(self.view.file.filename), text_report) + else: + show_plain_text_report("Results from angr", text_report) + +def find_instr(bv, addr): + # Highlight the instruction in green + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(GreenHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(block.arch, addr, GreenHighlightColor) + + # Add the instruction to the list associated with the current view + bv.data.angr_find.add(addr) + +def avoid_instr(bv, addr): + # Highlight the instruction in red + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(RedHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(block.arch, addr, RedHighlightColor) + + # Add the instruction to the list associated with the current view + bv.data.angr_avoid.add(addr) + +def solve(bv): + if len(bv.data.angr_find) == 0: + show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + + "continue.", OKButtonSet, ErrorIcon) + return + + # Start a solver thread for the path associated with the view + s = Solver(bv.data.angr_find, bv.data.angr_avoid, bv) + s.start() + +# Register commands for the user to interact with the plugin +PluginCommand.register_for_address("Find Path to This Instruction", + "When solving, find a path that gets to this instruction", find_instr) +PluginCommand.register_for_address("Avoid This Instruction", + "When solving, avoid paths that reach this instruction", avoid_instr) +PluginCommand.register("Solve With Angr", "Attempt to solve for a path that satisfies the constraints given", solve) -- cgit v1.3.1 From 9f1620b744c7b71fc898a46b668624d25fbff60f Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Tue, 20 Sep 2016 00:30:37 -0400 Subject: add file save dialog and fix spacing --- python/examples/export_svg.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'python') diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index ebf3e2d1..35f35f00 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -1,7 +1,7 @@ from binaryninja import * import os,sys -colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} +colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} escape_table = { "'": "'", @@ -16,9 +16,11 @@ def escape(string): return ''.join(escape_table.get(i,i) for i in string) #still escape the basics def save_svg(bv,function): - filename = os.path.basename(bv.file.filename) address = hex(function.start).replace('L','') - outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) + filename = 'binaryninja-{filename}-{function}.html'.format(filename=os.path.basename(bv.file.filename),function=address) + outputfile = get_save_filename_input('File name for svg-export', 'HTML files (*.html)', filename) + if outputfile is None: + return content = render_svg(function) output = open(outputfile,'w') output.write(content) @@ -136,13 +138,13 @@ def render_svg(function): output += ' Basic Block {i}\n'.format(i=i) rgb=colors['none'] try: - bb = block.basic_block - color_code = bb.highlight._get_core_struct().color - color_str = bb.highlight._standard_color_to_str(color_code) - if color_str in colors: - rgb=colors[color_str] + bb = block.basic_block + color_code = bb.highlight.color + color_str = bb.highlight._standard_color_to_str(color_code) + if color_str in colors: + rgb=colors[color_str] except: - pass + pass output += ' \n'.format(x=x,y=y,width=width,height=height,r=rgb[0],g=rgb[1],b=rgb[2]) #Render instructions, unfortunately tspans don't allow copying/pasting more @@ -174,4 +176,3 @@ def render_svg(function): return output PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) - -- cgit v1.3.1 From e7bb1c066ba1067e6ba93c9bffcf5beb4162f11f Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Tue, 20 Sep 2016 01:02:21 -0400 Subject: update svg exporter to use new dialogs --- python/examples/export_svg.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'python') diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index 35f35f00..a54dc879 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -1,5 +1,11 @@ from binaryninja import * -import os,sys +import os +import webbrowser +try: + from urllib import pathname2url # Python 2.x +except: + from urllib.request import pathname2url # Python 3.x + colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} @@ -17,15 +23,19 @@ def escape(string): def save_svg(bv,function): address = hex(function.start).replace('L','') - filename = 'binaryninja-{filename}-{function}.html'.format(filename=os.path.basename(bv.file.filename),function=address) - outputfile = get_save_filename_input('File name for svg-export', 'HTML files (*.html)', filename) + path = os.path.dirname(bv.file.filename) + origname = os.path.basename(bv.file.filename) + filename = os.path.join(path,'binaryninja-{filename}-{function}.html'.format(filename=origname,function=address)) + outputfile = get_save_filename_input('File name for export_svg', 'HTML files (*.html)', filename) if outputfile is None: return content = render_svg(function) output = open(outputfile,'w') output.write(content) output.close() - os.system('%s' % outputfile) + if show_message_box("Open SVG", "Would you like to view the exported SVG?", buttons = core.YesNoButtonSet, icon = core.QuestionIcon) == core.YesButton: + url = 'file:{}'.format(pathname2url(outputfile)) + webbrowser.open(url) def instruction_data_flow(function,address): ''' TODO: Extract data flow information ''' @@ -175,4 +185,4 @@ def render_svg(function): output += '' return output -PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) +PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) -- cgit v1.3.1 From 0ee439859c9f09ed0d725bef385bcc9ac6d635a1 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Wed, 21 Sep 2016 11:16:35 -0400 Subject: update for new plugins and mention difference between personal and commercial --- python/examples/README.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'python') diff --git a/python/examples/README.md b/python/examples/README.md index 5f89496f..43af34be 100644 --- a/python/examples/README.md +++ b/python/examples/README.md @@ -4,11 +4,14 @@ The following examples demonstrate some of the Binary Ninja API. They include bo ## Stand-alone -* bin-info.py - general binary information +These plugins only operate when run directly outside of the UI + +* bin_info.py - general binary information * print_syscalls.py - extract syscall numbers from IL on specified file. Can be run both headless and in Binary Ninja -* version-switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade +* version_switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade +* instruction_iterator.py - very simple plugin that iterates through functions, blocks, and instructions -To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, like: +To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, as shown below. Please note, this is a feature that requires the "GUI-less processing" capability not available in the personal edition. In the personal edition, all scripts must by run from the integrated python console (follow the directions below under "Loading Plugins" section) ``` PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python @@ -16,9 +19,21 @@ PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python ## GUI Plugins -* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser +These plugins require the UI to be running + * breakpoint.py - small example showing how to modify a file and register a GUI menu item -* jump-table.py - stop-gap jump table plugin triggered via right-click menu at an indirect jump +* jump_table.py - heuristic based jump table detection for when the data-flow based computation fails, triggered by right-clicking on the location where the jump value is computed +* angr_plugin.py - a plugin to demonstrate both background threads, the simplified plugin UI elements, and highlighting +* export_svg.py - exports the graph view of a function to an SVG file for including in reports + +## Both + +These plugins are able to operate in either the GUI or as stand-alone plugins + +* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser + + +## Loading Plugins Plugins are meant to be loaded into a running Binary Ninja GUI and should either be copied or symlinked into the appropriate plugin folder. You'll need to then re-start Binary Ninja. -- cgit v1.3.1 From ae7f75fd33a49a25f5d8d735ae876e2c4ba55339 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Thu, 22 Sep 2016 00:20:13 -0400 Subject: Adding API to create simple dialogs with multiple input fields --- binaryninjaapi.h | 30 +++++++ binaryninjacore.h | 33 +++++++ interaction.cpp | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++ python/__init__.py | 227 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 544 insertions(+), 1 deletion(-) (limited to 'python') diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 7e496591..a62b4239 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -276,6 +276,7 @@ namespace BinaryNinja class MainThreadAction; class MainThreadActionHandler; class InteractionHandler; + struct FormInputField; /*! Logs to the error console with the given BNLogLevel. @@ -401,6 +402,7 @@ namespace BinaryNinja bool GetSaveFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = "", const std::string& defaultName = ""); bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + bool GetFormInput(std::vector& fields, const std::string& title); BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text, BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon); @@ -2311,6 +2313,33 @@ namespace BinaryNinja static std::vector> GetRunningTasks(); }; + struct FormInputField + { + BNFormInputFieldType type; + std::string prompt; + Ref view; // For AddressFormField + uint64_t currentAddress; // For AddressFormField + std::vector choices; // For ChoiceFormField + std::string ext; // For OpenFileNameFormField, SaveFileNameFormField + std::string defaultName; // For SaveFileNameFormField + int64_t intResult; + uint64_t addressResult; + std::string stringResult; + size_t indexResult; + + static FormInputField Label(const std::string& text); + static FormInputField Separator(); + static FormInputField TextLine(const std::string& prompt); + static FormInputField MultilineText(const std::string& prompt); + static FormInputField Integer(const std::string& prompt); + static FormInputField Address(const std::string& prompt, BinaryView* view = nullptr, uint64_t currentAddress = 0); + static FormInputField Choice(const std::string& prompt, const std::vector& choices); + static FormInputField OpenFileName(const std::string& prompt, const std::string& ext); + static FormInputField SaveFileName(const std::string& prompt, const std::string& ext, + const std::string& defaultName = ""); + static FormInputField DirectoryName(const std::string& prompt, const std::string& defaultName = ""); + }; + class InteractionHandler { public: @@ -2331,6 +2360,7 @@ namespace BinaryNinja const std::string& ext = "", const std::string& defaultName = ""); virtual bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + virtual bool GetFormInput(std::vector& fields, const std::string& title) = 0; virtual BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text, BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon) = 0; diff --git a/binaryninjacore.h b/binaryninjacore.h index 6339b1c3..b6d2f7ed 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1073,6 +1073,36 @@ extern "C" CancelButton = 3 }; + enum BNFormInputFieldType + { + LabelFormField, + SeparatorFormField, + TextLineFormField, + MultilineTextFormField, + IntegerFormField, + AddressFormField, + ChoiceFormField, + OpenFileNameFormField, + SaveFileNameFormField, + DirectoryNameFormField + }; + + struct BNFormInputField + { + BNFormInputFieldType type; + const char* prompt; + BNBinaryView* view; // For AddressFormField + uint64_t currentAddress; // For AddressFormField + const char** choices; // For ChoiceFormField + size_t count; // For ChoiceFormField + const char* ext; // For OpenFileNameFormField, SaveFileNameFormField + const char* defaultName; // For SaveFileNameFormField + int64_t intResult; + uint64_t addressResult; + char* stringResult; + size_t indexResult; + }; + struct BNInteractionHandlerCallbacks { void* context; @@ -1091,6 +1121,7 @@ extern "C" bool (*getSaveFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName); bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName); + bool (*getFormInput)(void* ctxt, BNFormInputField* fields, size_t count, const char* title); BNMessageBoxButtonResult (*showMessageBox)(void* ctxt, const char* title, const char* text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon); }; @@ -2107,6 +2138,8 @@ extern "C" BINARYNINJACOREAPI bool BNGetSaveFileNameInput(char** result, const char* prompt, const char* ext, const char* defaultName); BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName); + BINARYNINJACOREAPI bool BNGetFormInput(BNFormInputField* fields, size_t count, const char* title); + BINARYNINJACOREAPI void BNFreeFormInputResults(BNFormInputField* fields, size_t count); BINARYNINJACOREAPI BNMessageBoxButtonResult BNShowMessageBox(const char* title, const char* text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon); diff --git a/interaction.cpp b/interaction.cpp index 432859d9..fb4eb6d4 100644 --- a/interaction.cpp +++ b/interaction.cpp @@ -6,6 +6,102 @@ using namespace std; using namespace BinaryNinja; +FormInputField FormInputField::Label(const string& text) +{ + FormInputField result; + result.type = LabelFormField; + result.prompt = text; + return result; +} + + +FormInputField FormInputField::Separator() +{ + FormInputField result; + result.type = SeparatorFormField; + return result; +} + + +FormInputField FormInputField::TextLine(const string& prompt) +{ + FormInputField result; + result.type = TextLineFormField; + result.prompt = prompt; + return result; +} + + +FormInputField FormInputField::MultilineText(const string& prompt) +{ + FormInputField result; + result.type = MultilineTextFormField; + result.prompt = prompt; + return result; +} + + +FormInputField FormInputField::Integer(const string& prompt) +{ + FormInputField result; + result.type = IntegerFormField; + result.prompt = prompt; + return result; +} + + +FormInputField FormInputField::Address(const std::string& prompt, BinaryView* view, uint64_t currentAddress) +{ + FormInputField result; + result.type = AddressFormField; + result.prompt = prompt; + result.view = view; + result.currentAddress = currentAddress; + return result; +} + + +FormInputField FormInputField::Choice(const string& prompt, const vector& choices) +{ + FormInputField result; + result.type = ChoiceFormField; + result.prompt = prompt; + result.choices = choices; + return result; +} + + +FormInputField FormInputField::OpenFileName(const string& prompt, const string& ext) +{ + FormInputField result; + result.type = OpenFileNameFormField; + result.prompt = prompt; + result.ext = ext; + return result; +} + + +FormInputField FormInputField::SaveFileName(const string& prompt, const string& ext, const string& defaultName) +{ + FormInputField result; + result.type = SaveFileNameFormField; + result.prompt = prompt; + result.ext = ext; + result.defaultName = defaultName; + return result; +} + + +FormInputField FormInputField::DirectoryName(const string& prompt, const string& defaultName) +{ + FormInputField result; + result.type = DirectoryNameFormField; + result.prompt = prompt; + result.defaultName = defaultName; + return result; +} + + void InteractionHandler::ShowMarkdownReport(Ref view, const string& title, const string& contents, const string& plainText) { @@ -172,6 +268,85 @@ static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char* } +static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t count, const char* title) +{ + InteractionHandler* handler = (InteractionHandler*)ctxt; + + // Convert list of fields from core structure to API structure + vector fields; + for (size_t i = 0; i < count; i++) + { + vector choices; + switch (fieldBuf[i].type) + { + case SeparatorFormField: + fields.push_back(FormInputField::Separator()); + break; + case TextLineFormField: + fields.push_back(FormInputField::TextLine(fieldBuf[i].prompt)); + break; + case MultilineTextFormField: + fields.push_back(FormInputField::MultilineText(fieldBuf[i].prompt)); + break; + case IntegerFormField: + fields.push_back(FormInputField::Integer(fieldBuf[i].prompt)); + break; + case AddressFormField: + fields.push_back(FormInputField::Address(fieldBuf[i].prompt, fieldBuf[i].view ? + new BinaryView(BNNewViewReference(fieldBuf[i].view)) : nullptr, fieldBuf[i].currentAddress)); + break; + case ChoiceFormField: + for (size_t j = 0; j < fieldBuf[i].count; j++) + choices.push_back(fieldBuf[i].choices[j]); + fields.push_back(FormInputField::Choice(fieldBuf[i].prompt, choices)); + break; + case OpenFileNameFormField: + fields.push_back(FormInputField::OpenFileName(fieldBuf[i].prompt, fieldBuf[i].ext)); + break; + case SaveFileNameFormField: + fields.push_back(FormInputField::SaveFileName(fieldBuf[i].prompt, fieldBuf[i].ext, fieldBuf[i].defaultName)); + break; + case DirectoryNameFormField: + fields.push_back(FormInputField::DirectoryName(fieldBuf[i].prompt, fieldBuf[i].defaultName)); + break; + default: + fields.push_back(FormInputField::Label(fieldBuf[i].prompt)); + break; + } + } + + if (!handler->GetFormInput(fields, title)) + return false; + + // Place results into core structure + for (size_t i = 0; i < count; i++) + { + switch (fieldBuf[i].type) + { + case TextLineFormField: + case MultilineTextFormField: + case OpenFileNameFormField: + case SaveFileNameFormField: + case DirectoryNameFormField: + fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str()); + break; + case IntegerFormField: + fieldBuf[i].intResult = fields[i].intResult; + break; + case AddressFormField: + fieldBuf[i].addressResult = fields[i].addressResult; + break; + case ChoiceFormField: + fieldBuf[i].indexResult = fields[i].indexResult; + break; + default: + break; + } + } + return true; +} + + static BNMessageBoxButtonResult ShowMessageBoxCallback(void* ctxt, const char* title, const char* text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon) { @@ -194,6 +369,7 @@ void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler) cb.getOpenFileNameInput = GetOpenFileNameInputCallback; cb.getSaveFileNameInput = GetSaveFileNameInputCallback; cb.getDirectoryNameInput = GetDirectoryNameInputCallback; + cb.getFormInput = GetFormInputCallback; cb.showMessageBox = ShowMessageBoxCallback; BNRegisterInteractionHandler(&cb); } @@ -295,6 +471,85 @@ bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, co } +bool BinaryNinja::GetFormInput(vector& fields, const string& title) +{ + // Construct field list in core format + BNFormInputField* fieldBuf = new BNFormInputField[fields.size()]; + for (size_t i = 0; i < fields.size(); i++) + { + fieldBuf[i].type = fields[i].type; + fieldBuf[i].prompt = fields[i].prompt.c_str(); + switch (fields[i].type) + { + case AddressFormField: + fieldBuf[i].view = fields[i].view ? fields[i].view->GetObject() : nullptr; + fieldBuf[i].currentAddress = fields[i].currentAddress; + break; + case ChoiceFormField: + fieldBuf[i].choices = new const char*[fields[i].choices.size()]; + fieldBuf[i].count = fields[i].choices.size(); + for (size_t j = 0; j < fields[i].choices.size(); j++) + fieldBuf[i].choices[j] = fields[i].choices[j].c_str(); + break; + case OpenFileNameFormField: + fieldBuf[i].ext = fields[i].ext.c_str(); + break; + case SaveFileNameFormField: + fieldBuf[i].ext = fields[i].ext.c_str(); + fieldBuf[i].defaultName = fields[i].defaultName.c_str(); + break; + case DirectoryNameFormField: + fieldBuf[i].defaultName = fields[i].defaultName.c_str(); + break; + default: + break; + } + } + + bool ok = BNGetFormInput(fieldBuf, fields.size(), title.c_str()); + + // Free any memory used by field descriptions + for (size_t i = 0; i < fields.size(); i++) + { + if (fields[i].type == ChoiceFormField) + delete[] fieldBuf[i].choices; + } + + // If user cancelled, there are no results + if (!ok) + return false; + + // Copy results to API structures + for (size_t i = 0; i < fields.size(); i++) + { + switch (fields[i].type) + { + case TextLineFormField: + case MultilineTextFormField: + case OpenFileNameFormField: + case SaveFileNameFormField: + case DirectoryNameFormField: + fields[i].stringResult = fieldBuf[i].stringResult; + break; + case IntegerFormField: + fields[i].intResult = fieldBuf[i].intResult; + break; + case AddressFormField: + fields[i].addressResult = fieldBuf[i].addressResult; + break; + case ChoiceFormField: + fields[i].indexResult = fieldBuf[i].indexResult; + break; + default: + break; + } + } + + BNFreeFormInputResults(fieldBuf, fields.size()); + return true; +} + + BNMessageBoxButtonResult BinaryNinja::ShowMessageBox(const string& title, const string& text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon) { diff --git a/python/__init__.py b/python/__init__.py index c8880648..847b2c06 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -10177,6 +10177,170 @@ class BackgroundTaskThread(BackgroundTask): def join(self): self.thread.join() +class LabelField(object): + def __init__(self, text): + self.text = text + + def _fill_core_struct(self, value): + value.type = core.LabelFormField + value.prompt = self.text + + def _fill_core_result(self, value): + pass + + def _get_result(self, value): + pass + +class SeparatorField(object): + def _fill_core_struct(self, value): + value.type = core.SeparatorFormField + + def _fill_core_result(self, value): + pass + + def _get_result(self, value): + pass + +class TextLineField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = core.TextLineFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + +class MultilineTextField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = core.MultilineTextFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + +class IntegerField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = core.IntegerFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.intResult = self.result + + def _get_result(self, value): + self.result = value.intResult + +class AddressField(object): + def __init__(self, prompt, view = None, current_address = 0): + self.prompt = prompt + self.view = view + self.current_address = current_address + self.result = None + + def _fill_core_struct(self, value): + value.type = core.AddressFormField + value.prompt = self.prompt + value.view = None + if self.view is not None: + value.view = self.view.handle + value.currentAddress = self.current_address + + def _fill_core_result(self, value): + value.addressResult = self.result + + def _get_result(self, value): + self.result = value.addressResult + +class ChoiceField(object): + def __init__(self, prompt, choices): + self.prompt = prompt + self.choices = choices + self.result = None + + def _fill_core_struct(self, value): + value.type = core.ChoiceFormField + value.prompt = self.prompt + choice_buf = (ctypes.c_char_p * len(self.choices))() + for i in xrange(0, len(self.choices)): + choice_buf[i] = str(self.choices[i]) + value.choices = choice_buf + value.count = len(self.choices) + + def _fill_core_result(self, value): + value.indexResult = self.result + + def _get_result(self, value): + self.result = value.indexResult + +class OpenFileNameField(object): + def __init__(self, prompt, ext = ""): + self.prompt = prompt + self.ext = ext + self.result = None + + def _fill_core_struct(self, value): + value.type = core.OpenFileNameFormField + value.prompt = self.prompt + value.ext = self.ext + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + +class SaveFileNameField(object): + def __init__(self, prompt, ext = "", default_name = ""): + self.prompt = prompt + self.ext = ext + self.default_name = default_name + self.result = None + + def _fill_core_struct(self, value): + value.type = core.SaveFileNameFormField + value.prompt = self.prompt + value.ext = self.ext + value.defaultName = self.default_name + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + +class DirectoryNameField(object): + def __init__(self, prompt, default_name = ""): + self.prompt = prompt + self.default_name = default_name + self.result = None + + def _fill_core_struct(self, value): + value.type = core.DirectoryNameField + value.prompt = self.prompt + value.defaultName = self.default_name + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + class InteractionHandler(object): _interaction_handler = None @@ -10193,6 +10357,7 @@ class InteractionHandler(object): self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input) self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input) self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input) + self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input) self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box) def register(self): @@ -10306,6 +10471,46 @@ class InteractionHandler(object): except: log_error(traceback.format_exc()) + def _get_form_input(self, ctxt, fields, count, title): + try: + field_objs = [] + for i in xrange(0, count): + if fields[i].type == core.LabelFormField: + field_objs.append(LabelField(fields[i].prompt)) + elif fields[i].type == core.SeparatorFormField: + field_objs.append(SeparatorField()) + elif fields[i].type == core.TextLineFormField: + field_objs.append(TextLineField(fields[i].prompt)) + elif fields[i].type == core.MultilineTextFormField: + field_objs.append(MultilineTextField(fields[i].prompt)) + elif fields[i].type == core.IntegerFormField: + field_objs.append(IntegerField(fields[i].prompt)) + elif fields[i].type == core.AddressFormField: + view = None + if fields[i].view: + view = BinaryView(None, handle = core.BNNewViewReference(fields[i].view)) + field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) + elif fields[i].type == core.ChoiceFormField: + choices = [] + for i in xrange(0, fields[i].count): + choices.append(fields[i].choices[i]) + field_objs.append(ChoiceField(fields[i].prompt, choices)) + elif fields[i].type == core.OpenFileNameFormField: + field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext)) + elif fields[i].type == core.SaveFileNameFormField: + field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName)) + elif fields[i].type == core.DirectoryNameField: + field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName)) + else: + field_objs.append(LabelField(fields[i].prompt)) + if not self.get_form_input(field_objs, title): + return False + for i in xrange(0, count): + field_objs[i]._fill_core_result(fields[i]) + return True + except: + log_error(traceback.format_exc()) + def _show_message_box(self, ctxt, title, text, buttons, icon): try: return self.show_message_box(title, text, buttons, icon) @@ -10350,8 +10555,11 @@ class InteractionHandler(object): def get_directory_name_input(self, prompt, default_name): return get_text_line_input(title, "Select Directory") + def get_form_input(self, fields, title): + return False + def show_message_box(self, title, text, buttons, icon): - return CancelButton + return core.CancelButton class _DestructionCallbackHandler: def __init__(self): @@ -10753,6 +10961,23 @@ def get_directory_name_input(prompt, default_name = ""): core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) return result +def get_form_input(fields, title): + value = (core.BNFormInputField * len(fields))() + for i in xrange(0, len(fields)): + if isinstance(fields[i], str): + LabelField(fields[i])._fill_core_struct(value[i]) + elif fields[i] is None: + SeparatorField()._fill_core_struct(value[i]) + else: + fields[i]._fill_core_struct(value[i]) + if not core.BNGetFormInput(value, len(fields), title): + return False + for i in xrange(0, len(fields)): + if not (isinstance(fields[i], str) or (fields[i] is None)): + fields[i]._get_result(value[i]) + core.BNFreeFormInputResults(value, len(fields)) + return True + def show_message_box(title, text, buttons = core.OKButtonSet, icon = core.InformationIcon): return core.BNShowMessageBox(title, text, buttons, icon) -- cgit v1.3.1 From 664dba493cfe2e2959990a73a79db91a88bb4cfc Mon Sep 17 00:00:00 2001 From: Dustin Fraze Date: Thu, 22 Sep 2016 20:56:38 -0400 Subject: Update __init__.py Stupid typo --- python/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index 847b2c06..80302c0c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -5696,7 +5696,7 @@ class _ArchitectureMetaClass(type): class Architecture(object): """ - ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implemnt assembly, + ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implement assembly, disassembly, IL lifting, and patching. ``class Architecture`` has a ``__metaclass__`` with the additional methods ``register``, and supports -- cgit v1.3.1 From 78a13604bfeba1798379d15d6e26f4b6c128bda1 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 26 Sep 2016 17:23:05 -0400 Subject: Fix NES plugin after API changes --- python/__init__.py | 4 +++- python/examples/nes.py | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index 80302c0c..26c8fb5d 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1022,7 +1022,7 @@ class BinaryView(object): self.file = file_metadata self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, self._cb) self.notifications = {} - self.next_address = self.entry_point + self.next_address = None # Do NOT try to access view before init() is called, use placeholder @classmethod def register(cls): @@ -1579,6 +1579,8 @@ class BinaryView(object): """ if arch is None: arch = self.arch + if self.next_address is None: + self.next_address = self.entry_point txt, size = arch.get_instruction_text(self.read(self.next_address, self.arch.max_instr_length), self.next_address) self.next_address += size if txt is None: diff --git a/python/examples/nes.py b/python/examples/nes.py index 5cb6bc0d..00f9d8eb 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -521,9 +521,9 @@ class NESView(BinaryView): def __init__(self, data): BinaryView.__init__(self, data.file) - self.data = data + self.raw = data self.notification = NESViewUpdateNotification(self) - self.data.register_notification(self.notification) + self.raw.register_notification(self.notification) @classmethod def is_valid_for_data(self, data): @@ -539,7 +539,7 @@ class NESView(BinaryView): def init(self): try: - hdr = self.data.read(0, 16) + hdr = self.raw.read(0, 16) self.rom_banks = struct.unpack("B", hdr[4])[0] self.vrom_banks = struct.unpack("B", hdr[5])[0] self.rom_flags = struct.unpack("B", hdr[6])[0] @@ -592,9 +592,9 @@ class NESView(BinaryView): self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) - sym_files = [self.data.file.filename + ".%x.nl" % self.__class__.bank, - self.data.file.filename + ".ram.nl", - self.data.file.filename + ".%x.nl" % (self.rom_banks - 1)] + sym_files = [self.raw.file.filename + ".%x.nl" % self.__class__.bank, + self.raw.file.filename + ".ram.nl", + self.raw.file.filename + ".%x.nl" % (self.rom_banks - 1)] for f in sym_files: if os.path.exists(f): sym_contents = open(f, "r").read() @@ -634,9 +634,9 @@ class NESView(BinaryView): else: to_read = length if addr < 0xc000: - data = self.data.read(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), to_read) + data = self.raw.read(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), to_read) else: - data = self.data.read(self.rom_offset + bank_ofs + self.rom_length - 0x4000, to_read) + data = self.raw.read(self.rom_offset + bank_ofs + self.rom_length - 0x4000, to_read) result += data if len(data) < to_read: break @@ -663,9 +663,9 @@ class NESView(BinaryView): else: to_write = length if addr < 0xc000: - written = self.data.write(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), value[offset : offset + to_write]) + written = self.raw.write(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), value[offset : offset + to_write]) else: - written = self.data.write(self.rom_offset + bank_ofs + self.rom_length - 0x4000, value[offset : offset + to_write]) + written = self.raw.write(self.rom_offset + bank_ofs + self.rom_length - 0x4000, value[offset : offset + to_write]) if written < to_write: break length -= to_write -- cgit v1.3.1