diff options
| author | Andrew Lamoureux <lwerdna@users.noreply.github.com> | 2017-03-15 20:33:55 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-03-15 20:33:55 -0400 |
| commit | 31d34d5d067ed4c5fe071eb8637b10ed50607555 (patch) | |
| tree | 9347ba4e5e6a2b26baf620f59e56db7965bb47b5 | |
| parent | 164c7a680697be72d7f42af28b89012e5c6dd53b (diff) | |
| parent | 1f7523ce2b1edac1014d781fc771d5b82568e2f1 (diff) | |
Merge branch 'dev' into dev
79 files changed, 22312 insertions, 12128 deletions
@@ -10,6 +10,7 @@ binaryninjacore.lib binaryninjacore.dll binaryninjaapi.lib python/_binaryninjacore.py +python/enums.py python/generator python/generator.exe .vs @@ -19,12 +20,13 @@ api-docs/build/* .env api-docs/source/binaryninja.* bin/ - # CMake CMakeCache.txt CMakeFiles CMakeScripts -Makefile cmake_install.cmake install_manifest.txt CTestTestfile.cmake +*.pyc +api-docs/source/python.rst +api-docs/source/index.rst diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9eb2d52a --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + LIB := -L/Applications/Binary\ Ninja.app/Contents/MacOS/ -lbinaryninjacore +else + LIB := -L$(HOME)/binaryninja/ -lbinaryninjacore +endif + + +TARGETDIR := bin +TARGETNAME := libbinaryninjaapi +TARGET := $(TARGETDIR)/$(TARGETNAME) + +SRCEXT = .cpp +SOURCES = $(wildcard *$(SRCEXT)) +OBJECTS = $(SOURCES:.cpp=.o) + + +CFLAGS := -c -fPIC -O2 -pipe -std=gnu++11 -Wall -W +ifeq ($(UNAME_S),Darwin) + CC := $(shell xcrun -f g++) + AR := $(shell xcrun -f ar) + CFLAGS += -stdlib=libc++ +else + CC := g++ + AR := ar +endif + +all: $(TARGET).a + +$(TARGET).a: $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(AR) rcs $@ $^ + +%.o: %.cpp + @echo " Compiling... $@ $<" + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + @echo " Cleaning..."; + $(RM) -r *.o $(TARGETDIR) + +.PHONY: clean diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 00000000..ea95ba7b --- /dev/null +++ b/Makefile.win @@ -0,0 +1,23 @@ +CFLAGS = /EHsc /DWIN32 + +TARGETDIR = bin +TARGETNAME = libbinaryninjaapi.lib +TARGET = .\$(TARGETDIR)\$(TARGETNAME) + +OBJS = architecture.obj backgroundtask.obj basicblock.obj binaryninjaapi.obj binaryreader.obj binaryview.obj binaryviewtype.obj binarywriter.obj callingconvention.obj databuffer.obj demangle.obj fileaccessor.obj filemetadata.obj function.obj functiongraph.obj functiongraphblock.obj functionrecognizer.obj interaction.obj log.obj lowlevelil.obj mainthread.obj platform.obj plugin.obj scriptingprovider.obj tempfile.obj transform.obj type.obj update.obj jsoncpp.obj + +$(TARGET): $(OBJS) + if not exist $(TARGETDIR) mkdir $(TARGETDIR) + lib $(OBJS) /OUT:$(TARGET) + +jsoncpp.obj: json\jsoncpp.cpp + cl $(CFLAGS) /I. /c json\jsoncpp.cpp + +# nmake "inference rules" are gnu make "pattern rules" +.cpp.obj: + cl $(CFLAGS) /c $< + +clean: + if exist *.obj del *.obj + if exist $(TARGETDIR) del /Q /S $(TARGETDIR) + @@ -12,3 +12,16 @@ If interested in contributing, first please read and sign the [Contribution Lice The issue tracker for this repository tracks not only issues with the source code contained here but also the broader Binary Ninja product. +## Building + +Starting mid March 2017, the C++ portion of this API can be built into a static library (.a, .lib) that binary plugins can link against. Use Makefile on MacOS, Linux, and Windows mingw environments, and Makefile.win (nmake file) for Windows Visual Studio environment (nmake -f). + +The compiled API contains names and functions you can use from your plugins, but most of the implementation is missing until you link up against libbinaryninjacore.dylib or libbinaryninjacore.dll (via import file libbinaryninjacore.lib). See the ./examples. + +Since BinaryNinja is a 64-bit only product, ensure that you are using a 64-bit compiling and linking environment. Errors on windows like LNK1107 might indicate that your bits don't match. + +## Examples + +* bin-info is a standalone executable that prints some information about a given binary to stdout +* breakpoint is a plugin that allows you to select a region within an x86 binary and use the context menu to fill it with breakpoint bytes +* print_syscalls is a standalone executable that prints the syscalls used in a given binary diff --git a/api-docs/Makefile b/api-docs/Makefile index 4092950c..fe751410 100644 --- a/api-docs/Makefile +++ b/api-docs/Makefile @@ -6,6 +6,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build +SOURCEDIR = source # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -47,6 +48,8 @@ help: .PHONY: clean clean: rm -rf $(BUILDDIR)/* + rm $(SOURCEDIR)/binaryninja.*.rst + rm $(SOURCEDIR)/python.rst .PHONY: html html: diff --git a/api-docs/source/conf.py b/api-docs/source/conf.py index ac6042b4..c87cb114 100644 --- a/api-docs/source/conf.py +++ b/api-docs/source/conf.py @@ -19,15 +19,73 @@ import os import sys import platform +import inspect if (platform.system() == "Darwin"): - bnpath=os.path.join(os.path.abspath('.'), "..", "..", "..", "ui", "binaryninja.app", "Contents", "Resources", "python") + bnpath=os.path.join(os.path.abspath('.'), "..", "..", "..", "ui", "binaryninja.app", "Contents", "Resources", "python") else: - bnpath=os.path.join(os.path.abspath('.'), "..", "..", "..", "ui", "python") + bnpath=os.path.join(os.path.abspath('.'), "..", "..", "..", "ui", "python") sys.path.insert(0, bnpath) import binaryninja +def modulelist(modulename): + modules = inspect.getmembers(modulename, inspect.ismodule) + return filter(lambda x: x[0] not in ("abc", "ctypes", "core", "struct", "sys", "_binaryninjacore", "traceback", "code", "enum", "json", "threading", "startup", "associateddatastore"), modules) + + +def classlist(module): + members = inspect.getmembers(module, inspect.isclass) + if module.__name__ != "binaryninja.enums": + members = filter(lambda x: type(x[1]) != binaryninja.enum.EnumMeta, members) + members.extend(inspect.getmembers(module, inspect.isfunction)) + return map(lambda x: x[0], filter(lambda x: not x[0].startswith("_"), members)) + + +def generaterst(): + pythonrst = open("index.rst", "w") + pythonrst.write('''Binary Ninja Python API Documentation +===================================== + +.. toctree:: + :maxdepth: 2 + +''') + + for modulename, module in modulelist(binaryninja): + filename = 'binaryninja.{module}-module.rst'.format(module=modulename) + pythonrst.write(' {module} <{filename}>\n'.format(module=modulename, filename=filename)) + modulefile = open(filename, "w") + modulefile.write('''{module} module +===================== + +.. autosummary:: + :toctree: + +'''.format(module=modulename)) + + for classname in classlist(module): + modulefile.write(" binaryninja.{module}.{classname}\n".format(module=modulename, classname=classname)) + + modulefile.write('''\n.. toctree:: + :maxdepth: 2\n''') + + modulefile.write('''\n\n.. automodule:: binaryninja.{module} + :members: + :undoc-members: + :show-inheritance:'''.format(module=modulename)) + modulefile.close() + + pythonrst.write('''.. automodule:: binaryninja + :members: + :undoc-members: + :show-inheritance: +''') + pythonrst.close() + + +generaterst() + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -50,7 +108,7 @@ extensions = [ ] autosummary_generate = True -autodoc_member_order = 'groupwise' +#autodoc_member_order = 'groupwise' breathe_projects = { "BinaryNinja": "../../xml/" } breathe_projects_source = { @@ -72,7 +130,7 @@ source_suffix = '.rst' # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'python' +master_doc = 'index' # General information about the project. project = u'Binary Ninja API' @@ -121,7 +179,7 @@ exclude_patterns = [] # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # -add_module_names = False +add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. @@ -211,7 +269,7 @@ html_static_path = ['_static'] # If false, no module index is generated. # -html_domain_indices = False +html_domain_indices = True # If false, no index is generated. # @@ -223,7 +281,7 @@ html_split_index = False # If true, links to the reST sources are added to the pages. # -html_show_sourcelink = False +html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # diff --git a/api-docs/source/index.rst b/api-docs/source/old-index.rst index 355d6901..355d6901 100644 --- a/api-docs/source/index.rst +++ b/api-docs/source/old-index.rst diff --git a/api-docs/source/python.rst b/api-docs/source/python.rst deleted file mode 100644 index 5e299535..00000000 --- a/api-docs/source/python.rst +++ /dev/null @@ -1,73 +0,0 @@ -Binary Ninja Python API Documentation -===================================== - -.. currentmodule:: binaryninja -.. autosummary:: - :toctree: - - AnalysisCompletionEvent - AnalysisProgress - Architecture - BasicBlock - BasicBlockEdge - BinaryDataNotification - BinaryDataNotificationCallbacks - BinaryReader - BinaryView - BinaryViewType - BinaryWriter - CallingConvention - CoreFileAccessor - DataBuffer - DataVariable - DisassemblySettings - DisassemblyTextLine - Enumeration - EnumerationMember - FileAccessor - Function - FunctionGraph - FunctionGraphBlock - FunctionGraphEdge - FunctionRecognizer - IndirectBranchInfo - InstructionBranch - InstructionInfo - InstructionTextToken - LinearDisassemblyLine - LinearDisassemblyPosition - LookupTableEntry - LowLevelILBasicBlock - LowLevelILExpr - LowLevelILFunction - LowLevelILInstruction - LowLevelILLabel - NavigationHandler - Platform - PluginCommand - PluginCommandContext - ReferenceSource - RegisterInfo - RegisterValue - StackVariable - StackVariableReference - StringReference - Structure - StructureMember - Symbol - Transform - TransformParameter - Type - TypeParserResult - UndoAction - UpdateChannel - UpdateProgressCallback - UpdateVersion - -.. toctree:: - :maxdepth: 2 - -.. automodule:: binaryninja - :members: - :undoc-members: - :show-inheritance: diff --git a/architecture.cpp b/architecture.cpp index 3c7d9af8..d72e7f42 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -52,7 +52,14 @@ InstructionTextToken::InstructionTextToken(): type(TextToken), value(0) InstructionTextToken::InstructionTextToken(BNInstructionTextTokenType t, const std::string& txt, uint64_t val, - size_t s, size_t o) : type(t), text(txt), value(val), size(s), operand(o) + size_t s, size_t o) : type(t), text(txt), value(val), size(s), operand(o), context(NoTokenContext), address(0) +{ +} + + +InstructionTextToken::InstructionTextToken(BNInstructionTextTokenType t, BNInstructionTextTokenContext ctxt, + const string& txt, uint64_t a, uint64_t val, size_t s, size_t o): + type(t), text(txt), value(val), size(s), operand(o), context(ctxt), address(a) { } @@ -153,6 +160,8 @@ bool Architecture::GetInstructionTextCallback(void* ctxt, const uint8_t* data, u (*result)[i].value = tokens[i].value; (*result)[i].size = tokens[i].size; (*result)[i].operand = tokens[i].operand; + (*result)[i].context = tokens[i].context; + (*result)[i].address = tokens[i].address; } return true; } @@ -737,9 +746,9 @@ void Architecture::SetBinaryViewTypeConstant(const string& type, const string& n bool Architecture::ParseTypesFromSource(const string& source, const string& fileName, - map<string, Ref<Type>>& types, map<string, Ref<Type>>& variables, - map<string, Ref<Type>>& functions, string& errors, - const vector<string>& includeDirs) + map<QualifiedName, Ref<Type>>& types, map<QualifiedName, Ref<Type>>& variables, + map<QualifiedName, Ref<Type>>& functions, string& errors, const vector<string>& includeDirs, + const string& autoTypeSource) { BNTypeParserResult result; char* errorStr; @@ -753,26 +762,35 @@ bool Architecture::ParseTypesFromSource(const string& source, const string& file functions.clear(); bool ok = BNParseTypesFromSource(m_object, source.c_str(), fileName.c_str(), &result, - &errorStr, includeDirList, includeDirs.size()); + &errorStr, includeDirList, includeDirs.size(), autoTypeSource.c_str()); errors = errorStr; BNFreeString(errorStr); if (!ok) return false; for (size_t i = 0; i < result.typeCount; i++) - types[result.types[i].name] = new Type(BNNewTypeReference(result.types[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.types[i].name); + types[name] = new Type(BNNewTypeReference(result.types[i].type)); + } for (size_t i = 0; i < result.variableCount; i++) - types[result.variables[i].name] = new Type(BNNewTypeReference(result.variables[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.variables[i].name); + types[name] = new Type(BNNewTypeReference(result.variables[i].type)); + } for (size_t i = 0; i < result.functionCount; i++) - types[result.functions[i].name] = new Type(BNNewTypeReference(result.functions[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.functions[i].name); + types[name] = new Type(BNNewTypeReference(result.functions[i].type)); + } BNFreeTypeParserResult(&result); return true; } -bool Architecture::ParseTypesFromSourceFile(const string& fileName, map<string, Ref<Type>>& types, - map<string, Ref<Type>>& variables, map<string, Ref<Type>>& functions, - string& errors, const vector<string>& includeDirs) +bool Architecture::ParseTypesFromSourceFile(const string& fileName, map<QualifiedName, Ref<Type>>& types, + map<QualifiedName, Ref<Type>>& variables, map<QualifiedName, Ref<Type>>& functions, + string& errors, const vector<string>& includeDirs, const string& autoTypeSource) { BNTypeParserResult result; char* errorStr; @@ -786,18 +804,27 @@ bool Architecture::ParseTypesFromSourceFile(const string& fileName, map<string, functions.clear(); bool ok = BNParseTypesFromSourceFile(m_object, fileName.c_str(), &result, &errorStr, - includeDirList, includeDirs.size()); + includeDirList, includeDirs.size(), autoTypeSource.c_str()); errors = errorStr; BNFreeString(errorStr); if (!ok) return false; for (size_t i = 0; i < result.typeCount; i++) - types[result.types[i].name] = new Type(BNNewTypeReference(result.types[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.types[i].name); + types[name] = new Type(BNNewTypeReference(result.types[i].type)); + } for (size_t i = 0; i < result.variableCount; i++) - variables[result.variables[i].name] = new Type(BNNewTypeReference(result.variables[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.variables[i].name); + variables[name] = new Type(BNNewTypeReference(result.variables[i].type)); + } for (size_t i = 0; i < result.functionCount; i++) - functions[result.functions[i].name] = new Type(BNNewTypeReference(result.functions[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&result.functions[i].name); + functions[name] = new Type(BNNewTypeReference(result.functions[i].type)); + } BNFreeTypeParserResult(&result); return true; } @@ -954,8 +981,8 @@ bool CoreArchitecture::GetInstructionText(const uint8_t* data, uint64_t addr, si for (size_t i = 0; i < count; i++) { - result.push_back(InstructionTextToken(tokens[i].type, tokens[i].text, tokens[i].value, - tokens[i].size, tokens[i].operand)); + result.push_back(InstructionTextToken(tokens[i].type, tokens[i].context, tokens[i].text, tokens[i].address, + tokens[i].value, tokens[i].size, tokens[i].operand)); } BNFreeInstructionText(tokens, count); diff --git a/basicblock.cpp b/basicblock.cpp index 93a279a1..d8ae0f65 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -108,6 +108,12 @@ uint64_t BasicBlock::GetLength() const } +size_t BasicBlock::GetIndex() const +{ + return BNGetBasicBlockIndex(m_object); +} + + vector<BasicBlockEdge> BasicBlock::GetOutgoingEdges() const { size_t count; @@ -118,12 +124,30 @@ vector<BasicBlockEdge> BasicBlock::GetOutgoingEdges() const { BasicBlockEdge edge; edge.type = array[i].type; - edge.target = array[i].target; - edge.arch = array[i].arch ? new CoreArchitecture(array[i].arch) : nullptr; + edge.target = array[i].target ? new BasicBlock(BNNewBasicBlockReference(array[i].target)) : nullptr; + result.push_back(edge); + } + + BNFreeBasicBlockEdgeList(array, count); + return result; +} + + +vector<BasicBlockEdge> BasicBlock::GetIncomingEdges() const +{ + size_t count; + BNBasicBlockEdge* array = BNGetBasicBlockIncomingEdges(m_object, &count); + + vector<BasicBlockEdge> result; + for (size_t i = 0; i < count; i++) + { + BasicBlockEdge edge; + edge.type = array[i].type; + edge.target = array[i].target ? new BasicBlock(BNNewBasicBlockReference(array[i].target)) : nullptr; result.push_back(edge); } - BNFreeBasicBlockOutgoingEdgeList(array); + BNFreeBasicBlockEdgeList(array, count); return result; } @@ -134,6 +158,91 @@ bool BasicBlock::HasUndeterminedOutgoingEdges() const } +set<Ref<BasicBlock>> BasicBlock::GetDominators() const +{ + size_t count; + BNBasicBlock** blocks = BNGetBasicBlockDominators(m_object, &count); + + set<Ref<BasicBlock>> result; + for (size_t i = 0; i < count; i++) + result.insert(new BasicBlock(BNNewBasicBlockReference(blocks[i]))); + + BNFreeBasicBlockList(blocks, count); + return result; +} + + +set<Ref<BasicBlock>> BasicBlock::GetStrictDominators() const +{ + size_t count; + BNBasicBlock** blocks = BNGetBasicBlockStrictDominators(m_object, &count); + + set<Ref<BasicBlock>> result; + for (size_t i = 0; i < count; i++) + result.insert(new BasicBlock(BNNewBasicBlockReference(blocks[i]))); + + BNFreeBasicBlockList(blocks, count); + return result; +} + + +Ref<BasicBlock> BasicBlock::GetImmediateDominator() const +{ + BNBasicBlock* result = BNGetBasicBlockImmediateDominator(m_object); + if (!result) + return nullptr; + return new BasicBlock(result); +} + + +set<Ref<BasicBlock>> BasicBlock::GetDominatorTreeChildren() const +{ + size_t count; + BNBasicBlock** blocks = BNGetBasicBlockDominatorTreeChildren(m_object, &count); + + set<Ref<BasicBlock>> result; + for (size_t i = 0; i < count; i++) + result.insert(new BasicBlock(BNNewBasicBlockReference(blocks[i]))); + + BNFreeBasicBlockList(blocks, count); + return result; +} + + +set<Ref<BasicBlock>> BasicBlock::GetDominanceFrontier() const +{ + size_t count; + BNBasicBlock** blocks = BNGetBasicBlockDominanceFrontier(m_object, &count); + + set<Ref<BasicBlock>> result; + for (size_t i = 0; i < count; i++) + result.insert(new BasicBlock(BNNewBasicBlockReference(blocks[i]))); + + BNFreeBasicBlockList(blocks, count); + return result; +} + + +set<Ref<BasicBlock>> BasicBlock::GetIteratedDominanceFrontier(const set<Ref<BasicBlock>>& blocks) +{ + BNBasicBlock** blockSet = new BNBasicBlock*[blocks.size()]; + size_t i = 0; + for (auto& j : blocks) + blockSet[i++] = j->GetObject(); + + size_t count; + BNBasicBlock** resultBlocks = BNGetBasicBlockIteratedDominanceFrontier(blockSet, blocks.size(), &count); + delete[] blockSet; + + set<Ref<BasicBlock>> result; + for (size_t i = 0; i < count; i++) + result.insert(new BasicBlock(BNNewBasicBlockReference(resultBlocks[i]))); + + BNFreeBasicBlockList(resultBlocks, count); + return result; +} + + void BasicBlock::MarkRecentUse() { BNMarkBasicBlockAsRecentlyUsed(m_object); @@ -164,6 +273,8 @@ vector<DisassemblyTextLine> BasicBlock::GetDisassemblyText(DisassemblySettings* token.value = lines[i].tokens[j].value; token.size = lines[i].tokens[j].size; token.operand = lines[i].tokens[j].operand; + token.context = lines[i].tokens[j].context; + token.address = lines[i].tokens[j].address; line.tokens.push_back(token); } result.push_back(line); @@ -282,3 +393,9 @@ void BasicBlock::SetUserBasicBlockHighlight(uint8_t r, uint8_t g, uint8_t b, uin hc.alpha = alpha; SetUserBasicBlockHighlight(hc); } + + +bool BasicBlock::IsBackEdge(BasicBlock* source, BasicBlock* target) +{ + return source->GetDominators().count(target) != 0; +} diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index e1dab528..68cc2f43 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -59,6 +59,17 @@ void BinaryNinja::SetBundledPluginDirectory(const string& path) } +string BinaryNinja::GetInstallDirectory() +{ + char* path = BNGetInstallDirectory(); + if (!path) + return string(); + string result = path; + BNFreeString(path); + return result; +} + + string BinaryNinja::GetUserPluginDirectory() { char* path = BNGetUserPluginDirectory(); @@ -122,6 +133,26 @@ string BinaryNinja::GetVersionString() return result; } +string BinaryNinja::GetProduct() +{ + char* str = BNGetProduct(); + string result = str; + BNFreeString(str); + return result; +} + +string BinaryNinja::GetProductType() +{ + char* str = BNGetProductType(); + string result = str; + BNFreeString(str); + return result; +} + +int BinaryNinja::GetLicenseCount() +{ + return BNGetLicenseCount(); +} uint32_t BinaryNinja::GetBuildId() { @@ -237,3 +268,12 @@ void BinaryNinja::SetWorkerThreadCount(size_t count) { BNSetWorkerThreadCount(count); } + + +string BinaryNinja::GetUniqueIdentifierString() +{ + char* str = BNGetUniqueIdentifierString(); + string result = str; + BNFreeString(str); + return result; +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index a918fc42..b8b684be 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -51,6 +51,8 @@ namespace BinaryNinja RefCountObject(): m_refs(0) {} virtual ~RefCountObject() {} + RefCountObject* GetObject() { return this; } + void AddRef() { #ifdef WIN32 @@ -101,7 +103,7 @@ namespace BinaryNinja CoreRefCountObject(): m_refs(0), m_object(nullptr) {} virtual ~CoreRefCountObject() {} - T* GetObject() { return m_object; } + T* GetObject() const { return m_object; } void AddRef() { @@ -158,7 +160,7 @@ namespace BinaryNinja StaticCoreRefCountObject(): m_refs(0), m_object(nullptr) {} virtual ~StaticCoreRefCountObject() {} - T* GetObject() { return m_object; } + T* GetObject() const { return m_object; } void AddRef() { @@ -246,6 +248,36 @@ namespace BinaryNinja return m_obj == NULL; } + bool operator==(const T* obj) const + { + return m_obj->GetObject() == obj->GetObject(); + } + + bool operator==(const Ref<T>& obj) const + { + return m_obj->GetObject() == obj.m_obj->GetObject(); + } + + bool operator!=(const T* obj) const + { + return m_obj->GetObject() != obj->GetObject(); + } + + bool operator!=(const Ref<T>& obj) const + { + return m_obj->GetObject() != obj.m_obj->GetObject(); + } + + bool operator<(const T* obj) const + { + return m_obj->GetObject() < obj->GetObject(); + } + + bool operator<(const Ref<T>& obj) const + { + return m_obj->GetObject() < obj.m_obj->GetObject(); + } + T* GetPtr() const { return m_obj; @@ -277,6 +309,7 @@ namespace BinaryNinja class MainThreadAction; class MainThreadActionHandler; class InteractionHandler; + class QualifiedName; struct FormInputField; /*! Logs to the error console with the given BNLogLevel. @@ -343,6 +376,7 @@ namespace BinaryNinja void InitUserPlugins(); std::string GetBundledPluginDirectory(); void SetBundledPluginDirectory(const std::string& path); + std::string GetInstallDirectory(); std::string GetUserPluginDirectory(); std::string GetPathRelativeToBundledPluginDirectory(const std::string& path); @@ -352,6 +386,9 @@ namespace BinaryNinja std::string& output, std::string& errors, bool stdoutIsText=false, bool stderrIsText=true); std::string GetVersionString(); + std::string GetProduct(); + std::string GetProductType(); + int GetLicenseCount(); uint32_t GetBuildId(); bool AreAutoUpdatesEnabled(); @@ -368,7 +405,7 @@ namespace BinaryNinja bool DemangleMS(Architecture* arch, const std::string& mangledName, Type** outType, - std::vector<std::string>& outVarName); + QualifiedName& outVarName); void RegisterMainThread(MainThreadActionHandler* handler); Ref<MainThreadAction> ExecuteOnMainThread(const std::function<void()>& action); @@ -408,6 +445,53 @@ namespace BinaryNinja BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text, BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon); + std::string GetUniqueIdentifierString(); + + class QualifiedName + { + std::vector<std::string> m_name; + + public: + QualifiedName(); + QualifiedName(const std::string& name); + QualifiedName(const std::vector<std::string>& name); + QualifiedName(const QualifiedName& name); + + QualifiedName& operator=(const std::string& name); + QualifiedName& operator=(const std::vector<std::string>& name); + QualifiedName& operator=(const QualifiedName& name); + + bool operator==(const QualifiedName& other) const; + bool operator!=(const QualifiedName& other) const; + bool operator<(const QualifiedName& other) const; + + QualifiedName operator+(const QualifiedName& other) const; + + std::string& operator[](size_t i); + const std::string& operator[](size_t i) const; + std::vector<std::string>::iterator begin(); + std::vector<std::string>::iterator end(); + std::vector<std::string>::const_iterator begin() const; + std::vector<std::string>::const_iterator end() const; + std::string& front(); + const std::string& front() const; + std::string& back(); + const std::string& back() const; + void insert(std::vector<std::string>::iterator loc, const std::string& name); + void insert(std::vector<std::string>::iterator loc, std::vector<std::string>::iterator b, + std::vector<std::string>::iterator e); + void erase(std::vector<std::string>::iterator i); + void clear(); + void push_back(const std::string& name); + size_t size() const; + + std::string GetString() const; + + BNQualifiedName GetAPIObject() const; + static void FreeAPIObject(BNQualifiedName* name); + static QualifiedName FromAPIObject(BNQualifiedName* name); + }; + class DataBuffer { BNDataBuffer* m_buffer; @@ -590,6 +674,8 @@ namespace BinaryNinja static void DataVariableUpdatedCallback(void* ctxt, BNBinaryView* data, BNDataVariable* var); static void StringFoundCallback(void* ctxt, BNBinaryView* data, BNStringType type, uint64_t offset, size_t len); static void StringRemovedCallback(void* ctxt, BNBinaryView* data, BNStringType type, uint64_t offset, size_t len); + static void TypeDefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type); + static void TypeUndefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type); public: BinaryDataNotification(); @@ -608,6 +694,8 @@ namespace BinaryNinja virtual void OnDataVariableUpdated(BinaryView* view, const DataVariable& var) { (void)view; (void)var; } virtual void OnStringFound(BinaryView* data, BNStringType type, uint64_t offset, size_t len) { (void)data; (void)type; (void)offset; (void)len; } virtual void OnStringRemoved(BinaryView* data, BNStringType type, uint64_t offset, size_t len) { (void)data; (void)type; (void)offset; (void)len; } + virtual void OnTypeDefined(BinaryView* data, const QualifiedName& name, Type* type) { (void)data; (void)name; (void)type; } + virtual void OnTypeUndefined(BinaryView* data, const QualifiedName& name, Type* type) { (void)data; (void)name; (void)type; } }; class FileAccessor @@ -679,10 +767,15 @@ namespace BinaryNinja std::string text; uint64_t value; size_t size, operand; + BNInstructionTextTokenContext context; + uint64_t address; InstructionTextToken(); InstructionTextToken(BNInstructionTextTokenType type, const std::string& text, uint64_t value = 0, size_t size = 0, size_t operand = BN_INVALID_OPERAND); + InstructionTextToken(BNInstructionTextTokenType type, BNInstructionTextTokenContext context, + const std::string& text, uint64_t address, uint64_t value = 0, size_t size = 0, + size_t operand = BN_INVALID_OPERAND); }; struct DisassemblyTextLine @@ -746,7 +839,7 @@ namespace BinaryNinja uint64_t align, entrySize; }; - struct NameAndType; + struct QualifiedNameAndType; /*! BinaryView is the base class for creating views on binary data (e.g. ELF, PE, Mach-O). BinaryView should be subclassed to create a new BinaryView @@ -968,15 +1061,21 @@ namespace BinaryNinja std::vector<LinearDisassemblyLine> GetNextLinearDisassemblyLines(LinearDisassemblyPosition& pos, DisassemblySettings* settings); - bool ParseTypeString(const std::string& text, NameAndType& result, std::string& errors); + bool ParseTypeString(const std::string& text, QualifiedNameAndType& result, std::string& errors); + + std::map<QualifiedName, Ref<Type>> GetTypes(); + Ref<Type> GetTypeByName(const QualifiedName& name); + Ref<Type> GetTypeById(const std::string& id); + std::string GetTypeId(const QualifiedName& name); + QualifiedName GetTypeNameById(const std::string& id); + bool IsTypeAutoDefined(const QualifiedName& name); + QualifiedName DefineType(const std::string& id, const QualifiedName& defaultName, Ref<Type> type); + void DefineUserType(const QualifiedName& name, Ref<Type> type); + void UndefineType(const std::string& id); + void UndefineUserType(const QualifiedName& name); + void RenameType(const QualifiedName& oldName, const QualifiedName& newName); - std::map<std::string, Ref<Type>> GetTypes(); - Ref<Type> GetTypeByName(const std::string& name); - bool IsTypeAutoDefined(const std::string& name); - void DefineType(const std::string& name, Ref<Type> type); - void DefineUserType(const std::string& name, Ref<Type> type); - void UndefineType(const std::string& name); - void UndefineUserType(const std::string& name); + void RegisterPlatformTypes(Platform* platform); bool FindNextData(uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = NoFindFlags); @@ -995,6 +1094,7 @@ namespace BinaryNinja void RemoveUserSegment(uint64_t start, uint64_t length); std::vector<Segment> GetSegments(); bool GetSegmentAt(uint64_t addr, Segment& result); + bool GetAddressForDataOffset(uint64_t offset, uint64_t& addr); void AddAutoSection(const std::string& name, uint64_t start, uint64_t length, const std::string& type = "", uint64_t align = 1, uint64_t entrySize = 0, const std::string& linkedSection = "", @@ -1426,13 +1526,17 @@ namespace BinaryNinja void SetBinaryViewTypeConstant(const std::string& type, const std::string& name, uint64_t value); bool ParseTypesFromSource(const std::string& source, const std::string& fileName, - std::map<std::string, Ref<Type>>& types, std::map<std::string, Ref<Type>>& variables, - std::map<std::string, Ref<Type>>& functions, std::string& errors, - const std::vector<std::string>& includeDirs = std::vector<std::string>()); - bool ParseTypesFromSourceFile(const std::string& fileName, std::map<std::string, Ref<Type>>& types, - std::map<std::string, Ref<Type>>& variables, - std::map<std::string, Ref<Type>>& functions, std::string& errors, - const std::vector<std::string>& includeDirs = std::vector<std::string>()); + std::map<QualifiedName, Ref<Type>>& types, + std::map<QualifiedName, Ref<Type>>& variables, + std::map<QualifiedName, Ref<Type>>& functions, std::string& errors, + const std::vector<std::string>& includeDirs = std::vector<std::string>(), + const std::string& autoTypeSource = ""); + bool ParseTypesFromSourceFile(const std::string& fileName, + std::map<QualifiedName, Ref<Type>>& types, + std::map<QualifiedName, Ref<Type>>& variables, + std::map<QualifiedName, Ref<Type>>& functions, std::string& errors, + const std::vector<std::string>& includeDirs = std::vector<std::string>(), + const std::string& autoTypeSource = ""); void RegisterCallingConvention(CallingConvention* cc); std::vector<Ref<CallingConvention>> GetCallingConventions(); @@ -1495,7 +1599,7 @@ namespace BinaryNinja }; class Structure; - class UnknownType; + class NamedTypeReference; class Enumeration; struct NameAndType @@ -1504,6 +1608,12 @@ namespace BinaryNinja Ref<Type> type; }; + struct QualifiedNameAndType + { + QualifiedName name; + Ref<Type> type; + }; + class Type: public CoreRefCountObject<BNType, BNNewTypeReference, BNFreeType> { public: @@ -1523,17 +1633,21 @@ namespace BinaryNinja bool CanReturn() const; Ref<Structure> GetStructure() const; Ref<Enumeration> GetEnumeration() const; - Ref<UnknownType> GetUnknownType() const; + Ref<NamedTypeReference> GetNamedTypeReference() const; uint64_t GetElementCount() const; void SetFunctionCanReturn(bool canReturn); std::string GetString() const; - std::string GetTypeAndName(const std::vector<std::string>& name) const; + std::string GetTypeAndName(const QualifiedName& name) const; std::string GetStringBeforeName() const; std::string GetStringAfterName() const; + std::vector<InstructionTextToken> GetTokens() const; + std::vector<InstructionTextToken> GetTokensBeforeName() const; + std::vector<InstructionTextToken> GetTokensAfterName() const; + Ref<Type> Duplicate() const; static Ref<Type> VoidType(); @@ -1541,7 +1655,10 @@ namespace BinaryNinja static Ref<Type> IntegerType(size_t width, bool sign, const std::string& altName = ""); static Ref<Type> FloatType(size_t width, const std::string& typeName = ""); static Ref<Type> StructureType(Structure* strct); - static Ref<Type> UnknownNamedType(UnknownType* unknwn); + static Ref<Type> NamedType(NamedTypeReference* ref, size_t width = 0, size_t align = 1); + static Ref<Type> NamedType(const QualifiedName& name, Type* type); + static Ref<Type> NamedType(const std::string& id, const QualifiedName& name, Type* type); + static Ref<Type> NamedType(BinaryView* view, const QualifiedName& name); static Ref<Type> EnumerationType(Architecture* arch, Enumeration* enm, size_t width = 0, bool issigned = false); static Ref<Type> PointerType(Architecture* arch, Type* type, bool cnst = false, bool vltl = false, BNReferenceType refType = PointerReferenceType); @@ -1549,15 +1666,29 @@ namespace BinaryNinja static Ref<Type> FunctionType(Type* returnValue, CallingConvention* callingConvention, const std::vector<NameAndType>& params, bool varArg = false); - static std::string GetQualifiedName(const std::vector<std::string>& names); + static std::string GenerateAutoTypeId(const std::string& source, const QualifiedName& name); + static std::string GenerateAutoDemangledTypeId(const QualifiedName& name); + static std::string GetAutoDemangledTypeIdSource(); }; - class UnknownType: public CoreRefCountObject<BNUnknownType, BNNewUnknownTypeReference, BNFreeUnknownType> + class NamedTypeReference: public CoreRefCountObject<BNNamedTypeReference, BNNewNamedTypeReference, + BNFreeNamedTypeReference> { public: - UnknownType(BNUnknownType* s, std::vector<std::string> name = {}); - std::vector<std::string> GetName() const; - void SetName(const std::vector<std::string>& name); + NamedTypeReference(BNNamedTypeReference* nt); + NamedTypeReference(BNNamedTypeReferenceClass cls = UnknownNamedTypeClass, const std::string& id = "", + const QualifiedName& name = QualifiedName()); + BNNamedTypeReferenceClass GetTypeClass() const; + void SetTypeClass(BNNamedTypeReferenceClass cls); + std::string GetTypeId() const; + void SetTypeId(const std::string& id); + QualifiedName GetName() const; + void SetName(const QualifiedName& name); + + static Ref<NamedTypeReference> GenerateAutoTypeReference(BNNamedTypeReferenceClass cls, + const std::string& source, const QualifiedName& name); + static Ref<NamedTypeReference> GenerateAutoDemangledTypeReference(BNNamedTypeReferenceClass cls, + const QualifiedName& name); }; struct StructureMember @@ -1570,13 +1701,14 @@ namespace BinaryNinja class Structure: public CoreRefCountObject<BNStructure, BNNewStructureReference, BNFreeStructure> { public: + Structure(); Structure(BNStructure* s); - std::vector<std::string> GetName() const; - void SetName(const std::vector<std::string>& name); std::vector<StructureMember> GetMembers() const; uint64_t GetWidth() const; + void SetWidth(size_t width); size_t GetAlignment() const; + void SetAlignment(size_t align); bool IsPacked() const; void SetPacked(bool packed); bool IsUnion() const; @@ -1585,6 +1717,7 @@ namespace BinaryNinja void AddMember(Type* type, const std::string& name); void AddMemberAtOffset(Type* type, const std::string& name, uint64_t offset); void RemoveMember(size_t idx); + void ReplaceMember(size_t idx, Type* type, const std::string& name); }; struct EnumerationMember @@ -1599,13 +1732,12 @@ namespace BinaryNinja public: Enumeration(BNEnumeration* e); - std::vector<std::string> GetName() const; - void SetName(const std::vector<std::string>& name); - std::vector<EnumerationMember> GetMembers() const; void AddMember(const std::string& name); void AddMemberWithValue(const std::string& name, uint64_t value); + void RemoveMember(size_t idx); + void ReplaceMember(size_t idx, const std::string& name, uint64_t value); }; class DisassemblySettings: public CoreRefCountObject<BNDisassemblySettings, @@ -1629,8 +1761,7 @@ namespace BinaryNinja struct BasicBlockEdge { BNBranchType type; - uint64_t target; - Ref<Architecture> arch; + Ref<BasicBlock> target; }; class BasicBlock: public CoreRefCountObject<BNBasicBlock, BNNewBasicBlockReference, BNFreeBasicBlock> @@ -1645,9 +1776,19 @@ namespace BinaryNinja uint64_t GetEnd() const; uint64_t GetLength() const; + size_t GetIndex() const; + std::vector<BasicBlockEdge> GetOutgoingEdges() const; + std::vector<BasicBlockEdge> GetIncomingEdges() const; bool HasUndeterminedOutgoingEdges() const; + std::set<Ref<BasicBlock>> GetDominators() const; + std::set<Ref<BasicBlock>> GetStrictDominators() const; + Ref<BasicBlock> GetImmediateDominator() const; + std::set<Ref<BasicBlock>> GetDominatorTreeChildren() const; + std::set<Ref<BasicBlock>> GetDominanceFrontier() const; + static std::set<Ref<BasicBlock>> GetIteratedDominanceFrontier(const std::set<Ref<BasicBlock>>& blocks); + void MarkRecentUse(); std::vector<std::vector<InstructionTextToken>> GetAnnotations(); @@ -1665,6 +1806,8 @@ namespace BinaryNinja 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); + + static bool IsBackEdge(BasicBlock* source, BasicBlock* target); }; struct StackVariable @@ -1837,8 +1980,7 @@ namespace BinaryNinja struct FunctionGraphEdge { BNBranchType type; - uint64_t target; - Ref<Architecture> arch; + Ref<BasicBlock> target; std::vector<BNPoint> points; }; @@ -2245,6 +2387,21 @@ namespace BinaryNinja Ref<Platform> GetRelatedPlatform(Architecture* arch); void AddRelatedPlatform(Architecture* arch, Platform* platform); Ref<Platform> GetAssociatedPlatformByAddress(uint64_t& addr); + + std::map<QualifiedName, Ref<Type>> GetTypes(); + std::map<QualifiedName, Ref<Type>> GetVariables(); + std::map<QualifiedName, Ref<Type>> GetFunctions(); + std::map<uint32_t, QualifiedNameAndType> GetSystemCalls(); + Ref<Type> GetTypeByName(const QualifiedName& name); + Ref<Type> GetVariableByName(const QualifiedName& name); + Ref<Type> GetFunctionByName(const QualifiedName& name); + std::string GetSystemCallName(uint32_t n); + Ref<Type> GetSystemCallType(uint32_t n); + + std::string GenerateAutoPlatformTypeId(const QualifiedName& name); + Ref<NamedTypeReference> GenerateAutoPlatformTypeReference(BNNamedTypeReferenceClass cls, + const QualifiedName& name); + std::string GetAutoPlatformTypeIdSource(); }; class ScriptingOutputListener diff --git a/binaryninjacore.h b/binaryninjacore.h index b2ed7cd3..3003ea3a 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -63,6 +63,24 @@ #define BN_DEFAULT_MIN_STRING_LENGTH 4 #define BN_MAX_STRING_LENGTH 128 +#define LLVM_SVCS_CB_NOTE 0 +#define LLVM_SVCS_CB_WARNING 1 +#define LLVM_SVCS_CB_ERROR 2 + +#define LLVM_SVCS_DIALECT_UNSPEC 0 +#define LLVM_SVCS_DIALECT_ATT 1 +#define LLVM_SVCS_DIALECT_INTEL 2 + +#define LLVM_SVCS_CM_DEFAULT 0 +#define LLVM_SVCS_CM_SMALL 1 +#define LLVM_SVCS_CM_KERNEL 2 +#define LLVM_SVCS_CM_MEDIUM 3 +#define LLVM_SVCS_CM_LARGE 4 + +#define LLVM_SVCS_RM_STATIC 0 +#define LLVM_SVCS_RM_PIC 1 +#define LLVM_SVCS_RM_DYNAMIC_NO_PIC 2 + #ifdef __cplusplus extern "C" { @@ -94,7 +112,7 @@ extern "C" struct BNLowLevelILFunction; struct BNType; struct BNStructure; - struct BNUnknownType; + struct BNNamedTypeReference; struct BNEnumeration; struct BNCallingConvention; struct BNPlatform; @@ -166,19 +184,17 @@ extern "C" FloatingPointToken = 8, AnnotationToken = 9, CodeRelativeAddressToken = 10, - StackVariableTypeToken = 11, - DataVariableTypeToken = 12, - FunctionReturnTypeToken = 13, - FunctionAttributeToken = 14, - ArgumentTypeToken = 15, - ArgumentNameToken = 16, - HexDumpByteValueToken = 17, - HexDumpSkippedByteToken = 18, - HexDumpInvalidByteToken = 19, - HexDumpTextToken = 20, - OpcodeToken = 21, - StringToken = 22, - CharacterConstantToken = 23, + ArgumentNameToken = 11, + HexDumpByteValueToken = 12, + HexDumpSkippedByteToken = 13, + HexDumpInvalidByteToken = 14, + HexDumpTextToken = 15, + OpcodeToken = 16, + StringToken = 17, + CharacterConstantToken = 18, + KeywordToken = 19, + TypeNameToken = 20, + FieldNameToken = 21, // The following are output by the analysis system automatically, these should // not be used directly by the architecture plugins @@ -189,6 +205,15 @@ extern "C" AddressDisplayToken = 68 }; + enum BNInstructionTextTokenContext + { + NoTokenContext = 0, + StackVariableTokenContext = 1, + DataVariableTokenContext = 2, + FunctionReturnTokenContext = 3, + ArgumentTokenContext = 4 + }; + enum BNLinearDisassemblyLineType { BlankLineType, @@ -364,7 +389,17 @@ extern "C" FunctionTypeClass = 8, VarArgsTypeClass = 9, ValueTypeClass = 10, - UnknownTypeClass = 11 + NamedTypeReferenceClass = 11 + }; + + enum BNNamedTypeReferenceClass + { + UnknownNamedTypeClass = 0, + TypedefNamedTypeClass = 1, + ClassNamedTypeClass = 2, + StructNamedTypeClass = 3, + UnionNamedTypeClass = 4, + EnumNamedTypeClass = 5 }; enum BNStructureType @@ -614,6 +649,12 @@ extern "C" bool (*navigate)(void* ctxt, const char* view, uint64_t offset); }; + struct BNQualifiedName + { + char** name; + size_t nameCount; + }; + struct BNBinaryDataNotification { void* context; @@ -628,6 +669,8 @@ extern "C" void (*dataVariableUpdated)(void* ctxt, BNBinaryView* view, BNDataVariable* var); void (*stringFound)(void* ctxt, BNBinaryView* view, BNStringType type, uint64_t offset, size_t len); void (*stringRemoved)(void* ctxt, BNBinaryView* view, BNStringType type, uint64_t offset, size_t len); + void (*typeDefined)(void* ctxt, BNBinaryView* view, BNQualifiedName* name, BNType* type); + void (*typeUndefined)(void* ctxt, BNBinaryView* view, BNQualifiedName* name, BNType* type); }; struct BNFileAccessor @@ -708,6 +751,8 @@ extern "C" char* text; uint64_t value; size_t size, operand; + BNInstructionTextTokenContext context; + uint64_t address; }; struct BNInstructionTextLine @@ -766,8 +811,7 @@ extern "C" struct BNBasicBlockEdge { BNBranchType type; - uint64_t target; - BNArchitecture* arch; + BNBasicBlock* target; }; struct BNPoint @@ -779,8 +823,7 @@ extern "C" struct BNFunctionGraphEdge { BNBranchType type; - uint64_t target; - BNArchitecture* arch; + BNBasicBlock* target; BNPoint* points; size_t pointCount; }; @@ -831,6 +874,12 @@ extern "C" BNType* type; }; + struct BNQualifiedNameAndType + { + BNQualifiedName name; + BNType* type; + }; + struct BNStructureMember { BNType* type; @@ -853,9 +902,9 @@ extern "C" struct BNTypeParserResult { - BNNameAndType* types; - BNNameAndType* variables; - BNNameAndType* functions; + BNQualifiedNameAndType* types; + BNQualifiedNameAndType* variables; + BNQualifiedNameAndType* functions; size_t typeCount, variableCount, functionCount; }; @@ -1187,6 +1236,13 @@ extern "C" uint64_t end; }; + struct BNSystemCallInfo + { + uint32_t number; + BNQualifiedName name; + BNType* type; + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); BINARYNINJACOREAPI void BNFreeStringList(char** strs, size_t count); @@ -1197,17 +1253,26 @@ extern "C" BINARYNINJACOREAPI uint32_t BNGetBuildId(void); BINARYNINJACOREAPI bool BNIsLicenseValidated(void); + BINARYNINJACOREAPI char* BNGetProduct(void); + BINARYNINJACOREAPI char* BNGetProductType(void); + BINARYNINJACOREAPI int BNGetLicenseCount(void); BINARYNINJACOREAPI void BNRegisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); BINARYNINJACOREAPI void BNUnregisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); + BINARYNINJACOREAPI char* BNGetUniqueIdentifierString(void); + // Plugin initialization BINARYNINJACOREAPI void BNInitCorePlugins(void); BINARYNINJACOREAPI void BNInitUserPlugins(void); + BINARYNINJACOREAPI char* BNGetInstallDirectory(void); BINARYNINJACOREAPI char* BNGetBundledPluginDirectory(void); BINARYNINJACOREAPI void BNSetBundledPluginDirectory(const char* path); + BINARYNINJACOREAPI char* BNGetUserDirectory(void); BINARYNINJACOREAPI char* BNGetUserPluginDirectory(void); + BINARYNINJACOREAPI void BNSaveLastRun(void); + BINARYNINJACOREAPI char* BNGetPathRelativeToBundledPluginDirectory(const char* path); BINARYNINJACOREAPI char* BNGetPathRelativeToUserPluginDirectory(const char* path); @@ -1387,6 +1452,7 @@ extern "C" BINARYNINJACOREAPI BNSegment* BNGetSegments(BNBinaryView* view, size_t* count); BINARYNINJACOREAPI void BNFreeSegmentList(BNSegment* segments); BINARYNINJACOREAPI bool BNGetSegmentAt(BNBinaryView* view, uint64_t addr, BNSegment* result); + BINARYNINJACOREAPI bool BNGetAddressForDataOffset(BNBinaryView* view, uint64_t offset, uint64_t* addr); BINARYNINJACOREAPI void BNAddAutoSection(BNBinaryView* view, const char* name, uint64_t start, uint64_t length, const char* type, uint64_t align, uint64_t entrySize, const char* linkedSection, const char* infoSection, @@ -1680,8 +1746,17 @@ extern "C" BINARYNINJACOREAPI uint64_t BNGetBasicBlockEnd(BNBasicBlock* block); BINARYNINJACOREAPI uint64_t BNGetBasicBlockLength(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockOutgoingEdges(BNBasicBlock* block, size_t* count); - BINARYNINJACOREAPI void BNFreeBasicBlockOutgoingEdgeList(BNBasicBlockEdge* edges); + BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockIncomingEdges(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI void BNFreeBasicBlockEdgeList(BNBasicBlockEdge* edges, size_t count); BINARYNINJACOREAPI bool BNBasicBlockHasUndeterminedOutgoingEdges(BNBasicBlock* block); + BINARYNINJACOREAPI size_t BNGetBasicBlockIndex(BNBasicBlock* block); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockDominators(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockStrictDominators(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI BNBasicBlock* BNGetBasicBlockImmediateDominator(BNBasicBlock* block); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockDominatorTreeChildren(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockDominanceFrontier(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockIteratedDominanceFrontier(BNBasicBlock** blocks, + size_t incomingCount, size_t* outputCount); BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetBasicBlockDisassemblyText(BNBasicBlock* block, BNDisassemblySettings* settings, size_t* count); @@ -1765,17 +1840,31 @@ extern "C" BINARYNINJACOREAPI void BNFreeDataVariables(BNDataVariable* vars, size_t count); BINARYNINJACOREAPI bool BNGetDataVariableAtAddress(BNBinaryView* view, uint64_t addr, BNDataVariable* var); - BINARYNINJACOREAPI bool BNParseTypeString(BNBinaryView* view, const char* text, BNNameAndType* result, char** errors); + BINARYNINJACOREAPI bool BNParseTypeString(BNBinaryView* view, const char* text, + BNQualifiedNameAndType* result, char** errors); BINARYNINJACOREAPI void BNFreeNameAndType(BNNameAndType* obj); + BINARYNINJACOREAPI void BNFreeQualifiedNameAndType(BNQualifiedNameAndType* obj); - BINARYNINJACOREAPI BNNameAndType* BNGetAnalysisTypeList(BNBinaryView* view, size_t* count); - BINARYNINJACOREAPI void BNFreeTypeList(BNNameAndType* types, size_t count); - BINARYNINJACOREAPI BNType* BNGetAnalysisTypeByName(BNBinaryView* view, const char* name); - BINARYNINJACOREAPI bool BNIsAnalysisTypeAutoDefined(BNBinaryView* view, const char* name); - BINARYNINJACOREAPI void BNDefineAnalysisType(BNBinaryView* view, const char* name, BNType* type); - BINARYNINJACOREAPI void BNDefineUserAnalysisType(BNBinaryView* view, const char* name, BNType* type); - BINARYNINJACOREAPI void BNUndefineAnalysisType(BNBinaryView* view, const char* name); - BINARYNINJACOREAPI void BNUndefineUserAnalysisType(BNBinaryView* view, const char* name); + BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetAnalysisTypeList(BNBinaryView* view, size_t* count); + BINARYNINJACOREAPI void BNFreeTypeList(BNQualifiedNameAndType* types, size_t count); + BINARYNINJACOREAPI BNType* BNGetAnalysisTypeByName(BNBinaryView* view, BNQualifiedName* name); + BINARYNINJACOREAPI BNType* BNGetAnalysisTypeById(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI char* BNGetAnalysisTypeId(BNBinaryView* view, BNQualifiedName* name); + BINARYNINJACOREAPI BNQualifiedName BNGetAnalysisTypeNameById(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI bool BNIsAnalysisTypeAutoDefined(BNBinaryView* view, BNQualifiedName* name); + BINARYNINJACOREAPI BNQualifiedName BNDefineAnalysisType(BNBinaryView* view, const char* id, + BNQualifiedName* defaultName, BNType* type); + BINARYNINJACOREAPI void BNDefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name, BNType* type); + BINARYNINJACOREAPI void BNUndefineAnalysisType(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI void BNUndefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name); + BINARYNINJACOREAPI void BNRenameAnalysisType(BNBinaryView* view, BNQualifiedName* oldName, BNQualifiedName* newName); + BINARYNINJACOREAPI char* BNGenerateAutoTypeId(const char* source, BNQualifiedName* name); + BINARYNINJACOREAPI char* BNGenerateAutoPlatformTypeId(BNPlatform* platform, BNQualifiedName* name); + BINARYNINJACOREAPI char* BNGenerateAutoDemangledTypeId(BNQualifiedName* name); + BINARYNINJACOREAPI char* BNGetAutoPlatformTypeIdSource(BNPlatform* platform); + BINARYNINJACOREAPI char* BNGetAutoDemangledTypeIdSource(void); + + BINARYNINJACOREAPI void BNRegisterPlatformTypes(BNBinaryView* view, BNPlatform* platform); BINARYNINJACOREAPI void BNReanalyzeAllFunctions(BNBinaryView* view); BINARYNINJACOREAPI void BNReanalyzeFunction(BNFunction* func); @@ -1939,7 +2028,7 @@ extern "C" BNNameAndType* params, size_t paramCount, bool varArg); BINARYNINJACOREAPI BNType* BNNewTypeReference(BNType* type); BINARYNINJACOREAPI BNType* BNDuplicateType(BNType* type); - BINARYNINJACOREAPI char* BNGetTypeAndName(BNType* type, const char** nameList, size_t nameCount); + BINARYNINJACOREAPI char* BNGetTypeAndName(BNType* type, BNQualifiedName* name); BINARYNINJACOREAPI void BNFreeType(BNType* type); BINARYNINJACOREAPI BNTypeClass BNGetTypeClass(BNType* type); @@ -1957,30 +2046,42 @@ extern "C" BINARYNINJACOREAPI bool BNFunctionTypeCanReturn(BNType* type); BINARYNINJACOREAPI BNStructure* BNGetTypeStructure(BNType* type); BINARYNINJACOREAPI BNEnumeration* BNGetTypeEnumeration(BNType* type); + BINARYNINJACOREAPI BNNamedTypeReference* BNGetTypeNamedTypeReference(BNType* type); BINARYNINJACOREAPI uint64_t BNGetTypeElementCount(BNType* type); BINARYNINJACOREAPI void BNSetFunctionCanReturn(BNType* type, bool canReturn); BINARYNINJACOREAPI char* BNGetTypeString(BNType* type); BINARYNINJACOREAPI char* BNGetTypeStringBeforeName(BNType* type); BINARYNINJACOREAPI char* BNGetTypeStringAfterName(BNType* type); + BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypeTokens(BNType* type, size_t* count); + BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypeTokensBeforeName(BNType* type, size_t* count); + BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypeTokensAfterName(BNType* type, size_t* count); + BINARYNINJACOREAPI void BNFreeTokenList(BNInstructionTextToken* tokens, size_t count); - BINARYNINJACOREAPI BNType* BNCreateUnknownNamedType(BNUnknownType* ut); - BINARYNINJACOREAPI BNUnknownType* BNCreateUnknownType(void); - BINARYNINJACOREAPI void BNSetUnknownTypeName(BNUnknownType* ut, const char** name, size_t size); - BINARYNINJACOREAPI char** BNGetUnknownTypeName(BNUnknownType* ut, size_t* size); - BINARYNINJACOREAPI void BNFreeUnknownType(BNUnknownType* ut); - BINARYNINJACOREAPI BNUnknownType* BNNewUnknownTypeReference(BNUnknownType* ut); + BINARYNINJACOREAPI BNType* BNCreateNamedTypeReference(BNNamedTypeReference* nt, size_t width, size_t align); + BINARYNINJACOREAPI BNType* BNCreateNamedTypeReferenceFromTypeAndId(const char* id, BNQualifiedName* name, BNType* type); + BINARYNINJACOREAPI BNType* BNCreateNamedTypeReferenceFromType(BNBinaryView* view, BNQualifiedName* name); + BINARYNINJACOREAPI BNNamedTypeReference* BNCreateNamedType(void); + BINARYNINJACOREAPI void BNSetTypeReferenceClass(BNNamedTypeReference* nt, BNNamedTypeReferenceClass cls); + BINARYNINJACOREAPI BNNamedTypeReferenceClass BNGetTypeReferenceClass(BNNamedTypeReference* nt); + BINARYNINJACOREAPI void BNSetTypeReferenceId(BNNamedTypeReference* nt, const char* id); + BINARYNINJACOREAPI char* BNGetTypeReferenceId(BNNamedTypeReference* nt); + BINARYNINJACOREAPI void BNSetTypeReferenceName(BNNamedTypeReference* nt, BNQualifiedName* name); + BINARYNINJACOREAPI BNQualifiedName BNGetTypeReferenceName(BNNamedTypeReference* nt); + BINARYNINJACOREAPI void BNFreeQualifiedName(BNQualifiedName* name); + BINARYNINJACOREAPI void BNFreeNamedTypeReference(BNNamedTypeReference* nt); + BINARYNINJACOREAPI BNNamedTypeReference* BNNewNamedTypeReference(BNNamedTypeReference* nt); BINARYNINJACOREAPI BNStructure* BNCreateStructure(void); BINARYNINJACOREAPI BNStructure* BNNewStructureReference(BNStructure* s); BINARYNINJACOREAPI void BNFreeStructure(BNStructure* s); - BINARYNINJACOREAPI char** BNGetStructureName(BNStructure* s, size_t* size); - BINARYNINJACOREAPI void BNSetStructureName(BNStructure* s, const char** names, size_t size); BINARYNINJACOREAPI BNStructureMember* BNGetStructureMembers(BNStructure* s, size_t* count); BINARYNINJACOREAPI void BNFreeStructureMemberList(BNStructureMember* members, size_t count); BINARYNINJACOREAPI uint64_t BNGetStructureWidth(BNStructure* s); + BINARYNINJACOREAPI void BNSetStructureWidth(BNStructure* s, uint64_t width); BINARYNINJACOREAPI size_t BNGetStructureAlignment(BNStructure* s); + BINARYNINJACOREAPI void BNSetStructureAlignment(BNStructure* s, size_t align); BINARYNINJACOREAPI bool BNIsStructurePacked(BNStructure* s); BINARYNINJACOREAPI void BNSetStructurePacked(BNStructure* s, bool packed); BINARYNINJACOREAPI bool BNIsStructureUnion(BNStructure* s); @@ -1989,28 +2090,29 @@ extern "C" BINARYNINJACOREAPI void BNAddStructureMember(BNStructure* s, BNType* type, const char* name); BINARYNINJACOREAPI void BNAddStructureMemberAtOffset(BNStructure* s, BNType* type, const char* name, uint64_t offset); BINARYNINJACOREAPI void BNRemoveStructureMember(BNStructure* s, size_t idx); + BINARYNINJACOREAPI void BNReplaceStructureMember(BNStructure* s, size_t idx, BNType* type, const char* name); BINARYNINJACOREAPI BNEnumeration* BNCreateEnumeration(void); BINARYNINJACOREAPI BNEnumeration* BNNewEnumerationReference(BNEnumeration* e); BINARYNINJACOREAPI void BNFreeEnumeration(BNEnumeration* e); - BINARYNINJACOREAPI char** BNGetEnumerationName(BNEnumeration* e, size_t* size); - BINARYNINJACOREAPI void BNSetEnumerationName(BNEnumeration* e, const char** name, size_t size); BINARYNINJACOREAPI BNEnumerationMember* BNGetEnumerationMembers(BNEnumeration* e, size_t* count); BINARYNINJACOREAPI void BNFreeEnumerationMemberList(BNEnumerationMember* members, size_t count); BINARYNINJACOREAPI void BNAddEnumerationMember(BNEnumeration* e, const char* name); BINARYNINJACOREAPI void BNAddEnumerationMemberWithValue(BNEnumeration* e, const char* name, uint64_t value); + BINARYNINJACOREAPI void BNRemoveEnumerationMember(BNEnumeration* e, size_t idx); + BINARYNINJACOREAPI void BNReplaceEnumerationMember(BNEnumeration* e, size_t idx, const char* name, uint64_t value); // Source code processing BINARYNINJACOREAPI bool BNPreprocessSource(const char* source, const char* fileName, char** output, char** errors, - const char** includeDirs, size_t includeDirCount); + const char** includeDirs, size_t includeDirCount); BINARYNINJACOREAPI bool BNParseTypesFromSource(BNArchitecture* arch, const char* source, const char* fileName, - BNTypeParserResult* result, char** errors, - const char** includeDirs, size_t includeDirCount); + BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount, + const char* autoTypeSource); BINARYNINJACOREAPI bool BNParseTypesFromSourceFile(BNArchitecture* arch, const char* fileName, - BNTypeParserResult* result, char** errors, - const char** includeDirs, size_t includeDirCount); + BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount, + const char* autoTypeSource); BINARYNINJACOREAPI void BNFreeTypeParserResult(BNTypeParserResult* result); // Updates @@ -2139,6 +2241,17 @@ extern "C" BINARYNINJACOREAPI void BNAddRelatedPlatform(BNPlatform* platform, BNArchitecture* arch, BNPlatform* related); BINARYNINJACOREAPI BNPlatform* BNGetAssociatedPlatformByAddress(BNPlatform* platform, uint64_t* addr); + BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetPlatformTypes(BNPlatform* platform, size_t* count); + BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetPlatformVariables(BNPlatform* platform, size_t* count); + BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetPlatformFunctions(BNPlatform* platform, size_t* count); + BINARYNINJACOREAPI BNSystemCallInfo* BNGetPlatformSystemCalls(BNPlatform* platform, size_t* count); + BINARYNINJACOREAPI void BNFreeSystemCallList(BNSystemCallInfo* syscalls, size_t count); + BINARYNINJACOREAPI BNType* BNGetPlatformTypeByName(BNPlatform* platform, BNQualifiedName* name); + BINARYNINJACOREAPI BNType* BNGetPlatformVariableByName(BNPlatform* platform, BNQualifiedName* name); + BINARYNINJACOREAPI BNType* BNGetPlatformFunctionByName(BNPlatform* platform, BNQualifiedName* name); + BINARYNINJACOREAPI char* BNGetPlatformSystemCallName(BNPlatform* platform, uint32_t number); + BINARYNINJACOREAPI BNType* BNGetPlatformSystemCallType(BNPlatform* platform, uint32_t number); + //Demangler BINARYNINJACOREAPI bool BNDemangleMS(BNArchitecture* arch, const char* mangledName, @@ -2243,6 +2356,15 @@ extern "C" char*** outVarName, size_t* outVarNameElements); BINARYNINJACOREAPI void BNFreeDemangledName(char*** name, size_t nameElements); + + // LLVM Services APIs + BINARYNINJACOREAPI void BNLlvmServicesInit(void); + + BINARYNINJACOREAPI int BNLlvmServicesAssemble(const char *src, int dialect, const char *triplet, + int codeModel, int relocMode, char **outBytes, int *outBytesLen, char **err, int *errLen); + + BINARYNINJACOREAPI void BNLlvmServicesAssembleFree(char *outBytes, char *err); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index cc6667c5..8edbc2bf 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -129,6 +129,24 @@ void BinaryDataNotification::StringRemovedCallback(void* ctxt, BNBinaryView* obj } +void BinaryDataNotification::TypeDefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + notify->OnTypeDefined(view, QualifiedName::FromAPIObject(name), typeObj); +} + + +void BinaryDataNotification::TypeUndefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + notify->OnTypeUndefined(view, QualifiedName::FromAPIObject(name), typeObj); +} + + BinaryDataNotification::BinaryDataNotification() { m_callbacks.context = this; @@ -143,6 +161,8 @@ BinaryDataNotification::BinaryDataNotification() m_callbacks.dataVariableUpdated = DataVariableUpdatedCallback; m_callbacks.stringFound = StringFoundCallback; m_callbacks.stringRemoved = StringRemovedCallback; + m_callbacks.typeDefined = TypeDefinedCallback; + m_callbacks.typeUndefined = TypeUndefinedCallback; } @@ -1366,6 +1386,8 @@ vector<LinearDisassemblyLine> BinaryView::GetPreviousLinearDisassemblyLines(Line token.value = lines[i].contents.tokens[j].value; token.size = lines[i].contents.tokens[j].size; token.operand = lines[i].contents.tokens[j].operand; + token.context = lines[i].contents.tokens[j].context; + token.address = lines[i].contents.tokens[j].address; line.contents.tokens.push_back(token); } result.push_back(line); @@ -1409,6 +1431,8 @@ vector<LinearDisassemblyLine> BinaryView::GetNextLinearDisassemblyLines(LinearDi token.value = lines[i].contents.tokens[j].value; token.size = lines[i].contents.tokens[j].size; token.operand = lines[i].contents.tokens[j].operand; + token.context = lines[i].contents.tokens[j].context; + token.address = lines[i].contents.tokens[j].address; line.contents.tokens.push_back(token); } result.push_back(line); @@ -1423,9 +1447,9 @@ vector<LinearDisassemblyLine> BinaryView::GetNextLinearDisassemblyLines(LinearDi } -bool BinaryView::ParseTypeString(const string& text, NameAndType& result, string& errors) +bool BinaryView::ParseTypeString(const string& text, QualifiedNameAndType& result, string& errors) { - BNNameAndType nt; + BNQualifiedNameAndType nt; char* errorStr; if (!BNParseTypeString(m_object, text.c_str(), &nt, &errorStr)) @@ -1435,64 +1459,127 @@ bool BinaryView::ParseTypeString(const string& text, NameAndType& result, string return false; } - result.name = nt.name; - result.type = new Type(nt.type); + result.name = QualifiedName::FromAPIObject(&nt.name); + result.type = new Type(BNNewTypeReference(nt.type)); errors = ""; - BNFreeString(nt.name); + BNFreeQualifiedNameAndType(&nt); return true; } -map<string, Ref<Type>> BinaryView::GetTypes() +map<QualifiedName, Ref<Type>> BinaryView::GetTypes() { size_t count; - BNNameAndType* types = BNGetAnalysisTypeList(m_object, &count); + BNQualifiedNameAndType* types = BNGetAnalysisTypeList(m_object, &count); - map<string, Ref<Type>> result; + map<QualifiedName, Ref<Type>> result; for (size_t i = 0; i < count; i++) - result[types[i].name] = new Type(BNNewTypeReference(types[i].type)); + { + QualifiedName name = QualifiedName::FromAPIObject(&types[i].name); + result[name] = new Type(BNNewTypeReference(types[i].type)); + } BNFreeTypeList(types, count); return result; } -Ref<Type> BinaryView::GetTypeByName(const string& name) +Ref<Type> BinaryView::GetTypeByName(const QualifiedName& name) { - BNType* type = BNGetAnalysisTypeByName(m_object, name.c_str()); + BNQualifiedName nameObj = name.GetAPIObject(); + BNType* type = BNGetAnalysisTypeByName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + if (!type) return nullptr; return new Type(type); } -bool BinaryView::IsTypeAutoDefined(const std::string& name) +Ref<Type> BinaryView::GetTypeById(const string& id) +{ + BNType* type = BNGetAnalysisTypeById(m_object, id.c_str()); + if (!type) + return nullptr; + return new Type(type); +} + + +QualifiedName BinaryView::GetTypeNameById(const string& id) +{ + BNQualifiedName name = BNGetAnalysisTypeNameById(m_object, id.c_str()); + QualifiedName result = QualifiedName::FromAPIObject(&name); + BNFreeQualifiedName(&name); + return result; +} + + +string BinaryView::GetTypeId(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + char* id = BNGetAnalysisTypeId(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + string result = id; + BNFreeString(id); + return result; +} + + +bool BinaryView::IsTypeAutoDefined(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + bool result = BNIsAnalysisTypeAutoDefined(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + return result; +} + + +QualifiedName BinaryView::DefineType(const string& id, const QualifiedName& defaultName, Ref<Type> type) { - return BNIsAnalysisTypeAutoDefined(m_object, name.c_str()); + BNQualifiedName nameObj = defaultName.GetAPIObject(); + BNQualifiedName regName = BNDefineAnalysisType(m_object, id.c_str(), &nameObj, type->GetObject()); + QualifiedName::FreeAPIObject(&nameObj); + QualifiedName result = QualifiedName::FromAPIObject(®Name); + BNFreeQualifiedName(®Name); + return result; } -void BinaryView::DefineType(const std::string& name, Ref<Type> type) +void BinaryView::DefineUserType(const QualifiedName& name, Ref<Type> type) { - BNDefineAnalysisType(m_object, name.c_str(), type->GetObject()); + BNQualifiedName nameObj = name.GetAPIObject(); + BNDefineUserAnalysisType(m_object, &nameObj, type->GetObject()); + QualifiedName::FreeAPIObject(&nameObj); } -void BinaryView::DefineUserType(const std::string& name, Ref<Type> type) +void BinaryView::UndefineType(const string& id) { - BNDefineUserAnalysisType(m_object, name.c_str(), type->GetObject()); + BNUndefineAnalysisType(m_object, id.c_str()); } -void BinaryView::UndefineType(const std::string& name) +void BinaryView::UndefineUserType(const QualifiedName& name) { - BNUndefineAnalysisType(m_object, name.c_str()); + BNQualifiedName nameObj = name.GetAPIObject(); + BNUndefineUserAnalysisType(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); } -void BinaryView::UndefineUserType(const std::string& name) +void BinaryView::RenameType(const QualifiedName& oldName, const QualifiedName& newName) { - BNUndefineUserAnalysisType(m_object, name.c_str()); + BNQualifiedName oldNameObj = oldName.GetAPIObject(); + BNQualifiedName newNameObj = newName.GetAPIObject(); + BNRenameAnalysisType(m_object, &oldNameObj, &newNameObj); + QualifiedName::FreeAPIObject(&oldNameObj); + QualifiedName::FreeAPIObject(&newNameObj); +} + + +void BinaryView::RegisterPlatformTypes(Platform* platform) +{ + BNRegisterPlatformTypes(m_object, platform->GetObject()); } @@ -1604,6 +1691,12 @@ bool BinaryView::GetSegmentAt(uint64_t addr, Segment& result) } +bool BinaryView::GetAddressForDataOffset(uint64_t offset, uint64_t& addr) +{ + return BNGetAddressForDataOffset(m_object, offset, &addr); +} + + void BinaryView::AddAutoSection(const string& name, uint64_t start, uint64_t length, const string& type, uint64_t align, uint64_t entrySize, const string& linkedSection, const string& infoSection, uint64_t infoData) { diff --git a/demangle.cpp b/demangle.cpp index a12303ca..70a2fe08 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -6,7 +6,7 @@ using namespace std; bool DemangleMS(Architecture* arch, const std::string& mangledName, Type** outType, - std::vector<std::string>& outVarName) + QualifiedName& outVarName) { BNType* localType = (*outType)->GetObject(); char** localVarName = nullptr; @@ -26,7 +26,7 @@ bool DemangleMS(Architecture* arch, bool DemangleGNU3(Architecture* arch, const std::string& mangledName, Type** outType, - std::vector<std::string>& outVarName) + QualifiedName& outVarName) { BNType* localType = (*outType)->GetObject(); char** localVarName = nullptr; diff --git a/docs/about/license.md b/docs/about/license.md index fe1e65c6..0fdad2df 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -2,7 +2,7 @@ Binary Ninja comes in different versions. Depending on the terms under which you purchased it, a different license below may apply. -## Personal License +## Non-commercial / Student License (NAMED) BINARY NINJA SOFTWARE LICENSE AGREEMENT @@ -42,7 +42,7 @@ We will license Binary Ninja™, a software application (the “Software”), to 14. Choice of Law & Jurisdiction. This License will be governed solely by the internal laws of the State of Florida, without reference to such State’s principles of conflicts of law. The parties consent to the personal and exclusive jurisdiction of the federal and state courts in or for Brevard County, Florida. -## Commercial License +## Commercial License (NAMED) BINARY NINJA SOFTWARE LICENSE AGREEMENT @@ -80,6 +80,84 @@ We will license Binary Ninja™, a software application (the "Software"), to you 14. Choice of Law & Jurisdiction. This License will be governed solely by the internal laws of the State of Florida, without reference to such State’s principles of conflicts of law. The parties consent to the personal and exclusive jurisdiction of the federal and state courts in or for Brevard County, Florida. +## Non-commercial / Student License (COMPUTER) + +BINARY NINJA SOFTWARE LICENSE AGREEMENT + +(Non-commercial Computer License) + +IMPORTANT! BE SURE TO CAREFULLY READ AND UNDERSTAND ALL OF THE TERMS SET FORTH IN THIS LICENSE AGREEMENT (”LICENSE”). BY CLICKING THE "I ACCEPT" BUTTON OR OTHERWISE ACCEPTING THIS LICENSE THROUGH AN ORDERING DOCUMENT THAT INCORPORATES THIS LICENSE, YOU AGREE TO FOLLOW AND BE BOUND BY THE TERMS AND CONDITIONS OF THIS LICENSE. IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS IN THIS LICENSE, YOU MUST SELECT THE "I DECLINE" BUTTON AND MAY NOT USE THE SOFTWARE. + +This License is entered into by and between you (“you” or “your”) and Vector 35 LLC, a Florida limited liability company (“us,” “we” or “our”). + +We will license Binary Ninja™, a software application (the “Software”), to you under the mutual terms and conditions in this License. By installing the Software, you agree to be bound by the terms of this License. If you do not agree to the terms of this License, please do not install or attempt to use the Software. + +1. Non-Exclusive License Grant for Non-commercial Use. Under the terms of this License, the Software is licensed on a non-exclusive basis and is not sold. This License is for non- commercial purposes only. This means that you may not exercise any of the rights granted to you under this License in any manner that is intended for or directed toward commercial advantage or private monetary gain such as (a) activities undertaken for profit, or (b) activities intended to produce works, services, or data for commercial use, or (c) activities conducted, or funded, by a person or an entity engaged in the commercial use, application or exploitation of works similar to the Software. If you have any question regarding a particular use, please feel free to contact us regarding your particular circumstances. Please note, the Software under this non-commercial license is a different version than the standard version of Binary Ninja™. There is a fee to upgrade to the standard version of Binary Ninja™. This License grants you the rights to a computer license that allows a copy of the Software to be installed and used by you on a particular single computer you own (the “Designated Computer”) which Designated Computer may be used by any user as long as it is used for non-commercial purposes. In other words, you may install the Software only on one computer owned by you but you may allow multiple users to use the Software on such Designated Computer as long as the Designated Computer is the only physical computer running the Software at any time and it is used for non-commercial purposes. This License does not permit any concurrent use. If you will use the Software on any computers other than the Designated Computer that the application will be installed on, then you are required to obtain additional licenses for each such computer upon which the Software will be installed. If your needs require concurrent use, please contact us for alternative licensing arrangements. We reserve all rights not expressly granted in this License. + +2. License Fee. Prices are subject to change without prior notice and the price of a License today does not guarantee a similar price in the future. + +3. Termination. Your license to the Software automatically terminates if you fail to comply with the terms of this License. Upon termination of this License, all licenses granted in Section 1 will terminate and you are required to stop using the Software and delete all copies in your possession or control. The following provisions will survive termination of this License: (i) your obligation to pay for services rendered before termination; (ii) Sections 6 through 14; and (iii) any other provision of this License that must survive termination to fulfill its essential purpose. + +4. Modification and Upgrades. We may, from time to time, and in certain cases for a fee, replace, modify or upgrade the Software. The license fee includes one year of free upgrades. When accepted by you, any such replacement or modified Software code or upgrade to the Software will be considered part of the Software and subject to the terms of this License (unless this License is superseded by a further License accompanying such replacement or modified version of or upgrade to the Software). + +5. Restrictions. Subject to applicable copyright, trade secret and other laws, you are permitted under this License to reverse engineer or de-compile the Software but you may not alter, duplicate, modify, rent, lease, loan, sublicense, create derivative works from or provide others with the Software in whole or part, or transmit or communicate any of the Software over a network in order to share it with others. These restrictions include prohibitions on the use the Software for service bureau or time-sharing purposes or in any other way allow third parties to exploit the Software. Time-sharing means sharing the Software with customers or other third parties and permitting their use of the Software. Service bureau involves your use of the Software on behalf of third parties, instead of your own use. It is your responsibility to determine if your use of the Software is in compliance with applicable laws. + +6. Export Restrictions. You must use the Software in accordance with export laws and this means that you may not export, ship, transmit or re-export the Software, in whole or in part, in violation of any applicable law or regulation including but not limited to applicable export administration regulations issued by the U.S. Department of Commerce. + +7. Disclaimer of Warranties. The Software is provided "as is" which means that we are providing no warranty of any kind. WE MAKE NO WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. We do not warrant that the Software will perform without error or that it will run without interruption. + +8. Limitation of Liability. IN NO EVENT WILL OUR LIABILITY ARISING OUT OF OR RELATED TO THIS LICENSE EXCEED THE AGGREGATE OF FEES PAYABLE TO US UNDER THIS LICENSE (INCLUDING FEES BOTH PAID AND DUE) AT THE TIME OF THE EVENT GIVING RISE TO THE LIABILITY. IN NO EVENT WILL WE BE LIABLE FOR ANY CONSEQUENTIAL, INDIRECT, SPECIAL, INCIDENTAL, OR PUNITIVE DAMAGES. THE LIABILITIES LIMITED BY THIS SECTION 8 APPLY: (A) TO LIABILITY FOR NEGLIGENCE; (B) REGARDLESS OF THE FORM OF ACTION, WHETHER IN CONTRACT, TORT, STRICT PRODUCT LIABILITY, OR OTHERWISE; (C) EVEN IF WE ARE ADVISED IN ADVANCE OF THE POSSIBILITY OF THE DAMAGES IN QUESTION AND EVEN IF SUCH DAMAGES WERE FORESEEABLE; AND (D) EVEN IF YOUR REMEDIES FAIL OF THEIR ESSENTIAL PURPOSE. If applicable law limits the application of the provisions of this Section 8, our liability will be limited to the maximum extent permissible. + +9. Severability. To the extent permitted by law, we waive and you waive any provision of law that would render any clause of this License invalid or otherwise unenforceable in any respect. In the event that a provision of this License is held to be invalid or otherwise unenforceable, such provision will be interpreted to fulfill its intended purpose to the maximum extent permitted by applicable law, and the remaining provisions of this License will continue in full force and effect. + +10. Independent Contractors. We are not your agent and you are not our agent and so neither party may bind the other in any way. The parties are independent contractors and will represent themselves in all regards as independent contractors. + +11. No Waiver. Neither party will be deemed to have waived any of its rights under this License by lapse of time or by any statement or representation other than in an explicit written waiver. No waiver of a breach of this License will constitute a waiver of any prior or subsequent breach of this License. + +12. Force Majeure. To the extent caused by force majeure, no delay, failure, or default will constitute a breach of this License. + +13. Assignment & Successors. Neither party may assign this License or any of its rights or obligations hereunder without the other’s express written consent, except that either party may assign this License to the surviving party in a merger of that party into another entity. Except to the extent forbidden in the previous sentence, this License will be binding upon and inure to the benefit of the respective successors and assigns of the parties. + +14. Choice of Law & Jurisdiction. This License will be governed solely by the internal laws of the State of Florida, without reference to such State’s principles of conflicts of law. The parties consent to the personal and exclusive jurisdiction of the federal and state courts in or for Brevard County, Florida. + +## Commercial License (COMPUTER) + +BINARY NINJA SOFTWARE LICENSE AGREEMENT + +IMPORTANT! BE SURE TO CAREFULLY READ AND UNDERSTAND ALL OF THE TERMS SET FORTH IN THIS LICENSE AGREEMENT ("LICENSE"). BY CLICKING THE "I ACCEPT" BUTTON OR OTHERWISE ACCEPTING THIS LICENSE THROUGH AN ORDERING DOCUMENT THAT INCORPORATES THIS LICENSE, YOU AGREE TO FOLLOW AND BE BOUND BY THE TERMS AND CONDITIONS OF THIS LICENSE. IF YOU ARE ENTERING INTO THIS LICENSE ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY TO THE TERMS AND CONDITIONS OF THIS LICENSE AND, IN SUCH EVENT, "YOU" AND "YOUR" AS USED IN THIS LICENSE SHALL REFER TO SUCH ENTITY, IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS IN THIS LICENSE, YOU MUST SELECT THE "I DECLINE" BUTTON AND MAY NOT USE THE SOFTWARE. + +This License is entered into by and between you ("you" or "your") and Vector 35 LLC, a Florida limited liability company ("us", "we" or "our"). + +We will license Binary Ninja™, a software application (the "Software"), to you under the mutual terms and conditions in this License. By installing the Software, you agree to be bound by the terms of this License. If you do not agree to the terms of this License, please do not install or attempt to use the Software. + +1. Non-Exclusive License Grant. Under the terms of this License, the Software is licensed on a non-exclusive basis and is not sold. You receive no title to or ownership of the Software itself. This License grants you the rights to a computer license that allows a copy of the Software to be installed and used by you on a particular single computer you own (the “Designated Computer”) which Designated Computer may be used by any user. In other words, you may install the Software only on one computer owned by you but you may allow multiple users to use the Software on such Designated Computer as long as the Designated Computer is the only physical computer running the Software at any time. This License does not permit any concurrent use. If you will use the Software on any computers other than the Designated Computer that the application will be installed on, then you are required to obtain additional licenses for each such computer upon which the Software will be installed. If your needs require concurrent use, please contact us for alternative licensing arrangements. All rights not expressly granted herein reserved by us. + +2. License Fee. Prices are subject to change without prior notice and the price of a License today does not guarantee a similar price in the future. + +3. Termination. Your license to the Software automatically terminates if you fail to comply with the terms of this License. Upon termination of this License, all licenses granted in Section 1 will terminate and you are required to stop using the Software and delete all copies in your possession or control. The following provisions will survive termination of this License: (i) your obligation to pay for services rendered before termination; (ii) Sections 6 through 14; and (iii) any other provision of this License that must survive termination to fulfill its essential purpose. + +4. Modification and Upgrades. We may, from time to time, and in certain cases for a fee, replace, modify or upgrade the Software. The license fee includes one year of free upgrades. When accepted by you, any such replacement or modified Software code or upgrade to the Software will be considered part of the Software and subject to the terms of this License (unless this License is superseded by a further License accompanying such replacement or modified version of or upgrade to the Software). + +5. Restrictions. Subject to applicable copyright, trade secret and other laws, you are permitted under this License to reverse engineer or de-compile the Software but you may not alter, duplicate, modify, rent, lease, loan, sublicense, create derivative works from or provide others with the Software in whole or part, or transmit or communicate any of the Software over a network in order to share it with others. These restrictions include prohibitions on the use the Software for service bureau or time-sharing purposes or in any other way allow third parties to exploit the Software. Time-sharing means sharing the Software with customers or other third parties and permitting their use of the Software. Service bureau involves your use of the Software on behalf of third parties, instead of your own use. It is your responsibility to determine if your use of the Software is in compliance with applicable laws. + +6. Export Restrictions. You must use the Software in accordance with export laws and this means that you may not export, ship, transmit or re-export the Software, in whole or in part, in violation of any applicable law or regulation including but not limited to applicable export administration regulations issued by the U.S. Department of Commerce. + +7. Disclaimer of Warranties. The Software is provided "as is" which means that we are providing no warranty of any kind. WE MAKE NO WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. We do not warrant that the Software will perform without error or that it will run without interruption. + +8. Limitation of Liability. IN NO EVENT WILL OUR LIABILITY ARISING OUT OF OR RELATED TO THIS LICENSE EXCEED THE AGGREGATE OF FEES PAYABLE TO US UNDER THIS LICENSE (INCLUDING FEES BOTH PAID AND DUE) AT THE TIME OF THE EVENT GIVING RISE TO THE LIABILITY. IN NO EVENT WILL WE BE LIABLE FOR ANY CONSEQUENTIAL, INDIRECT, SPECIAL, INCIDENTAL, OR PUNITIVE DAMAGES. THE LIABILITIES LIMITED BY THIS SECTION 8 APPLY: (A) TO LIABILITY FOR NEGLIGENCE; (B) REGARDLESS OF THE FORM OF ACTION, WHETHER IN CONTRACT, TORT, STRICT PRODUCT LIABILITY, OR OTHERWISE; (C) EVEN IF WE ARE ADVISED IN ADVANCE OF THE POSSIBILITY OF THE DAMAGES IN QUESTION AND EVEN IF SUCH DAMAGES WERE FORESEEABLE; AND (D) EVEN IF YOUR REMEDIES FAIL OF THEIR ESSENTIAL PURPOSE. If applicable law limits the application of the provisions of this Section 8, our liability will be limited to the maximum extent permissible. + +9. Severability. To the extent permitted by law, we waive and you waive any provision of law that would render any clause of this License invalid or otherwise unenforceable in any respect. In the event that a provision of this License is held to be invalid or otherwise unenforceable, such provision will be interpreted to fulfill its intended purpose to the maximum extent permitted by applicable law, and the remaining provisions of this License will continue in full force and effect. + +10. Independent Contractors. We are not your agent and you are not our agent and so neither party may bind the other in any way. The parties are independent contractors and will represent themselves in all regards as independent contractors. + +11. No Waiver. Neither party will be deemed to have waived any of its rights under this License by lapse of time or by any statement or representation other than in an explicit written waiver. No waiver of a breach of this License will constitute a waiver of any prior or subsequent breach of this License. + +12. Force Majeure. To the extent caused by force majeure, no delay, failure, or default will constitute a breach of this License. + +13. Assignment & Successors. Neither party may assign this License or any of its rights or obligations hereunder without the other’s express written consent, except that either party may assign this License to the surviving party in a merger of that party into another entity. Except to the extent forbidden in the previous sentence, this License will be binding upon and inure to the benefit of the respective successors and assigns of the parties. + +14. Choice of Law & Jurisdiction. This License will be governed solely by the internal laws of the State of Florida, without reference to such State’s principles of conflicts of law. The parties consent to the personal and exclusive jurisdiction of the federal and state courts in or for Brevard County, Florida. + ## Demo License BINARY NINJA™ TRIAL PERIOD SOFTWARE DEMONSTRATION LICENSE AGREEMENT diff --git a/docs/about/open-source.md b/docs/about/open-source.md index dcebbfa5..60b90635 100644 --- a/docs/about/open-source.md +++ b/docs/about/open-source.md @@ -27,11 +27,12 @@ The previous tools are used in the generation of our documentation, but are not - [discount] ([discount license] - BSD) - [sqlite] ([sqlite license] - public domain) - [llvm] ([llvm license] - BSD-style) + - [libgit2] ([libgit2 license] - GPLv2 with linking exception) * Other - [yasm] ([yasm license] - 2-clause BSD) -* Upvector update Library +* Upvector update library - [tomcrypt] ([tomcrypt license] - public domain) @@ -72,6 +73,8 @@ Please note that we offer no support for running Binary Ninja with modified Qt l [sqlite license]: https://www.sqlite.org/copyright.html [llvm]: http://llvm.org/releases/3.8.1/ [llvm license]: http://llvm.org/releases/3.8.1/LICENSE.TXT +[libgit2]: https://libgit2.github.com/ +[libgit2 license]: https://github.com/libgit2/libgit2/blob/master/COPYING [yasm]: http://yasm.tortall.net/ [yasm license]: https://github.com/yasm/yasm/blob/master/BSD.txt [zlib]: http://www.zlib.net/ diff --git a/docs/getting-started.md b/docs/getting-started.md index 72c4796c..b7d01115 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -62,6 +62,7 @@ Switching views happens multiple ways. In some instances, it's automatic (clicki - `;` : Adds a comment - `i` : Switches between disassembly and low-level il in graph view - `y` : Change type + - `a` : Change the data type to an ASCII string - [1248] : Change type directly to a data variable of the indicated widths - `a` : Change the data type to an ASCII string - `d` : Switches between data variables of various widths diff --git a/docs/guide/interface.md b/docs/guide/interface.md index 460b8df3..74bf141c 100644 --- a/docs/guide/interface.md +++ b/docs/guide/interface.md @@ -2,6 +2,8 @@ ## UI +### Zoom +You can change the zoom of level of the graph view by holding CTRL and scrolling with the mouse. ## Views diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index a74ecb68..560b0f1f 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -7,6 +7,12 @@ - Did you read all the items on this page? - Then you should contact [support]! +## Bug Reproduction +Running Binary Ninja with debug logging will make your bug report more useful. +``` +./binaryninja --debug --stderr-log +``` + ## License Problems - If experiencing problems with Windows UAC permissions during an update, the easiest fix is to completely un-install and [recover][recover] the latest installer and license. Preferences are saved outside the installation folder and are preserved, though you might want to remove your [license](/getting-started/index.html#license). @@ -16,10 +22,18 @@ Given the diversity of Linux distributions, some work-arounds are required to run Binary Ninja on platforms that are not [officially supported][faq]. +### Headless Ubuntu + +If you're having trouble getting Binary Ninja installed in a headless server install where you want to be able to X-Forward the GUI on a remote machine, the following should meet requiremetns (for at least 14.04 LTS): + +``` +apt-get install libgl1-mesa-glx libfontconfig1 libxrender1 libegl1-mesa libxi6 libnspr4 libsm6 +``` + ### Arch Linux - - Install python2 from the [official repositories][archrepo] - - Install the [libcurl-compat] library from AUR, and run Binary Ninja via `LD_PRELOAD=libcurl.so.3 ~/binaryninja/binaryninja` + - Install python2 from the [official repositories][archrepo] (`sudo pacman -S python2`) and create a sym link: `sudo ln -s /usr/lib/libpython2.7.so.1.0 /usr/lib/libpython2.7.so.1` + - Install the [libcurl-compat] library with `sudo pacman -S libcurl-compat`, and run Binary Ninja via `LD_PRELOAD=libcurl.so.3 ~/binaryninja/binaryninja` ### KDE @@ -36,7 +50,7 @@ QT_PLUGIN_PATH=./qt ./binaryninja - If the GUI launches but the license file is not valid when launched from the command-line, check that you're using the right version of Python. Only a 64-bit Python 2.7 is supported at this time. Additionally, the [personal][purchase] edition does not support headless operation. [known issues]: https://github.com/Vector35/binaryninja-api/issues?q=is%3Aissue -[libcurl-compat]: https://aur.archlinux.org/packages/libcurl-compat/ +[libcurl-compat]: https://www.archlinux.org/packages/community/x86_64/libcurl-compat/ [archrepo]: https://wiki.archlinux.org/index.php/Official_repositories [recover]: https://binary.ninja/recover.html [support]: https://binary.ninja/support.html diff --git a/examples/bin-info/Makefile b/examples/bin-info/Makefile new file mode 100644 index 00000000..c8b2efc2 --- /dev/null +++ b/examples/bin-info/Makefile @@ -0,0 +1,54 @@ +# Path to prebuilt libbinaryninjaapi.a +BINJA_API_A := ../../bin/libbinaryninjaapi.a + +# Path to binaryninjaapi.h and json +INC := -I../../ + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + # Path to binaryninja install + BINJAPATH := $(HOME)/binaryninja/ + CC := g++ +else + BINJAPATH := /Applications/Binary\ Ninja.app/Contents/MacOS + CC := $(shell xcrun -f clang++) +endif + +SRCDIR := src +BUILDDIR := build +TARGETDIR := bin + +TARGETNAME := bininfo +TARGET := $(TARGETDIR)/$(TARGETNAME) + +SRCEXT := cpp +SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) +OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) + +LIBS := -L $(BINJAPATH) -lbinaryninjacore +CFLAGS := -c -std=gnu++11 -O2 -Wall -W -fPIC -pipe +ifeq ($(UNAME_S),Darwin) + CFLAGS += -arch x86_64 -pipe -stdlib=libc++ +endif + +all: $(TARGET) + +ifeq ($(UNAME_S),Linux) +$(TARGET): $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) $^ $(BINJA_API_A) $(LIBS) -Wl,-rpath=$(BINJAPATH) -ldl -o $@ +else +$(TARGET): $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) $^ $(BINJA_API_A) $(LIBS) -o $@ + install_name_tool -change @rpath/libbinaryninjacore.dylib $(BINJAPATH)/libbinaryninjacore.dylib $@ +endif + +$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) + @mkdir -p $(BUILDDIR) + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + $(RM) -r $(BUILDDIR) $(TARGETDIR) + +.PHONY: clean diff --git a/examples/bin-info/Makefile.win b/examples/bin-info/Makefile.win new file mode 100644 index 00000000..b65bfb86 --- /dev/null +++ b/examples/bin-info/Makefile.win @@ -0,0 +1,9 @@ +BINJA_API_INC_PATH = ..\..\ +BINJA_API_LIB = ..\..\bin\libbinaryninjaapi.lib +BINJA_CORE_LIB = "c:\Program Files\Vector35\BinaryNinja\binaryninjacore.lib" + +FLAGS = /DWIN32 /D__WIN32__ /EHsc /I$(BINJA_API_INC_PATH) /link $(BINJA_API_LIB) $(BINJA_CORE_LIB) + +bininfo: ./src/bin-info.cpp + if not exist bin mkdir bin + cl ./src/bin-info.cpp $(FLAGS) /Fe:.\bin\bininfo diff --git a/examples/bin-info/src/bin-info.cpp b/examples/bin-info/src/bin-info.cpp new file mode 100644 index 00000000..2a9a5e98 --- /dev/null +++ b/examples/bin-info/src/bin-info.cpp @@ -0,0 +1,118 @@ +/* + * Command line executable file that outputs + * some information about the exectuable passed to. + */ + +#include <sys/stat.h> + +#include <iostream> +#include <cstdlib> + +#include "binaryninjacore.h" +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + +#ifndef __WIN32__ +#include <libgen.h> +#include <dlfcn.h> +string get_plugins_directory() +{ + Dl_info info; + if (!dladdr((void *)BNGetBundledPluginDirectory, &info)) + return NULL; + + stringstream ss; + ss << dirname((char *)info.dli_fname) << "/plugins/"; + return ss.str(); +} +#else +string get_plugins_directory() +{ + return "C:\\Program Files\\Vector35\\Binary Ninja\\plugins\\"; +} +#endif + +bool is_file(char *fname) +{ + struct stat buf; + if (stat(fname, &buf) == 0 && (buf.st_mode & S_IFREG) == S_IFREG) + return true; + + return false; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + cerr << "USAGE: " << argv[0] << " <file_name>" << endl; + exit(-1); + } + + char *fname = argv[1]; + if (!is_file(fname)) { + cerr << "Error: " << fname << " is not a regular file" << endl; + exit(-1); + } + + /* In order to initiate the bundled plugins properly, the location + * of where bundled plugins directory is must be set. Since + * libbinaryninjacore is in the path get the path to it and use it to + * determine the plugins directory */ + SetBundledPluginDirectory(get_plugins_directory()); + InitCorePlugins(); + InitUserPlugins(); + + auto bd = BinaryData(new FileMetadata(), fname); + BinaryView *bv; + + for (auto type : BinaryViewType::GetViewTypes()) { + if (type->IsTypeValidForData(&bd) && type->GetName() != "Raw") { + bv = type->Create(&bd); + break; + } + } + + if (bv->GetTypeName() == "Raw"){ + cerr << "Error: Unable to get any other view type besides Raw"; + exit(-1); + } + + bv->UpdateAnalysis(); + while (bv->GetAnalysisProgress().state != IdleState); + + cout << "Target: " << fname << endl << endl; + cout << "TYPE: " << bv->GetTypeName() << endl; + cout << "START: 0x" << hex << bv->GetStart() << endl; + cout << "ENTRY: 0x" << hex << bv->GetEntryPoint() << endl; + cout << "PLATFORM: " << bv->GetDefaultPlatform()->GetName() << endl; + cout << endl; + + cout << "---------- 10 Functions ----------" << endl; + int x = 0; + for (auto func : bv->GetAnalysisFunctionList()) { + cout << hex << func->GetStart() << " " << func->GetSymbol()->GetFullName() << endl; + if (++x >= 10) + break; + } + cout << endl; + + cout << "---------- 10 Strings ----------" << endl; + x = 0; + for (auto str_ref : bv->GetStrings()) { + char *str = (char *)malloc(str_ref.length+1); + bv->Read(str, str_ref.start, str_ref.length); + str[str_ref.length] = 0; + + cout << hex << str_ref.start << " (" + << dec << str_ref.length << ") " + << str << endl; + free(str); + + if (++x >= 10) + break; + } + + return 0; +} diff --git a/examples/breakpoint/Makefile b/examples/breakpoint/Makefile new file mode 100644 index 00000000..d85b63e5 --- /dev/null +++ b/examples/breakpoint/Makefile @@ -0,0 +1,57 @@ +# Path to prebuilt libbinaryninjaapi.a +BINJA_API_A := ../../bin/libbinaryninjaapi.a + +# Path to binaryninjaapi.h and json +INC := -I../../ + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + # Path to binaryninja install + BINJAPATH := $(HOME)/binaryninja +else + BINJAPATH := /Applications/Binary\ Ninja.app/Contents/MacOS +endif + +SRCDIR := src +BUILDDIR := build +TARGETDIR := bin + +TARGETNAME := breakpoint +TARGET := $(TARGETDIR)/$(TARGETNAME) +INSTALLBINDIR := $(BINJAPATH)/plugins/ + +SRCEXT := cpp +SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) +OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) + +LIBS := -L $(BINJAPATH) -lbinaryninjacore +CFLAGS := -c -std=gnu++11 -O2 -Wall -W -fPIC -pipe +ifeq ($(UNAME_S),Linux) + CC := g++ +all: $(TARGET).so +else + CC := $(shell xcrun -f clang++) + CFLAGS += -arch x86_64 -pipe -stdlib=libc++ -mmacosx-version-min=10.7 +all: $(TARGET).dylib +endif + +$(TARGET).so: $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) -shared $^ $(BINJA_API_A) $(LIBS) -o $@ + +$(TARGET).dylib: $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) -dynamiclib $^ $(BINJA_API_A) $(LIBS) -o $@ + install_name_tool -id @loader_path/$(TARGETNAME).dylib -add_rpath @loader_path/.. $@ + +$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) + @mkdir -p $(BUILDDIR) + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + $(RM) -r $(BUILDDIR) $(TARGETDIR) + +install: + cp $(TARGET) $(INSTALLBINDIR) + +.PHONY: clean diff --git a/examples/breakpoint/Makefile.win b/examples/breakpoint/Makefile.win new file mode 100644 index 00000000..00cbbfa1 --- /dev/null +++ b/examples/breakpoint/Makefile.win @@ -0,0 +1,16 @@ +BINJA_API_INC_PATH = ..\..\ +BINJA_API_LIB = ..\..\bin\libbinaryninjaapi.lib +BINJA_CORE_LIB = "c:\Program Files\Vector35\BinaryNinja\binaryninjacore.lib" + +FLAGS = /DWIN32 /D__WIN32__ /EHsc /I$(BINJA_API_INC_PATH) /link $(BINJA_API_LIB) $(BINJA_CORE_LIB) + +bininfo.dll: ./src/breakpoint.cpp + if not exist bin mkdir bin + cl ./src/breakpoint.cpp /LD $(FLAGS) /OUT:.\bin\breakpoint.dll + +clean: + if exist *.obj del *.obj + if exist *.exp del *.exp + if exist *.lib del *.lib + if exist *.dll del *.dll + if exist .\bin del /S /Q bin diff --git a/examples/breakpoint/src/breakpoint.cpp b/examples/breakpoint/src/breakpoint.cpp index 99d9b169..4fac98ac 100644 --- a/examples/breakpoint/src/breakpoint.cpp +++ b/examples/breakpoint/src/breakpoint.cpp @@ -7,10 +7,10 @@ using namespace std; void write_breakpoint(BinaryNinja::BinaryView *view, uint64_t start, uint64_t length) { // Sample function to show registering a plugin menu item for a range of bytes. - // Also possible: - // register - // register_for_address - // register_for_function + // Also possible: + // register + // register_for_address + // register_for_function Ref<Architecture> arch = view->GetDefaultArchitecture(); string arch_name = arch->GetName(); @@ -18,9 +18,9 @@ void write_breakpoint(BinaryNinja::BinaryView *view, uint64_t start, uint64_t le if (arch_name.compare(0, 3, "x86") == 0) { string int3s = string(length, '\xcc'); view->Write(start, int3s.c_str(), length); - } else { + } else { LogError("No support for breakpoint on %s", arch_name.c_str()); - } + } } extern "C" @@ -33,4 +33,4 @@ extern "C" &write_breakpoint); return true; } -} +}
\ No newline at end of file diff --git a/examples/print_syscalls/Makefile b/examples/print_syscalls/Makefile new file mode 100644 index 00000000..54fd1237 --- /dev/null +++ b/examples/print_syscalls/Makefile @@ -0,0 +1,54 @@ +# Path to prebuilt libbinaryninjaapi.a +BINJA_API_A := ../../bin/libbinaryninjaapi.a + +# Path to binaryninjaapi.h and json +INC := -I../../ + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + # Path to binaryninja install + BINJAPATH := $(HOME)/binaryninja/ + CC := g++ +else + BINJAPATH := /Applications/Binary\ Ninja.app/Contents/MacOS + CC := $(shell xcrun -f clang++) +endif + +SRCDIR := src +BUILDDIR := build +TARGETDIR := bin + +TARGETNAME := print_syscalls +TARGET := $(TARGETDIR)/$(TARGETNAME) + +SRCEXT := cpp +SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) +OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) + +LIBS := -L $(BINJAPATH) -lbinaryninjacore +CFLAGS := -c -std=gnu++11 -O2 -Wall -W -fPIC -pipe -ggdb +ifeq ($(UNAME_S),Darwin) + CFLAGS += -arch x86_64 -pipe -stdlib=libc++ +endif + +all: $(TARGET) + +ifeq ($(UNAME_S),Linux) +$(TARGET): $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) $^ $(BINJA_API_A) $(LIBS) -Wl,-rpath=$(BINJAPATH) -ldl -o $@ +else +$(TARGET): $(OBJECTS) + @mkdir -p $(TARGETDIR) + $(CC) $^ $(BINJA_API_A) $(LIBS) -o $@ + install_name_tool -change @rpath/libbinaryninjacore.dylib $(BINJAPATH)/libbinaryninjacore.dylib $@ +endif + +$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) + @mkdir -p $(BUILDDIR) + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + $(RM) -r $(BUILDDIR) $(TARGETDIR) + +.PHONY: clean diff --git a/examples/print_syscalls/Makefile.win b/examples/print_syscalls/Makefile.win new file mode 100644 index 00000000..afc0cbd8 --- /dev/null +++ b/examples/print_syscalls/Makefile.win @@ -0,0 +1,9 @@ +BINJA_API_INC_PATH = ..\..\ +BINJA_API_LIB = ..\..\bin\libbinaryninjaapi.lib +BINJA_CORE_LIB = "c:\Program Files\Vector35\BinaryNinja\binaryninjacore.lib" + +FLAGS = /DWIN32 /D__WIN32__ /EHsc /I$(BINJA_API_INC_PATH) /link $(BINJA_API_LIB) $(BINJA_CORE_LIB) + +print_syscalls: ./src/arm-syscall.cpp + if not exist bin mkdir bin + cl ./src/arm-syscall.cpp $(FLAGS) /Fe:.\bin\print_syscalls diff --git a/examples/print_syscalls/src/arm-syscall.cpp b/examples/print_syscalls/src/arm-syscall.cpp new file mode 100644 index 00000000..3424619b --- /dev/null +++ b/examples/print_syscalls/src/arm-syscall.cpp @@ -0,0 +1,115 @@ +/* + * Outputs the syscall numbers called by a binary. + */ + +#include <sys/stat.h> + +#include <iostream> +#include <cstdlib> + +#include "binaryninjacore.h" +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + +#ifndef __WIN32__ +#include <libgen.h> +#include <dlfcn.h> +string get_plugins_directory() +{ + Dl_info info; + if (!dladdr((void *)BNGetBundledPluginDirectory, &info)) + return NULL; + + stringstream ss; + ss << dirname((char *)info.dli_fname) << "/plugins/"; + return ss.str(); +} +#else +string get_plugins_directory() +{ + return "C:\\Program Files\\Vector35\\Binary Ninja\\plugins\\"; +} +#endif + +bool is_file(char *fname) +{ + struct stat buf; + if (stat(fname, &buf) == 0 && (buf.st_mode & S_IFREG) == S_IFREG) + return true; + + return false; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + cerr << "USAGE: " << argv[0] << " <file_name>" << endl; + exit(-1); + } + + char *fname = argv[1]; + if (!is_file(fname)) { + cerr << "Error: " << fname << " is not a regular file" << endl; + exit(-1); + } + + /* In order to initiate the bundled plugins properly, the location + * of where bundled plugins directory is must be set. Since + * libbinaryninjacore is in the path get the path to it and use it to + * determine the plugins directory */ + SetBundledPluginDirectory(get_plugins_directory()); + InitCorePlugins(); + InitUserPlugins(); + + auto bd = BinaryData(new FileMetadata(), fname); + BinaryView *bv = 0; + + for (auto type : BinaryViewType::GetViewTypesForData(&bd)) { + if (type->GetName() != "Raw") { + bv = type->Create(&bd); + break; + } + } + + if (!bv || bv->GetTypeName() == "Raw"){ + cerr << "Error: Unable to get any other view type besides Raw"; + exit(-1); + } + + bv->UpdateAnalysis(); + while (bv->GetAnalysisProgress().state != IdleState); + + auto arch = bv->GetDefaultArchitecture(); + auto platform = bv->GetDefaultPlatform(); + + auto cc = platform->GetSystemCallConvention(); + if (!cc) { + cerr << "Error: No system call conventions found for " + << platform->GetName() << endl; + exit(-1); + } + + auto reg = cc->GetIntegerArgumentRegisters()[0]; + + for (Function *func : bv->GetAnalysisFunctionList()) { + auto il_func = func->GetLowLevelIL(); + + for (size_t i = 0; i < il_func->GetInstructionCount(); i++) { + auto instr = (*il_func)[il_func->GetIndexForInstruction(i)]; + + if (instr.operation == LLIL_SYSCALL) { + auto reg_value = func->GetRegisterValueAtLowLevelILInstruction(i, reg); + + cout << "System call address: 0x" + << hex << instr.address + << " - " + << dec << reg_value.value + << endl; + } + } + } + + return 0; +} diff --git a/function.cpp b/function.cpp index 109b6f49..b1d008ee 100644 --- a/function.cpp +++ b/function.cpp @@ -567,6 +567,10 @@ vector<vector<InstructionTextToken>> Function::GetBlockAnnotations(Architecture* token.type = lines[i].tokens[j].type; token.text = lines[i].tokens[j].text; token.value = lines[i].tokens[j].value; + token.size = lines[i].tokens[j].size; + token.operand = lines[i].tokens[j].operand; + token.context = lines[i].tokens[j].context; + token.address = lines[i].tokens[j].address; line.push_back(token); } result.push_back(line); diff --git a/functiongraphblock.cpp b/functiongraphblock.cpp index 47261453..a37c0b49 100644 --- a/functiongraphblock.cpp +++ b/functiongraphblock.cpp @@ -101,6 +101,8 @@ const vector<DisassemblyTextLine>& FunctionGraphBlock::GetLines() token.value = lines[i].tokens[j].value; token.size = lines[i].tokens[j].size; token.operand = lines[i].tokens[j].operand; + token.context = lines[i].tokens[j].context; + token.address = lines[i].tokens[j].address; line.tokens.push_back(token); } result.push_back(line); @@ -126,8 +128,7 @@ const vector<FunctionGraphEdge>& FunctionGraphBlock::GetOutgoingEdges() { FunctionGraphEdge edge; edge.type = edges[i].type; - edge.target = edges[i].target; - edge.arch = edges[i].arch ? new CoreArchitecture(edges[i].arch) : nullptr; + edge.target = edges[i].target ? new BasicBlock(BNNewBasicBlockReference(edges[i].target)) : nullptr; edge.points.insert(edge.points.begin(), &edges[i].points[0], &edges[i].points[edges[i].pointCount]); result.push_back(edge); } diff --git a/json/json-forwards.h b/json/json-forwards.h index a1bac45c..a20d79d9 100644 --- a/json/json-forwards.h +++ b/json/json-forwards.h @@ -91,7 +91,8 @@ license you like. #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED #include <stddef.h> -#include <string> //typdef String +#include <string> //typedef String +#include <stdint.h> //typedef int64_t, uint64_t /// If defined, indicates that json library is embedded in CppTL library. //# define JSON_IN_CPPTL 1 @@ -165,12 +166,19 @@ license you like. // In c++11 the override keyword allows you to explicity define that a function // is intended to override the base-class version. This makes the code more // managable and fixes a set of common hard-to-find bugs. -#if __cplusplus >= 201103L + +#if __cplusplus >= 201103L +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept +#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 # define JSONCPP_OVERRIDE override -#elif defined(_MSC_VER) && _MSC_VER > 1600 +# define JSONCPP_NOEXCEPT throw() +#elif defined(_MSC_VER) && _MSC_VER >= 1900 # define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept #else # define JSONCPP_OVERRIDE +# define JSONCPP_NOEXCEPT throw() #endif #ifndef JSON_HAS_RVALUE_REFERENCES @@ -237,8 +245,10 @@ typedef unsigned int LargestUInt; typedef __int64 Int64; typedef unsigned __int64 UInt64; #else // if defined(_MSC_VER) // Other platforms, use long long -typedef long long int Int64; -typedef unsigned long long int UInt64; + +typedef int64_t Int64; +typedef uint64_t UInt64; + #endif // if defined(_MSC_VER) typedef Int64 LargestInt; typedef UInt64 LargestUInt; diff --git a/json/json.h b/json/json.h index 167d227a..1561a120 100644 --- a/json/json.h +++ b/json/json.h @@ -87,10 +87,11 @@ license you like. #ifndef JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED -# define JSONCPP_VERSION_STRING "1.7.2" +# define JSONCPP_VERSION_STRING "1.8.0" # define JSONCPP_VERSION_MAJOR 1 -# define JSONCPP_VERSION_MINOR 7 -# define JSONCPP_VERSION_PATCH 2 +# define JSONCPP_VERSION_MINOR 8 +# define JSONCPP_VERSION_PATCH 0 + # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) @@ -124,7 +125,9 @@ license you like. #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED #include <stddef.h> -#include <string> //typdef String + +#include <string> //typedef String +#include <stdint.h> //typedef int64_t, uint64_t /// If defined, indicates that json library is embedded in CppTL library. //# define JSON_IN_CPPTL 1 @@ -198,12 +201,19 @@ license you like. // In c++11 the override keyword allows you to explicity define that a function // is intended to override the base-class version. This makes the code more // managable and fixes a set of common hard-to-find bugs. -#if __cplusplus >= 201103L + +#if __cplusplus >= 201103L # define JSONCPP_OVERRIDE override -#elif defined(_MSC_VER) && _MSC_VER > 1600 +# define JSONCPP_NOEXCEPT noexcept +#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 # define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT throw() +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept #else # define JSONCPP_OVERRIDE +# define JSONCPP_NOEXCEPT throw() #endif #ifndef JSON_HAS_RVALUE_REFERENCES @@ -270,8 +280,10 @@ typedef unsigned int LargestUInt; typedef __int64 Int64; typedef unsigned __int64 UInt64; #else // if defined(_MSC_VER) // Other platforms, use long long -typedef long long int Int64; -typedef unsigned long long int UInt64; + +typedef int64_t Int64; +typedef uint64_t UInt64; + #endif // if defined(_MSC_VER) typedef Int64 LargestInt; typedef UInt64 LargestUInt; @@ -370,6 +382,8 @@ class ValueConstIterator; #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) +#pragma pack(push, 8) + namespace Json { /** \brief Configuration passed to reader and writer. @@ -414,6 +428,8 @@ public: } // namespace Json +#pragma pack(pop) + #endif // CPPTL_JSON_FEATURES_H_INCLUDED // ////////////////////////////////////////////////////////////////////// @@ -473,6 +489,8 @@ public: #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma pack(push, 8) + /** \brief JSON (JavaScript Object Notation). */ namespace Json { @@ -484,8 +502,10 @@ namespace Json { class JSON_API Exception : public std::exception { public: Exception(JSONCPP_STRING const& msg); - ~Exception() throw() JSONCPP_OVERRIDE; - char const* what() const throw() JSONCPP_OVERRIDE; + + ~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; + char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; + protected: JSONCPP_STRING msg_; }; @@ -621,6 +641,9 @@ public: static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null + + static Value const& nullSingleton(); ///< Prefer this to null or nullRef. + /// Minimum signed integer value that can be stored in a Json::Value. static const LargestInt minLargestInt; /// Maximum signed integer value that can be stored in a Json::Value. @@ -1288,6 +1311,7 @@ template<> inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } } +#pragma pack(pop) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) @@ -1333,6 +1357,8 @@ inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma pack(push, 8) + namespace Json { /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a @@ -1707,6 +1733,8 @@ JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); } // namespace Json +#pragma pack(pop) + #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) @@ -1748,6 +1776,9 @@ JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + namespace Json { class Value; @@ -2052,6 +2083,8 @@ JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); } // namespace Json +#pragma pack(pop) + #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) diff --git a/json/jsoncpp.cpp b/json/jsoncpp.cpp new file mode 100644 index 00000000..7634ed4f --- /dev/null +++ b/json/jsoncpp.cpp @@ -0,0 +1,5312 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json/json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include <clocale> +#endif + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { +static char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} + +/// Converts a unicode code-point to UTF-8. +static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { + JSONCPP_STRING result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast<char>(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast<char>(0x80 | (0x3f & cp)); + result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast<char>(0x80 | (0x3f & cp)); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast<char>(0x80 | (0x3f & cp)); + result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [1,31]). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +static inline void fixNumericLocaleInput(char* begin, char* end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint != '\0' && decimalPoint != '.') { + while (begin < end) { + if (*begin == '.') { + *begin = decimalPoint; + } + ++begin; + } + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include <json/assertions.h> +#include <json/reader.h> +#include <json/value.h> +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <utility> +#include <cstdio> +#include <cassert> +#include <cstring> +#include <istream> +#include <sstream> +#include <memory> +#include <set> +#include <limits> + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__QNXNTO__) +#define sscanf std::sscanf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<CharReader> CharReaderPtr; +#else +typedef std::auto_ptr<CharReader> CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + JSONCPP_STRING documentCopy(document.data(), document.data() + document.capacity()); + std::swap(documentCopy, document_); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator<char> begin(sin); + // std::istream_iterator<char> end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since JSONCPP_STRING is reference-counted, this at least does not + // create an extra copy. + JSONCPP_STRING doc; + std::getline(sin, doc, (char)EOF); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). + // parse() executes one nodes_.push(), so > instead of >=. + if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +static JSONCPP_STRING normalizeEOL(Reader::Location begin, Reader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast<size_t>(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = JSONCPP_STRING(numberName.asCString()); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast<Value::UInt>(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + JSONCPP_STRING buffer(token.start_, token.end_); + JSONCPP_ISTRINGSTREAM is(buffer); + if (!(is >> value)) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast<unsigned int>(unicode); + return true; +} + +bool +Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +JSONCPP_STRING Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +JSONCPP_STRING Reader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { + std::vector<Reader::StructuredError> allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + JSONCPP_STRING getFormattedErrorMessages() const; + std::vector<StructuredError> getStructuredErrors() const; + bool pushError(const Value& value, const JSONCPP_STRING& message); + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack<Value*> Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (static_cast<int>(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value v(std::numeric_limits<double>::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value v(std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value v(-std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char *p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast<Value::UInt>(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + ptrdiff_t const length = token.end_ - token.start_; + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + size_t const ulength = static_cast<size_t>(length); + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, ulength); + buffer[length] = 0; + fixNumericLocaleInput(buffer, buffer + length); + count = sscanf(buffer, format, &value); + } else { + JSONCPP_STRING buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast<unsigned int>(unicode); + return true; +} + +bool +OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +JSONCPP_STRING OurReader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { + std::vector<OurReader::StructuredError> allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool OurReader::good() const { + return !errors_.size(); +} + + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); + valid_keys->insert("allowSpecialFloats"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<JSONCPP_STRING> valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, + Value* root, JSONCPP_STRING* errs) +{ + JSONCPP_OSTRINGSTREAM ssin; + ssin << sin.rdbuf(); + JSONCPP_STRING doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { + CharReaderBuilder b; + JSONCPP_STRING errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + errs.c_str()); + + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() + : current_(), isNull_(true) { +} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { + return current_->second; +} + +void ValueIteratorBase::increment() { + ++current_; +} + +void ValueIteratorBase::decrement() { + --current_; +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +JSONCPP_STRING ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return JSONCPP_STRING(); + return JSONCPP_STRING(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include <json/assertions.h> +#include <json/value.h> +#include <json/writer.h> +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <math.h> +#include <sstream> +#include <utility> +#include <cstring> +#include <cassert> +#ifdef JSON_USE_CPPTL +#include <cpptl/conststring.h> +#endif +#include <cstddef> // size_t +#include <algorithm> // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +//const unsigned char& kNullRef = kNull[0]; +//const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); +//const Value& Value::nullRef = null; + +// static +Value const& Value::nullSingleton() +{ + static Value const nullStatic; + return nullStatic; +} + +// for backwards compatibility, we'll leave these global references around, but DO NOT +// use them in JSONCPP library code any more! +Value const& Value::null = Value::nullSingleton(); +Value const& Value::nullRef = Value::nullSingleton(); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + //return d >= static_cast<double>(min) && d <= static_cast<double>(max); + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1)); +} + +template <typename T> static inline double integerToDouble(T value) { + return static_cast<double>(value); +} + +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + size_t length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast<size_t>(Value::maxInt)) + length = Value::maxInt - 1; + + char* newString = static_cast<char*>(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; + char* newString = static_cast<char*>(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast<unsigned*>(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast<unsigned>(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast<unsigned const*>(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length==0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + free(value); +} +static inline void releaseStringValue(char* value, unsigned) { + free(value); +} +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(JSONCPP_STRING const& msg) + : msg_(msg) +{} +Exception::~Exception() JSONCPP_NOEXCEPT +{} +char const* Exception::what() const JSONCPP_NOEXCEPT +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +LogicError::LogicError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) +{ + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) +{ + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) +{} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_, 0u); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_, 0u); + comment_ = 0; + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} + +Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = static_cast<unsigned>(other.cstr_ + ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast<DuplicationPolicy>(other.storage_.policy_)) & 3U; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast<char*>(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType vtype) { + static char const emptyString[] = ""; + initBasic(vtype); + switch (vtype) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + // allocated_ == false, so this is safe. + value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString)); + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue)); +} + +Value::Value(const JSONCPP_STRING& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast<char*>(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(Value const& other) + : type_(other.type_), allocated_(false) + , + comments_(0), start_(other.start_), limit_(other.limit_) +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.string_ = other.value_.string_; + allocated_ = false; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); + } + } +} + +#if JSON_HAS_RVALUE_REFERENCES +// Move constructor +Value::Value(Value&& other) { + initBasic(nullValue); + swap(other); +} +#endif + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + delete[] comments_; + + value_.uint_ = 0; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2 & 0x1; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; +} + +JSONCPP_STRING Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return JSONCPP_STRING(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast<double>(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<double>(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast<float>(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<float>(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast<float>(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast<float>(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; + allocated_ = allocated; + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* key, char const* cend) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullSingleton(); + return *found; +} +Value const& Value::operator[](JSONCPP_STRING const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const JSONCPP_STRING& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullSingleton(); + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +bool Value::removeMember(const char* key, const char* cend, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullSingleton(); + + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const JSONCPP_STRING& key) +{ + return removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* key, char const* cend) const +{ + Value const* value = find(key, cend); + return NULL != value; +} +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(JSONCPP_STRING const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(JSONCPP_STRING((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type<const Value &>() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif + case uintValue: +#if defined(JSON_HAS_INT64) + return value_.uint_ <= maxUInt; +#else + return true; +#endif + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; } + +bool Value::isNumeric() const { return isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +JSONCPP_STRING Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } + +ptrdiff_t Value::getOffsetStart() const { return start_; } + +ptrdiff_t Value::getOffsetLimit() const { return limit_; } + +JSONCPP_STRING Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const JSONCPP_STRING& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const JSONCPP_STRING& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *++current != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.' || *current == ']') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(JSONCPP_STRING(beginName, current)); + } + } +} + +void Path::addPathInArg(const JSONCPP_STRING& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg++); + } +} + +void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + return Value::null; + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + return Value::null; + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + return Value::null; + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include <json/writer.h> +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <iomanip> +#include <memory> +#include <sstream> +#include <utility> +#include <set> +#include <cassert> +#include <cstring> +#include <cstdio> + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include <float.h> +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#if !defined(isfinite) +#include <ieeefp.h> +#define isfinite finite +#endif +#elif defined(_AIX) +#if !defined(isfinite) +#include <math.h> +#define isfinite finite +#endif +#elif defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ + _Isfinitef(x) : _IsFinite(x))) +#else +#include <math.h> +#define isfinite finite +#endif +#endif +#else +#include <cmath> +#if !(defined(__QNXNTO__)) // QNX already defines isfinite +#define isfinite std::isfinite +#endif +#endif + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__BORLANDC__) +#include <float.h> +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +#else +typedef std::auto_ptr<StreamWriter> StreamWriterPtr; +#endif + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +JSONCPP_STRING valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +JSONCPP_STRING valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +JSONCPP_STRING valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +JSONCPP_STRING valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[36]; + int len = -1; + + char formatString[6]; + sprintf(formatString, "%%.%dg", precision); + + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distingish the + // concepts of reals and integers. + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), formatString, value); + + // try to ensure we preserve the fact that this was given to us as a double on input + if (!strstr(buffer, ".") && !strstr(buffer, "e")) { + strcat(buffer, ".0"); + } + + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} +} + +JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } + +JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } + +JSONCPP_STRING valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid </ + // sequence. + // Should add a flag to allow this compatibility mode and prevent this + // sequence from occurring. + default: + if (isControlCharacter(*c)) { + JSONCPP_OSTRINGSTREAM oss; + oss << "\\u" << std::hex << std::uppercase << std::setfill('0') + << std::setw(4) << static_cast<int>(*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid </ + // sequence. + // Should add a flag to allow this compatibility mode and prevent this + // sequence from occurring. + default: + if ((isControlCharacter(*c)) || (*c == 0)) { + JSONCPP_OSTRINGSTREAM oss; + oss << "\\u" << std::hex << std::uppercase << std::setfill('0') + << std::setw(4) << static_cast<int>(*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +JSONCPP_STRING FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const JSONCPP_STRING& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +JSONCPP_STRING StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(JSONCPP_STRING const& value); + void writeIndent(); + void writeWithIndent(JSONCPP_STRING const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector<JSONCPP_STRING> ChildValues; + + ChildValues childValues_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + CommentStyle::Enum cs_; + JSONCPP_STRING colonSymbol_; + JSONCPP_STRING nullSymbol_; + JSONCPP_STRING endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) + , useSpecialFloats_(useSpecialFloats) + , precision_(precision) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + break; + case stringValue: + { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + JSONCPP_STRING const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + JSONCPP_STRING indentation = settings_["indentation"].asString(); + JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + JSONCPP_STRING colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + JSONCPP_STRING nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + if (pre > 17) pre = 17; + JSONCPP_STRING endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); + valid_keys->insert("useSpecialFloats"); + valid_keys->insert("precision"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<JSONCPP_STRING> valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { + JSONCPP_OSTRINGSTREAM sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/lowlevelil.cpp b/lowlevelil.cpp index a312bbd6..bf3b980e 100644 --- a/lowlevelil.cpp +++ b/lowlevelil.cpp @@ -579,6 +579,10 @@ bool LowLevelILFunction::GetExprText(Architecture* arch, ExprId expr, vector<Ins token.type = list[i].type; token.text = list[i].text; token.value = list[i].value; + token.size = list[i].size; + token.operand = list[i].operand; + token.context = list[i].context; + token.address = list[i].address; tokens.push_back(token); } @@ -603,6 +607,10 @@ bool LowLevelILFunction::GetInstructionText(Function* func, Architecture* arch, token.type = list[i].type; token.text = list[i].text; token.value = list[i].value; + token.size = list[i].size; + token.operand = list[i].operand; + token.context = list[i].context; + token.address = list[i].address; tokens.push_back(token); } diff --git a/platform.cpp b/platform.cpp index 7a6571cc..afbcbbf3 100644 --- a/platform.cpp +++ b/platform.cpp @@ -253,3 +253,152 @@ Ref<Platform> Platform::GetAssociatedPlatformByAddress(uint64_t& addr) return nullptr; return new Platform(platform); } + + +map<QualifiedName, Ref<Type>> Platform::GetTypes() +{ + size_t count; + BNQualifiedNameAndType* types = BNGetPlatformTypes(m_object, &count); + + map<QualifiedName, Ref<Type>> result; + for (size_t i = 0; i < count; i++) + { + QualifiedName name = QualifiedName::FromAPIObject(&types[i].name); + result[name] = new Type(BNNewTypeReference(types[i].type)); + } + + BNFreeTypeList(types, count); + return result; +} + + +map<QualifiedName, Ref<Type>> Platform::GetVariables() +{ + size_t count; + BNQualifiedNameAndType* types = BNGetPlatformVariables(m_object, &count); + + map<QualifiedName, Ref<Type>> result; + for (size_t i = 0; i < count; i++) + { + QualifiedName name = QualifiedName::FromAPIObject(&types[i].name); + result[name] = new Type(BNNewTypeReference(types[i].type)); + } + + BNFreeTypeList(types, count); + return result; +} + + +map<QualifiedName, Ref<Type>> Platform::GetFunctions() +{ + size_t count; + BNQualifiedNameAndType* types = BNGetPlatformFunctions(m_object, &count); + + map<QualifiedName, Ref<Type>> result; + for (size_t i = 0; i < count; i++) + { + QualifiedName name = QualifiedName::FromAPIObject(&types[i].name); + result[name] = new Type(BNNewTypeReference(types[i].type)); + } + + BNFreeTypeList(types, count); + return result; +} + + +map<uint32_t, QualifiedNameAndType> Platform::GetSystemCalls() +{ + size_t count; + BNSystemCallInfo* calls = BNGetPlatformSystemCalls(m_object, &count); + + map<uint32_t, QualifiedNameAndType> result; + for (size_t i = 0; i < count; i++) + { + QualifiedNameAndType nt; + nt.name = QualifiedName::FromAPIObject(&calls[i].name); + nt.type = new Type(BNNewTypeReference(calls[i].type)); + result[calls[i].number] = nt; + } + + BNFreeSystemCallList(calls, count); + return result; +} + + +Ref<Type> Platform::GetTypeByName(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + BNType* type = BNGetPlatformTypeByName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + if (!type) + return nullptr; + return new Type(type); +} + + +Ref<Type> Platform::GetVariableByName(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + BNType* type = BNGetPlatformVariableByName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + if (!type) + return nullptr; + return new Type(type); +} + + +Ref<Type> Platform::GetFunctionByName(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + BNType* type = BNGetPlatformFunctionByName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + if (!type) + return nullptr; + return new Type(type); +} + + +string Platform::GetSystemCallName(uint32_t n) +{ + char* str = BNGetPlatformSystemCallName(m_object, n); + string result = str; + BNFreeString(str); + return result; +} + + +Ref<Type> Platform::GetSystemCallType(uint32_t n) +{ + BNType* type = BNGetPlatformSystemCallType(m_object, n); + if (!type) + return nullptr; + return new Type(type); +} + + +string Platform::GenerateAutoPlatformTypeId(const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + char* str = BNGenerateAutoPlatformTypeId(m_object, &nameObj); + string result = str; + QualifiedName::FreeAPIObject(&nameObj); + BNFreeString(str); + return result; +} + + +Ref<NamedTypeReference> Platform::GenerateAutoPlatformTypeReference(BNNamedTypeReferenceClass cls, + const QualifiedName& name) +{ + string id = GenerateAutoPlatformTypeId(name); + return new NamedTypeReference(cls, id, name); +} + + +string Platform::GetAutoPlatformTypeIdSource() +{ + char* str = BNGetAutoPlatformTypeIdSource(m_object); + string result = str; + BNFreeString(str); + return result; +} diff --git a/python/__init__.py b/python/__init__.py index c5b7e5be..dcbb8276 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -18,10808 +18,56 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import _binaryninjacore as core -import abc -import ctypes -import traceback -import json -import struct -import threading -import code -import sys -import copy - -_plugin_init = False -def _init_plugins(): - global _plugin_init - if not _plugin_init: - _plugin_init = True - core.BNInitCorePlugins() - core.BNInitUserPlugins() - if not core.BNIsLicenseValidated(): - raise RuntimeError, "License is not valid. Please supply a valid license." - -class DataBuffer(object): - def __init__(self, contents="", handle=None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNDataBuffer) - elif isinstance(contents, int) or isinstance(contents, long): - self.handle = core.BNCreateDataBuffer(None, contents) - elif isinstance(contents, DataBuffer): - self.handle = core.BNDuplicateDataBuffer(contents.handle) - else: - self.handle = core.BNCreateDataBuffer(contents, len(contents)) - - def __del__(self): - core.BNFreeDataBuffer(self.handle) - - def __len__(self): - return int(core.BNGetDataBufferLength(self.handle)) - - def __getitem__(self, i): - if isinstance(i, tuple): - result = "" - source = str(self) - for s in i: - result += source[s] - return result - elif isinstance(i, slice): - if i.step is not None: - i = i.indices(len(self)) - start = i[0] - stop = i[1] - if stop <= start: - return "" - buf = ctypes.create_string_buffer(stop - start) - ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) - return buf.raw - else: - return str(self)[i] - elif i < 0: - if i >= -len(self): - return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) - raise IndexError, "index out of range" - elif i < len(self): - return chr(core.BNGetDataBufferByte(self.handle, int(i))) - else: - raise IndexError, "index out of range" - - def __setitem__(self, i, value): - if isinstance(i, slice): - if i.step is not None: - raise IndexError, "step not supported on assignment" - i = i.indices(len(self)) - start = i[0] - stop = i[1] - if stop < start: - stop = start - if len(value) != (stop - start): - data = str(self) - data = data[0:start] + value + data[stop:] - core.BNSetDataBufferContents(self.handle, data, len(data)) - else: - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) - elif i < 0: - if i >= -len(self): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) - else: - raise IndexError, "index out of range" - elif i < len(self): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) - else: - raise IndexError, "index out of range" - - def __str__(self): - buf = ctypes.create_string_buffer(len(self)) - ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) - return buf.raw - - def __repr__(self): - return repr(str(self)) - - def escape(self): - return core.BNDataBufferToEscapedString(self.handle) - - def unescape(self): - return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) - - def base64_encode(self): - return core.BNDataBufferToBase64(self.handle) - - def base64_decode(self): - return DataBuffer(handle = core.BNDecodeBase64(str(self))) - - def zlib_compress(self): - buf = core.BNZlibCompress(self.handle) - if buf is None: - return None - return DataBuffer(handle = buf) - - def zlib_decompress(self): - buf = core.BNZlibDecompress(self.handle) - if buf is None: - return None - return DataBuffer(handle = buf) - -class NavigationHandler(object): - def _register(self, handle): - self._cb = core.BNNavigationHandler() - self._cb.context = 0 - self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) - self._cb.getCurrentOffset = self._cb.getCurrentOffset.__class__(self._get_current_offset) - self._cb.navigate = self._cb.navigate.__class__(self._navigate) - core.BNSetFileMetadataNavigationHandler(handle, self._cb) - - def _get_current_view(self, ctxt): - try: - view = self.get_current_view() - except: - log_error(traceback.format_exc()) - view = "" - return core.BNAllocString(view) - - def _get_current_offset(self, ctxt): - try: - return self.get_current_offset() - except: - log_error(traceback.format_exc()) - return 0 - - def _navigate(self, ctxt, view, offset): - try: - return self.navigate(view, offset) - except: - 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. - """ - def __init__(self, filename = None, handle = None): - """ - Instantiates a new FileMetadata class. - - :param filename: The string path to the file to be opened. Defaults to None. - :param handle: A handle to the underlying C FileMetadata object. Defaults to None. - """ - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNFileMetadata) - else: - _init_plugins() - self.handle = core.BNCreateFileMetadata() - if filename is not None: - core.BNSetFilename(self.handle, str(filename)) - self.nav = None - - def __del__(self): - if self.navigation is not None: - 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_session_data(cls, name, value): - _FileMetadataAssociatedDataStore.set_default(name, value) - - @property - def filename(self): - """The name of the file (read/write)""" - return core.BNGetFilename(self.handle) - - @filename.setter - def filename(self, value): - core.BNSetFilename(self.handle, str(value)) - - @property - def modified(self): - """Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)""" - return core.BNIsFileModified(self.handle) - - @modified.setter - def modified(self, value): - if value: - core.BNMarkFileModified(self.handle) - else: - core.BNMarkFileSaved(self.handle) - - @property - def analysis_changed(self): - """Boolean result of whether the auto-analysis results have changed (read-only)""" - return core.BNIsAnalysisChanged(self.handle) - - @property - def has_database(self): - """Whether the FileMetadata is backed by a database (read-only)""" - return core.BNIsBackedByDatabase(self.handle) - - @property - def view(self): - return core.BNGetCurrentView(self.handle) - - @view.setter - def view(self, value): - core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) - - @property - def offset(self): - """The current offset into the file (read/write)""" - return core.BNGetCurrentOffset(self.handle) - - @offset.setter - def offset(self, value): - core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) - - @property - def raw(self): - """Gets the "Raw" BinaryView of the file""" - view = core.BNGetFileViewOfType(self.handle, "Raw") - if view is None: - return None - return BinaryView(file_metadata = self, handle = view) - - @property - def saved(self): - """Boolean result of whether the file has been saved (Inverse of 'modified' property) (read/write)""" - return not core.BNIsFileModified(self.handle) - - @saved.setter - def saved(self, value): - if value: - core.BNMarkFileSaved(self.handle) - 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 - - @property - def session_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 - `finally` clause to avoid handle leaks. - """ - core.BNCloseFile(self.handle) - - def begin_undo_actions(self): - """ - ``begin_undo_actions`` start recording actions taken so the can be undone at some point. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> - """ - core.BNBeginUndoActions(self.handle) - - def commit_undo_actions(self): - """ - ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> - """ - core.BNCommitUndoActions(self.handle) - - def undo(self): - """ - ``undo`` undo the last commited action in the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.redo() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> - """ - core.BNUndo(self.handle) - - def redo(self): - """ - ``redo`` redo the last commited action in the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.redo() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> - """ - core.BNRedo(self.handle) - - def navigate(self, view, offset): - return core.BNNavigate(self.handle, str(view), offset) - - 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, 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(file_metadata = self, handle = view) - - 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)) - if view is None: - view_type = core.BNGetBinaryViewTypeByName(str(name)) - if view_type is None: - return None - view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) - if view is None: - return None - return BinaryView(file_metadata = self, handle = view) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class FileAccessor: - def __init__(self): - self._cb = core.BNFileAccessor() - self._cb.context = 0 - self._cb.getLength = self._cb.getLength.__class__(self._get_length) - self._cb.read = self._cb.read.__class__(self._read) - self._cb.write = self._cb.write.__class__(self._write) - - def __len__(self): - return self.get_length() - - def _get_length(self, ctxt): - try: - return self.get_length() - except: - log_error(traceback.format_exc()) - return 0 - - def _read(self, ctxt, dest, offset, length): - try: - data = self.read(offset, length) - if data is None: - return 0 - if len(data) > length: - data = data[0:length] - ctypes.memmove(dest, data, len(data)) - return len(data) - except: - log_error(traceback.format_exc()) - return 0 - - def _write(self, ctxt, offset, src, length): - try: - data = ctypes.create_string_buffer(length) - ctypes.memmove(data, src, length) - return self.write(offset, data.raw) - except: - log_error(traceback.format_exc()) - return 0 - -class CoreFileAccessor(FileAccessor): - def __init__(self, accessor): - self._cb.context = accessor.context - self._cb.getLength = accessor.getLength - self._cb.read = accessor.read - self._cb.write = accessor.write - - def get_length(self): - return self._cb.getLength(self._cb.context) - - def read(self, offset, length): - data = ctypes.create_string_buffer(length) - length = self._cb.read(self._cb.context, data, offset, length) - return data.raw[0:length] - - def write(self, offset, value): - value = str(value) - data = ctypes.create_string_buffer(value) - return self._cb.write(self._cb.context, offset, data, len(value)) - -class BinaryDataNotification: - def data_written(self, view, offset, length): - pass - - def data_inserted(self, view, offset, length): - pass - - def data_removed(self, view, offset, length): - pass - - def function_added(self, view, func): - pass - - def function_removed(self, view, func): - pass - - def function_updated(self, view, func): - pass - - def data_var_added(self, view, var): - pass - - def data_var_removed(self, view, var): - pass - - def data_var_updated(self, view, var): - pass - - def string_found(self, view, string_type, offset, length): - pass - - def string_removed(self, view, string_type, offset, length): - pass - -class UndoAction: - name = None - action_type = None - _registered = False - _registered_cb = None - - def __init__(self, view): - self._cb = core.BNUndoAction() - if not self.__class__._registered: - raise TypeError, "undo action type not registered" - action_type = self.__class__.action_type - if isinstance(action_type, str): - self._cb.type = core.BNActionType_by_name[action_type] - else: - self._cb.type = action_type - self._cb.context = 0 - self._cb.undo = self._cb.undo.__class__(self._undo) - self._cb.redo = self._cb.redo.__class__(self._redo) - self._cb.serialize = self._cb.serialize.__class__(self._serialize) - self.view = view - - @classmethod - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "undo action 'name' not defined" - if cls.action_type is None: - raise ValueError, "undo action 'action_type' not defined" - cb_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(core.BNUndoAction)) - cls._registered_cb = cb_type(cls._deserialize) - core.BNRegisterUndoActionType(cls.name, 0, cls._registered_cb) - cls._registered = True - - @classmethod - def _deserialize(cls, ctxt, data, result): - try: - action = cls.deserialize(json.loads(data)) - if action is None: - return False - result.context = action._cb.context - result.undo = action._cb.undo - result.redo = action._cb.redo - result.serialize = action._cb.serialize - return True - except: - log_error(traceback.format_exc()) - return False - - def _undo(self, ctxt, view): - try: - self.undo() - except: - log_error(traceback.format_exc()) - return False - - def _redo(self, ctxt, view): - try: - self.redo() - except: - log_error(traceback.format_exc()) - return False - - def _serialize(self, ctxt): - try: - return json.dumps(self.serialize()) - except: - log_error(traceback.format_exc()) - return "null" - -class StringReference(object): - def __init__(self, string_type, start, length): - self.type = string_type - self.start = start - self.length = length - - def __repr__(self): - return "<%s: %#x, len %#x>" % (self.type, self.start, self.length) - -class BinaryDataNotificationCallbacks(object): - def __init__(self, view, notify): - self.view = view - self.notify = notify - self._cb = core.BNBinaryDataNotification() - self._cb.context = 0 - self._cb.dataWritten = self._cb.dataWritten.__class__(self._data_written) - self._cb.dataInserted = self._cb.dataInserted.__class__(self._data_inserted) - self._cb.dataRemoved = self._cb.dataRemoved.__class__(self._data_removed) - self._cb.functionAdded = self._cb.functionAdded.__class__(self._function_added) - self._cb.functionRemoved = self._cb.functionRemoved.__class__(self._function_removed) - self._cb.functionUpdated = self._cb.functionUpdated.__class__(self._function_updated) - self._cb.dataVariableAdded = self._cb.dataVariableAdded.__class__(self._data_var_added) - self._cb.dataVariableRemoved = self._cb.dataVariableRemoved.__class__(self._data_var_removed) - self._cb.dataVariableUpdated = self._cb.dataVariableUpdated.__class__(self._data_var_updated) - self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) - self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) - - def _register(self): - core.BNRegisterDataNotification(self.view.handle, self._cb) - - def _unregister(self): - core.BNUnregisterDataNotification(self.view.handle, self._cb) - - def _data_written(self, ctxt, view, offset, length): - try: - self.notify.data_written(self.view, offset, length) - except OSError: - log_error(traceback.format_exc()) - - def _data_inserted(self, ctxt, view, offset, length): - try: - self.notify.data_inserted(self.view, offset, length) - except: - log_error(traceback.format_exc()) - - def _data_removed(self, ctxt, view, offset, length): - try: - self.notify.data_removed(self.view, offset, length) - except: - log_error(traceback.format_exc()) - - def _function_added(self, ctxt, view, func): - try: - self.notify.function_added(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _function_removed(self, ctxt, view, func): - try: - self.notify.function_removed(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _function_updated(self, ctxt, view, func): - try: - self.notify.function_updated(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _data_var_added(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _data_var_removed(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _data_var_updated(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _string_found(self, ctxt, view, string_type, offset, length): - try: - self.notify.string_found(self.view, core.BNStringType_names[string_type], offset, length) - except: - log_error(traceback.format_exc()) - - def _string_removed(self, ctxt, view, string_type, offset, length): - try: - self.notify.string_removed(self.view, core.BNStringType_names[string_type], offset, length) - except: - log_error(traceback.format_exc()) - -class _BinaryViewTypeMetaclass(type): - @property - def list(self): - """List all BinaryView types (read-only)""" - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetBinaryViewTypes(count) - result = [] - for i in xrange(0, count.value): - result.append(BinaryViewType(types[i])) - core.BNFreeBinaryViewTypeList(types) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetBinaryViewTypes(count) - try: - for i in xrange(0, count.value): - yield BinaryViewType(types[i]) - finally: - core.BNFreeBinaryViewTypeList(types) - - def __getitem__(self, value): - _init_plugins() - view_type = core.BNGetBinaryViewTypeByName(str(value)) - if view_type is None: - raise KeyError, "'%s' is not a valid view type" % str(value) - return BinaryViewType(view_type) - - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class BinaryViewType(object): - __metaclass__ = _BinaryViewTypeMetaclass - - def __init__(self, handle): - self.handle = core.handle_of_type(handle, core.BNBinaryViewType) - - @property - def name(self): - """Binary View name (read-only)""" - return core.BNGetBinaryViewTypeName(self.handle) - - @property - def long_name(self): - """BinaryView long name (read-only)""" - return core.BNGetBinaryViewTypeLongName(self.handle) - - def __repr__(self): - return "<view type: '%s'>" % self.name - - def create(self, data): - view = core.BNCreateBinaryViewOfType(self.handle, data.handle) - if view is None: - return None - return BinaryView(file_metadata = data.file, handle = view) - - def open(self, src, file_metadata = None): - data = BinaryView.open(src, file_metadata) - if data is None: - return None - return self.create(data) - - def is_valid_for_data(self, data): - return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle) - - def register_arch(self, ident, endian, arch): - core.BNRegisterArchitectureForViewType(self.handle, ident, endian, arch.handle) - - def get_arch(self, ident, endian): - arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) - if arch is None: - return None - return Architecture(arch) - - def register_platform(self, ident, arch, platform): - core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, platform.handle) - - def register_default_platform(self, arch, platform): - core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, platform.handle) - - def get_platform(self, ident, arch): - platform = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) - if platform is None: - return None - return Platform(None, platform) - - def __setattr__(self, name, value): - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class AnalysisCompletionEvent(object): - def __init__(self, view, callback): - self.view = view - self.callback = callback - self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) - self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb) - - def __del__(self): - core.BNFreeAnalysisCompletionEvent(self.handle) - - def _notify(self, ctxt): - try: - self.callback() - except: - log_error(traceback.format_exc()) - - def _empty_callback(self): - pass - - def cancel(self): - self.callback = self._empty_callback - core.BNCancelAnalysisCompletionEvent(self.handle) - -class AnalysisProgress(object): - def __init__(self, state, count, total): - self.state = state - self.count = count - self.total = total - - def __str__(self): - if self.state == core.DisassembleState: - return "Disassembling (%d/%d)" % (self.count, self.total) - if self.state == core.AnalyzeState: - return "Analyzing (%d/%d)" % (self.count, self.total) - return "Idle" - - def __repr__(self): - return "<progress: %s>" % str(self) - -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. - """ - def __init__(self, func, block, addr): - self.function = func - self.block = block - self.address = addr - -class LinearDisassemblyLine(object): - def __init__(self, line_type, func, block, line_offset, contents): - self.type = line_type - self.function = func - self.block = block - self.line_offset = line_offset - self.contents = contents - - def __str__(self): - return str(self.contents) - - def __repr__(self): - return repr(self.contents) - -class DataVariable(object): - def __init__(self, addr, var_type, auto_discovered): - self.address = addr - self.type = var_type - self.auto_discovered = auto_discovered - - def __repr__(self): - return "<var 0x%x: %s>" % (self.address, str(self.type)) - -class Segment(object): - def __init__(self, start, length, data_offset, data_length, flags): - self.start = start - self.length = length - self.data_offset = data_offset - self.data_length = data_length - self.flags = flags - - @property - def end(self): - return self.start + self.length - - def __len__(self): - return self.length - - def __repr__(self): - return "<segment: %#x-%#x, %s%s%s>" % (self.start, self.end, - "r" if (self.flags & core.SegmentReadable) != 0 else "-", - "w" if (self.flags & core.SegmentWritable) != 0 else "-", - "x" if (self.flags & core.SegmentExecutable) != 0 else "-") - -class Section(object): - def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size): - self.name = name - self.type = section_type - self.start = start - self.length = length - self.linked_section = linked_section - self.info_section = info_section - self.info_data = info_data - self.align = align - self.entry_size = entry_size - - @property - def end(self): - return self.start + self.length - - def __len__(self): - return self.length - - def __repr__(self): - return "<section %s: %#x-%#x>" % (self.name, self.start, self.end) - -class AddressRange(object): - def __init__(self, start, end): - self.start = start - self.end = end - - @property - def length(self): - return self.end - self.start - - def __len__(self): - return self.end - self.start - - def __repr__(self): - return "<%#x-%#x>" % (self.start, self.end) - -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 - job of BinaryView is file format parsing which allows Binary Ninja to read, write, insert, remove portions - of the file given a virtual address. For the purposes of this documentation we define a virtual address as the - memory address that the various pieces of the physical file will be loaded at. - - A binary file does not have to have just one BinaryView, thus much of the interface to manipulate disassembly exists - within or is accessed through a BinaryView. All files are guaranteed to have at least the ``Raw`` BinaryView. The - ``Raw`` BinaryView is simply a hex editor, but is helpful for manipulating binary files via their absolute addresses. - - BinaryViews are plugins and thus registered with Binary Ninja at startup, and thus should **never** be instantiated - directly as this is already done. The list of available BinaryViews can be seen in the BinaryViewType class which - provides an iterator and map of the various installed BinaryViews:: - - >>> list(BinaryViewType) - [<view type: 'Raw'>, <view type: 'ELF'>, <view type: 'Mach-O'>, <view type: 'PE'>] - >>> BinaryViewType['ELF'] - <view type: 'ELF'> - - To open a file with a given BinaryView the following code can be used:: - - >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") - >>> bv - <BinaryView: '/bin/ls', start 0x100000000, len 0xa000> - - `By convention in the rest of this document we will use bv to mean an open BinaryView of an executable file.` - When a BinaryView is open on an executable view, analysis does not automatically run, this can be done by running - the ``update_analysis_and_wait()`` method which disassembles the executable and returns when all disassembly is - finished:: - - >>> bv.update_analysis_and_wait() - >>> - - Since BinaryNinja's analysis is multi-threaded (depending on version) this can also be done in the background by - using the ``update_analysis()`` method instead. - - By standard python convention methods which start with '_' should be considered private and should not be called - externally. Additionanlly, methods which begin with ``perform_`` should not be called either and are - used explicitly for subclassing the BinaryView. - - .. 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 - _registered = False - _registered_cb = None - registered_view_type = None - next_address = 0 - _associated_data = {} - - def __init__(self, file_metadata = None, parent_view = None, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNBinaryView) - if file_metadata is None: - self.file = FileMetadata(handle = core.BNGetFileForView(handle)) - else: - self.file = file_metadata - elif self.__class__ is BinaryView: - _init_plugins() - if file_metadata is None: - file_metadata = FileMetadata() - self.handle = core.BNCreateBinaryDataView(file_metadata.handle) - self.file = FileMetadata(handle = core.BNNewFileReference(file_metadata)) - else: - _init_plugins() - if not self.__class__._registered: - raise TypeError, "view type not registered" - self._cb = core.BNCustomBinaryView() - self._cb.context = 0 - self._cb.init = self._cb.init.__class__(self._init) - self._cb.read = self._cb.read.__class__(self._read) - self._cb.write = self._cb.write.__class__(self._write) - self._cb.insert = self._cb.insert.__class__(self._insert) - self._cb.remove = self._cb.remove.__class__(self._remove) - self._cb.getModification = self._cb.getModification.__class__(self._get_modification) - self._cb.isValidOffset = self._cb.isValidOffset.__class__(self._is_valid_offset) - 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.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) - self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) - self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) - self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) - self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) - self._cb.save = self._cb.save.__class__(self._save) - self.file = file_metadata - if parent_view is not None: - parent_view = parent_view.handle - self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, parent_view, self._cb) - self.notifications = {} - self.next_address = None # Do NOT try to access view before init() is called, use placeholder - - @classmethod - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "view 'name' not defined" - if cls.long_name is None: - cls.long_name = cls.name - cls._registered_cb = core.BNCustomBinaryViewType() - cls._registered_cb.context = 0 - cls._registered_cb.create = cls._registered_cb.create.__class__(cls._create) - cls._registered_cb.isValidForData = cls._registered_cb.isValidForData.__class__(cls._is_valid_for_data) - cls.registered_view_type = BinaryViewType(core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb)) - cls._registered = True - - @classmethod - def _create(cls, ctxt, data): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(data)) - view = cls(BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data))) - if view is None: - return None - return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return None - - @classmethod - def _is_valid_for_data(cls, ctxt, data): - try: - return cls.is_valid_for_data(BinaryView(handle = core.BNNewViewReference(data))) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def open(cls, src, file_metadata = None): - _init_plugins() - if isinstance(src, FileAccessor): - if file_metadata is None: - file_metadata = FileMetadata() - view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) - else: - if file_metadata is None: - file_metadata = FileMetadata(str(src)) - view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) - if view is None: - return None - result = BinaryView(file_metadata = file_metadata, handle = view) - return result - - @classmethod - def new(cls, data = None, file_metadata = None): - _init_plugins() - if file_metadata is None: - file_metadata = FileMetadata() - if data is None: - view = core.BNCreateBinaryDataView(file_metadata.handle) - else: - buf = DataBuffer(data) - view = core.BNCreateBinaryDataViewFromBuffer(file_metadata.handle, buf.handle) - if view is None: - return None - result = BinaryView(file_metadata = 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_session_data(cls, name, value): - """ - ```set_default_session_data``` saves a variable to the BinaryView. - :param name: name of the variable to be saved - :param value: value of the variable to be saved - - :Example: - >>> BinaryView.set_default_session_data("variable_name", "value") - >>> bv.session_data.variable_name - 'value' - """ - _BinaryViewAssociatedDataStore.set_default(name, value) - - def __del__(self): - for i in self.notifications.values(): - i._unregister() - core.BNFreeBinaryView(self.handle) - - def __iter__(self): - count = ctypes.c_ulonglong(0) - funcs = core.BNGetAnalysisFunctionList(self.handle, count) - try: - for i in xrange(0, count.value): - yield Function(self, core.BNNewFunctionReference(funcs[i])) - finally: - core.BNFreeFunctionList(funcs, count.value) - - @property - def parent_view(self): - """View that contains the raw data used by this view (read-only)""" - result = core.BNGetParentView(self.handle) - if result is None: - return None - return BinaryView(handle = result) - - @property - def modified(self): - """boolean modification state of the BinaryView (read/write)""" - return self.file.modified - - @modified.setter - def modified(self, value): - self.file.modified = value - - @property - def analysis_changed(self): - """boolean analysis state changed of the currently running analysis (read-only)""" - return self.file.analysis_changed - - @property - def has_database(self): - """boolean has a database been written to disk (read-only)""" - return self.file.has_database - - @property - def view(self): - return self.file.view - - @view.setter - def view(self, value): - self.file.view = value - - @property - def offset(self): - return self.file.offset - - @offset.setter - def offset(self, value): - self.file.offset = value - - @property - def start(self): - """Start offset of the binary (read-only)""" - return core.BNGetStartOffset(self.handle) - - @property - def end(self): - """End offset of the binary (read-only)""" - return core.BNGetEndOffset(self.handle) - - @property - def entry_point(self): - """Entry point of the binary (read-only)""" - return core.BNGetEntryPoint(self.handle) - - @property - def arch(self): - """The architecture associated with the current BinaryView (read/write)""" - arch = core.BNGetDefaultArchitecture(self.handle) - if arch is None: - return None - return Architecture(handle = arch) - - @arch.setter - def arch(self, value): - if value is None: - core.BNSetDefaultArchitecture(self.handle, None) - else: - core.BNSetDefaultArchitecture(self.handle, value.handle) - - @property - def platform(self): - """The platform associated with the current BinaryView (read/write)""" - platform = core.BNGetDefaultPlatform(self.handle) - if platform is None: - return None - return Platform(self.arch, handle = platform) - - @platform.setter - def platform(self, value): - if value is None: - core.BNSetDefaultPlatform(self.handle, None) - else: - core.BNSetDefaultPlatform(self.handle, value.handle) - - @property - def endianness(self): - """Endianness of the binary (read-only)""" - return core.BNGetDefaultEndianness(self.handle) - - @property - def address_size(self): - """Address size of the binary (read-only)""" - return core.BNGetViewAddressSize(self.handle) - - @property - def executable(self): - """Whether the binary is an executable (read-only)""" - return core.BNIsExecutableView(self.handle) - - @property - def functions(self): - """List of functions (read-only)""" - count = ctypes.c_ulonglong(0) - funcs = core.BNGetAnalysisFunctionList(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(Function(self, core.BNNewFunctionReference(funcs[i]))) - core.BNFreeFunctionList(funcs, count.value) - return result - - @property - def has_functions(self): - """Boolean whether the binary has functions (read-only)""" - return core.BNHasFunctions(self.handle) - - @property - def entry_function(self): - """Entry function (read-only)""" - func = core.BNGetAnalysisEntryPoint(self.handle) - if func is None: - return None - return Function(self, func) - - @property - def symbols(self): - """Dict of symbols (read-only)""" - count = ctypes.c_ulonglong(0) - syms = core.BNGetSymbols(self.handle, count) - result = {} - for i in xrange(0, count.value): - sym = Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i])) - result[sym.raw_name] = sym - core.BNFreeSymbolList(syms, count.value) - return result - - @property - def view_type(self): - """View type (read-only)""" - return core.BNGetViewType(self.handle) - - @property - def available_view_types(self): - """Available view types (read-only)""" - count = ctypes.c_ulonglong(0) - types = core.BNGetBinaryViewTypesForData(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(BinaryViewType(types[i])) - core.BNFreeBinaryViewTypeList(types) - return result - - @property - def strings(self): - """List of strings (read-only)""" - return self.get_strings() - - @property - def saved(self): - """boolean state of whether or not the file has been saved (read/write)""" - return self.file.saved - - @saved.setter - def saved(self, value): - self.file.saved = value - - @property - def analysis_progress(self): - """Status of current analysis (read-only)""" - result = core.BNGetAnalysisProgress(self.handle) - return AnalysisProgress(result.state, result.count, result.total) - - @property - def linear_disassembly(self): - """Iterator for all lines in the linear disassembly of the view""" - return self.get_linear_disassembly(None) - - @property - def data_vars(self): - """List of data variables (read-only)""" - count = ctypes.c_ulonglong(0) - var_list = core.BNGetDataVariables(self.handle, count) - result = {} - for i in xrange(0, count.value): - addr = var_list[i].address - var_type = Type(core.BNNewTypeReference(var_list[i].type)) - auto_discovered = var_list[i].autoDiscovered - result[addr] = DataVariable(addr, var_type, auto_discovered) - core.BNFreeDataVariables(var_list, count.value) - return result - - @property - def types(self): - """List of defined types (read-only)""" - count = ctypes.c_ulonglong(0) - type_list = core.BNGetAnalysisTypeList(self.handle, count) - result = {} - for i in xrange(0, count.value): - result[type_list[i].name] = Type(core.BNNewTypeReference(type_list[i].type)) - core.BNFreeTypeList(type_list, count.value) - return result - - @property - def segments(self): - """List of segments (read-only)""" - count = ctypes.c_ulonglong(0) - segment_list = core.BNGetSegments(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(Segment(segment_list[i].start, segment_list[i].length, - segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags)) - core.BNFreeSegmentList(segment_list) - return result - - @property - def sections(self): - """List of sections (read-only)""" - count = ctypes.c_ulonglong(0) - section_list = core.BNGetSections(self.handle, count) - result = {} - for i in xrange(0, count.value): - result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, - section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, - section_list[i].infoData, section_list[i].align, section_list[i].entrySize) - core.BNFreeSectionList(section_list, count.value) - return result - - @property - def allocated_ranges(self): - """List of valid address ranges for this view (read-only)""" - count = ctypes.c_ulonglong(0) - range_list = core.BNGetAllocatedRanges(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(AddressRange(range_list[i].start, range_list[i].end)) - core.BNFreeAddressRanges(range_list) - return result - - @property - def session_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)) - - def __getitem__(self, i): - if isinstance(i, tuple): - result = "" - for s in i: - result += self.__getitem__(s) - return result - elif isinstance(i, slice): - if i.step is not None: - raise IndexError, "step not implemented" - i = i.indices(self.end) - start = i[0] - stop = i[1] - if stop <= start: - return "" - return str(self.read(start, stop - start)) - elif i < 0: - if i >= -len(self): - value = str(self.read(int(len(self) + i), 1)) - if len(value) == 0: - return IndexError, "index not readable" - return value - raise IndexError, "index out of range" - elif (i >= self.start) and (i < self.end): - value = str(self.read(int(i), 1)) - if len(value) == 0: - return IndexError, "index not readable" - return value - else: - raise IndexError, "index out of range" - - def __setitem__(self, i, value): - if isinstance(i, slice): - if i.step is not None: - raise IndexError, "step not supported on assignment" - i = i.indices(self.end) - start = i[0] - stop = i[1] - if stop < start: - stop = start - if len(value) != (stop - start): - self.remove(start, stop - start) - self.insert(start, value) - else: - self.write(start, value) - elif i < 0: - if i >= -len(self): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - if self.write(int(len(self) + i), value) != 1: - raise IndexError, "index not writable" - else: - raise IndexError, "index out of range" - elif (i >= self.start) and (i < self.end): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - if self.write(int(i), value) != 1: - raise IndexError, "index not writable" - else: - raise IndexError, "index out of range" - - def __repr__(self): - start = self.start - length = len(self) - if start != 0: - size = "start %#x, len %#x" % (start, length) - else: - size = "len %#x" % length - filename = self.file.filename - if len(filename) > 0: - return "<BinaryView: '%s', %s>" % (filename, size) - return "<BinaryView: %s>" % (size) - - def _init(self, ctxt): - try: - return self.init() - except: - log_error(traceback.format_exc()) - return False - - def _read(self, ctxt, dest, offset, length): - try: - data = self.perform_read(offset, length) - if data is None: - return 0 - if len(data) > length: - data = data[0:length] - ctypes.memmove(dest, str(data), len(data)) - return len(data) - except: - log_error(traceback.format_exc()) - return 0 - - def _write(self, ctxt, offset, src, length): - try: - data = ctypes.create_string_buffer(length) - ctypes.memmove(data, src, length) - return self.perform_write(offset, data.raw) - except: - log_error(traceback.format_exc()) - return 0 - - def _insert(self, ctxt, offset, src, length): - try: - data = ctypes.create_string_buffer(length) - ctypes.memmove(data, src, length) - return self.perform_insert(offset, data.raw) - except: - log_error(traceback.format_exc()) - return 0 - - def _remove(self, ctxt, offset, length): - try: - return self.perform_remove(offset, length) - except: - log_error(traceback.format_exc()) - return 0 - - def _get_modification(self, ctxt, offset): - try: - return self.perform_get_modification(offset) - except: - log_error(traceback.format_exc()) - return core.Original - - def _is_valid_offset(self, ctxt, offset): - try: - return self.perform_is_valid_offset(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_readable(self, ctxt, offset): - try: - return self.perform_is_offset_readable(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_writable(self, ctxt, offset): - try: - return self.perform_is_offset_writable(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_executable(self, ctxt, offset): - try: - return self.perform_is_offset_executable(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) - except: - log_error(traceback.format_exc()) - return offset - - def _get_start(self, ctxt): - try: - return self.perform_get_start() - except: - log_error(traceback.format_exc()) - return 0 - - def _get_length(self, ctxt): - try: - return self.perform_get_length() - except: - log_error(traceback.format_exc()) - return 0 - - def _get_entry_point(self, ctxt): - try: - return self.perform_get_entry_point() - except: - log_error(traceback.format_exc()) - return 0 - - def _is_executable(self, ctxt): - try: - return self.perform_is_executable() - except: - log_error(traceback.format_exc()) - return False - - def _get_default_endianness(self, ctxt): - try: - return self.perform_get_default_endianness() - except: - log_error(traceback.format_exc()) - return core.LittleEndian - - def _get_address_size(self, ctxt): - try: - return self.perform_get_address_size() - except: - log_error(traceback.format_exc()) - return 8 - - def _save(self, ctxt, file_accessor): - try: - return self.perform_save(CoreFileAccessor(file_accessor)) - except: - log_error(traceback.format_exc()) - return False - - def init(self): - return True - - def get_disassembly(self, addr, arch=None): - """ - ``get_disassembly`` simple helper function for printing disassembly of a given address - - :param int addr: virtual address of instruction - :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None - :return: a str representation of the instruction at virtual address ``addr`` or None - :rtype: str or None - :Example: - - >>> bv.get_disassembly(bv.entry_point) - 'push ebp' - >>> - """ - if arch is None: - arch = self.arch - txt, size = arch.get_instruction_text(self.read(addr, self.arch.max_instr_length), addr) - self.next_address = addr + size - if txt is None: - return None - return ''.join(str(a) for a in txt).strip() - - def get_next_disassembly(self, arch=None): - """ - ``get_next_disassembly`` simple helper function for printing disassembly of the next instruction. - The internal state of the instruction to be printed is stored in the ``next_address`` attribute - - :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None - :return: a str representation of the instruction at virtual address ``self.next_address`` - :rtype: str or None - :Example: - - >>> bv.get_next_disassembly() - 'push ebp' - >>> bv.get_next_disassembly() - 'mov ebp, esp' - >>> #Now reset the starting point back to the entry point - >>> bv.next_address = bv.entry_point - >>> bv.get_next_disassembly() - 'push ebp' - >>> - """ - 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: - return None - return ''.join(str(a) for a in txt).strip() - - def perform_save(self, accessor): - if self.parent_view is not None: - return self.parent_view.save(accessor) - return False - - @abc.abstractmethod - def perform_get_address_size(self): - raise NotImplementedError - - def perform_get_length(self): - """ - ``perform_get_length`` implements a query for the size of the virtual address range used by - the BinaryView. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :return: returns the size of the virtual address range used by the BinaryView. - :rtype: int - """ - return 0 - - def perform_read(self, addr, length): - """ - ``perform_read`` implements a mapping between a virtual address and an absolute file offset, reading - ``length`` bytes from the rebased address ``addr``. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to attempt to read from - :param int length: the number of bytes to be read - :return: length bytes read from addr, should return empty string on error - :rtype: str - """ - return "" - - def perform_write(self, addr, data): - """ - ``perform_write`` implements a mapping between a virtual address and an absolute file offset, writing - the bytes ``data`` to rebased address ``addr``. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address - :param str data: the data to be written - :return: length of data written, should return 0 on error - :rtype: int - """ - return 0 - - def perform_insert(self, addr, data): - """ - ``perform_insert`` implements a mapping between a virtual address and an absolute file offset, inserting - the bytes ``data`` to rebased address ``addr``. - - .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, inserting is disallowed - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address - :param str data: the data to be inserted - :return: length of data inserted, should return 0 on error - :rtype: int - """ - return 0 - - def perform_remove(self, addr, length): - """ - ``perform_remove`` implements a mapping between a virtual address and an absolute file offset, removing - ``length`` bytes from the rebased address ``addr``. - - .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, removing data is disallowed - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address - :param str data: the data to be removed - :return: length of data removed, should return 0 on error - :rtype: int - """ - return 0 - - def perform_get_modification(self, addr): - """ - ``perform_get_modification`` implements query to the whether the virtual address ``addr`` is modified. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to be checked - :return: One of the following: Original = 0, Changed = 1, Inserted = 2 - :rtype: BNModificationStatus - """ - return core.Original - - def perform_is_valid_offset(self, addr): - """ - ``perform_is_valid_offset`` implements a check if an virtual address ``addr`` is valid. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to be checked - :return: true if the virtual address is valid, false if the virtual address is invalid or error - :rtype: bool - """ - data = self.read(addr, 1) - return (data is not None) and (len(data) == 1) - - def perform_is_offset_readable(self, offset): - """ - ``perform_is_offset_readable`` implements a check if an virtual address is readable. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int offset: a virtual address to be checked - :return: true if the virtual address is readable, false if the virtual address is not readable or error - :rtype: bool - """ - return self.is_valid_offset(offset) - - def perform_is_offset_writable(self, addr): - """ - ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is writable. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to be checked - :return: true if the virtual address is writable, false if the virtual address is not writable or error - :rtype: bool - """ - return self.is_valid_offset(addr) - - def perform_is_offset_executable(self, addr): - """ - ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is executable. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to be checked - :return: true if the virtual address is executable, false if the virtual address is not executable or error - :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 - memory address. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :param int addr: a virtual address to start checking from. - :return: the next readable, writable, or executable virtual memory address - :rtype: int - """ - if addr < self.perform_get_start(): - return self.perform_get_start() - return addr - - def perform_get_start(self): - """ - ``perform_get_start`` implements a query for the first readable, writable, or executable virtual address in - the BinaryView. - - .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide - data without overriding this method. - .. warning:: This method **must not** be called directly. - - :return: returns the first virtual address in the BinaryView. - :rtype: int - """ - return 0 - - def perform_get_entry_point(self): - """ - ``perform_get_entry_point`` implements a query for the initial entry point for code execution. - - .. note:: This method **should** be implmented for custom BinaryViews that are executable. - .. warning:: This method **must not** be called directly. - - :return: the virtual address of the entry point - :rtype: int - """ - return 0 - - def perform_is_executable(self): - """ - ``perform_is_executable`` implements a check which returns true if the BinaryView is executable. - - .. note:: This method **must** be implemented for custom BinaryViews that are executable. - .. warning:: This method **must not** be called directly. - - :return: true if the current BinaryView is executable, false if it is not executable or on error - :rtype: bool - """ - return False - - def perform_get_default_endianness(self): - """ - ``perform_get_default_endianness`` implements a check which returns true if the BinaryView is executable. - - .. 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`` - :rtype: BNEndianness - """ - return core.LittleEndian - - 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, progress_func) - - 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(progress_func) - - def get_view_of_type(self, name): - """ - ``get_view_of_type`` returns the BinaryView associated with the provided name if it exists. - - :param str name: Name of the view to be retrieved - :return: BinaryView object assocated with the provided name or None on failure - :rtype: BinaryView or None - """ - return self.file.get_view_of_type(name) - - def begin_undo_actions(self): - """ - ``begin_undo_actions`` start recording actions taken so the can be undone at some point. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> - """ - self.file.begin_undo_actions() - - def add_undo_action(self, action): - core.BNAddUndoAction(self.handle, action.__class__.name, action._cb) - - def commit_undo_actions(self): - """ - ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> - """ - self.file.commit_undo_actions() - - def undo(self): - """ - ``undo`` undo the last commited action in the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.redo() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> - """ - self.file.undo() - - def redo(self): - """ - ``redo`` redo the last commited action in the undo database. - - :rtype: None - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) - True - >>> bv.commit_undo_actions() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> bv.undo() - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.redo() - >>> bv.get_disassembly(0x100012f1) - 'nop' - >>> - """ - self.file.redo() - - def navigate(self, view, offset): - self.file.navigate(view, offset) - - def read(self, addr, length): - """ - ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. - - :param int addr: virtual address to read from. - :param int length: number of bytes to read. - :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data. - :rtype: str - :Example: - - >>> #Opening a x86_64 Mach-O binary - >>> bv = BinaryViewType['Raw'].open("/bin/ls") - >>> bv.read(0,4) - \'\\xcf\\xfa\\xed\\xfe\' - """ - buf = DataBuffer(handle = core.BNReadViewBuffer(self.handle, addr, length)) - return str(buf) - - def write(self, addr, data): - """ - ``write`` writes the bytes in ``data`` to the virtual address ``addr``. - - :param int addr: virtual address to write to. - :param str data: data to be written at addr. - :return: number of bytes written to virtual address ``addr`` - :rtype: int - :Example: - - >>> bv.read(0,4) - 'BBBB' - >>> bv.write(0, "AAAA") - 4L - >>> bv.read(0,4) - 'AAAA' - """ - buf = DataBuffer(data) - return core.BNWriteViewBuffer(self.handle, addr, buf.handle) - - def insert(self, addr, data): - """ - ``insert`` inserts the bytes in ``data`` to the virtual address ``addr``. - - :param int addr: virtual address to write to. - :param str data: data to be inserted at addr. - :return: number of bytes inserted to virtual address ``addr`` - :rtype: int - :Example: - - >>> bv.insert(0,"BBBB") - 4L - >>> bv.read(0,8) - 'BBBBAAAA' - """ - buf = DataBuffer(data) - return core.BNInsertViewBuffer(self.handle, addr, buf.handle) - - def remove(self, addr, length): - """ - ``remove`` removes at most ``length`` bytes from virtual address ``addr``. - - :param int addr: virtual address to remove from. - :param int length: number of bytes to remove. - :return: number of bytes removed from virtual address ``addr`` - :rtype: int - :Example: - - >>> bv.read(0,8) - 'BBBBAAAA' - >>> bv.remove(0,4) - 4L - >>> bv.read(0,4) - 'AAAA' - """ - return core.BNRemoveViewData(self.handle, addr, length) - - def get_modification(self, addr, length = None): - """ - ``get_modification`` returns the modified bytes of up to ``length`` bytes from virtual address ``addr``, or if - ``length`` is None returns the core.BNModificationStatus. - - :param int addr: virtual address to get modification from - :param int length: optional length of modification - :return: Either core.BNModificationStatus of the byte at ``addr``, or string of modified bytes at ``addr`` - :rtype: core.BNModificationStatus or str - """ - if length is None: - return core.BNGetModification(self.handle, addr) - data = (core.BNModificationStatus * length)() - length = core.BNGetModificationArray(self.handle, addr, data, length) - return data[0:length] - - def is_valid_offset(self, addr): - """ - ``is_valid_offset`` checks if an virtual address ``addr`` is valid . - - :param int addr: a virtual address to be checked - :return: true if the virtual address is valid, false if the virtual address is invalid or error - :rtype: bool - """ - return core.BNIsValidOffset(self.handle, addr) - - def is_offset_readable(self, addr): - """ - ``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 - :rtype: bool - """ - return core.BNIsOffsetReadable(self.handle, addr) - - def is_offset_writable(self, addr): - """ - ``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 - :rtype: bool - """ - return core.BNIsOffsetWritable(self.handle, addr) - - def is_offset_executable(self, addr): - """ - ``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 - :rtype: bool - """ - return core.BNIsOffsetExecutable(self.handle, addr) - - def save(self, dest): - """ - ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. - - :param str dest: destination path and filename of file to be written - :return: boolean True on success, False on failure - :rtype: bool - """ - if isinstance(dest, FileAccessor): - return core.BNSaveToFile(self.handle, dest._cb) - return core.BNSaveToFilename(self.handle, str(dest)) - - def register_notification(self, notify): - cb = BinaryDataNotificationCallbacks(self, notify) - cb._register() - self.notifications[notify] = cb - - def unregister_notification(self, notify): - if notify in self.notifications: - self.notifications[notify]._unregister() - del self.notifications[notify] - - def add_function(self, platform, addr): - """ - ``add_function`` add a new function of the given ``platform`` at the virtual address ``addr`` - - :param Platform platform: Platform for the function to be added - :param int addr: virtual address of the function to be added - :rtype: None - :Example: - - >>> bv.add_function(bv.platform, 1) - >>> bv.functions - [<func: x86_64@0x1>] - - """ - core.BNAddFunctionForAnalysis(self.handle, platform.handle, addr) - - def add_entry_point(self, platform, addr): - """ - ``add_entry_point`` adds an virtual address to start analysis from for a given platform. - - :param Platform platform: Platform for the entry point analysis - :param int addr: virtual address to start analysis from - :rtype: None - :Example: - >>> bv.add_entry_point(bv.platform, 0xdeadbeef) - >>> - """ - core.BNAddEntryPointForAnalysis(self.handle, platform.handle, addr) - - def remove_function(self, func): - """ - ``remove_function`` removes the function ``func`` from the list of functions - - :param Function func: a Function object. - :rtype: None - :Example: - - >>> bv.functions - [<func: x86_64@0x1>] - >>> bv.remove_function(bv.functions[0]) - >>> bv.functions - [] - """ - core.BNRemoveAnalysisFunction(self.handle, func.handle) - - def create_user_function(self, platform, addr): - """ - ``create_user_function`` add a new *user* function of the given ``platform`` at the virtual address ``addr`` - - :param Platform platform: Platform for the function to be added - :param int addr: virtual address of the *user* function to be added - :rtype: None - :Example: - - >>> bv.create_user_function(bv.platform, 1) - >>> bv.functions - [<func: x86_64@0x1>] - - """ - core.BNCreateUserFunction(self.handle, platform.handle, addr) - - def remove_user_function(self, func): - """ - ``remove_user_function`` removes the *user* function ``func`` from the list of functions - - :param Function func: a Function object. - :rtype: None - :Example: - - >>> bv.functions - [<func: x86_64@0x1>] - >>> bv.remove_user_function(bv.functions[0]) - >>> bv.functions - [] - """ - core.BNRemoveUserFunction(self.handle, func.handle) - - def update_analysis(self): - """ - ``update_analysis`` asynchronously starts the analysis running and returns immediately. Analysis of BinaryViews - does not occur automatically, the user must start analysis by calling either ``update_analysis()`` or - ``update_analysis_and_wait()``. An analysis update **must** be run after changes are made which could change - analysis results such as adding functions. - - :rtype: None - """ - core.BNUpdateAnalysis(self.handle) - - def update_analysis_and_wait(self): - """ - ``update_analysis_and_wait`` blocking call to update the analysis, this call returns when the analysis is - complete. Analysis of BinaryViews does not occur automatically, the user must start analysis by calling either - ``update_analysis()`` or ``update_analysis_and_wait()``. An analysis update **must** be run after changes are - made which could change analysis results such as adding functions. - - :rtype: None - """ - class WaitEvent: - def __init__(self): - self.cond = threading.Condition() - self.done = False - - def complete(self): - self.cond.acquire() - self.done = True - self.cond.notify() - self.cond.release() - - def wait(self): - self.cond.acquire() - while not self.done: - self.cond.wait() - self.cond.release() - - wait = WaitEvent() - event = AnalysisCompletionEvent(self, lambda: wait.complete()) - core.BNUpdateAnalysis(self.handle) - wait.wait() - - def abort_analysis(self): - """ - ``abort_analysis`` will abort the currently running analysis. - - :rtype: None - """ - core.BNAbortAnalysis(self.handle) - - def define_data_var(self, addr, var_type): - """ - ``define_data_var`` defines a non-user data variable ``var_type`` at the virtual address ``addr``. - - :param int addr: virtual address to define the given data variable - :param Type var_type: type to be defined at the given virtual address - :rtype: None - :Example: - - >>> t = bv.parse_type_string("int foo") - >>> t - (<type: int32_t>, 'foo') - >>> bv.define_data_var(bv.entry_point, t[0]) - >>> - """ - core.BNDefineDataVariable(self.handle, addr, var_type.handle) - - def define_user_data_var(self, addr, var_type): - """ - ``define_data_var`` defines a user data variable ``var_type`` at the virtual address ``addr``. - - :param int addr: virtual address to define the given data variable - :param binaryninja.Type var_type: type to be defined at the given virtual address - :rtype: None - :Example: - - >>> t = bv.parse_type_string("int foo") - >>> t - (<type: int32_t>, 'foo') - >>> bv.define_user_data_var(bv.entry_point, t[0]) - >>> - """ - core.BNDefineUserDataVariable(self.handle, addr, var_type.handle) - - def undefine_data_var(self, addr): - """ - ``undefine_data_var`` removes the non-user data variable at the virtual address ``addr``. - - :param int addr: virtual address to define the data variable to be removed - :rtype: None - :Example: - - >>> bv.undefine_data_var(bv.entry_point) - >>> - """ - core.BNUndefineDataVariable(self.handle, addr) - - def undefine_user_data_var(self, addr): - """ - ``undefine_data_var`` removes the user data variable at the virtual address ``addr``. - - :param int addr: virtual address to define the data variable to be removed - :rtype: None - :Example: - - >>> bv.undefine_user_data_var(bv.entry_point) - >>> - """ - core.BNUndefineUserDataVariable(self.handle, addr) - - def get_data_var_at(self, addr): - """ - ``get_data_var_at`` returns the data type at a given virtual address. - - :param int addr: virtual address to get the data type from - :return: returns the DataVariable at the given virtual address, None on error. - :rtype: DataVariable - :Example: - - >>> t = bv.parse_type_string("int foo") - >>> bv.define_data_var(bv.entry_point, t[0]) - >>> bv.get_data_var_at(bv.entry_point) - <var 0x100001174: int32_t> - - """ - var = core.BNDataVariable() - if not core.BNGetDataVariableAtAddress(self.handle, addr, var): - return None - return DataVariable(var.address, Type(var.type), var.autoDiscovered) - - def get_function_at(self, platform, addr): - """ - ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: - - :param binaryninja.Platform platform: platform of the desired function - :param int addr: virtual address of the desired function - :return: returns a Function object or None for the function at the virtual address provided - :rtype: Function - :Example: - - >>> bv.get_function_at(bv.platform, bv.entry_point) - <func: x86_64@0x100001174> - >>> - """ - func = core.BNGetAnalysisFunction(self.handle, platform.handle, addr) - if func is None: - return None - return Function(self, func) - - def get_functions_at(self, addr): - """ - ``get_functions_at`` get a list of binaryninja.Function objects (one for each valid platform) at the given - virtual address. Binary Ninja does not limit the number of platforms in a given file thus there may be multiple - functions defined from different architectures at the same location. This API allows you to query all of valid - platforms. - - :param int addr: virtual address of the desired Function object list. - :return: a list of binaryninja.Function objects defined at the provided virtual address - :rtype: list(Function) - """ - count = ctypes.c_ulonglong(0) - funcs = core.BNGetAnalysisFunctionsForAddress(self.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(Function(self, core.BNNewFunctionReference(funcs[i]))) - core.BNFreeFunctionList(funcs, count.value) - return result - - def get_recent_function_at(self, addr): - func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) - if func is None: - return None - return Function(self, func) - - def get_basic_blocks_at(self, addr): - """ - ``get_basic_blocks_at`` get a list of :py:Class:`BasicBlock` objects which exist 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.BNGetBasicBlocksForAddress(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_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: - return None - return BasicBlock(self, block) - - def get_code_refs(self, addr, length = None): - """ - ``get_code_refs`` returns a list of ReferenceSource objects (xrefs or cross-references) that point to the provided virtual address. - - :param int addr: virtual address to query for references - :return: List of References for the given virtual address - :rtype: list(ReferenceSource) - :Example: - - >>> bv.get_code_refs(here) - [<ref: x86@0x4165ff>] - >>> - - """ - count = ctypes.c_ulonglong(0) - if length is None: - refs = core.BNGetCodeReferences(self.handle, addr, count) - else: - refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, count) - result = [] - for i in xrange(0, count.value): - if refs[i].func: - func = Function(self, core.BNNewFunctionReference(refs[i].func)) - else: - func = None - if refs[i].arch: - arch = Architecture(refs[i].arch) - else: - arch = None - addr = refs[i].addr - result.append(ReferenceSource(func, arch, addr)) - core.BNFreeCodeReferences(refs, count.value) - return result - - def get_symbol_at(self, addr): - """ - ``get_symbol_at`` returns the Symbol at the provided virtual address. - - :param int addr: virtual address to query for symbol - :return: Symbol for the given virtual address - :rtype: Symbol - :Example: - - >>> bv.get_symbol_at(bv.entry_point) - <FunctionSymbol: "_start" @ 0x100001174> - >>> - """ - sym = core.BNGetSymbolByAddress(self.handle, addr) - if sym is None: - return None - return Symbol(None, None, None, handle = sym) - - def get_symbol_by_raw_name(self, name): - """ - ``get_symbol_by_raw_name`` retrieves a Symbol object for the given a raw (mangled) name. - - :param str name: raw (mangled) name of Symbol to be retrieved - :return: Symbol object corresponding to the provided raw name - :rtype: Symbol - :Example: - - >>> bv.get_symbol_by_raw_name('?testf@Foobar@@SA?AW4foo@1@W421@@Z') - <FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100> - >>> - """ - sym = core.BNGetSymbolByRawName(self.handle, name) - if sym is None: - return None - return Symbol(None, None, None, handle = sym) - - def get_symbols_by_name(self, name): - """ - ``get_symbols_by_name`` retrieves a list of Symbol objects for the given symbol name. - - :param str name: name of Symbol object to be retrieved - :return: Symbol object corresponding to the provided name - :rtype: Symbol - :Example: - - >>> bv.get_symbols_by_name('?testf@Foobar@@SA?AW4foo@1@W421@@Z') - [<FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100>] - >>> - """ - count = ctypes.c_ulonglong(0) - syms = core.BNGetSymbolsByName(self.handle, name, count) - result = [] - for i in xrange(0, count.value): - result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) - core.BNFreeSymbolList(syms, count.value) - return result - - def get_symbols(self, start = None, length = None): - """ - ``get_symbols`` retrieves the list of all Symbol objects in the optionally provided range. - - :param int start: optional start virtual address - :param int length: optional length - :return: list of all Symbol objects, or those Symbol objects in the range of ``start``-``start+length`` - :rtype: list(Symbol) - :Example: - - >>> bv.get_symbols(0x1000200c, 1) - [<ImportAddressSymbol: "KERNEL32!IsProcessorFeaturePresent@IAT" @ 0x1000200c>] - >>> - """ - count = ctypes.c_ulonglong(0) - if start is None: - syms = core.BNGetSymbols(self.handle, count) - else: - syms = core.BNGetSymbolsInRange(self.handle, start, length, count) - result = [] - for i in xrange(0, count.value): - result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) - core.BNFreeSymbolList(syms, count.value) - return result - - def get_symbols_of_type(self, sym_type, start = None, length = None): - """ - ``get_symbols_of_type`` retrieves a list of all Symbol objects of the provided symbol type in the optionally - provided range. - - :param SymbolType sym_type: A Symbol type: :py:Class:`Symbol`. - :param int start: optional start virtual address - :param int length: optional length - :return: list of all Symbol objects of type sym_type, or those Symbol objects in the range of ``start``-``start+length`` - :rtype: list(Symbol) - :Example: - - >>> bv.get_symbols_of_type(core.ImportAddressSymbol, 0x10002028, 1) - [<ImportAddressSymbol: "KERNEL32!GetCurrentThreadId@IAT" @ 0x10002028>] - >>> - """ - if isinstance(sym_type, str): - sym_type = core.BNSymbolType_by_name[sym_type] - count = ctypes.c_ulonglong(0) - if start is None: - syms = core.BNGetSymbolsOfType(self.handle, sym_type, count) - else: - syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) - result = [] - for i in xrange(0, count.value): - result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) - core.BNFreeSymbolList(syms, count.value) - return result - - def define_auto_symbol(self, sym): - """ - ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. - - :param Symbol sym: the symbol to define - :rtype: None - """ - core.BNDefineAutoSymbol(self.handle, sym.handle) - - def define_auto_symbol_and_var_or_function(self, sym, sym_type, platform = None): - """ - ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. - - :param Symbol sym: the symbol to define - :rtype: None - """ - if platform is None: - platform = self.platform - if platform is not None: - platform = platform.handle - if sym_type is not None: - sym_type = sym_type.handle - core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, platform, sym.handle, sym_type) - - def undefine_auto_symbol(self, sym): - """ - ``undefine_auto_symbol`` removes a symbol from the internal list of automatically discovered Symbol objects. - - :param Symbol sym: the symbol to undefine - :rtype: None - """ - core.BNUndefineAutoSymbol(self.handle, sym.handle) - - def define_user_symbol(self, sym): - """ - ``define_user_symbol`` adds a symbol to the internal list of user added Symbol objects. - - :param Symbol sym: the symbol to define - :rtype: None - """ - core.BNDefineUserSymbol(self.handle, sym.handle) - - def undefine_user_symbol(self, sym): - """ - ``undefine_user_symbol`` removes a symbol from the internal list of user added Symbol objects. - - :param Symbol sym: the symbol to undefine - :rtype: None - """ - core.BNUndefineUserSymbol(self.handle, sym.handle) - - def define_imported_function(self, import_addr_sym, func): - """ - ``define_imported_function`` defines an imported Function ``func`` with a ImportedFunctionSymbol type. - - :param Symbol import_addr_sym: A Symbol object with type ImportedFunctionSymbol - :param Function func: A Function object to define as an imported function - :rtype: None - """ - core.BNDefineImportedFunction(self.handle, import_addr_sym.handle, func.handle) - - def is_never_branch_patch_available(self, arch, addr): - """ - ``is_never_branch_patch_available`` queries the architecture plugin to determine if the instruction at the - instruction at ``addr`` can be made to **never branch**. The actual logic of which is implemented in the - ``perform_is_never_branch_patch_available`` in the corresponding architecture. - - :param Architecture arch: the architecture for the current view - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012ed) - 'test eax, eax' - >>> bv.is_never_branch_patch_available(bv.arch, 0x100012ed) - False - >>> bv.get_disassembly(0x100012ef) - 'jg 0x100012f5' - >>> bv.is_never_branch_patch_available(bv.arch, 0x100012ef) - True - >>> - """ - return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) - - def is_always_branch_patch_available(self, arch, addr): - """ - ``is_always_branch_patch_available`` queries the architecture plugin to determine if the - instruction at ``addr`` can be made to **always branch**. The actual logic of which is implemented in the - ``perform_is_always_branch_patch_available`` in the corresponding architecture. - - :param Architecture arch: the architecture for the current view - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012ed) - 'test eax, eax' - >>> bv.is_always_branch_patch_available(bv.arch, 0x100012ed) - False - >>> bv.get_disassembly(0x100012ef) - 'jg 0x100012f5' - >>> bv.is_always_branch_patch_available(bv.arch, 0x100012ef) - True - >>> - """ - return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) - - def is_invert_branch_patch_available(self, arch, addr): - """ - ``is_invert_branch_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` - is a branch that can be inverted. The actual logic of which is implemented in the - ``perform_is_invert_branch_patch_available`` in the corresponding architecture. - - :param Architecture arch: the architecture for the current view - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012ed) - 'test eax, eax' - >>> bv.is_invert_branch_patch_available(bv.arch, 0x100012ed) - False - >>> bv.get_disassembly(0x100012ef) - 'jg 0x100012f5' - >>> bv.is_invert_branch_patch_available(bv.arch, 0x100012ef) - True - >>> - """ - return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) - - def is_skip_and_return_zero_patch_available(self, arch, addr): - """ - ``is_skip_and_return_zero_patch_available`` queries the architecture plugin to determine if the - instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return zero. The actual - logic of which is implemented in the ``perform_is_skip_and_return_zero_patch_available`` in the corresponding - architecture. - - :param Architecture arch: the architecture for the current view - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012f6) - 'mov dword [0x10003020], eax' - >>> bv.is_skip_and_return_zero_patch_available(bv.arch, 0x100012f6) - False - >>> bv.get_disassembly(0x100012fb) - 'call 0x10001629' - >>> bv.is_skip_and_return_zero_patch_available(bv.arch, 0x100012fb) - True - >>> - """ - return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) - - def is_skip_and_return_value_patch_available(self, arch, addr): - """ - ``is_skip_and_return_value_patch_available`` queries the architecture plugin to determine if the - instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return a value. The actual - logic of which is implemented in the ``perform_is_skip_and_return_value_patch_available`` in the corresponding - architecture. - - :param Architecture arch: the architecture for the current view - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012f6) - 'mov dword [0x10003020], eax' - >>> bv.is_skip_and_return_value_patch_available(bv.arch, 0x100012f6) - False - >>> bv.get_disassembly(0x100012fb) - 'call 0x10001629' - >>> bv.is_skip_and_return_value_patch_available(bv.arch, 0x100012fb) - True - >>> - """ - return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) - - def convert_to_nop(self, arch, addr): - """ - ``convert_to_nop`` converts the instruction at virtual address ``addr`` to a nop of the provided architecture. - - .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ - file must be saved in order to preserve the changes made. - - :param Architecture arch: architecture of the current BinaryView - :param int addr: virtual address of the instruction to conver to nops - :return: True on success, False on falure. - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012fb) - 'call 0x10001629' - >>> bv.convert_to_nop(bv.arch, 0x100012fb) - True - >>> #The above 'call' instruction is 5 bytes, a nop in x86 is 1 byte, - >>> # thus 5 nops are used: - >>> bv.get_disassembly(0x100012fb) - 'nop' - >>> bv.get_next_disassembly() - 'nop' - >>> bv.get_next_disassembly() - 'nop' - >>> bv.get_next_disassembly() - 'nop' - >>> bv.get_next_disassembly() - 'nop' - >>> bv.get_next_disassembly() - 'mov byte [ebp-0x1c], al' - """ - return core.BNConvertToNop(self.handle, arch.handle, addr) - - def always_branch(self, arch, addr): - """ - ``always_branch`` convert the instruction of architecture ``arch`` at the virtual address ``addr`` to an - unconditional branch. - - .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ - file must be saved in order to preserve the changes made. - - :param Architecture arch: architecture of the current binary view - :param int addr: virtual address of the instruction to be modified - :return: True on success, False on falure. - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x100012ef) - 'jg 0x100012f5' - >>> bv.always_branch(bv.arch, 0x100012ef) - True - >>> bv.get_disassembly(0x100012ef) - 'jmp 0x100012f5' - >>> - """ - return core.BNAlwaysBranch(self.handle, arch.handle, addr) - - def never_branch(self, arch, addr): - """ - ``never_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to - a fall through. - - .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ - file must be saved in order to preserve the changes made. - - :param Architecture arch: architecture of the current binary view - :param int addr: virtual address of the instruction to be modified - :return: True on success, False on falure. - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x1000130e) - 'jne 0x10001317' - >>> bv.never_branch(bv.arch, 0x1000130e) - True - >>> bv.get_disassembly(0x1000130e) - 'nop' - >>> - """ - return core.BNConvertToNop(self.handle, arch.handle, addr) - - def invert_branch(self, arch, addr): - """ - ``invert_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to the - inverse branch. - - .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary - file must be saved in order to preserve the changes made. - - :param Architecture arch: architecture of the current binary view - :param int addr: virtual address of the instruction to be modified - :return: True on success, False on falure. - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x1000130e) - 'je 0x10001317' - >>> bv.invert_branch(bv.arch, 0x1000130e) - True - >>> - >>> bv.get_disassembly(0x1000130e) - 'jne 0x10001317' - >>> - """ - return core.BNInvertBranch(self.handle, arch.handle, addr) - - def skip_and_return_value(self, arch, addr, value): - """ - ``skip_and_return_value`` convert the ``call`` instruction of architecture ``arch`` at the virtual address - ``addr`` to the equivilent of returning a value. - - :param Architecture arch: architecture of the current binary view - :param int addr: virtual address of the instruction to be modified - :param int value: value to make the instruction *return* - :return: True on success, False on falure. - :rtype: bool - :Example: - - >>> bv.get_disassembly(0x1000132a) - 'call 0x1000134a' - >>> bv.skip_and_return_value(bv.arch, 0x1000132a, 42) - True - >>> #The return value from x86 functions is stored in eax thus: - >>> bv.get_disassembly(0x1000132a) - 'mov eax, 0x2a' - >>> - """ - return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) - - def get_instruction_length(self, arch, addr): - """ - ``get_instruction_length`` returns the number of bytes in the instruction of Architecture ``arch`` at the virtual - address ``addr`` - - :param Architecture arch: architecture of the current binary view - :param int addr: virtual address of the instruction query - :return: Number of bytes in instruction - :rtype: int - :Example: - - >>> bv.get_disassembly(0x100012f1) - 'xor eax, eax' - >>> bv.get_instruction_length(bv.arch, 0x100012f1) - 2L - >>> - """ - return core.BNGetInstructionLength(self.handle, arch.handle, addr) - - def notify_data_written(self, offset, length): - core.BNNotifyDataWritten(self.handle, offset, length) - - def notify_data_inserted(self, offset, length): - core.BNNotifyDataInserted(self.handle, offset, length) - - def notify_data_removed(self, offset, length): - core.BNNotifyDataRemoved(self.handle, offset, length) - - def get_strings(self, start = None, length = None): - """ - ``get_strings`` returns a list of strings defined in the binary in the optional virtual address range: - ``start-(start+length)`` - - :param int start: optional virtual address to start the string list from, defaults to start of the binary - :param int length: optional length range to return strings from, defaults to length of the binary - :return: a list of all strings or a list of strings defined between ``start`` and ``start+length`` - :rtype: list(str()) - :Example: - - >>> bv.get_strings(0x1000004d, 1) - [<AsciiString: 0x1000004d, len 0x2c>] - >>> - """ - count = ctypes.c_ulonglong(0) - if start is None: - strings = core.BNGetStrings(self.handle, count) - else: - strings = core.BNGetStringsInRange(self.handle, start, length, count) - result = [] - for i in xrange(0, count.value): - result.append(StringReference(core.BNStringType_names[strings[i].type], strings[i].start, strings[i].length)) - core.BNFreeStringReferenceList(strings) - return result - - def add_analysis_completion_event(self, callback): - """ - ``add_analysis_completion_event`` sets up a call back function to be called when analysis has been completed. - This is helpful when using asynchronously analysis. - - :param callable() callback: A function to be called with no parameters when analysis has completed. - :return: An initialized AnalysisCompletionEvent object. - :rtype: AnalysisCompletionEvent - :Example: - - >>> def completionEvent(): - ... print "done" - ... - >>> bv.add_analysis_completion_event(completionEvent) - <binaryninja.AnalysisCompletionEvent object at 0x10a2c9f10> - >>> bv.update_analysis() - done - >>> - """ - return AnalysisCompletionEvent(self, callback) - - def get_next_function_start_after(self, addr): - """ - ``get_next_function_start_after`` returns the virtual address of the Function that occurs after the virtual address - ``addr`` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the next Function - :rtype: int - :Example: - - >>> bv.get_next_function_start_after(bv.entry_point) - 268441061L - >>> hex(bv.get_next_function_start_after(bv.entry_point)) - '0x100015e5L' - >>> hex(bv.get_next_function_start_after(0x100015e5)) - '0x10001629L' - >>> hex(bv.get_next_function_start_after(0x10001629)) - '0x1000165eL' - >>> - """ - return core.BNGetNextFunctionStartAfterAddress(self.handle, addr) - - def get_next_basic_block_start_after(self, addr): - """ - ``get_next_basic_block_start_after`` returns the virtual address of the BasicBlock that occurs after the virtual - address ``addr`` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the next BasicBlock - :rtype: int - :Example: - - >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) - '0x100014a8L' - >>> hex(bv.get_next_basic_block_start_after(0x100014a8)) - '0x100014adL' - >>> - """ - return core.BNGetNextBasicBlockStartAfterAddress(self.handle, addr) - - def get_next_data_after(self, addr): - """ - ``get_next_data_after`` retrieves the virtual address of the next non-code byte. - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the next data byte which is data, not code - :rtype: int - :Example: - - >>> hex(bv.get_next_data_after(0x10000000)) - '0x10000001L' - """ - return core.BNGetNextDataAfterAddress(self.handle, addr) - - def get_next_data_var_after(self, addr): - """ - ``get_next_data_var_after`` retrieves the next virtual address of the next :py:Class:`DataVariable` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the next :py:Class:`DataVariable` - :rtype: int - :Example: - - >>> hex(bv.get_next_data_var_after(0x10000000)) - '0x1000003cL' - >>> bv.get_data_var_at(0x1000003c) - <var 0x1000003c: int32_t> - >>> - """ - return core.BNGetNextDataVariableAfterAddress(self.handle, addr) - - def get_previous_function_start_before(self, addr): - """ - ``get_previous_function_start_before`` returns the virtual address of the Function that occurs prior to the - virtual address provided - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the previous Function - :rtype: int - :Example: - - >>> hex(bv.entry_point) - '0x1000149fL' - >>> hex(bv.get_next_function_start_after(bv.entry_point)) - '0x100015e5L' - >>> hex(bv.get_previous_function_start_before(0x100015e5)) - '0x1000149fL' - >>> - """ - return core.BNGetPreviousFunctionStartBeforeAddress(self.handle, addr) - - def get_previous_basic_block_start_before(self, addr): - """ - ``get_previous_basic_block_start_before`` returns the virtual address of the BasicBlock that occurs prior to the - provided virtual address - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the previous BasicBlock - :rtype: int - :Example: - - >>> hex(bv.entry_point) - '0x1000149fL' - >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) - '0x100014a8L' - >>> hex(bv.get_previous_basic_block_start_before(0x100014a8)) - '0x1000149fL' - >>> - """ - return core.BNGetPreviousBasicBlockStartBeforeAddress(self.handle, addr) - - def get_previous_basic_block_end_before(self, addr): - """ - ``get_previous_basic_block_end_before`` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the previous BasicBlock end - :rtype: int - :Example: - >>> hex(bv.entry_point) - '0x1000149fL' - >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) - '0x100014a8L' - >>> hex(bv.get_previous_basic_block_end_before(0x100014a8)) - '0x100014a8L' - """ - return core.BNGetPreviousBasicBlockEndBeforeAddress(self.handle, addr) - - def get_previous_data_before(self, addr): - """ - ``get_previous_data_before`` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the previous data (non-code) byte - :rtype: int - :Example: - - >>> hex(bv.get_previous_data_before(0x1000001)) - '0x1000000L' - >>> - """ - return core.BNGetPreviousDataBeforeAddress(self.handle, addr) - - def get_previous_data_var_before(self, addr): - """ - ``get_previous_data_var_before`` - - :param int addr: the virtual address to start looking from. - :return: the virtual address of the previous :py:Class:`DataVariable` - :rtype: int - :Example: - - >>> hex(bv.get_previous_data_var_before(0x1000003c)) - '0x10000000L' - >>> bv.get_data_var_at(0x10000000) - <var 0x10000000: int16_t> - >>> - """ - return core.BNGetPreviousDataVariableBeforeAddress(self.handle, addr) - - def get_linear_disassembly_position_at(self, addr, settings): - """ - ``get_linear_disassembly_position_at`` instantiates a :py:class:`LinearDisassemblyPosition` object for use in - :py:method:`get_previous_linear_disassembly_lines` or :py:method:`get_next_linear_disassembly_lines`. - - :param int addr: virtual address of linear disassembly position - :param DisassemblySettings settings: an instantiated :py:class:`DisassemblySettings` object - :return: An instantied :py:class:`LinearDisassemblyPosition` object for the provided virtual address - :rtype: LinearDisassemblyPosition - :Example: - - >>> settings = DisassemblySettings() - >>> pos = bv.get_linear_disassembly_position_at(0x1000149f, settings) - >>> lines = bv.get_previous_linear_disassembly_lines(pos, settings) - >>> lines - [<0x1000149a: pop esi>, <0x1000149b: pop ebp>, - <0x1000149c: retn 0xc>, <0x1000149f: >] - """ - if settings is not None: - settings = settings.handle - pos = core.BNGetLinearDisassemblyPositionForAddress(self.handle, addr, settings) - func = None - block = None - if pos.function: - func = Function(self, pos.function) - if pos.block: - block = BasicBlock(self, pos.block) - return LinearDisassemblyPosition(func, block, pos.address) - - def _get_linear_disassembly_lines(self, api, pos, settings): - pos_obj = core.BNLinearDisassemblyPosition() - pos_obj.function = None - pos_obj.block = None - pos_obj.address = pos.address - if pos.function is not None: - pos_obj.function = core.BNNewFunctionReference(pos.function.handle) - if pos.block is not None: - pos_obj.block = core.BNNewBasicBlockReference(pos.block.handle) - - if settings is not None: - settings = settings.handle - - count = ctypes.c_ulonglong(0) - lines = api(self.handle, pos_obj, settings, count) - - result = [] - for i in xrange(0, count.value): - func = None - block = None - if lines[i].function: - func = Function(self, core.BNNewFunctionReference(lines[i].function)) - if lines[i].block: - block = BasicBlock(self, core.BNNewBasicBlockReference(lines[i].block)) - addr = lines[i].contents.addr - tokens = [] - for j in xrange(0, lines[i].contents.count): - token_type = core.BNInstructionTextTokenType_names[lines[i].contents.tokens[j].type] - text = lines[i].contents.tokens[j].text - value = lines[i].contents.tokens[j].value - size = lines[i].contents.tokens[j].size - operand = lines[i].contents.tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - contents = DisassemblyTextLine(addr, tokens) - result.append(LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) - - func = None - block = None - if pos_obj.function: - func = Function(self, pos_obj.function) - if pos_obj.block: - block = BasicBlock(self, pos_obj.block) - pos.function = func - pos.block = block - pos.address = pos_obj.address - - core.BNFreeLinearDisassemblyLines(lines, count.value) - return result - - def get_previous_linear_disassembly_lines(self, pos, settings): - """ - ``get_previous_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the - previous disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called - repeatedly to get more lines of linear disassembly. - - :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from - :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly - :return: a list of :py:class:`LinearDisassemblyLine` objects for the previous lines. - :Example: - - >>> settings = DisassemblySettings() - >>> pos = bv.get_linear_disassembly_position_at(0x1000149a, settings) - >>> bv.get_previous_linear_disassembly_lines(pos, settings) - [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] - >>> bv.get_previous_linear_disassembly_lines(pos, settings) - [<0x10001483: xor eax, eax {0x0}>, ... , <0x10001488: >] - """ - return self._get_linear_disassembly_lines(core.BNGetPreviousLinearDisassemblyLines, pos, settings) - - def get_next_linear_disassembly_lines(self, pos, settings): - """ - ``get_next_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the - next disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called - repeatedly to get more lines of linear disassembly. - - :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from - :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly - :return: a list of :py:class:`LinearDisassemblyLine` objects for the next lines. - :Example: - - >>> settings = DisassemblySettings() - >>> pos = bv.get_linear_disassembly_position_at(0x10001483, settings) - >>> bv.get_next_linear_disassembly_lines(pos, settings) - [<0x10001483: xor eax, eax {0x0}>, <0x10001485: inc eax {0x1}>, ... , <0x10001488: >] - >>> bv.get_next_linear_disassembly_lines(pos, settings) - [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] - >>> - """ - return self._get_linear_disassembly_lines(core.BNGetNextLinearDisassemblyLines, pos, settings) - - def get_linear_disassembly(self, settings): - """ - ``get_linear_disassembly`` gets an iterator for all lines in the linear disassembly of the view for the given - disassembly settings. - - .. note:: linear_disassembly doesn't just return disassembly it will return a single line from the linear view,\ - and thus will contain both data views, and disassembly. - - :param DisassemblySettings settings: instance specifying the desired output formatting. - :return: An iterator containing formatted dissassembly lines. - :rtype: LinearDisassemblyIterator - :Example: - - >>> settings = DisassemblySettings() - >>> lines = bv.get_linear_disassembly(settings) - >>> for line in lines: - ... print line - ... break - ... - cf fa ed fe 07 00 00 01 ........ - """ - class LinearDisassemblyIterator(object): - def __init__(self, view, settings): - self.view = view - self.settings = settings - - def __iter__(self): - pos = self.view.get_linear_disassembly_position_at(self.view.start, self.settings) - while True: - lines = self.view.get_next_linear_disassembly_lines(pos, self.settings) - if len(lines) == 0: - break - for line in lines: - yield line - - return iter(LinearDisassemblyIterator(self, settings)) - - def parse_type_string(self, text): - """ - ``parse_type_string`` converts `C-style` string into a :py:Class:`Type`. - - :param str text: `C-style` string of type to create - :return: A tuple of a :py:Class:`Type` and string type name - :rtype: tuple(Type, str) - :Example: - - >>> bv.parse_type_string("int foo") - (<type: int32_t>, 'foo') - >>> - """ - result = core.BNNameAndType() - errors = ctypes.c_char_p() - if not core.BNParseTypeString(self.handle, text, result, errors): - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise SyntaxError, error_str - type_obj = Type(core.BNNewTypeReference(result.type)) - name = result.name - core.BNFreeNameAndType(result) - return type_obj, name - - def get_type_by_name(self, name): - """ - ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` - - :param str name: Type name to lookup - :return: A :py:Class:`Type` or None if the type does not exist - :rtype: Type or None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - >>> - """ - obj = core.BNGetAnalysisTypeByName(self.handle, name) - if not obj: - return None - return Type(obj) - - def is_type_auto_defined(self, name): - """ - ``is_type_auto_defined`` queries the user type list of name. If name is not in the *user* type list then the name - is considered an *auto* type. - - :param str name: Name of type to query - :return: True if the type is not a *user* type. False if the type is a *user* type. - :Example: - >>> bv.is_type_auto_defined("foo") - True - >>> bv.define_user_type("foo", bv.parse_type_string("struct {int x,y;}")[0]) - >>> bv.is_type_auto_defined("foo") - False - >>> - """ - return core.BNIsAnalysisTypeAutoDefined(self.handle, name) - - def define_type(self, name, type_obj): - """ - ``define_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of types for - the current :py:Class:`BinaryView`. - - :param str name: Name of the type to be registered - :param Type type_obj: Type object to be registered - :rtype: None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - """ - core.BNDefineAnalysisType(self.handle, name, type_obj.handle) - - def define_user_type(self, name, type_obj): - """ - ``define_user_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of user - types for the current :py:Class:`BinaryView`. - - :param str name: Name of the user type to be registered - :param Type type_obj: Type object to be registered - :rtype: None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_user_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - """ - core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) - - def undefine_type(self, name): - """ - ``undefine_type`` removes a :py:Class:`Type` from the global list of types for the current :py:Class:`BinaryView` - - :param str name: Name of type to be undefined - :rtype: None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - >>> bv.undefine_type(name) - >>> bv.get_type_by_name(name) - >>> - """ - core.BNUndefineAnalysisType(self.handle, name) - - def undefine_user_type(self, name): - """ - ``undefine_user_type`` removes a :py:Class:`Type` from the global list of user types for the current - :py:Class:`BinaryView` - - :param str name: Name of user type to be undefined - :rtype: None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - >>> bv.undefine_type(name) - >>> bv.get_type_by_name(name) - >>> - """ - core.BNUndefineUserAnalysisType(self.handle, name) - - def find_next_data(self, start, data, flags = 0): - """ - ``find_next_data`` searchs for the bytes in data starting at the virtual address ``start`` either, case-sensitive, - or case-insensitive. - - :param int start: virtual address to start searching from. - :param str data: bytes to search for - :param FindFlags flags: case-sensitivity flag, one of the following: - - ==================== ====================== - FindFlags Description - ==================== ====================== - NoFindFlags Case-sensitive find - FindCaseInsensitive Case-insensitive find - ==================== ====================== - """ - buf = DataBuffer(str(data)) - result = ctypes.c_ulonglong() - if not core.BNFindNextData(self.handle, start, buf.handle, result, flags): - 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 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 add_auto_segment(self, start, length, data_offset, data_length, flags): - core.BNAddAutoSegment(self.handle, start, length, data_offset, data_length, flags) - - def remove_auto_segment(self, start, length): - core.BNRemoveAutoSegment(self.handle, start, length) - - def add_user_segment(self, start, length, data_offset, data_length, flags): - core.BNAddUserSegment(self.handle, start, length, data_offset, data_length, flags) - - def remove_user_segment(self, start, length): - core.BNRemoveUserSegment(self.handle, start, length) - - def get_segment_at(self, addr): - segment = core.BNSegment() - if not core.BNGetSegmentAt(self.handle, addr, segment): - return None - result = Segment(segment.start, segment.length, segment.dataOffset, segment.dataLength, - segment.flags) - return result - - def add_auto_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", - info_section = "", info_data = 0): - core.BNAddAutoSection(self.handle, name, start, length, type, align, entry_size, linked_section, - info_section, info_data) - - def remove_auto_section(self, name): - core.BNRemoveAutoSection(self.handle, name) - - def add_user_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", - info_section = "", info_data = 0): - core.BNAddUserSection(self.handle, name, start, length, type, align, entry_size, linked_section, - info_section, info_data) - - def remove_user_section(self, name): - core.BNRemoveUserSection(self.handle, name) - - def get_sections_at(self, addr): - count = ctypes.c_ulonglong(0) - section_list = core.BNGetSectionsAt(self.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start, - section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, - section_list[i].infoData, section_list[i].align, section_list[i].entrySize)) - core.BNFreeSectionList(section_list, count.value) - return result - - def get_section_by_name(self, name): - section = core.BNSection() - if not core.BNGetSectionByName(self.handle, name, section): - return None - result = Section(section.name, section.type, section.start, section.length, section.linkedSection, - section.infoSection, section.infoData, section.align, section.entrySize) - core.BNFreeSection(section) - return result - - def get_unique_section_names(self, name_list): - incoming_names = (ctypes.c_char_p * len(name_list))() - for i in xrange(0, len(name_list)): - incoming_names[i] = name_list[i] - outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) - result = [] - for i in xrange(0, len(name_list)): - result.append(str(outgoing_names[i])) - core.BNFreeStringList(outgoing_names, len(name_list)) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class BinaryReader(object): - """ - ``class BinaryReader`` is a convenience class for reading binary data. - - BinaryReader can be instantiated as follows and the rest of the document will start from this context :: - - >>> from binaryninja import * - >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") - >>> br = BinaryReader(bv) - >>> hex(br.read32()) - '0xfeedfacfL' - >>> - - Or using the optional endian parameter :: - - >>> from binaryninja import * - >>> br = BinaryReader(bv, core.BigEndian) - >>> hex(br.read32()) - '0xcffaedfeL' - >>> - """ - def __init__(self, view, endian = None): - self.handle = core.BNCreateBinaryReader(view.handle) - if endian is None: - core.BNSetBinaryReaderEndianness(self.handle, view.endianness) - else: - core.BNSetBinaryReaderEndianness(self.handle, endian) - - def __del__(self): - core.BNFreeBinaryReader(self.handle) - - @property - def endianness(self): - """ - The Endianness to read data. (read/write) - - :getter: returns the endianness of the reader - :setter: sets the endianness of the reader (BigEndian or LittleEndian) - :type: Endianness - """ - return core.BNGetBinaryReaderEndianness(self.handle) - - @endianness.setter - def endianness(self, value): - core.BNSetBinaryReaderEndianness(self.handle, value) - - @property - def offset(self): - """ - The current read offset (read/write). - - :getter: returns the current internal offset - :setter: sets the internal offset - :type: int - """ - return core.BNGetReaderPosition(self.handle) - - @offset.setter - def offset(self, value): - core.BNSeekBinaryReader(self.handle, value) - - @property - def eof(self): - """ - Is end of file (read-only) - - :getter: returns boolean, true if end of file, false otherwise - :type: bool - """ - return core.BNIsEndOfFile(self.handle) - - def read(self, length): - """ - ``read`` returns ``length`` bytes read from the current offset, adding ``length`` to offset. - - :param int length: number of bytes to read. - :return: ``length`` bytes from current offset - :rtype: str, or None on failure - :Example: - - >>> br.read(8) - '\\xcf\\xfa\\xed\\xfe\\x07\\x00\\x00\\x01' - >>> - """ - dest = ctypes.create_string_buffer(length) - if not core.BNReadData(self.handle, dest, length): - return None - return dest.raw - - def read8(self): - """ - ``read8`` returns a one byte integer from offet incrementing the offset. - - :return: byte at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> br.read8() - 207 - >>> - """ - result = ctypes.c_ubyte() - if not core.BNRead8(self.handle, result): - return None - return result.value - - def read16(self): - """ - ``read16`` returns a two byte integer from offet incrementing the offset by two, using specified endianness. - - :return: a two byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read16()) - '0xfacf' - >>> - """ - result = ctypes.c_ushort() - if not core.BNRead16(self.handle, result): - return None - return result.value - - def read32(self): - """ - ``read32`` returns a four byte integer from offet incrementing the offset by four, using specified endianness. - - :return: a four byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read32()) - '0xfeedfacfL' - >>> - """ - result = ctypes.c_uint() - if not core.BNRead32(self.handle, result): - return None - return result.value - - def read64(self): - """ - ``read64`` returns an eight byte integer from offet incrementing the offset by eight, using specified endianness. - - :return: an eight byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read64()) - '0x1000007feedfacfL' - >>> - """ - result = ctypes.c_ulonglong() - if not core.BNRead64(self.handle, result): - return None - return result.value - - def read16le(self): - """ - ``read16le`` returns a two byte little endian integer from offet incrementing the offset by two. - - :return: a two byte integer at offset. - :rtype: int, or None on failure - :Exmaple: - - >>> br.seek(0x100000000) - >>> hex(br.read16le()) - '0xfacf' - >>> - """ - result = self.read(2) - if (result is None) or (len(result) != 2): - return None - return struct.unpack("<H", result)[0] - - def read32le(self): - """ - ``read32le`` returns a four byte little endian integer from offet incrementing the offset by four. - - :return: a four byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read32le()) - '0xfeedfacf' - >>> - """ - result = self.read(4) - if (result is None) or (len(result) != 4): - return None - return struct.unpack("<I", result)[0] - - def read64le(self): - """ - ``read64le`` returns an eight byte little endian integer from offet incrementing the offset by eight. - - :return: a eight byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read64le()) - '0x1000007feedfacf' - >>> - """ - result = self.read(8) - if (result is None) or (len(result) != 8): - return None - return struct.unpack("<Q", result)[0] - - def read16be(self): - """ - ``read16be`` returns a two byte big endian integer from offet incrementing the offset by two. - - :return: a two byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read16be()) - '0xcffa' - >>> - """ - result = self.read(2) - if (result is None) or (len(result) != 2): - return None - return struct.unpack(">H", result)[0] - - def read32be(self): - """ - ``read32be`` returns a four byte big endian integer from offet incrementing the offset by four. - - :return: a four byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read32be()) - '0xcffaedfe' - >>> - """ - result = self.read(4) - if (result is None) or (len(result) != 4): - return None - return struct.unpack(">I", result)[0] - - def read64be(self): - """ - ``read64be`` returns an eight byte big endian integer from offet incrementing the offset by eight. - - :return: a eight byte integer at offset. - :rtype: int, or None on failure - :Example: - - >>> br.seek(0x100000000) - >>> hex(br.read64be()) - '0xcffaedfe07000001L' - """ - result = self.read(8) - if (result is None) or (len(result) != 8): - return None - return struct.unpack(">Q", result)[0] - - def seek(self, offset): - """ - ``seek`` update internal offset to ``offset``. - - :param int offset: offset to set the internal offset to - :rtype: None - :Example: - - >>> hex(br.offset) - '0x100000008L' - >>> br.seek(0x100000000) - >>> hex(br.offset) - '0x100000000L' - >>> - """ - core.BNSeekBinaryReader(self.handle, offset) - - def seek_relative(self, offset): - """ - ``seek_relative`` updates the internal offset by ``offset``. - - :param int offset: offset to add to the internal offset - :rtype: None - :Example: - - >>> hex(br.offset) - '0x100000008L' - >>> br.seek_relative(-8) - >>> hex(br.offset) - '0x100000000L' - >>> - """ - core.BNSeekBinaryReaderRelative(self.handle, offset) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class BinaryWriter(object): - """ - ``class BinaryWriter`` is a convenience class for writing binary data. - - BinaryWriter can be instantiated as follows and the rest of the document will start from this context :: - - >>> from binaryninja import * - >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") - >>> br = BinaryReader(bv) - >>> bw = BinaryWriter(bv) - >>> - - Or using the optional endian parameter :: - - >>> from binaryninja import * - >>> br = BinaryReader(bv, core.BigEndian) - >>> bw = BinaryWriter(bv, core.BigEndian) - >>> - """ - def __init__(self, view, endian = None): - self.handle = core.BNCreateBinaryWriter(view.handle) - if endian is None: - core.BNSetBinaryWriterEndianness(self.handle, view.endianness) - else: - core.BNSetBinaryWriterEndianness(self.handle, endian) - - def __del__(self): - core.BNFreeBinaryWriter(self.handle) - - @property - def endianness(self): - """ - The Endianness to written data. (read/write) - - :getter: returns the endianness of the reader - :setter: sets the endianness of the reader (BigEndian or LittleEndian) - :type: Endianness - """ - return core.BNGetBinaryWriterEndianness(self.handle) - - @endianness.setter - def endianness(self, value): - core.BNSetBinaryWriterEndianness(self.handle, value) - - @property - def offset(self): - """ - The current write offset (read/write). - - :getter: returns the current internal offset - :setter: sets the internal offset - :type: int - """ - return core.BNGetWriterPosition(self.handle) - - @offset.setter - def offset(self, value): - core.BNSeekBinaryWriter(self.handle, value) - - def write(self, value): - """ - ``write`` writes ``len(value)`` bytes to the internal offset, without regard to endianness. - - :param str value: bytes to be written at current offset - :return: boolean True on success, False on failure. - :rtype: bool - :Example: - - >>> bw.write("AAAA") - True - >>> br.read(4) - 'AAAA' - >>> - """ - value = str(value) - buf = ctypes.create_string_buffer(len(value)) - ctypes.memmove(buf, value, len(value)) - return core.BNWriteData(self.handle, buf, len(value)) - - def write8(self, value): - """ - ``write8`` lowest order byte from the integer ``value`` to the current offset. - - :param str value: bytes to be written at current offset - :return: boolean - :rtype: int - :Example: - - >>> bw.write8(0x42) - True - >>> br.read(1) - 'B' - >>> - """ - return core.BNWrite8(self.handle, value) - - def write16(self, value): - """ - ``write16`` writes the lowest order two bytes from the integer ``value`` to the current offset, using internal endianness. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - return core.BNWrite16(self.handle, value) - - def write32(self, value): - """ - ``write32`` writes the lowest order four bytes from the integer ``value`` to the current offset, using internal endianness. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - return core.BNWrite32(self.handle, value) - - def write64(self, value): - """ - ``write64`` writes the lowest order eight bytes from the integer ``value`` to the current offset, using internal endianness. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - return core.BNWrite64(self.handle, value) - - def write16le(self, value): - """ - ``write16le`` writes the lowest order two bytes from the little endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack("<H", value) - return self.write(value) - - def write32le(self, value): - """ - ``write32le`` writes the lowest order four bytes from the little endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack("<I", value) - return self.write(value) - - def write64le(self, value): - """ - ``write64le`` writes the lowest order eight bytes from the little endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack("<Q", value) - return self.write(value) - - def write16be(self, value): - """ - ``write16be`` writes the lowest order two bytes from the big endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack(">H", value) - return self.write(value) - - def write32be(self, value): - """ - ``write32be`` writes the lowest order four bytes from the big endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack(">I", value) - return self.write(value) - - def write64be(self, value): - """ - ``write64be`` writes the lowest order eight bytes from the big endian integer ``value`` to the current offset. - - :param int value: integer value to write. - :return: boolean True on success, False on failure. - :rtype: bool - """ - value = struct.pack(">Q", value) - return self.write(value) - - def seek(self, offset): - """ - ``seek`` update internal offset to ``offset``. - - :param int offset: offset to set the internal offset to - :rtype: None - :Example: - - >>> hex(bw.offset) - '0x100000008L' - >>> bw.seek(0x100000000) - >>> hex(br.offset) - '0x100000000L' - >>> - """ - core.BNSeekBinaryWriter(self.handle, offset) - - def seek_relative(self, offset): - """ - ``seek_relative`` updates the internal offset by ``offset``. - - :param int offset: offset to add to the internal offset - :rtype: None - :Example: - - >>> hex(bw.offset) - '0x100000008L' - >>> bw.seek_relative(-8) - >>> hex(br.offset) - '0x100000000L' - >>> - """ - core.BNSeekBinaryWriterRelative(self.handle, offset) - -class Symbol(object): - """ - Symbols are defined as one of the following types: - - =========================== ============================================================== - SymbolType Description - =========================== ============================================================== - FunctionSymbol Symbol for Function that exists in the current binary - ImportAddressSymbol Symbol defined in the Import Address Table - ImportedFunctionSymbol Symbol for Function that is not defined in the current binary - DataSymbol Symbol for Data in the current binary - ImportedDataSymbol Symbol for Data that is not defined in the current binary - =========================== ============================================================== - """ - def __init__(self, sym_type, addr, short_name, full_name = None, raw_name = None, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNSymbol) - else: - if isinstance(sym_type, str): - sym_type = core.BNSymbolType_by_name[sym_type] - if full_name is None: - full_name = short_name - if raw_name is None: - raw_name = full_name - self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr) - - def __del__(self): - core.BNFreeSymbol(self.handle) - - @property - def type(self): - """Symbol type (read-only)""" - return core.BNSymbolType_names[core.BNGetSymbolType(self.handle)] - - @property - def name(self): - """Symbol name (read-only)""" - return core.BNGetSymbolRawName(self.handle) - - @property - def short_name(self): - """Symbol short name (read-only)""" - return core.BNGetSymbolShortName(self.handle) - - @property - def full_name(self): - """Symbol full name (read-only)""" - return core.BNGetSymbolFullName(self.handle) - - @property - def raw_name(self): - """Symbol raw name (read-only)""" - return core.BNGetSymbolRawName(self.handle) - - @property - def address(self): - """Symbol address (read-only)""" - return core.BNGetSymbolAddress(self.handle) - - @property - def auto(self): - return core.BNIsSymbolAutoDefined(self.handle) - - @auto.setter - def auto(self, value): - core.BNSetSymbolAutoDefined(self.handle, value) - - def __repr__(self): - return "<%s: \"%s\" @ %#x>" % (self.type, self.full_name, self.address) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class Type(object): - def __init__(self, handle): - self.handle = handle - - def __del__(self): - core.BNFreeType(self.handle) - - @property - def type_class(self): - """Type class (read-only)""" - return core.BNTypeClass_names[core.BNGetTypeClass(self.handle)] - - @property - def width(self): - """Type width (read-only)""" - return core.BNGetTypeWidth(self.handle) - - @property - def alignment(self): - """Type alignment (read-only)""" - return core.BNGetTypeAlignment(self.handle) - - @property - def signed(self): - """Wether type is signed (read-only)""" - return core.BNIsTypeSigned(self.handle) - - @property - def const(self): - """Whether type is const (read-only)""" - return core.BNIsTypeConst(self.handle) - - @property - def modified(self): - """Whether type is modified (read-only)""" - return core.BNIsTypeFloatingPoint(self.handle) - - @property - def target(self): - """Target (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def element_type(self): - """Target (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def return_value(self): - """Return value (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def calling_convention(self): - """Calling convention (read-only)""" - result = core.BNGetTypeCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @property - def parameters(self): - """Type parameters list (read-only)""" - count = ctypes.c_ulonglong() - params = core.BNGetTypeParameters(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) - core.BNFreeTypeParameterList(params, count.value) - return result - - @property - def has_variable_arguments(self): - """Whether type has variable arguments (read-only)""" - return core.BNTypeHasVariableArguments(self.handle) - - @property - def can_return(self): - """Whether type can return (read-only)""" - return core.BNFunctionTypeCanReturn(self.handle) - - @property - def structure(self): - """Structure of the type (read-only)""" - result = core.BNGetTypeStructure(self.handle) - if result is None: - return None - return Structure(result) - - @property - def enumeration(self): - """Type enumeration (read-only)""" - result = core.BNGetTypeEnumeration(self.handle) - if result is None: - return None - return Enumeration(result) - - @property - def count(self): - """Type count (read-only)""" - return core.BNGetTypeElementCount(self.handle) - - def __str__(self): - return core.BNGetTypeString(self.handle) - - def __repr__(self): - return "<type: %s>" % str(self) - - def get_string_before_name(self): - return core.BNGetTypeStringBeforeName(self.handle) - - def get_string_after_name(self): - return core.BNGetTypeStringAfterName(self.handle) - - @classmethod - def void(cls): - return Type(core.BNCreateVoidType()) - - @classmethod - def bool(self): - return Type(core.BNCreateBoolType()) - - @classmethod - def int(self, width, sign = True, altname = ""): - return Type(core.BNCreateIntegerType(width, sign, - ctypes.create_string_buffer(altname))) - - @classmethod - def float(self, width): - return Type(core.BNCreateFloatType(width)) - - @classmethod - 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 unknown_type(self, s): - return Type(core.BNCreateUnknownType(s.handle)) - - @classmethod - def enumeration_type(self, arch, e, width = None): - if width is None: - width = arch.default_int_size - return Type(core.BNCreateEnumerationType(e.handle, width)) - - @classmethod - def pointer(self, arch, t, const = False): - return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) - - @classmethod - def array(self, t, count): - return Type(core.BNCreateArrayType(t.handle, count)) - - @classmethod - def function(self, ret, params, calling_convention = None, variable_arguments = False): - 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].type = params[i].handle - else: - param_buf[i].name = params[i][1] - param_buf[i].type = params[i][0] - if calling_convention is not None: - calling_convention = calling_convention.handle - return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), - variable_arguments)) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - -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 - self.offset = offset - - def __repr__(self): - if len(name) == 0: - return "<member: %s, offset %#x>" % (str(self.type), self.offset) - return "<%s %s%s, offset %#x>" % (self.type.get_string_before_name(), self.name, - self.type.get_string_after_name(), self.offset) - -class Structure(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateStructure() - else: - self.handle = handle - - def __del__(self): - core.BNFreeStructure(self.handle) - - @property - def name(self): - 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): - core.BNSetStructureName(self.handle, value) - - @property - def members(self): - """Structure member list (read-only)""" - count = ctypes.c_ulonglong() - members = core.BNGetStructureMembers(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), - members[i].name, members[i].offset)) - core.BNFreeStructureMemberList(members, count.value) - return result - - @property - def width(self): - """Structure width (read-only)""" - return core.BNGetStructureWidth(self.handle) - - @property - def alignment(self): - """Structure alignment (read-only)""" - return core.BNGetStructureAlignment(self.handle) - - @property - def packed(self): - return core.BNIsStructurePacked(self.handle) - - @packed.setter - def packed(self, value): - core.BNSetStructurePacked(self.handle, value) - - @property - def union(self): - return core.BNIsStructureUnion(self.handle) - - @union.setter - def union(self, value): - core.BNSetStructureUnion(self.handle, value) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - if len(self.name) > 0: - return "<struct: %s>" % self.name - return "<struct: size %#x>" % self.width - - def append(self, t, name = ""): - core.BNAddStructureMember(self.handle, t.handle, name) - - def insert(self, offset, t, name = ""): - core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) - - def remove(self, i): - core.BNRemoveStructureMember(self.handle, i) - -class EnumerationMember(object): - def __init__(self, name, value, default): - self.name = name - self.value = value - self.default = default - - def __repr__(self): - return "<%s = %#x>" % (self.name, self.value) - -class Enumeration(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateEnumeration() - else: - self.handle = handle - - def __del__(self): - core.BNFreeEnumeration(self.handle) - - @property - def name(self): - return core.BNGetEnumerationName(self.handle) - - @name.setter - def name(self, value): - core.BNSetEnumerationName(self.handle, value) - - @property - def members(self): - """Enumeration member list (read-only)""" - count = ctypes.c_ulonglong() - members = core.BNGetEnumerationMembers(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) - core.BNFreeEnumerationMemberList(members, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - if len(self.name) > 0: - return "<enum: %s>" % self.name - return "<enum: %s>" % repr(self.members) - - def append(self, name, value = None): - if value is None: - core.BNAddEnumerationMember(self.handle, name) - else: - core.BNAddEnumerationMemberWithValue(self.handle, name, value) - -class LookupTableEntry(object): - def __init__(self, from_values, to_value): - self.from_values = from_values - self.to_value = to_value - - def __repr__(self): - return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) - -class RegisterValue(object): - def __init__(self, arch, value): - self.type = value.state - if value.state == core.EntryValue: - self.reg = arch.get_reg_name(value.reg) - elif value.state == core.OffsetFromEntryValue: - self.reg = arch.get_reg_name(value.reg) - self.offset = value.value - elif value.state == core.ConstantValue: - self.value = value.value - elif value.state == core.StackFrameOffset: - self.offset = value.value - elif value.state == core.SignedRangeValue: - self.offset = value.value - self.start = value.rangeStart - self.end = value.rangeEnd - self.step = value.rangeStep - if self.start & (1 << 63): - self.start |= ~((1 << 63) - 1) - if self.end & (1 << 63): - self.end |= ~((1 << 63) - 1) - elif value.state == core.UnsignedRangeValue: - self.offset = value.value - self.start = value.rangeStart - self.end = value.rangeEnd - self.step = value.rangeStep - elif value.state == core.LookupTableValue: - self.table = [] - self.mapping = {} - for i in xrange(0, value.rangeEnd): - from_list = [] - for j in xrange(0, value.table[i].fromCount): - 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 == core.OffsetFromUndeterminedValue: - self.offset = value.value - - def __repr__(self): - if self.type == core.EntryValue: - return "<entry %s>" % self.reg - if self.type == core.OffsetFromEntryValue: - return "<entry %s + %#x>" % (self.reg, self.offset) - if self.type == core.ConstantValue: - return "<const %#x>" % self.value - if self.type == core.StackFrameOffset: - return "<stack frame offset %#x>" % self.offset - if (self.type == core.SignedRangeValue) or (self.type == core.UnsignedRangeValue): - if self.step == 1: - return "<range: %#x to %#x>" % (self.start, self.end) - return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step) - if self.type == core.LookupTableValue: - return "<table: %s>" % ', '.join([repr(i) for i in self.table]) - if self.type == core.OffsetFromUndeterminedValue: - return "<undetermined with offset %#x>" % self.offset - return "<undetermined>" - -class StackVariable(object): - def __init__(self, ofs, name, t): - self.offset = ofs - self.name = name - self.type = t - - def __repr__(self): - return "<var@%x: %s %s>" % (self.offset, self.type, self.name) - - def __str__(self): - return self.name - -class StackVariableReference: - def __init__(self, src_operand, t, name, start_ofs, ref_ofs): - self.source_operand = src_operand - self.type = t - self.name = name - self.starting_offset = start_ofs - self.referenced_offset = ref_ofs - if self.source_operand == 0xffffffff: - self.source_operand = None - - def __repr__(self): - if self.source_operand is None: - if self.referenced_offset != self.starting_offset: - return "<ref to %s%+#x>" % (self.name, self.referenced_offset - self.starting_offset) - return "<ref to %s>" % self.name - if self.referenced_offset != self.starting_offset: - return "<operand %d ref to %s%+#x>" % (self.source_operand, self.name, self.referenced_offset) - return "<operand %d ref to %s>" % (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 "<constant %#x>" % self.value - return "<constant %#x size %d>" % (self.value, self.size) - -class IndirectBranchInfo: - def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): - self.source_arch = source_arch - self.source_addr = source_addr - self.dest_arch = dest_arch - self.dest_addr = dest_addr - self.auto_defined = auto_defined - - def __repr__(self): - return "<branch %s:%#x -> %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 "<color: %s>" % self._standard_color_to_str(self.color) - return "<color: %s, alpha %d>" % (self._standard_color_to_str(self.color), self.alpha) - if self.style == core.MixedHighlightColor: - if self.alpha == 255: - return "<color: mix %s to %s factor %d>" % (self._standard_color_to_str(self.color), - self._standard_color_to_str(self.mix_color), self.mix) - return "<color: mix %s to %s factor %d, alpha %d>" % (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 "<color: #%.2x%.2x%.2x>" % (self.red, self.green, self.blue) - return "<color: #%.2x%.2x%.2x, alpha %d>" % (self.red, self.green, self.blue, self.alpha) - return "<color>" - - 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 _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) - 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) - - @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_session_data(cls, name, value): - _FunctionAssociatedDataStore.set_default(name, value) - - @property - def name(self): - """Symbol name for the function""" - return self.symbol.name - - @name.setter - def name(self,value): - if value is None: - if self.symbol is not None: - self.view.undefine_user_symbol(self.symbol) - else: - symbol = Symbol(core.FunctionSymbol,self.start,value) - self.view.define_user_symbol(symbol) - - @property - def view(self): - """Function view (read-only)""" - return self._view - - @property - def arch(self): - """Function architecture (read-only)""" - arch = core.BNGetFunctionArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def platform(self): - """Function platform (read-only)""" - platform = core.BNGetFunctionPlatform(self.handle) - if platform is None: - return None - return Platform(None, handle = platform) - - @property - def start(self): - """Function start (read-only)""" - return core.BNGetFunctionStart(self.handle) - - @property - def symbol(self): - """Function symbol(read-only)""" - sym = core.BNGetFunctionSymbol(self.handle) - if sym is None: - return None - return Symbol(None, None, None, handle = sym) - - @property - def auto(self): - """Whether function was automatically discovered (read-only)""" - return core.BNWasFunctionAutomaticallyDiscovered(self.handle) - - @property - def can_return(self): - """Whether function can return (read-only)""" - return core.BNCanFunctionReturn(self.handle) - - @property - def explicitly_defined_type(self): - """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)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) - core.BNFreeBasicBlockList(blocks, count.value) - return result - - @property - def comments(self): - """Dict of comments (read-only)""" - count = ctypes.c_ulonglong() - addrs = core.BNGetCommentedAddresses(self.handle, count) - result = {} - for i in xrange(0, count.value): - result[addrs[i]] = self.get_comment_at(addrs[i]) - core.BNFreeAddressList(addrs) - return result - - @property - def low_level_il(self): - """Function low level IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) - - @property - def lifted_il(self): - """Function lifted IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) - - @property - def function_type(self): - """Function type""" - return Type(core.BNGetFunctionType(self.handle)) - - @function_type.setter - def function_type(self, value): - self.set_user_type(value) - - @property - def stack_layout(self): - """List of function stack (read-only)""" - count = ctypes.c_ulonglong() - v = core.BNGetStackLayout(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(StackVariable(v[i].offset, v[i].name, Type(handle = core.BNNewTypeReference(v[i].type)))) - result.sort(key = lambda x: x.offset) - core.BNFreeStackLayout(v, count.value) - return result - - @property - def indirect_branches(self): - """List of indirect branches (read-only)""" - 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 session_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) - try: - for i in xrange(0, count.value): - yield BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])) - finally: - core.BNFreeBasicBlockList(blocks, count.value) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - arch = self.arch - if arch: - return "<func: %s@%#x>" % (arch.name, self.start) - else: - return "<func: %#x>" % self.start - - def mark_recent_use(self): - core.BNMarkFunctionAsRecentlyUsed(self.handle) - - def get_comment_at(self, addr): - return core.BNGetCommentForAddress(self.handle, addr) - - def set_comment(self, addr, comment): - core.BNSetCommentForAddress(self.handle, addr, comment) - - def get_low_level_il_at(self, arch, addr): - return core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) - - def get_low_level_il_exits_at(self, arch, addr): - count = ctypes.c_ulonglong() - exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(exits[i]) - core.BNFreeLowLevelILInstructionList(exits) - return result - - def get_reg_value_at(self, arch, addr, reg): - if isinstance(reg, str): - reg = arch.regs[reg].index - value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_after(self, arch, addr, reg): - if isinstance(reg, str): - reg = arch.regs[reg].index - value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_at_low_level_il_instruction(self, i, reg): - if isinstance(reg, str): - reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAtLowLevelILInstruction(self.handle, i, reg) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_after_low_level_il_instruction(self, i, reg): - if isinstance(reg, str): - reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAfterLowLevelILInstruction(self.handle, i, reg) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_at(self, arch, addr, offset, size): - value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_after(self, arch, addr, offset, size): - value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_at_low_level_il_instruction(self, i, offset, size): - value = core.BNGetStackContentsAtLowLevelILInstruction(self.handle, i, offset, size) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_after_low_level_il_instruction(self, i, offset, size): - value = core.BNGetStackContentsAfterInstruction(self.handle, i, offset, size) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_parameter_at(self, arch, addr, func_type, i): - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_regs_read_by(self, arch, addr): - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result - - def get_regs_written_by(self, arch, addr): - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result - - def get_stack_vars_referenced_by(self, arch, addr): - count = ctypes.c_ulonglong() - refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(StackVariableReference(refs[i].sourceOperand, Type(core.BNNewTypeReference(refs[i].type)), - refs[i].name, refs[i].startingOffset, refs[i].referencedOffset)) - 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) - - def get_lifted_il_flag_uses_for_definition(self, i, flag): - if isinstance(flag, str): - flag = self.arch._flags[flag] - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) - result = [] - for i in xrange(0, count.value): - result.append(instrs[i]) - core.BNFreeLowLevelILInstructionList(instrs) - return result - - def get_lifted_il_flag_definitions_for_use(self, i, flag): - if isinstance(flag, str): - flag = self.arch._flags[flag] - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) - result = [] - for i in xrange(0, count.value): - result.append(instrs[i]) - core.BNFreeLowLevelILInstructionList(instrs) - return result - - def get_flags_read_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) - result = [] - for i in xrange(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return result - - def get_flags_written_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) - result = [] - for i in xrange(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return result - - def create_graph(self): - return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) - - def apply_imported_types(self, sym): - core.BNApplyImportedTypes(self.handle, sym.handle) - - def apply_auto_discovered_type(self, func_type): - core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) - - def set_auto_indirect_branches(self, source_arch, source, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) - - def set_user_indirect_branches(self, source_arch, source, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) - - def get_indirect_branches_at(self, arch, addr): - count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, 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 - - def get_block_annotations(self, arch, addr): - count = ctypes.c_ulonglong(0) - lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(tokens) - core.BNFreeInstructionTextLines(lines, count.value) - return result - - def set_auto_type(self, value): - core.BNSetFunctionAutoType(self.handle, value.handle) - - def set_user_type(self, value): - core.BNSetFunctionUserType(self.handle, value.handle) - - def get_int_display_type(self, arch, instr_addr, value, operand): - return core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) - - def set_int_display_type(self, arch, instr_addr, value, operand, display_type): - if isinstance(display_type, str): - 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) - - 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 - - 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 - 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(object): - def __init__(self, branch_type, target, arch): - self.type = core.BNBranchType_names[branch_type] - if self.type != "UnresolvedBranch": - self.target = target - self.arch = arch - - def __repr__(self): - if self.type == "UnresolvedBranch": - return "<%s>" % self.type - elif self.arch: - return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) - else: - return "<%s: %#x>" % (self.type, self.target) - -class BasicBlock(object): - def __init__(self, view, handle): - self.view = view - self.handle = core.handle_of_type(handle, core.BNBasicBlock) - - def __del__(self): - core.BNFreeBasicBlock(self.handle) - - @property - def function(self): - """Basic block function (read-only)""" - func = core.BNGetBasicBlockFunction(self.handle) - if func is None: - return None - return Function(self.view, func) - - @property - def arch(self): - """Basic block architecture (read-only)""" - arch = core.BNGetBasicBlockArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def start(self): - """Basic block start (read-only)""" - return core.BNGetBasicBlockStart(self.handle) - - @property - def end(self): - """Basic block end (read-only)""" - return core.BNGetBasicBlockEnd(self.handle) - - @property - def length(self): - """Basic block length (read-only)""" - return core.BNGetBasicBlockLength(self.handle) - - @property - def outgoing_edges(self): - """List of basic block outgoing edges (read-only)""" - count = ctypes.c_ulonglong(0) - edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) - result = [] - for i in xrange(0, count.value): - branch_type = edges[i].type - target = edges[i].target - if edges[i].arch: - arch = Architecture(edges[i].arch) - else: - arch = None - result.append(BasicBlockEdge(branch_type, target, arch)) - core.BNFreeBasicBlockOutgoingEdgeList(edges) - return result - - @property - def has_undetermined_outgoing_edges(self): - """Whether basic block has undetermined outgoing edges (read-only)""" - return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) - - @property - def annotations(self): - """List of automatic annotations for the start of this block (read-only)""" - return self.function.get_block_annotations(self.arch, self.start) - - @property - 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) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __len__(self): - return int(core.BNGetBasicBlockLength(self.handle)) - - def __repr__(self): - arch = self.arch - if arch: - return "<block: %s@%#x-%#x>" % (arch.name, self.start, self.end) - else: - return "<block: %#x-%#x>" % (self.start, self.end) - - def __iter__(self): - start = self.start - end = self.end - idx = start - while idx < end: - data = self.view.read(idx, 16) - inst_info = self.view.arch.get_instruction_info(data, idx) - inst_text = self.view.arch.get_instruction_text(data, idx) - - yield inst_text - idx += inst_info.length - - def mark_recent_use(self): - core.BNMarkBasicBlockAsRecentlyUsed(self.handle) - - def get_disassembly_text(self, settings = None): - settings_obj = None - if settings: - settings_obj = settings.handle - - count = ctypes.c_ulonglong() - lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) - result = [] - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(DisassemblyTextLine(addr, tokens)) - 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) - self.il_function = owner - - def __iter__(self): - for idx in xrange(self.start, self.end): - yield self.il_function[idx] - -class DisassemblyTextLine(object): - def __init__(self, addr, tokens): - self.address = addr - self.tokens = tokens - - def __str__(self): - result = "" - for token in self.tokens: - result += token.text - return result - - def __repr__(self): - return "<%#x: %s>" % (self.address, str(self)) - -class FunctionGraphEdge: - def __init__(self, branch_type, arch, target, points): - self.type = branch_type - self.arch = arch - self.target = target - self.points = points - - def __repr__(self): - if self.arch: - return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) - return "<%s: %#x>" % (self.type, self.target) - -class FunctionGraphBlock(object): - def __init__(self, handle): - self.handle = handle - - 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(handle = core.BNGetFunctionData(func)), block) - core.BNFreeFunction(func) - return block - - @property - def arch(self): - """Function graph block architecture (read-only)""" - arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def start(self): - """Function graph block start (read-only)""" - return core.BNGetFunctionGraphBlockStart(self.handle) - - @property - def end(self): - """Function graph block end (read-only)""" - return core.BNGetFunctionGraphBlockEnd(self.handle) - - @property - def x(self): - """Function graph block X (read-only)""" - return core.BNGetFunctionGraphBlockX(self.handle) - - @property - def y(self): - """Function graph block Y (read-only)""" - return core.BNGetFunctionGraphBlockY(self.handle) - - @property - def width(self): - """Function graph block width (read-only)""" - return core.BNGetFunctionGraphBlockWidth(self.handle) - - @property - def height(self): - """Function graph block height (read-only)""" - return core.BNGetFunctionGraphBlockHeight(self.handle) - - @property - def lines(self): - """Function graph block list of lines (read-only)""" - count = ctypes.c_ulonglong() - lines = core.BNGetFunctionGraphBlockLines(self.handle, count) - result = [] - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(DisassemblyTextLine(addr, tokens)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result - - @property - def outgoing_edges(self): - """Function graph block list of outgoing edges (read-only)""" - count = ctypes.c_ulonglong() - edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count) - result = [] - for i in xrange(0, count.value): - branch_type = core.BNBranchType_names[edges[i].type] - target = edges[i].target - arch = None - if edges[i].arch is not None: - arch = Architecture(edges[i].arch) - points = [] - for j in xrange(0, edges[i].pointCount): - points.append((edges[i].points[j].x, edges[i].points[j].y)) - result.append(FunctionGraphEdge(branch_type, arch, target, points)) - core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - arch = self.arch - if arch: - return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end) - else: - return "<graph block: %#x-%#x>" % (self.start, self.end) - - def __iter__(self): - count = ctypes.c_ulonglong() - lines = core.BNGetFunctionGraphBlockLines(self.handle, count) - try: - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - yield DisassemblyTextLine(addr, tokens) - finally: - core.BNFreeDisassemblyTextLines(lines, count.value) - -class DisassemblySettings(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateDisassemblySettings() - else: - self.handle = handle - - def __del__(self): - core.BNFreeDisassemblySettings(self.handle) - - @property - def width(self): - return core.BNGetDisassemblyWidth(self.handle) - - @width.setter - def width(self, value): - core.BNSetDisassemblyWidth(self.handle, value) - - @property - def max_symbol_width(self): - return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) - - @max_symbol_width.setter - def max_symbol_width(self, value): - core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) - - def is_option_set(self, option): - if isinstance(option, str): - 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] - core.BNSetDisassemblySettingsOption(self.handle, option, state) - -class FunctionGraph(object): - def __init__(self, view, handle): - self.view = view - self.handle = handle - self._on_complete = None - self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete) - - def __del__(self): - self.abort() - core.BNFreeFunctionGraph(self.handle) - - @property - def function(self): - """Function for a function graph (read-only)""" - func = core.BNGetFunctionForFunctionGraph(self.handle) - if func is None: - return None - return Function(self.view, func) - - @property - def complete(self): - """Whether function graph layout is complete (read-only)""" - return core.BNIsFunctionGraphLayoutComplete(self.handle) - - @property - def type(self): - """Function graph type (read-only)""" - return core.BNFunctionGraphType_names[core.BNGetFunctionGraphType(self.handle)] - - @property - def blocks(self): - """List of basic blocks in function (read-only)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocks(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) - core.BNFreeFunctionGraphBlockList(blocks, count.value) - return result - - @property - def width(self): - """Function graph width (read-only)""" - return core.BNGetFunctionGraphWidth(self.handle) - - @property - def height(self): - """Function graph height (read-only)""" - return core.BNGetFunctionGraphHeight(self.handle) - - @property - def horizontal_block_margin(self): - return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) - - @horizontal_block_margin.setter - def horizontal_block_margin(self, value): - core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) - - @property - def vertical_block_margin(self): - return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) - - @vertical_block_margin.setter - def vertical_block_margin(self, value): - core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) - - @property - def settings(self): - return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle)) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<graph of %s>" % repr(self.function) - - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocks(self.handle, count) - try: - for i in xrange(0, count.value): - yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])) - finally: - core.BNFreeFunctionGraphBlockList(blocks, count.value) - - def _complete(self, ctxt): - try: - if self._on_complete is not None: - self._on_complete() - except: - log_error(traceback.format_exc()) - - def layout(self, graph_type = core.NormalFunctionGraph): - if isinstance(graph_type, str): - graph_type = core.BNFunctionGraphType_by_name[graph_type] - core.BNStartFunctionGraphLayout(self.handle, graph_type) - - def _wait_complete(self): - self._wait_cond.acquire() - self._wait_cond.notify() - self._wait_cond.release() - - def layout_and_wait(self, graph_type = core.NormalFunctionGraph): - self._wait_cond = threading.Condition() - self.on_complete(self._wait_complete) - self.layout(graph_type) - - self._wait_cond.acquire() - while not self.complete: - self._wait_cond.wait() - self._wait_cond.release() - - def on_complete(self, callback): - self._on_complete = callback - core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) - - def abort(self): - core.BNAbortFunctionGraph(self.handle) - - def get_blocks_in_region(self, left, top, right, bottom): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count) - result = [] - for i in xrange(0, count.value): - result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) - core.BNFreeFunctionGraphBlockList(blocks, count.value) - return result - - def is_option_set(self, option): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) - return core.BNIsFunctionGraphOptionSet(self.handle, option) - - def set_option(self, option, state = True): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) - core.BNSetFunctionGraphOption(self.handle, option, state) - -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 - self.size = size - self.extend = extend - self.index = index - - def __repr__(self): - if (self.extend == core.ZeroExtendToFullWidth) or (self.extend == "ZeroExtendToFullWidth"): - extend = ", zero extend" - elif (self.extend == core.SignExtendToFullWidth) or (self.extend == "SignExtendToFullWidth"): - extend = ", sign extend" - else: - extend = "" - return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend) - -class InstructionBranch(object): - def __init__(self, branch_type, target = 0, arch = None): - self.type = branch_type - self.target = target - self.arch = arch - - def __repr__(self): - branch_type = self.type - if not isinstance(branch_type, str): - branch_type = core.BNBranchType_names[branch_type] - if self.arch is not None: - return "<%s: %s@%#x>" % (branch_type, self.arch.name, self.target) - return "<%s: %#x>" % (branch_type, self.target) - -class InstructionInfo(object): - def __init__(self): - self.length = 0 - self.branch_delay = False - self.branches = [] - - def add_branch(self, branch_type, target = 0, arch = None): - self.branches.append(InstructionBranch(branch_type, target, arch)) - - def __repr__(self): - branch_delay = "" - if self.branch_delay: - branch_delay = ", delay slot" - return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches)) - -class InstructionTextToken(object): - """ - ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. - - ======================== ============================================ - InstructionTextTokenType Description - ======================== ============================================ - TextToken Text that doesn't fit into the other tokens - InstructionToken The instruction mnemonic - OperandSeparatorToken The comma or whatever else separates tokens - RegisterToken Registers - IntegerToken Integers - PossibleAddressToken Integers that are likely addresses - BeginMemoryOperandToken The start of memory operand - EndMemoryOperandToken The end of a memory operand - FloatingPointToken Floating point number - AnnotationToken **For internal use only** - CodeRelativeAddressToken **For internal use only** - StackVariableTypeToken **For internal use only** - DataVariableTypeToken **For internal use only** - FunctionReturnTypeToken **For internal use only** - FunctionAttributeToken **For internal use only** - ArgumentTypeToken **For internal use only** - ArgumentNameToken **For internal use only** - HexDumpByteValueToken **For internal use only** - HexDumpSkippedByteToken **For internal use only** - HexDumpInvalidByteToken **For internal use only** - HexDumpTextToken **For internal use only** - OpcodeToken **For internal use only** - StringToken **For internal use only** - CharacterConstantToken **For internal use only** - CodeSymbolToken **For internal use only** - DataSymbolToken **For internal use only** - StackVariableToken **For internal use only** - ImportToken **For internal use only** - AddressDisplayToken **For internal use only** - ======================== ============================================ - - """ - def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff): - self.type = token_type - self.text = text - self.value = value - self.size = size - self.operand = operand - - def __str__(self): - return self.text - - def __repr__(self): - return repr(self.text) - -class _ArchitectureMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - archs = core.BNGetArchitectureList(count) - result = [] - for i in xrange(0, count.value): - result.append(Architecture(archs[i])) - core.BNFreeArchitectureList(archs) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - archs = core.BNGetArchitectureList(count) - try: - for i in xrange(0, count.value): - yield Architecture(archs[i]) - finally: - core.BNFreeArchitectureList(archs) - - def __getitem__(cls, name): - _init_plugins() - arch = core.BNGetArchitectureByName(name) - if arch is None: - raise KeyError, "'%s' is not a valid architecture" % str(name) - return Architecture(arch) - - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "architecture 'name' is not defined" - arch = cls() - cls._registered_cb = arch._cb - arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class Architecture(object): - """ - ``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 - iteration:: - - >>> #List the architectures - >>> list(Architecture) - [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, - <arch: x86>, <arch: x86_64>] - >>> #Register a new Architecture - >>> class MyArch(Architecture): - ... name = "MyArch" - ... - >>> MyArch.register() - >>> list(Architecture) - [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, - <arch: x86>, <arch: x86_64>, <arch: MyArch>] - >>> - - For the purposes of this documentation the variable ``arch`` will be used in the following context :: - - >>> from binaryninja import * - >>> arch = Architecture['x86'] - """ - name = None - endianness = core.LittleEndian - address_size = 8 - default_int_size = 4 - max_instr_length = 16 - opcode_display_length = 8 - regs = {} - stack_pointer = None - link_reg = None - flags = [] - flag_write_types = [] - flag_roles = {} - flags_required_for_flag_condition = {} - flags_written_by_flag_write_type = {} - __metaclass__ = _ArchitectureMetaClass - next_address = 0 - - def __init__(self, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNArchitecture) - self.__dict__["name"] = core.BNGetArchitectureName(self.handle) - self.__dict__["endianness"] = core.BNEndianness_names[core.BNGetArchitectureEndianness(self.handle)] - self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) - self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) - self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) - self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) - self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, - core.BNGetArchitectureStackPointerRegister(self.handle)) - self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, - core.BNGetArchitectureLinkRegister(self.handle)) - - count = ctypes.c_ulonglong() - regs = core.BNGetAllArchitectureRegisters(self.handle, count) - self.__dict__["regs"] = {} - for i in xrange(0, count.value): - name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) - info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) - full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) - self.regs[name] = RegisterInfo(full_width_reg, info.size, info.offset, - core.BNImplicitRegisterExtend_names[info.extend], regs[i]) - core.BNFreeRegisterList(regs) - - count = ctypes.c_ulonglong() - flags = core.BNGetAllArchitectureFlags(self.handle, count) - self._flags = {} - self._flags_by_index = {} - self.__dict__["flags"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureFlagName(self.handle, flags[i]) - self._flags[name] = flags[i] - self._flags_by_index[flags[i]] = name - self.flags.append(name) - core.BNFreeRegisterList(flags) - - count = ctypes.c_ulonglong() - types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) - self._flag_write_types = {} - self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) - self._flag_write_types[name] = types[i] - self._flag_write_types_by_index[types[i]] = name - self.flag_write_types.append(name) - core.BNFreeRegisterList(types) - - self._flag_roles = {} - self.__dict__["flag_roles"] = {} - for flag in self.__dict__["flags"]: - role = core.BNGetArchitectureFlagRole(self.handle, self._flags[flag]) - self.__dict__["flag_roles"][flag] = role - self._flag_roles[self._flags[flag]] = role - - self._flags_required_for_flag_condition = {} - self.__dict__["flags_required_for_flag_condition"] = {} - for cond in core.BNLowLevelILFlagCondition_names: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) - flag_indexes = [] - flag_names = [] - for i in xrange(0, count.value): - flag_indexes.append(flags[i]) - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - self._flags_required_for_flag_condition[cond] = flag_indexes - self.__dict__["flags_required_for_flag_condition"][cond] = flag_names - - self._flags_written_by_flag_write_type = {} - self.__dict__["flags_written_by_flag_write_type"] = {} - for write_type in self.flag_write_types: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, - self._flag_write_types[write_type], count) - flag_indexes = [] - flag_names = [] - for i in xrange(0, count.value): - flag_indexes.append(flags[i]) - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes - self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names - else: - _init_plugins() - - if self.__class__.opcode_display_length > self.__class__.max_instr_length: - self.__class__.opcode_display_length = self.__class__.max_instr_length - - self._cb = core.BNCustomArchitecture() - self._cb.context = 0 - self._cb.init = self._cb.init.__class__(self._init) - self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) - self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) - self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) - self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) - self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) - self._cb.getAssociatedArchitectureByAddress = self._cb.getAssociatedArchitectureByAddress.__class__( - self._get_associated_arch_by_address) - self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) - self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) - self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) - self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( - self._get_instruction_low_level_il) - self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) - self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) - self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) - self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) - self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) - self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) - self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) - self._cb.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) - self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( - self._get_flags_required_for_flag_condition) - self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( - self._get_flags_written_by_flag_write_type) - self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( - self._get_flag_write_low_level_il) - self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( - self._get_flag_condition_low_level_il) - self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) - self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) - self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( - self._get_stack_pointer_register) - self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) - self._cb.assemble = self._cb.assemble.__class__(self._assemble) - self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( - self._is_never_branch_patch_available) - self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( - self._is_always_branch_patch_available) - self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( - self._is_invert_branch_patch_available) - self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( - self._is_skip_and_return_zero_patch_available) - self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( - self._is_skip_and_return_value_patch_available) - self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) - self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) - self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) - self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) - - self._all_regs = {} - self._full_width_regs = {} - self._regs_by_index = {} - self.__dict__["regs"] = self.__class__.regs - reg_index = 0 - for reg in self.regs: - info = self.regs[reg] - if reg not in self._all_regs: - self._all_regs[reg] = reg_index - self._regs_by_index[reg_index] = reg - self.regs[reg].index = reg_index - reg_index += 1 - if info.full_width_reg not in self._all_regs: - self._all_regs[info.full_width_reg] = reg_index - self._regs_by_index[reg_index] = info.full_width_reg - self.regs[info.full_width_reg].index = reg_index - reg_index += 1 - if info.full_width_reg not in self._full_width_regs: - self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] - - self._flags = {} - self._flags_by_index = {} - self.__dict__["flags"] = self.__class__.flags - flag_index = 0 - for flag in self.__class__.flags: - if flag not in self._flags: - self._flags[flag] = flag_index - self._flags_by_index[flag_index] = flag - flag_index += 1 - - self._flag_write_types = {} - self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = self.__class__.flag_write_types - write_type_index = 0 - for write_type in self.__class__.flag_write_types: - if write_type not in self._flag_write_types: - self._flag_write_types[write_type] = write_type_index - self._flag_write_types_by_index[write_type_index] = write_type - write_type_index += 1 - - self._flag_roles = {} - self.__dict__["flag_roles"] = self.__class__.flag_roles - for flag in self.__class__.flag_roles: - role = self.__class__.flag_roles[flag] - if isinstance(role, str): - role = core.BNFlagRole_by_name[role] - self._flag_roles[self._flags[flag]] = role - - 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: - flags = [] - for flag in self.__class__.flags_required_for_flag_condition[cond]: - flags.append(self._flags[flag]) - self._flags_required_for_flag_condition[cond] = flags - - 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: - flags = [] - for flag in self.__class__.flags_written_by_flag_write_type[write_type]: - flags.append(self._flags[flag]) - self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags - - self._pending_reg_lists = {} - self._pending_token_lists = {} - - @property - def full_width_regs(self): - """List of full width register strings (read-only)""" - count = ctypes.c_ulonglong() - regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) - core.BNFreeRegisterList(regs) - return result - - @property - def calling_conventions(self): - """Dict of CallingConvention objects (read-only)""" - count = ctypes.c_ulonglong() - cc = core.BNGetArchitectureCallingConventions(self.handle, count) - result = {} - for i in xrange(0, count.value): - obj = CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) - result[obj.name] = obj - core.BNFreeCallingConventionList(cc, count) - return result - - @property - def standalone_platform(self): - """Architecture standalone platform (read-only)""" - pl = core.BNGetArchitectureStandalonePlatform(self.handle) - return Platform(self, pl) - - def __setattr__(self, name, value): - if ((name == "name") or (name == "endianness") or (name == "address_size") or - (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): - raise AttributeError, "attribute '%s' is read only" % name - else: - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<arch: %s>" % self.name - - def _init(self, ctxt, handle): - self.handle = handle - - def _get_endianness(self, ctxt): - try: - return self.__class__.endianness - except: - log_error(traceback.format_exc()) - return core.LittleEndian - - def _get_address_size(self, ctxt): - try: - return self.__class__.address_size - except: - log_error(traceback.format_exc()) - return 8 - - def _get_default_integer_size(self, ctxt): - try: - return self.__class__.default_int_size - except: - log_error(traceback.format_exc()) - return 4 - - def _get_max_instruction_length(self, ctxt): - try: - return self.__class__.max_instr_length - except: - log_error(traceback.format_exc()) - return 16 - - def _get_opcode_display_length(self, ctxt): - try: - return self.__class__.opcode_display_length - except: - log_error(traceback.format_exc()) - return 8 - - def _get_associated_arch_by_address(self, ctxt, addr): - try: - result, new_addr = self.perform_get_associated_arch_by_address(addr[0]) - addr[0] = new_addr - return ctypes.cast(result.handle, ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return ctypes.cast(self.handle, ctypes.c_void_p).value - - def _get_instruction_info(self, ctxt, data, addr, max_len, result): - try: - buf = ctypes.create_string_buffer(max_len) - ctypes.memmove(buf, data, max_len) - info = self.perform_get_instruction_info(buf.raw, addr) - if info is None: - return False - result[0].length = info.length - result[0].branchDelay = info.branch_delay - result[0].branchCount = len(info.branches) - for i in xrange(0, len(info.branches)): - if isinstance(info.branches[i].type, str): - result[0].branchType[i] = core.BNBranchType_by_name[info.branches[i].type] - else: - result[0].branchType[i] = info.branches[i].type - result[0].branchTarget[i] = info.branches[i].target - if info.branches[i].arch is None: - result[0].branchArch[i] = None - else: - result[0].branchArch[i] = info.branches[i].arch.handle - return True - except (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _get_instruction_text(self, ctxt, data, addr, length, result, count): - try: - buf = ctypes.create_string_buffer(length[0]) - ctypes.memmove(buf, data, length[0]) - info = self.perform_get_instruction_text(buf.raw, addr) - if info is None: - return False - tokens = info[0] - length[0] = info[1] - count[0] = len(tokens) - token_buf = (core.BNInstructionTextToken * len(tokens))() - for i in xrange(0, len(tokens)): - if isinstance(tokens[i].type, str): - 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 - token_buf[i].value = tokens[i].value - token_buf[i].size = tokens[i].size - token_buf[i].operand = tokens[i].operand - result[0] = token_buf - ptr = ctypes.cast(token_buf, ctypes.c_void_p) - self._pending_token_lists[ptr.value] = (ptr.value, token_buf) - return True - except (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _free_instruction_text(self, tokens, count): - try: - buf = ctypes.cast(tokens, ctypes.c_void_p) - 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 KeyError: - log_error(traceback.format_exc()) - - def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): - try: - buf = ctypes.create_string_buffer(length[0]) - ctypes.memmove(buf, data, length[0]) - result = self.perform_get_instruction_low_level_il(buf.raw, addr, - LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) - if result is None: - return False - length[0] = result - return True - except OSError: - log_error(traceback.format_exc()) - return False - - def _get_register_name(self, ctxt, reg): - try: - if reg in self._regs_by_index: - return core.BNAllocString(self._regs_by_index[reg]) - return core.BNAllocString("") - except (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_flag_name(self, ctxt, flag): - try: - if flag in self._flags_by_index: - return core.BNAllocString(self._flags_by_index[flag]) - return core.BNAllocString("") - except (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_flag_write_type_name(self, ctxt, write_type): - try: - 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 (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_full_width_registers(self, ctxt, count): - try: - regs = self._full_width_regs.values() - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = regs[i] - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_registers(self, ctxt, count): - try: - regs = self._regs_by_index.keys() - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = regs[i] - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_flags(self, ctxt, count): - try: - flags = self._flags_by_index.keys() - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_flag_write_types(self, ctxt, count): - try: - types = self._flag_write_types_by_index.keys() - count[0] = len(types) - type_buf = (ctypes.c_uint * len(types))() - for i in xrange(0, len(types)): - type_buf[i] = types[i] - result = ctypes.cast(type_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, type_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flag_role(self, ctxt, flag): - try: - if flag in self._flag_roles: - return self._flag_roles[flag] - return core.SpecialFlagRole - except KeyError: - log_error(traceback.format_exc()) - return None - - def _get_flags_required_for_flag_condition(self, ctxt, cond, count): - try: - if cond in self._flags_required_for_flag_condition: - flags = self._flags_required_for_flag_condition[cond] - else: - flags = [] - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flags_written_by_flag_write_type(self, ctxt, write_type, count): - try: - if write_type in self._flags_written_by_flag_write_type: - flags = self._flags_written_by_flag_write_type[write_type] - else: - flags = [] - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except (KeyError, OSError): - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): - try: - write_type_name = None - if write_type != 0: - write_type_name = self._flag_write_types_by_index[write_type] - flag_name = self._flags_by_index[flag] - operand_list = [] - for i in xrange(operand_count): - if operands[i].constant: - operand_list.append(("const", operands[i].value)) - elif LLIL_REG_IS_TEMP(operands[i].reg): - operand_list.append(("reg", operands[i].reg)) - else: - 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 (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _get_flag_condition_low_level_il(self, ctxt, cond, il): - try: - return self.perform_get_flag_condition_low_level_il(cond, - LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index - except OSError: - log_error(traceback.format_exc()) - return 0 - - def _free_register_list(self, ctxt, regs): - try: - buf = ctypes.cast(regs, ctypes.c_void_p) - 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 (ValueError, KeyError): - log_error(traceback.format_exc()) - - def _get_register_info(self, ctxt, reg, result): - try: - if reg not in self._regs_by_index: - result[0].fullWidthRegister = 0 - result[0].offset = 0 - result[0].size = 0 - result[0].extend = core.NoExtend - return - info = self.__class__.regs[self._regs_by_index[reg]] - result[0].fullWidthRegister = self._all_regs[info.full_width_reg] - result[0].offset = info.offset - result[0].size = info.size - if isinstance(info.extend, str): - result[0].extend = core.BNImplicitRegisterExtend_by_name[info.extend] - else: - result[0].extend = info.extend - except KeyError: - log_error(traceback.format_exc()) - result[0].fullWidthRegister = 0 - result[0].offset = 0 - result[0].size = 0 - result[0].extend = core.NoExtend - - def _get_stack_pointer_register(self, ctxt): - try: - return self._all_regs[self.__class__.stack_pointer] - except KeyError: - log_error(traceback.format_exc()) - return 0 - - def _get_link_register(self, ctxt): - try: - if self.__class__.link_reg is None: - return 0xffffffff - return self._all_regs[self.__class__.link_reg] - except KeyError: - log_error(traceback.format_exc()) - return 0 - - def _assemble(self, ctxt, code, addr, result, errors): - try: - data, error_str = self.perform_assemble(code, addr) - errors[0] = core.BNAllocString(str(error_str)) - if data is None: - return False - data = str(data) - buf = ctypes.create_string_buffer(len(data)) - ctypes.memmove(buf, data, len(data)) - core.BNSetDataBufferContents(result, buf, len(data)) - return True - except: - log_error(traceback.format_exc()) - errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") - return False - - def _is_never_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_never_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_always_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_always_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_invert_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_invert_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _convert_to_nop(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_convert_to_nop(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _always_branch(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_always_branch(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _invert_branch(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_invert_branch(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _skip_and_return_value(self, ctxt, data, addr, length, value): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_skip_and_return_value(buf.raw, addr, value) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def perform_get_associated_arch_by_address(self, addr): - return self, addr - - @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 - :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 always be taken - FalseBranch False branch condition - TrueBranch True branch condition - CallDestination Branch is a call instruction (Branch with Link) - FunctionReturn Branch returns from a function - 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 - :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction - :rtype: InstructionInfo - """ - 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 - list of :py:class:`InstructionTextToken` objects. - - :param str data: bytes to decode - :param int addr: virtual address of the byte to be decoded - :return: a tuple of list(InstructionTextToken) and length of instruction decoded - :rtype: tuple(list(InstructionTextToken), int) - """ - 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 - low-level IL instructions. The il instructions must be appended to the :py:class:`LowLevelILFunction`. - - .. note:: Architecture subclasses should implement this method. - - :param str data: bytes to be interpreted as low-level IL instructions - :param int addr: virtual address of start of ``data`` - :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: 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. - .. warning:: This method should never be called directly. - - :param LowLevelILOperation op: - :param int size: - :param int write_type: - :param int flag: - :param list(int_or_str): - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - return il.unimplemented() - - @abc.abstractmethod - def perform_get_flag_condition_low_level_il(self, cond, il): - """ - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param LowLevelILFlagCondition cond: - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - 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 - virtual address ``addr`` to the byte representation of those instructions. This can be done by simply shelling - out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. - - .. note:: Architecture subclasses should implement this method. - .. note :: It is important that the assembler used accepts a syntax identical to the one emitted by the \ - disassembler. This will prevent confusing the user. - .. warning:: This method should never be called directly. - - :param str code: string representation of the instructions to be assembled - :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) - """ - 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 - the bytes contained in ``data`` at address addr is a branch instruction that can be made to never branch. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - 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 - the bytes contained in ``data`` at address addr is a conditional branch that can be made unconditional. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - 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 - the bytes contained in ``data`` at address addr is a conditional branch which can be inverted. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - 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 - the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions - that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call eax`` which could be - converted into ``xor eax,eax`` thus this function would return True. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - 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 - the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions - that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call 0xdeadbeef`` which could be - converted into ``mov eax, 42`` thus this function would return True. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - 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. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes at virtual address ``addr`` - :param int addr: the virtual address of the instruction to be patched - :return: nop sequence of same length as ``data`` or None - :rtype: str or None - """ - 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 - at ``addr`` to an unconditional branch. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - 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 - at ``addr``. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - 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 - the bytes in ``data`` at ``addr`` to one or more instructions that are equivilent to a function returning a - value. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :param int value: value to be returned - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - def get_associated_arch_by_address(self, addr): - new_addr = ctypes.c_ulonglong() - new_addr.value = addr - result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) - return Architecture(handle = result), new_addr.value - - def get_instruction_info(self, data, addr): - """ - ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address - ``addr`` with data ``data``. - - .. note :: The instruction info object should always set the InstructionInfo.length to the instruction length, \ - and the branches of the proper types shoulde be added if the instruction is a branch. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :return: the InstructionInfo for the current instruction - :rtype: InstructionInfo - """ - info = core.BNInstructionInfo() - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): - return None - result = InstructionInfo() - result.length = info.length - result.branch_delay = info.branchDelay - for i in xrange(0, info.branchCount): - branch_type = core.BNBranchType_names[info.branchType[i]] - target = info.branchTarget[i] - if info.branchArch[i]: - arch = Architecture(info.branchArch[i]) - else: - arch = None - result.add_branch(branch_type, target, arch) - return result - - def get_instruction_text(self, data, addr): - """ - ``get_instruction_text`` returns a list of InstructionTextToken objects for the instruction at the given virtual - address ``addr`` with data ``data``. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :return: an InstructionTextToken list for the current instruction - :rtype: list(InstructionTextToken) - """ - data = str(data) - count = ctypes.c_ulonglong() - length = ctypes.c_ulonglong() - length.value = len(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - tokens = ctypes.POINTER(core.BNInstructionTextToken)() - if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): - return None, 0 - result = [] - for i in xrange(0, count.value): - token_type = core.BNInstructionTextTokenType_names[tokens[i].type] - text = tokens[i].text - value = tokens[i].value - size = tokens[i].size - operand = tokens[i].operand - result.append(InstructionTextToken(token_type, text, value, size, operand)) - core.BNFreeInstructionText(tokens, count.value) - return result, length.value - - def get_instruction_low_level_il(self, data, addr, il): - """ - ``get_instruction_low_level_il`` appends LowLevelILExpr objects for the instruction at the given virtual - address ``addr`` with data ``data``. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :param LowLevelILFunction il: The function the current instruction belongs to - :return: the length of the current instruction - :rtype: int - """ - data = str(data) - length = ctypes.c_ulonglong() - length.value = len(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) - return length.value - - def get_reg_name(self, reg): - """ - ``get_reg_name`` gets a register name from a register number. - - :param int reg: register number - :return: the corresponding register string - :rtype: str - """ - return core.BNGetArchitectureRegisterName(self.handle, reg) - - def get_flag_name(self, flag): - """ - ``get_flag_name`` gets a flag name from a flag number. - - :param int reg: register number - :return: the corresponding register string - :rtype: str - """ - return core.BNGetArchitectureFlagName(self.handle, flag) - - def get_flag_write_type_name(self, write_type): - """ - ``get_flag_write_type_name`` gets the flag write type name for the given flag. - - :param int write_type: flag - :return: flag write type name - :rtype: str - """ - return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) - - def get_flag_by_name(self, flag): - """ - ``get_flag_by_name`` get flag name for flag index. - - :param int flag: flag index - :return: flag name for flag index - :rtype: str - """ - return self._flags[flag] - - def get_flag_write_type_by_name(self, write_type): - """ - ``get_flag_write_type_by_name`` gets the flag write type name for the flage write type. - - :param int write_type: flag write type - :return: flag write type - :rtype: str - """ - return self._flag_write_types[write_type] - - def get_flag_write_low_level_il(self, op, size, write_type, operands, il): - """ - :param LowLevelILOperation op: - :param int size: - :param str write_type: - :param list(str or int) operands: a list of either items that are either string register names or constant \ - integer values - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): - if isinstance(operands[i], str): - operand_list[i].constant = False - operand_list[i].reg = self._flags[operands[i]] - else: - operand_list[i].constant = True - operand_list[i].value = operands[i] - return LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, - self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) - - def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): - """ - :param LowLevelILOperation op: - :param int size: - :param str write_type: - :param list(str or int) operands: a list of either items that are either string register names or constant \ - integer values - :param LowLevelILFunction il: - :rtype: LowLevelILExpr index - """ - operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): - if isinstance(operands[i], str): - operand_list[i].constant = False - operand_list[i].reg = self._flags[operands[i]] - else: - operand_list[i].constant = True - operand_list[i].value = operands[i] - return LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, - self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) - - def get_flag_condition_low_level_il(self, cond, il): - """ - :param LowLevelILFlagCondition cond: - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) - - def get_modified_regs_on_write(self, reg): - """ - ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. - - :param str reg: string register name - :return: list of register names - :rtype: list(str) - """ - reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) - count = ctypes.c_ulonglong() - regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) - result = [] - for i in xrange(0, count.value): - result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) - core.BNFreeRegisterList(regs) - return result - - def assemble(self, code, addr = 0): - """ - ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the - byte representation of those instructions. - - :param str code: string representation of the instructions to be assembled - :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) - :Example: - - >>> arch.assemble("je 10") - ('\\x0f\\x84\\x04\\x00\\x00\\x00', '') - >>> - """ - result = DataBuffer() - errors = ctypes.c_char_p() - if not core.BNAssemble(self.handle, code, addr, result.handle, errors): - return None, errors.value - return str(result), errors.value - - def is_never_branch_patch_available(self, data, addr): - """ - ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. - - :param str data: bytes for the instruction to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> arch.is_never_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_never_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_always_branch_patch_available(self, data, addr): - """ - ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to - **always branch**. - - :param str data: bytes for the instruction to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> arch.is_always_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_always_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_invert_branch_patch_available(self, data, addr): - """ - ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. - - :param str data: bytes for the instruction to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> arch.is_invert_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_invert_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_skip_and_return_zero_patch_available(self, data, addr): - """ - ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* - instruction that can be made into an instruction *returns zero*. - - :param str data: bytes for the instruction to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call eax")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) - - def is_skip_and_return_value_patch_available(self, data, addr): - """ - ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* - instruction that can be made into an instruction *returns a value*. - - :param str data: bytes for the instruction to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - :Example: - - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) - - def convert_to_nop(self, data, addr): - """ - ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop - instructions of the same length as data. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) worth of no-operation instructions - :rtype: str - :Example: - - >>> arch.convert_to_nop("\\x00\\x00", 0) - '\\x90\\x90' - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def always_branch(self, data, addr): - """ - ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes - of the same length which always branches. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> bytes = arch.always_branch(arch.assemble("je 10")[0], 0) - >>> arch.get_instruction_text(bytes, 0) - (['nop '], 1L) - >>> arch.get_instruction_text(bytes[1:], 0) - (['jmp ', '0x9'], 5L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def invert_branch(self, data, addr): - """ - ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes - of the same length which inverts the branch of provided instruction. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("je 10")[0], 0), 0) - (['jne ', '0xa'], 6L) - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jo 10")[0], 0), 0) - (['jno ', '0xa'], 6L) - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jge 10")[0], 0), 0) - (['jl ', '0xa'], 6L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def skip_and_return_value(self, data, addr, value): - """ - ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of - bytes of the same length which doesn't call and instead *return a value*. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> arch.get_instruction_text(arch.skip_and_return_value(arch.assemble("call 10")[0], 0, 0), 0) - (['mov ', 'eax', ', ', '0x0'], 5L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def is_view_type_constant_defined(self, type_name, const_name): - """ - - :param str type_name: the BinaryView type name of the constant to query - :param str const_name: the constant name to query - :rtype: None - :Example: - - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> arch.is_view_type_constant_defined("ELF", "R_COPY") - True - >>> arch.is_view_type_constant_defined("ELF", "NOT_THERE") - False - >>> - """ - return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) - - def get_view_type_constant(self, type_name, const_name, default_value = 0): - """ - ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. - - :param str type_name: the BinaryView type name of the constant to be retrieved - :param str const_name: the constant name to retrieved - :param int value: optional default value if the type_name is not present. default value is zero. - :return: The BinaryView type constant or the default_value if not found - :rtype: int - :Example: - - >>> ELF_RELOC_COPY = 5 - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> arch.get_view_type_constant("ELF", "R_COPY") - 5L - >>> arch.get_view_type_constant("ELF", "NOT_HERE", 100) - 100L - """ - return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) - - def set_view_type_constant(self, type_name, const_name, value): - """ - ``set_view_type_constant`` creates a new binaryview type constant. - - :param str type_name: the BinaryView type name of the constant to be registered - :param str const_name: the constant name to register - :param int value: the value of the constant - :rtype: None - :Example: - - >>> ELF_RELOC_COPY = 5 - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> - """ - core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) - - def parse_types_from_source(self, source, filename = None, include_dirs = []): - """ - ``parse_types_from_source`` parses the source string and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str source: source string to be parsed - :param str filename: optional source filename - :param list(str) include_dirs: optional list of string filename include directories - :return: a tuple of py:class:`TypeParserResult` and error string - :rtype: tuple(TypeParserResult,str) - :Example: - - >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': - <type: int32_t(int32_t x)>}}, '') - >>> - """ - - if filename is None: - filename = "input" - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs)) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - return (None, error_str) - types = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - types[parse.types[i].name] = Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - 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 = []): - """ - ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str filename: filename of file to be parsed - :param list(str) include_dirs: optional list of string filename include directories - :return: a tuple of py:class:`TypeParserResult` and error string - :rtype: tuple(TypeParserResult, str) - :Example: - - >>> file = "/Users/binja/tmp.c" - >>> open(file).read() - 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' - >>> arch.parse_types_from_source_file(file) - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: - {'bar': <type: int32_t(int32_t x)>}}, '') - >>> - """ - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs)) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - return (None, error_str) - types = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - types[parse.types[i].name] = Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - 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): - """ - ``register_calling_convention`` registers a new calling convention for the Architecture. - - :param CallingConvention cc: CallingConvention object to be registered - :rtype: None - """ - core.BNRegisterCallingConvention(self.handle, cc.handle) - -class ReferenceSource(object): - def __init__(self, func, arch, addr): - self.function = func - self.arch = arch - self.address = addr - - def __repr__(self): - if self.arch: - return "<ref: %s@%#x>" % (self.arch.name, self.address) - else: - return "<ref: %#x>" % self.address +# Binary Ninja components +import _binaryninjacore as core +from .enums import * +from .databuffer import * +from .filemetadata import * +from .fileaccessor import * +from .binaryview import * +from .transform import * +from .architecture import * +from .basicblock import * +from .function import * +from .log import * +from .lowlevelil import * +from .types import * +from .functionrecognizer import * +from .update import * +from .plugin import * +from .callingconvention import * +from .platform import * +from .demangle import * +from .mainthread import * +from .interaction import * +from .lineardisassembly import * +from .undoaction import * +from .highlight import * +from .scriptingprovider import * -class LowLevelILLabel(object): - def __init__(self, handle = None): - if handle is None: - self.handle = (core.BNLowLevelILLabel * 1)() - core.BNLowLevelILInitLabel(self.handle) - else: - self.handle = handle -class LowLevelILInstruction(object): +def shutdown(): """ - ``class LowLevelILInstruction`` Low Level Intermediate Language Instructions are infinite length tree-based - instructions. Tree-based instructions use infix notation with the left hand operand being the destination operand. - Infix notation is thus more natural to read than other notations (e.g. x86 ``mov eax, 0`` vs. LLIL ``eax = 0``). + ``shutdown`` cleanly shuts down the core, stopping all workers and closing all log files. """ + core.BNShutdown() - ILOperations = { - 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")] - } - - def __init__(self, func, expr_index, instr_index = None): - instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) - self.function = func - self.expr_index = expr_index - self.instr_index = instr_index - self.operation = instr.operation - self.operation_name = core.BNLowLevelILOperation_names[instr.operation] - self.size = instr.size - self.address = instr.address - self.source_operand = instr.sourceOperand - if instr.flags == 0: - self.flags = None - else: - self.flags = func.arch.get_flag_write_type_name(instr.flags) - if self.source_operand == 0xffffffff: - self.source_operand = None - operands = LowLevelILInstruction.ILOperations[instr.operation] - self.operands = [] - for i in xrange(0, len(operands)): - name, operand_type = operands[i] - if operand_type == "int": - value = instr.operands[i] - elif operand_type == "expr": - value = LowLevelILInstruction(func, instr.operands[i]) - elif operand_type == "reg": - if (instr.operands[i] & 0x80000000) != 0: - value = instr.operands[i] - else: - value = func.arch.get_reg_name(instr.operands[i]) - elif operand_type == "flag": - value = func.arch.get_flag_name(instr.operands[i]) - elif operand_type == "cond": - value = core.BNLowLevelILFlagCondition_names[instr.operands[i]] - elif operand_type == "int_list": - count = ctypes.c_ulonglong() - operands = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) - value = [] - for i in xrange(count.value): - value.append(operands[i]) - core.BNLowLevelILFreeOperandList(operands) - self.operands.append(value) - self.__dict__[name] = value - - def __str__(self): - tokens = self.tokens - if tokens is None: - return "invalid" - result = "" - for token in tokens: - result += token.text - return result - - def __repr__(self): - return "<il: %s>" % str(self) - - @property - def tokens(self): - """LLIL tokens (read-only)""" - count = ctypes.c_ulonglong() - tokens = ctypes.POINTER(core.BNInstructionTextToken)() - if (self.instr_index is not None) and (self.function.source_function is not None): - if not core.BNGetLowLevelILInstructionText(self.function.handle, self.function.source_function.handle, - self.function.arch.handle, self.instr_index, tokens, count): - return None - else: - if not core.BNGetLowLevelILExprText(self.function.handle, self.function.arch.handle, - self.expr_index, tokens, count): - return None - result = [] - for i in xrange(0, count.value): - token_type = core.BNInstructionTextTokenType_names[tokens[i].type] - text = tokens[i].text - value = tokens[i].value - size = tokens[i].size - operand = tokens[i].operand - result.append(InstructionTextToken(token_type, text, value, size, operand)) - core.BNFreeInstructionText(tokens, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name -class LowLevelILExpr(object): - """ - ``class LowLevelILExpr`` hold the index of IL Expressions. +def get_unique_identifier(): + return core.BNGetUniqueIdentifierString() - .. note:: This class shouldn't be instantiated directly. Rather the helper members of LowLevelILFunction should be \ - used instead. - """ - def __init__(self, index): - self.index = index -class LowLevelILFunction(object): +def get_install_directory(): """ - ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr - objects can be added to the LowLevelILFunction by calling ``append`` and passing the result of the various class - methods which return LowLevelILExpr objects. - + ``get_install_directory`` returns a string pointing to the installed binary currently running - LowLevelILFlagCondition values used as parameters in the ``flag_condition`` method. - - ======================= ========== =============================== - LowLevelILFlagCondition Operator Description - ======================= ========== =============================== - LLFC_E == Equal - LLFC_NE != Not equal - LLFC_SLT s< Signed less than - LLFC_ULT u< Unsigned less than - LLFC_SLE s<= Signed less than or equal - LLFC_ULE u<= Unsigned less than or equal - LLFC_SGE s>= Signed greater than or equal - LLFC_UGE u>= Unsigned greater than or equal - LLFC_SGT s> Signed greather than - LLFC_UGT u> Unsigned greater than - LLFC_NEG - Negative - LLFC_POS + Positive - LLFC_O overflow Overflow - LLFC_NO !overflow No overflow - ======================= ========== =============================== + .warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly """ - def __init__(self, arch, handle = None, source_func = None): - self.arch = arch - self.source_function = source_func - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) - else: - 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) - - @property - def current_address(self): - """Current IL Address (read/write)""" - return core.BNLowLevelILGetCurrentAddress(self.handle) - - @current_address.setter - def current_address(self, value): - core.BNLowLevelILSetCurrentAddress(self.handle, value) - - @property - def temp_reg_count(self): - """Number of temporary registers (read-only)""" - return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) - - @property - def temp_flag_count(self): - """Number of temporary flags (read-only)""" - return core.BNGetLowLevelILTemporaryFlagCount(self.handle) - - @property - def basic_blocks(self): - """list of LowLevelILBasicBlock objects (read-only)""" - 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 - for i in xrange(0, count.value): - result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) - core.BNFreeBasicBlockList(blocks, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __len__(self): - return int(core.BNGetLowLevelILInstructionCount(self.handle)) - - def __getitem__(self, i): - if isinstance(i, slice) or isinstance(i, tuple): - raise IndexError, "expected integer instruction index" - if isinstance(i, LowLevelILExpr): - return LowLevelILInstruction(self, i.index) - if (i < 0) or (i >= len(self)): - raise IndexError, "index out of range" - return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i), 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) - view = None - if self.source_function is not None: - view = self.source_function.view - try: - for i in xrange(0, count.value): - yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) - finally: - core.BNFreeBasicBlockList(blocks, count.value) - - def clear_indirect_branches(self): - core.BNLowLevelILClearIndirectBranches(self.handle) - - def set_indirect_branches(self, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) - - def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): - if isinstance(operation, str): - operation = core.BNLowLevelILOperation_by_name[operation] - if isinstance(flags, str): - flags = self.arch.get_flag_write_type_by_name(flags) - elif flags is None: - flags = 0 - return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) - - def append(self, expr): - """ - ``append`` adds the LowLevelILExpr ``expr`` to the current LowLevelILFunction. - - :param LowLevelILExpr expr: the LowLevelILExpr to add to the current LowLevelILFunction - :return: number of LowLevelILExpr in the current function - :rtype: int - """ - return core.BNLowLevelILAddInstruction(self.handle, expr.index) - - def nop(self): - """ - ``nop`` no operation, this instruction does nothing - - :return: The no operation expression - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NOP) - - def set_reg(self, size, reg, value, flags = 0): - """ - ``set_reg`` sets the register ``reg`` of size ``size`` to the expression ``value`` - - :param int size: size of the register parameter in bytes - :param str reg: the register name - :param LowLevelILExpr value: an expression to set the register to - :param str flags: which flags are set by this operation - :return: The expression ``reg = value`` - :rtype: LowLevelILExpr - """ - if isinstance(reg, str): - reg = self.arch.regs[reg].index - return self.expr(core.LLIL_SET_REG, reg, value.index, size = size, flags = flags) - - def set_reg_split(self, size, hi, lo, value, flags = 0): - """ - ``set_reg_split`` uses ``hi`` and ``lo`` as a single extended register setting ``hi:lo`` to the expression - ``value``. - - :param int size: size of the register parameter in bytes - :param str hi: the high register name - :param str lo: the low register name - :param LowLevelILExpr value: an expression to set the split regiters to - :param str flags: which flags are set by this operation - :return: The expression ``hi:lo = value`` - :rtype: LowLevelILExpr - """ - if isinstance(hi, str): - hi = self.arch.regs[hi].index - if isinstance(lo, str): - lo = self.arch.regs[lo].index - return self.expr(core.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) - - def set_flag(self, flag, value): - """ - ``set_flag`` sets the flag ``flag`` to the LowLevelILExpr ``value`` - - :param str flag: the low register name - :param LowLevelILExpr value: an expression to set the flag to - :return: The expression FLAG.flag = value - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SET_FLAG, self.arch.get_flag_by_name(flag), value.index) - - def load(self, size, addr): - """ - ``laod`` Reads ``size`` bytes from the expression ``addr`` - - :param int size: number of bytes to read - :param LowLevelILExpr addr: the expression to read memory from - :return: The expression ``[addr].size`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LOAD, addr.index, size = size) - - def store(self, size, addr, value): - """ - ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` - - :param int size: number of bytes to write - :param LowLevelILExpr addr: the expression to write to - :param LowLevelILExpr value: the expression to be written - :return: The expression ``[addr].size = value`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_STORE, addr.index, value.index, size = size) - - def push(self, size, value): - """ - ``push`` writes ``size`` bytes from expression ``value`` to the stack, adjusting the stack by ``size``. - - :param int size: number of bytes to write and adjust the stack by - :param LowLevelILExpr value: the expression to write - :return: The expression push(value) - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_PUSH, value.index, size = size) - - def pop(self, size): - """ - ``pop`` reads ``size`` bytes from the stack, adjusting the stack by ``size``. - - :param int size: number of bytes to read from the stack - :return: The expression ``pop`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_POP, size = size) - - def reg(self, size, reg): - """ - ``reg`` returns a register of size ``size`` with name ``name`` - - :param int size: the size of the register in bytes - :param str reg: the name of the register - :return: A register expression for the given string - :rtype: LowLevelILExpr - """ - if isinstance(reg, str): - reg = self.arch.regs[reg].index - return self.expr(core.LLIL_REG, reg, size = size) - - def const(self, size, value): - """ - ``const`` returns an expression for the constant integer ``value`` with size ``size`` - - :param int size: the size of the constant in bytes - :param int value: integer value of the constant - :return: A constant expression of given value and size - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CONST, value, size = size) - - def flag(self, reg): - """ - ``flag`` returns a flag expression for the given flag name. - - :param str reg: name of the flag expression to retrieve - :return: A flag expression of given flag name - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_FLAG, self.arch.get_flag_by_name(reg)) - - def flag_bit(self, size, reg, bit): - """ - ``flag_bit`` sets the flag named ``reg`` and size ``size`` to the constant integer value ``bit`` - - :param int size: the size of the flag - :param str reg: flag value - :param int bit: integer value to set the bit to - :return: A constant expression of given value and size ``FLAG.reg = bit`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size = size) - - def add(self, size, a, b, flags = None): - """ - ``add`` adds expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``add.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ADD, a.index, b.index, size = size, flags = flags) - - def add_carry(self, size, a, b, flags = None): - """ - ``add_carry`` adds with carry expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``adc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ADC, a.index, b.index, size = size, flags = flags) - - def sub(self, size, a, b, flags = None): - """ - ``sub`` subtracts expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``sub.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SUB, a.index, b.index, size = size, flags = flags) - - def sub_borrow(self, size, a, b, flags = None): - """ - ``sub_borrow`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``sbc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SBB, a.index, b.index, size = size, flags = flags) - - def and_expr(self, size, a, b, flags = None): - """ - ``and_expr`` bitwise and's expression ``a`` and expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``and.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_AND, a.index, b.index, size = size, flags = flags) - - def or_expr(self, size, a, b, flags = None): - """ - ``or_expr`` bitwise or's expression ``a`` and expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``or.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_OR, a.index, b.index, size = size, flags = flags) - - def xor_expr(self, size, a, b, flags = None): - """ - ``xor_expr`` xor's expression ``a`` with expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``xor.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_XOR, a.index, b.index, size = size, flags = flags) - - def shift_left(self, size, a, b, flags = None): - """ - ``shift_left`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``lsl.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LSL, a.index, b.index, size = size, flags = flags) - - def logical_shift_right(self, size, a, b, flags = None): - """ - ``logical_shift_right`` shifts logically right expression ``a`` by expression ``b`` potentially setting flags - ``flags``and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``lsr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LSR, a.index, b.index, size = size, flags = flags) - - def arith_shift_right(self, size, a, b, flags = None): - """ - ``arith_shift_right`` shifts arithmatic right expression ``a`` by expression ``b`` potentially setting flags - ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``asr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ASR, a.index, b.index, size = size, flags = flags) - - def rotate_left(self, size, a, b, flags = None): - """ - ``rotate_left`` bitwise rotates left expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rol.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ROL, a.index, b.index, size = size, flags = flags) - - def rotate_left_carry(self, size, a, b, flags = None): - """ - ``rotate_left_carry`` bitwise rotates left with carry expression ``a`` by expression ``b`` potentially setting - flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rcl.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RLC, a.index, b.index, size = size, flags = flags) - - def rotate_right(self, size, a, b, flags = None): - """ - ``rotate_right`` bitwise rotates right expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``ror.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ROR, a.index, b.index, size = size, flags = flags) - - def rotate_right_carry(self, size, a, b, flags = None): - """ - ``rotate_right_carry`` bitwise rotates right with carry expression ``a`` by expression ``b`` potentially setting - flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rcr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RRC, a.index, b.index, size = size, flags = flags) - - def mult(self, size, a, b, flags = None): - """ - ``mult`` multiplies expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``sbc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MUL, a.index, b.index, size = size, flags = flags) - - def mult_double_prec_signed(self, size, a, b, flags = None): - """ - ``mult_double_prec_signed`` multiplies signed with double precision expression ``a`` by expression ``b`` - potentially setting flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MULS_DP, a.index, b.index, size = size, flags = flags) - - def mult_double_prec_unsigned(self, size, a, b, flags = None): - """ - ``mult_double_prec_unsigned`` multiplies unsigned with double precision expression ``a`` by expression ``b`` - potentially setting flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MULU_DP, a.index, b.index, size = size, flags = flags) - - def div_signed(self, size, a, b, flags = None): - """ - ``div_signed`` signed divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) - - def div_double_prec_signed(self, size, hi, lo, b, flags = None): - """ - ``div_double_prec_signed`` signed double precision divide using expression ``hi`` and expression ``lo`` as a single - double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression - of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def div_unsigned(self, size, a, b, flags = None): - """ - ``div_unsigned`` unsigned divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) - - def div_double_prec_unsigned(self, size, hi, lo, b, flags = None): - """ - ``div_double_prec_unsigned`` unsigned double precision divide using expression ``hi`` and expression ``lo`` as - a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def mod_signed(self, size, a, b, flags = None): - """ - ``mod_signed`` signed modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``mods.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) - - def mod_double_prec_signed(self, size, hi, lo, b, flags = None): - """ - ``mod_double_prec_signed`` signed double precision modulus using expression ``hi`` and expression ``lo`` as a single - double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression - of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``mods.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def mod_unsigned(self, size, a, b, flags = None): - """ - ``mod_unsigned`` unsigned modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``modu.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) - - def mod_double_prec_unsigned(self, size, hi, lo, b, flags = None): - """ - ``mod_double_prec_unsigned`` unsigned double precision modulus using expression ``hi`` and expression ``lo`` as - a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``modu.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def neg_expr(self, size, value, flags = None): - """ - ``neg_expr`` two's complement sign negation of expression ``value`` of size ``size`` potentially setting flags - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to negate - :param str flags: optional, flags to set - :return: The expression ``neg.<size>{<flags>}(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NEG, value.index, size = size, flags = flags) - - def not_expr(self, size, value, flags = None): - """ - ``not_expr`` bitwise inverse of expression ``value`` of size ``size`` potentially setting flags - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to bitwise invert - :param str flags: optional, flags to set - :return: The expression ``not.<size>{<flags>}(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NOT, value.index, size = size, flags = flags) - - def sign_extend(self, size, value, flags = None): - """ - ``sign_extend`` two's complement sign-extends the expression in ``value`` to ``size`` bytes - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to sign extend - :param str flags: optional, flags to set - :return: The expression ``sx.<size>(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SX, value.index, size = size, flags = flags) - - def zero_extend(self, size, value): - """ - ``sign_extend`` zero-extends the expression in ``value`` to ``size`` bytes - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to zero extend - :return: The expression ``sx.<size>(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ZX, value.index, size = size) - - def jump(self, dest): - """ - ``jump`` returns an expression which jumps (branches) to the expression ``dest`` - - :param LowLevelILExpr dest: the expression to jump to - :return: The expression ``jump(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_JUMP, dest.index) - - def call(self, dest): - """ - ``call`` returns an expression which first pushes the address of the next instruction onto the stack then jumps - (branches) to the expression ``dest`` - - :param LowLevelILExpr dest: the expression to call - :return: The expression ``call(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CALL, dest.index) - - def ret(self, dest): - """ - ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for - jump that makes the disassembler top disassembling. - - :param LowLevelILExpr dest: the expression to jump to - :return: The expression ``jump(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RET, dest.index) - - def no_ret(self): - """ - ``no_ret`` returns an expression halts disassembly - - :return: The expression ``noreturn`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NORET) - - def flag_condition(self, cond): - """ - ``flag_condition`` returns a flag_condition expression for the given LowLevelILFlagCondition - - :param LowLevelILFlagCondition cond: Flag condition expression to retrieve - :return: A flag_condition expression - :rtype: LowLevelILExpr - """ - if isinstance(cond, str): - cond = core.BNLowLevelILFlagCondition_by_name[cond] - return self.expr(core.LLIL_FLAG_COND, cond) - - def compare_equal(self, size, a, b): - """ - ``compare_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is equal to - expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_E, a.index, b.index, size = size) - - def compare_not_equal(self, size, a, b): - """ - ``compare_not_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is not equal to - expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_NE, a.index, b.index, size = size) - - def compare_signed_less_than(self, size, a, b): - """ - ``compare_signed_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed less than expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SLT, a.index, b.index, size = size) - - def compare_unsigned_less_than(self, size, a, b): - """ - ``compare_unsigned_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned less than expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_ULT, a.index, b.index, size = size) - - def compare_signed_less_equal(self, size, a, b): - """ - ``compare_signed_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed less than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SLE, a.index, b.index, size = size) - - def compare_unsigned_less_equal(self, size, a, b): - """ - ``compare_unsigned_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned less than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_ULE, a.index, b.index, size = size) - - def compare_signed_greater_equal(self, size, a, b): - """ - ``compare_signed_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed greater than or equal toexpression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SGE, a.index, b.index, size = size) - - def compare_unsigned_greater_equal(self, size, a, b): - """ - ``compare_unsigned_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` - is unsigned greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_UGE, a.index, b.index, size = size) - - def compare_signed_greater_than(self, size, a, b): - """ - ``compare_signed_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SGT, a.index, b.index, size = size) - - def compare_unsigned_greater_than(self, size, a, b): - """ - ``compare_unsigned_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_UGT, a.index, b.index, size = size) - - def test_bit(self, size, a, b): - return self.expr(core.LLIL_TEST_BIT, a.index, b.index, size = size) - - def system_call(self): - """ - ``system_call`` return a system call expression. - - :return: a system call expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SYSCALL) - - def breakpoint(self): - """ - ``breakpoint`` returns a processor breakpoint expression. - - :return: a breakpoint expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_BP) - - def trap(self, value): - """ - ``trap`` returns a processor trap (interrupt) expression of the given integer ``value``. - - :param int value: trap (interrupt) number - :return: a trap expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_TRAP, value) - - def undefined(self): - """ - ``undefined`` returns the undefined expression. This should be used for instructions which perform functions but - aren't important for dataflow or partial emulation purposes. - - :return: the unimplemented expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNDEF) - - def unimplemented(self): - """ - ``unimplemented`` returns the unimplemented expression. This should be used for all instructions which aren't - implemented. - - :return: the unimplemented expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNIMPL) - - def unimplemented_memory_ref(self, size, addr): - """ - ``unimplemented_memory_ref`` a memory reference to expression ``addr`` of size ``size`` with unimplemented operation. - - :param int size: size in bytes of the memory reference - :param LowLevelILExpr addr: expression to reference memory - :return: the unimplemented memory reference expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNIMPL_MEM, addr.index, size = size) - - def goto(self, label): - """ - ``goto`` returns a goto expression which jumps to the provided LowLevelILLabel. - - :param LowLevelILLabel label: Label to jump to - :return: the LowLevelILExpr that jumps to the provided label - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) - - def if_expr(self, operand, t, f): - """ - ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the LowLevelILLabel - ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. - - :param LowLevelILExpr operand: comparison expression to evaluate. - :param LowLevelILLabel t: Label for the true branch - :param LowLevelILLabel f: Label for the false branch - :return: the LowLevelILExpr for the if expression - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) - - def mark_label(self, label): - """ - ``mark_label`` assigns a LowLevelILLabel to the current IL address. - - :param LowLevelILLabel label: - :rtype: None - """ - core.BNLowLevelILMarkLabel(self.handle, label.handle) - - def add_label_list(self, labels): - """ - ``add_label_list`` returns a label list expression for the given list of LowLevelILLabel objects. - - :param list(LowLevelILLabel) lables: the list of LowLevelILLabel to get a label list expression from - :return: the label list expression - :rtype: LowLevelILExpr - """ - 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))) - - def add_operand_list(self, operands): - """ - ``add_operand_list`` returns an operand list expression for the given list of integer operands. - - :param list(int) operands: list of operand numbers - :return: an operand list expression - :rtype: LowLevelILExpr - """ - operand_list = (ctypes.c_ulonglong * len(operands))() - for i in xrange(len(operands)): - operand_list[i] = operands[i] - return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) - - def operand(self, n, expr): - """ - ``operand`` sets the operand number of the expression ``expr`` and passes back ``expr`` without modification. - - :param int n: - :param LowLevelILExpr expr: - :return: returns the expression ``expr`` unmodified - :rtype: LowLevelILExpr - """ - core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) - return expr - - def finalize(self): - """ - ``finalize`` ends the function and computes the list of basic blocks. - - :rtype: None - """ - core.BNFinalizeLowLevelILFunction(self.handle) - - def add_label_for_address(self, arch, addr): - """ - ``add_label_for_address`` adds a low-level IL label for the given architecture ``arch`` at the given virtual - address ``addr`` - - :param Architecture arch: Architecture to add labels for - :param int addr: the IL address to add a label at - """ - if arch is not None: - arch = arch.handle - core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) + return core.BNGetInstallDirectory() - def get_label_for_address(self, arch, addr): - """ - ``get_label_for_address`` returns the LowLevelILLabel for the given Architecture ``arch`` and IL address ``addr``. - - :param Architecture arch: - :param int addr: IL Address label to retrieve - :return: the LowLevelILLabel for the given IL address - :rtype: LowLevelILLabel - """ - if arch is not None: - arch = arch.handle - label = core.BNGetLowLevelILLabelForAddress(self.handle, arch, addr) - if label is None: - return None - return LowLevelILLabel(label) - -class TypeParserResult(object): - def __init__(self, types, variables, functions): - self.types = types - self.variables = variables - self.functions = functions - - def __repr__(self): - return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) - -class _TransformMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - xforms = core.BNGetTransformTypeList(count) - result = [] - for i in xrange(0, count.value): - result.append(Transform(xforms[i])) - core.BNFreeTransformTypeList(xforms) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - xforms = core.BNGetTransformTypeList(count) - try: - for i in xrange(0, count.value): - yield Transform(xforms[i]) - finally: - core.BNFreeTransformTypeList(xforms) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, name): - _init_plugins() - xform = core.BNGetTransformByName(name) - if xform is None: - raise KeyError, "'%s' is not a valid transform" % str(name) - return Transform(xform) - - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "transform 'name' is not defined" - if cls.long_name is None: - cls.long_name = cls.name - if cls.transform_type is None: - raise ValueError, "transform 'transform_type' is not defined" - if cls.group is None: - cls.group = "" - xform = cls(None) - cls._registered_cb = xform._cb - xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) - -class TransformParameter(object): - def __init__(self, name, long_name = None, fixed_length = 0): - self.name = name - if long_name is None: - self.long_name = name - else: - self.long_name = long_name - self.fixed_length = fixed_length - -class Transform: - transform_type = None - name = None - long_name = None - group = None - parameters = [] - _registered_cb = None - __metaclass__ = _TransformMetaClass - - def __init__(self, handle): - if handle is None: - self._cb = core.BNCustomTransform() - self._cb.context = 0 - self._cb.getParameters = self._cb.getParameters.__class__(self._get_parameters) - self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters) - self._cb.decode = self._cb.decode.__class__(self._decode) - self._cb.encode = self._cb.encode.__class__(self._encode) - self._pending_param_lists = {} - self.type = self.__class__.transform_type - if not isinstance(self.type, str): - self.type = core.BNTransformType_names[self.type] - self.name = self.__class__.name - self.long_name = self.__class__.long_name - self.group = self.__class__.group - self.parameters = self.__class__.parameters - else: - self.handle = handle - self.type = core.BNTransformType_names[core.BNGetTransformType(self.handle)] - self.name = core.BNGetTransformName(self.handle) - self.long_name = core.BNGetTransformLongName(self.handle) - self.group = core.BNGetTransformGroup(self.handle) - count = ctypes.c_ulonglong() - params = core.BNGetTransformParameterList(self.handle, count) - self.parameters = [] - for i in xrange(0, count.value): - self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) - core.BNFreeTransformParameterList(params, count.value) - - def __repr__(self): - return "<transform: %s>" % self.name - - def _get_parameters(self, ctxt, count): - try: - count[0] = len(self.parameters) - param_buf = (core.BNTransformParameterInfo * len(self.parameters))() - for i in xrange(0, len(self.parameters)): - param_buf[i].name = self.parameters[i].name - param_buf[i].longName = self.parameters[i].long_name - param_buf[i].fixedLength = self.parameters[i].fixed_length - result = ctypes.cast(param_buf, ctypes.c_void_p) - self._pending_param_lists[result.value] = (result, param_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _free_parameters(self, params, count): - try: - buf = ctypes.cast(params, ctypes.c_void_p) - if buf.value not in self._pending_param_lists: - raise ValueError, "freeing parameter list that wasn't allocated" - del self._pending_param_lists[buf.value] - except: - log_error(traceback.format_exc()) - - def _decode(self, ctxt, input_buf, output_buf, params, count): - try: - input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) - param_map = {} - for i in xrange(0, count): - data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) - param_map[params[i].name] = str(data) - result = self.perform_decode(str(input_obj), param_map) - if result is None: - return False - result = str(result) - core.BNSetDataBufferContents(output_buf, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _encode(self, ctxt, input_buf, output_buf, params, count): - try: - input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) - param_map = {} - for i in xrange(0, count): - data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) - param_map[params[i].name] = str(data) - result = self.perform_encode(str(input_obj), param_map) - if result is None: - return False - result = str(result) - core.BNSetDataBufferContents(output_buf, result, len(result)) - return True - except: - 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 - - def decode(self, input_buf, params = {}): - input_buf = DataBuffer(input_buf) - output_buf = DataBuffer() - keys = params.keys() - param_buf = (core.BNTransformParameter * len(keys))() - param_data = [] - for i in xrange(0, len(keys)): - data = DataBuffer(params[keys[i]]) - param_buf[i].name = keys[i] - param_buf[i].value = data.handle - if not core.BNDecode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): - return None - return str(output_buf) - - def encode(self, input_buf, params = {}): - input_buf = DataBuffer(input_buf) - output_buf = DataBuffer() - keys = params.keys() - param_buf = (core.BNTransformParameter * len(keys))() - param_data = [] - for i in xrange(0, len(keys)): - data = DataBuffer(params[keys[i]]) - param_buf[i].name = keys[i] - param_buf[i].value = data.handle - if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): - return None - return str(output_buf) - -class FunctionRecognizer(object): - _instance = None - def __init__(self): - self._cb = core.BNFunctionRecognizer() - self._cb.context = 0 - self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) - - @classmethod - def register_global(cls): - if cls._instance is None: - cls._instance = cls() - core.BNRegisterGlobalFunctionRecognizer(cls._instance._cb) - - @classmethod - def register_arch(cls, arch): - if cls._instance is None: - cls._instance = cls() - core.BNRegisterArchitectureFunctionRecognizer(arch.handle, cls._instance._cb) - - def _recognize_low_level_il(self, ctxt, data, func, il): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(data)) - view = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) - func = Function(view, handle = core.BNNewFunctionReference(func)) - il = LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) - return self.recognize_low_level_il(view, func, il) - except: - log_error(traceback.format_exc()) - return False - - def recognize_low_level_il(self, data, func, il): - return False - -class _UpdateChannelMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = [] - for i in xrange(0, count.value): - result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) - core.BNFreeUpdateChannelList(channels, count.value) - return result - - @property - def active(self): - return core.BNGetActiveUpdateChannel() - - @active.setter - def active(self, value): - return core.BNSetActiveUpdateChannel(value) - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - try: - for i in xrange(0, count.value): - yield UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) - finally: - core.BNFreeUpdateChannelList(channels, count.value) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, name): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = None - for i in xrange(0, count.value): - if channels[i].name == str(name): - result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) - break - core.BNFreeUpdateChannelList(channels, count.value) - if result is None: - raise KeyError, "'%s' is not a valid channel" % str(name) - return result - -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 - - def callback(self, ctxt, progress, total): - try: - if self.func is not None: - return self.func(progress, total) - return True - except: - log_error(traceback.format_exc()) - -class UpdateChannel(object): - __metaclass__ = _UpdateChannelMetaClass - - def __init__(self, name, desc, ver): - self.name = name - self.description = desc - self.latest_version_num = ver - - @property - def versions(self): - """List of versions (read-only)""" - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - versions = core.BNGetUpdateChannelVersions(self.name, count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = [] - for i in xrange(0, count.value): - result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) - core.BNFreeUpdateChannelVersionList(versions, count.value) - return result - - @property - def latest_version(self): - """Latest version (read-only)""" - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - versions = core.BNGetUpdateChannelVersions(self.name, count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = None - for i in xrange(0, count.value): - if versions[i].version == self.latest_version_num: - result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) - break - core.BNFreeUpdateChannelVersionList(versions, count.value) - return result - - @property - def updates_available(self): - """Whether updates are available (read-only)""" - errors = ctypes.c_char_p() - result = core.BNAreUpdatesAvailable(self.name, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<channel: %s>" % self.name - - def __str__(self): - return self.name - - def update_to_latest(self, progress = None): - cb = UpdateProgressCallback(progress) - errors = ctypes.c_char_p() - result = core.BNUpdateToLatestVersion(self.name, errors, cb.cb, None) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return core.BNUpdateResult_names[result] - -class UpdateVersion(object): - def __init__(self, channel, ver, notes, t): - self.channel = channel - self.version = ver - self.notes = notes - self.time = t - - def __repr__(self): - return "<version: %s>" % self.version - - def __str__(self): - return self.version - - def update(self, progress = None): - cb = UpdateProgressCallback(progress) - errors = ctypes.c_char_p() - result = core.BNUpdateToVersion(self.channel.name, self.version, errors, cb.cb, None) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return core.BNUpdateResult_names[result] - -class PluginCommandContext(object): - def __init__(self, view): - self.view = view - self.address = 0 - self.length = 0 - self.function = None - -class _PluginCommandMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - commands = core.BNGetAllPluginCommands(count) - result = [] - for i in xrange(0, count.value): - result.append(PluginCommand(commands[i])) - core.BNFreePluginCommandList(commands) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - commands = core.BNGetAllPluginCommands(count) - try: - for i in xrange(0, count.value): - yield PluginCommand(commands[i]) - finally: - core.BNFreePluginCommandList(commands) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class PluginCommand: - _registered_commands = [] - __metaclass__ = _PluginCommandMetaClass - - def __init__(self, cmd): - self.command = core.BNPluginCommand() - ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) - self.name = str(cmd.name) - self.description = str(cmd.description) - self.type = core.BNPluginCommandType_names[cmd.type] - - @classmethod - def _default_action(cls, view, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj) - except: - log_error(traceback.format_exc()) - - @classmethod - def _address_action(cls, view, addr, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj, addr) - except: - log_error(traceback.format_exc()) - - @classmethod - def _range_action(cls, view, addr, length, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj, addr, length) - except: - log_error(traceback.format_exc()) - - @classmethod - def _function_action(cls, view, func, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = Function(view_obj, core.BNNewFunctionReference(func)) - action(view_obj, func_obj) - except: - log_error(traceback.format_exc()) - - @classmethod - def _default_is_valid(cls, view, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _address_is_valid(cls, view, addr, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj, addr) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _range_is_valid(cls, view, addr, length, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj, addr, length) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _function_is_valid(cls, view, func, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = Function(view_obj, core.BNNewFunctionReference(func)) - return is_valid(view_obj, func_obj) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def register(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_action(view, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_is_valid(view, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommand(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_address(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_action(view, addr, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_is_valid(view, addr, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForAddress(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_range(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_action(view, addr, length, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_is_valid(view, addr, length, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForRange(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_function(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_action(view, func, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_is_valid(view, func, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None) - - @classmethod - def get_valid_list(cls, context): - commands = cls.list - result = [] - for cmd in commands: - if cmd.is_valid(context): - result.append(cmd) - return result - - def is_valid(self, context): - if context.view is None: - return False - if self.command.type == core.DefaultPluginCommand: - if not self.command.defaultIsValid: - return True - return self.command.defaultIsValid(self.command.context, context.view.handle) - elif self.command.type == core.AddressPluginCommand: - if not self.command.addressIsValid: - return True - return self.command.addressIsValid(self.command.context, context.view.handle, context.address) - elif self.command.type == core.RangePluginCommand: - if context.length == 0: - return False - if not self.command.rangeIsValid: - return True - return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == core.FunctionPluginCommand: - if context.function is None: - return False - if not self.command.functionIsValid: - return True - return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle) - return False - - def execute(self, context): - if not self.is_valid(context): - return - if self.command.type == core.DefaultPluginCommand: - self.command.defaultCommand(self.command.context, context.view.handle) - elif self.command.type == core.AddressPluginCommand: - self.command.addressCommand(self.command.context, context.view.handle, context.address) - elif self.command.type == core.RangePluginCommand: - self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == core.FunctionPluginCommand: - self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) - - def __repr__(self): - return "<PluginCommand: %s>" % self.name - -class CallingConvention(object): - name = None - caller_saved_regs = [] - int_arg_regs = [] - float_arg_regs = [] - arg_regs_share_index = False - stack_reserved_for_arg_regs = False - int_return_reg = None - high_int_return_reg = None - float_return_reg = None - - _registered_calling_conventions = [] - - def __init__(self, arch, handle = None): - if handle is None: - self.arch = arch - self._pending_reg_lists = {} - self._cb = core.BNCustomCallingConvention() - self._cb.context = 0 - self._cb.getCallerSavedRegisters = self._cb.getCallerSavedRegisters.__class__(self._get_caller_saved_regs) - self._cb.getIntegerArgumentRegisters = self._cb.getIntegerArgumentRegisters.__class__(self._get_int_arg_regs) - self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs) - self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) - self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) - self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) - self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) - self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) - self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) - self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) - self.__class__._registered_calling_conventions.append(self) - else: - self.handle = handle - self.arch = Architecture(core.BNGetCallingConventionArchitecture(self.handle)) - self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) - self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) - self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) - - count = ctypes.c_ulonglong() - regs = core.BNGetCallerSavedRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["caller_saved_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetIntegerArgumentRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["int_arg_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetFloatArgumentRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["float_arg_regs"] = result - - reg = core.BNGetIntegerReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["int_return_reg"] = None - else: - self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetHighIntegerReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["high_int_return_reg"] = None - else: - self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetFloatReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["float_return_reg"] = None - else: - self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) - - def __del__(self): - core.BNFreeCallingConvention(self.handle) - - def _get_caller_saved_regs(self, ctxt, count): - try: - regs = self.__class__.caller_saved_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_int_arg_regs(self, ctxt, count): - try: - regs = self.__class__.int_arg_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_float_arg_regs(self, ctxt, count): - try: - regs = self.__class__.float_arg_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _free_register_list(self, ctxt, regs): - try: - buf = ctypes.cast(regs, ctypes.c_void_p) - 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: - log_error(traceback.format_exc()) - - def _arg_regs_share_index(self, ctxt): - try: - return self.__class__.arg_regs_share_index - except: - log_error(traceback.format_exc()) - return False - - def _stack_reserved_for_arg_regs(self, ctxt): - try: - return self.__class__.stack_reserved_for_arg_regs - except: - log_error(traceback.format_exc()) - return False - - def _get_int_return_reg(self, ctxt): - try: - return self.arch.regs[self.__class__.int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def _get_high_int_return_reg(self, ctxt): - try: - if self.__class__.high_int_return_reg is None: - return 0xffffffff - return self.arch.regs[self.__class__.high_int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def _get_float_return_reg(self, ctxt): - try: - if self.__class__.float_return_reg is None: - return 0xffffffff - return self.arch.regs[self.__class__.float_int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def __repr__(self): - return "<calling convention: %s %s>" % (self.arch.name, self.name) - - def __str__(self): - return self.name - -class _PlatformMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformList(count) - result = [] - for i in xrange(0, count.value): - result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - - @property - def os_list(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformOSList(count) - result = [] - for i in xrange(0, count.value): - result.append(str(platforms[i])) - core.BNFreePlatformOSList(platforms, count.value) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformList(count) - try: - for i in xrange(0, count.value): - yield Platform(None, core.BNNewPlatformReference(platforms[i])) - finally: - core.BNFreePlatformList(platforms, count.value) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, value): - _init_plugins() - platform = core.BNGetPlatformByName(str(value)) - if platform is None: - raise KeyError, "'%s' is not a valid platform" % str(value) - return Platform(None, platform) - - def get_list(cls, os = None, arch = None): - _init_plugins() - count = ctypes.c_ulonglong() - if os is None: - platforms = core.BNGetPlatformList(count) - elif arch is None: - platforms = core.BNGetPlatformListByOS(os) - else: - platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) - result = [] - for i in xrange(0, count.value): - result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - -class Platform(object): - """ - ``class Platform`` contains all information releated to the execution environment of the binary, mainly the - calling conventions used. - """ - __metaclass__ = _PlatformMetaClass - name = None - - def __init__(self, arch, handle = None): - if handle is None: - self.arch = arch - self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) - else: - self.handle = handle - self.__dict__["name"] = core.BNGetPlatformName(self.handle) - self.arch = Architecture(core.BNGetPlatformArchitecture(self.handle)) - - def __del__(self): - core.BNFreePlatform(self.handle) - - @property - def default_calling_convention(self): - """ - Default calling convention. - - :getter: returns a CallingConvention object for the default calling convention. - :setter: sets the default calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformDefaultCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @default_calling_convention.setter - def default_calling_convention(self, value): - core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) - - @property - def cdecl_calling_convention(self): - """ - Cdecl calling convention. - - :getter: returns a CallingConvention object for the cdecl calling convention. - :setter sets the cdecl calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformCdeclCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @cdecl_calling_convention.setter - def cdecl_calling_convention(self, value): - core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) - - @property - def stdcall_calling_convention(self): - """ - Stdcall calling convention. - - :getter: returns a CallingConvention object for the stdcall calling convention. - :setter sets the stdcall calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformStdcallCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @stdcall_calling_convention.setter - def stdcall_calling_convention(self, value): - core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) - - @property - def fastcall_calling_convention(self): - """ - Fastcall calling convention. - - :getter: returns a CallingConvention object for the fastcall calling convention. - :setter sets the fastcall calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformFastcallCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @fastcall_calling_convention.setter - def fastcall_calling_convention(self, value): - core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) - - @property - def system_call_convention(self): - """ - System call convention. - - :getter: returns a CallingConvention object for the system call convention. - :setter sets the system call convention - :type: CallingConvention - """ - result = core.BNGetPlatformSystemCallConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @system_call_convention.setter - def system_call_convention(self, value): - core.BNSetPlatformSystemCallConvention(self.handle, value.handle) - - @property - def calling_conventions(self): - """ - List of platform CallingConvention objects (read-only) - - :getter: returns the list of supported CallingConvention objects - :type: list(CallingConvention) - """ - count = ctypes.c_ulonglong() - cc = core.BNGetPlatformCallingConventions(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) - core.BNFreeCallingConventionList(cc, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<platform: %s>" % self.name - - def __str__(self): - return self.name - - def register(self, os): - """ - ``register`` registers the platform for given OS name. - - :param str os: OS name to register - :rtype: None - """ - core.BNRegisterPlatform(os, self.handle) - - def register_calling_convention(self, cc): - """ - ``register_calling_convention`` register a new calling convention. - - :param CallingConvention cc: a CallingConvention object to register - :rtype: None - """ - core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) - - def get_related_platform(self, arch): - result = core.BNGetRelatedPlatform(self.handle, arch.handle) - if not result: - return None - return Platform(None, handle = result) - - def add_related_platform(self, arch, platform): - core.BNAddRelatedPlatform(self.handle, arch.handle, platform.handle) - - def get_associated_platform_by_address(self, addr): - new_addr = ctypes.c_ulonglong() - new_addr.value = addr - result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) - return Platform(None, handle = result), new_addr.value - -class ScriptingOutputListener(object): - def _register(self, handle): - self._cb = core.BNScriptingOutputListener() - self._cb.context = 0 - self._cb.output = self._cb.output.__class__(self._output) - self._cb.error = self._cb.error.__class__(self._error) - self._cb.inputReadyStateChanged = self._cb.inputReadyStateChanged.__class__(self._input_ready_state_changed) - core.BNRegisterScriptingInstanceOutputListener(handle, self._cb) - - def _unregister(self, handle): - core.BNUnregisterScriptingInstanceOutputListener(handle, self._cb) - - def _output(self, ctxt, text): - try: - self.notify_output(text) - except: - log_error(traceback.format_exc()) - - def _error(self, ctxt, text): - try: - self.notify_error(text) - except: - log_error(traceback.format_exc()) - - def _input_ready_state_changed(self, ctxt, state): - try: - self.notify_input_ready_state_changed(state) - except: - log_error(traceback.format_exc()) - - def notify_output(self, text): - pass - - def notify_error(self, text): - pass - - def notify_input_ready_state_changed(self, state): - pass - -class ScriptingInstance(object): - def __init__(self, provider, handle = None): - if handle is None: - self._cb = core.BNScriptingInstanceCallbacks() - self._cb.context = 0 - self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) - self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) - self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view) - self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function) - self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block) - self._cb.setCurrentAddress = self._cb.setCurrentAddress.__class__(self._set_current_address) - self._cb.setCurrentSelection = self._cb.setCurrentSelection.__class__(self._set_current_selection) - self.handle = core.BNInitScriptingInstance(provider.handle, self._cb) - else: - self.handle = core.handle_of_type(handle, core.BNScriptingInstance) - self.listeners = [] - - def __del__(self): - core.BNFreeScriptingInstance(self.handle) - - def _destroy_instance(self, ctxt): - try: - self.perform_destroy_instance() - except: - log_error(traceback.format_exc()) - - def _execute_script_input(self, ctxt, text): - try: - return self.perform_execute_script_input(text) - except: - log_error(traceback.format_exc()) - return core.InvalidScriptInput - - def _set_current_binary_view(self, ctxt, view): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - self.perform_set_current_binary_view(view) - except: - log_error(traceback.format_exc()) - - def _set_current_function(self, ctxt, func): - try: - if func: - func = Function(BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) - else: - func = None - self.perform_set_current_function(func) - except: - log_error(traceback.format_exc()) - - def _set_current_basic_block(self, ctxt, block): - try: - if block: - func = core.BNGetBasicBlockFunction(block) - if func is None: - block = None - else: - block = BasicBlock(BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) - core.BNFreeFunction(func) - else: - block = None - self.perform_set_current_basic_block(block) - except: - log_error(traceback.format_exc()) - - def _set_current_address(self, ctxt, addr): - try: - self.perform_set_current_address(addr) - except: - log_error(traceback.format_exc()) - - def _set_current_selection(self, ctxt, begin, end): - try: - self.perform_set_current_selection(begin, end) - except: - log_error(traceback.format_exc()) - - @abc.abstractmethod - def perform_destroy_instance(self): - raise NotImplementedError - - @abc.abstractmethod - def perform_execute_script_input(self, text): - return core.InvalidScriptInput - - @abc.abstractmethod - def perform_set_current_binary_view(self, view): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_function(self, func): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_basic_block(self, block): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_address(self, addr): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_selection(self, begin, end): - raise NotImplementedError - - @property - def input_ready_state(self): - return core.BNGetScriptingInstanceInputReadyState(self.handle) - - @input_ready_state.setter - def input_ready_state(self, value): - core.BNNotifyInputReadyStateForScriptingInstance(self.handle, value) - - def output(self, text): - core.BNNotifyOutputForScriptingInstance(self.handle, text) - - def error(self, text): - core.BNNotifyErrorForScriptingInstance(self.handle, text) - - def execute_script_input(self, text): - return core.BNExecuteScriptInput(self.handle, text) - - def set_current_binary_view(self, view): - if view is not None: - view = view.handle - core.BNSetScriptingInstanceCurrentBinaryView(self.handle, view) - - def set_current_function(self, func): - if func is not None: - func = func.handle - core.BNSetScriptingInstanceCurrentFunction(self.handle, func) - - def set_current_basic_block(self, block): - if block is not None: - block = block.handle - core.BNSetScriptingInstanceCurrentBasicBlock(self.handle, block) - - def set_current_address(self, addr): - core.BNSetScriptingInstanceCurrentAddress(self.handle, addr) - - def set_current_selection(self, begin, end): - core.BNSetScriptingInstanceCurrentSelection(self.handle, begin, end) - - def register_output_listener(self, listener): - listener._register(self.handle) - self.listeners.append(listener) - - def unregister_output_listener(self, listener): - if listener in self.listeners: - listener._unregister(self.handle) - self.listeners.remove(listener) - -class _ScriptingProviderMetaclass(type): - @property - def list(self): - """List all ScriptingProvider types (read-only)""" - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetScriptingProviderList(count) - result = [] - for i in xrange(0, count.value): - result.append(ScriptingProvider(types[i])) - core.BNFreeScriptingProviderList(types) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetScriptingProviderList(count) - try: - for i in xrange(0, count.value): - yield ScriptingProvider(types[i]) - finally: - core.BNFreeScriptingProviderList(types) - - def __getitem__(self, value): - _init_plugins() - provider = core.BNGetScriptingProviderByName(str(value)) - if provider is None: - raise KeyError, "'%s' is not a valid scripting provider" % str(value) - return ScriptingProvider(provider) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class ScriptingProvider(object): - __metaclass__ = _ScriptingProviderMetaclass - - name = None - instance_class = None - _registered_providers = [] - - def __init__(self, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNScriptingProvider) - self.__dict__["name"] = core.BNGetScriptingProviderName(handle) - - def register(self): - self._cb = core.BNScriptingProviderCallbacks() - self._cb.context = 0 - self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) - self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self._cb) - self.__class__._registered_providers.append(self) - - def _create_instance(self, ctxt): - try: - result = self.__class__.instance_class(self) - if result is None: - return None - return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return None - - def create_instance(self): - result = core.BNCreateScriptingProviderInstance(self.handle) - if result is None: - return None - return ScriptingInstance(self, handle = result) - -class _PythonScriptingInstanceOutput(object): - def __init__(self, orig, is_error): - self.orig = orig - self.is_error = is_error - self.buffer = "" - - def write(self, data): - global _output_to_log - - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - if _output_to_log: - self.buffer += data - while True: - i = self.buffer.find('\n') - if i == -1: - break - line = self.buffer[:i] - self.buffer = self.buffer[i + 1:] - - if self.is_error: - log_error(line) - else: - log_info(line) - else: - self.orig.write(data) - else: - PythonScriptingInstance._interpreter.value = None - try: - if self.is_error: - interpreter.instance.error(data) - else: - interpreter.instance.output(data) - finally: - PythonScriptingInstance._interpreter.value = interpreter - -class _PythonScriptingInstanceInput(object): - def __init__(self, orig): - self.orig = orig - - def read(self, size): - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - return self.orig.read(size) - else: - PythonScriptingInstance._interpreter.value = None - try: - result = interpreter.read(size) - finally: - PythonScriptingInstance._interpreter.value = interpreter - return result - - def readline(self): - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - return self.orig.readline() - else: - result = "" - while True: - data = interpreter.read(1) - result += data - if (len(data) == 0) or (data == "\n"): - break - return result - -class PythonScriptingInstance(ScriptingInstance): - _interpreter = threading.local() - - class InterpreterThread(threading.Thread): - def __init__(self, instance): - super(PythonScriptingInstance.InterpreterThread, self).__init__() - self.instance = instance - self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} - self.interpreter = code.InteractiveInterpreter(self.locals) - self.event = threading.Event() - self.daemon = True - - # Latest selections from UI - self.current_view = None - self.current_func = None - self.current_block = None - self.current_addr = 0 - self.current_selection_begin = 0 - self.current_selection_end = 0 - - # Selections that were current as of last issued command - self.active_view = None - self.active_func = None - self.active_block = None - self.active_addr = 0 - self.active_selection_begin = 0 - self.active_selection_end = 0 - - self.locals["get_selected_data"] = self.get_selected_data - self.locals["write_at_cursor"] = self.write_at_cursor - - self.exit = False - self.code = None - self.input = "" - - self.interpreter.runsource("from binaryninja import *\n") - - def execute(self, code): - self.code = code - self.event.set() - - def add_input(self, data): - self.input += data - self.event.set() - - def end(self): - self.exit = True - self.event.set() - - def read(self, size): - while not self.exit: - if len(self.input) > size: - result = self.input[:size] - self.input = self.input[size:] - return result - elif len(self.input) > 0: - result = self.input - self.input = "" - return result - self.instance.input_ready_state = core.ReadyForScriptProgramInput - self.event.wait() - self.event.clear() - return "" - - def run(self): - while not self.exit: - self.event.wait() - self.event.clear() - if self.exit: - break - if self.code is not None: - self.instance.input_ready_state = core.NotReadyForInput - code = self.code - self.code = None - - PythonScriptingInstance._interpreter.value = self - try: - self.active_view = self.current_view - self.active_func = self.current_func - self.active_block = self.current_block - self.active_addr = self.current_addr - self.active_selection_begin = self.current_selection_begin - self.active_selection_end = self.current_selection_end - - self.locals["current_view"] = self.active_view - self.locals["bv"] = self.active_view - self.locals["current_function"] = self.active_func - self.locals["current_basic_block"] = self.active_block - self.locals["current_address"] = self.active_addr - self.locals["here"] = self.active_addr - 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 - - def get_selected_data(self): - if self.active_view is None: - return None - length = self.active_selection_end - self.active_selection_begin - return self.active_view.read(self.active_selection_begin, length) - - def write_at_cursor(self, data): - if self.active_view is None: - return 0 - selected_length = self.active_selection_end - self.active_selection_begin - data = str(data) - if (len(data) == selected_length) or (selected_length == 0): - return self.active_view.write(self.active_selection_begin, data) - else: - self.active_view.remove(self.active_selection_begin, selected_length) - return self.active_view.insert(self.active_selection_begin, data) - - def __init__(self, provider): - super(PythonScriptingInstance, self).__init__(provider) - self.interpreter = PythonScriptingInstance.InterpreterThread(self) - self.interpreter.start() - 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 - - if self.input_ready_state == core.ReadyForScriptProgramInput: - if len(text) == 0: - return core.SuccessfulScriptExecution - self.input_ready_state = core.NotReadyForInput - self.interpreter.add_input(text) - return core.SuccessfulScriptExecution - - try: - result = code.compile_command(text) - except: - result = False - - if result is None: - # Command is not complete, ask for more input - return core.IncompleteScriptInput - - self.input_ready_state = core.NotReadyForInput - 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 - -class PythonScriptingProvider(ScriptingProvider): - name = "Python" - instance_class = PythonScriptingInstance - -class MainThreadAction(object): - def __init__(self, handle): - self.handle = handle - - def __del__(self): - core.BNFreeMainThreadAction(self.handle) - - def execute(self): - core.BNExecuteMainThreadAction(self.handle) - - @property - def done(self): - return core.BNIsMainThreadActionDone(self.handle) - - def wait(self): - core.BNWaitForMainThreadAction(self.handle) - -class MainThreadActionHandler(object): - _main_thread = None - - def __init__(self): - self._cb = core.BNMainThreadCallbacks() - self._cb.context = 0 - self._cb.addAction = self._cb.addAction.__class__(self._add_action) - - def register(self): - self.__class__._main_thread = self - core.BNRegisterMainThread(self._cb) - - def _add_action(self, ctxt, action): - try: - self.add_action(MainThreadAction(action)) - except: - log_error(traceback.format_exc()) - - 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() - -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 - - 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) - 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): - self.__class__._interaction_handler = self - core.BNRegisterInteractionHandler(self._cb) - - def _show_plain_text_report(self, ctxt, view, title, contents): - try: - if view: - view = BinaryView(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(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(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(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 _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(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) - 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 get_form_input(self, fields, title): - return False - - def show_message_box(self, title, text, buttons, icon): - return core.CancelButton - -class _DestructionCallbackHandler: +class _DestructionCallbackHandler(object): def __init__(self): self._cb = core.BNObjectDestructionCallbacks() self._cb.context = 0 @@ -10837,436 +85,23 @@ class _DestructionCallbackHandler: def destruct_function(self, ctxt, func): Function._unregister(func) -_destruct_callbacks = _DestructionCallbackHandler() - -def LLIL_TEMP(n): - return n | 0x80000000 -def LLIL_REG_IS_TEMP(n): - return (n & 0x80000000) != 0 - -def LLIL_GET_TEMP_REG_INDEX(n): - return n & 0x7fffffff - -def shutdown(): - core.BNShutdown() - -def log(level, text): - """ - ``log`` writes messages to the log console for the given log level. - - ============ ======== ======================================================================= - LogLevelName LogLevel Description - ============ ======== ======================================================================= - DebugLog 0 Logs debuging information messages to the console. - InfoLog 1 Logs general information messages to the console. - WarningLog 2 Logs message to console with **Warning** icon. - ErrorLog 3 Logs message to console with **Error** icon, focusing the error console. - AlertLog 4 Logs message to pop up window. - ============ ======== ======================================================================= - - :param LogLevel level: Log level to use - :param str text: message to print - :rtype: None - """ - core.BNLog(level, "%s", str(text)) - -def log_debug(text): - """ - ``log_debug`` Logs debuging information messages to the console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_debug("Hotdogs!") - Hotdogs! - """ - core.BNLogDebug("%s", str(text)) - -def log_info(text): - """ - ``log_info`` Logs general information messages to the console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_info("Saucisson!") - Saucisson! - >>> - """ - core.BNLogInfo("%s", str(text)) - -def log_warn(text): - """ - ``log_warn`` Logs message to console, if run through the GUI it logs with **Warning** icon. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_info("Chilidogs!") - Chilidogs! - >>> - """ - core.BNLogWarn("%s", str(text)) - -def log_error(text): - """ - ``log_error`` Logs message to console, if run through the GUI it logs with **Error** icon, focusing the error console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_error("Spanferkel!") - Spanferkel! - >>> - """ - core.BNLogError("%s", str(text)) - -def log_alert(text): - """ - ``log_alert`` Logs message console and to a pop up window if run through the GUI. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_alert("Kielbasa!") - Kielbasa! - >>> - """ - core.BNLogAlert("%s", str(text)) - -def log_to_stdout(min_level): - """ - ``log_to_stdout`` redirects minimum log level to standard out. - - :param int min_level: minimum level to log to - :rtype: None - :Example: - - >>> log_debug("Hotdogs!") - >>> log_to_stdout(core.DebugLog) - >>> log_debug("Hotdogs!") - Hotdogs! - >>> - """ - core.BNLogToStdout(min_level) - -def log_to_stderr(min_level): - """ - ``log_to_stderr`` redirects minimum log level to standard error. - - :param int min_level: minimum level to log to - :rtype: None - """ - core.BNLogToStderr(min_level) - -def log_to_file(min_level, path, append = False): - """ - ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwritting. - - :param int min_level: minimum level to log to - :param str path: path to log to - :param bool append: optional flag for specifying appending. True = append, False = overwrite. - :rtype: None - """ - core.BNLogToFile(min_level, str(path), append) - -def close_logs(): - """ - ``close_logs`` close all log files. - - :rtype: None - """ - core.BNCloseLogs() - -def escape_string(text): - return DataBuffer(text).escape() - -def unescape_string(text): - return DataBuffer(text).unescape() - -def preprocess_source(source, filename = None, include_dirs = []): - """ - ``preprocess_source`` run the C preprocessor on the given source or source filename. - - :param str source: source to preprocess - :param str filename: optional filename to preprocess - :param list(str) include_dirs: list of string directorires to use as include directories. - :return: returns a tuple of (preprocessed_source, error_string) - :rtype: tuple(str,str) - :Example: - - >>> source = "#define TEN 10\\nint x[TEN];\\n" - >>> preprocess_source(source) - ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') - >>> - """ - if filename is None: - filename = "input" - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - output = ctypes.c_char_p() - errors = ctypes.c_char_p() - result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) - output_str = output.value - error_str = errors.value - core.BNFreeString(ctypes.cast(output, ctypes.POINTER(ctypes.c_byte))) - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if result: - return (output_str, error_str) - return (None, error_str) - -def are_auto_updates_enabled(): - """ - ``are_auto_updates_enabled`` queries if auto updates are enabled. - - :return: boolean True if auto updates are enabled. False if they are disabled. - :rtype: bool - """ - return core.BNAreAutoUpdatesEnabled() - -def set_auto_updates_enabled(enabled): - """ - ``set_auto_updates_enabled`` sets auto update enabled status. - - :param bool enabled: True to enable update, Flase to disable updates. - :rtype: None - """ - core.BNSetAutoUpdatesEnabled(enabled) - -def get_time_since_last_update_check(): - """ - ``get_time_since_last_update_check`` returns the time stamp for the last time updates were checked. - - :return: time stacmp for last update check - :rtype: int - """ - return core.BNGetTimeSinceLastUpdateCheck() - -def updates_checked(): - core.BNUpdatesChecked() - -def get_qualified_name(names): - """ - ``get_qualified_name`` gets a qualified name for the provied name list. - - :param list(str) names: name list to qualify - :return: a qualified name - :rtype: str - :Example: - - >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - >>> get_qualified_name(name) - 'Foobar::testf' - >>> - """ - return "::".join(names) - -def demangle_ms(arch, mangled_name): - """ - ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. - - :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. - :param str mangled_name: a mangled Microsoft Visual Studio C++ name - :return: returns a Type object for the mangled name - :rtype: Type - :Example: - - >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (<type: public: static enum Foobar::foo __cdecl (enum Foobar::foo)>, ['Foobar', 'testf']) - >>> - """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - 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, mangledName) - - -def demangle_gnu3(arch, mangled_name): - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if core.BNDemangleGNU3(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) - if not handle: - return (None, names) - 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(object): - _actions = [] - - def __init__(self, func): - self.func = func - self.interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - self.interpreter = PythonScriptingInstance._interpreter.value - self.__class__._actions.append(self) - self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute()) - - def execute(self): - old_interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - old_interpreter = PythonScriptingInstance._interpreter.value - PythonScriptingInstance._interpreter.value = self.interpreter - try: - self.func() - finally: - PythonScriptingInstance._interpreter.value = old_interpreter - self.__class__._actions.remove(self) - -def execute_on_main_thread(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 = _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.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) - -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 - -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) +_destruct_callbacks = _DestructionCallbackHandler() bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() core_version = core.BNGetVersionString() +'''Core version''' + core_build_id = core.BNGetBuildId() +'''Build ID''' -# Ensure all enumeration constants from the core are exposed by this module -for name in core.all_enum_values: - globals()[name] = core.all_enum_values[name] +core_product = core.BNGetProduct() +'''Product string from the license file''' -PythonScriptingProvider().register() +core_product_type = core.BNGetProductType() +'''Product type from the license file''' -# Wrap stdin/stdout/stderr for Python scripting provider implementation -sys.stdin = _PythonScriptingInstanceInput(sys.stdin) -sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) -sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) +core_license_count = core.BNGetLicenseCount() +'''License count from the license file''' diff --git a/python/architecture.py b/python/architecture.py new file mode 100644 index 00000000..079d3f5f --- /dev/null +++ b/python/architecture.py @@ -0,0 +1,1729 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes +import abc + +# Binary Ninja components +import _binaryninjacore as core +from enums import (Endianness, ImplicitRegisterExtend, BranchType, + InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) +import startup +import function +import lowlevelil +import callingconvention +import platform +import log +import databuffer +import types + + +class _ArchitectureMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + result = [] + for i in xrange(0, count.value): + result.append(Architecture(archs[i])) + core.BNFreeArchitectureList(archs) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + try: + for i in xrange(0, count.value): + yield Architecture(archs[i]) + finally: + core.BNFreeArchitectureList(archs) + + def __getitem__(cls, name): + startup._init_plugins() + arch = core.BNGetArchitectureByName(name) + if arch is None: + raise KeyError("'%s' is not a valid architecture" % str(name)) + return Architecture(arch) + + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("architecture 'name' is not defined") + arch = cls() + cls._registered_cb = arch._cb + arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Architecture(object): + """ + ``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 + iteration:: + + >>> #List the architectures + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>] + >>> #Register a new Architecture + >>> class MyArch(Architecture): + ... name = "MyArch" + ... + >>> MyArch.register() + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>, <arch: MyArch>] + >>> + + For the purposes of this documentation the variable ``arch`` will be used in the following context :: + + >>> from binaryninja import * + >>> arch = Architecture['x86'] + """ + name = None + endianness = Endianness.LittleEndian + address_size = 8 + default_int_size = 4 + max_instr_length = 16 + opcode_display_length = 8 + regs = {} + stack_pointer = None + link_reg = None + flags = [] + flag_write_types = [] + flag_roles = {} + flags_required_for_flag_condition = {} + flags_written_by_flag_write_type = {} + __metaclass__ = _ArchitectureMetaClass + next_address = 0 + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNArchitecture) + self.__dict__["name"] = core.BNGetArchitectureName(self.handle) + self.__dict__["endianness"] = Endianness(core.BNGetArchitectureEndianness(self.handle)).name + self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) + self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) + self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) + self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureStackPointerRegister(self.handle)) + self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureLinkRegister(self.handle)) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisters(self.handle, count) + self.__dict__["regs"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) + info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) + full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + self.regs[name] = function.RegisterInfo(full_width_reg, info.size, info.offset, + ImplicitRegisterExtend(info.extend).name, regs[i]) + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + flags = core.BNGetAllArchitectureFlags(self.handle, count) + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagName(self.handle, flags[i]) + self._flags[name] = flags[i] + self._flags_by_index[flags[i]] = name + self.flags.append(name) + core.BNFreeRegisterList(flags) + + count = ctypes.c_ulonglong() + types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) + self._flag_write_types[name] = types[i] + self._flag_write_types_by_index[types[i]] = name + self.flag_write_types.append(name) + core.BNFreeRegisterList(types) + + self._flag_roles = {} + self.__dict__["flag_roles"] = {} + for flag in self.__dict__["flags"]: + role = FlagRole(core.BNGetArchitectureFlagRole(self.handle, self._flags[flag])) + self.__dict__["flag_roles"][flag] = role + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = {} + for cond in LowLevelILFlagCondition: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_required_for_flag_condition[cond] = flag_indexes + self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = {} + for write_type in self.flag_write_types: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, + self._flag_write_types[write_type], count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes + self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names + else: + startup._init_plugins() + + if self.__class__.opcode_display_length > self.__class__.max_instr_length: + self.__class__.opcode_display_length = self.__class__.max_instr_length + + self._cb = core.BNCustomArchitecture() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) + self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) + self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) + self._cb.getAssociatedArchitectureByAddress = \ + self._cb.getAssociatedArchitectureByAddress.__class__(self._get_associated_arch_by_address) + self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) + self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) + self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) + self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( + self._get_instruction_low_level_il) + self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) + self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) + self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) + self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) + self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) + self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) + self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) + self._cb.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) + self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( + self._get_flags_required_for_flag_condition) + self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( + self._get_flags_written_by_flag_write_type) + self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( + self._get_flag_write_low_level_il) + self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( + self._get_flag_condition_low_level_il) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) + self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( + self._get_stack_pointer_register) + self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) + self._cb.assemble = self._cb.assemble.__class__(self._assemble) + self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( + self._is_never_branch_patch_available) + self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( + self._is_always_branch_patch_available) + self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( + self._is_invert_branch_patch_available) + self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( + self._is_skip_and_return_zero_patch_available) + self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( + self._is_skip_and_return_value_patch_available) + self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) + self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) + self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) + self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) + + self._all_regs = {} + self._full_width_regs = {} + self._regs_by_index = {} + self.__dict__["regs"] = self.__class__.regs + reg_index = 0 + for reg in self.regs: + info = self.regs[reg] + if reg not in self._all_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._all_regs: + self._all_regs[info.full_width_reg] = reg_index + self._regs_by_index[reg_index] = info.full_width_reg + self.regs[info.full_width_reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._full_width_regs: + self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] + + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = self.__class__.flags + flag_index = 0 + for flag in self.__class__.flags: + if flag not in self._flags: + self._flags[flag] = flag_index + self._flags_by_index[flag_index] = flag + flag_index += 1 + + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = self.__class__.flag_write_types + write_type_index = 0 + for write_type in self.__class__.flag_write_types: + if write_type not in self._flag_write_types: + self._flag_write_types[write_type] = write_type_index + self._flag_write_types_by_index[write_type_index] = write_type + write_type_index += 1 + + self._flag_roles = {} + self.__dict__["flag_roles"] = self.__class__.flag_roles + for flag in self.__class__.flag_roles: + role = self.__class__.flag_roles[flag] + if isinstance(role, str): + role = FlagRole[role] + self._flag_roles[self._flags[flag]] = role + + 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: + flags = [] + for flag in self.__class__.flags_required_for_flag_condition[cond]: + flags.append(self._flags[flag]) + self._flags_required_for_flag_condition[cond] = flags + + 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: + flags = [] + for flag in self.__class__.flags_written_by_flag_write_type[write_type]: + flags.append(self._flags[flag]) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags + + self._pending_reg_lists = {} + self._pending_token_lists = {} + + def __eq__(self, value): + if not isinstance(value, Architecture): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Architecture): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def full_width_regs(self): + """List of full width register strings (read-only)""" + count = ctypes.c_ulonglong() + regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + @property + def calling_conventions(self): + """Dict of CallingConvention objects (read-only)""" + count = ctypes.c_ulonglong() + cc = core.BNGetArchitectureCallingConventions(self.handle, count) + result = {} + for i in xrange(0, count.value): + obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + result[obj.name] = obj + core.BNFreeCallingConventionList(cc, count) + return result + + @property + def standalone_platform(self): + """Architecture standalone platform (read-only)""" + pl = core.BNGetArchitectureStandalonePlatform(self.handle) + return platform.Platform(self, pl) + + def __setattr__(self, name, value): + if ((name == "name") or (name == "endianness") or (name == "address_size") or + (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): + raise AttributeError("attribute '%s' is read only" % name) + else: + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<arch: %s>" % self.name + + def _init(self, ctxt, handle): + self.handle = handle + + def _get_endianness(self, ctxt): + try: + return self.__class__.endianness + except: + log.log_error(traceback.format_exc()) + return Endianness.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.__class__.address_size + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_default_integer_size(self, ctxt): + try: + return self.__class__.default_int_size + except: + log.log_error(traceback.format_exc()) + return 4 + + def _get_max_instruction_length(self, ctxt): + try: + return self.__class__.max_instr_length + except: + log.log_error(traceback.format_exc()) + return 16 + + def _get_opcode_display_length(self, ctxt): + try: + return self.__class__.opcode_display_length + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_associated_arch_by_address(self, ctxt, addr): + try: + result, new_addr = self.perform_get_associated_arch_by_address(addr[0]) + addr[0] = new_addr + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return ctypes.cast(self.handle, ctypes.c_void_p).value + + def _get_instruction_info(self, ctxt, data, addr, max_len, result): + try: + buf = ctypes.create_string_buffer(max_len) + ctypes.memmove(buf, data, max_len) + info = self.perform_get_instruction_info(buf.raw, addr) + if info is None: + return False + result[0].length = info.length + result[0].branchDelay = info.branch_delay + result[0].branchCount = len(info.branches) + for i in xrange(0, len(info.branches)): + if isinstance(info.branches[i].type, str): + result[0].branchType[i] = BranchType[info.branches[i].type] + else: + result[0].branchType[i] = info.branches[i].type + result[0].branchTarget[i] = info.branches[i].target + if info.branches[i].arch is None: + result[0].branchArch[i] = None + else: + result[0].branchArch[i] = info.branches[i].arch.handle + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_instruction_text(self, ctxt, data, addr, length, result, count): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + info = self.perform_get_instruction_text(buf.raw, addr) + if info is None: + return False + tokens = info[0] + length[0] = info[1] + count[0] = len(tokens) + token_buf = (core.BNInstructionTextToken * len(tokens))() + for i in xrange(0, len(tokens)): + if isinstance(tokens[i].type, str): + token_buf[i].type = InstructionTextTokenType[tokens[i].type] + else: + token_buf[i].type = tokens[i].type + token_buf[i].text = tokens[i].text + token_buf[i].value = tokens[i].value + token_buf[i].size = tokens[i].size + token_buf[i].operand = tokens[i].operand + token_buf[i].context = tokens[i].context + token_buf[i].address = tokens[i].address + result[0] = token_buf + ptr = ctypes.cast(token_buf, ctypes.c_void_p) + self._pending_token_lists[ptr.value] = (ptr.value, token_buf) + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _free_instruction_text(self, tokens, count): + try: + buf = ctypes.cast(tokens, ctypes.c_void_p) + 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 KeyError: + log.log_error(traceback.format_exc()) + + def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + result = self.perform_get_instruction_low_level_il(buf.raw, addr, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) + if result is None: + return False + length[0] = result + return True + except OSError: + log.log_error(traceback.format_exc()) + return False + + def _get_register_name(self, ctxt, reg): + try: + if reg in self._regs_by_index: + return core.BNAllocString(self._regs_by_index[reg]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_name(self, ctxt, flag): + try: + if flag in self._flags_by_index: + return core.BNAllocString(self._flags_by_index[flag]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_write_type_name(self, ctxt, write_type): + try: + 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 (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_full_width_registers(self, ctxt, count): + try: + regs = self._full_width_regs.values() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_registers(self, ctxt, count): + try: + regs = self._regs_by_index.keys() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flags(self, ctxt, count): + try: + flags = self._flags_by_index.keys() + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flag_write_types(self, ctxt, count): + try: + types = self._flag_write_types_by_index.keys() + count[0] = len(types) + type_buf = (ctypes.c_uint * len(types))() + for i in xrange(0, len(types)): + type_buf[i] = types[i] + result = ctypes.cast(type_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, type_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_role(self, ctxt, flag): + try: + if flag in self._flag_roles: + return self._flag_roles[flag] + return FlagRole.SpecialFlagRole + except KeyError: + log.log_error(traceback.format_exc()) + return None + + def _get_flags_required_for_flag_condition(self, ctxt, cond, count): + try: + if cond in self._flags_required_for_flag_condition: + flags = self._flags_required_for_flag_condition[cond] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flags_written_by_flag_write_type(self, ctxt, write_type, count): + try: + if write_type in self._flags_written_by_flag_write_type: + flags = self._flags_written_by_flag_write_type[write_type] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): + try: + write_type_name = None + if write_type != 0: + write_type_name = self._flag_write_types_by_index[write_type] + flag_name = self._flags_by_index[flag] + operand_list = [] + for i in xrange(operand_count): + if operands[i].constant: + operand_list.append(("const", operands[i].value)) + elif lowlevelil.LLIL_REG_IS_TEMP(operands[i].reg): + operand_list.append(("reg", operands[i].reg)) + else: + 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, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_flag_condition_low_level_il(self, ctxt, cond, il): + try: + return self.perform_get_flag_condition_low_level_il(cond, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except OSError: + log.log_error(traceback.format_exc()) + return 0 + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + 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 (ValueError, KeyError): + log.log_error(traceback.format_exc()) + + def _get_register_info(self, ctxt, reg, result): + try: + if reg not in self._regs_by_index: + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = ImplicitRegisterExtend.NoExtend + return + info = self.__class__.regs[self._regs_by_index[reg]] + result[0].fullWidthRegister = self._all_regs[info.full_width_reg] + result[0].offset = info.offset + result[0].size = info.size + if isinstance(info.extend, str): + result[0].extend = ImplicitRegisterExtend[info.extend] + else: + result[0].extend = info.extend + except KeyError: + log.log_error(traceback.format_exc()) + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = ImplicitRegisterExtend.NoExtend + + def _get_stack_pointer_register(self, ctxt): + try: + return self._all_regs[self.__class__.stack_pointer] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _get_link_register(self, ctxt): + try: + if self.__class__.link_reg is None: + return 0xffffffff + return self._all_regs[self.__class__.link_reg] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _assemble(self, ctxt, code, addr, result, errors): + try: + data, error_str = self.perform_assemble(code, addr) + errors[0] = core.BNAllocString(str(error_str)) + if data is None: + return False + data = str(data) + buf = ctypes.create_string_buffer(len(data)) + ctypes.memmove(buf, data, len(data)) + core.BNSetDataBufferContents(result, buf, len(data)) + return True + except: + log.log_error(traceback.format_exc()) + errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") + return False + + def _is_never_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_never_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_always_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_always_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_invert_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_invert_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _convert_to_nop(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_convert_to_nop(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _always_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_always_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _invert_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_invert_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _skip_and_return_value(self, ctxt, data, addr, length, value): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_skip_and_return_value(buf.raw, addr, value) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def perform_get_associated_arch_by_address(self, addr): + return self, addr + + @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 + :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 always be taken + FalseBranch False branch condition + TrueBranch True branch condition + CallDestination Branch is a call instruction (Branch with Link) + FunctionReturn Branch returns from a function + 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 + :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction + :rtype: InstructionInfo + """ + 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 + list of :py:class:`InstructionTextToken` objects. + + :param str data: bytes to decode + :param int addr: virtual address of the byte to be decoded + :return: a tuple of list(InstructionTextToken) and length of instruction decoded + :rtype: tuple(list(InstructionTextToken), int) + """ + 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 + low-level IL instructions. The il instructions must be appended to the :py:class:`LowLevelILFunction`. + + .. note:: Architecture subclasses should implement this method. + + :param str data: bytes to be interpreted as low-level IL instructions + :param int addr: virtual address of start of ``data`` + :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to + :rtype: 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. + .. warning:: This method should never be called directly. + + :param LowLevelILOperation op: + :param int size: + :param int write_type: + :param int flag: + :param list(int_or_str): + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return il.unimplemented() + + @abc.abstractmethod + def perform_get_flag_condition_low_level_il(self, cond, il): + """ + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + 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 + virtual address ``addr`` to the byte representation of those instructions. This can be done by simply shelling + out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. + + .. note:: Architecture subclasses should implement this method. + .. note :: It is important that the assembler used accepts a syntax identical to the one emitted by the \ + disassembler. This will prevent confusing the user. + .. warning:: This method should never be called directly. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + """ + 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 + the bytes contained in ``data`` at address addr is a branch instruction that can be made to never branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + 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 + the bytes contained in ``data`` at address addr is a conditional branch that can be made unconditional. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + 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 + the bytes contained in ``data`` at address addr is a conditional branch which can be inverted. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + 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 + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call eax`` which could be + converted into ``xor eax,eax`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + 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 + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call 0xdeadbeef`` which could be + converted into ``mov eax, 42`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + 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. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes at virtual address ``addr`` + :param int addr: the virtual address of the instruction to be patched + :return: nop sequence of same length as ``data`` or None + :rtype: str or None + """ + 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 + at ``addr`` to an unconditional branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + 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 + at ``addr``. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + 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 + the bytes in ``data`` at ``addr`` to one or more instructions that are equivilent to a function returning a + value. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :param int value: value to be returned + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + def get_associated_arch_by_address(self, addr): + new_addr = ctypes.c_ulonglong() + new_addr.value = addr + result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) + return Architecture(handle = result), new_addr.value + + def get_instruction_info(self, data, addr): + """ + ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address + ``addr`` with data ``data``. + + .. note :: The instruction info object should always set the InstructionInfo.length to the instruction length, \ + and the branches of the proper types shoulde be added if the instruction is a branch. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :return: the InstructionInfo for the current instruction + :rtype: InstructionInfo + """ + info = core.BNInstructionInfo() + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): + return None + result = function.InstructionInfo() + result.length = info.length + result.branch_delay = info.branchDelay + for i in xrange(0, info.branchCount): + branch_type = BranchType(info.branchType[i]).name + target = info.branchTarget[i] + if info.branchArch[i]: + arch = Architecture(info.branchArch[i]) + else: + arch = None + result.add_branch(branch_type, target, arch) + return result + + def get_instruction_text(self, data, addr): + """ + ``get_instruction_text`` returns a list of InstructionTextToken objects for the instruction at the given virtual + address ``addr`` with data ``data``. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :return: an InstructionTextToken list for the current instruction + :rtype: list(InstructionTextToken) + """ + data = str(data) + count = ctypes.c_ulonglong() + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): + return None, 0 + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeInstructionText(tokens, count.value) + return result, length.value + + def get_instruction_low_level_il_instruction(self, bv, addr): + il = lowlevelil.LowLevelILFunction(self) + data = bv.read(addr, self.max_instr_length) + self.get_instruction_low_level_il(data, addr, il) + return il[0] + + def get_instruction_low_level_il(self, data, addr, il): + """ + ``get_instruction_low_level_il`` appends LowLevelILExpr objects to ``il`` for the instruction at the given + virtual address ``addr`` with data ``data``. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :param LowLevelILFunction il: The function the current instruction belongs to + :return: the length of the current instruction + :rtype: int + """ + data = str(data) + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) + return length.value + + def get_low_level_il_from_bytes(self, data, addr): + """ + ``get_low_level_il_from_bytes`` converts the instruction in bytes to ``il`` at the given virtual address + + :param str data: the bytes of the instruction + :param int addr: virtual address of bytes in ``data`` + :return: the instruction + :rtype: LowLevelILInstruction + :Example: + + >>> arch.get_low_level_il_from_bytes('\xeb\xfe', 0x40DEAD) + <il: jump(0x40dead)> + >>> + """ + func = lowlevelil.LowLevelILFunction(self) + self.get_instruction_low_level_il(data, addr, func) + return func[0] + + def get_reg_name(self, reg): + """ + ``get_reg_name`` gets a register name from a register number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureRegisterName(self.handle, reg) + + def get_flag_name(self, flag): + """ + ``get_flag_name`` gets a flag name from a flag number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureFlagName(self.handle, flag) + + def get_flag_write_type_name(self, write_type): + """ + ``get_flag_write_type_name`` gets the flag write type name for the given flag. + + :param int write_type: flag + :return: flag write type name + :rtype: str + """ + return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) + + def get_flag_by_name(self, flag): + """ + ``get_flag_by_name`` get flag name for flag index. + + :param int flag: flag index + :return: flag name for flag index + :rtype: str + """ + return self._flags[flag] + + def get_flag_write_type_by_name(self, write_type): + """ + ``get_flag_write_type_by_name`` gets the flag write type name for the flage write type. + + :param int write_type: flag write type + :return: flag write type + :rtype: str + """ + return self._flag_write_types[write_type] + + def get_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr index + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_flag_condition_low_level_il(self, cond, il): + """ + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) + + def get_modified_regs_on_write(self, reg): + """ + ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. + + :param str reg: string register name + :return: list of register names + :rtype: list(str) + """ + reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) + count = ctypes.c_ulonglong() + regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + def assemble(self, code, addr=0): + """ + ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the + byte representation of those instructions. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + :Example: + + >>> arch.assemble("je 10") + ('\\x0f\\x84\\x04\\x00\\x00\\x00', '') + >>> + """ + result = databuffer.DataBuffer() + errors = ctypes.c_char_p() + if not core.BNAssemble(self.handle, code, addr, result.handle, errors): + return None, errors.value + return str(result), errors.value + + def is_never_branch_patch_available(self, data, addr): + """ + ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_never_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_never_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_always_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to + **always branch**. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_always_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_always_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_invert_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_invert_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_invert_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_zero_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns zero*. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call eax")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_value_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns a value*. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) + + def convert_to_nop(self, data, addr): + """ + ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop + instructions of the same length as data. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) worth of no-operation instructions + :rtype: str + :Example: + + >>> arch.convert_to_nop("\\x00\\x00", 0) + '\\x90\\x90' + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def always_branch(self, data, addr): + """ + ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which always branches. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> bytes = arch.always_branch(arch.assemble("je 10")[0], 0) + >>> arch.get_instruction_text(bytes, 0) + (['nop '], 1L) + >>> arch.get_instruction_text(bytes[1:], 0) + (['jmp ', '0x9'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def invert_branch(self, data, addr): + """ + ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which inverts the branch of provided instruction. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("je 10")[0], 0), 0) + (['jne ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jo 10")[0], 0), 0) + (['jno ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jge 10")[0], 0), 0) + (['jl ', '0xa'], 6L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def skip_and_return_value(self, data, addr, value): + """ + ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of + bytes of the same length which doesn't call and instead *return a value*. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.skip_and_return_value(arch.assemble("call 10")[0], 0, 0), 0) + (['mov ', 'eax', ', ', '0x0'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def is_view_type_constant_defined(self, type_name, const_name): + """ + + :param str type_name: the BinaryView type name of the constant to query + :param str const_name: the constant name to query + :rtype: None + :Example: + + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.is_view_type_constant_defined("ELF", "R_COPY") + True + >>> arch.is_view_type_constant_defined("ELF", "NOT_THERE") + False + >>> + """ + return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) + + def get_view_type_constant(self, type_name, const_name, default_value=0): + """ + ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. + + :param str type_name: the BinaryView type name of the constant to be retrieved + :param str const_name: the constant name to retrieved + :param int value: optional default value if the type_name is not present. default value is zero. + :return: The BinaryView type constant or the default_value if not found + :rtype: int + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.get_view_type_constant("ELF", "R_COPY") + 5L + >>> arch.get_view_type_constant("ELF", "NOT_HERE", 100) + 100L + """ + return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) + + def set_view_type_constant(self, type_name, const_name, value): + """ + ``set_view_type_constant`` creates a new binaryview type constant. + + :param str type_name: the BinaryView type name of the constant to be registered + :param str const_name: the constant name to register + :param int value: the value of the constant + :rtype: None + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> + """ + core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) + + def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None): + """ + ``parse_types_from_source`` parses the source string and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str source: source string to be parsed + :param str filename: optional source filename + :param list(str) include_dirs: optional list of string filename include directories + :param str auto_type_source: optional source of types if used for automatically generated types + :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': + <type: int32_t(int32_t x)>}}, '') + >>> + """ + + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, + len(include_dirs), auto_type_source) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError(error_str) + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + + def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None): + """ + ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str filename: filename of file to be parsed + :param list(str) include_dirs: optional list of string filename include directories + :param str auto_type_source: optional source of types if used for automatically generated types + :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> file = "/Users/binja/tmp.c" + >>> open(file).read() + 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' + >>> arch.parse_types_from_source_file(file) + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: + {'bar': <type: int32_t(int32_t x)>}}, '') + >>> + """ + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, + len(include_dirs), auto_type_source) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError(error_str) + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + + def register_calling_convention(self, cc): + """ + ``register_calling_convention`` registers a new calling convention for the Architecture. + + :param CallingConvention cc: CallingConvention object to be registered + :rtype: None + """ + core.BNRegisterCallingConvention(self.handle, cc.handle) + + +class ReferenceSource(object): + def __init__(self, func, arch, addr): + self.function = func + self.arch = arch + self.address = addr + + def __repr__(self): + if self.arch: + return "<ref: %s@%#x>" % (self.arch.name, self.address) + else: + return "<ref: %#x>" % self.address diff --git a/python/associateddatastore.py b/python/associateddatastore.py new file mode 100644 index 00000000..6b5e688e --- /dev/null +++ b/python/associateddatastore.py @@ -0,0 +1,45 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import copy + + +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) diff --git a/python/basicblock.py b/python/basicblock.py new file mode 100644 index 00000000..9f256aa7 --- /dev/null +++ b/python/basicblock.py @@ -0,0 +1,339 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType +import architecture +import highlight +import function + + +class BasicBlockEdge(object): + def __init__(self, branch_type, source, target): + self.type = branch_type + self.source = source + self.target = target + + def __repr__(self): + if self.type == BranchType.UnresolvedBranch: + return "<%s>" % BranchType(self.type).name + elif self.target.arch: + return "<%s: %s@%#x>" % (BranchType(self.type).name, self.target.arch.name, self.target.start) + else: + return "<%s: %#x>" % (BranchType(self.type).name, self.target.start) + + @property + def back_edge(self): + """Whether the edge is a back edge (end of a loop)""" + return self.target in self.source.dominators + + +class BasicBlock(object): + def __init__(self, view, handle): + self.view = view + self.handle = core.handle_of_type(handle, core.BNBasicBlock) + + def __del__(self): + core.BNFreeBasicBlock(self.handle) + + def __eq__(self, value): + if not isinstance(value, BasicBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BasicBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def function(self): + """Basic block function (read-only)""" + func = core.BNGetBasicBlockFunction(self.handle) + if func is None: + return None + return function.Function(self.view, func) + + @property + def arch(self): + """Basic block architecture (read-only)""" + arch = core.BNGetBasicBlockArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def start(self): + """Basic block start (read-only)""" + return core.BNGetBasicBlockStart(self.handle) + + @property + def end(self): + """Basic block end (read-only)""" + return core.BNGetBasicBlockEnd(self.handle) + + @property + def length(self): + """Basic block length (read-only)""" + return core.BNGetBasicBlockLength(self.handle) + + @property + def index(self): + """Basic block index in list of blocks for the function (read-only)""" + return core.BNGetBasicBlockIndex(self.handle) + + @property + def outgoing_edges(self): + """List of basic block outgoing edges (read-only)""" + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + else: + target = None + result.append(BasicBlockEdge(branch_type, self, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) + return result + + @property + def incoming_edges(self): + """List of basic block incoming edges (read-only)""" + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockIncomingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + else: + target = None + result.append(BasicBlockEdge(branch_type, self, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) + return result + + @property + def has_undetermined_outgoing_edges(self): + """Whether basic block has undetermined outgoing edges (read-only)""" + return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) + + @property + def dominators(self): + """List of dominators for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockDominators(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def strict_dominators(self): + """List of strict dominators for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockStrictDominators(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def immediate_dominator(self): + """Immediate dominator of this basic block (read-only)""" + result = core.BNGetBasicBlockImmediateDominator(self.handle) + if not result: + return None + return BasicBlock(self.view, result) + + @property + def dominator_tree_children(self): + """List of child blocks in the dominator tree for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def dominance_frontier(self): + """Dominance frontier for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def annotations(self): + """List of automatic annotations for the start of this block (read-only)""" + return self.function.get_block_annotations(self.arch, self.start) + + @property + def disassembly_text(self): + """ + ``disassembly_text`` property which returns a list of function.DisassemblyTextLine objects for the current basic block. + :Example: + + >>> current_basic_block.disassembly_text + [<0x100000f30: _main:>, ...] + """ + return self.get_disassembly_text() + + @property + def highlight(self): + """Gets or sets the highlight color for basic block + + :Example: + + >>> current_basic_block.highlight = HighlightStandardColor.BlueHighlightColor + >>> current_basic_block.highlight + <color: blue> + """ + color = core.BNGetBasicBlockHighlight(self.handle) + if color.style == HighlightColorStyle.StandardHighlightColor: + return highlight.HighlightColor(color=color.color, alpha=color.alpha) + elif color.style == HighlightColorStyle.MixedHighlightColor: + return highlight.HighlightColor(color=color.color, mix_color=color.mixColor, mix=color.mix, alpha=color.alpha) + elif color.style == HighlightColorStyle.CustomHighlightColor: + return highlight.HighlightColor(red=color.r, green=color.g, blue=color.b, alpha=color.alpha) + return highlight.HighlightColor(color=HighlightStandardColor.NoHighlightColor) + + @highlight.setter + def highlight(self, value): + self.set_user_highlight(value) + + @classmethod + def get_iterated_dominance_frontier(self, blocks): + if len(blocks) == 0: + return [] + block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() + for i in xrange(len(blocks)): + block_set[i] = blocks[i].handle + count = ctypes.c_ulonglong() + out_blocks = core.BNGetBasicBlockIteratedDominanceFrontier(block_set, len(blocks), count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(blocks[0].view, core.BNNewBasicBlockReference(out_blocks[i]))) + core.BNFreeBasicBlockList(out_blocks, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __len__(self): + return int(core.BNGetBasicBlockLength(self.handle)) + + def __repr__(self): + arch = self.arch + if arch: + return "<block: %s@%#x-%#x>" % (arch.name, self.start, self.end) + else: + return "<block: %#x-%#x>" % (self.start, self.end) + + def __iter__(self): + start = self.start + end = self.end + + idx = start + while idx < end: + data = self.view.read(idx, 16) + inst_info = self.view.arch.get_instruction_info(data, idx) + inst_text = self.view.arch.get_instruction_text(data, idx) + + yield inst_text + idx += inst_info.length + + def mark_recent_use(self): + core.BNMarkBasicBlockAsRecentlyUsed(self.handle) + + def get_disassembly_text(self, settings=None): + """ + ``get_disassembly_text`` returns a list of function.DisassemblyTextLine objects for the current basic block. + :Example: + + >>>current_basic_block.get_disassembly_text() + [<0x100000f30: _main:>, <0x100000f30: push rbp>, ... ] + """ + settings_obj = None + if settings: + settings_obj = settings.handle + + count = ctypes.c_ulonglong() + lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.DisassemblyTextLine(addr, tokens)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def set_auto_highlight(self, color): + """ + ``set_auto_highlight`` highlights the current BasicBlock with the supplied color. + + .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + """ + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) + + def set_user_highlight(self, color): + """ + ``set_user_highlight`` highlights the current BasicBlock with the supplied color + + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :Example: + + >>> current_basic_block.set_user_highlight(highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_basic_block.set_user_highlight(HighlightStandardColor.BlueHighlightColor) + """ + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) diff --git a/python/binaryview.py b/python/binaryview.py new file mode 100644 index 00000000..dd37be69 --- /dev/null +++ b/python/binaryview.py @@ -0,0 +1,3800 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import struct +import traceback +import ctypes +import abc +import threading + +# Binary Ninja components +import _binaryninjacore as core +from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag +import function +import startup +import architecture +import platform +import associateddatastore +import fileaccessor +import filemetadata +import log +import databuffer +import basicblock +import types +import lineardisassembly + + +class BinaryDataNotification(object): + def __init__(self): + pass + + def data_written(self, view, offset, length): + pass + + def data_inserted(self, view, offset, length): + pass + + def data_removed(self, view, offset, length): + pass + + def function_added(self, view, func): + pass + + def function_removed(self, view, func): + pass + + def function_updated(self, view, func): + pass + + def data_var_added(self, view, var): + pass + + def data_var_removed(self, view, var): + pass + + def data_var_updated(self, view, var): + pass + + def string_found(self, view, string_type, offset, length): + pass + + def string_removed(self, view, string_type, offset, length): + pass + + def type_defined(self, view, name, type): + pass + + def type_undefined(self, view, name, type): + pass + + +class StringReference(object): + def __init__(self, string_type, start, length): + self.type = string_type + self.start = start + self.length = length + + def __repr__(self): + return "<%s: %#x, len %#x>" % (self.type, self.start, self.length) + + +class AnalysisCompletionEvent(object): + def __init__(self, view, callback): + self.view = view + self.callback = callback + self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) + self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb) + + def __del__(self): + core.BNFreeAnalysisCompletionEvent(self.handle) + + def _notify(self, ctxt): + try: + self.callback() + except: + log.log_error(traceback.format_exc()) + + def _empty_callback(self): + pass + + def cancel(self): + self.callback = self._empty_callback + core.BNCancelAnalysisCompletionEvent(self.handle) + + +class AnalysisProgress(object): + def __init__(self, state, count, total): + self.state = state + self.count = count + self.total = total + + def __str__(self): + if self.state == AnalysisState.DisassembleState: + return "Disassembling (%d/%d)" % (self.count, self.total) + if self.state == AnalysisState.AnalyzeState: + return "Analyzing (%d/%d)" % (self.count, self.total) + return "Idle" + + def __repr__(self): + return "<progress: %s>" % str(self) + + +class DataVariable(object): + def __init__(self, addr, var_type, auto_discovered): + self.address = addr + self.type = var_type + self.auto_discovered = auto_discovered + + def __repr__(self): + return "<var 0x%x: %s>" % (self.address, str(self.type)) + + +class BinaryDataNotificationCallbacks(object): + def __init__(self, view, notify): + self.view = view + self.notify = notify + self._cb = core.BNBinaryDataNotification() + self._cb.context = 0 + self._cb.dataWritten = self._cb.dataWritten.__class__(self._data_written) + self._cb.dataInserted = self._cb.dataInserted.__class__(self._data_inserted) + self._cb.dataRemoved = self._cb.dataRemoved.__class__(self._data_removed) + self._cb.functionAdded = self._cb.functionAdded.__class__(self._function_added) + self._cb.functionRemoved = self._cb.functionRemoved.__class__(self._function_removed) + self._cb.functionUpdated = self._cb.functionUpdated.__class__(self._function_updated) + self._cb.dataVariableAdded = self._cb.dataVariableAdded.__class__(self._data_var_added) + self._cb.dataVariableRemoved = self._cb.dataVariableRemoved.__class__(self._data_var_removed) + self._cb.dataVariableUpdated = self._cb.dataVariableUpdated.__class__(self._data_var_updated) + self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) + self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) + self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) + self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) + + def _register(self): + core.BNRegisterDataNotification(self.view.handle, self._cb) + + def _unregister(self): + core.BNUnregisterDataNotification(self.view.handle, self._cb) + + def _data_written(self, ctxt, view, offset, length): + try: + self.notify.data_written(self.view, offset, length) + except OSError: + log.log_error(traceback.format_exc()) + + def _data_inserted(self, ctxt, view, offset, length): + try: + self.notify.data_inserted(self.view, offset, length) + except: + log.log_error(traceback.format_exc()) + + def _data_removed(self, ctxt, view, offset, length): + try: + self.notify.data_removed(self.view, offset, length) + except: + log.log_error(traceback.format_exc()) + + def _function_added(self, ctxt, view, func): + try: + self.notify.function_added(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _function_removed(self, ctxt, view, func): + try: + self.notify.function_removed(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _function_updated(self, ctxt, view, func): + try: + self.notify.function_updated(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _data_var_added(self, ctxt, view, var): + try: + address = var.address + var_type = types.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.autoDiscovered + self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered)) + except: + log.log_error(traceback.format_exc()) + + def _data_var_removed(self, ctxt, view, var): + try: + address = var.address + var_type = types.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.autoDiscovered + self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered)) + except: + log.log_error(traceback.format_exc()) + + def _data_var_updated(self, ctxt, view, var): + try: + address = var.address + var_type = types.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.autoDiscovered + self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered)) + except: + log.log_error(traceback.format_exc()) + + def _string_found(self, ctxt, view, string_type, offset, length): + try: + self.notify.string_found(self.view, StringType(string_type), offset, length) + except: + log.log_error(traceback.format_exc()) + + def _string_removed(self, ctxt, view, string_type, offset, length): + try: + self.notify.string_removed(self.view, StringType(string_type), offset, length) + except: + log.log_error(traceback.format_exc()) + + def _type_defined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_defined(self.view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + except: + log.log_error(traceback.format_exc()) + + def _type_undefined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_undefined(self.view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + except: + log.log_error(traceback.format_exc()) + + +class _BinaryViewTypeMetaclass(type): + @property + def list(self): + """List all BinaryView types (read-only)""" + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetBinaryViewTypes(count) + result = [] + for i in xrange(0, count.value): + result.append(BinaryViewType(types[i])) + core.BNFreeBinaryViewTypeList(types) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetBinaryViewTypes(count) + try: + for i in xrange(0, count.value): + yield BinaryViewType(types[i]) + finally: + core.BNFreeBinaryViewTypeList(types) + + def __getitem__(self, value): + startup._init_plugins() + view_type = core.BNGetBinaryViewTypeByName(str(value)) + if view_type is None: + raise KeyError("'%s' is not a valid view type" % str(value)) + return BinaryViewType(view_type) + + +class BinaryViewType(object): + __metaclass__ = _BinaryViewTypeMetaclass + + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + + def __eq__(self, value): + if not isinstance(value, BinaryViewType): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryViewType): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def name(self): + """BinaryView name (read-only)""" + return core.BNGetBinaryViewTypeName(self.handle) + + @property + def long_name(self): + """BinaryView long name (read-only)""" + return core.BNGetBinaryViewTypeLongName(self.handle) + + def __repr__(self): + return "<view type: '%s'>" % self.name + + def create(self, data): + view = core.BNCreateBinaryViewOfType(self.handle, data.handle) + if view is None: + return None + return BinaryView(file_metadata=data.file, handle=view) + + def open(self, src, file_metadata=None): + data = BinaryView.open(src, file_metadata) + if data is None: + return None + return self.create(data) + + @classmethod + def get_view_of_file(cls, filename, update_analysis=True): + """ + ``get_view_of_file`` returns the first available, non-Raw `BinaryView` available. + + :param str filename: Path to filename or bndb + :param bool update_analysis: defaults to True. Pass False to not run update_analysis_and_wait. + :return: returns a BinaryView object for the given filename. + :rtype: BinaryView or None + """ + sqlite = "SQLite format 3" + if filename.endswith(".bndb"): + f = open(filename, 'r') + if f is None or f.read(len(sqlite)) != sqlite: + return None + f.close() + view = filemetadata.FileMetadata().open_existing_database(filename) + else: + view = BinaryView.open(filename) + + if view is None: + return None + for available in view.available_view_types: + if available.name != "Raw": + if filename.endswith(".bndb"): + bv = view.get_view_of_type(available.name) + else: + bv = cls[available.name].open(filename) + + if update_analysis: + bv.update_analysis_and_wait() + return bv + return None + + def is_valid_for_data(self, data): + return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle) + + def register_arch(self, ident, endian, arch): + core.BNRegisterArchitectureForViewType(self.handle, ident, endian, arch.handle) + + def get_arch(self, ident, endian): + arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) + if arch is None: + return None + return architecture.Architecture(arch) + + def register_platform(self, ident, arch, plat): + core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle) + + def register_default_platform(self, arch, plat): + core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, plat.handle) + + def get_platform(self, ident, arch): + plat = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) + if plat is None: + return None + return platform.Platform(None, plat) + + +class Segment(object): + def __init__(self, start, length, data_offset, data_length, flags): + self.start = start + self.length = length + self.data_offset = data_offset + self.data_length = data_length + self.flags = flags + + @property + def end(self): + return self.start + self.length + + def __len__(self): + return self.length + + def __repr__(self): + return "<segment: %#x-%#x, %s%s%s>" % (self.start, self.end, + "r" if (self.flags & SegmentFlag.SegmentReadable) != 0 else "-", + "w" if (self.flags & SegmentFlag.SegmentWritable) != 0 else "-", + "x" if (self.flags & SegmentFlag.SegmentExecutable) != 0 else "-") + + +class Section(object): + def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size): + self.name = name + self.type = section_type + self.start = start + self.length = length + self.linked_section = linked_section + self.info_section = info_section + self.info_data = info_data + self.align = align + self.entry_size = entry_size + + @property + def end(self): + return self.start + self.length + + def __len__(self): + return self.length + + def __repr__(self): + return "<section %s: %#x-%#x>" % (self.name, self.start, self.end) + + +class AddressRange(object): + def __init__(self, start, end): + self.start = start + self.end = end + + @property + def length(self): + return self.end - self.start + + def __len__(self): + return self.end - self.start + + def __repr__(self): + return "<%#x-%#x>" % (self.start, self.end) + + +class _BinaryViewAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} + + +class BinaryView(object): + """ + ``class BinaryView`` implements a view on binary data, and presents a queryable interface of a binary file. One key + job of BinaryView is file format parsing which allows Binary Ninja to read, write, insert, remove portions + of the file given a virtual address. For the purposes of this documentation we define a virtual address as the + memory address that the various pieces of the physical file will be loaded at. + + A binary file does not have to have just one BinaryView, thus much of the interface to manipulate disassembly exists + within or is accessed through a BinaryView. All files are guaranteed to have at least the ``Raw`` BinaryView. The + ``Raw`` BinaryView is simply a hex editor, but is helpful for manipulating binary files via their absolute addresses. + + BinaryViews are plugins and thus registered with Binary Ninja at startup, and thus should **never** be instantiated + directly as this is already done. The list of available BinaryViews can be seen in the BinaryViewType class which + provides an iterator and map of the various installed BinaryViews:: + + >>> list(BinaryViewType) + [<view type: 'Raw'>, <view type: 'ELF'>, <view type: 'Mach-O'>, <view type: 'PE'>] + >>> BinaryViewType['ELF'] + <view type: 'ELF'> + + To open a file with a given BinaryView the following code can be used:: + + >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") + >>> bv + <BinaryView: '/bin/ls', start 0x100000000, len 0xa000> + + `By convention in the rest of this document we will use bv to mean an open BinaryView of an executable file.` + When a BinaryView is open on an executable view, analysis does not automatically run, this can be done by running + the ``update_analysis_and_wait()`` method which disassembles the executable and returns when all disassembly is + finished:: + + >>> bv.update_analysis_and_wait() + >>> + + Since BinaryNinja's analysis is multi-threaded (depending on version) this can also be done in the background by + using the ``update_analysis()`` method instead. + + By standard python convention methods which start with '_' should be considered private and should not be called + externally. Additionanlly, methods which begin with ``perform_`` should not be called either and are + used explicitly for subclassing the BinaryView. + + .. 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 + _registered = False + _registered_cb = None + registered_view_type = None + next_address = 0 + _associated_data = {} + + def __init__(self, file_metadata=None, parent_view=None, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNBinaryView) + if file_metadata is None: + self.file = filemetadata.FileMetadata(handle=core.BNGetFileForView(handle)) + else: + self.file = file_metadata + elif self.__class__ is BinaryView: + startup._init_plugins() + if file_metadata is None: + file_metadata = filemetadata.FileMetadata() + self.handle = core.BNCreateBinaryDataView(file_metadata.handle) + self.file = filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) + else: + startup._init_plugins() + if not self.__class__._registered: + raise TypeError("view type not registered") + self._cb = core.BNCustomBinaryView() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + self._cb.insert = self._cb.insert.__class__(self._insert) + self._cb.remove = self._cb.remove.__class__(self._remove) + self._cb.getModification = self._cb.getModification.__class__(self._get_modification) + self._cb.isValidOffset = self._cb.isValidOffset.__class__(self._is_valid_offset) + 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.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) + self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) + self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) + self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.save = self._cb.save.__class__(self._save) + self.file = file_metadata + if parent_view is not None: + parent_view = parent_view.handle + self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, parent_view, self._cb) + self.notifications = {} + self.next_address = None # Do NOT try to access view before init() is called, use placeholder + + def __eq__(self, value): + if not isinstance(value, BinaryView): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryView): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @classmethod + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("view 'name' not defined") + if cls.long_name is None: + cls.long_name = cls.name + cls._registered_cb = core.BNCustomBinaryViewType() + cls._registered_cb.context = 0 + cls._registered_cb.create = cls._registered_cb.create.__class__(cls._create) + cls._registered_cb.isValidForData = cls._registered_cb.isValidForData.__class__(cls._is_valid_for_data) + cls.registered_view_type = BinaryViewType(core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb)) + cls._registered = True + + @classmethod + def _create(cls, ctxt, data): + try: + file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) + view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) + if view is None: + return None + return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return None + + @classmethod + def _is_valid_for_data(cls, ctxt, data): + try: + return cls.is_valid_for_data(BinaryView(handle=core.BNNewViewReference(data))) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def open(cls, src, file_metadata=None): + startup._init_plugins() + if isinstance(src, fileaccessor.FileAccessor): + if file_metadata is None: + file_metadata = filemetadata.FileMetadata() + view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) + else: + if file_metadata is None: + file_metadata = filemetadata.FileMetadata(str(src)) + view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) + if view is None: + return None + result = BinaryView(file_metadata=file_metadata, handle=view) + return result + + @classmethod + def new(cls, data=None, file_metadata=None): + startup._init_plugins() + if file_metadata is None: + file_metadata = filemetadata.FileMetadata() + if data is None: + view = core.BNCreateBinaryDataView(file_metadata.handle) + else: + buf = databuffer.DataBuffer(data) + view = core.BNCreateBinaryDataViewFromBuffer(file_metadata.handle, buf.handle) + if view is None: + return None + result = BinaryView(file_metadata=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_session_data(cls, name, value): + """ + ```set_default_session_data``` saves a variable to the BinaryView. + :param name: name of the variable to be saved + :param value: value of the variable to be saved + + :Example: + >>> BinaryView.set_default_session_data("variable_name", "value") + >>> bv.session_data.variable_name + 'value' + """ + _BinaryViewAssociatedDataStore.set_default(name, value) + + def __del__(self): + for i in self.notifications.values(): + i._unregister() + core.BNFreeBinaryView(self.handle) + + def __iter__(self): + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionList(self.handle, count) + try: + for i in xrange(0, count.value): + yield function.Function(self, core.BNNewFunctionReference(funcs[i])) + finally: + core.BNFreeFunctionList(funcs, count.value) + + @property + def parent_view(self): + """View that contains the raw data used by this view (read-only)""" + result = core.BNGetParentView(self.handle) + if result is None: + return None + return BinaryView(handle=result) + + @property + def modified(self): + """boolean modification state of the BinaryView (read/write)""" + return self.file.modified + + @modified.setter + def modified(self, value): + self.file.modified = value + + @property + def analysis_changed(self): + """boolean analysis state changed of the currently running analysis (read-only)""" + return self.file.analysis_changed + + @property + def has_database(self): + """boolean has a database been written to disk (read-only)""" + return self.file.has_database + + @property + def view(self): + return self.file.view + + @view.setter + def view(self, value): + self.file.view = value + + @property + def offset(self): + return self.file.offset + + @offset.setter + def offset(self, value): + self.file.offset = value + + @property + def start(self): + """Start offset of the binary (read-only)""" + return core.BNGetStartOffset(self.handle) + + @property + def end(self): + """End offset of the binary (read-only)""" + return core.BNGetEndOffset(self.handle) + + @property + def entry_point(self): + """Entry point of the binary (read-only)""" + return core.BNGetEntryPoint(self.handle) + + @property + def arch(self): + """The architecture associated with the current BinaryView (read/write)""" + arch = core.BNGetDefaultArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(handle=arch) + + @arch.setter + def arch(self, value): + if value is None: + core.BNSetDefaultArchitecture(self.handle, None) + else: + core.BNSetDefaultArchitecture(self.handle, value.handle) + + @property + def platform(self): + """The platform associated with the current BinaryView (read/write)""" + plat = core.BNGetDefaultPlatform(self.handle) + if plat is None: + return None + return platform.Platform(self.arch, handle=plat) + + @platform.setter + def platform(self, value): + if value is None: + core.BNSetDefaultPlatform(self.handle, None) + else: + core.BNSetDefaultPlatform(self.handle, value.handle) + + @property + def endianness(self): + """Endianness of the binary (read-only)""" + return Endianness(core.BNGetDefaultEndianness(self.handle)) + + @property + def address_size(self): + """Address size of the binary (read-only)""" + return core.BNGetViewAddressSize(self.handle) + + @property + def executable(self): + """Whether the binary is an executable (read-only)""" + return core.BNIsExecutableView(self.handle) + + @property + def functions(self): + """List of functions (read-only)""" + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(function.Function(self, core.BNNewFunctionReference(funcs[i]))) + core.BNFreeFunctionList(funcs, count.value) + return result + + @property + def has_functions(self): + """Boolean whether the binary has functions (read-only)""" + return core.BNHasFunctions(self.handle) + + @property + def entry_function(self): + """Entry function (read-only)""" + func = core.BNGetAnalysisEntryPoint(self.handle) + if func is None: + return None + return function.Function(self, func) + + @property + def symbols(self): + """Dict of symbols (read-only)""" + count = ctypes.c_ulonglong(0) + syms = core.BNGetSymbols(self.handle, count) + result = {} + for i in xrange(0, count.value): + sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) + result[sym.raw_name] = sym + core.BNFreeSymbolList(syms, count.value) + return result + + @property + def view_type(self): + """View type (read-only)""" + return core.BNGetViewType(self.handle) + + @property + def available_view_types(self): + """Available view types (read-only)""" + count = ctypes.c_ulonglong(0) + types = core.BNGetBinaryViewTypesForData(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BinaryViewType(types[i])) + core.BNFreeBinaryViewTypeList(types) + return result + + @property + def strings(self): + """List of strings (read-only)""" + return self.get_strings() + + @property + def saved(self): + """boolean state of whether or not the file has been saved (read/write)""" + return self.file.saved + + @saved.setter + def saved(self, value): + self.file.saved = value + + @property + def analysis_progress(self): + """Status of current analysis (read-only)""" + result = core.BNGetAnalysisProgress(self.handle) + return AnalysisProgress(result.state, result.count, result.total) + + @property + def linear_disassembly(self): + """Iterator for all lines in the linear disassembly of the view""" + return self.get_linear_disassembly(None) + + @property + def data_vars(self): + """List of data variables (read-only)""" + count = ctypes.c_ulonglong(0) + var_list = core.BNGetDataVariables(self.handle, count) + result = {} + for i in xrange(0, count.value): + addr = var_list[i].address + var_type = types.Type(core.BNNewTypeReference(var_list[i].type)) + auto_discovered = var_list[i].autoDiscovered + result[addr] = DataVariable(addr, var_type, auto_discovered) + core.BNFreeDataVariables(var_list, count.value) + return result + + @property + def types(self): + """List of defined types (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetAnalysisTypeList(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def segments(self): + """List of segments (read-only)""" + count = ctypes.c_ulonglong(0) + segment_list = core.BNGetSegments(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(Segment(segment_list[i].start, segment_list[i].length, + segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags)) + core.BNFreeSegmentList(segment_list) + return result + + @property + def sections(self): + """List of sections (read-only)""" + count = ctypes.c_ulonglong(0) + section_list = core.BNGetSections(self.handle, count) + result = {} + for i in xrange(0, count.value): + result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, + section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, + section_list[i].infoData, section_list[i].align, section_list[i].entrySize) + core.BNFreeSectionList(section_list, count.value) + return result + + @property + def allocated_ranges(self): + """List of valid address ranges for this view (read-only)""" + count = ctypes.c_ulonglong(0) + range_list = core.BNGetAllocatedRanges(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(AddressRange(range_list[i].start, range_list[i].end)) + core.BNFreeAddressRanges(range_list) + return result + + @property + def session_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)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + for s in i: + result += self.__getitem__(s) + return result + elif isinstance(i, slice): + if i.step is not None: + raise IndexError("step not implemented") + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop <= start: + return "" + return str(self.read(start, stop - start)) + elif i < 0: + if i >= -len(self): + value = str(self.read(int(len(self) + i), 1)) + if len(value) == 0: + return IndexError("index not readable") + return value + raise IndexError("index out of range") + elif (i >= self.start) and (i < self.end): + value = str(self.read(int(i), 1)) + if len(value) == 0: + return IndexError("index not readable") + return value + else: + raise IndexError("index out of range") + + def __setitem__(self, i, value): + if isinstance(i, slice): + if i.step is not None: + raise IndexError("step not supported on assignment") + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + self.remove(start, stop - start) + self.insert(start, value) + else: + self.write(start, value) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + if self.write(int(len(self) + i), value) != 1: + raise IndexError("index not writable") + else: + raise IndexError("index out of range") + elif (i >= self.start) and (i < self.end): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + if self.write(int(i), value) != 1: + raise IndexError("index not writable") + else: + raise IndexError("index out of range") + + def __repr__(self): + start = self.start + length = len(self) + if start != 0: + size = "start %#x, len %#x" % (start, length) + else: + size = "len %#x" % length + filename = self.file.filename + if len(filename) > 0: + return "<BinaryView: '%s', %s>" % (filename, size) + return "<BinaryView: %s>" % (size) + + def _init(self, ctxt): + try: + return self.init() + except: + log.log_error(traceback.format_exc()) + return False + + def _read(self, ctxt, dest, offset, length): + try: + data = self.perform_read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, str(data), len(data)) + return len(data) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _write(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_write(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _insert(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_insert(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _remove(self, ctxt, offset, length): + try: + return self.perform_remove(offset, length) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_modification(self, ctxt, offset): + try: + return self.perform_get_modification(offset) + except: + log.log_error(traceback.format_exc()) + return ModificationStatus.Original + + def _is_valid_offset(self, ctxt, offset): + try: + return self.perform_is_valid_offset(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_readable(self, ctxt, offset): + try: + return self.perform_is_offset_readable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_writable(self, ctxt, offset): + try: + return self.perform_is_offset_writable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_executable(self, ctxt, offset): + try: + return self.perform_is_offset_executable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _get_next_valid_offset(self, ctxt, offset): + try: + return self.perform_get_next_valid_offset(offset) + except: + log.log_error(traceback.format_exc()) + return offset + + def _get_start(self, ctxt): + try: + return self.perform_get_start() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_length(self, ctxt): + try: + return self.perform_get_length() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_entry_point(self, ctxt): + try: + return self.perform_get_entry_point() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _is_executable(self, ctxt): + try: + return self.perform_is_executable() + except: + log.log_error(traceback.format_exc()) + return False + + def _get_default_endianness(self, ctxt): + try: + return self.perform_get_default_endianness() + except: + log.log_error(traceback.format_exc()) + return Endianness.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.perform_get_address_size() + except: + log.log_error(traceback.format_exc()) + return 8 + + def _save(self, ctxt, file_accessor): + try: + return self.perform_save(fileaccessor.CoreFileAccessor(file_accessor)) + except: + log.log_error(traceback.format_exc()) + return False + + def init(self): + return True + + def get_disassembly(self, addr, arch=None): + """ + ``get_disassembly`` simple helper function for printing disassembly of a given address + + :param int addr: virtual address of instruction + :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None + :return: a str representation of the instruction at virtual address ``addr`` or None + :rtype: str or None + :Example: + + >>> bv.get_disassembly(bv.entry_point) + 'push ebp' + >>> + """ + if arch is None: + arch = self.arch + txt, size = arch.get_instruction_text(self.read(addr, arch.max_instr_length), addr) + self.next_address = addr + size + if txt is None: + return None + return ''.join(str(a) for a in txt).strip() + + def get_next_disassembly(self, arch=None): + """ + ``get_next_disassembly`` simple helper function for printing disassembly of the next instruction. + The internal state of the instruction to be printed is stored in the ``next_address`` attribute + + :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None + :return: a str representation of the instruction at virtual address ``self.next_address`` + :rtype: str or None + :Example: + + >>> bv.get_next_disassembly() + 'push ebp' + >>> bv.get_next_disassembly() + 'mov ebp, esp' + >>> #Now reset the starting point back to the entry point + >>> bv.next_address = bv.entry_point + >>> bv.get_next_disassembly() + 'push ebp' + >>> + """ + 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, arch.max_instr_length), self.next_address) + self.next_address += size + if txt is None: + return None + return ''.join(str(a) for a in txt).strip() + + def perform_save(self, accessor): + if self.parent_view is not None: + return self.parent_view.save(accessor) + return False + + @abc.abstractmethod + def perform_get_address_size(self): + raise NotImplementedError + + def perform_get_length(self): + """ + ``perform_get_length`` implements a query for the size of the virtual address range used by + the BinaryView. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :return: returns the size of the virtual address range used by the BinaryView. + :rtype: int + """ + return 0 + + def perform_read(self, addr, length): + """ + ``perform_read`` implements a mapping between a virtual address and an absolute file offset, reading + ``length`` bytes from the rebased address ``addr``. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to attempt to read from + :param int length: the number of bytes to be read + :return: length bytes read from addr, should return empty string on error + :rtype: str + """ + return "" + + def perform_write(self, addr, data): + """ + ``perform_write`` implements a mapping between a virtual address and an absolute file offset, writing + the bytes ``data`` to rebased address ``addr``. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address + :param str data: the data to be written + :return: length of data written, should return 0 on error + :rtype: int + """ + return 0 + + def perform_insert(self, addr, data): + """ + ``perform_insert`` implements a mapping between a virtual address and an absolute file offset, inserting + the bytes ``data`` to rebased address ``addr``. + + .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, inserting is disallowed + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address + :param str data: the data to be inserted + :return: length of data inserted, should return 0 on error + :rtype: int + """ + return 0 + + def perform_remove(self, addr, length): + """ + ``perform_remove`` implements a mapping between a virtual address and an absolute file offset, removing + ``length`` bytes from the rebased address ``addr``. + + .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, removing data is disallowed + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address + :param str data: the data to be removed + :return: length of data removed, should return 0 on error + :rtype: int + """ + return 0 + + def perform_get_modification(self, addr): + """ + ``perform_get_modification`` implements query to the whether the virtual address ``addr`` is modified. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to be checked + :return: One of the following: Original = 0, Changed = 1, Inserted = 2 + :rtype: ModificationStatus + """ + return ModificationStatus.Original + + def perform_is_valid_offset(self, addr): + """ + ``perform_is_valid_offset`` implements a check if an virtual address ``addr`` is valid. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is valid, false if the virtual address is invalid or error + :rtype: bool + """ + data = self.read(addr, 1) + return (data is not None) and (len(data) == 1) + + def perform_is_offset_readable(self, offset): + """ + ``perform_is_offset_readable`` implements a check if an virtual address is readable. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int offset: a virtual address to be checked + :return: true if the virtual address is readable, false if the virtual address is not readable or error + :rtype: bool + """ + return self.is_valid_offset(offset) + + def perform_is_offset_writable(self, addr): + """ + ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is writable. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is writable, false if the virtual address is not writable or error + :rtype: bool + """ + return self.is_valid_offset(addr) + + def perform_is_offset_executable(self, addr): + """ + ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is executable. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is executable, false if the virtual address is not executable or error + :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 + memory address. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :param int addr: a virtual address to start checking from. + :return: the next readable, writable, or executable virtual memory address + :rtype: int + """ + if addr < self.perform_get_start(): + return self.perform_get_start() + return addr + + def perform_get_start(self): + """ + ``perform_get_start`` implements a query for the first readable, writable, or executable virtual address in + the BinaryView. + + .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide + data without overriding this method. + .. warning:: This method **must not** be called directly. + + :return: returns the first virtual address in the BinaryView. + :rtype: int + """ + return 0 + + def perform_get_entry_point(self): + """ + ``perform_get_entry_point`` implements a query for the initial entry point for code execution. + + .. note:: This method **should** be implmented for custom BinaryViews that are executable. + .. warning:: This method **must not** be called directly. + + :return: the virtual address of the entry point + :rtype: int + """ + return 0 + + def perform_is_executable(self): + """ + ``perform_is_executable`` implements a check which returns true if the BinaryView is executable. + + .. note:: This method **must** be implemented for custom BinaryViews that are executable. + .. warning:: This method **must not** be called directly. + + :return: true if the current BinaryView is executable, false if it is not executable or on error + :rtype: bool + """ + return False + + def perform_get_default_endianness(self): + """ + ``perform_get_default_endianness`` implements a check which returns true if the BinaryView is executable. + + .. note:: This method **may** be implemented for custom BinaryViews that are not LittleEndian. + .. warning:: This method **must not** be called directly. + + :return: either ``Endianness.LittleEndian`` or ``Endianness.BigEndian`` + :rtype: Endianness + """ + return Endianness.LittleEndian + + 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, progress_func) + + 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(progress_func) + + def get_view_of_type(self, name): + """ + ``get_view_of_type`` returns the BinaryView associated with the provided name if it exists. + + :param str name: Name of the view to be retrieved + :return: BinaryView object assocated with the provided name or None on failure + :rtype: BinaryView or None + """ + return self.file.get_view_of_type(name) + + def begin_undo_actions(self): + """ + ``begin_undo_actions`` start recording actions taken so the can be undone at some point. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + self.file.begin_undo_actions() + + def add_undo_action(self, action): + core.BNAddUndoAction(self.handle, action.__class__.name, action._cb) + + def commit_undo_actions(self): + """ + ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + self.file.commit_undo_actions() + + def undo(self): + """ + ``undo`` undo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + self.file.undo() + + def redo(self): + """ + ``redo`` redo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + self.file.redo() + + def navigate(self, view, offset): + self.file.navigate(view, offset) + + def read(self, addr, length): + """ + ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. + + :param int addr: virtual address to read from. + :param int length: number of bytes to read. + :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data. + :rtype: str + :Example: + + >>> #Opening a x86_64 Mach-O binary + >>> bv = BinaryViewType['Raw'].open("/bin/ls") + >>> bv.read(0,4) + \'\\xcf\\xfa\\xed\\xfe\' + """ + buf = databuffer.DataBuffer(handle=core.BNReadViewBuffer(self.handle, addr, length)) + return str(buf) + + def write(self, addr, data): + """ + ``write`` writes the bytes in ``data`` to the virtual address ``addr``. + + :param int addr: virtual address to write to. + :param str data: data to be written at addr. + :return: number of bytes written to virtual address ``addr`` + :rtype: int + :Example: + + >>> bv.read(0,4) + 'BBBB' + >>> bv.write(0, "AAAA") + 4L + >>> bv.read(0,4) + 'AAAA' + """ + buf = databuffer.DataBuffer(data) + return core.BNWriteViewBuffer(self.handle, addr, buf.handle) + + def insert(self, addr, data): + """ + ``insert`` inserts the bytes in ``data`` to the virtual address ``addr``. + + :param int addr: virtual address to write to. + :param str data: data to be inserted at addr. + :return: number of bytes inserted to virtual address ``addr`` + :rtype: int + :Example: + + >>> bv.insert(0,"BBBB") + 4L + >>> bv.read(0,8) + 'BBBBAAAA' + """ + buf = databuffer.DataBuffer(data) + return core.BNInsertViewBuffer(self.handle, addr, buf.handle) + + def remove(self, addr, length): + """ + ``remove`` removes at most ``length`` bytes from virtual address ``addr``. + + :param int addr: virtual address to remove from. + :param int length: number of bytes to remove. + :return: number of bytes removed from virtual address ``addr`` + :rtype: int + :Example: + + >>> bv.read(0,8) + 'BBBBAAAA' + >>> bv.remove(0,4) + 4L + >>> bv.read(0,4) + 'AAAA' + """ + return core.BNRemoveViewData(self.handle, addr, length) + + def get_modification(self, addr, length=None): + """ + ``get_modification`` returns the modified bytes of up to ``length`` bytes from virtual address ``addr``, or if + ``length`` is None returns the ModificationStatus. + + :param int addr: virtual address to get modification from + :param int length: optional length of modification + :return: Either ModificationStatus of the byte at ``addr``, or string of modified bytes at ``addr`` + :rtype: ModificationStatus or str + """ + if length is None: + return ModificationStatus(core.BNGetModification(self.handle, addr)) + data = (ModificationStatus * length)() + length = core.BNGetModificationArray(self.handle, addr, data, length) + return data[0:length] + + def is_valid_offset(self, addr): + """ + ``is_valid_offset`` checks if an virtual address ``addr`` is valid . + + :param int addr: a virtual address to be checked + :return: true if the virtual address is valid, false if the virtual address is invalid or error + :rtype: bool + """ + return core.BNIsValidOffset(self.handle, addr) + + def is_offset_readable(self, addr): + """ + ``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 + :rtype: bool + """ + return core.BNIsOffsetReadable(self.handle, addr) + + def is_offset_writable(self, addr): + """ + ``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 + :rtype: bool + """ + return core.BNIsOffsetWritable(self.handle, addr) + + def is_offset_executable(self, addr): + """ + ``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 + :rtype: bool + """ + return core.BNIsOffsetExecutable(self.handle, addr) + + def save(self, dest): + """ + ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. + + :param str dest: destination path and filename of file to be written + :return: boolean True on success, False on failure + :rtype: bool + """ + if isinstance(dest, fileaccessor.FileAccessor): + return core.BNSaveToFile(self.handle, dest._cb) + return core.BNSaveToFilename(self.handle, str(dest)) + + def register_notification(self, notify): + cb = BinaryDataNotificationCallbacks(self, notify) + cb._register() + self.notifications[notify] = cb + + def unregister_notification(self, notify): + if notify in self.notifications: + self.notifications[notify]._unregister() + del self.notifications[notify] + + def add_function(self, addr, plat=None): + """ + ``add_function`` add a new function of the given ``plat`` at the virtual address ``addr`` + + :param int addr: virtual address of the function to be added + :param Platform plat: Platform for the function to be added + :rtype: None + :Example: + + >>> bv.add_function(1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + if self.platform is None: + raise Exception("Default platform not set in BinaryView") + if plat is None: + plat = self.platform + core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr) + + def add_entry_point(self, addr, plat=None): + """ + ``add_entry_point`` adds an virtual address to start analysis from for a given plat. + + :param int addr: virtual address to start analysis from + :param Platform plat: Platform for the entry point analysis + :rtype: None + :Example: + >>> bv.add_entry_point(0xdeadbeef) + >>> + """ + if self.platform is None: + raise Exception("Default platform not set in BinaryView") + if plat is None: + plat = self.platform + core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) + + def remove_function(self, func): + """ + ``remove_function`` removes the function ``func`` from the list of functions + + :param Function func: a Function object. + :rtype: None + :Example: + + >>> bv.functions + [<func: x86_64@0x1>] + >>> bv.remove_function(bv.functions[0]) + >>> bv.functions + [] + """ + core.BNRemoveAnalysisFunction(self.handle, func.handle) + + def create_user_function(self, addr, plat=None): + """ + ``create_user_function`` add a new *user* function of the given ``plat`` at the virtual address ``addr`` + + :param int addr: virtual address of the *user* function to be added + :param Platform plat: Platform for the function to be added + :rtype: None + :Example: + + >>> bv.create_user_function(1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + if plat is None: + plat = self.platform + core.BNCreateUserFunction(self.handle, plat.handle, addr) + + def remove_user_function(self, func): + """ + ``remove_user_function`` removes the *user* function ``func`` from the list of functions + + :param Function func: a Function object. + :rtype: None + :Example: + + >>> bv.functions + [<func: x86_64@0x1>] + >>> bv.remove_user_function(bv.functions[0]) + >>> bv.functions + [] + """ + core.BNRemoveUserFunction(self.handle, func.handle) + + def update_analysis(self): + """ + ``update_analysis`` asynchronously starts the analysis running and returns immediately. Analysis of BinaryViews + does not occur automatically, the user must start analysis by calling either ``update_analysis()`` or + ``update_analysis_and_wait()``. An analysis update **must** be run after changes are made which could change + analysis results such as adding functions. + + :rtype: None + """ + core.BNUpdateAnalysis(self.handle) + + def update_analysis_and_wait(self): + """ + ``update_analysis_and_wait`` blocking call to update the analysis, this call returns when the analysis is + complete. Analysis of BinaryViews does not occur automatically, the user must start analysis by calling either + ``update_analysis()`` or ``update_analysis_and_wait()``. An analysis update **must** be run after changes are + made which could change analysis results such as adding functions. + + :rtype: None + """ + class WaitEvent(object): + def __init__(self): + self.cond = threading.Condition() + self.done = False + + def complete(self): + self.cond.acquire() + self.done = True + self.cond.notify() + self.cond.release() + + def wait(self): + self.cond.acquire() + while not self.done: + self.cond.wait() + self.cond.release() + + wait = WaitEvent() + # TODO: figure out if we actually need this 'event' variable, likely we do + event = AnalysisCompletionEvent(self, lambda: wait.complete()) + core.BNUpdateAnalysis(self.handle) + wait.wait() + + def abort_analysis(self): + """ + ``abort_analysis`` will abort the currently running analysis. + + :rtype: None + """ + core.BNAbortAnalysis(self.handle) + + def define_data_var(self, addr, var_type): + """ + ``define_data_var`` defines a non-user data variable ``var_type`` at the virtual address ``addr``. + + :param int addr: virtual address to define the given data variable + :param Type var_type: type to be defined at the given virtual address + :rtype: None + :Example: + + >>> t = bv.parse_type_string("int foo") + >>> t + (<type: int32_t>, 'foo') + >>> bv.define_data_var(bv.entry_point, t[0]) + >>> + """ + core.BNDefineDataVariable(self.handle, addr, var_type.handle) + + def define_user_data_var(self, addr, var_type): + """ + ``define_data_var`` defines a user data variable ``var_type`` at the virtual address ``addr``. + + :param int addr: virtual address to define the given data variable + :param binaryninja.Type var_type: type to be defined at the given virtual address + :rtype: None + :Example: + + >>> t = bv.parse_type_string("int foo") + >>> t + (<type: int32_t>, 'foo') + >>> bv.define_user_data_var(bv.entry_point, t[0]) + >>> + """ + core.BNDefineUserDataVariable(self.handle, addr, var_type.handle) + + def undefine_data_var(self, addr): + """ + ``undefine_data_var`` removes the non-user data variable at the virtual address ``addr``. + + :param int addr: virtual address to define the data variable to be removed + :rtype: None + :Example: + + >>> bv.undefine_data_var(bv.entry_point) + >>> + """ + core.BNUndefineDataVariable(self.handle, addr) + + def undefine_user_data_var(self, addr): + """ + ``undefine_data_var`` removes the user data variable at the virtual address ``addr``. + + :param int addr: virtual address to define the data variable to be removed + :rtype: None + :Example: + + >>> bv.undefine_user_data_var(bv.entry_point) + >>> + """ + core.BNUndefineUserDataVariable(self.handle, addr) + + def get_data_var_at(self, addr): + """ + ``get_data_var_at`` returns the data type at a given virtual address. + + :param int addr: virtual address to get the data type from + :return: returns the DataVariable at the given virtual address, None on error. + :rtype: DataVariable + :Example: + + >>> t = bv.parse_type_string("int foo") + >>> bv.define_data_var(bv.entry_point, t[0]) + >>> bv.get_data_var_at(bv.entry_point) + <var 0x100001174: int32_t> + + """ + var = core.BNDataVariable() + if not core.BNGetDataVariableAtAddress(self.handle, addr, var): + return None + return DataVariable(var.address, types.Type(var.type), var.autoDiscovered) + + def get_function_at(self, addr, plat=None): + """ + ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: + + :param int addr: virtual address of the desired function + :param Platform plat: plat of the desired function + :return: returns a Function object or None for the function at the virtual address provided + :rtype: Function + :Example: + + >>> bv.get_function_at(bv.entry_point) + <func: x86_64@0x100001174> + >>> + """ + if plat is None: + plat = self.platform + func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) + if func is None: + return None + return function.Function(self, func) + + def get_functions_at(self, addr): + """ + ``get_functions_at`` get a list of binaryninja.Function objects (one for each valid plat) at the given + virtual address. Binary Ninja does not limit the number of platforms in a given file thus there may be multiple + functions defined from different architectures at the same location. This API allows you to query all of valid + platforms. + + :param int addr: virtual address of the desired Function object list. + :return: a list of binaryninja.Function objects defined at the provided virtual address + :rtype: list(Function) + """ + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionsForAddress(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(function.Function(self, core.BNNewFunctionReference(funcs[i]))) + core.BNFreeFunctionList(funcs, count.value) + return result + + def get_recent_function_at(self, addr): + func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) + if func is None: + return None + return function.Function(self, func) + + def get_basic_blocks_at(self, addr): + """ + ``get_basic_blocks_at`` get a list of :py:Class:`BasicBlock` objects which exist 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.BNGetBasicBlocksForAddress(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(basicblock.BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + def get_basic_blocks_starting_at(self, addr): + """ + ``get_basic_blocks_starting_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.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: + return None + return basicblock.BasicBlock(self, block) + + def get_code_refs(self, addr, length=None): + """ + ``get_code_refs`` returns a list of ReferenceSource objects (xrefs or cross-references) that point to the provided virtual address. + + :param int addr: virtual address to query for references + :return: List of References for the given virtual address + :rtype: list(ReferenceSource) + :Example: + + >>> bv.get_code_refs(here) + [<ref: x86@0x4165ff>] + >>> + + """ + count = ctypes.c_ulonglong(0) + if length is None: + refs = core.BNGetCodeReferences(self.handle, addr, count) + else: + refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, count) + result = [] + for i in xrange(0, count.value): + if refs[i].func: + func = function.Function(self, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.Architecture(refs[i].arch) + else: + arch = None + addr = refs[i].addr + result.append(architecture.ReferenceSource(func, arch, addr)) + core.BNFreeCodeReferences(refs, count.value) + return result + + def get_symbol_at(self, addr): + """ + ``get_symbol_at`` returns the Symbol at the provided virtual address. + + :param int addr: virtual address to query for symbol + :return: Symbol for the given virtual address + :rtype: Symbol + :Example: + + >>> bv.get_symbol_at(bv.entry_point) + <FunctionSymbol: "_start" @ 0x100001174> + >>> + """ + sym = core.BNGetSymbolByAddress(self.handle, addr) + if sym is None: + return None + return types.Symbol(None, None, None, handle = sym) + + def get_symbol_by_raw_name(self, name): + """ + ``get_symbol_by_raw_name`` retrieves a Symbol object for the given a raw (mangled) name. + + :param str name: raw (mangled) name of Symbol to be retrieved + :return: Symbol object corresponding to the provided raw name + :rtype: Symbol + :Example: + + >>> bv.get_symbol_by_raw_name('?testf@Foobar@@SA?AW4foo@1@W421@@Z') + <FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100> + >>> + """ + sym = core.BNGetSymbolByRawName(self.handle, name) + if sym is None: + return None + return types.Symbol(None, None, None, handle = sym) + + def get_symbols_by_name(self, name): + """ + ``get_symbols_by_name`` retrieves a list of Symbol objects for the given symbol name. + + :param str name: name of Symbol object to be retrieved + :return: Symbol object corresponding to the provided name + :rtype: Symbol + :Example: + + >>> bv.get_symbols_by_name('?testf@Foobar@@SA?AW4foo@1@W421@@Z') + [<FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100>] + >>> + """ + count = ctypes.c_ulonglong(0) + syms = core.BNGetSymbolsByName(self.handle, name, count) + result = [] + for i in xrange(0, count.value): + result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def get_symbols(self, start = None, length = None): + """ + ``get_symbols`` retrieves the list of all Symbol objects in the optionally provided range. + + :param int start: optional start virtual address + :param int length: optional length + :return: list of all Symbol objects, or those Symbol objects in the range of ``start``-``start+length`` + :rtype: list(Symbol) + :Example: + + >>> bv.get_symbols(0x1000200c, 1) + [<ImportAddressSymbol: "KERNEL32!IsProcessorFeaturePresent@IAT" @ 0x1000200c>] + >>> + """ + count = ctypes.c_ulonglong(0) + if start is None: + syms = core.BNGetSymbols(self.handle, count) + else: + syms = core.BNGetSymbolsInRange(self.handle, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def get_symbols_of_type(self, sym_type, start = None, length = None): + """ + ``get_symbols_of_type`` retrieves a list of all Symbol objects of the provided symbol type in the optionally + provided range. + + :param SymbolType sym_type: A Symbol type: :py:Class:`Symbol`. + :param int start: optional start virtual address + :param int length: optional length + :return: list of all Symbol objects of type sym_type, or those Symbol objects in the range of ``start``-``start+length`` + :rtype: list(Symbol) + :Example: + + >>> bv.get_symbols_of_type(SymbolType.ImportAddressSymbol, 0x10002028, 1) + [<ImportAddressSymbol: "KERNEL32!GetCurrentThreadId@IAT" @ 0x10002028>] + >>> + """ + if isinstance(sym_type, str): + sym_type = SymbolType[sym_type] + count = ctypes.c_ulonglong(0) + if start is None: + syms = core.BNGetSymbolsOfType(self.handle, sym_type, count) + else: + syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def define_auto_symbol(self, sym): + """ + ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. + + :param Symbol sym: the symbol to define + :rtype: None + """ + core.BNDefineAutoSymbol(self.handle, sym.handle) + + def define_auto_symbol_and_var_or_function(self, sym, sym_type, plat=None): + """ + ``define_auto_symbol_and_var_or_function`` + + :param Symbol sym: the symbol to define + :param SymbolType sym_type: Type of symbol being defined + :param Platform plat: (optional) platform + :rtype: None + """ + if plat is None: + plat = self.plat + if plat is not None: + plat = plat.handle + if sym_type is not None: + sym_type = sym_type.handle + core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, plat, sym.handle, sym_type) + + def undefine_auto_symbol(self, sym): + """ + ``undefine_auto_symbol`` removes a symbol from the internal list of automatically discovered Symbol objects. + + :param Symbol sym: the symbol to undefine + :rtype: None + """ + core.BNUndefineAutoSymbol(self.handle, sym.handle) + + def define_user_symbol(self, sym): + """ + ``define_user_symbol`` adds a symbol to the internal list of user added Symbol objects. + + :param Symbol sym: the symbol to define + :rtype: None + """ + core.BNDefineUserSymbol(self.handle, sym.handle) + + def undefine_user_symbol(self, sym): + """ + ``undefine_user_symbol`` removes a symbol from the internal list of user added Symbol objects. + + :param Symbol sym: the symbol to undefine + :rtype: None + """ + core.BNUndefineUserSymbol(self.handle, sym.handle) + + def define_imported_function(self, import_addr_sym, func): + """ + ``define_imported_function`` defines an imported Function ``func`` with a ImportedFunctionSymbol type. + + :param Symbol import_addr_sym: A Symbol object with type ImportedFunctionSymbol + :param Function func: A Function object to define as an imported function + :rtype: None + """ + core.BNDefineImportedFunction(self.handle, import_addr_sym.handle, func.handle) + + def is_never_branch_patch_available(self, addr, arch=None): + """ + ``is_never_branch_patch_available`` queries the architecture plugin to determine if the instruction at the + instruction at ``addr`` can be made to **never branch**. The actual logic of which is implemented in the + ``perform_is_never_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_never_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_never_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_always_branch_patch_available(self, addr, arch=None): + """ + ``is_always_branch_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` can be made to **always branch**. The actual logic of which is implemented in the + ``perform_is_always_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture for the current view + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_always_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_always_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_invert_branch_patch_available(self, addr, arch=None): + """ + ``is_invert_branch_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` + is a branch that can be inverted. The actual logic of which is implemented in the + ``perform_is_invert_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_invert_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_invert_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_zero_patch_available(self, addr, arch=None): + """ + ``is_skip_and_return_zero_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return zero. The actual + logic of which is implemented in the ``perform_is_skip_and_return_zero_patch_available`` in the corresponding + architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012f6) + 'mov dword [0x10003020], eax' + >>> bv.is_skip_and_return_zero_patch_available(0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_zero_patch_available(0x100012fb) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_value_patch_available(self, addr, arch=None): + """ + ``is_skip_and_return_value_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return a value. The actual + logic of which is implemented in the ``perform_is_skip_and_return_value_patch_available`` in the corresponding + architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012f6) + 'mov dword [0x10003020], eax' + >>> bv.is_skip_and_return_value_patch_available(0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_value_patch_available(0x100012fb) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) + + def convert_to_nop(self, addr, arch=None): + """ + ``convert_to_nop`` converts the instruction at virtual address ``addr`` to a nop of the provided architecture. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to conver to nops + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.convert_to_nop(0x100012fb) + True + >>> #The above 'call' instruction is 5 bytes, a nop in x86 is 1 byte, + >>> # thus 5 nops are used: + >>> bv.get_disassembly(0x100012fb) + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'mov byte [ebp-0x1c], al' + """ + if arch is None: + arch = self.arch + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def always_branch(self, addr, arch=None): + """ + ``always_branch`` convert the instruction of architecture ``arch`` at the virtual address ``addr`` to an + unconditional branch. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.always_branch(0x100012ef) + True + >>> bv.get_disassembly(0x100012ef) + 'jmp 0x100012f5' + >>> + """ + if arch is None: + arch = self.arch + return core.BNAlwaysBranch(self.handle, arch.handle, addr) + + def never_branch(self, addr, arch=None): + """ + ``never_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to + a fall through. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> bv.never_branch(0x1000130e) + True + >>> bv.get_disassembly(0x1000130e) + 'nop' + >>> + """ + if arch is None: + arch = self.arch + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def invert_branch(self, addr, arch=None): + """ + ``invert_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to the + inverse branch. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'je 0x10001317' + >>> bv.invert_branch(0x1000130e) + True + >>> + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> + """ + if arch is None: + arch = self.arch + return core.BNInvertBranch(self.handle, arch.handle, addr) + + def skip_and_return_value(self, addr, value, arch=None): + """ + ``skip_and_return_value`` convert the ``call`` instruction of architecture ``arch`` at the virtual address + ``addr`` to the equivilent of returning a value. + + :param int addr: virtual address of the instruction to be modified + :param int value: value to make the instruction *return* + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000132a) + 'call 0x1000134a' + >>> bv.skip_and_return_value(0x1000132a, 42) + True + >>> #The return value from x86 functions is stored in eax thus: + >>> bv.get_disassembly(0x1000132a) + 'mov eax, 0x2a' + >>> + """ + if arch is None: + arch = self.arch + return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) + + def get_instruction_length(self, addr, arch=None): + """ + ``get_instruction_length`` returns the number of bytes in the instruction of Architecture ``arch`` at the virtual + address ``addr`` + + :param int addr: virtual address of the instruction query + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: Number of bytes in instruction + :rtype: int + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.get_instruction_length(0x100012f1) + 2L + >>> + """ + if arch is None: + arch = self.arch + return core.BNGetInstructionLength(self.handle, arch.handle, addr) + + def notify_data_written(self, offset, length): + core.BNNotifyDataWritten(self.handle, offset, length) + + def notify_data_inserted(self, offset, length): + core.BNNotifyDataInserted(self.handle, offset, length) + + def notify_data_removed(self, offset, length): + core.BNNotifyDataRemoved(self.handle, offset, length) + + def get_strings(self, start = None, length = None): + """ + ``get_strings`` returns a list of strings defined in the binary in the optional virtual address range: + ``start-(start+length)`` + + :param int start: optional virtual address to start the string list from, defaults to start of the binary + :param int length: optional length range to return strings from, defaults to length of the binary + :return: a list of all strings or a list of strings defined between ``start`` and ``start+length`` + :rtype: list(str()) + :Example: + + >>> bv.get_strings(0x1000004d, 1) + [<AsciiString: 0x1000004d, len 0x2c>] + >>> + """ + count = ctypes.c_ulonglong(0) + if start is None: + strings = core.BNGetStrings(self.handle, count) + else: + strings = core.BNGetStringsInRange(self.handle, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(StringReference(StringType(strings[i].type), strings[i].start, strings[i].length)) + core.BNFreeStringReferenceList(strings) + return result + + def add_analysis_completion_event(self, callback): + """ + ``add_analysis_completion_event`` sets up a call back function to be called when analysis has been completed. + This is helpful when using asynchronously analysis. + + :param callable() callback: A function to be called with no parameters when analysis has completed. + :return: An initialized AnalysisCompletionEvent object. + :rtype: AnalysisCompletionEvent + :Example: + + >>> def completionEvent(): + ... print "done" + ... + >>> bv.add_analysis_completion_event(completionEvent) + <binaryninja.AnalysisCompletionEvent object at 0x10a2c9f10> + >>> bv.update_analysis() + done + >>> + """ + return AnalysisCompletionEvent(self, callback) + + def get_next_function_start_after(self, addr): + """ + ``get_next_function_start_after`` returns the virtual address of the Function that occurs after the virtual address + ``addr`` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the next Function + :rtype: int + :Example: + + >>> bv.get_next_function_start_after(bv.entry_point) + 268441061L + >>> hex(bv.get_next_function_start_after(bv.entry_point)) + '0x100015e5L' + >>> hex(bv.get_next_function_start_after(0x100015e5)) + '0x10001629L' + >>> hex(bv.get_next_function_start_after(0x10001629)) + '0x1000165eL' + >>> + """ + return core.BNGetNextFunctionStartAfterAddress(self.handle, addr) + + def get_next_basic_block_start_after(self, addr): + """ + ``get_next_basic_block_start_after`` returns the virtual address of the BasicBlock that occurs after the virtual + address ``addr`` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the next BasicBlock + :rtype: int + :Example: + + >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) + '0x100014a8L' + >>> hex(bv.get_next_basic_block_start_after(0x100014a8)) + '0x100014adL' + >>> + """ + return core.BNGetNextBasicBlockStartAfterAddress(self.handle, addr) + + def get_next_data_after(self, addr): + """ + ``get_next_data_after`` retrieves the virtual address of the next non-code byte. + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the next data byte which is data, not code + :rtype: int + :Example: + + >>> hex(bv.get_next_data_after(0x10000000)) + '0x10000001L' + """ + return core.BNGetNextDataAfterAddress(self.handle, addr) + + def get_next_data_var_after(self, addr): + """ + ``get_next_data_var_after`` retrieves the next virtual address of the next :py:Class:`DataVariable` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the next :py:Class:`DataVariable` + :rtype: int + :Example: + + >>> hex(bv.get_next_data_var_after(0x10000000)) + '0x1000003cL' + >>> bv.get_data_var_at(0x1000003c) + <var 0x1000003c: int32_t> + >>> + """ + return core.BNGetNextDataVariableAfterAddress(self.handle, addr) + + def get_previous_function_start_before(self, addr): + """ + ``get_previous_function_start_before`` returns the virtual address of the Function that occurs prior to the + virtual address provided + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the previous Function + :rtype: int + :Example: + + >>> hex(bv.entry_point) + '0x1000149fL' + >>> hex(bv.get_next_function_start_after(bv.entry_point)) + '0x100015e5L' + >>> hex(bv.get_previous_function_start_before(0x100015e5)) + '0x1000149fL' + >>> + """ + return core.BNGetPreviousFunctionStartBeforeAddress(self.handle, addr) + + def get_previous_basic_block_start_before(self, addr): + """ + ``get_previous_basic_block_start_before`` returns the virtual address of the BasicBlock that occurs prior to the + provided virtual address + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the previous BasicBlock + :rtype: int + :Example: + + >>> hex(bv.entry_point) + '0x1000149fL' + >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) + '0x100014a8L' + >>> hex(bv.get_previous_basic_block_start_before(0x100014a8)) + '0x1000149fL' + >>> + """ + return core.BNGetPreviousBasicBlockStartBeforeAddress(self.handle, addr) + + def get_previous_basic_block_end_before(self, addr): + """ + ``get_previous_basic_block_end_before`` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the previous BasicBlock end + :rtype: int + :Example: + >>> hex(bv.entry_point) + '0x1000149fL' + >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) + '0x100014a8L' + >>> hex(bv.get_previous_basic_block_end_before(0x100014a8)) + '0x100014a8L' + """ + return core.BNGetPreviousBasicBlockEndBeforeAddress(self.handle, addr) + + def get_previous_data_before(self, addr): + """ + ``get_previous_data_before`` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the previous data (non-code) byte + :rtype: int + :Example: + + >>> hex(bv.get_previous_data_before(0x1000001)) + '0x1000000L' + >>> + """ + return core.BNGetPreviousDataBeforeAddress(self.handle, addr) + + def get_previous_data_var_before(self, addr): + """ + ``get_previous_data_var_before`` + + :param int addr: the virtual address to start looking from. + :return: the virtual address of the previous :py:Class:`DataVariable` + :rtype: int + :Example: + + >>> hex(bv.get_previous_data_var_before(0x1000003c)) + '0x10000000L' + >>> bv.get_data_var_at(0x10000000) + <var 0x10000000: int16_t> + >>> + """ + return core.BNGetPreviousDataVariableBeforeAddress(self.handle, addr) + + def get_linear_disassembly_position_at(self, addr, settings): + """ + ``get_linear_disassembly_position_at`` instantiates a :py:class:`LinearDisassemblyPosition` object for use in + :py:method:`get_previous_linear_disassembly_lines` or :py:method:`get_next_linear_disassembly_lines`. + + :param int addr: virtual address of linear disassembly position + :param DisassemblySettings settings: an instantiated :py:class:`DisassemblySettings` object + :return: An instantied :py:class:`LinearDisassemblyPosition` object for the provided virtual address + :rtype: LinearDisassemblyPosition + :Example: + + >>> settings = DisassemblySettings() + >>> pos = bv.get_linear_disassembly_position_at(0x1000149f, settings) + >>> lines = bv.get_previous_linear_disassembly_lines(pos, settings) + >>> lines + [<0x1000149a: pop esi>, <0x1000149b: pop ebp>, + <0x1000149c: retn 0xc>, <0x1000149f: >] + """ + if settings is not None: + settings = settings.handle + pos = core.BNGetLinearDisassemblyPositionForAddress(self.handle, addr, settings) + func = None + block = None + if pos.function: + func = function.Function(self, pos.function) + if pos.block: + block = basicblock.BasicBlock(self, pos.block) + return lineardisassembly.LinearDisassemblyPosition(func, block, pos.address) + + def _get_linear_disassembly_lines(self, api, pos, settings): + pos_obj = core.BNLinearDisassemblyPosition() + pos_obj.function = None + pos_obj.block = None + pos_obj.address = pos.address + if pos.function is not None: + pos_obj.function = core.BNNewFunctionReference(pos.function.handle) + if pos.block is not None: + pos_obj.block = core.BNNewBasicBlockReference(pos.block.handle) + + if settings is not None: + settings = settings.handle + + count = ctypes.c_ulonglong(0) + lines = api(self.handle, pos_obj, settings, count) + + result = [] + for i in xrange(0, count.value): + func = None + block = None + if lines[i].function: + func = function.Function(self, core.BNNewFunctionReference(lines[i].function)) + if lines[i].block: + block = basicblock.BasicBlock(self, core.BNNewBasicBlockReference(lines[i].block)) + addr = lines[i].contents.addr + tokens = [] + for j in xrange(0, lines[i].contents.count): + token_type = InstructionTextTokenType(lines[i].contents.tokens[j].type) + text = lines[i].contents.tokens[j].text + value = lines[i].contents.tokens[j].value + size = lines[i].contents.tokens[j].size + operand = lines[i].contents.tokens[j].operand + context = lines[i].contents.tokens[j].context + address = lines[i].contents.tokens[j].address + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + contents = function.DisassemblyTextLine(addr, tokens) + result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) + + func = None + block = None + if pos_obj.function: + func = function.Function(self, pos_obj.function) + if pos_obj.block: + block = basicblock.BasicBlock(self, pos_obj.block) + pos.function = func + pos.block = block + pos.address = pos_obj.address + + core.BNFreeLinearDisassemblyLines(lines, count.value) + return result + + def get_previous_linear_disassembly_lines(self, pos, settings): + """ + ``get_previous_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the + previous disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called + repeatedly to get more lines of linear disassembly. + + :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from + :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly + :return: a list of :py:class:`LinearDisassemblyLine` objects for the previous lines. + :Example: + + >>> settings = DisassemblySettings() + >>> pos = bv.get_linear_disassembly_position_at(0x1000149a, settings) + >>> bv.get_previous_linear_disassembly_lines(pos, settings) + [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] + >>> bv.get_previous_linear_disassembly_lines(pos, settings) + [<0x10001483: xor eax, eax {0x0}>, ... , <0x10001488: >] + """ + return self._get_linear_disassembly_lines(core.BNGetPreviousLinearDisassemblyLines, pos, settings) + + def get_next_linear_disassembly_lines(self, pos, settings): + """ + ``get_next_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the + next disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called + repeatedly to get more lines of linear disassembly. + + :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from + :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly + :return: a list of :py:class:`LinearDisassemblyLine` objects for the next lines. + :Example: + + >>> settings = DisassemblySettings() + >>> pos = bv.get_linear_disassembly_position_at(0x10001483, settings) + >>> bv.get_next_linear_disassembly_lines(pos, settings) + [<0x10001483: xor eax, eax {0x0}>, <0x10001485: inc eax {0x1}>, ... , <0x10001488: >] + >>> bv.get_next_linear_disassembly_lines(pos, settings) + [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] + >>> + """ + return self._get_linear_disassembly_lines(core.BNGetNextLinearDisassemblyLines, pos, settings) + + def get_linear_disassembly(self, settings): + """ + ``get_linear_disassembly`` gets an iterator for all lines in the linear disassembly of the view for the given + disassembly settings. + + .. note:: linear_disassembly doesn't just return disassembly it will return a single line from the linear view,\ + and thus will contain both data views, and disassembly. + + :param DisassemblySettings settings: instance specifying the desired output formatting. + :return: An iterator containing formatted dissassembly lines. + :rtype: LinearDisassemblyIterator + :Example: + + >>> settings = DisassemblySettings() + >>> lines = bv.get_linear_disassembly(settings) + >>> for line in lines: + ... print line + ... break + ... + cf fa ed fe 07 00 00 01 ........ + """ + class LinearDisassemblyIterator(object): + def __init__(self, view, settings): + self.view = view + self.settings = settings + + def __iter__(self): + pos = self.view.get_linear_disassembly_position_at(self.view.start, self.settings) + while True: + lines = self.view.get_next_linear_disassembly_lines(pos, self.settings) + if len(lines) == 0: + break + for line in lines: + yield line + + return iter(LinearDisassemblyIterator(self, settings)) + + def parse_type_string(self, text): + """ + ``parse_type_string`` converts `C-style` string into a :py:Class:`Type`. + + :param str text: `C-style` string of type to create + :return: A tuple of a :py:Class:`Type` and type name + :rtype: tuple(Type, QualifiedName) + :Example: + + >>> bv.parse_type_string("int foo") + (<type: int32_t>, 'foo') + >>> + """ + result = core.BNQualifiedNameAndType() + errors = ctypes.c_char_p() + if not core.BNParseTypeString(self.handle, text, result, errors): + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise SyntaxError(error_str) + type_obj = types.Type(core.BNNewTypeReference(result.type)) + name = types.QualifiedName._from_core_struct(result.name) + core.BNFreeQualifiedNameAndType(result) + return type_obj, name + + def get_type_by_name(self, name): + """ + ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` + + :param QualifiedName name: Type name to lookup + :return: A :py:Class:`Type` or None if the type does not exist + :rtype: Type or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetAnalysisTypeByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + def get_type_by_id(self, id): + """ + ``get_type_by_id`` returns the defined type whose unique identifier corresponds with the provided ``id`` + + :param str id: Unique identifier to lookup + :return: A :py:Class:`Type` or None if the type does not exist + :rtype: Type or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) + >>> bv.get_type_by_id(type_id) + <type: int32_t> + >>> + """ + obj = core.BNGetAnalysisTypeById(self.handle, id) + if not obj: + return None + return types.Type(obj) + + def get_type_name_by_id(self, id): + """ + ``get_type_name_by_id`` returns the defined type name whose unique identifier corresponds with the provided ``id`` + + :param str id: Unique identifier to lookup + :return: A QualifiedName or None if the type does not exist + :rtype: QualifiedName or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) + 'foo' + >>> bv.get_type_name_by_id(type_id) + 'foo' + >>> + """ + name = core.BNGetAnalysisTypeNameById(self.handle, id) + result = types.QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + if len(result) == 0: + return None + return result + + def get_type_id(self, name): + """ + ``get_type_id`` returns the unique indentifier of the defined type whose name corresponds with the + provided ``name`` + + :param QualifiedName name: Type name to lookup + :return: The unique identifier of the type + :rtype: str + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> registered_name = bv.define_type(type_id, name, type) + >>> bv.get_type_id(registered_name) == type_id + True + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + return core.BNGetAnalysisTypeId(self.handle, name) + + def is_type_auto_defined(self, name): + """ + ``is_type_auto_defined`` queries the user type list of name. If name is not in the *user* type list then the name + is considered an *auto* type. + + :param QualifiedName name: Name of type to query + :return: True if the type is not a *user* type. False if the type is a *user* type. + :Example: + >>> bv.is_type_auto_defined("foo") + True + >>> bv.define_user_type("foo", bv.parse_type_string("struct {int x,y;}")[0]) + >>> bv.is_type_auto_defined("foo") + False + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + return core.BNIsAnalysisTypeAutoDefined(self.handle, name) + + def define_type(self, type_id, default_name, type_obj): + """ + ``define_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of types for + the current :py:Class:`BinaryView`. This method should only be used for automatically generated types. + + :param str type_id: Unique identifier for the automatically generated type + :param QualifiedName default_name: Name of the type to be registered + :param Type type_obj: Type object to be registered + :return: Registered name of the type. May not be the same as the requested name if the user has renamed types. + :rtype: QualifiedName + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> registered_name = bv.define_type(Type.generate_auto_type_id("source", name), name, type) + >>> bv.get_type_by_name(registered_name) + <type: int32_t> + """ + name = types.QualifiedName(default_name)._get_core_struct() + reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle) + result = types.QualifiedName._from_core_struct(reg_name) + core.BNFreeQualifiedName(reg_name) + return result + + def define_user_type(self, name, type_obj): + """ + ``define_user_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of user + types for the current :py:Class:`BinaryView`. + + :param QualifiedName name: Name of the user type to be registered + :param Type type_obj: Type object to be registered + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + """ + name = types.QualifiedName(name)._get_core_struct() + core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) + + def undefine_type(self, type_id): + """ + ``undefine_type`` removes a :py:Class:`Type` from the global list of types for the current :py:Class:`BinaryView` + + :param str type_id: Unique identifier of type to be undefined + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> bv.undefine_type(type_id) + >>> bv.get_type_by_name(name) + >>> + """ + core.BNUndefineAnalysisType(self.handle, type_id) + + def undefine_user_type(self, name): + """ + ``undefine_user_type`` removes a :py:Class:`Type` from the global list of user types for the current + :py:Class:`BinaryView` + + :param QualifiedName name: Name of user type to be undefined + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> bv.undefine_user_type(name) + >>> bv.get_type_by_name(name) + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + core.BNUndefineUserAnalysisType(self.handle, name) + + def rename_type(self, old_name, new_name): + """ + ``rename_type`` renames a type in the global list of types for the current :py:Class:`BinaryView` + + :param QualifiedName old_name: Existing name of type to be renamed + :param QualifiedName new_name: New name of type to be renamed + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name("foo") + <type: int32_t> + >>> bv.rename_type("foo", "bar") + >>> bv.get_type_by_name("bar") + <type: int32_t> + >>> + """ + old_name = types.QualifiedName(old_name)._get_core_struct() + new_name = types.QualifiedName(new_name)._get_core_struct() + core.BNRenameAnalysisType(self.handle, old_name, new_name) + + def register_platform_types(self, platform): + """ + ``register_platform_types`` ensures that the platform-specific types for a :py:Class:`Platform` are available + for the current :py:Class:`BinaryView`. This is automatically performed when adding a new function or setting + the default platform. + + :param Platform platform: Platform containing types to be registered + :rtype: None + :Example: + + >>> platform = Platform["linux-x86"] + >>> bv.register_platform_types(platform) + >>> + """ + core.BNRegisterPlatformTypes(self.handle, platform.handle) + + def find_next_data(self, start, data, flags = 0): + """ + ``find_next_data`` searchs for the bytes in data starting at the virtual address ``start`` either, case-sensitive, + or case-insensitive. + + :param int start: virtual address to start searching from. + :param str data: bytes to search for + :param FindFlags flags: case-sensitivity flag, one of the following: + + ==================== ====================== + FindFlags Description + ==================== ====================== + NoFindFlags Case-sensitive find + FindCaseInsensitive Case-insensitive find + ==================== ====================== + """ + buf = databuffer.DataBuffer(str(data)) + result = ctypes.c_ulonglong() + if not core.BNFindNextData(self.handle, start, buf.handle, result, flags): + 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 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 add_auto_segment(self, start, length, data_offset, data_length, flags): + core.BNAddAutoSegment(self.handle, start, length, data_offset, data_length, flags) + + def remove_auto_segment(self, start, length): + core.BNRemoveAutoSegment(self.handle, start, length) + + def add_user_segment(self, start, length, data_offset, data_length, flags): + core.BNAddUserSegment(self.handle, start, length, data_offset, data_length, flags) + + def remove_user_segment(self, start, length): + core.BNRemoveUserSegment(self.handle, start, length) + + def get_segment_at(self, addr): + segment = core.BNSegment() + if not core.BNGetSegmentAt(self.handle, addr, segment): + return None + result = Segment(segment.start, segment.length, segment.dataOffset, segment.dataLength, + segment.flags) + return result + + def get_address_for_data_offset(self, offset): + address = ctypes.c_ulonglong() + if not core.BNGetAddressForDataOffset(self.handle, offset, address): + return None + return address.value + + def add_auto_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", + info_section = "", info_data = 0): + core.BNAddAutoSection(self.handle, name, start, length, type, align, entry_size, linked_section, + info_section, info_data) + + def remove_auto_section(self, name): + core.BNRemoveAutoSection(self.handle, name) + + def add_user_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", + info_section = "", info_data = 0): + core.BNAddUserSection(self.handle, name, start, length, type, align, entry_size, linked_section, + info_section, info_data) + + def remove_user_section(self, name): + core.BNRemoveUserSection(self.handle, name) + + def get_sections_at(self, addr): + count = ctypes.c_ulonglong(0) + section_list = core.BNGetSectionsAt(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start, + section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, + section_list[i].infoData, section_list[i].align, section_list[i].entrySize)) + core.BNFreeSectionList(section_list, count.value) + return result + + def get_section_by_name(self, name): + section = core.BNSection() + if not core.BNGetSectionByName(self.handle, name, section): + return None + result = Section(section.name, section.type, section.start, section.length, section.linkedSection, + section.infoSection, section.infoData, section.align, section.entrySize) + core.BNFreeSection(section) + return result + + def get_unique_section_names(self, name_list): + incoming_names = (ctypes.c_char_p * len(name_list))() + for i in xrange(0, len(name_list)): + incoming_names[i] = name_list[i] + outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) + result = [] + for i in xrange(0, len(name_list)): + result.append(str(outgoing_names[i])) + core.BNFreeStringList(outgoing_names, len(name_list)) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class BinaryReader(object): + """ + ``class BinaryReader`` is a convenience class for reading binary data. + + BinaryReader can be instantiated as follows and the rest of the document will start from this context :: + + >>> from binaryninja import * + >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") + >>> br = BinaryReader(bv) + >>> hex(br.read32()) + '0xfeedfacfL' + >>> + + Or using the optional endian parameter :: + + >>> from binaryninja import * + >>> br = BinaryReader(bv, Endianness.BigEndian) + >>> hex(br.read32()) + '0xcffaedfeL' + >>> + """ + def __init__(self, view, endian = None): + self.handle = core.BNCreateBinaryReader(view.handle) + if endian is None: + core.BNSetBinaryReaderEndianness(self.handle, view.endianness) + else: + core.BNSetBinaryReaderEndianness(self.handle, endian) + + def __del__(self): + core.BNFreeBinaryReader(self.handle) + + def __eq__(self, value): + if not isinstance(value, BinaryReader): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryReader): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def endianness(self): + """ + The Endianness to read data. (read/write) + + :getter: returns the endianness of the reader + :setter: sets the endianness of the reader (BigEndian or LittleEndian) + :type: Endianness + """ + return core.BNGetBinaryReaderEndianness(self.handle) + + @endianness.setter + def endianness(self, value): + core.BNSetBinaryReaderEndianness(self.handle, value) + + @property + def offset(self): + """ + The current read offset (read/write). + + :getter: returns the current internal offset + :setter: sets the internal offset + :type: int + """ + return core.BNGetReaderPosition(self.handle) + + @offset.setter + def offset(self, value): + core.BNSeekBinaryReader(self.handle, value) + + @property + def eof(self): + """ + Is end of file (read-only) + + :getter: returns boolean, true if end of file, false otherwise + :type: bool + """ + return core.BNIsEndOfFile(self.handle) + + def read(self, length): + """ + ``read`` returns ``length`` bytes read from the current offset, adding ``length`` to offset. + + :param int length: number of bytes to read. + :return: ``length`` bytes from current offset + :rtype: str, or None on failure + :Example: + + >>> br.read(8) + '\\xcf\\xfa\\xed\\xfe\\x07\\x00\\x00\\x01' + >>> + """ + dest = ctypes.create_string_buffer(length) + if not core.BNReadData(self.handle, dest, length): + return None + return dest.raw + + def read8(self): + """ + ``read8`` returns a one byte integer from offet incrementing the offset. + + :return: byte at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> br.read8() + 207 + >>> + """ + result = ctypes.c_ubyte() + if not core.BNRead8(self.handle, result): + return None + return result.value + + def read16(self): + """ + ``read16`` returns a two byte integer from offet incrementing the offset by two, using specified endianness. + + :return: a two byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read16()) + '0xfacf' + >>> + """ + result = ctypes.c_ushort() + if not core.BNRead16(self.handle, result): + return None + return result.value + + def read32(self): + """ + ``read32`` returns a four byte integer from offet incrementing the offset by four, using specified endianness. + + :return: a four byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read32()) + '0xfeedfacfL' + >>> + """ + result = ctypes.c_uint() + if not core.BNRead32(self.handle, result): + return None + return result.value + + def read64(self): + """ + ``read64`` returns an eight byte integer from offet incrementing the offset by eight, using specified endianness. + + :return: an eight byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read64()) + '0x1000007feedfacfL' + >>> + """ + result = ctypes.c_ulonglong() + if not core.BNRead64(self.handle, result): + return None + return result.value + + def read16le(self): + """ + ``read16le`` returns a two byte little endian integer from offet incrementing the offset by two. + + :return: a two byte integer at offset. + :rtype: int, or None on failure + :Exmaple: + + >>> br.seek(0x100000000) + >>> hex(br.read16le()) + '0xfacf' + >>> + """ + result = self.read(2) + if (result is None) or (len(result) != 2): + return None + return struct.unpack("<H", result)[0] + + def read32le(self): + """ + ``read32le`` returns a four byte little endian integer from offet incrementing the offset by four. + + :return: a four byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read32le()) + '0xfeedfacf' + >>> + """ + result = self.read(4) + if (result is None) or (len(result) != 4): + return None + return struct.unpack("<I", result)[0] + + def read64le(self): + """ + ``read64le`` returns an eight byte little endian integer from offet incrementing the offset by eight. + + :return: a eight byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read64le()) + '0x1000007feedfacf' + >>> + """ + result = self.read(8) + if (result is None) or (len(result) != 8): + return None + return struct.unpack("<Q", result)[0] + + def read16be(self): + """ + ``read16be`` returns a two byte big endian integer from offet incrementing the offset by two. + + :return: a two byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read16be()) + '0xcffa' + >>> + """ + result = self.read(2) + if (result is None) or (len(result) != 2): + return None + return struct.unpack(">H", result)[0] + + def read32be(self): + """ + ``read32be`` returns a four byte big endian integer from offet incrementing the offset by four. + + :return: a four byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read32be()) + '0xcffaedfe' + >>> + """ + result = self.read(4) + if (result is None) or (len(result) != 4): + return None + return struct.unpack(">I", result)[0] + + def read64be(self): + """ + ``read64be`` returns an eight byte big endian integer from offet incrementing the offset by eight. + + :return: a eight byte integer at offset. + :rtype: int, or None on failure + :Example: + + >>> br.seek(0x100000000) + >>> hex(br.read64be()) + '0xcffaedfe07000001L' + """ + result = self.read(8) + if (result is None) or (len(result) != 8): + return None + return struct.unpack(">Q", result)[0] + + def seek(self, offset): + """ + ``seek`` update internal offset to ``offset``. + + :param int offset: offset to set the internal offset to + :rtype: None + :Example: + + >>> hex(br.offset) + '0x100000008L' + >>> br.seek(0x100000000) + >>> hex(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryReader(self.handle, offset) + + def seek_relative(self, offset): + """ + ``seek_relative`` updates the internal offset by ``offset``. + + :param int offset: offset to add to the internal offset + :rtype: None + :Example: + + >>> hex(br.offset) + '0x100000008L' + >>> br.seek_relative(-8) + >>> hex(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryReaderRelative(self.handle, offset) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class BinaryWriter(object): + """ + ``class BinaryWriter`` is a convenience class for writing binary data. + + BinaryWriter can be instantiated as follows and the rest of the document will start from this context :: + + >>> from binaryninja import * + >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") + >>> br = BinaryReader(bv) + >>> bw = BinaryWriter(bv) + >>> + + Or using the optional endian parameter :: + + >>> from binaryninja import * + >>> br = BinaryReader(bv, Endianness.BigEndian) + >>> bw = BinaryWriter(bv, Endianness.BigEndian) + >>> + """ + def __init__(self, view, endian = None): + self.handle = core.BNCreateBinaryWriter(view.handle) + if endian is None: + core.BNSetBinaryWriterEndianness(self.handle, view.endianness) + else: + core.BNSetBinaryWriterEndianness(self.handle, endian) + + def __del__(self): + core.BNFreeBinaryWriter(self.handle) + + def __eq__(self, value): + if not isinstance(value, BinaryWriter): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryWriter): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def endianness(self): + """ + The Endianness to written data. (read/write) + + :getter: returns the endianness of the reader + :setter: sets the endianness of the reader (BigEndian or LittleEndian) + :type: Endianness + """ + return core.BNGetBinaryWriterEndianness(self.handle) + + @endianness.setter + def endianness(self, value): + core.BNSetBinaryWriterEndianness(self.handle, value) + + @property + def offset(self): + """ + The current write offset (read/write). + + :getter: returns the current internal offset + :setter: sets the internal offset + :type: int + """ + return core.BNGetWriterPosition(self.handle) + + @offset.setter + def offset(self, value): + core.BNSeekBinaryWriter(self.handle, value) + + def write(self, value): + """ + ``write`` writes ``len(value)`` bytes to the internal offset, without regard to endianness. + + :param str value: bytes to be written at current offset + :return: boolean True on success, False on failure. + :rtype: bool + :Example: + + >>> bw.write("AAAA") + True + >>> br.read(4) + 'AAAA' + >>> + """ + value = str(value) + buf = ctypes.create_string_buffer(len(value)) + ctypes.memmove(buf, value, len(value)) + return core.BNWriteData(self.handle, buf, len(value)) + + def write8(self, value): + """ + ``write8`` lowest order byte from the integer ``value`` to the current offset. + + :param str value: bytes to be written at current offset + :return: boolean + :rtype: int + :Example: + + >>> bw.write8(0x42) + True + >>> br.read(1) + 'B' + >>> + """ + return core.BNWrite8(self.handle, value) + + def write16(self, value): + """ + ``write16`` writes the lowest order two bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite16(self.handle, value) + + def write32(self, value): + """ + ``write32`` writes the lowest order four bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite32(self.handle, value) + + def write64(self, value): + """ + ``write64`` writes the lowest order eight bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite64(self.handle, value) + + def write16le(self, value): + """ + ``write16le`` writes the lowest order two bytes from the little endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack("<H", value) + return self.write(value) + + def write32le(self, value): + """ + ``write32le`` writes the lowest order four bytes from the little endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack("<I", value) + return self.write(value) + + def write64le(self, value): + """ + ``write64le`` writes the lowest order eight bytes from the little endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack("<Q", value) + return self.write(value) + + def write16be(self, value): + """ + ``write16be`` writes the lowest order two bytes from the big endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack(">H", value) + return self.write(value) + + def write32be(self, value): + """ + ``write32be`` writes the lowest order four bytes from the big endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack(">I", value) + return self.write(value) + + def write64be(self, value): + """ + ``write64be`` writes the lowest order eight bytes from the big endian integer ``value`` to the current offset. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + value = struct.pack(">Q", value) + return self.write(value) + + def seek(self, offset): + """ + ``seek`` update internal offset to ``offset``. + + :param int offset: offset to set the internal offset to + :rtype: None + :Example: + + >>> hex(bw.offset) + '0x100000008L' + >>> bw.seek(0x100000000) + >>> hex(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryWriter(self.handle, offset) + + def seek_relative(self, offset): + """ + ``seek_relative`` updates the internal offset by ``offset``. + + :param int offset: offset to add to the internal offset + :rtype: None + :Example: + + >>> hex(bw.offset) + '0x100000008L' + >>> bw.seek_relative(-8) + >>> hex(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryWriterRelative(self.handle, offset) diff --git a/python/callingconvention.py b/python/callingconvention.py new file mode 100644 index 00000000..04ba711f --- /dev/null +++ b/python/callingconvention.py @@ -0,0 +1,222 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import architecture +import log + + +class CallingConvention(object): + name = None + caller_saved_regs = [] + int_arg_regs = [] + float_arg_regs = [] + arg_regs_share_index = False + stack_reserved_for_arg_regs = False + int_return_reg = None + high_int_return_reg = None + float_return_reg = None + + _registered_calling_conventions = [] + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self._pending_reg_lists = {} + self._cb = core.BNCustomCallingConvention() + self._cb.context = 0 + self._cb.getCallerSavedRegisters = self._cb.getCallerSavedRegisters.__class__(self._get_caller_saved_regs) + self._cb.getIntegerArgumentRegisters = self._cb.getIntegerArgumentRegisters.__class__(self._get_int_arg_regs) + self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) + self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) + self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) + self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) + self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) + self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) + self.__class__._registered_calling_conventions.append(self) + else: + self.handle = handle + self.arch = architecture.Architecture(core.BNGetCallingConventionArchitecture(self.handle)) + self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) + self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) + self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) + + count = ctypes.c_ulonglong() + regs = core.BNGetCallerSavedRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["caller_saved_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetIntegerArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["int_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetFloatArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["float_arg_regs"] = result + + reg = core.BNGetIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["int_return_reg"] = None + else: + self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetHighIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["high_int_return_reg"] = None + else: + self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetFloatReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["float_return_reg"] = None + else: + self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) + + def __del__(self): + core.BNFreeCallingConvention(self.handle) + + def __eq__(self, value): + if not isinstance(value, CallingConvention): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, CallingConvention): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + def _get_caller_saved_regs(self, ctxt, count): + try: + regs = self.__class__.caller_saved_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_int_arg_regs(self, ctxt, count): + try: + regs = self.__class__.int_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_float_arg_regs(self, ctxt, count): + try: + regs = self.__class__.float_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + 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: + log.log_error(traceback.format_exc()) + + def _arg_regs_share_index(self, ctxt): + try: + return self.__class__.arg_regs_share_index + except: + log.log_error(traceback.format_exc()) + return False + + def _stack_reserved_for_arg_regs(self, ctxt): + try: + return self.__class__.stack_reserved_for_arg_regs + except: + log.log_error(traceback.format_exc()) + return False + + def _get_int_return_reg(self, ctxt): + try: + return self.arch.regs[self.__class__.int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def _get_high_int_return_reg(self, ctxt): + try: + if self.__class__.high_int_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.high_int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def _get_float_return_reg(self, ctxt): + try: + if self.__class__.float_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.float_int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def __repr__(self): + return "<calling convention: %s %s>" % (self.arch.name, self.name) + + def __str__(self): + return self.name diff --git a/python/databuffer.py b/python/databuffer.py new file mode 100644 index 00000000..6b3423da --- /dev/null +++ b/python/databuffer.py @@ -0,0 +1,145 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core + + +class DataBuffer(object): + def __init__(self, contents="", handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNDataBuffer) + elif isinstance(contents, int) or isinstance(contents, long): + self.handle = core.BNCreateDataBuffer(None, contents) + elif isinstance(contents, DataBuffer): + self.handle = core.BNDuplicateDataBuffer(contents.handle) + else: + self.handle = core.BNCreateDataBuffer(contents, len(contents)) + + def __del__(self): + core.BNFreeDataBuffer(self.handle) + + def __len__(self): + return int(core.BNGetDataBufferLength(self.handle)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + source = str(self) + for s in i: + result += source[s] + return result + elif isinstance(i, slice): + if i.step is not None: + i = i.indices(len(self)) + start = i[0] + stop = i[1] + if stop <= start: + return "" + buf = ctypes.create_string_buffer(stop - start) + ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) + return buf.raw + else: + return str(self)[i] + elif i < 0: + if i >= -len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) + raise IndexError("index out of range") + elif i < len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(i))) + else: + raise IndexError("index out of range") + + def __setitem__(self, i, value): + if isinstance(i, slice): + if i.step is not None: + raise IndexError("step not supported on assignment") + i = i.indices(len(self)) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + data = str(self) + data = data[0:start] + value + data[stop:] + core.BNSetDataBufferContents(self.handle, data, len(data)) + else: + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) + else: + raise IndexError("index out of range") + elif i < len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) + else: + raise IndexError("index out of range") + + def __str__(self): + buf = ctypes.create_string_buffer(len(self)) + ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) + return buf.raw + + def __repr__(self): + return repr(str(self)) + + def escape(self): + return core.BNDataBufferToEscapedString(self.handle) + + def unescape(self): + return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) + + def base64_encode(self): + return core.BNDataBufferToBase64(self.handle) + + def base64_decode(self): + return DataBuffer(handle = core.BNDecodeBase64(str(self))) + + def zlib_compress(self): + buf = core.BNZlibCompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + + def zlib_decompress(self): + buf = core.BNZlibDecompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + + +def escape_string(text): + return DataBuffer(text).escape() + + +def unescape_string(text): + return DataBuffer(text).unescape() diff --git a/python/demangle.py b/python/demangle.py new file mode 100644 index 00000000..ed38674a --- /dev/null +++ b/python/demangle.py @@ -0,0 +1,83 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import types + + +def get_qualified_name(names): + """ + ``get_qualified_name`` gets a qualified name for the provied name list. + + :param list(str) names: name list to qualify + :return: a qualified name + :rtype: str + :Example: + + >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + >>> get_qualified_name(name) + 'Foobar::testf' + >>> + """ + return "::".join(names) + + +def demangle_ms(arch, mangled_name): + """ + ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. + + :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. + :param str mangled_name: a mangled Microsoft Visual Studio C++ name + :return: returns a Type object for the mangled name + :rtype: Type + :Example: + + >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + (<type: public: static enum Foobar::foo __cdecl (enum Foobar::foo)>, ['Foobar', 'testf']) + >>> + """ + handle = ctypes.POINTER(core.BNType)() + outName = ctypes.POINTER(ctypes.c_char_p)() + outSize = ctypes.c_ulonglong() + names = [] + 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(ctypes.byref(outName), outSize.value) + return (types.Type(handle), names) + return (None, mangled_name) + + +def demangle_gnu3(arch, mangled_name): + handle = ctypes.POINTER(core.BNType)() + outName = ctypes.POINTER(ctypes.c_char_p)() + outSize = ctypes.c_ulonglong() + names = [] + if core.BNDemangleGNU3(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(ctypes.byref(outName), outSize.value) + if not handle: + return (None, names) + return (types.Type(handle), names) + return (None, mangled_name) diff --git a/python/enum/LICENSE b/python/enum/LICENSE new file mode 100644 index 00000000..9003b885 --- /dev/null +++ b/python/enum/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2013, Ethan Furman. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + Neither the name Ethan Furman nor the names of any + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/python/enum/README b/python/enum/README new file mode 100644 index 00000000..aa2333d8 --- /dev/null +++ b/python/enum/README @@ -0,0 +1,3 @@ +enum34 is the new Python stdlib enum module available in Python 3.4 +backported for previous versions of Python from 2.4 to 3.3. +tested on 2.6, 2.7, and 3.3+ diff --git a/python/enum/__init__.py b/python/enum/__init__.py new file mode 100644 index 00000000..d6ffb3a4 --- /dev/null +++ b/python/enum/__init__.py @@ -0,0 +1,837 @@ +"""Python Enumerations""" + +import sys as _sys + +__all__ = ['Enum', 'IntEnum', 'unique'] + +version = 1, 1, 6 + +pyver = float('%s.%s' % _sys.version_info[:2]) + +try: + any +except NameError: + def any(iterable): + for element in iterable: + if element: + return True + return False + +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None + +try: + basestring +except NameError: + # In Python 2 basestring is the ancestor of both str and unicode + # in Python 3 it's just str, but was missing in 3.1 + basestring = str + +try: + unicode +except NameError: + # In Python 3 unicode no longer exists (it's just str) + unicode = str + +class _RouteClassAttributeToGetattr(object): + """Route attribute access on a class to __getattr__. + + This is a descriptor, used to define attributes that act differently when + accessed through an instance and through a class. Instance access remains + normal, but access to an attribute through a class will be routed to the + class's __getattr__ method; this is done by raising AttributeError. + + """ + def __init__(self, fget=None): + self.fget = fget + + def __get__(self, instance, ownerclass=None): + if instance is None: + raise AttributeError() + return self.fget(instance) + + def __set__(self, instance, value): + raise AttributeError("can't set attribute") + + def __delete__(self, instance): + raise AttributeError("can't delete attribute") + + +def _is_descriptor(obj): + """Returns True if obj is a descriptor, False otherwise.""" + return ( + hasattr(obj, '__get__') or + hasattr(obj, '__set__') or + hasattr(obj, '__delete__')) + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (name[:2] == name[-2:] == '__' and + name[2:3] != '_' and + name[-3:-2] != '_' and + len(name) > 4) + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_' and + len(name) > 2) + + +def _make_class_unpicklable(cls): + """Make the given class un-picklable.""" + def _break_on_call_reduce(self, protocol=None): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce_ex__ = _break_on_call_reduce + cls.__module__ = '<unknown>' + + +class _EnumDict(dict): + """Track enum member order and ensure member names are not reused. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + + """ + def __init__(self): + super(_EnumDict, self).__init__() + self._member_names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or not a descriptor. + + If a descriptor is added with the same name as an enum member, the name + is removed from _member_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + + Note: in 3.x __order__ is simply discarded as a not necessary piece + leftover from 2.x + + """ + if pyver >= 3.0 and key in ('_order_', '__order__'): + return + elif key == '__order__': + key = '_order_' + if _is_sunder(key): + if key != '_order_': + raise ValueError('_names_ are reserved for future Enum use') + elif _is_dunder(key): + pass + elif key in self._member_names: + # descriptor overwriting an enum? + raise TypeError('Attempted to reuse key: %r' % key) + elif not _is_descriptor(value): + if key in self: + # enum overwriting a descriptor? + raise TypeError('Key already defined as: %r' % self[key]) + self._member_names.append(key) + super(_EnumDict, self).__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """Metaclass for Enum""" + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + if type(classdict) is dict: + original_dict = classdict + classdict = _EnumDict() + for k, v in original_dict.items(): + classdict[k] = v + + member_type, first_enum = metacls._get_mixins_(bases) + __new__, save_new, use_args = metacls._find_new_(classdict, member_type, + first_enum) + # save enum items into separate mapping so they don't get baked into + # the new class + members = dict((k, classdict[k]) for k in classdict._member_names) + for name in classdict._member_names: + del classdict[name] + + # py2 support for definition order + _order_ = classdict.get('_order_') + if _order_ is None: + if pyver < 3.0: + try: + _order_ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])] + except TypeError: + _order_ = [name for name in sorted(members.keys())] + else: + _order_ = classdict._member_names + else: + del classdict['_order_'] + if pyver < 3.0: + _order_ = _order_.replace(',', ' ').split() + aliases = [name for name in members if name not in _order_] + _order_ += aliases + + # check for illegal enum names (any others?) + invalid_names = set(members) & set(['mro']) + if invalid_names: + raise ValueError('Invalid enum member name(s): %s' % ( + ', '.join(invalid_names), )) + + # save attributes from super classes so we know if we can take + # the shortcut of storing members in the class dict + base_attributes = set([a for b in bases for a in b.__dict__]) + # create our new Enum type + enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict) + enum_class._member_names_ = [] # names in random order + if OrderedDict is not None: + enum_class._member_map_ = OrderedDict() + else: + enum_class._member_map_ = {} # name->value map + enum_class._member_type_ = member_type + + # Reverse value->name map for hashable values. + enum_class._value2member_map_ = {} + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + if __new__ is None: + __new__ = enum_class.__new__ + for member_name in _order_: + value = members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args or not args: + enum_member = __new__(enum_class) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = member_type(*args) + value = enum_member._value_ + enum_member._name_ = member_name + enum_member.__objclass__ = enum_class + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map_.items(): + if canonical_member.value == enum_member._value_: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names_.append(member_name) + # performance boost for any member that would not shadow + # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr) + if member_name not in base_attributes: + setattr(enum_class, member_name, enum_member) + # now add to _member_map_ + enum_class._member_map_[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_[value] = enum_member + except TypeError: + pass + + + # If a custom type is mixed into the Enum, and it does not know how + # to pickle itself, pickle.dumps will succeed but pickle.loads will + # fail. Rather than have the error show up later and possibly far + # from the source, sabotage the pickle protocol for this class so + # that pickle.dumps also fails. + # + # However, if the new class implements its own __reduce_ex__, do not + # sabotage -- it's on them to make sure it works correctly. We use + # __reduce_ex__ instead of any of the others as it is preferred by + # pickle over __reduce__, and it handles all pickle protocols. + unpicklable = False + if '__reduce_ex__' not in classdict: + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(m in member_type.__dict__ for m in methods): + _make_class_unpicklable(enum_class) + unpicklable = True + + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if name not in classdict and class_method is not enum_method: + if name == '__reduce_ex__' and unpicklable: + continue + setattr(enum_class, name, enum_method) + + # method resolution and int's are not playing nice + # Python's less than 2.6 use __cmp__ + + if pyver < 2.6: + + if issubclass(enum_class, int): + setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) + + elif pyver < 3.0: + + if issubclass(enum_class, int): + for method in ( + '__le__', + '__lt__', + '__gt__', + '__ge__', + '__eq__', + '__ne__', + '__hash__', + ): + setattr(enum_class, method, getattr(int, method)) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + setattr(enum_class, '__member_new__', enum_class.__dict__['__new__']) + setattr(enum_class, '__new__', Enum.__dict__['__new__']) + return enum_class + + def __bool__(cls): + """ + classes/types should always be True. + """ + return True + + def __call__(cls, value, names=None, module=None, type=None, start=1): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, type=type, start=start) + + def __contains__(cls, member): + return isinstance(member, cls) and member.name in cls._member_map_ + + def __delattr__(cls, attr): + # nicer error message when someone tries to delete an attribute + # (see issue19025). + if attr in cls._member_map_: + raise AttributeError( + "%s: cannot delete Enum member." % cls.__name__) + super(EnumMeta, cls).__delattr__(attr) + + def __dir__(self): + return (['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_) + + @property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a copy of the internal mapping. + + """ + return cls._member_map_.copy() + + def __getattr__(cls, name): + """Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map_[name] + except KeyError: + raise AttributeError(name) + + def __getitem__(cls, name): + return cls._member_map_[name] + + def __iter__(cls): + return (cls._member_map_[name] for name in cls._member_names_) + + def __reversed__(cls): + return (cls._member_map_[name] for name in reversed(cls._member_names_)) + + def __len__(cls): + return len(cls._member_names_) + + __nonzero__ = __bool__ + + def __repr__(cls): + return "<enum %r>" % cls.__name__ + + def __setattr__(cls, name, value): + """Block attempts to reassign Enum members. + + A simple assignment to the class namespace only changes one of the + several possible ways to get an Enum member from the Enum class, + resulting in an inconsistent Enumeration. + + """ + member_map = cls.__dict__.get('_member_map_', {}) + if name in member_map: + raise AttributeError('Cannot reassign members.') + super(EnumMeta, cls).__setattr__(name, value) + + def _create_(cls, class_name, names=None, module=None, type=None, start=1): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + """ + if pyver < 3.0: + # if class_name is unicode, attempt a conversion to ASCII + if isinstance(class_name, unicode): + try: + class_name = class_name.encode('ascii') + except UnicodeEncodeError: + raise TypeError('%r is not representable in ASCII' % class_name) + metacls = cls.__class__ + if type is None: + bases = (cls, ) + else: + bases = (type, cls) + classdict = metacls.__prepare__(class_name, bases) + _order_ = [] + + # special processing needed for names? + if isinstance(names, basestring): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): + names = [(e, i+start) for (i, e) in enumerate(names)] + + # Here, names is either an iterable of (name, value) or a mapping. + item = None # in case names is empty + for item in names: + if isinstance(item, basestring): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + _order_.append(member_name) + # only set _order_ in classdict if name/value was not from a mapping + if not isinstance(item, basestring): + classdict['_order_'] = ' '.join(_order_) + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = _sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError): + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + + return enum_class + + @staticmethod + def _get_mixins_(bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + + """ + if not bases or Enum is None: + return object, Enum + + + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + member_type = first_enum = None + for base in bases: + if (base is not Enum and + issubclass(base, Enum) and + base._member_names_): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + member_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, + # <class 'int'>, <Enum 'Enum'>, + # <class 'object'>) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if member_type is None: + member_type = base + + return member_type, first_enum + + if pyver < 3.0: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + if __new__: + return None, True, True # __new__, save_new, use_args + + N__new__ = getattr(None, '__new__') + O__new__ = getattr(object, '__new__') + if Enum is None: + E__new__ = N__new__ + else: + E__new__ = Enum.__dict__['__new__'] + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + try: + target = possible.__dict__[method] + except (AttributeError, KeyError): + target = getattr(possible, method, None) + if target not in [ + None, + N__new__, + O__new__, + E__new__, + ]: + if method == '__member_new__': + classdict['__new__'] = target + return None, False, True + if isinstance(target, staticmethod): + target = target.__get__(member_type) + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, False, use_args + else: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __member_new__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in ( + None, + None.__new__, + object.__new__, + Enum.__new__, + ): + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, save_new, use_args + + +######################################################## +# In order to support Python 2 and 3 with a single +# codebase we have to create the Enum methods separately +# and then use the `type(name, bases, dict)` method to +# create the class. +######################################################## +temp_enum_dict = {} +temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n" + +def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.red) + value = value.value + #return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + try: + if value in cls._value2member_map_: + return cls._value2member_map_[value] + except TypeError: + # not there, now do long search -- O(n) behavior + for member in cls._member_map_.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) +temp_enum_dict['__new__'] = __new__ +del __new__ + +def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name_, self._value_) +temp_enum_dict['__repr__'] = __repr__ +del __repr__ + +def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name_) +temp_enum_dict['__str__'] = __str__ +del __str__ + +if pyver >= 3.0: + def __dir__(self): + added_behavior = [ + m + for cls in self.__class__.mro() + for m in cls.__dict__ + if m[0] != '_' and m not in self._member_map_ + ] + return (['__class__', '__doc__', '__module__', ] + added_behavior) + temp_enum_dict['__dir__'] = __dir__ + del __dir__ + +def __format__(self, format_spec): + # mixed-in Enums should use the mixed-in type's __format__, otherwise + # we can get strange results with the Enum name showing up instead of + # the value + + # pure Enum branch + if self._member_type_ is object: + cls = str + val = str(self) + # mix-in branch + else: + cls = self._member_type_ + val = self.value + return cls.__format__(val, format_spec) +temp_enum_dict['__format__'] = __format__ +del __format__ + + +#################################### +# Python's less than 2.6 use __cmp__ + +if pyver < 2.6: + + def __cmp__(self, other): + if type(other) is self.__class__: + if self is other: + return 0 + return -1 + return NotImplemented + raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__cmp__'] = __cmp__ + del __cmp__ + +else: + + def __le__(self, other): + raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__le__'] = __le__ + del __le__ + + def __lt__(self, other): + raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__lt__'] = __lt__ + del __lt__ + + def __ge__(self, other): + raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__ge__'] = __ge__ + del __ge__ + + def __gt__(self, other): + raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__gt__'] = __gt__ + del __gt__ + + +def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented +temp_enum_dict['__eq__'] = __eq__ +del __eq__ + +def __ne__(self, other): + if type(other) is self.__class__: + return self is not other + return NotImplemented +temp_enum_dict['__ne__'] = __ne__ +del __ne__ + +def __hash__(self): + return hash(self._name_) +temp_enum_dict['__hash__'] = __hash__ +del __hash__ + +def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) +temp_enum_dict['__reduce_ex__'] = __reduce_ex__ +del __reduce_ex__ + +# _RouteClassAttributeToGetattr is used to provide access to the `name` +# and `value` properties of enum members while keeping some measure of +# protection from modification, while still allowing for an enumeration +# to have members named `name` and `value`. This works because enumeration +# members are not set directly on the enum class -- __getattr__ is +# used to look them up. + +@_RouteClassAttributeToGetattr +def name(self): + return self._name_ +temp_enum_dict['name'] = name +del name + +@_RouteClassAttributeToGetattr +def value(self): + return self._value_ +temp_enum_dict['value'] = value +del value + +@classmethod +def _convert(cls, name, module, filter, source=None): + """ + Create a new Enum subclass that replaces a collection of global constants + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(_sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + members = dict((name, value) for name, value in source.items() if filter(name)) + cls = cls(name, members, module=module) + cls.__reduce_ex__ = _reduce_ex_by_name + module_globals.update(cls.__members__) + module_globals[name] = cls + return cls +temp_enum_dict['_convert'] = _convert +del _convert + +Enum = EnumMeta('Enum', (object, ), temp_enum_dict) +del temp_enum_dict + +# Enum has now been created +########################### + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" + +def _reduce_ex_by_name(self, proto): + return self.name + +def unique(enumeration): + """Class decorator that ensures only unique members exist in an enumeration.""" + duplicates = [] + for name, member in enumeration.__members__.items(): + if name != member.name: + duplicates.append((name, member.name)) + if duplicates: + duplicate_names = ', '.join( + ["%s -> %s" % (alias, name) for (alias, name) in duplicates] + ) + raise ValueError('duplicate names found in %r: %s' % + (enumeration, duplicate_names) + ) + return enumeration diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py index c6e6f87d..90217d65 100644 --- a/python/examples/angr_plugin.py +++ b/python/examples/angr_plugin.py @@ -1,3 +1,24 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + # 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. # @@ -9,14 +30,20 @@ # 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 +__name__ = "__console__" # angr looks for this, it won't load from within a UI without it + +import angr +# For the lazy instead you can just import everything 'from binaryninja import *'' +from binaryninja.binaryview import BinaryView +from binaryninja.plugin import BackgroundTaskThread, PluginCommand +from binaryninja.interaction import show_plain_text_report, show_message_box +from binaryninja.highlight import HighlightColor +from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet + # Disable warning logs as they show up as errors in the UI logging.disable(logging.WARNING) @@ -24,9 +51,11 @@ logging.disable(logging.WARNING) BinaryView.set_default_session_data("angr_find", set()) BinaryView.set_default_session_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): @@ -81,37 +110,41 @@ class Solver(BackgroundTaskThread): 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) + block.set_auto_highlight(HighlightColor(HighlightStandardColor.GreenHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.GreenHighlightColor) # Add the instruction to the list associated with the current view bv.session_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) + block.set_auto_highlight(HighlightColor(HighlightStandardColor.RedHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.RedHighlightColor) # Add the instruction to the list associated with the current view bv.session_data.angr_avoid.add(addr) + def solve(bv): if len(bv.session_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) + "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxButtonSet.ErrorIcon) return # Start a solver thread for the path associated with the view s = Solver(bv.session_data.angr_find, bv.session_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) diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py index 48073894..4c4ab8fd 100644 --- a/python/examples/bin_info.py +++ b/python/examples/bin_info.py @@ -1,34 +1,73 @@ #!/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 +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. -if len(sys.argv) > 1: - target = sys.argv[1] -else: - target = "/bin/ls" +import sys +import binaryninja.log as log +from binaryninja.binaryview import BinaryViewType +import binaryninja.interaction as interaction +from binaryninja.plugin import PluginCommand + + +def get_bininfo(bv): + if bv is None: + filename = "" + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = interaction.get_open_filename_input("Filename:") + if filename is None: + log.log_warn("No file specified") + sys.exit(1) -bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis_and_wait() + bv = BinaryViewType.get_view_of_file(filename) + log.redirect_output_to_log() + log.log_to_stdout(True) -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 --------" + contents = "## %s ##\n" % bv.file.filename + contents += "- START: 0x%x\n\n" % bv.start + contents += "- ENTRY: 0x%x\n\n" % bv.entry_point + contents += "- ARCH: %s\n\n" % bv.arch.name + contents += "### First 10 Functions ###\n" -for func in bv.functions: - print func.symbol.name + contents += "| Start | Name |\n" + contents += "|------:|:-------|\n" + for i in xrange(min(10, len(bv.functions))): + contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) + contents += "### First 10 Strings ###\n" + contents += "| Start | Length | String |\n" + contents += "|------:|-------:|:-------|\n" + for i in xrange(min(10, len(bv.strings))): + start = bv.strings[i].start + length = bv.strings[i].length + string = bv.read(start, length) + contents += "| 0x%x |%d | %s |\n" % (start, length, string) + return contents -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) +def display_bininfo(bv): + interaction.show_markdown_report("Binary Info Report", get_bininfo(bv)) + + +if __name__ == "__main__": + print get_bininfo(None) +else: + PluginCommand.register("Binary Info", "Display basic info about the binary", display_bininfo) diff --git a/python/examples/breakpoint.py b/python/examples/breakpoint.py index 44df19e2..b1297e26 100644 --- a/python/examples/breakpoint.py +++ b/python/examples/breakpoint.py @@ -1,4 +1,27 @@ -from binaryninja import * +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +from binaryninja.plugin import PluginCommand +from binaryninja.log import log_error + def write_breakpoint(view, start, length): """Sample function to show registering a plugin menu item for a range of bytes. Also possible: @@ -6,11 +29,22 @@ def write_breakpoint(view, start, length): register_for_address register_for_function """ - if view.arch.name.startswith("x86"): - view.write(start, "\xcc" * length) - elif view.arch.name == "armv7": - view.write(start, "\x7a\x00\x20\xe1" * (length/4)) - else: - log_error("No support for breakpoint on %s" % view.arch.name) + bkpt_str = { + "x86": "int3", + "x86_64": "int3", + "armv7": "bkpt", + "aarch64": "brk #0", + "mips32": "break"} + + if view.arch.name not in bkpt_str: + log_error("Architecture %s not supported" % view.arch.name) + return + + bkpt, err = view.arch.assemble(bkpt_str[view.arch.name]) + if bkpt is None: + log_error(err) + return + view.write(start, bkpt * length / len(bkpt)) + PluginCommand.register_for_range("Convert to breakpoint", "Fill region with breakpoint instructions.", write_breakpoint) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index a54dc879..95e2d62d 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -1,11 +1,14 @@ -from binaryninja import * +# from binaryninja import * import os import webbrowser try: - from urllib import pathname2url # Python 2.x + from urllib import pathname2url # Python 2.x except: - from urllib.request import pathname2url # Python 3.x + from urllib.request import pathname2url # Python 3.x +from binaryninja.interaction import get_save_filename_input, show_message_box +from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult, InstructionTextTokenType, BranchType +from binaryninja.plugin import PluginCommand 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,47 +20,58 @@ 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): - address = hex(function.start).replace('L','') +def escape(toescape): + toescape = toescape.decode('utf-8').encode('ascii', 'xmlcharrefreplace') # handle extended unicode + return ''.join(escape_table.get(i, i) for i in toescape) # still escape the basics + + +def save_svg(bv, function): + address = hex(function.start).replace('L', '') 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)) + 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 = open(outputfile, 'w') output.write(content) output.close() - if show_message_box("Open SVG", "Would you like to view the exported SVG?", buttons = core.YesNoButtonSet, icon = core.QuestionIcon) == core.YesButton: + result = show_message_box("Open SVG", "Would you like to view the exported SVG?", + buttons = MessageBoxButtonSet.YesNoButtonSet, icon = MessageBoxIcon.QuestionIcon) + if result == MessageBoxButtonResult.YesButton: url = 'file:{}'.format(pathname2url(outputfile)) webbrowser.open(url) -def instruction_data_flow(function,address): + +def instruction_data_flow(function, address): ''' TODO: Extract data flow information ''' - length = function.view.get_instruction_length(function.arch,address) + length = function.view.get_instruction_length(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)]) + 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 + widthconst = heightconst * ratio output = '''<html> <head> <style type="text/css"> @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro); + body { + background-color: rgb(42, 42, 42); + } svg { background-color: rgb(42, 42, 42); + display: block; + margin: 0 auto; } .basicblock { stroke: rgb(224, 224, 224); @@ -66,6 +80,10 @@ def render_svg(function): fill: none; stroke-width: 1px; } + .back_edge { + fill: none; + stroke-width: 2px; + } .UnconditionalBranch, .IndirectBranch { stroke: rgb(128, 198, 233); color: rgb(128, 198, 233); @@ -130,59 +148,67 @@ def render_svg(function): <path d="M 0 0 L 10 5 L 0 10 z" /> </marker> </defs> - '''.format(width=graph.width*widthconst, height=graph.height*heightconst) + '''.format(width=graph.width * widthconst + 20, height=graph.height * heightconst + 20) output += ''' <g id="functiongraph0" class="functiongraph"> <title>Function Graph 0</title> ''' edges = '' - for i,block in enumerate(graph.blocks): + for i, block in enumerate(graph.blocks): - #Calculate basic block location and coordinates + # Calculate basic block location and coordinates x = ((block.x) * widthconst) y = ((block.y) * heightconst) width = ((block.width) * widthconst) height = ((block.height) * heightconst) - #Render block + # Render block output += ' <g id="basicblock{i}">\n'.format(i=i) output += ' <title>Basic Block {i}</title>\n'.format(i=i) - rgb=colors['none'] + rgb = colors['none'] try: 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] + rgb = colors[color_str] except: pass - output += ' <rect class="basicblock" x="{x}" y="{y}" fill-opacity="0.4" height="{height}" width="{width}" fill="rgb({r},{g},{b})"/>\n'.format(x=x,y=y,width=width,height=height,r=rgb[0],g=rgb[1],b=rgb[2]) + output += ' <rect class="basicblock" x="{x}" y="{y}" fill-opacity="0.4" height="{height}" width="{width}" fill="rgb({r},{g},{b})"/>\n'.format(x=x, y=y, width=width + 16, height=height + 12, 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 + # 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 += ' <text x="{x}" y="{y}">\n'.format(x=x,y=y + (i + 1) * heightconst) - for i,line in enumerate(block.lines): - output += ' <tspan id="instr-{address}" x="{x}" y="{y}">'.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) + output += ' <text x="{x}" y="{y}">\n'.format(x=x, y=y + (i + 1) * heightconst) + for i, line in enumerate(block.lines): + output += ' <tspan id="instr-{address}" x="{x}" y="{y}">'.format(x=x + 6, y=y + 6 + (i + 0.7) * heightconst, address=hex(line.address)[:-1]) hover = instruction_data_flow(function, line.address) output += '<title>{hover}</title>'.format(hover=hover) for token in line.tokens: # TODO: add hover for hex, function, and reg tokens - output+='<tspan class="{tokentype}">{text}</tspan>'.format(text=escape(token.text),tokentype=token.type) + output += '<tspan class="{tokentype}">{text}</tspan>'.format(text=escape(token.text), tokentype=InstructionTextTokenType(token.type).name) output += '</tspan>\n' output += ' </text>\n' output += ' </g>\n' - #Edges are rendered in a seperate chunk so they have priority over the - #basic blocks or else they'd render below them + # 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 += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=edge.type,points=points) + x, y = edge.points[0] + points += str(x * widthconst) + "," + str(y * heightconst + 12) + " " + for x, y in edge.points[1:-1]: + points += str(x * widthconst) + "," + str(y * heightconst) + " " + x, y = edge.points[-1] + points += str(x * widthconst) + "," + str(y * heightconst + 0) + " " + if edge.back_edge: + edges += ' <polyline class="back_edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) + else: + edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) output += ' ' + edges + '\n' output += ' </g>\n' output += '</svg></html>' return output + PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) diff --git a/python/examples/instruction_iterator.py b/python/examples/instruction_iterator.py index 43bc000e..7ff2d692 100644 --- a/python/examples/instruction_iterator.py +++ b/python/examples/instruction_iterator.py @@ -1,49 +1,53 @@ #!/usr/bin/env python +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. 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 +import binaryninja as binja 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 --------" +bv = binja.BinaryViewType.get_view_of_file(target) +binja.log_to_stdout(True) +binja.log_info("-------- %s --------" % target) +binja.log_info("START: 0x%x" % bv.start) +binja.log_info("ENTRY: 0x%x" % bv.entry_point) +binja.log_info("ARCH: %s" % bv.arch.name) +binja.log_info("\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) + binja.log_info(repr(func)) + for block in func.low_level_il: + binja.log_info("\t{0}".format(block)) - for insn in block: - print "\t\t{0}".format(insn) + for insn in block: + binja.log_info("\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) + binja.log_info(repr(func)) + for block in func: + binja.log_info("\t{0}".format(block)) - for insn in block: - print "\t\t{0}".format(insn) + for insn in block: + binja.log_info("\t\t{0}".format(insn)) diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py index 39fed1a5..439e2ab6 100644 --- a/python/examples/jump_table.py +++ b/python/examples/jump_table.py @@ -1,8 +1,30 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + # 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 * +from binaryninja.plugin import PluginCommand +from binaryninja.enums import InstructionTextTokenType import struct + def find_jump_table(bv, addr): for block in bv.get_basic_blocks_at(addr): func = block.function @@ -28,7 +50,7 @@ def find_jump_table(bv, 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 + if InstructionTextTokenType(token.type) == InstructionTextTokenType.PossibleAddressToken: # Table addresses will be a "possible address" token tbl = token.value print "Found possible table at 0x%x" % tbl i = 0 @@ -56,7 +78,8 @@ def find_jump_table(bv, addr): 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) + func.set_user_indirect_branches(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 diff --git a/python/examples/nds.py b/python/examples/nds.py index 5300018c..ff137b4b 100644 --- a/python/examples/nds.py +++ b/python/examples/nds.py @@ -1,88 +1,117 @@ -from binaryninja import *
-import struct
-import traceback
-import os
-
-def crc16(data):
- crc = 0xffff
- for ch in data:
- crc ^= ord(ch)
- for bit in xrange(0, 8):
- if (crc & 1) == 1:
- crc = (crc >> 1) ^ 0xa001
- else:
- crc >>= 1
- return crc
-
-class DSView(BinaryView):
- def __init__(self, data):
- BinaryView.__init__(self, file_metadata = data.file, parent_view = data)
- self.raw = data
-
- @classmethod
- def is_valid_for_data(self, data):
- hdr = data.read(0, 0x160)
- if len(hdr) < 0x160:
- return False
- if struct.unpack("<H", hdr[0x15e:0x160])[0] != crc16(hdr[0:0x15e]):
- return False
- if struct.unpack("<H", hdr[0x15c:0x15e])[0] != crc16(hdr[0xc0:0x15c]):
- return False
- return True
-
- def init_common(self):
- self.platform = Architecture["armv7"].standalone_platform
- self.hdr = self.raw.read(0, 0x160)
-
- def init_arm9(self):
- try:
- self.init_common()
- self.arm9_offset = struct.unpack("<L", self.hdr[0x20:0x24])[0]
- self.arm_entry_addr = struct.unpack("<L", self.hdr[0x24:0x28])[0]
- self.arm9_load_addr = struct.unpack("<L", self.hdr[0x28:0x2C])[0]
- self.arm9_size = struct.unpack("<L", self.hdr[0x2C:0x30])[0]
- self.add_auto_segment(self.arm9_load_addr, self.arm9_size, self.arm9_offset, self.arm9_size,
- SegmentReadable | SegmentExecutable)
- self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr)
- return True
- except:
- log_error(traceback.format_exc())
- return False
-
- def init_arm7(self):
- try:
- self.init_common()
- self.arm7_offset = struct.unpack("<L", self.hdr[0x30:0x34])[0]
- self.arm_entry_addr = struct.unpack("<L", self.hdr[0x34:0x38])[0]
- self.arm7_load_addr = struct.unpack("<L", self.hdr[0x38:0x3C])[0]
- self.arm7_size = struct.unpack("<L", self.hdr[0x3C:0x40])[0]
- self.add_auto_segment(self.arm7_load_addr, self.arm7_size, self.arm7_offset, self.arm7_size,
- SegmentReadable | SegmentExecutable)
- self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr)
- return True
- except:
- log_error(traceback.format_exc())
- return False
-
- def perform_is_executable(self):
- return True
-
- def perform_get_entry_point(self):
- return self.arm_entry_addr
-
-class DSARM9View(DSView):
- name = "DSARM9"
- long_name = "DS ARM9 ROM"
-
- def init(self):
- return self.init_arm9()
-
-class DSARM7View(DSView):
- name = "DSARM7"
- long_name = "DS ARM7 ROM"
-
- def init(self):
- return self.init_arm7()
-
-DSARM9View.register()
-DSARM7View.register()
+# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# from binaryninja import * +from binaryninja.binaryview import BinaryView +from binaryninja.architecture import Architecture +from binaryninja.enums import SegmentFlag +from binaryninja.log import log_error + +import struct +import traceback + + +def crc16(data): + crc = 0xffff + for ch in data: + crc ^= ord(ch) + for bit in xrange(0, 8): + if (crc & 1) == 1: + crc = (crc >> 1) ^ 0xa001 + else: + crc >>= 1 + return crc + + +class DSView(BinaryView): + def __init__(self, data): + BinaryView.__init__(self, file_metadata = data.file, parent_view = data) + self.raw = data + + @classmethod + def is_valid_for_data(self, data): + hdr = data.read(0, 0x160) + if len(hdr) < 0x160: + return False + if struct.unpack("<H", hdr[0x15e:0x160])[0] != crc16(hdr[0:0x15e]): + return False + if struct.unpack("<H", hdr[0x15c:0x15e])[0] != crc16(hdr[0xc0:0x15c]): + return False + return True + + def init_common(self): + self.platform = Architecture["armv7"].standalone_platform + self.hdr = self.raw.read(0, 0x160) + + def init_arm9(self): + try: + self.init_common() + self.arm9_offset = struct.unpack("<L", self.hdr[0x20:0x24])[0] + self.arm_entry_addr = struct.unpack("<L", self.hdr[0x24:0x28])[0] + self.arm9_load_addr = struct.unpack("<L", self.hdr[0x28:0x2C])[0] + self.arm9_size = struct.unpack("<L", self.hdr[0x2C:0x30])[0] + self.add_auto_segment(self.arm9_load_addr, self.arm9_size, self.arm9_offset, self.arm9_size, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + return True + except: + log_error(traceback.format_exc()) + return False + + def init_arm7(self): + try: + self.init_common() + self.arm7_offset = struct.unpack("<L", self.hdr[0x30:0x34])[0] + self.arm_entry_addr = struct.unpack("<L", self.hdr[0x34:0x38])[0] + self.arm7_load_addr = struct.unpack("<L", self.hdr[0x38:0x3C])[0] + self.arm7_size = struct.unpack("<L", self.hdr[0x3C:0x40])[0] + self.add_auto_segment(self.arm7_load_addr, self.arm7_size, self.arm7_offset, self.arm7_size, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_is_executable(self): + return True + + def perform_get_entry_point(self): + return self.arm_entry_addr + + +class DSARM9View(DSView): + name = "DSARM9" + long_name = "DS ARM9 ROM" + + def init(self): + return self.init_arm9() + + +class DSARM7View(DSView): + name = "DSARM7" + long_name = "DS ARM7 ROM" + + def init(self): + return self.init_arm7() + + +DSARM9View.register() +DSARM7View.register() diff --git a/python/examples/nes.py b/python/examples/nes.py index 23f5f3d8..4122cde0 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -18,44 +18,52 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from binaryninja import * import struct import traceback import os +from binaryninja.architecture import Architecture +from binaryninja.lowlevelil import LowLevelILLabel, LLIL_TEMP +from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken +from binaryninja.binaryview import BinaryView +from binaryninja.types import Symbol +from binaryninja.log import log_error +from binaryninja.enums import (BranchType, InstructionTextTokenType, + LowLevelILOperation, LowLevelILFlagCondition, FlagRole, SegmentFlag, SymbolType) + InstructionNames = [ - "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 - "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 - "bpl", "ora", None, None, None, "ora", "asl", None, # 0x10 - "clc", "ora", None, None, None, "ora", "asl", None, # 0x18 - "jsr", "and", None, None, "bit", "and", "rol", None, # 0x20 - "plp", "and", "rol@", None, "bit", "and", "rol", None, # 0x28 - "bmi", "and", None, None, None, "and", "rol", None, # 0x30 - "sec", "and", None, None, None, "and", "rol", None, # 0x38 - "rti", "eor", None, None, None, "eor", "lsr", None, # 0x40 - "pha", "eor", "lsr@", None, "jmp", "eor", "lsr", None, # 0x48 - "bvc", "eor", None, None, None, "eor", "lsr", None, # 0x50 - "cli", "eor", None, None, None, "eor", "lsr", None, # 0x58 - "rts", "adc", None, None, None, "adc", "ror", None, # 0x60 - "pla", "adc", "ror@", None, "jmp", "adc", "ror", None, # 0x68 - "bvs", "adc", None, None, None, "adc", "ror", None, # 0x70 - "sei", "adc", None, None, None, "adc", "ror", None, # 0x78 - None, "sta", None, None, "sty", "sta", "stx", None, # 0x80 - "dey", None, "txa", None, "sty", "sta", "stx", None, # 0x88 - "bcc", "sta", None, None, "sty", "sta", "stx", None, # 0x90 - "tya", "sta", "txs", None, None, "sta", None, None, # 0x98 - "ldy", "lda", "ldx", None, "ldy", "lda", "ldx", None, # 0xa0 - "tay", "lda", "tax", None, "ldy", "lda", "ldx", None, # 0xa8 - "bcs", "lda", None, None, "ldy", "lda", "ldx", None, # 0xb0 - "clv", "lda", "tsx", None, "ldy", "lda", "ldx", None, # 0xb8 - "cpy", "cmp", None, None, "cpy", "cmp", "dec", None, # 0xc0 - "iny", "cmp", "dex", None, "cpy", "cmp", "dec", None, # 0xc8 - "bne", "cmp", None, None, None, "cmp", "dec", None, # 0xd0 - "cld", "cmp", None, None, None, "cmp", "dec", None, # 0xd8 - "cpx", "sbc", None, None, "cpx", "sbc", "inc", None, # 0xe0 - "inx", "sbc", "nop", None, "cpx", "sbc", "inc", None, # 0xe8 - "beq", "sbc", None, None, None, "sbc", "inc", None, # 0xf0 - "sed", "sbc", None, None, None, "sbc", "inc", None # 0xf8 + "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 + "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 + "bpl", "ora", None, None, None, "ora", "asl", None, # 0x10 + "clc", "ora", None, None, None, "ora", "asl", None, # 0x18 + "jsr", "and", None, None, "bit", "and", "rol", None, # 0x20 + "plp", "and", "rol@", None, "bit", "and", "rol", None, # 0x28 + "bmi", "and", None, None, None, "and", "rol", None, # 0x30 + "sec", "and", None, None, None, "and", "rol", None, # 0x38 + "rti", "eor", None, None, None, "eor", "lsr", None, # 0x40 + "pha", "eor", "lsr@", None, "jmp", "eor", "lsr", None, # 0x48 + "bvc", "eor", None, None, None, "eor", "lsr", None, # 0x50 + "cli", "eor", None, None, None, "eor", "lsr", None, # 0x58 + "rts", "adc", None, None, None, "adc", "ror", None, # 0x60 + "pla", "adc", "ror@", None, "jmp", "adc", "ror", None, # 0x68 + "bvs", "adc", None, None, None, "adc", "ror", None, # 0x70 + "sei", "adc", None, None, None, "adc", "ror", None, # 0x78 + None, "sta", None, None, "sty", "sta", "stx", None, # 0x80 + "dey", None, "txa", None, "sty", "sta", "stx", None, # 0x88 + "bcc", "sta", None, None, "sty", "sta", "stx", None, # 0x90 + "tya", "sta", "txs", None, None, "sta", None, None, # 0x98 + "ldy", "lda", "ldx", None, "ldy", "lda", "ldx", None, # 0xa0 + "tay", "lda", "tax", None, "ldy", "lda", "ldx", None, # 0xa8 + "bcs", "lda", None, None, "ldy", "lda", "ldx", None, # 0xb0 + "clv", "lda", "tsx", None, "ldy", "lda", "ldx", None, # 0xb8 + "cpy", "cmp", None, None, "cpy", "cmp", "dec", None, # 0xc0 + "iny", "cmp", "dex", None, "cpy", "cmp", "dec", None, # 0xc8 + "bne", "cmp", None, None, None, "cmp", "dec", None, # 0xd0 + "cld", "cmp", None, None, None, "cmp", "dec", None, # 0xd8 + "cpx", "sbc", None, None, "cpx", "sbc", "inc", None, # 0xe0 + "inx", "sbc", "nop", None, "cpx", "sbc", "inc", None, # 0xe8 + "beq", "sbc", None, None, None, "sbc", "inc", None, # 0xf0 + "sed", "sbc", None, None, None, "sbc", "inc", None # 0xf8 ] NONE = 0 @@ -81,105 +89,106 @@ ZERO_X_DEST = 19 ZERO_Y = 20 ZERO_Y_DEST = 21 InstructionOperandTypes = [ - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x00 - NONE, IMMED, ACCUM, NONE, NONE, ABS, ABS_DEST, NONE, # 0x08 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x10 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x18 - ADDR, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0x20 - NONE, IMMED, ACCUM, NONE, ABS, ABS, ABS_DEST, NONE, # 0x28 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x30 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x38 - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x40 - NONE, IMMED, ACCUM, NONE, ADDR, ABS, ABS_DEST, NONE, # 0x48 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x50 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x58 - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x60 - NONE, IMMED, ACCUM, NONE, IND, ABS, ABS_DEST, NONE, # 0x68 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x70 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x78 - NONE, IND_X_DEST, NONE, NONE, ZERO_DEST, ZERO_DEST, ZERO_DEST, NONE, # 0x80 - NONE, NONE, NONE, NONE, ABS_DEST, ABS_DEST, ABS_DEST, NONE, # 0x88 - REL, IND_Y_DEST, NONE, NONE, ZERO_X_DEST, ZERO_X_DEST, ZERO_Y_DEST, NONE, # 0x90 - NONE, ABS_Y_DEST, NONE, NONE, NONE, ABS_X_DEST, NONE, NONE, # 0x98 - IMMED, IND_X, IMMED, NONE, ZERO, ZERO, ZERO, NONE, # 0xa0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS, NONE, # 0xa8 - REL, IND_Y, NONE, NONE, ZERO_X, ZERO_X, ZERO_Y, NONE, # 0xb0 - NONE, ABS_Y, NONE, NONE, ABS_X, ABS_X, ABS_Y, NONE, # 0xb8 - IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xc0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xc8 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xd0 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0xd8 - IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xe0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xe8 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xf0 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE # 0xf8 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x00 + NONE, IMMED, ACCUM, NONE, NONE, ABS, ABS_DEST, NONE, # 0x08 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x10 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x18 + ADDR, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0x20 + NONE, IMMED, ACCUM, NONE, ABS, ABS, ABS_DEST, NONE, # 0x28 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x30 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x38 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x40 + NONE, IMMED, ACCUM, NONE, ADDR, ABS, ABS_DEST, NONE, # 0x48 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x50 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x58 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x60 + NONE, IMMED, ACCUM, NONE, IND, ABS, ABS_DEST, NONE, # 0x68 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x70 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x78 + NONE, IND_X_DEST, NONE, NONE, ZERO_DEST, ZERO_DEST, ZERO_DEST, NONE, # 0x80 + NONE, NONE, NONE, NONE, ABS_DEST, ABS_DEST, ABS_DEST, NONE, # 0x88 + REL, IND_Y_DEST, NONE, NONE, ZERO_X_DEST, ZERO_X_DEST, ZERO_Y_DEST, NONE, # 0x90 + NONE, ABS_Y_DEST, NONE, NONE, NONE, ABS_X_DEST, NONE, NONE, # 0x98 + IMMED, IND_X, IMMED, NONE, ZERO, ZERO, ZERO, NONE, # 0xa0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS, NONE, # 0xa8 + REL, IND_Y, NONE, NONE, ZERO_X, ZERO_X, ZERO_Y, NONE, # 0xb0 + NONE, ABS_Y, NONE, NONE, ABS_X, ABS_X, ABS_Y, NONE, # 0xb8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xc0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xc8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xd0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0xd8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xe0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xe8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xf0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE # 0xf8 ] OperandLengths = [ - 0, # NONE - 2, # ABS - 2, # ABS_DEST - 2, # ABS_X - 2, # ABS_X_DEST - 2, # ABS_Y - 2, # ABS_Y_DEST - 0, # ACCUM - 2, # ADDR - 1, # IMMED - 2, # IND - 1, # IND_X - 1, # IND_X_DEST - 1, # IND_Y - 1, # IND_Y_DEST - 1, # REL - 1, # ZERO - 1, # ZREO_DEST - 1, # ZERO_X - 1, # ZERO_X_DEST - 1, # ZERO_Y - 1 # ZERO_Y_DEST + 0, # NONE + 2, # ABS + 2, # ABS_DEST + 2, # ABS_X + 2, # ABS_X_DEST + 2, # ABS_Y + 2, # ABS_Y_DEST + 0, # ACCUM + 2, # ADDR + 1, # IMMED + 2, # IND + 1, # IND_X + 1, # IND_X_DEST + 1, # IND_Y + 1, # IND_Y_DEST + 1, # REL + 1, # ZERO + 1, # ZREO_DEST + 1, # ZERO_X + 1, # ZERO_X_DEST + 1, # ZERO_Y + 1 # ZERO_Y_DEST ] OperandTokens = [ - lambda value: [], # NONE - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y_DEST - lambda value: [InstructionTextToken(RegisterToken, "a")], # ACCUM - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ADDR - lambda value: [InstructionTextToken(TextToken, "#"), InstructionTextToken(IntegerToken, "$%.2x" % value, value)], # IMMED - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, "]")], # IND - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), - InstructionTextToken(TextToken, "]")], # IND_X - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), - InstructionTextToken(TextToken, "]")], # IND_X_DEST - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # REL - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ZERO_Y - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")] # ZERO_Y_DEST + lambda value: [], # NONE + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ABS_X + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ABS_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ABS_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ABS_Y_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.RegisterToken, "a")], # ACCUM + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ADDR + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "#"), InstructionTextToken(InstructionTextTokenType.IntegerToken, "$%.2x" % value, value)], # IMMED + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x"), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND_X + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x"), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "], "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # IND_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "], "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # IND_Y_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # REL + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value)], # ZERO + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value)], # ZERO_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ZERO_X + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ZERO_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ZERO_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")] # ZERO_Y_DEST ] + def indirect_load(il, value): if (value & 0xff) == 0xff: lo_addr = il.const(2, value) @@ -189,8 +198,9 @@ def indirect_load(il, value): return il.or_expr(2, lo, hi) return il.load(2, il.const(2, value)) + def load_zero_page_16(il, value): - if il[value].operation == "LLIL_CONST": + if il[value].operation == LowLevelILOperation.LLIL_CONST: if il[value].value == 0xff: lo = il.zero_extend(2, il.load(1, il.const(2, 0xff))) hi = il.shift_left(2, il.zero_extend(2, il.load(1, il.const(2, 0)), il.const(2, 8))) @@ -204,34 +214,36 @@ def load_zero_page_16(il, value): hi = il.shift_left(2, il.zero_extend(2, il.load(1, hi_addr)), il.const(2, 8)) return il.or_expr(2, lo, hi) + OperandIL = [ - lambda il, value: None, # NONE - lambda il, value: il.load(1, il.const(2, value)), # ABS - lambda il, value: il.const(2, value), # ABS_DEST - lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x")))), # ABS_X - lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x"))), # ABS_X_DEST - lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y")))), # ABS_Y - lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y"))), # ABS_Y_DEST - lambda il, value: il.reg(1, "a"), # ACCUM - lambda il, value: il.const(2, value), # ADDR - lambda il, value: il.const(1, value), # IMMED - lambda il, value: indirect_load(il, value), # IND - lambda il, value: il.load(1, load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x")))), # IND_X - lambda il, value: load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x"))), # IND_X_DEST - lambda il, value: il.load(1, il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y"))), # IND_Y - lambda il, value: il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y")), # IND_Y_DEST - lambda il, value: il.const(2, value), # REL - lambda il, value: il.load(1, il.const(2, value)), # ZERO - lambda il, value: il.const(2, value), # ZERO_DEST - lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x")))), # ZERO_X - lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x"))), # ZERO_X_DEST - lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y")))), # ZERO_Y - lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y"))) # ZERO_Y_DEST + lambda il, value: None, # NONE + lambda il, value: il.load(1, il.const(2, value)), # ABS + lambda il, value: il.const(2, value), # ABS_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x")))), # ABS_X + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x"))), # ABS_X_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y")))), # ABS_Y + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y"))), # ABS_Y_DEST + lambda il, value: il.reg(1, "a"), # ACCUM + lambda il, value: il.const(2, value), # ADDR + lambda il, value: il.const(1, value), # IMMED + lambda il, value: indirect_load(il, value), # IND + lambda il, value: il.load(1, load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x")))), # IND_X + lambda il, value: load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x"))), # IND_X_DEST + lambda il, value: il.load(1, il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y"))), # IND_Y + lambda il, value: il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y")), # IND_Y_DEST + lambda il, value: il.const(2, value), # REL + lambda il, value: il.load(1, il.const(2, value)), # ZERO + lambda il, value: il.const(2, value), # ZERO_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x")))), # ZERO_X + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x"))), # ZERO_X_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y")))), # ZERO_Y + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y"))) # ZERO_Y_DEST ] + def cond_branch(il, cond, dest): t = None - if il[dest].operation == LLIL_CONST: + if il[dest].operation == LowLevelILOperation.LLIL_CONST: t = il.get_label_for_address(Architecture['6502'], il[dest].value) if t is None: t = LowLevelILLabel() @@ -246,9 +258,10 @@ def cond_branch(il, cond, dest): il.mark_label(f) return None + def jump(il, dest): label = None - if il[dest].operation == LLIL_CONST: + if il[dest].operation == LowLevelILOperation.LLIL_CONST: label = il.get_label_for_address(Architecture['6502'], il[dest].value) if label is None: il.append(il.jump(dest)) @@ -256,6 +269,7 @@ def jump(il, dest): il.append(il.goto(label)) return None + def get_p_value(il): c = il.flag_bit(1, "c", 0) z = il.flag_bit(1, "z", 1) @@ -267,6 +281,7 @@ def get_p_value(il): return il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, c, z), i), d), b), v), s) + def set_p_value(il, value): il.append(il.set_reg(1, LLIL_TEMP(0), value)) il.append(il.set_flag("c", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x01)))) @@ -278,22 +293,24 @@ def set_p_value(il, value): il.append(il.set_flag("s", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x80)))) return None + def rti(il): set_p_value(il, il.pop(1)) return il.ret(il.pop(2)) + InstructionIL = { "adc": lambda il, operand: il.set_reg(1, "a", il.add_carry(1, il.reg(1, "a"), operand, flags = "*")), "asl": lambda il, operand: il.store(1, operand, il.shift_left(1, il.load(1, operand), il.const(1, 1), flags = "czs")), "asl@": lambda il, operand: il.set_reg(1, "a", il.shift_left(1, operand, il.const(1, 1), flags = "czs")), "and": lambda il, operand: il.set_reg(1, "a", il.and_expr(1, il.reg(1, "a"), operand, flags = "zs")), - "bcc": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_UGE), operand), - "bcs": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_ULT), operand), - "beq": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_E), operand), + "bcc": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_UGE), operand), + "bcs": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_ULT), operand), + "beq": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_E), operand), "bit": lambda il, operand: il.and_expr(1, il.reg(1, "a"), operand, flags = "czs"), - "bmi": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NEG), operand), - "bne": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NE), operand), - "bpl": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_POS), operand), + "bmi": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_NEG), operand), + "bne": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_NE), operand), + "bpl": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_POS), operand), "brk": lambda il, operand: il.system_call(), "bvc": lambda il, operand: cond_branch(il, il.not_expr(0, il.flag("v")), operand), "bvs": lambda il, operand: cond_branch(il, il.flag("v"), operand), @@ -345,6 +362,7 @@ InstructionIL = { "tya": lambda il, operand: il.set_reg(1, "a", il.reg(1, "y"), flags = "zs") } + class M6502(Architecture): name = "6502" address_size = 2 @@ -360,18 +378,18 @@ class M6502(Architecture): flags = ["c", "z", "i", "d", "b", "v", "s"] flag_write_types = ["*", "czs", "zvs", "zs"] flag_roles = { - "c": SpecialFlagRole, # Not a normal carry flag, subtract result is inverted - "z": ZeroFlagRole, - "v": OverflowFlagRole, - "s": NegativeSignFlagRole + "c": FlagRole.SpecialFlagRole, # Not a normal carry flag, subtract result is inverted + "z": FlagRole.ZeroFlagRole, + "v": FlagRole.OverflowFlagRole, + "s": FlagRole.NegativeSignFlagRole } flags_required_for_flag_condition = { - LLFC_UGE: ["c"], - LLFC_ULT: ["c"], - LLFC_E: ["z"], - LLFC_NE: ["z"], - LLFC_NEG: ["s"], - LLFC_POS: ["s"] + LowLevelILFlagCondition.LLFC_UGE: ["c"], + LowLevelILFlagCondition.LLFC_ULT: ["c"], + LowLevelILFlagCondition.LLFC_E: ["z"], + LowLevelILFlagCondition.LLFC_NE: ["z"], + LowLevelILFlagCondition.LLFC_NEG: ["s"], + LowLevelILFlagCondition.LLFC_POS: ["s"] } flags_written_by_flag_write_type = { "*": ["c", "z", "v", "s"], @@ -413,17 +431,17 @@ class M6502(Architecture): result.length = length if instr == "jmp": if operand == ADDR: - result.add_branch(UnconditionalBranch, struct.unpack("<H", data[1:3])[0]) + result.add_branch(BranchType.UnconditionalBranch, struct.unpack("<H", data[1:3])[0]) else: - result.add_branch(UnresolvedBranch) + result.add_branch(BranchType.UnresolvedBranch) elif instr == "jsr": - result.add_branch(CallDestination, struct.unpack("<H", data[1:3])[0]) + result.add_branch(BranchType.CallDestination, struct.unpack("<H", data[1:3])[0]) elif instr in ["rti", "rts"]: - result.add_branch(FunctionReturn) + result.add_branch(BranchType.FunctionReturn) if instr in ["bcc", "bcs", "beq", "bmi", "bne", "bpl", "bvc", "bvs"]: dest = (addr + 2 + struct.unpack("b", data[1])[0]) & 0xffff - result.add_branch(TrueBranch, dest) - result.add_branch(FalseBranch, addr + 2) + result.add_branch(BranchType.TrueBranch, dest) + result.add_branch(BranchType.FalseBranch, addr + 2) return result def perform_get_instruction_text(self, data, addr): @@ -432,7 +450,7 @@ class M6502(Architecture): return None tokens = [] - tokens.append(InstructionTextToken(TextToken, "%-7s " % instr.replace("@", ""))) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "%-7s " % instr.replace("@", ""))) tokens += OperandTokens[operand](value) return tokens, length @@ -488,12 +506,14 @@ class M6502(Architecture): return None return "\xa9" + chr(value & 0xff) + "\xea" + class NESView(BinaryView): name = "NES" long_name = "NES ROM" def __init__(self, data): BinaryView.__init__(self, parent_view = data, file_metadata = data.file) + self.platform = Architecture['6502'].standalone_platform @classmethod def is_valid_for_data(self, data): @@ -521,55 +541,55 @@ class NESView(BinaryView): self.rom_length = self.rom_banks * 0x4000 # Add mapping for RAM and hardware registers, not backed by file contents - self.add_auto_segment(0, 0x8000, 0, 0, SegmentReadable | SegmentWritable | SegmentExecutable) + self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) # Add ROM mappings self.add_auto_segment(0x8000, 0x4000, self.rom_offset + (self.__class__.bank * 0x4000), 0x4000, - SegmentReadable | SegmentExecutable) + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) self.add_auto_segment(0xc000, 0x4000, self.rom_offset + self.rom_length - 0x4000, 0x4000, - SegmentReadable | SegmentExecutable) + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) nmi = struct.unpack("<H", self.read(0xfffa, 2))[0] start = struct.unpack("<H", self.read(0xfffc, 2))[0] irq = struct.unpack("<H", self.read(0xfffe, 2))[0] - self.define_auto_symbol(Symbol(FunctionSymbol, nmi, "_nmi")) - self.define_auto_symbol(Symbol(FunctionSymbol, start, "_start")) - self.define_auto_symbol(Symbol(FunctionSymbol, irq, "_irq")) - self.add_function(Architecture['6502'].standalone_platform, nmi) - self.add_function(Architecture['6502'].standalone_platform, irq) - self.add_entry_point(Architecture['6502'].standalone_platform, start) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, nmi, "_nmi")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, start, "_start")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, irq, "_irq")) + self.add_function(nmi) + self.add_function(irq) + self.add_entry_point(start) # Hardware registers - self.define_auto_symbol(Symbol(DataSymbol, 0x2000, "PPUCTRL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2001, "PPUMASK")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2002, "PPUSTATUS")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2003, "OAMADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2004, "OAMDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2005, "PPUSCROLL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2006, "PPUADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2007, "PPUDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4000, "SQ1_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4001, "SQ1_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4002, "SQ1_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4003, "SQ1_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4004, "SQ2_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4005, "SQ2_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4006, "SQ2_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4007, "SQ2_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4008, "TRI_LINEAR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400a, "TRI_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400b, "TRI_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400c, "NOISE_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400e, "NOISE_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400f, "NOISE_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4010, "DMC_FREQ")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4011, "DMC_RAW")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4012, "DMC_START")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4013, "DMC_LEN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4014, "OAMDMA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4015, "SND_CHN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2000, "PPUCTRL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2001, "PPUMASK")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2002, "PPUSTATUS")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2003, "OAMADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2004, "OAMDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2005, "PPUSCROLL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2006, "PPUADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2007, "PPUDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4000, "SQ1_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4001, "SQ1_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4002, "SQ1_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4003, "SQ1_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4004, "SQ2_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4005, "SQ2_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4006, "SQ2_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4007, "SQ2_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4008, "TRI_LINEAR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400a, "TRI_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400b, "TRI_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400c, "NOISE_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400e, "NOISE_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400f, "NOISE_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4010, "DMC_FREQ")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4011, "DMC_RAW")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4012, "DMC_START")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4013, "DMC_LEN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4014, "OAMDMA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4015, "SND_CHN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4016, "JOY1")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4017, "JOY2")) sym_files = [self.file.filename + ".%x.nl" % self.__class__.bank, self.file.filename + ".ram.nl", @@ -584,9 +604,9 @@ class NESView(BinaryView): break addr = int(sym[0][1:], 16) name = sym[1] - self.define_auto_symbol(Symbol(FunctionSymbol, addr, name)) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) if addr >= 0x8000: - self.add_function(Architecture['6502'].standalone_platform, addr) + self.add_function(addr) return True except: @@ -599,6 +619,7 @@ class NESView(BinaryView): def perform_get_entry_point(self): return struct.unpack("<H", str(self.perform_read(0xfffc, 2)))[0] + banks = [] for i in xrange(0, 32): class NESViewBank(NESView): diff --git a/python/examples/nsf.py b/python/examples/nsf.py index 9d4ebd5c..b1bac3a8 100644 --- a/python/examples/nsf.py +++ b/python/examples/nsf.py @@ -23,17 +23,23 @@ # https://scarybeastsecurity.blogspot.com/2016/11/0day-exploit-compromising-linux-desktop.html # -from binaryninja import * +from binaryninja.binaryview import BinaryView +from binaryninja.architecture import Architecture +from binaryninja.log import log_error, log_info +from binaryninja.types import Symbol +from binaryninja.enums import SymbolType, SegmentFlag + import struct import traceback -import os + class NSFView(BinaryView): name = "NSF" long_name = "Nintendo Sound Format" def __init__(self, data): - BinaryView.__init__(self, parent_view = data, file_metadata = data.file) + BinaryView.__init__(self, parent_view=data, file_metadata=data.file) + self.platform = Architecture["6502"].standalone_platform @classmethod def is_valid_for_data(self, data): @@ -71,58 +77,58 @@ class NSFView(BinaryView): self.ntsc = True self.extra_sound_bits = struct.unpack("B", hdr[123])[0] - if self.bank_switching == "\0"*8: - #no bank switching + if self.bank_switching == "\0" * 8: + # no bank switching self.load_address & 0xFFF self.rom_offset = 128 else: - #bank switching not implemented + # bank switching not implemented log_info("Bank switching not implemented in this loader.") # Add mapping for RAM and hardware registers, not backed by file contents - self.add_auto_segment(0, 0x8000, 0, 0, SegmentReadable | SegmentWritable | SegmentExecutable) + self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) # Add ROM mappings self.add_auto_segment(0x8000, 0x4000, self.rom_offset, 0x4000, - SegmentReadable | SegmentExecutable) + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) - self.define_auto_symbol(Symbol(FunctionSymbol, self.play_address, "_play")) - self.define_auto_symbol(Symbol(FunctionSymbol, self.init_address, "_init")) - self.add_entry_point(Architecture['6502'].standalone_platform, self.init_address) - self.add_function(Architecture['6502'].standalone_platform, self.play_address) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, self.play_address, "_play")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, self.init_address, "_init")) + self.add_entry_point(self.init_address) + self.add_function(self.play_address) # Hardware registers - self.define_auto_symbol(Symbol(DataSymbol, 0x2000, "PPUCTRL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2001, "PPUMASK")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2002, "PPUSTATUS")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2003, "OAMADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2004, "OAMDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2005, "PPUSCROLL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2006, "PPUADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2007, "PPUDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4000, "SQ1_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4001, "SQ1_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4002, "SQ1_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4003, "SQ1_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4004, "SQ2_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4005, "SQ2_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4006, "SQ2_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4007, "SQ2_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4008, "TRI_LINEAR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400a, "TRI_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400b, "TRI_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400c, "NOISE_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400e, "NOISE_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400f, "NOISE_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4010, "DMC_FREQ")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4011, "DMC_RAW")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4012, "DMC_START")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4013, "DMC_LEN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4014, "OAMDMA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4015, "SND_CHN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2000, "PPUCTRL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2001, "PPUMASK")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2002, "PPUSTATUS")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2003, "OAMADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2004, "OAMDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2005, "PPUSCROLL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2006, "PPUADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2007, "PPUDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4000, "SQ1_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4001, "SQ1_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4002, "SQ1_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4003, "SQ1_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4004, "SQ2_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4005, "SQ2_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4006, "SQ2_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4007, "SQ2_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4008, "TRI_LINEAR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400a, "TRI_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400b, "TRI_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400c, "NOISE_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400e, "NOISE_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400f, "NOISE_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4010, "DMC_FREQ")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4011, "DMC_RAW")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4012, "DMC_START")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4013, "DMC_LEN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4014, "OAMDMA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4015, "SND_CHN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4016, "JOY1")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4017, "JOY2")) return True except: @@ -135,4 +141,5 @@ class NSFView(BinaryView): def perform_get_entry_point(self): return struct.unpack("<H", str(self.perform_read(0x0a, 2)))[0] + NSFView.register() diff --git a/python/examples/print_syscalls.py b/python/examples/print_syscalls.py index 7e93356c..2af4d38d 100644 --- a/python/examples/print_syscalls.py +++ b/python/examples/print_syscalls.py @@ -1,50 +1,55 @@ #!/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 - +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. -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)) +# 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 -def main(): - if len(sys.argv) != 2: - print('Usage: {} <file>'.format(sys.argv[0])) - return -1 +from binaryninja.binaryview import BinaryViewType +from binaryninja.enums import LowLevelILOperation - 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 +def print_syscalls(fileName): + """ Print Syscall numbers for a provided file """ + bv = BinaryViewType.get_view_of_file(fileName) + calling_convention = bv.platform.system_call_convention + if calling_convention is None: + print('Error: No syscall convention available for {:s}'.format(bv.platform)) + return - bv = bv.file.get_view_of_type(view_type.name) - bv.update_analysis_and_wait() + register = calling_convention.int_arg_regs[0] - print_syscalls(bv) + for func in bv.functions: + syscalls = (il for il in chain.from_iterable(func.low_level_il) + if il.operation == LowLevelILOperation.LLIL_SYSCALL) + for il in syscalls: + value = func.get_reg_value_at(il.address, register).value + print("System call address: {:#x} - {:d}".format(il.address, value)) if __name__ == "__main__": - sys.exit(main()) + if len(sys.argv) != 2: + print('Usage: {} <file>'.format(sys.argv[0])) + else: + print_syscalls(sys.argv[1]) diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py index 6199c578..9d5bbf05 100644 --- a/python/examples/version_switcher.py +++ b/python/examples/version_switcher.py @@ -1,26 +1,50 @@ #!/usr/bin/env python +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + import sys -import binaryninja + +from binaryninja.update import UpdateChannel, are_auto_updates_enabled, set_auto_updates_enabled, is_update_installation_pending, install_pending_update +from binaryninja import core_version import datetime -chandefault = binaryninja.UpdateChannel.list[0].name +chandefault = UpdateChannel.list[0].name channel = None versions = [] + def load_channel(newchannel): global channel global versions - if (channel != None and newchannel == channel.name): + if (channel is not None and newchannel == channel.name): print "Same channel, not updating." else: try: print "Loading channel %s" % newchannel - channel = binaryninja.UpdateChannel[newchannel] + channel = 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] + channel = UpdateChannel[chandefault] + def select(version): done = False @@ -44,43 +68,50 @@ def select(version): print "Requesting update to latest version." else: print "Requesting update to prior version." - if binaryninja.are_auto_updates_enabled(): + if are_auto_updates_enabled(): print "Disabling automatic updates." - binaryninja.set_auto_updates_enabled(False) - if (version.version == binaryninja.core_version): + set_auto_updates_enabled(False) + if (version.version == 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 "core_version %s" % core_version + print "Downloading..." print version.update() - #forward updating won't work without reloading + print "Installing..." + if is_update_installation_pending: + #note that the GUI will be launched after update but should still do the upgrade headless + install_pending_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 + channel_list = 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") + 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): + 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()) + set_auto_updates_enabled(not are_auto_updates_enabled()) + def main(): global channel @@ -89,8 +120,8 @@ def main(): 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() + print "\t\tCurrent Version:\t%s" % core_version + print "\t\tAuto-Updates On:\t%s\n" % 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) diff --git a/python/fileaccessor.py b/python/fileaccessor.py new file mode 100644 index 00000000..8fec43a1 --- /dev/null +++ b/python/fileaccessor.py @@ -0,0 +1,88 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import log + + +class FileAccessor(object): + def __init__(self): + self._cb = core.BNFileAccessor() + self._cb.context = 0 + self._cb.getLength = self._cb.getLength.__class__(self._get_length) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + + def __len__(self): + return self.get_length() + + def _get_length(self, ctxt): + try: + return self.get_length() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _read(self, ctxt, dest, offset, length): + try: + data = self.read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, data, len(data)) + return len(data) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _write(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.write(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + +class CoreFileAccessor(FileAccessor): + def __init__(self, accessor): + self._cb.context = accessor.context + self._cb.getLength = accessor.getLength + self._cb.read = accessor.read + self._cb.write = accessor.write + + def get_length(self): + return self._cb.getLength(self._cb.context) + + def read(self, offset, length): + data = ctypes.create_string_buffer(length) + length = self._cb.read(self._cb.context, data, offset, length) + return data.raw[0:length] + + def write(self, offset, value): + value = str(value) + data = ctypes.create_string_buffer(value) + return self._cb.write(self._cb.context, offset, data, len(value)) diff --git a/python/filemetadata.py b/python/filemetadata.py new file mode 100644 index 00000000..b489d5bb --- /dev/null +++ b/python/filemetadata.py @@ -0,0 +1,350 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import startup +import associateddatastore +import log +import binaryview + + +class NavigationHandler(object): + def _register(self, handle): + self._cb = core.BNNavigationHandler() + self._cb.context = 0 + self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) + self._cb.getCurrentOffset = self._cb.getCurrentOffset.__class__(self._get_current_offset) + self._cb.navigate = self._cb.navigate.__class__(self._navigate) + core.BNSetFileMetadataNavigationHandler(handle, self._cb) + + def _get_current_view(self, ctxt): + try: + view = self.get_current_view() + except: + log.log_error(traceback.format_exc()) + view = "" + return core.BNAllocString(view) + + def _get_current_offset(self, ctxt): + try: + return self.get_current_offset() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _navigate(self, ctxt, view, offset): + try: + return self.navigate(view, offset) + except: + log.log_error(traceback.format_exc()) + return False + + +class _FileMetadataAssociatedDataStore(associateddatastore._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. + """ + def __init__(self, filename = None, handle = None): + """ + Instantiates a new FileMetadata class. + + :param filename: The string path to the file to be opened. Defaults to None. + :param handle: A handle to the underlying C FileMetadata object. Defaults to None. + """ + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNFileMetadata) + else: + startup._init_plugins() + self.handle = core.BNCreateFileMetadata() + if filename is not None: + core.BNSetFilename(self.handle, str(filename)) + self.nav = None + + def __del__(self): + if self.navigation is not None: + core.BNSetFileMetadataNavigationHandler(self.handle, None) + core.BNFreeFileMetadata(self.handle) + + def __eq__(self, value): + if not isinstance(value, FileMetadata): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FileMetadata): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @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_session_data(cls, name, value): + _FileMetadataAssociatedDataStore.set_default(name, value) + + @property + def filename(self): + """The name of the file (read/write)""" + return core.BNGetFilename(self.handle) + + @filename.setter + def filename(self, value): + core.BNSetFilename(self.handle, str(value)) + + @property + def modified(self): + """Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)""" + return core.BNIsFileModified(self.handle) + + @modified.setter + def modified(self, value): + if value: + core.BNMarkFileModified(self.handle) + else: + core.BNMarkFileSaved(self.handle) + + @property + def analysis_changed(self): + """Boolean result of whether the auto-analysis results have changed (read-only)""" + return core.BNIsAnalysisChanged(self.handle) + + @property + def has_database(self): + """Whether the FileMetadata is backed by a database (read-only)""" + return core.BNIsBackedByDatabase(self.handle) + + @property + def view(self): + return core.BNGetCurrentView(self.handle) + + @view.setter + def view(self, value): + core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) + + @property + def offset(self): + """The current offset into the file (read/write)""" + return core.BNGetCurrentOffset(self.handle) + + @offset.setter + def offset(self, value): + core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) + + @property + def raw(self): + """Gets the "Raw" BinaryView of the file""" + view = core.BNGetFileViewOfType(self.handle, "Raw") + if view is None: + return None + return binaryview.BinaryView(file_metadata = self, handle = view) + + @property + def saved(self): + """Boolean result of whether the file has been saved (Inverse of 'modified' property) (read/write)""" + return not core.BNIsFileModified(self.handle) + + @saved.setter + def saved(self, value): + if value: + core.BNMarkFileSaved(self.handle) + 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 + + @property + def session_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 + `finally` clause to avoid handle leaks. + """ + core.BNCloseFile(self.handle) + + def begin_undo_actions(self): + """ + ``begin_undo_actions`` start recording actions taken so the can be undone at some point. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + core.BNBeginUndoActions(self.handle) + + def commit_undo_actions(self): + """ + ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + core.BNCommitUndoActions(self.handle) + + def undo(self): + """ + ``undo`` undo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + core.BNUndo(self.handle) + + def redo(self): + """ + ``redo`` redo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + core.BNRedo(self.handle) + + def navigate(self, view, offset): + return core.BNNavigate(self.handle, str(view), offset) + + 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, 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.BinaryView(file_metadata = self, handle = view) + + 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)) + if view is None: + view_type = core.BNGetBinaryViewTypeByName(str(name)) + if view_type is None: + return None + view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) + if view is None: + return None + return binaryview.BinaryView(file_metadata = self, handle = view) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) diff --git a/python/function.py b/python/function.py new file mode 100644 index 00000000..86c93bf7 --- /dev/null +++ b/python/function.py @@ -0,0 +1,1303 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import threading +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import (FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, + HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, + DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext) +import architecture +import highlight +import associateddatastore +import types +import basicblock +import lowlevelil +import binaryview +import log + + +class LookupTableEntry(object): + def __init__(self, from_values, to_value): + self.from_values = from_values + self.to_value = to_value + + def __repr__(self): + return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) + + +class RegisterValue(object): + def __init__(self, arch, value): + self.type = RegisterValueType(value.state) + if value.state == RegisterValueType.EntryValue: + self.reg = arch.get_reg_name(value.reg) + elif value.state == RegisterValueType.OffsetFromEntryValue: + self.reg = arch.get_reg_name(value.reg) + self.offset = value.value + elif value.state == RegisterValueType.ConstantValue: + self.value = value.value + elif value.state == RegisterValueType.StackFrameOffset: + self.offset = value.value + elif value.state == RegisterValueType.SignedRangeValue: + self.offset = value.value + self.start = value.rangeStart + self.end = value.rangeEnd + self.step = value.rangeStep + if self.start & (1 << 63): + self.start |= ~((1 << 63) - 1) + if self.end & (1 << 63): + self.end |= ~((1 << 63) - 1) + elif value.state == RegisterValueType.UnsignedRangeValue: + self.offset = value.value + self.start = value.rangeStart + self.end = value.rangeEnd + self.step = value.rangeStep + elif value.state == RegisterValueType.LookupTableValue: + self.table = [] + self.mapping = {} + for i in xrange(0, value.rangeEnd): + from_list = [] + for j in xrange(0, value.table[i].fromCount): + 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 == RegisterValueType.OffsetFromUndeterminedValue: + self.offset = value.value + + def __repr__(self): + if self.type == RegisterValueType.EntryValue: + return "<entry %s>" % self.reg + if self.type == RegisterValueType.OffsetFromEntryValue: + return "<entry %s + %#x>" % (self.reg, self.offset) + if self.type == RegisterValueType.ConstantValue: + return "<const %#x>" % self.value + if self.type == RegisterValueType.StackFrameOffset: + return "<stack frame offset %#x>" % self.offset + if (self.type == RegisterValueType.SignedRangeValue) or (self.type == RegisterValueType.UnsignedRangeValue): + if self.step == 1: + return "<range: %#x to %#x>" % (self.start, self.end) + return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step) + if self.type == RegisterValueType.LookupTableValue: + return "<table: %s>" % ', '.join([repr(i) for i in self.table]) + if self.type == RegisterValueType.OffsetFromUndeterminedValue: + return "<undetermined with offset %#x>" % self.offset + return "<undetermined>" + + +class StackVariable(object): + def __init__(self, ofs, name, t): + self.offset = ofs + self.name = name + self.type = t + + def __repr__(self): + return "<var@%x: %s %s>" % (self.offset, self.type, self.name) + + def __str__(self): + return self.name + + +class StackVariableReference(object): + def __init__(self, src_operand, t, name, start_ofs, ref_ofs): + self.source_operand = src_operand + self.type = t + self.name = name + self.starting_offset = start_ofs + self.referenced_offset = ref_ofs + if self.source_operand == 0xffffffff: + self.source_operand = None + + def __repr__(self): + if self.source_operand is None: + if self.referenced_offset != self.starting_offset: + return "<ref to %s%+#x>" % (self.name, self.referenced_offset - self.starting_offset) + return "<ref to %s>" % self.name + if self.referenced_offset != self.starting_offset: + return "<operand %d ref to %s%+#x>" % (self.source_operand, self.name, self.referenced_offset) + return "<operand %d ref to %s>" % (self.source_operand, self.name) + + +class ConstantReference(object): + def __init__(self, val, size): + self.value = val + self.size = size + + def __repr__(self): + if self.size == 0: + return "<constant %#x>" % self.value + return "<constant %#x size %d>" % (self.value, self.size) + + +class IndirectBranchInfo(object): + def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): + self.source_arch = source_arch + self.source_addr = source_addr + self.dest_arch = dest_arch + self.dest_addr = dest_addr + self.auto_defined = auto_defined + + def __repr__(self): + return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) + + +class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} + + +class Function(object): + _associated_data = {} + + 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) + + def __eq__(self, value): + if not isinstance(value, Function): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Function): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @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_session_data(cls, name, value): + _FunctionAssociatedDataStore.set_default(name, value) + + @property + def name(self): + """Symbol name for the function""" + return self.symbol.name + + @name.setter + def name(self, value): + if value is None: + if self.symbol is not None: + self.view.undefine_user_symbol(self.symbol) + else: + symbol = types.Symbol(SymbolType.FunctionSymbol, self.start, value) + self.view.define_user_symbol(symbol) + + @property + def view(self): + """Function view (read-only)""" + return self._view + + @property + def arch(self): + """Function architecture (read-only)""" + arch = core.BNGetFunctionArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def platform(self): + """Function platform (read-only)""" + platform = core.BNGetFunctionPlatform(self.handle) + if platform is None: + return None + return platform.Platform(None, handle = platform) + + @property + def start(self): + """Function start (read-only)""" + return core.BNGetFunctionStart(self.handle) + + @property + def symbol(self): + """Function symbol(read-only)""" + sym = core.BNGetFunctionSymbol(self.handle) + if sym is None: + return None + return types.Symbol(None, None, None, handle = sym) + + @property + def auto(self): + """Whether function was automatically discovered (read-only)""" + return core.BNWasFunctionAutomaticallyDiscovered(self.handle) + + @property + def can_return(self): + """Whether function can return (read-only)""" + return core.BNCanFunctionReturn(self.handle) + + @property + def explicitly_defined_type(self): + """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)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def comments(self): + """Dict of comments (read-only)""" + count = ctypes.c_ulonglong() + addrs = core.BNGetCommentedAddresses(self.handle, count) + result = {} + for i in xrange(0, count.value): + result[addrs[i]] = self.get_comment_at(addrs[i]) + core.BNFreeAddressList(addrs) + return result + + @property + def low_level_il(self): + """Function low level IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) + + @property + def lifted_il(self): + """Function lifted IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) + + @property + def function_type(self): + """Function type object""" + return types.Type(core.BNGetFunctionType(self.handle)) + + @function_type.setter + def function_type(self, value): + self.set_user_type(value) + + @property + def stack_layout(self): + """List of function stack (read-only)""" + count = ctypes.c_ulonglong() + v = core.BNGetStackLayout(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariable(v[i].offset, v[i].name, types.Type(handle = core.BNNewTypeReference(v[i].type)))) + result.sort(key = lambda x: x.offset) + core.BNFreeStackLayout(v, count.value) + return result + + @property + def indirect_branches(self): + """List of indirect branches (read-only)""" + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranches(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + + @property + def session_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) + try: + for i in xrange(0, count.value): + yield basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + arch = self.arch + if arch: + return "<func: %s@%#x>" % (arch.name, self.start) + else: + return "<func: %#x>" % self.start + + def mark_recent_use(self): + core.BNMarkFunctionAsRecentlyUsed(self.handle) + + def get_comment_at(self, addr): + return core.BNGetCommentForAddress(self.handle, addr) + + def set_comment(self, addr, comment): + core.BNSetCommentForAddress(self.handle, addr, comment) + + def get_low_level_il_at(self, addr, arch=None): + """ + ``get_low_level_il_at`` gets the LowLevelIL instruction address corresponding to the given virtual address + + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: int + :Example: + + >>> func = bv.functions[0] + >>> func.get_low_level_il_at(func.start) + 0L + """ + if arch is None: + arch = self.arch + return core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) + + def get_low_level_il_exits_at(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(exits[i]) + core.BNFreeLowLevelILInstructionList(exits) + return result + + def get_reg_value_at(self, addr, reg, arch=None): + """ + ``get_reg_value_at`` gets the value the provided string register address corresponding to the given virtual address + + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_at(0x400dbe, 'rdi') + <const 0x2> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_after(self, addr, reg, arch=None): + """ + ``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address + + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_after(0x400dbe, 'rdi') + <undetermined> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_at_low_level_il_instruction(self, i, reg, arch=None): + """ + ``get_reg_value_at_low_level_il_instruction`` returns the value of the specified register ``reg`` at the il address + i + + :param int i: il address of instruction to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_at_low_level_il_instruction(15, 'rdi') + <const 0x2> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAtLowLevelILInstruction(self.handle, i, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_after_low_level_il_instruction(self, i, reg): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAfterLowLevelILInstruction(self.handle, i, reg) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at(self, addr, offset, size, arch=None): + """ + ``get_stack_contents_at`` returns the RegisterValue for the item on the stack in the current function at the + given virtual address ``addr``, stack offset ``offset`` and size of ``size``. Optionally specifying the architecture. + + :param int addr: virtual address of the instruction to query + :param int offset: stack offset base of stack + :param int size: size of memory to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + + .. note:: Stack base is zero on entry into the function unless the architecture places the return address on the + stack as in (x86/x86_64) where the stack base will start at address_size + + :Example: + + >>> func.get_stack_contents_at(0x400fad, -16, 4) + <range: 0x8 to 0xffffffff> + """ + if arch is None: + arch = self.arch + value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after(self, addr, offset, size, arch=None): + if arch is None: + arch = self.arch + value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAtLowLevelILInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAfterInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at(self, addr, func_type, i, arch=None): + if arch is None: + arch = self.arch + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_regs_read_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_regs_written_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_stack_vars_referenced_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariableReference(refs[i].sourceOperand, types.Type(core.BNNewTypeReference(refs[i].type)), + refs[i].name, refs[i].startingOffset, refs[i].referencedOffset)) + core.BNFreeStackVariableReferenceList(refs, count.value) + return result + + def get_constants_referenced_by(self, addr, arch=None): + if arch is None: + arch = self.arch + 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, addr, arch=None): + if arch is None: + arch = self.arch + return core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + + def get_lifted_il_flag_uses_for_definition(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_lifted_il_flag_definitions_for_use(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_flags_read_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + + def get_flags_written_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + + def create_graph(self): + return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) + + def apply_imported_types(self, sym): + core.BNApplyImportedTypes(self.handle, sym.handle) + + def apply_auto_discovered_type(self, func_type): + core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) + + def set_auto_indirect_branches(self, source, branches, source_arch=None): + if source_arch is None: + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def set_user_indirect_branches(self, source, branches, source_arch=None): + if source_arch is None: + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def get_indirect_branches_at(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + + def get_block_annotations(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong(0) + lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(tokens) + core.BNFreeInstructionTextLines(lines, count.value) + return result + + def set_auto_type(self, value): + core.BNSetFunctionAutoType(self.handle, value.handle) + + def set_user_type(self, value): + core.BNSetFunctionUserType(self.handle, value.handle) + + def get_int_display_type(self, instr_addr, value, operand, arch=None): + if arch is None: + arch = self.arch + return IntegerDisplayType(core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand)) + + def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None): + """ + + :param int instr_addr: + :param int value: + :param int operand: + :param IntegerDisplayTypeEnum display_type: + :param Architecture arch: (optional) + """ + if arch is None: + arch = self.arch + if isinstance(display_type, str): + display_type = IntegerDisplayType[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) + + 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 + + def get_basic_block_at(self, addr, arch=None): + """ + ``get_basic_block_at`` returns the BasicBlock of the optionally specified Architecture ``arch`` at the given + address ``addr``. + + :param int addr: Address of the BasicBlock to retrieve. + :param Architecture arch: (optional) Architecture of the basic block if different from the Function's self.arch + :Example: + >>> current_function.get_basic_block_at(current_function.start) + <block: x86_64@0x100000f30-0x100000f50> + """ + if arch is None: + arch = self.arch + block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) + if not block: + return None + return basicblock.BasicBlock(self._view, handle = block) + + def get_instr_highlight(self, addr, arch=None): + """ + :Example: + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_function.get_instr_highlight(here) + <color: #ff00ff> + """ + if arch is None: + arch = self.arch + color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) + if color.style == HighlightColorStyle.StandardHighlightColor: + return highlight.HighlightColor(color = color.color, alpha = color.alpha) + elif color.style == HighlightColorStyle.MixedHighlightColor: + return highlight.HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) + elif color.style == HighlightColorStyle.CustomHighlightColor: + return highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) + return highlight.HighlightColor(color = HighlightStandardColor.NoHighlightColor) + + def set_auto_instr_highlight(self, addr, color, arch=None): + """ + ``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color + + .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + + :param int addr: virtual address of the instruction to be highlighted + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch + """ + if arch is None: + arch = self.arch + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color = color) + core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + + def set_user_instr_highlight(self, addr, color, arch=None): + """ + ``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color + + :param int addr: virtual address of the instruction to be highlighted + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch + :Example: + + >>> current_function.set_user_instr_highlight(here, HighlightStandardColor.BlueHighlightColor) + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + """ + if arch is None: + arch = self.arch + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + + +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 DisassemblyTextLine(object): + def __init__(self, addr, tokens): + self.address = addr + self.tokens = tokens + + def __str__(self): + result = "" + for token in self.tokens: + result += token.text + return result + + def __repr__(self): + return "<%#x: %s>" % (self.address, str(self)) + + +class FunctionGraphEdge(object): + def __init__(self, branch_type, source, target, points): + self.type = BranchType(branch_type) + self.source = source + self.target = target + self.points = points + + def __repr__(self): + return "<%s: %s>" % (self.type.name, repr(self.target)) + + @property + def back_edge(self): + """Whether the edge is a back edge (end of a loop)""" + return self.target in self.source.basic_block.dominators + + +class FunctionGraphBlock(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeFunctionGraphBlock(self.handle) + + def __eq__(self, value): + if not isinstance(value, FunctionGraphBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraphBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def basic_block(self): + """Basic block associated with this part of the function graph (read-only)""" + block = core.BNGetFunctionGraphBasicBlock(self.handle) + func = core.BNGetBasicBlockFunction(block) + if func is None: + core.BNFreeBasicBlock(block) + block = None + else: + block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), block) + core.BNFreeFunction(func) + return block + + @property + def arch(self): + """Function graph block architecture (read-only)""" + arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def start(self): + """Function graph block start (read-only)""" + return core.BNGetFunctionGraphBlockStart(self.handle) + + @property + def end(self): + """Function graph block end (read-only)""" + return core.BNGetFunctionGraphBlockEnd(self.handle) + + @property + def x(self): + """Function graph block X (read-only)""" + return core.BNGetFunctionGraphBlockX(self.handle) + + @property + def y(self): + """Function graph block Y (read-only)""" + return core.BNGetFunctionGraphBlockY(self.handle) + + @property + def width(self): + """Function graph block width (read-only)""" + return core.BNGetFunctionGraphBlockWidth(self.handle) + + @property + def height(self): + """Function graph block height (read-only)""" + return core.BNGetFunctionGraphBlockHeight(self.handle) + + @property + def lines(self): + """Function graph block list of lines (read-only)""" + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionGraphBlockLines(self.handle, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(DisassemblyTextLine(addr, tokens)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def outgoing_edges(self): + """Function graph block list of outgoing edges (read-only)""" + count = ctypes.c_ulonglong() + edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + target = edges[i].target + if target: + func = core.BNGetBasicBlockFunction(target) + if func is None: + core.BNFreeBasicBlock(target) + target = None + else: + target = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + core.BNNewBasicBlockReference(target)) + core.BNFreeFunction(func) + points = [] + for j in xrange(0, edges[i].pointCount): + points.append((edges[i].points[j].x, edges[i].points[j].y)) + result.append(FunctionGraphEdge(branch_type, self, target, points)) + core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + arch = self.arch + if arch: + return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end) + else: + return "<graph block: %#x-%#x>" % (self.start, self.end) + + def __iter__(self): + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionGraphBlockLines(self.handle, count) + try: + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + yield DisassemblyTextLine(addr, tokens) + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) + + +class DisassemblySettings(object): + def __init__(self, handle = None): + if handle is None: + self.handle = core.BNCreateDisassemblySettings() + else: + self.handle = handle + + def __del__(self): + core.BNFreeDisassemblySettings(self.handle) + + @property + def width(self): + return core.BNGetDisassemblyWidth(self.handle) + + @width.setter + def width(self, value): + core.BNSetDisassemblyWidth(self.handle, value) + + @property + def max_symbol_width(self): + return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) + + @max_symbol_width.setter + def max_symbol_width(self, value): + core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) + + def is_option_set(self, option): + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsDisassemblySettingsOptionSet(self.handle, option) + + def set_option(self, option, state = True): + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetDisassemblySettingsOption(self.handle, option, state) + + +class FunctionGraph(object): + def __init__(self, view, handle): + self.view = view + self.handle = handle + self._on_complete = None + self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete) + + def __del__(self): + self.abort() + core.BNFreeFunctionGraph(self.handle) + + def __eq__(self, value): + if not isinstance(value, FunctionGraph): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraph): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def function(self): + """Function for a function graph (read-only)""" + func = core.BNGetFunctionForFunctionGraph(self.handle) + if func is None: + return None + return Function(self.view, func) + + @property + def complete(self): + """Whether function graph layout is complete (read-only)""" + return core.BNIsFunctionGraphLayoutComplete(self.handle) + + @property + def type(self): + """Function graph type (read-only)""" + return FunctionGraphType(core.BNGetFunctionGraphType(self.handle)) + + @property + def blocks(self): + """List of basic blocks in function (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocks(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + + @property + def width(self): + """Function graph width (read-only)""" + return core.BNGetFunctionGraphWidth(self.handle) + + @property + def height(self): + """Function graph height (read-only)""" + return core.BNGetFunctionGraphHeight(self.handle) + + @property + def horizontal_block_margin(self): + return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) + + @horizontal_block_margin.setter + def horizontal_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) + + @property + def vertical_block_margin(self): + return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) + + @vertical_block_margin.setter + def vertical_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) + + @property + def settings(self): + return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle)) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<graph of %s>" % repr(self.function) + + def __iter__(self): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocks(self.handle, count) + try: + for i in xrange(0, count.value): + yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])) + finally: + core.BNFreeFunctionGraphBlockList(blocks, count.value) + + def _complete(self, ctxt): + try: + if self._on_complete is not None: + self._on_complete() + except: + log.log_error(traceback.format_exc()) + + def layout(self, graph_type = FunctionGraphType.NormalFunctionGraph): + if isinstance(graph_type, str): + graph_type = FunctionGraphType[graph_type] + core.BNStartFunctionGraphLayout(self.handle, graph_type) + + def _wait_complete(self): + self._wait_cond.acquire() + self._wait_cond.notify() + self._wait_cond.release() + + def layout_and_wait(self, graph_type=FunctionGraphType.NormalFunctionGraph): + self._wait_cond = threading.Condition() + self.on_complete(self._wait_complete) + self.layout(graph_type) + + self._wait_cond.acquire() + while not self.complete: + self._wait_cond.wait() + self._wait_cond.release() + + def on_complete(self, callback): + self._on_complete = callback + core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) + + def abort(self): + core.BNAbortFunctionGraph(self.handle) + + def get_blocks_in_region(self, left, top, right, bottom): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + + def is_option_set(self, option): + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsFunctionGraphOptionSet(self.handle, option) + + def set_option(self, option, state = True): + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetFunctionGraphOption(self.handle, option, state) + + +class RegisterInfo(object): + def __init__(self, full_width_reg, size, offset=0, extend=ImplicitRegisterExtend.NoExtend, index=None): + self.full_width_reg = full_width_reg + self.offset = offset + self.size = size + self.extend = extend + self.index = index + + def __repr__(self): + if self.extend == ImplicitRegisterExtend.ZeroExtendToFullWidth: + extend = ", zero extend" + elif self.extend == ImplicitRegisterExtend.SignExtendToFullWidth: + extend = ", sign extend" + else: + extend = "" + return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend) + + +class InstructionBranch(object): + def __init__(self, branch_type, target = 0, arch = None): + self.type = branch_type + self.target = target + self.arch = arch + + def __repr__(self): + branch_type = self.type + if self.arch is not None: + return "<%s: %s@%#x>" % (branch_type.name, self.arch.name, self.target) + return "<%s: %#x>" % (branch_type, self.target) + + +class InstructionInfo(object): + def __init__(self): + self.length = 0 + self.branch_delay = False + self.branches = [] + + def add_branch(self, branch_type, target = 0, arch = None): + self.branches.append(InstructionBranch(branch_type, target, arch)) + + def __repr__(self): + branch_delay = "" + if self.branch_delay: + branch_delay = ", delay slot" + return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches)) + + +class InstructionTextToken(object): + """ + ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. + + ========================== ============================================ + InstructionTextTokenType Description + ========================== ============================================ + TextToken Text that doesn't fit into the other tokens + InstructionToken The instruction mnemonic + OperandSeparatorToken The comma or whatever else separates tokens + RegisterToken Registers + IntegerToken Integers + PossibleAddressToken Integers that are likely addresses + BeginMemoryOperandToken The start of memory operand + EndMemoryOperandToken The end of a memory operand + FloatingPointToken Floating point number + AnnotationToken **For internal use only** + CodeRelativeAddressToken **For internal use only** + StackVariableTypeToken **For internal use only** + DataVariableTypeToken **For internal use only** + FunctionReturnTypeToken **For internal use only** + FunctionAttributeToken **For internal use only** + ArgumentTypeToken **For internal use only** + ArgumentNameToken **For internal use only** + HexDumpByteValueToken **For internal use only** + HexDumpSkippedByteToken **For internal use only** + HexDumpInvalidByteToken **For internal use only** + HexDumpTextToken **For internal use only** + OpcodeToken **For internal use only** + StringToken **For internal use only** + CharacterConstantToken **For internal use only** + CodeSymbolToken **For internal use only** + DataSymbolToken **For internal use only** + StackVariableToken **For internal use only** + ImportToken **For internal use only** + AddressDisplayToken **For internal use only** + ========================== ============================================ + + """ + def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, + context = InstructionTextTokenContext.NoTokenContext, address = 0): + self.type = InstructionTextTokenType(token_type) + self.text = text + self.value = value + self.size = size + self.operand = operand + self.context = InstructionTextTokenContext(context) + self.address = address + + def __str__(self): + return self.text + + def __repr__(self): + return repr(self.text) diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py new file mode 100644 index 00000000..960aee2f --- /dev/null +++ b/python/functionrecognizer.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback + +# Binary Ninja components +import _binaryninjacore as core +import function +import filemetadata +import binaryview +import lowlevelil +import log + + +class FunctionRecognizer(object): + _instance = None + + def __init__(self): + self._cb = core.BNFunctionRecognizer() + self._cb.context = 0 + self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) + + @classmethod + def register_global(cls): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterGlobalFunctionRecognizer(cls._instance._cb) + + @classmethod + def register_arch(cls, arch): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterArchitectureFunctionRecognizer(arch.handle, cls._instance._cb) + + def _recognize_low_level_il(self, ctxt, data, func, il): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(data)) + view = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) + func = function.Function(view, handle = core.BNNewFunctionReference(func)) + il = lowlevelil.LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) + return self.recognize_low_level_il(view, func, il) + except: + log.log_error(traceback.format_exc()) + return False + + def recognize_low_level_il(self, data, func, il): + return False diff --git a/python/generator.cpp b/python/generator.cpp index 08485315..1c0e7b16 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -97,11 +97,18 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac else fprintf(out, "ctypes.c_double"); break; - case StructureTypeClass: - fprintf(out, "%s", type->GetQualifiedName(type->GetStructure()->GetName()).c_str()); - break; - case EnumerationTypeClass: - fprintf(out, "%s", type->GetQualifiedName(type->GetEnumeration()->GetName()).c_str()); + case NamedTypeReferenceClass: + if (type->GetNamedTypeReference()->GetTypeClass() == EnumNamedTypeClass) + { + string name = type->GetNamedTypeReference()->GetName().GetString(); + if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + fprintf(out, "%sEnum", name.c_str()); + } + else + { + fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + } break; case PointerTypeClass: if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) @@ -147,16 +154,16 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac int main(int argc, char* argv[]) { - if (argc < 3) + if (argc < 4) { - fprintf(stderr, "Usage: generator <header> <output>\n"); + fprintf(stderr, "Usage: generator <header> <output> <output_enum>\n"); return 1; } Architecture::Register(new GeneratorArchitecture()); // Parse API header to get type and function information - map<string, Ref<Type>> types, vars, funcs; + map<QualifiedName, Ref<Type>> types, vars, funcs; string errors; bool ok = Architecture::GetByName("generator")->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); fprintf(stderr, "%s", errors.c_str()); @@ -164,83 +171,92 @@ int main(int argc, char* argv[]) return 1; FILE* out = fopen(argv[2], "w"); + FILE* enums = fopen(argv[3], "w"); + fprintf(out, "from __future__ import absolute_import\n"); fprintf(out, "import ctypes, os\n\n"); + fprintf(enums, "import enum"); fprintf(out, "# Load core module\n"); -#if defined(__APPLE__) - fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"..\", \"MacOS\")\n"); -#else - fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); -#endif - -#ifdef WIN32 - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"binaryninjacore.dll\"))\n\n"); -#elif defined(__APPLE__) - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.dylib\"))\n\n"); -#else - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.so.1\"))\n\n"); -#endif + fprintf(out, "import platform\n"); + fprintf(out, "core = None\n"); + fprintf(out, "_base_path = None\n"); + fprintf(out, "if platform.system() == \"Darwin\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"..\", \"MacOS\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.dylib\"))\n\n"); + fprintf(out, "elif platform.system() == \"Linux\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.so.1\"))\n\n"); + fprintf(out, "elif platform.system() == \"Windows\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"binaryninjacore.dll\"))\n"); + fprintf(out, "else:\n"); + fprintf(out, "\traise Exception(\"OS not supported\")\n\n"); // Create type objects fprintf(out, "# Type definitions\n"); - map<string, int64_t> enumMembers; for (auto& i : types) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; if (i.second->GetClass() == StructureTypeClass) { - fprintf(out, "class %s(ctypes.Structure):\n", i.first.c_str()); - fprintf(out, " pass\n"); + fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); + fprintf(out, "\tpass\n"); } else if (i.second->GetClass() == EnumerationTypeClass) { - fprintf(out, "%s = ctypes.c_int\n", i.first.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, "%s = %" PRId64 "\n", j.name.c_str(), j.value); - fprintf(out, "%s_names = {\n", i.first.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, " %" PRId64 ": \"%s\",\n", j.value, j.name.c_str()); - fprintf(out, "}\n"); - fprintf(out, "%s_by_name = {\n", i.first.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, " \"%s\": %" PRId64 ",\n", j.name.c_str(), j.value); - fprintf(out, "}\n"); + if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + + fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str()); + + fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str()); for (auto& j : i.second->GetEnumeration()->GetMembers()) - enumMembers[j.name] = j.value; + { + fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value); + } } else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) { - fprintf(out, "%s = ", i.first.c_str()); + fprintf(out, "%s = ", name.c_str()); OutputType(out, i.second); fprintf(out, "\n"); } } - fprintf(out, "all_enum_values = {\n"); - for (auto& i : enumMembers) - fprintf(out, " \"%s\": %" PRId64 ",\n", i.first.c_str(), i.second); - fprintf(out, "}\n"); fprintf(out, "\n# Structure definitions\n"); for (auto& i : types) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; if ((i.second->GetClass() == StructureTypeClass) && (i.second->GetStructure()->GetMembers().size() != 0)) { - fprintf(out, "%s._fields_ = [\n", i.first.c_str()); + fprintf(out, "%s._fields_ = [\n", name.c_str()); for (auto& j : i.second->GetStructure()->GetMembers()) { - fprintf(out, " (\"%s\", ", j.name.c_str()); + fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); OutputType(out, j.type); fprintf(out, "),\n"); } - fprintf(out, " ]\n"); + fprintf(out, "\t]\n"); } } fprintf(out, "\n# Function definitions\n"); for (auto& i : funcs) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; + // Check for a string result, these will be automatically wrapped to free the string // memory and return a Python string bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && @@ -249,7 +265,7 @@ int main(int argc, char* argv[]) // Pointer returns will be automatically wrapped to return None on null pointer bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); bool callbackConvention = false; - if (i.first == "BNAllocString") + if (name == "BNAllocString") { // Don't perform automatic wrapping of string allocation, and return a void // pointer so that callback functions (which is the only valid use of BNAllocString) @@ -258,11 +274,11 @@ int main(int argc, char* argv[]) callbackConvention = true; } - string funcName = i.first; + string funcName = name; if (stringResult || pointerResult) funcName = string("_") + funcName; - fprintf(out, "%s = core.%s\n", funcName.c_str(), i.first.c_str()); + fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str()); fprintf(out, "%s.restype = ", funcName.c_str()); OutputType(out, i.second->GetChildType(), true, callbackConvention); fprintf(out, "\n"); @@ -271,8 +287,8 @@ int main(int argc, char* argv[]) fprintf(out, "%s.argtypes = [\n", funcName.c_str()); for (auto& j : i.second->GetParameters()) { - fprintf(out, " "); - if (i.first == "BNFreeString") + fprintf(out, "\t\t"); + if (name == "BNFreeString") { // BNFreeString expects a pointer to a string allocated by the core, so do not use // a c_char_p here, as that would be allocated by the Python runtime. This can @@ -285,38 +301,39 @@ int main(int argc, char* argv[]) } fprintf(out, ",\n"); } - fprintf(out, " ]\n"); + fprintf(out, "\t]\n"); } if (stringResult) { // Emit wrapper to get Python string and free native memory - fprintf(out, "def %s(*args):\n", i.first.c_str()); - fprintf(out, " result = %s(*args)\n", funcName.c_str()); - fprintf(out, " string = ctypes.cast(result, ctypes.c_char_p).value\n"); - fprintf(out, " BNFreeString(result)\n"); - fprintf(out, " return string\n"); + fprintf(out, "def %s(*args):\n", name.c_str()); + fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + fprintf(out, "\tstring = ctypes.cast(result, ctypes.c_char_p).value\n"); + fprintf(out, "\tBNFreeString(result)\n"); + fprintf(out, "\treturn string\n"); } else if (pointerResult) { // Emit wrapper to return None on null pointer - fprintf(out, "def %s(*args):\n", i.first.c_str()); - fprintf(out, " result = %s(*args)\n", funcName.c_str()); - fprintf(out, " if not result:\n"); - fprintf(out, " return None\n"); - fprintf(out, " return result\n"); + fprintf(out, "def %s(*args):\n", name.c_str()); + fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + fprintf(out, "\tif not result:\n"); + fprintf(out, "\t\treturn None\n"); + fprintf(out, "\treturn result\n"); } } fprintf(out, "\n# Helper functions\n"); fprintf(out, "def handle_of_type(value, handle_type):\n"); - fprintf(out, " if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); - fprintf(out, " return ctypes.cast(value, ctypes.POINTER(handle_type))\n"); - fprintf(out, " raise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); + fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); + fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n"); + fprintf(out, "\traise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); fprintf(out, "\n# Set path for core plugins\n"); fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); fclose(out); + fclose(enums); return 0; } diff --git a/python/highlight.py b/python/highlight.py new file mode 100644 index 00000000..6af1cf95 --- /dev/null +++ b/python/highlight.py @@ -0,0 +1,112 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +# Binary Ninja components +import _binaryninjacore as core +from enums import HighlightColorStyle, HighlightStandardColor + + +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 = HighlightColorStyle.CustomHighlightColor + self.red = red + self.green = green + self.blue = blue + elif (mix_color is not None) and (mix is not None): + self.style = HighlightColorStyle.MixedHighlightColor + if color is None: + self.color = HighlightStandardColor.NoHighlightColor + else: + self.color = color + self.mix_color = mix_color + self.mix = mix + else: + self.style = HighlightColorStyle.StandardHighlightColor + if color is None: + self.color = HighlightStandardColor.NoHighlightColor + else: + self.color = color + self.alpha = alpha + + def _standard_color_to_str(self, color): + if color == HighlightStandardColor.NoHighlightColor: + return "none" + if color == HighlightStandardColor.BlueHighlightColor: + return "blue" + if color == HighlightStandardColor.GreenHighlightColor: + return "green" + if color == HighlightStandardColor.CyanHighlightColor: + return "cyan" + if color == HighlightStandardColor.RedHighlightColor: + return "red" + if color == HighlightStandardColor.MagentaHighlightColor: + return "magenta" + if color == HighlightStandardColor.YellowHighlightColor: + return "yellow" + if color == HighlightStandardColor.OrangeHighlightColor: + return "orange" + if color == HighlightStandardColor.WhiteHighlightColor: + return "white" + if color == HighlightStandardColor.BlackHighlightColor: + return "black" + return "%d" % color + + def __repr__(self): + if self.style == HighlightColorStyle.StandardHighlightColor: + if self.alpha == 255: + return "<color: %s>" % self._standard_color_to_str(self.color) + return "<color: %s, alpha %d>" % (self._standard_color_to_str(self.color), self.alpha) + if self.style == HighlightColorStyle.MixedHighlightColor: + if self.alpha == 255: + return "<color: mix %s to %s factor %d>" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix) + return "<color: mix %s to %s factor %d, alpha %d>" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix, self.alpha) + if self.style == HighlightColorStyle.CustomHighlightColor: + if self.alpha == 255: + return "<color: #%.2x%.2x%.2x>" % (self.red, self.green, self.blue) + return "<color: #%.2x%.2x%.2x, alpha %d>" % (self.red, self.green, self.blue, self.alpha) + return "<color>" + + def _get_core_struct(self): + result = core.BNHighlightColor() + result.style = self.style + result.color = HighlightStandardColor.NoHighlightColor + result.mix_color = HighlightStandardColor.NoHighlightColor + result.mix = 0 + result.r = 0 + result.g = 0 + result.b = 0 + result.alpha = self.alpha + + if self.style == HighlightColorStyle.StandardHighlightColor: + result.color = self.color + elif self.style == HighlightColorStyle.MixedHighlightColor: + result.color = self.color + result.mixColor = self.mix_color + result.mix = self.mix + elif self.style == HighlightColorStyle.CustomHighlightColor: + result.r = self.red + result.g = self.green + result.b = self.blue + + return result diff --git a/python/interaction.py b/python/interaction.py new file mode 100644 index 00000000..6d640d17 --- /dev/null +++ b/python/interaction.py @@ -0,0 +1,521 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes +import traceback + +# Binary Ninja components +import _binaryninjacore as core +from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonResult +import binaryview +import log + + +class LabelField(object): + def __init__(self, text): + self.text = text + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = FormInputFieldType.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 = 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 + + 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) + 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): + self.__class__._interaction_handler = self + core.BNRegisterInteractionHandler(self._cb) + + def _show_plain_text_report(self, ctxt, view, title, contents): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_plain_text_report(view, title, contents) + except: + log.log_error(traceback.format_exc()) + + def _show_markdown_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_markdown_report(view, title, contents, plaintext) + except: + log.log_error(traceback.format_exc()) + + def _show_html_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_html_report(view, title, contents, plaintext) + except: + log.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.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.log_error(traceback.format_exc()) + + def _get_address_input(self, ctxt, result, prompt, title, view, current_address): + try: + if view: + view = binaryview.BinaryView(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.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.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.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.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.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 == FormInputFieldType.LabelFormField: + field_objs.append(LabelField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.SeparatorFormField: + field_objs.append(SeparatorField()) + elif fields[i].type == FormInputFieldType.TextLineFormField: + field_objs.append(TextLineField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.MultilineTextFormField: + field_objs.append(MultilineTextField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.IntegerFormField: + field_objs.append(IntegerField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.AddressFormField: + view = None + if fields[i].view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(fields[i].view)) + field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) + elif fields[i].type == FormInputFieldType.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 == FormInputFieldType.OpenFileNameFormField: + field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext)) + elif fields[i].type == FormInputFieldType.SaveFileNameFormField: + field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName)) + elif fields[i].type == 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.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.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(prompt, "Save File") + + def get_directory_name_input(self, prompt, default_name): + return get_text_line_input(prompt, "Select Directory") + + def get_form_input(self, fields, title): + return False + + def show_message_box(self, title, text, buttons, icon): + return MessageBoxButtonResult.CancelButton + + +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 + + +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 = MessageBoxButtonResult.OKButton, icon = MessageBoxIcon.InformationIcon): + return core.BNShowMessageBox(title, text, buttons, icon) diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py new file mode 100644 index 00000000..9b88b78a --- /dev/null +++ b/python/lineardisassembly.py @@ -0,0 +1,47 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +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. + """ + def __init__(self, func, block, addr): + self.function = func + self.block = block + self.address = addr + + +class LinearDisassemblyLine(object): + def __init__(self, line_type, func, block, line_offset, contents): + self.type = line_type + self.function = func + self.block = block + self.line_offset = line_offset + self.contents = contents + + def __str__(self): + return str(self.contents) + + def __repr__(self): + return repr(self.contents) diff --git a/python/log.py b/python/log.py new file mode 100644 index 00000000..45adb4aa --- /dev/null +++ b/python/log.py @@ -0,0 +1,175 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +# Binary Ninja components +import _binaryninjacore as core + + +def redirect_output_to_log(): + global _output_to_log + _output_to_log = True + + +def log(level, text): + """ + ``log`` writes messages to the log console for the given log level. + + ============ ======== ======================================================================= + LogLevelName LogLevel Description + ============ ======== ======================================================================= + DebugLog 0 Logs debuging information messages to the console. + InfoLog 1 Logs general information messages to the console. + WarningLog 2 Logs message to console with **Warning** icon. + ErrorLog 3 Logs message to console with **Error** icon, focusing the error console. + AlertLog 4 Logs message to pop up window. + ============ ======== ======================================================================= + + :param LogLevel level: Log level to use + :param str text: message to print + :rtype: None + """ + core.BNLog(level, "%s", str(text)) + + +def log_debug(text): + """ + ``log_debug`` Logs debuging information messages to the console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_debug("Hotdogs!") + Hotdogs! + """ + core.BNLogDebug("%s", str(text)) + + +def log_info(text): + """ + ``log_info`` Logs general information messages to the console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_info("Saucisson!") + Saucisson! + >>> + """ + core.BNLogInfo("%s", str(text)) + + +def log_warn(text): + """ + ``log_warn`` Logs message to console, if run through the GUI it logs with **Warning** icon. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_info("Chilidogs!") + Chilidogs! + >>> + """ + core.BNLogWarn("%s", str(text)) + + +def log_error(text): + """ + ``log_error`` Logs message to console, if run through the GUI it logs with **Error** icon, focusing the error console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_error("Spanferkel!") + Spanferkel! + >>> + """ + core.BNLogError("%s", str(text)) + + +def log_alert(text): + """ + ``log_alert`` Logs message console and to a pop up window if run through the GUI. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_alert("Kielbasa!") + Kielbasa! + >>> + """ + core.BNLogAlert("%s", str(text)) + + +def log_to_stdout(min_level): + """ + ``log_to_stdout`` redirects minimum log level to standard out. + + :param int min_level: minimum level to log to + :rtype: None + :Example: + + >>> log_debug("Hotdogs!") + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_debug("Hotdogs!") + Hotdogs! + >>> + """ + core.BNLogToStdout(min_level) + + +def log_to_stderr(min_level): + """ + ``log_to_stderr`` redirects minimum log level to standard error. + + :param int min_level: minimum level to log to + :rtype: None + """ + core.BNLogToStderr(min_level) + + +def log_to_file(min_level, path, append = False): + """ + ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwritting. + + :param int min_level: minimum level to log to + :param str path: path to log to + :param bool append: optional flag for specifying appending. True = append, False = overwrite. + :rtype: None + """ + core.BNLogToFile(min_level, str(path), append) + + +def close_logs(): + """ + ``close_logs`` close all log files. + + :rtype: None + """ + core.BNCloseLogs() diff --git a/python/lowlevelil.py b/python/lowlevelil.py new file mode 100644 index 00000000..c80fcd0d --- /dev/null +++ b/python/lowlevelil.py @@ -0,0 +1,1294 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from .enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType +import function +import basicblock + + +class LowLevelILLabel(object): + def __init__(self, handle = None): + if handle is None: + self.handle = (core.BNLowLevelILLabel * 1)() + core.BNLowLevelILInitLabel(self.handle) + else: + self.handle = handle + + +class LowLevelILInstruction(object): + """ + ``class LowLevelILInstruction`` Low Level Intermediate Language Instructions are infinite length tree-based + instructions. Tree-based instructions use infix notation with the left hand operand being the destination operand. + Infix notation is thus more natural to read than other notations (e.g. x86 ``mov eax, 0`` vs. LLIL ``eax = 0``). + """ + + ILOperations = { + LowLevelILOperation.LLIL_NOP: [], + LowLevelILOperation.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], + LowLevelILOperation.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), ("src", "expr")], + LowLevelILOperation.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], + LowLevelILOperation.LLIL_LOAD: [("src", "expr")], + LowLevelILOperation.LLIL_STORE: [("dest", "expr"), ("src", "expr")], + LowLevelILOperation.LLIL_PUSH: [("src", "expr")], + LowLevelILOperation.LLIL_POP: [], + LowLevelILOperation.LLIL_REG: [("src", "reg")], + LowLevelILOperation.LLIL_CONST: [("value", "int")], + LowLevelILOperation.LLIL_FLAG: [("src", "flag")], + LowLevelILOperation.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], + LowLevelILOperation.LLIL_ADD: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ADC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_SUB: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_SBB: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_AND: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_OR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_XOR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_LSL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_LSR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ASR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ROL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_RLC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ROR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_RRC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MUL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVU: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVS: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODU: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODS: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_NEG: [("src", "expr")], + LowLevelILOperation.LLIL_NOT: [("src", "expr")], + LowLevelILOperation.LLIL_SX: [("src", "expr")], + LowLevelILOperation.LLIL_ZX: [("src", "expr")], + LowLevelILOperation.LLIL_JUMP: [("dest", "expr")], + LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], + LowLevelILOperation.LLIL_CALL: [("dest", "expr")], + LowLevelILOperation.LLIL_RET: [("dest", "expr")], + LowLevelILOperation.LLIL_NORET: [], + LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], + LowLevelILOperation.LLIL_GOTO: [("dest", "int")], + LowLevelILOperation.LLIL_FLAG_COND: [("condition", "cond")], + LowLevelILOperation.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_BOOL_TO_INT: [("src", "expr")], + LowLevelILOperation.LLIL_SYSCALL: [], + LowLevelILOperation.LLIL_BP: [], + LowLevelILOperation.LLIL_TRAP: [("value", "int")], + LowLevelILOperation.LLIL_UNDEF: [], + LowLevelILOperation.LLIL_UNIMPL: [], + LowLevelILOperation.LLIL_UNIMPL_MEM: [("src", "expr")] + } + + def __init__(self, func, expr_index, instr_index=None): + instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) + self.function = func + self.expr_index = expr_index + self.instr_index = instr_index + self.operation = LowLevelILOperation(instr.operation) + self.size = instr.size + self.address = instr.address + self.source_operand = instr.sourceOperand + if instr.flags == 0: + self.flags = None + else: + self.flags = func.arch.get_flag_write_type_name(instr.flags) + if self.source_operand == 0xffffffff: + self.source_operand = None + operands = LowLevelILInstruction.ILOperations[instr.operation] + self.operands = [] + for i in xrange(0, len(operands)): + name, operand_type = operands[i] + if operand_type == "int": + value = instr.operands[i] + elif operand_type == "expr": + value = LowLevelILInstruction(func, instr.operands[i]) + elif operand_type == "reg": + if (instr.operands[i] & 0x80000000) != 0: + value = instr.operands[i] + else: + value = func.arch.get_reg_name(instr.operands[i]) + elif operand_type == "flag": + value = func.arch.get_flag_name(instr.operands[i]) + elif operand_type == "cond": + value = LowLevelILFlagCondition(instr.operands[i]) + elif operand_type == "int_list": + count = ctypes.c_ulonglong() + operands = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + value = [] + for i in xrange(count.value): + value.append(operands[i]) + core.BNLowLevelILFreeOperandList(operands) + self.operands.append(value) + self.__dict__[name] = value + + def __str__(self): + tokens = self.tokens + if tokens is None: + return "invalid" + result = "" + for token in tokens: + result += token.text + return result + + def __repr__(self): + return "<il: %s>" % str(self) + + @property + def tokens(self): + """LLIL tokens (read-only)""" + count = ctypes.c_ulonglong() + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if (self.instr_index is not None) and (self.function.source_function is not None): + if not core.BNGetLowLevelILInstructionText(self.function.handle, self.function.source_function.handle, + self.function.arch.handle, self.instr_index, tokens, count): + return None + else: + if not core.BNGetLowLevelILExprText(self.function.handle, self.function.arch.handle, + self.expr_index, tokens, count): + return None + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeInstructionText(tokens, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class LowLevelILExpr(object): + """ + ``class LowLevelILExpr`` hold the index of IL Expressions. + + .. note:: This class shouldn't be instantiated directly. Rather the helper members of LowLevelILFunction should be \ + used instead. + """ + def __init__(self, index): + self.index = index + + +class LowLevelILFunction(object): + """ + ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr + objects can be added to the LowLevelILFunction by calling ``append`` and passing the result of the various class + methods which return LowLevelILExpr objects. + + + LowLevelILFlagCondition values used as parameters in the ``flag_condition`` method. + + ======================= ========== =============================== + LowLevelILFlagCondition Operator Description + ======================= ========== =============================== + LLFC_E == Equal + LLFC_NE != Not equal + LLFC_SLT s< Signed less than + LLFC_ULT u< Unsigned less than + LLFC_SLE s<= Signed less than or equal + LLFC_ULE u<= Unsigned less than or equal + LLFC_SGE s>= Signed greater than or equal + LLFC_UGE u>= Unsigned greater than or equal + LLFC_SGT s> Signed greather than + LLFC_UGT u> Unsigned greater than + LLFC_NEG - Negative + LLFC_POS + Positive + LLFC_O overflow Overflow + LLFC_NO !overflow No overflow + ======================= ========== =============================== + """ + def __init__(self, arch, handle = None, source_func = None): + self.arch = arch + self.source_function = source_func + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) + else: + 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) + + def __eq__(self, value): + if not isinstance(value, LowLevelILFunction): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, LowLevelILFunction): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def current_address(self): + """Current IL Address (read/write)""" + return core.BNLowLevelILGetCurrentAddress(self.handle) + + @current_address.setter + def current_address(self, value): + core.BNLowLevelILSetCurrentAddress(self.handle, value) + + @property + def temp_reg_count(self): + """Number of temporary registers (read-only)""" + return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) + + @property + def temp_flag_count(self): + """Number of temporary flags (read-only)""" + return core.BNGetLowLevelILTemporaryFlagCount(self.handle) + + @property + def basic_blocks(self): + """list of LowLevelILBasicBlock objects (read-only)""" + 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 + for i in xrange(0, count.value): + result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __len__(self): + return int(core.BNGetLowLevelILInstructionCount(self.handle)) + + def __getitem__(self, i): + if isinstance(i, slice) or isinstance(i, tuple): + raise IndexError("expected integer instruction index") + if isinstance(i, LowLevelILExpr): + return LowLevelILInstruction(self, i.index) + if (i < 0) or (i >= len(self)): + raise IndexError("index out of range") + return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i), 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) + view = None + if self.source_function is not None: + view = self.source_function.view + try: + for i in xrange(0, count.value): + yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + def clear_indirect_branches(self): + core.BNLowLevelILClearIndirectBranches(self.handle) + + def set_indirect_branches(self, branches): + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) + + def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): + if isinstance(operation, str): + operation = LowLevelILOperation[operation] + elif isinstance(operation, LowLevelILOperation): + operation = operation.value + if isinstance(flags, str): + flags = self.arch.get_flag_write_type_by_name(flags) + elif flags is None: + flags = 0 + return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) + + def append(self, expr): + """ + ``append`` adds the LowLevelILExpr ``expr`` to the current LowLevelILFunction. + + :param LowLevelILExpr expr: the LowLevelILExpr to add to the current LowLevelILFunction + :return: number of LowLevelILExpr in the current function + :rtype: int + """ + return core.BNLowLevelILAddInstruction(self.handle, expr.index) + + def nop(self): + """ + ``nop`` no operation, this instruction does nothing + + :return: The no operation expression + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NOP) + + def set_reg(self, size, reg, value, flags = 0): + """ + ``set_reg`` sets the register ``reg`` of size ``size`` to the expression ``value`` + + :param int size: size of the register parameter in bytes + :param str reg: the register name + :param LowLevelILExpr value: an expression to set the register to + :param str flags: which flags are set by this operation + :return: The expression ``reg = value`` + :rtype: LowLevelILExpr + """ + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(LowLevelILOperation.LLIL_SET_REG, reg, value.index, size = size, flags = flags) + + def set_reg_split(self, size, hi, lo, value, flags = 0): + """ + ``set_reg_split`` uses ``hi`` and ``lo`` as a single extended register setting ``hi:lo`` to the expression + ``value``. + + :param int size: size of the register parameter in bytes + :param str hi: the high register name + :param str lo: the low register name + :param LowLevelILExpr value: an expression to set the split regiters to + :param str flags: which flags are set by this operation + :return: The expression ``hi:lo = value`` + :rtype: LowLevelILExpr + """ + if isinstance(hi, str): + hi = self.arch.regs[hi].index + if isinstance(lo, str): + lo = self.arch.regs[lo].index + return self.expr(LowLevelILOperation.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) + + def set_flag(self, flag, value): + """ + ``set_flag`` sets the flag ``flag`` to the LowLevelILExpr ``value`` + + :param str flag: the low register name + :param LowLevelILExpr value: an expression to set the flag to + :return: The expression FLAG.flag = value + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SET_FLAG, self.arch.get_flag_by_name(flag), value.index) + + def load(self, size, addr): + """ + ``laod`` Reads ``size`` bytes from the expression ``addr`` + + :param int size: number of bytes to read + :param LowLevelILExpr addr: the expression to read memory from + :return: The expression ``[addr].size`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size) + + def store(self, size, addr, value): + """ + ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` + + :param int size: number of bytes to write + :param LowLevelILExpr addr: the expression to write to + :param LowLevelILExpr value: the expression to be written + :return: The expression ``[addr].size = value`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size) + + def push(self, size, value): + """ + ``push`` writes ``size`` bytes from expression ``value`` to the stack, adjusting the stack by ``size``. + + :param int size: number of bytes to write and adjust the stack by + :param LowLevelILExpr value: the expression to write + :return: The expression push(value) + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_PUSH, value.index, size=size) + + def pop(self, size): + """ + ``pop`` reads ``size`` bytes from the stack, adjusting the stack by ``size``. + + :param int size: number of bytes to read from the stack + :return: The expression ``pop`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_POP, size=size) + + def reg(self, size, reg): + """ + ``reg`` returns a register of size ``size`` with name ``name`` + + :param int size: the size of the register in bytes + :param str reg: the name of the register + :return: A register expression for the given string + :rtype: LowLevelILExpr + """ + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(LowLevelILOperation.LLIL_REG, reg, size=size) + + def const(self, size, value): + """ + ``const`` returns an expression for the constant integer ``value`` with size ``size`` + + :param int size: the size of the constant in bytes + :param int value: integer value of the constant + :return: A constant expression of given value and size + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CONST, value, size=size) + + def flag(self, reg): + """ + ``flag`` returns a flag expression for the given flag name. + + :param str reg: name of the flag expression to retrieve + :return: A flag expression of given flag name + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_FLAG, self.arch.get_flag_by_name(reg)) + + def flag_bit(self, size, reg, bit): + """ + ``flag_bit`` sets the flag named ``reg`` and size ``size`` to the constant integer value ``bit`` + + :param int size: the size of the flag + :param str reg: flag value + :param int bit: integer value to set the bit to + :return: A constant expression of given value and size ``FLAG.reg = bit`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size=size) + + def add(self, size, a, b, flags=None): + """ + ``add`` adds expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning + an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``add.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ADD, a.index, b.index, size=size, flags=flags) + + def add_carry(self, size, a, b, flags=None): + """ + ``add_carry`` adds with carry expression ``a`` to expression ``b`` potentially setting flags ``flags`` and + returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``adc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ADC, a.index, b.index, size=size, flags=flags) + + def sub(self, size, a, b, flags=None): + """ + ``sub`` subtracts expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning + an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``sub.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SUB, a.index, b.index, size=size, flags=flags) + + def sub_borrow(self, size, a, b, flags=None): + """ + ``sub_borrow`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``sbc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SBB, a.index, b.index, size=size, flags=flags) + + def and_expr(self, size, a, b, flags=None): + """ + ``and_expr`` bitwise and's expression ``a`` and expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``and.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_AND, a.index, b.index, size=size, flags=flags) + + def or_expr(self, size, a, b, flags=None): + """ + ``or_expr`` bitwise or's expression ``a`` and expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``or.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_OR, a.index, b.index, size=size, flags=flags) + + def xor_expr(self, size, a, b, flags=None): + """ + ``xor_expr`` xor's expression ``a`` with expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``xor.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_XOR, a.index, b.index, size=size, flags=flags) + + def shift_left(self, size, a, b, flags=None): + """ + ``shift_left`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``lsl.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LSL, a.index, b.index, size=size, flags=flags) + + def logical_shift_right(self, size, a, b, flags=None): + """ + ``logical_shift_right`` shifts logically right expression ``a`` by expression ``b`` potentially setting flags + ``flags``and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``lsr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LSR, a.index, b.index, size=size, flags=flags) + + def arith_shift_right(self, size, a, b, flags=None): + """ + ``arith_shift_right`` shifts arithmatic right expression ``a`` by expression ``b`` potentially setting flags + ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``asr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ASR, a.index, b.index, size=size, flags=flags) + + def rotate_left(self, size, a, b, flags=None): + """ + ``rotate_left`` bitwise rotates left expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rol.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ROL, a.index, b.index, size=size, flags=flags) + + def rotate_left_carry(self, size, a, b, flags=None): + """ + ``rotate_left_carry`` bitwise rotates left with carry expression ``a`` by expression ``b`` potentially setting + flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rcl.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RLC, a.index, b.index, size=size, flags=flags) + + def rotate_right(self, size, a, b, flags=None): + """ + ``rotate_right`` bitwise rotates right expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``ror.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ROR, a.index, b.index, size=size, flags=flags) + + def rotate_right_carry(self, size, a, b, flags=None): + """ + ``rotate_right_carry`` bitwise rotates right with carry expression ``a`` by expression ``b`` potentially setting + flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rcr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RRC, a.index, b.index, size=size, flags=flags) + + def mult(self, size, a, b, flags=None): + """ + ``mult`` multiplies expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``sbc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MUL, a.index, b.index, size=size, flags=flags) + + def mult_double_prec_signed(self, size, a, b, flags=None): + """ + ``mult_double_prec_signed`` multiplies signed with double precision expression ``a`` by expression ``b`` + potentially setting flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MULS_DP, a.index, b.index, size=size, flags=flags) + + def mult_double_prec_unsigned(self, size, a, b, flags=None): + """ + ``mult_double_prec_unsigned`` multiplies unsigned with double precision expression ``a`` by expression ``b`` + potentially setting flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MULU_DP, a.index, b.index, size=size, flags=flags) + + def div_signed(self, size, a, b, flags=None): + """ + ``div_signed`` signed divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS, a.index, b.index, size=size, flags=flags) + + def div_double_prec_signed(self, size, hi, lo, b, flags=None): + """ + ``div_double_prec_signed`` signed double precision divide using expression ``hi`` and expression ``lo`` as a + single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def div_unsigned(self, size, a, b, flags=None): + """ + ``div_unsigned`` unsigned divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS, a.index, b.index, size=size, flags=flags) + + def div_double_prec_unsigned(self, size, hi, lo, b, flags=None): + """ + ``div_double_prec_unsigned`` unsigned double precision divide using expression ``hi`` and expression ``lo`` as + a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def mod_signed(self, size, a, b, flags=None): + """ + ``mod_signed`` signed modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``mods.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS, a.index, b.index, size=size, flags=flags) + + def mod_double_prec_signed(self, size, hi, lo, b, flags=None): + """ + ``mod_double_prec_signed`` signed double precision modulus using expression ``hi`` and expression ``lo`` as a single + double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression + of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``mods.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def mod_unsigned(self, size, a, b, flags=None): + """ + ``mod_unsigned`` unsigned modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``modu.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS, a.index, b.index, size=size, flags=flags) + + def mod_double_prec_unsigned(self, size, hi, lo, b, flags=None): + """ + ``mod_double_prec_unsigned`` unsigned double precision modulus using expression ``hi`` and expression ``lo`` as + a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``modu.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def neg_expr(self, size, value, flags=None): + """ + ``neg_expr`` two's complement sign negation of expression ``value`` of size ``size`` potentially setting flags + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to negate + :param str flags: optional, flags to set + :return: The expression ``neg.<size>{<flags>}(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NEG, value.index, size=size, flags=flags) + + def not_expr(self, size, value, flags=None): + """ + ``not_expr`` bitwise inverse of expression ``value`` of size ``size`` potentially setting flags + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to bitwise invert + :param str flags: optional, flags to set + :return: The expression ``not.<size>{<flags>}(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NOT, value.index, size=size, flags=flags) + + def sign_extend(self, size, value, flags=None): + """ + ``sign_extend`` two's complement sign-extends the expression in ``value`` to ``size`` bytes + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to sign extn + :param str flags: optional, flags to set + :return: The expression ``sx.<size>(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SX, value.index, size=size, flags=flags) + + def zero_extend(self, size, value): + """ + ``zero_extend`` zero-extends the expression in ``value`` to ``size`` bytes + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to zero extend + :return: The expression ``sx.<size>(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ZX, value.index, size=size) + + def jump(self, dest): + """ + ``jump`` returns an expression which jumps (branches) to the expression ``dest`` + + :param LowLevelILExpr dest: the expression to jump to + :return: The expression ``jump(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_JUMP, dest.index) + + def call(self, dest): + """ + ``call`` returns an expression which first pushes the address of the next instruction onto the stack then jumps + (branches) to the expression ``dest`` + + :param LowLevelILExpr dest: the expression to call + :return: The expression ``call(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CALL, dest.index) + + def ret(self, dest): + """ + ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for + jump that makes the disassembler top disassembling. + + :param LowLevelILExpr dest: the expression to jump to + :return: The expression ``jump(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RET, dest.index) + + def no_ret(self): + """ + ``no_ret`` returns an expression halts disassembly + + :return: The expression ``noreturn`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NORET) + + def flag_condition(self, cond): + """ + ``flag_condition`` returns a flag_condition expression for the given LowLevelILFlagCondition + + :param LowLevelILFlagCondition cond: Flag condition expression to retrieve + :return: A flag_condition expression + :rtype: LowLevelILExpr + """ + if isinstance(cond, str): + cond = LowLevelILFlagCondition[cond] + elif isinstance(cond, LowLevelILFlagCondition): + cond = cond.value + return self.expr(LowLevelILOperation.LLIL_FLAG_COND, cond) + + def compare_equal(self, size, a, b): + """ + ``compare_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is equal to + expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_E, a.index, b.index, size = size) + + def compare_not_equal(self, size, a, b): + """ + ``compare_not_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is not equal to + expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_NE, a.index, b.index, size = size) + + def compare_signed_less_than(self, size, a, b): + """ + ``compare_signed_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed less than expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SLT, a.index, b.index, size = size) + + def compare_unsigned_less_than(self, size, a, b): + """ + ``compare_unsigned_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned less than expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_ULT, a.index, b.index, size = size) + + def compare_signed_less_equal(self, size, a, b): + """ + ``compare_signed_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed less than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SLE, a.index, b.index, size = size) + + def compare_unsigned_less_equal(self, size, a, b): + """ + ``compare_unsigned_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned less than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_ULE, a.index, b.index, size = size) + + def compare_signed_greater_equal(self, size, a, b): + """ + ``compare_signed_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed greater than or equal toexpression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SGE, a.index, b.index, size = size) + + def compare_unsigned_greater_equal(self, size, a, b): + """ + ``compare_unsigned_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` + is unsigned greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_UGE, a.index, b.index, size = size) + + def compare_signed_greater_than(self, size, a, b): + """ + ``compare_signed_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SGT, a.index, b.index, size = size) + + def compare_unsigned_greater_than(self, size, a, b): + """ + ``compare_unsigned_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_UGT, a.index, b.index, size = size) + + def test_bit(self, size, a, b): + return self.expr(LowLevelILOperation.LLIL_TEST_BIT, a.index, b.index, size = size) + + def system_call(self): + """ + ``system_call`` return a system call expression. + + :return: a system call expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SYSCALL) + + def breakpoint(self): + """ + ``breakpoint`` returns a processor breakpoint expression. + + :return: a breakpoint expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_BP) + + def trap(self, value): + """ + ``trap`` returns a processor trap (interrupt) expression of the given integer ``value``. + + :param int value: trap (interrupt) number + :return: a trap expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_TRAP, value) + + def undefined(self): + """ + ``undefined`` returns the undefined expression. This should be used for instructions which perform functions but + aren't important for dataflow or partial emulation purposes. + + :return: the unimplemented expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNDEF) + + def unimplemented(self): + """ + ``unimplemented`` returns the unimplemented expression. This should be used for all instructions which aren't + implemented. + + :return: the unimplemented expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNIMPL) + + def unimplemented_memory_ref(self, size, addr): + """ + ``unimplemented_memory_ref`` a memory reference to expression ``addr`` of size ``size`` with unimplemented operation. + + :param int size: size in bytes of the memory reference + :param LowLevelILExpr addr: expression to reference memory + :return: the unimplemented memory reference expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNIMPL_MEM, addr.index, size = size) + + def goto(self, label): + """ + ``goto`` returns a goto expression which jumps to the provided LowLevelILLabel. + + :param LowLevelILLabel label: Label to jump to + :return: the LowLevelILExpr that jumps to the provided label + :rtype: LowLevelILExpr + """ + return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) + + def if_expr(self, operand, t, f): + """ + ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the LowLevelILLabel + ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. + + :param LowLevelILExpr operand: comparison expression to evaluate. + :param LowLevelILLabel t: Label for the true branch + :param LowLevelILLabel f: Label for the false branch + :return: the LowLevelILExpr for the if expression + :rtype: LowLevelILExpr + """ + return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) + + def mark_label(self, label): + """ + ``mark_label`` assigns a LowLevelILLabel to the current IL address. + + :param LowLevelILLabel label: + :rtype: None + """ + core.BNLowLevelILMarkLabel(self.handle, label.handle) + + def add_label_list(self, labels): + """ + ``add_label_list`` returns a label list expression for the given list of LowLevelILLabel objects. + + :param list(LowLevelILLabel) lables: the list of LowLevelILLabel to get a label list expression from + :return: the label list expression + :rtype: LowLevelILExpr + """ + 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))) + + def add_operand_list(self, operands): + """ + ``add_operand_list`` returns an operand list expression for the given list of integer operands. + + :param list(int) operands: list of operand numbers + :return: an operand list expression + :rtype: LowLevelILExpr + """ + operand_list = (ctypes.c_ulonglong * len(operands))() + for i in xrange(len(operands)): + operand_list[i] = operands[i] + return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) + + def operand(self, n, expr): + """ + ``operand`` sets the operand number of the expression ``expr`` and passes back ``expr`` without modification. + + :param int n: + :param LowLevelILExpr expr: + :return: returns the expression ``expr`` unmodified + :rtype: LowLevelILExpr + """ + core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) + return expr + + def finalize(self): + """ + ``finalize`` ends the function and computes the list of basic blocks. + + :rtype: None + """ + core.BNFinalizeLowLevelILFunction(self.handle) + + def add_label_for_address(self, arch, addr): + """ + ``add_label_for_address`` adds a low-level IL label for the given architecture ``arch`` at the given virtual + address ``addr`` + + :param Architecture arch: Architecture to add labels for + :param int addr: the IL address to add a label at + """ + if arch is not None: + arch = arch.handle + core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) + + def get_label_for_address(self, arch, addr): + """ + ``get_label_for_address`` returns the LowLevelILLabel for the given Architecture ``arch`` and IL address ``addr``. + + :param Architecture arch: + :param int addr: IL Address label to retrieve + :return: the LowLevelILLabel for the given IL address + :rtype: LowLevelILLabel + """ + if arch is not None: + arch = arch.handle + label = core.BNGetLowLevelILLabelForAddress(self.handle, arch, addr) + if label is None: + return None + return LowLevelILLabel(label) + + +class LowLevelILBasicBlock(basicblock.BasicBlock): + def __init__(self, view, handle, owner): + super(LowLevelILBasicBlock, self).__init__(view, handle) + self.il_function = owner + + def __iter__(self): + for idx in xrange(self.start, self.end): + yield self.il_function[idx] + + def __getitem__(self, idx): + size = self.end - self.start + if idx > size or idx < -size: + raise IndexError("list index is out of range") + if idx >= 0: + return self.il_function[idx + self.start] + else: + return self.il_function[self.end + idx] + + +def LLIL_TEMP(n): + return n | 0x80000000 + + +def LLIL_REG_IS_TEMP(n): + return (n & 0x80000000) != 0 + + +def LLIL_GET_TEMP_REG_INDEX(n): + return n & 0x7fffffff diff --git a/python/mainthread.py b/python/mainthread.py new file mode 100644 index 00000000..220f0b64 --- /dev/null +++ b/python/mainthread.py @@ -0,0 +1,59 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# Binary Ninja components +import _binaryninjacore as core +import scriptingprovider + + +def execute_on_main_thread(func): + action = scriptingprovider._ThreadActionContext(func) + obj = core.BNExecuteOnMainThread(0, action.callback) + if obj: + return scriptingprovider.MainThreadAction(obj) + return None + + +def execute_on_main_thread_and_wait(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNExecuteOnMainThreadAndWait(0, action.callback) + + +def worker_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerEnqueue(0, action.callback) + + +def worker_priority_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerPriorityEnqueue(0, action.callback) + + +def worker_interactive_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerInteractiveEnqueue(0, action.callback) + + +def get_worker_thread_count(): + return core.BNGetWorkerThreadCount() + + +def set_worker_thread_count(count): + core.BNSetWorkerThreadCount(count) diff --git a/python/platform.py b/python/platform.py new file mode 100644 index 00000000..139b7d5e --- /dev/null +++ b/python/platform.py @@ -0,0 +1,362 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import startup +import architecture +import callingconvention +import types + + +class _PlatformMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformList(count) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + + @property + def os_list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformOSList(count) + result = [] + for i in xrange(0, count.value): + result.append(str(platforms[i])) + core.BNFreePlatformOSList(platforms, count.value) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformList(count) + try: + for i in xrange(0, count.value): + yield Platform(None, core.BNNewPlatformReference(platforms[i])) + finally: + core.BNFreePlatformList(platforms, count.value) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, value): + startup._init_plugins() + platform = core.BNGetPlatformByName(str(value)) + if platform is None: + raise KeyError("'%s' is not a valid platform" % str(value)) + return Platform(None, platform) + + def get_list(cls, os = None, arch = None): + startup._init_plugins() + count = ctypes.c_ulonglong() + if os is None: + platforms = core.BNGetPlatformList(count) + elif arch is None: + platforms = core.BNGetPlatformListByOS(os) + else: + platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + + +class Platform(object): + """ + ``class Platform`` contains all information releated to the execution environment of the binary, mainly the + calling conventions used. + """ + __metaclass__ = _PlatformMetaClass + name = None + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) + else: + self.handle = handle + self.__dict__["name"] = core.BNGetPlatformName(self.handle) + self.arch = architecture.Architecture(core.BNGetPlatformArchitecture(self.handle)) + + def __del__(self): + core.BNFreePlatform(self.handle) + + def __eq__(self, value): + if not isinstance(value, Platform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Platform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def default_calling_convention(self): + """ + Default calling convention. + + :getter: returns a CallingConvention object for the default calling convention. + :setter: sets the default calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformDefaultCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @default_calling_convention.setter + def default_calling_convention(self, value): + core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) + + @property + def cdecl_calling_convention(self): + """ + Cdecl calling convention. + + :getter: returns a CallingConvention object for the cdecl calling convention. + :setter sets the cdecl calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformCdeclCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @cdecl_calling_convention.setter + def cdecl_calling_convention(self, value): + core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) + + @property + def stdcall_calling_convention(self): + """ + Stdcall calling convention. + + :getter: returns a CallingConvention object for the stdcall calling convention. + :setter sets the stdcall calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformStdcallCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @stdcall_calling_convention.setter + def stdcall_calling_convention(self, value): + core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) + + @property + def fastcall_calling_convention(self): + """ + Fastcall calling convention. + + :getter: returns a CallingConvention object for the fastcall calling convention. + :setter sets the fastcall calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformFastcallCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @fastcall_calling_convention.setter + def fastcall_calling_convention(self, value): + core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) + + @property + def system_call_convention(self): + """ + System call convention. + + :getter: returns a CallingConvention object for the system call convention. + :setter sets the system call convention + :type: CallingConvention + """ + result = core.BNGetPlatformSystemCallConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @system_call_convention.setter + def system_call_convention(self, value): + core.BNSetPlatformSystemCallConvention(self.handle, value.handle) + + @property + def calling_conventions(self): + """ + List of platform CallingConvention objects (read-only) + + :getter: returns the list of supported CallingConvention objects + :type: list(CallingConvention) + """ + count = ctypes.c_ulonglong() + cc = core.BNGetPlatformCallingConventions(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) + core.BNFreeCallingConventionList(cc, count.value) + return result + + @property + def types(self): + """List of platform-specific types (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformTypes(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def variables(self): + """List of platform-specific variable definitions (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformVariables(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def functions(self): + """List of platform-specific function definitions (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformFunctions(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def system_calls(self): + """List of system calls for this platform (read-only)""" + count = ctypes.c_ulonglong(0) + call_list = core.BNGetPlatformSystemCalls(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(call_list[i].name) + t = types.Type(core.BNNewTypeReference(call_list[i].type)) + result[call_list[i].number] = (name, t) + core.BNFreeSystemCallList(call_list, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<platform: %s>" % self.name + + def __str__(self): + return self.name + + def register(self, os): + """ + ``register`` registers the platform for given OS name. + + :param str os: OS name to register + :rtype: None + """ + core.BNRegisterPlatform(os, self.handle) + + def register_calling_convention(self, cc): + """ + ``register_calling_convention`` register a new calling convention. + + :param CallingConvention cc: a CallingConvention object to register + :rtype: None + """ + core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) + + def get_related_platform(self, arch): + result = core.BNGetRelatedPlatform(self.handle, arch.handle) + if not result: + return None + return Platform(None, handle = result) + + def add_related_platform(self, arch, platform): + core.BNAddRelatedPlatform(self.handle, arch.handle, platform.handle) + + def get_associated_platform_by_address(self, addr): + new_addr = ctypes.c_ulonglong() + new_addr.value = addr + result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) + return Platform(None, handle = result), new_addr.value + + def get_type_by_name(self, name): + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetPlatformTypeByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + def get_variable_by_name(self, name): + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetPlatformVariableByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + def get_function_by_name(self, name): + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetPlatformFunctionByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + def get_system_call_name(self, number): + return core.BNGetPlatformSystemCallName(self.handle, number) + + def get_system_call_type(self, number): + obj = core.BNGetPlatformSystemCallType(self.handle, number) + if not obj: + return None + return types.Type(obj) + + def generate_auto_platform_type_id(self, name): + name = types.QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoPlatformTypeId(self.handle, name) + + def generate_auto_platform_type_ref(self, type_class, name): + type_id = self.generate_auto_platform_type_id(name) + return types.NamedTypeReference(type_class, type_id, name) + + def get_auto_platform_type_id_source(self): + return core.BNGetAutoPlatformTypeIdSource(self.handle) diff --git a/python/plugin.py b/python/plugin.py new file mode 100644 index 00000000..2632b5a9 --- /dev/null +++ b/python/plugin.py @@ -0,0 +1,389 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes +import threading + +# Binary Ninja components +import _binaryninjacore as core +from enums import PluginCommandType +import startup +import filemetadata +import binaryview +import function +import log + + +class PluginCommandContext(object): + def __init__(self, view): + self.view = view + self.address = 0 + self.length = 0 + self.function = None + + +class _PluginCommandMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + commands = core.BNGetAllPluginCommands(count) + result = [] + for i in xrange(0, count.value): + result.append(PluginCommand(commands[i])) + core.BNFreePluginCommandList(commands) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + commands = core.BNGetAllPluginCommands(count) + try: + for i in xrange(0, count.value): + yield PluginCommand(commands[i]) + finally: + core.BNFreePluginCommandList(commands) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class PluginCommand(object): + _registered_commands = [] + __metaclass__ = _PluginCommandMetaClass + + def __init__(self, cmd): + self.command = core.BNPluginCommand() + ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) + self.name = str(cmd.name) + self.description = str(cmd.description) + self.type = PluginCommandType(cmd.type) + + @classmethod + def _default_action(cls, view, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _address_action(cls, view, addr, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _range_action(cls, view, addr, length, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr, length) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _function_action(cls, view, func, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) + action(view_obj, func_obj) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _default_is_valid(cls, view, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _address_is_valid(cls, view, addr, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _range_is_valid(cls, view, addr, length, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr, length) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _function_is_valid(cls, view, func, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) + return is_valid(view_obj, func_obj) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def register(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_action(view, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_is_valid(view, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommand(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_address(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_action(view, addr, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_is_valid(view, addr, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForAddress(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_range(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_action(view, addr, length, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_is_valid(view, addr, length, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForRange(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_function(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_action(view, func, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_is_valid(view, func, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None) + + @classmethod + def get_valid_list(cls, context): + commands = cls.list + result = [] + for cmd in commands: + if cmd.is_valid(context): + result.append(cmd) + return result + + def is_valid(self, context): + if context.view is None: + return False + if self.command.type == PluginCommandType.DefaultPluginCommand: + if not self.command.defaultIsValid: + return True + return self.command.defaultIsValid(self.command.context, context.view.handle) + elif self.command.type == PluginCommandType.AddressPluginCommand: + if not self.command.addressIsValid: + return True + return self.command.addressIsValid(self.command.context, context.view.handle, context.address) + elif self.command.type == PluginCommandType.RangePluginCommand: + if context.length == 0: + return False + if not self.command.rangeIsValid: + return True + return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == PluginCommandType.FunctionPluginCommand: + if context.function is None: + return False + if not self.command.functionIsValid: + return True + return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle) + return False + + def execute(self, context): + if not self.is_valid(context): + return + if self.command.type == PluginCommandType.DefaultPluginCommand: + self.command.defaultCommand(self.command.context, context.view.handle) + elif self.command.type == PluginCommandType.AddressPluginCommand: + self.command.addressCommand(self.command.context, context.view.handle, context.address) + elif self.command.type == PluginCommandType.RangePluginCommand: + self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == PluginCommandType.FunctionPluginCommand: + self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) + + def __repr__(self): + return "<PluginCommand: %s>" % self.name + + +class MainThreadAction(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeMainThreadAction(self.handle) + + def execute(self): + core.BNExecuteMainThreadAction(self.handle) + + @property + def done(self): + return core.BNIsMainThreadActionDone(self.handle) + + def wait(self): + core.BNWaitForMainThreadAction(self.handle) + + +class MainThreadActionHandler(object): + _main_thread = None + + def __init__(self): + self._cb = core.BNMainThreadCallbacks() + self._cb.context = 0 + self._cb.addAction = self._cb.addAction.__class__(self._add_action) + + def register(self): + self.__class__._main_thread = self + core.BNRegisterMainThread(self._cb) + + def _add_action(self, ctxt, action): + try: + self.add_action(MainThreadAction(action)) + except: + log.log_error(traceback.format_exc()) + + 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): + startup._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() diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py new file mode 100644 index 00000000..71402b1b --- /dev/null +++ b/python/scriptingprovider.py @@ -0,0 +1,645 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +import code +import traceback +import ctypes +import threading +import abc +import sys + +# Binary Ninja Components +import _binaryninjacore as core +from enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState +import binaryview +import function +import basicblock +import startup +import log + +_output_to_log = False + + +class _ThreadActionContext(object): + _actions = [] + + def __init__(self, func): + self.func = func + self.interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + self.interpreter = PythonScriptingInstance._interpreter.value + self.__class__._actions.append(self) + self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute()) + + def execute(self): + old_interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + old_interpreter = PythonScriptingInstance._interpreter.value + PythonScriptingInstance._interpreter.value = self.interpreter + try: + self.func() + finally: + PythonScriptingInstance._interpreter.value = old_interpreter + self.__class__._actions.remove(self) + + +class ScriptingOutputListener(object): + def _register(self, handle): + self._cb = core.BNScriptingOutputListener() + self._cb.context = 0 + self._cb.output = self._cb.output.__class__(self._output) + self._cb.error = self._cb.error.__class__(self._error) + self._cb.inputReadyStateChanged = self._cb.inputReadyStateChanged.__class__(self._input_ready_state_changed) + core.BNRegisterScriptingInstanceOutputListener(handle, self._cb) + + def _unregister(self, handle): + core.BNUnregisterScriptingInstanceOutputListener(handle, self._cb) + + def _output(self, ctxt, text): + try: + self.notify_output(text) + except: + log.log_error(traceback.format_exc()) + + def _error(self, ctxt, text): + try: + self.notify_error(text) + except: + log.log_error(traceback.format_exc()) + + def _input_ready_state_changed(self, ctxt, state): + try: + self.notify_input_ready_state_changed(state) + except: + log.log_error(traceback.format_exc()) + + def notify_output(self, text): + pass + + def notify_error(self, text): + pass + + def notify_input_ready_state_changed(self, state): + pass + + +class ScriptingInstance(object): + def __init__(self, provider, handle = None): + if handle is None: + self._cb = core.BNScriptingInstanceCallbacks() + self._cb.context = 0 + self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) + self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) + self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view) + self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function) + self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block) + self._cb.setCurrentAddress = self._cb.setCurrentAddress.__class__(self._set_current_address) + self._cb.setCurrentSelection = self._cb.setCurrentSelection.__class__(self._set_current_selection) + self.handle = core.BNInitScriptingInstance(provider.handle, self._cb) + else: + self.handle = core.handle_of_type(handle, core.BNScriptingInstance) + self.listeners = [] + + def __del__(self): + core.BNFreeScriptingInstance(self.handle) + + def _destroy_instance(self, ctxt): + try: + self.perform_destroy_instance() + except: + log.log_error(traceback.format_exc()) + + def _execute_script_input(self, ctxt, text): + try: + return self.perform_execute_script_input(text) + except: + log.log_error(traceback.format_exc()) + return ScriptingProviderExecuteResult.InvalidScriptInput + + def _set_current_binary_view(self, ctxt, view): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.perform_set_current_binary_view(view) + except: + log.log_error(traceback.format_exc()) + + def _set_current_function(self, ctxt, func): + try: + if func: + func = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) + else: + func = None + self.perform_set_current_function(func) + except: + log.log.log_error(traceback.format_exc()) + + def _set_current_basic_block(self, ctxt, block): + try: + if block: + func = core.BNGetBasicBlockFunction(block) + if func is None: + block = None + else: + block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) + core.BNFreeFunction(func) + else: + block = None + self.perform_set_current_basic_block(block) + except: + log.log_error(traceback.format_exc()) + + def _set_current_address(self, ctxt, addr): + try: + self.perform_set_current_address(addr) + except: + log.log_error(traceback.format_exc()) + + def _set_current_selection(self, ctxt, begin, end): + try: + self.perform_set_current_selection(begin, end) + except: + log.log_error(traceback.format_exc()) + + @abc.abstractmethod + def perform_destroy_instance(self): + raise NotImplementedError + + @abc.abstractmethod + def perform_execute_script_input(self, text): + return ScriptingProviderExecuteResult.InvalidScriptInput + + @abc.abstractmethod + def perform_set_current_binary_view(self, view): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_function(self, func): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_basic_block(self, block): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_address(self, addr): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_selection(self, begin, end): + raise NotImplementedError + + @property + def input_ready_state(self): + return core.BNGetScriptingInstanceInputReadyState(self.handle) + + @input_ready_state.setter + def input_ready_state(self, value): + core.BNNotifyInputReadyStateForScriptingInstance(self.handle, value.value) + + def output(self, text): + core.BNNotifyOutputForScriptingInstance(self.handle, text) + + def error(self, text): + core.BNNotifyErrorForScriptingInstance(self.handle, text) + + def execute_script_input(self, text): + return core.BNExecuteScriptInput(self.handle, text) + + def set_current_binary_view(self, view): + if view is not None: + view = view.handle + core.BNSetScriptingInstanceCurrentBinaryView(self.handle, view) + + def set_current_function(self, func): + if func is not None: + func = func.handle + core.BNSetScriptingInstanceCurrentFunction(self.handle, func) + + def set_current_basic_block(self, block): + if block is not None: + block = block.handle + core.BNSetScriptingInstanceCurrentBasicBlock(self.handle, block) + + def set_current_address(self, addr): + core.BNSetScriptingInstanceCurrentAddress(self.handle, addr) + + def set_current_selection(self, begin, end): + core.BNSetScriptingInstanceCurrentSelection(self.handle, begin, end) + + def register_output_listener(self, listener): + listener._register(self.handle) + self.listeners.append(listener) + + def unregister_output_listener(self, listener): + if listener in self.listeners: + listener._unregister(self.handle) + self.listeners.remove(listener) + + +class _ScriptingProviderMetaclass(type): + @property + def list(self): + """List all ScriptingProvider types (read-only)""" + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + result = [] + for i in xrange(0, count.value): + result.append(ScriptingProvider(types[i])) + core.BNFreeScriptingProviderList(types) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + try: + for i in xrange(0, count.value): + yield ScriptingProvider(types[i]) + finally: + core.BNFreeScriptingProviderList(types) + + def __getitem__(self, value): + startup._init_plugins() + provider = core.BNGetScriptingProviderByName(str(value)) + if provider is None: + raise KeyError("'%s' is not a valid scripting provider" % str(value)) + return ScriptingProvider(provider) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class ScriptingProvider(object): + __metaclass__ = _ScriptingProviderMetaclass + + name = None + instance_class = None + _registered_providers = [] + + def __init__(self, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNScriptingProvider) + self.__dict__["name"] = core.BNGetScriptingProviderName(handle) + + def register(self): + self._cb = core.BNScriptingProviderCallbacks() + self._cb.context = 0 + self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) + self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self._cb) + self.__class__._registered_providers.append(self) + + def _create_instance(self, ctxt): + try: + result = self.__class__.instance_class(self) + if result is None: + return None + return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return None + + def create_instance(self): + result = core.BNCreateScriptingProviderInstance(self.handle) + if result is None: + return None + return ScriptingInstance(self, handle = result) + + +class _PythonScriptingInstanceOutput(object): + def __init__(self, orig, is_error): + self.orig = orig + self.is_error = is_error + self.buffer = "" + self.encoding = 'UTF-8' + self.errors = None + self.isatty = False + self.mode = 'w' + self.name = 'PythonScriptingInstanceOutput' + self.newlines = None + + def close(self): + pass + + def closed(self): + return False + + def flush(self): + pass + + def next(self): + raise IOError("File not open for reading") + + def read(self): + raise IOError("File not open for reading") + + def readinto(self): + raise IOError("File not open for reading") + + def readlines(self): + raise IOError("File not open for reading") + + def seek(self): + pass + + def sofspace(self): + return 0 + + def truncate(self): + pass + + def tell(self): + return self.orig.tell() + + def writelines(self, lines): + return self.write('\n'.join(lines)) + + def write(self, data): + global _output_to_log + + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + if _output_to_log: + self.buffer += data + while True: + i = self.buffer.find('\n') + if i == -1: + break + line = self.buffer[:i] + self.buffer = self.buffer[i + 1:] + + if self.is_error: + log.log_error(line) + else: + log.log_info(line) + else: + self.orig.write(data) + else: + PythonScriptingInstance._interpreter.value = None + try: + if self.is_error: + interpreter.instance.error(data) + else: + interpreter.instance.output(data) + finally: + PythonScriptingInstance._interpreter.value = interpreter + + +class _PythonScriptingInstanceInput(object): + def __init__(self, orig): + self.orig = orig + + def read(self, size): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.read(size) + else: + PythonScriptingInstance._interpreter.value = None + try: + result = interpreter.read(size) + finally: + PythonScriptingInstance._interpreter.value = interpreter + return result + + def readline(self): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.readline() + else: + result = "" + while True: + data = interpreter.read(1) + result += data + if (len(data) == 0) or (data == "\n"): + break + return result + + +class PythonScriptingInstance(ScriptingInstance): + _interpreter = threading.local() + + class InterpreterThread(threading.Thread): + def __init__(self, instance): + super(PythonScriptingInstance.InterpreterThread, self).__init__() + self.instance = instance + self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} + self.interpreter = code.InteractiveInterpreter(self.locals) + self.event = threading.Event() + self.daemon = True + + # Latest selections from UI + self.current_view = None + self.current_func = None + self.current_block = None + self.current_addr = 0 + self.current_selection_begin = 0 + self.current_selection_end = 0 + + # Selections that were current as of last issued command + self.active_view = None + self.active_func = None + self.active_block = None + self.active_addr = 0 + self.active_selection_begin = 0 + self.active_selection_end = 0 + + self.locals["get_selected_data"] = self.get_selected_data + self.locals["write_at_cursor"] = self.write_at_cursor + + self.exit = False + self.code = None + self.input = "" + + self.interpreter.runsource("from binaryninja import *\n") + + def execute(self, code): + self.code = code + self.event.set() + + def add_input(self, data): + self.input += data + self.event.set() + + def end(self): + self.exit = True + self.event.set() + + def read(self, size): + while not self.exit: + if len(self.input) > size: + result = self.input[:size] + self.input = self.input[size:] + return result + elif len(self.input) > 0: + result = self.input + self.input = "" + return result + self.instance.input_ready_state = ScriptingProviderInputReadyState.ReadyForScriptProgramInput + self.event.wait() + self.event.clear() + return "" + + def run(self): + while not self.exit: + self.event.wait() + self.event.clear() + if self.exit: + break + if self.code is not None: + self.instance.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + code = self.code + self.code = None + + PythonScriptingInstance._interpreter.value = self + try: + self.active_view = self.current_view + self.active_func = self.current_func + self.active_block = self.current_block + self.active_addr = self.current_addr + self.active_selection_begin = self.current_selection_begin + self.active_selection_end = self.current_selection_end + + self.locals["current_view"] = self.active_view + self.locals["bv"] = self.active_view + self.locals["current_function"] = self.active_func + self.locals["current_basic_block"] = self.active_block + self.locals["current_address"] = self.active_addr + self.locals["here"] = self.active_addr + 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 = ScriptingProviderInputReadyState.ReadyForScriptExecution + + def get_selected_data(self): + if self.active_view is None: + return None + length = self.active_selection_end - self.active_selection_begin + return self.active_view.read(self.active_selection_begin, length) + + def write_at_cursor(self, data): + if self.active_view is None: + return 0 + selected_length = self.active_selection_end - self.active_selection_begin + data = str(data) + if (len(data) == selected_length) or (selected_length == 0): + return self.active_view.write(self.active_selection_begin, data) + else: + self.active_view.remove(self.active_selection_begin, selected_length) + return self.active_view.insert(self.active_selection_begin, data) + + def __init__(self, provider): + super(PythonScriptingInstance, self).__init__(provider) + self.interpreter = PythonScriptingInstance.InterpreterThread(self) + self.interpreter.start() + self.queued_input = "" + self.input_ready_state = ScriptingProviderInputReadyState.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 == ScriptingProviderInputReadyState.NotReadyForInput: + return ScriptingProviderExecuteResult.InvalidScriptInput + + if self.input_ready_state == ScriptingProviderInputReadyState.ReadyForScriptProgramInput: + if len(text) == 0: + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + self.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + self.interpreter.add_input(text) + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + + try: + result = code.compile_command(text) + except: + result = False + + if result is None: + # Command is not complete, ask for more input + return ScriptingProviderExecuteResult.IncompleteScriptInput + + self.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + self.interpreter.execute(text) + return ScriptingProviderExecuteResult.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 + + +class PythonScriptingProvider(ScriptingProvider): + name = "Python" + instance_class = PythonScriptingInstance + + +PythonScriptingProvider().register() +# Wrap stdin/stdout/stderr for Python scripting provider implementation +original_stdin = sys.stdin +original_stdout = sys.stdout +original_stderr = sys.stderr + +sys.stdin = _PythonScriptingInstanceInput(sys.stdin) +sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) +sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) diff --git a/python/startup.py b/python/startup.py new file mode 100644 index 00000000..809f185b --- /dev/null +++ b/python/startup.py @@ -0,0 +1,34 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import _binaryninjacore as core + + +_plugin_init = False + + +def _init_plugins(): + global _plugin_init + if not _plugin_init: + _plugin_init = True + core.BNInitCorePlugins() + core.BNInitUserPlugins() + if not core.BNIsLicenseValidated(): + raise RuntimeError("License is not valid. Please supply a valid license.") diff --git a/python/transform.py b/python/transform.py new file mode 100644 index 00000000..40382c69 --- /dev/null +++ b/python/transform.py @@ -0,0 +1,237 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes +import abc + +# Binary Ninja components +import _binaryninjacore as core +from enums import TransformType +import startup +import log +import databuffer + + +class _TransformMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + xforms = core.BNGetTransformTypeList(count) + result = [] + for i in xrange(0, count.value): + result.append(Transform(xforms[i])) + core.BNFreeTransformTypeList(xforms) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + xforms = core.BNGetTransformTypeList(count) + try: + for i in xrange(0, count.value): + yield Transform(xforms[i]) + finally: + core.BNFreeTransformTypeList(xforms) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, name): + startup._init_plugins() + xform = core.BNGetTransformByName(name) + if xform is None: + raise KeyError("'%s' is not a valid transform" % str(name)) + return Transform(xform) + + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("transform 'name' is not defined") + if cls.long_name is None: + cls.long_name = cls.name + if cls.transform_type is None: + raise ValueError("transform 'transform_type' is not defined") + if cls.group is None: + cls.group = "" + xform = cls(None) + cls._registered_cb = xform._cb + xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) + + +class TransformParameter(object): + def __init__(self, name, long_name = None, fixed_length = 0): + self.name = name + if long_name is None: + self.long_name = name + else: + self.long_name = long_name + self.fixed_length = fixed_length + + +class Transform(object): + transform_type = None + name = None + long_name = None + group = None + parameters = [] + _registered_cb = None + __metaclass__ = _TransformMetaClass + + def __init__(self, handle): + if handle is None: + self._cb = core.BNCustomTransform() + self._cb.context = 0 + self._cb.getParameters = self._cb.getParameters.__class__(self._get_parameters) + self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters) + self._cb.decode = self._cb.decode.__class__(self._decode) + self._cb.encode = self._cb.encode.__class__(self._encode) + self._pending_param_lists = {} + self.type = self.__class__.transform_type + if not isinstance(self.type, str): + self.type = TransformType(self.type) + self.name = self.__class__.name + self.long_name = self.__class__.long_name + self.group = self.__class__.group + self.parameters = self.__class__.parameters + else: + self.handle = handle + self.type = TransformType(core.BNGetTransformType(self.handle)) + self.name = core.BNGetTransformName(self.handle) + self.long_name = core.BNGetTransformLongName(self.handle) + self.group = core.BNGetTransformGroup(self.handle) + count = ctypes.c_ulonglong() + params = core.BNGetTransformParameterList(self.handle, count) + self.parameters = [] + for i in xrange(0, count.value): + self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) + core.BNFreeTransformParameterList(params, count.value) + + def __repr__(self): + return "<transform: %s>" % self.name + + def __eq__(self, value): + if not isinstance(value, Transform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Transform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + def _get_parameters(self, ctxt, count): + try: + count[0] = len(self.parameters) + param_buf = (core.BNTransformParameterInfo * len(self.parameters))() + for i in xrange(0, len(self.parameters)): + param_buf[i].name = self.parameters[i].name + param_buf[i].longName = self.parameters[i].long_name + param_buf[i].fixedLength = self.parameters[i].fixed_length + result = ctypes.cast(param_buf, ctypes.c_void_p) + self._pending_param_lists[result.value] = (result, param_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_parameters(self, params, count): + try: + buf = ctypes.cast(params, ctypes.c_void_p) + if buf.value not in self._pending_param_lists: + raise ValueError("freeing parameter list that wasn't allocated") + del self._pending_param_lists[buf.value] + except: + log.log_error(traceback.format_exc()) + + def _decode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_decode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _encode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_encode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + @abc.abstractmethod + def perform_decode(self, data, params): + if self.type == TransformType.InvertingTransform: + return self.perform_encode(data, params) + return None + + @abc.abstractmethod + def perform_encode(self, data, params): + return None + + def decode(self, input_buf, params = {}): + input_buf = databuffer.DataBuffer(input_buf) + output_buf = databuffer.DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + for i in xrange(0, len(keys)): + data = databuffer.DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNDecode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) + + def encode(self, input_buf, params = {}): + input_buf = databuffer.DataBuffer(input_buf) + output_buf = databuffer.DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + for i in xrange(0, len(keys)): + data = databuffer.DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) diff --git a/python/types.py b/python/types.py new file mode 100644 index 00000000..ebaa36fc --- /dev/null +++ b/python/types.py @@ -0,0 +1,778 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType +import callingconvention +import function + + +class QualifiedName(object): + def __init__(self, name = []): + if isinstance(name, str): + self.name = [name] + elif isinstance(name, QualifiedName): + self.name = name.name + else: + self.name = name + + def __str__(self): + return "::".join(self.name) + + def __repr__(self): + return repr(str(self)) + + def __len__(self): + return len(self.name) + + def __hash__(self): + return hash(str(self)) + + def __eq__(self, other): + if isinstance(other, str): + return str(self) == other + elif isinstance(other, list): + return self.name == other + elif isinstance(other, QualifiedName): + return self.name == other.name + return False + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + if isinstance(other, QualifiedName): + return self.name < other.name + return False + + def __le__(self, other): + if isinstance(other, QualifiedName): + return self.name <= other.name + return False + + def __gt__(self, other): + if isinstance(other, QualifiedName): + return self.name > other.name + return False + + def __ge__(self, other): + if isinstance(other, QualifiedName): + return self.name >= other.name + return False + + def __cmp__(self, other): + if self == other: + return 0 + if self < other: + return -1 + return 1 + + def __getitem__(self, key): + return self.name[key] + + def __iter__(self): + return iter(self.name) + + def _get_core_struct(self): + result = core.BNQualifiedName() + name_list = (ctypes.c_char_p * len(self.name))() + for i in xrange(0, len(self.name)): + name_list[i] = self.name[i] + result.name = name_list + result.nameCount = len(self.name) + return result + + @classmethod + def _from_core_struct(cls, name): + result = [] + for i in xrange(0, name.nameCount): + result.append(name.name[i]) + return QualifiedName(result) + + +class Symbol(object): + """ + Symbols are defined as one of the following types: + + =========================== ============================================================== + SymbolType Description + =========================== ============================================================== + FunctionSymbol Symbol for Function that exists in the current binary + ImportAddressSymbol Symbol defined in the Import Address Table + ImportedFunctionSymbol Symbol for Function that is not defined in the current binary + DataSymbol Symbol for Data in the current binary + ImportedDataSymbol Symbol for Data that is not defined in the current binary + =========================== ============================================================== + """ + def __init__(self, sym_type, addr, short_name, full_name = None, raw_name = None, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNSymbol) + else: + if isinstance(sym_type, str): + sym_type = SymbolType[sym_type] + if full_name is None: + full_name = short_name + if raw_name is None: + raw_name = full_name + self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr) + + def __del__(self): + core.BNFreeSymbol(self.handle) + + def __eq__(self, value): + if not isinstance(value, Symbol): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Symbol): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def type(self): + """Symbol type (read-only)""" + return SymbolType(core.BNGetSymbolType(self.handle)) + + @property + def name(self): + """Symbol name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def short_name(self): + """Symbol short name (read-only)""" + return core.BNGetSymbolShortName(self.handle) + + @property + def full_name(self): + """Symbol full name (read-only)""" + return core.BNGetSymbolFullName(self.handle) + + @property + def raw_name(self): + """Symbol raw name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def address(self): + """Symbol address (read-only)""" + return core.BNGetSymbolAddress(self.handle) + + @property + def auto(self): + return core.BNIsSymbolAutoDefined(self.handle) + + @auto.setter + def auto(self, value): + core.BNSetSymbolAutoDefined(self.handle, value) + + def __repr__(self): + return "<%s: \"%s\" @ %#x>" % (self.type, self.full_name, self.address) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Type(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeType(self.handle) + + def __eq__(self, value): + if not isinstance(value, Type): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Type): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def type_class(self): + """Type class (read-only)""" + return TypeClass(core.BNGetTypeClass(self.handle)) + + @property + def width(self): + """Type width (read-only)""" + return core.BNGetTypeWidth(self.handle) + + @property + def alignment(self): + """Type alignment (read-only)""" + return core.BNGetTypeAlignment(self.handle) + + @property + def signed(self): + """Wether type is signed (read-only)""" + return core.BNIsTypeSigned(self.handle) + + @property + def const(self): + """Whether type is const (read-only)""" + return core.BNIsTypeConst(self.handle) + + @property + def modified(self): + """Whether type is modified (read-only)""" + return core.BNIsTypeFloatingPoint(self.handle) + + @property + def target(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def element_type(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def return_value(self): + """Return value (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def calling_convention(self): + """Calling convention (read-only)""" + result = core.BNGetTypeCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @property + def parameters(self): + """Type parameters list (read-only)""" + count = ctypes.c_ulonglong() + params = core.BNGetTypeParameters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) + core.BNFreeTypeParameterList(params, count.value) + return result + + @property + def has_variable_arguments(self): + """Whether type has variable arguments (read-only)""" + return core.BNTypeHasVariableArguments(self.handle) + + @property + def can_return(self): + """Whether type can return (read-only)""" + return core.BNFunctionTypeCanReturn(self.handle) + + @property + def structure(self): + """Structure of the type (read-only)""" + result = core.BNGetTypeStructure(self.handle) + if result is None: + return None + return Structure(result) + + @property + def enumeration(self): + """Type enumeration (read-only)""" + result = core.BNGetTypeEnumeration(self.handle) + if result is None: + return None + return Enumeration(result) + + @property + def named_type_reference(self): + """Reference to a named type (read-only)""" + result = core.BNGetTypeNamedTypeReference(self.handle) + if result is None: + return None + return NamedTypeReference(handle = result) + + @property + def count(self): + """Type count (read-only)""" + return core.BNGetTypeElementCount(self.handle) + + def __str__(self): + return core.BNGetTypeString(self.handle) + + def __repr__(self): + return "<type: %s>" % str(self) + + def get_string_before_name(self): + return core.BNGetTypeStringBeforeName(self.handle) + + def get_string_after_name(self): + return core.BNGetTypeStringAfterName(self.handle) + + @property + def tokens(self): + """Type string as a list of tokens (read-only)""" + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokens(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + + def get_tokens_before_name(self): + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokensBeforeName(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + + def get_tokens_after_name(self): + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokensAfterName(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + + @classmethod + def void(cls): + return Type(core.BNCreateVoidType()) + + @classmethod + def bool(self): + return Type(core.BNCreateBoolType()) + + @classmethod + def int(self, width, sign = True, altname=""): + """ + ``int`` class method for creating an int Type. + + :param int width: width of the integer in bytes + :param bool sign: optional variable representing signedness + """ + return Type(core.BNCreateIntegerType(width, sign, altname)) + + @classmethod + def float(self, width): + return Type(core.BNCreateFloatType(width)) + + @classmethod + def structure_type(self, structure_type): + return Type(core.BNCreateStructureType(structure_type.handle)) + + @classmethod + def named_type(self, named_type, width = 0, align = 1): + return Type(core.BNCreateNamedTypeReference(named_type.handle, width, align)) + + @classmethod + def named_type_from_type_and_id(self, type_id, name, t): + name = QualifiedName(name)._get_core_struct() + if t is not None: + t = t.handle + return Type(core.BNCreateNamedTypeReferenceFromTypeAndId(type_id, name, t)) + + @classmethod + def named_type_from_type(self, name, t): + name = QualifiedName(name)._get_core_struct() + if t is not None: + t = t.handle + return Type(core.BNCreateNamedTypeReferenceFromTypeAndId("", name, t)) + + @classmethod + def named_type_from_registered_type(self, view, name): + name = QualifiedName(name)._get_core_struct() + return Type(core.BNCreateNamedTypeReferenceFromType(view.handle, name)) + + @classmethod + def enumeration_type(self, arch, e, width=None): + if width is None: + width = arch.default_int_size + return Type(core.BNCreateEnumerationType(e.handle, width)) + + @classmethod + def pointer(self, arch, t, const=False): + return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) + + @classmethod + def array(self, t, count): + return Type(core.BNCreateArrayType(t.handle, count)) + + @classmethod + def function(self, ret, params, calling_convention=None, variable_arguments=False): + """ + ``function`` class method for creating an function Type. + + :param Type ret: width of the integer in bytes + :param list(Type) params: list of parameter Types + :param CallingConvention calling_convention: optional argument for function calling convention + :param bool variable_arguments: optional argument for functions that have a variable number of arguments + """ + 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].type = params[i].handle + else: + param_buf[i].name = params[i][1] + param_buf[i].type = params[i][0] + if calling_convention is not None: + calling_convention = calling_convention.handle + return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), + variable_arguments)) + + @classmethod + def generate_auto_type_id(self, source, name): + name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoTypeId(source, name) + + @classmethod + def generate_auto_demangled_type_id(self, name): + name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoDemangledTypeId(name) + + @classmethod + def get_auto_demanged_type_id_source(self): + return core.BNGetAutoDemangledTypeIdSource() + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class NamedTypeReference(object): + def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, type_id = None, name = None, handle = None): + if handle is None: + self.handle = core.BNCreateNamedType() + core.BNSetTypeReferenceClass(self.handle, type_class) + if type_id is not None: + core.BNSetTypeReferenceId(self.handle, type_id) + if name is not None: + name = QualifiedName(name)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, name) + else: + self.handle = handle + + def __del__(self): + core.BNFreeNamedTypeReference(self.handle) + + def __eq__(self, value): + if not isinstance(value, NamedTypeReference): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, NamedTypeReference): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def type_class(self): + return NamedTypeReferenceClass(core.BNGetTypeReferenceClass(self.handle)) + + @type_class.setter + def type_class(self, value): + core.BNSetTypeReferenceClass(self.handle, value) + + @property + def type_id(self): + return core.BNGetTypeReferenceId(self.handle) + + @type_id.setter + def type_id(self, value): + core.BNSetTypeReferenceId(self.handle, value) + + @property + def name(self): + name = core.BNGetTypeReferenceName(self.handle) + result = QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + return result + + @name.setter + def name(self, value): + value = QualifiedName(value)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, value) + + def __repr__(self): + if self.type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: + return "<named type: typedef %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.StructNamedTypeClass: + return "<named type: struct %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.UnionNamedTypeClass: + return "<named type: union %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.EnumNamedTypeClass: + return "<named type: enum %s>" % str(self.name) + return "<named type: unknown %s>" % str(self.name) + + @classmethod + def generate_auto_type_ref(self, type_class, source, name): + type_id = Type.generate_auto_type_id(source, name) + return NamedTypeReference(type_class, type_id, name) + + @classmethod + def generate_auto_demangled_type_ref(self, type_class, name): + type_id = Type.generate_auto_demangled_type_id(name) + return NamedTypeReference(type_class, type_id, name) + + +class StructureMember(object): + def __init__(self, t, name, offset): + self.type = t + self.name = name + self.offset = offset + + def __repr__(self): + if len(self.name) == 0: + return "<member: %s, offset %#x>" % (str(self.type), self.offset) + return "<%s %s%s, offset %#x>" % (self.type.get_string_before_name(), self.name, + self.type.get_string_after_name(), self.offset) + + +class Structure(object): + def __init__(self, handle=None): + if handle is None: + self.handle = core.BNCreateStructure() + else: + self.handle = handle + + def __del__(self): + core.BNFreeStructure(self.handle) + + def __eq__(self, value): + if not isinstance(value, Structure): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Structure): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def members(self): + """Structure member list (read-only)""" + count = ctypes.c_ulonglong() + members = core.BNGetStructureMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), + members[i].name, members[i].offset)) + core.BNFreeStructureMemberList(members, count.value) + return result + + @property + def width(self): + """Structure width""" + return core.BNGetStructureWidth(self.handle) + + @width.setter + def width(self, new_width): + core.BNSetStructureWidth(self.handle, new_width) + + @property + def alignment(self): + """Structure alignment""" + return core.BNGetStructureAlignment(self.handle) + + @alignment.setter + def alignment(self, align): + core.BNSetStructureAlignment(self.handle, align) + + @property + def packed(self): + return core.BNIsStructurePacked(self.handle) + + @packed.setter + def packed(self, value): + core.BNSetStructurePacked(self.handle, value) + + @property + def union(self): + return core.BNIsStructureUnion(self.handle) + + @union.setter + def union(self, value): + core.BNSetStructureUnion(self.handle, value) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<struct: size %#x>" % self.width + + def append(self, t, name = ""): + core.BNAddStructureMember(self.handle, t.handle, name) + + def insert(self, offset, t, name = ""): + core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) + + def remove(self, i): + core.BNRemoveStructureMember(self.handle, i) + + def replace(self, i, t, name = ""): + core.BNReplaceStructureMember(self.handle, i, t.handle, name) + + +class EnumerationMember(object): + def __init__(self, name, value, default): + self.name = name + self.value = value + self.default = default + + def __repr__(self): + return "<%s = %#x>" % (self.name, self.value) + + +class Enumeration(object): + def __init__(self, handle=None): + if handle is None: + self.handle = core.BNCreateEnumeration() + else: + self.handle = handle + + def __del__(self): + core.BNFreeEnumeration(self.handle) + + def __eq__(self, value): + if not isinstance(value, Enumeration): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Enumeration): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def members(self): + """Enumeration member list (read-only)""" + count = ctypes.c_ulonglong() + members = core.BNGetEnumerationMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) + core.BNFreeEnumerationMemberList(members, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<enum: %s>" % repr(self.members) + + def append(self, name, value = None): + if value is None: + core.BNAddEnumerationMember(self.handle, name) + else: + core.BNAddEnumerationMemberWithValue(self.handle, name, value) + + def remove(self, i): + core.BNRemoveEnumerationMember(self.handle, i) + + def replace(self, i, name, value): + core.BNReplaceEnumerationMember(self.handle, i, name, value) + + +class TypeParserResult(object): + def __init__(self, types, variables, functions): + self.types = types + self.variables = variables + self.functions = functions + + def __repr__(self): + return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) + + +def preprocess_source(source, filename=None, include_dirs=[]): + """ + ``preprocess_source`` run the C preprocessor on the given source or source filename. + + :param str source: source to preprocess + :param str filename: optional filename to preprocess + :param list(str) include_dirs: list of string directorires to use as include directories. + :return: returns a tuple of (preprocessed_source, error_string) + :rtype: tuple(str,str) + :Example: + + >>> source = "#define TEN 10\\nint x[TEN];\\n" + >>> preprocess_source(source) + ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') + >>> + """ + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + output = ctypes.c_char_p() + errors = ctypes.c_char_p() + result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) + output_str = output.value + error_str = errors.value + core.BNFreeString(ctypes.cast(output, ctypes.POINTER(ctypes.c_byte))) + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if result: + return (output_str, error_str) + return (None, error_str) diff --git a/python/undoaction.py b/python/undoaction.py new file mode 100644 index 00000000..9f742e00 --- /dev/null +++ b/python/undoaction.py @@ -0,0 +1,99 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import json +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import ActionType +import startup +import log + + +class UndoAction(object): + name = None + action_type = None + _registered = False + _registered_cb = None + + def __init__(self, view): + self._cb = core.BNUndoAction() + if not self.__class__._registered: + raise TypeError("undo action type not registered") + action_type = self.__class__.action_type + if isinstance(action_type, str): + self._cb.type = ActionType[action_type] + else: + self._cb.type = action_type + self._cb.context = 0 + self._cb.undo = self._cb.undo.__class__(self._undo) + self._cb.redo = self._cb.redo.__class__(self._redo) + self._cb.serialize = self._cb.serialize.__class__(self._serialize) + self.view = view + + @classmethod + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("undo action 'name' not defined") + if cls.action_type is None: + raise ValueError("undo action 'action_type' not defined") + cb_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(core.BNUndoAction)) + cls._registered_cb = cb_type(cls._deserialize) + core.BNRegisterUndoActionType(cls.name, 0, cls._registered_cb) + cls._registered = True + + @classmethod + def _deserialize(cls, ctxt, data, result): + try: + action = cls.deserialize(json.loads(data)) + if action is None: + return False + result.context = action._cb.context + result.undo = action._cb.undo + result.redo = action._cb.redo + result.serialize = action._cb.serialize + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _undo(self, ctxt, view): + try: + self.undo() + except: + log.log_error(traceback.format_exc()) + return False + + def _redo(self, ctxt, view): + try: + self.redo() + except: + log.log_error(traceback.format_exc()) + return False + + def _serialize(self, ctxt): + try: + return json.dumps(self.serialize()) + except: + log.log_error(traceback.format_exc()) + return "null" diff --git a/python/update.py b/python/update.py new file mode 100644 index 00000000..6417e4a0 --- /dev/null +++ b/python/update.py @@ -0,0 +1,266 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import UpdateResult +import startup +import log + + +class _UpdateChannelMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = [] + for i in xrange(0, count.value): + result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) + core.BNFreeUpdateChannelList(channels, count.value) + return result + + @property + def active(self): + return core.BNGetActiveUpdateChannel() + + @active.setter + def active(self, value): + return core.BNSetActiveUpdateChannel(value) + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + try: + for i in xrange(0, count.value): + yield UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) + finally: + core.BNFreeUpdateChannelList(channels, count.value) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, name): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = None + for i in xrange(0, count.value): + if channels[i].name == str(name): + result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) + break + core.BNFreeUpdateChannelList(channels, count.value) + if result is None: + raise KeyError("'%s' is not a valid channel" % str(name)) + return result + + +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 + + def callback(self, ctxt, progress, total): + try: + if self.func is not None: + return self.func(progress, total) + return True + except: + log.log_error(traceback.format_exc()) + + +class UpdateChannel(object): + __metaclass__ = _UpdateChannelMetaClass + + def __init__(self, name, desc, ver): + self.name = name + self.description = desc + self.latest_version_num = ver + + @property + def versions(self): + """List of versions (read-only)""" + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = [] + for i in xrange(0, count.value): + result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + + @property + def latest_version(self): + """Latest version (read-only)""" + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = None + for i in xrange(0, count.value): + if versions[i].version == self.latest_version_num: + result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) + break + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + + @property + def updates_available(self): + """Whether updates are available (read-only)""" + errors = ctypes.c_char_p() + result = core.BNAreUpdatesAvailable(self.name, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<channel: %s>" % self.name + + def __str__(self): + return self.name + + def update_to_latest(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToLatestVersion(self.name, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return UpdateResult(result) + + +class UpdateVersion(object): + def __init__(self, channel, ver, notes, t): + self.channel = channel + self.version = ver + self.notes = notes + self.time = t + + def __repr__(self): + return "<version: %s>" % self.version + + def __str__(self): + return self.version + + def update(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToVersion(self.channel.name, self.version, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return UpdateResult(result) + + +def are_auto_updates_enabled(): + """ + ``are_auto_updates_enabled`` queries if auto updates are enabled. + + :return: boolean True if auto updates are enabled. False if they are disabled. + :rtype: bool + """ + return core.BNAreAutoUpdatesEnabled() + + +def set_auto_updates_enabled(enabled): + """ + ``set_auto_updates_enabled`` sets auto update enabled status. + + :param bool enabled: True to enable update, Flase to disable updates. + :rtype: None + """ + core.BNSetAutoUpdatesEnabled(enabled) + + +def get_time_since_last_update_check(): + """ + ``get_time_since_last_update_check`` returns the time stamp for the last time updates were checked. + + :return: time stacmp for last update check + :rtype: int + """ + return core.BNGetTimeSinceLastUpdateCheck() + + +def is_update_installation_pending(): + """ + ``is_update_installation_pending`` whether an update has been downloaded and is waiting installation + + :return: boolean True if an update is pending, false if no update is pending + :rtype: bool + """ + return core.BNIsUpdateInstallationPending() + + +def install_pending_update(): + """ + ``install_pending_update`` installs any pending updates + + :rtype: None + """ + errors = ctypes.c_char_p() + core.BNInstallPendingUpdate(errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + + +def updates_checked(): + core.BNUpdatesChecked() diff --git a/scripts/linux-setup.sh b/scripts/linux-setup.sh index 08c2a22f..2f5bd489 100755 --- a/scripts/linux-setup.sh +++ b/scripts/linux-setup.sh @@ -2,7 +2,7 @@ # Note is setup script currently does three things: # -# 1. It creates a binaryninja.desktop file in ~/.local/share/applications and +# 1. It creates a binaryninja.desktop file in ~/.local/share/applications and # copies it to the desktop # 2. It creates a .xml file to add a mime type for .bndb files. # 3. It adds a binaryninja: url handler. @@ -16,9 +16,9 @@ EXEC="${BNPATH}/binaryninja" PNG="${BNPATH}/docs/images/logo.png" EXT="bndb" SHARE=/usr/share #For system -SUDO="sudo" #For system +SUDO="sudo" #For system SHARE=~/.local/share #For user only -SUDO="" #For user only +SUDO="" #For user only DESKTOPFILE=$SHARE/applications/${APP}.desktop MIMEFILE=$SHARE/mime/packages/application-x-$APP.xml IMAGEPATH=$SHARE/pixmaps @@ -26,7 +26,7 @@ IMAGEPATH=$SHARE/pixmaps createdesktopfile() { mkdir -p $SHARE/{mime/packages,applications,pixmaps} - echo Creating .desktop file + echo Creating .desktop file # Desktop File echo "[Desktop Entry] @@ -44,7 +44,7 @@ Comment=$APPCOMMENT $SUDO update-desktop-database $SHARE/applications } -createmime() +createmime() { echo Creating MIME settings if [ ! -f $DESKTOPFILE ] @@ -65,7 +65,7 @@ createmime() #echo Copying icon #$SUDO cp $PNG $IMAGEPATH/$APP.png - #$SUDO cp $PNG $IMAGEPATH/application-x-$APP.png + $SUDO cp $PNG $IMAGEPATH/application-x-$APP.png $SUDO update-mime-database $SHARE/mime } @@ -24,6 +24,219 @@ using namespace BinaryNinja; using namespace std; +QualifiedName::QualifiedName() +{ +} + + +QualifiedName::QualifiedName(const string& name) +{ + m_name.push_back(name); +} + + +QualifiedName::QualifiedName(const vector<string>& name): m_name(name) +{ +} + + +QualifiedName::QualifiedName(const QualifiedName& name): m_name(name.m_name) +{ +} + + +QualifiedName& QualifiedName::operator=(const string& name) +{ + m_name = vector<string>{name}; + return *this; +} + + +QualifiedName& QualifiedName::operator=(const vector<string>& name) +{ + m_name = name; + return *this; +} + + +QualifiedName& QualifiedName::operator=(const QualifiedName& name) +{ + m_name = name.m_name; + return *this; +} + + +bool QualifiedName::operator==(const QualifiedName& other) const +{ + return m_name == other.m_name; +} + + +bool QualifiedName::operator!=(const QualifiedName& other) const +{ + return m_name != other.m_name; +} + + +bool QualifiedName::operator<(const QualifiedName& other) const +{ + return m_name < other.m_name; +} + + +QualifiedName QualifiedName::operator+(const QualifiedName& other) const +{ + QualifiedName result(*this); + result.m_name.insert(result.m_name.end(), other.m_name.begin(), other.m_name.end()); + return result; +} + + +string& QualifiedName::operator[](size_t i) +{ + return m_name[i]; +} + + +const string& QualifiedName::operator[](size_t i) const +{ + return m_name[i]; +} + + +vector<string>::iterator QualifiedName::begin() +{ + return m_name.begin(); +} + + +vector<string>::iterator QualifiedName::end() +{ + return m_name.end(); +} + + +vector<string>::const_iterator QualifiedName::begin() const +{ + return m_name.begin(); +} + + +vector<string>::const_iterator QualifiedName::end() const +{ + return m_name.end(); +} + + +string& QualifiedName::front() +{ + return m_name.front(); +} + + +const string& QualifiedName::front() const +{ + return m_name.front(); +} + + +string& QualifiedName::back() +{ + return m_name.back(); +} + + +const string& QualifiedName::back() const +{ + return m_name.back(); +} + + +void QualifiedName::insert(vector<string>::iterator loc, const string& name) +{ + m_name.insert(loc, name); +} + + +void QualifiedName::insert(vector<string>::iterator loc, vector<string>::iterator b, vector<string>::iterator e) +{ + m_name.insert(loc, b, e); +} + + +void QualifiedName::erase(vector<string>::iterator i) +{ + m_name.erase(i); +} + + +void QualifiedName::clear() +{ + m_name.clear(); +} + + +void QualifiedName::push_back(const string& name) +{ + m_name.push_back(name); +} + + +size_t QualifiedName::size() const +{ + return m_name.size(); +} + + +string QualifiedName::GetString() const +{ + bool first = true; + string out; + for (auto &name : m_name) + { + if (!first) + { + out += "::" + name; + } + else + { + out += name; + } + if (name.length() != 0) + first = false; + } + return out; +} + + +BNQualifiedName QualifiedName::GetAPIObject() const +{ + BNQualifiedName result; + result.nameCount = m_name.size(); + result.name = new char*[m_name.size()]; + for (size_t i = 0; i < m_name.size(); i++) + result.name[i] = BNAllocString(m_name[i].c_str()); + return result; +} + + +void QualifiedName::FreeAPIObject(BNQualifiedName* name) +{ + for (size_t i = 0; i < name->nameCount; i++) + BNFreeString(name->name[i]); + delete[] name->name; +} + + +QualifiedName QualifiedName::FromAPIObject(BNQualifiedName* name) +{ + QualifiedName result; + for (size_t i = 0; i < name->nameCount; i++) + result.push_back(name->name[i]); + return result; +} + + Type::Type(BNType* type) { m_object = type; @@ -133,30 +346,18 @@ Ref<Enumeration> Type::GetEnumeration() const } -uint64_t Type::GetElementCount() const +Ref<NamedTypeReference> Type::GetNamedTypeReference() const { - return BNGetTypeElementCount(m_object); + BNNamedTypeReference* ref = BNGetTypeNamedTypeReference(m_object); + if (ref) + return new NamedTypeReference(ref); + return nullptr; } -string Type::GetQualifiedName(const vector<string>& names) +uint64_t Type::GetElementCount() const { - bool first = true; - string out; - for (auto &name : names) - { - if (!first) - { - out += "::" + name; - } - else - { - out += name; - } - if (name.length() != 0) - first = false; - } - return out; + return BNGetTypeElementCount(m_object); } @@ -169,15 +370,11 @@ string Type::GetString() const } -string Type::GetTypeAndName(const vector<string>& nameList) const +string Type::GetTypeAndName(const QualifiedName& nameList) const { - const char ** str = new const char*[nameList.size()]; - for (size_t i = 0; i < nameList.size(); i++) - { - str[i] = nameList[i].c_str(); - } - char* outName = BNGetTypeAndName(m_object, str, nameList.size()); - delete [] str; + BNQualifiedName name = nameList.GetAPIObject(); + char* outName = BNGetTypeAndName(m_object, &name); + QualifiedName::FreeAPIObject(&name); return outName; } @@ -199,6 +396,78 @@ string Type::GetStringAfterName() const } +vector<InstructionTextToken> Type::GetTokens() const +{ + size_t count; + BNInstructionTextToken* tokens = BNGetTypeTokens(m_object, &count); + + vector<InstructionTextToken> result; + for (size_t i = 0; i < count; i++) + { + InstructionTextToken token; + token.type = tokens[i].type; + token.text = tokens[i].text; + token.value = tokens[i].value; + token.size = tokens[i].size; + token.operand = tokens[i].operand; + token.context = tokens[i].context; + token.address = tokens[i].address; + result.push_back(token); + } + + BNFreeTokenList(tokens, count); + return result; +} + + +vector<InstructionTextToken> Type::GetTokensBeforeName() const +{ + size_t count; + BNInstructionTextToken* tokens = BNGetTypeTokensBeforeName(m_object, &count); + + vector<InstructionTextToken> result; + for (size_t i = 0; i < count; i++) + { + InstructionTextToken token; + token.type = tokens[i].type; + token.text = tokens[i].text; + token.value = tokens[i].value; + token.size = tokens[i].size; + token.operand = tokens[i].operand; + token.context = tokens[i].context; + token.address = tokens[i].address; + result.push_back(token); + } + + BNFreeTokenList(tokens, count); + return result; +} + + +vector<InstructionTextToken> Type::GetTokensAfterName() const +{ + size_t count; + BNInstructionTextToken* tokens = BNGetTypeTokensAfterName(m_object, &count); + + vector<InstructionTextToken> result; + for (size_t i = 0; i < count; i++) + { + InstructionTextToken token; + token.type = tokens[i].type; + token.text = tokens[i].text; + token.value = tokens[i].value; + token.size = tokens[i].size; + token.operand = tokens[i].operand; + token.context = tokens[i].context; + token.address = tokens[i].address; + result.push_back(token); + } + + BNFreeTokenList(tokens, count); + return result; +} + + Ref<Type> Type::Duplicate() const { return new Type(BNDuplicateType(m_object)); @@ -235,9 +504,34 @@ Ref<Type> Type::StructureType(Structure* strct) } -Ref<Type> Type::UnknownNamedType(UnknownType* unknwn) +Ref<Type> Type::NamedType(NamedTypeReference* ref, size_t width, size_t align) { - return new Type(BNCreateUnknownNamedType(unknwn->GetObject())); + return new Type(BNCreateNamedTypeReference(ref->GetObject(), width, align)); +} + + +Ref<Type> Type::NamedType(const QualifiedName& name, Type* type) +{ + return NamedType("", name, type); +} + + +Ref<Type> Type::NamedType(const string& id, const QualifiedName& name, Type* type) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + Type* result = new Type(BNCreateNamedTypeReferenceFromTypeAndId(id.c_str(), &nameObj, + type ? type->GetObject() : nullptr)); + QualifiedName::FreeAPIObject(&nameObj); + return result; +} + + +Ref<Type> Type::NamedType(BinaryView* view, const QualifiedName& name) +{ + BNQualifiedName nameObj = name.GetAPIObject(); + Type* result = new Type(BNCreateNamedTypeReferenceFromType(view->GetObject(), &nameObj)); + QualifiedName::FreeAPIObject(&nameObj); + return result; } @@ -283,76 +577,129 @@ void Type::SetFunctionCanReturn(bool canReturn) } -UnknownType::UnknownType(BNUnknownType* ut, vector<string> names) +string Type::GenerateAutoTypeId(const string& source, const QualifiedName& name) { - m_object = ut; - const char ** nameList = new const char*[names.size()]; - for (size_t i = 0; i < names.size(); i++) - { - nameList[i] = names[i].c_str(); - } - BNSetUnknownTypeName(ut, nameList, names.size()); - delete [] nameList; + BNQualifiedName nameObj = name.GetAPIObject(); + char* str = BNGenerateAutoTypeId(source.c_str(), &nameObj); + string result = str; + QualifiedName::FreeAPIObject(&nameObj); + BNFreeString(str); + return result; } -void UnknownType::SetName(const vector<string>& names) +string Type::GenerateAutoDemangledTypeId(const QualifiedName& name) { - const char ** nameList = new const char*[names.size()]; - for (size_t i = 0; i < names.size(); i++) - { - nameList[i] = names[i].c_str(); - } - BNSetUnknownTypeName(m_object, nameList, names.size()); - delete [] nameList; + BNQualifiedName nameObj = name.GetAPIObject(); + char* str = BNGenerateAutoDemangledTypeId(&nameObj); + string result = str; + QualifiedName::FreeAPIObject(&nameObj); + BNFreeString(str); + return result; } -vector<string> UnknownType::GetName() const +string Type::GetAutoDemangledTypeIdSource() { - size_t size; - char** name = BNGetUnknownTypeName(m_object, &size); - vector<string> result; - for (size_t i = 0; i < size; i++) - { - result.push_back(name[i]); - BNFreeString(name[i]); - } - delete [] name; + char* str = BNGetAutoDemangledTypeIdSource(); + string result = str; + BNFreeString(str); return result; } -Structure::Structure(BNStructure* s) +NamedTypeReference::NamedTypeReference(BNNamedTypeReference* nt) { - m_object = s; + m_object = nt; } -vector<string> Structure::GetName() const +NamedTypeReference::NamedTypeReference(BNNamedTypeReferenceClass cls, const string& id, const QualifiedName& names) { - size_t size; - char** name = BNGetStructureName(m_object, &size); - vector<string> result; - for (size_t i = 0; i < size; i++) + m_object = BNCreateNamedType(); + BNSetTypeReferenceClass(m_object, cls); + if (id.size() != 0) { - result.push_back(name[i]); - BNFreeString(name[i]); + BNSetTypeReferenceId(m_object, id.c_str()); } - delete [] name; + if (names.size() != 0) + { + BNQualifiedName nameObj = names.GetAPIObject(); + BNSetTypeReferenceName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); + } +} + + +void NamedTypeReference::SetTypeClass(BNNamedTypeReferenceClass cls) +{ + BNSetTypeReferenceClass(m_object, cls); +} + + +BNNamedTypeReferenceClass NamedTypeReference::GetTypeClass() const +{ + return BNGetTypeReferenceClass(m_object); +} + + +string NamedTypeReference::GetTypeId() const +{ + char* str = BNGetTypeReferenceId(m_object); + string result = str; + BNFreeString(str); return result; } -void Structure::SetName(const vector<string>& names) +void NamedTypeReference::SetTypeId(const string& id) { - const char ** nameList = new const char*[names.size()]; - for (size_t i = 0; i < names.size(); i++) - { - nameList[i] = names[i].c_str(); - } - BNSetStructureName(m_object, nameList, names.size()); - delete [] nameList; + BNSetTypeReferenceId(m_object, id.c_str()); +} + + +void NamedTypeReference::SetName(const QualifiedName& names) +{ + BNQualifiedName nameObj = names.GetAPIObject(); + BNSetTypeReferenceName(m_object, &nameObj); + QualifiedName::FreeAPIObject(&nameObj); +} + + +QualifiedName NamedTypeReference::GetName() const +{ + BNQualifiedName name = BNGetTypeReferenceName(m_object); + QualifiedName result = QualifiedName::FromAPIObject(&name); + BNFreeQualifiedName(&name); + return result; +} + + +Ref<NamedTypeReference> NamedTypeReference::GenerateAutoTypeReference(BNNamedTypeReferenceClass cls, + const string& source, const QualifiedName& name) +{ + string id = Type::GenerateAutoTypeId(source, name); + return new NamedTypeReference(cls, id, name); +} + + +Ref<NamedTypeReference> NamedTypeReference::GenerateAutoDemangledTypeReference(BNNamedTypeReferenceClass cls, + const QualifiedName& name) +{ + string id = Type::GenerateAutoDemangledTypeId(name); + return new NamedTypeReference(cls, id, name); +} + + +Structure::Structure() +{ + m_object = BNCreateStructure(); +} + + +Structure::Structure(BNStructure* s) +{ + m_object = s; } @@ -382,12 +729,24 @@ uint64_t Structure::GetWidth() const } +void Structure::SetWidth(size_t width) +{ + BNSetStructureWidth(m_object, width); +} + + size_t Structure::GetAlignment() const { return BNGetStructureAlignment(m_object); } +void Structure::SetAlignment(size_t align) +{ + BNSetStructureAlignment(m_object, align); +} + + bool Structure::IsPacked() const { return BNIsStructurePacked(m_object); @@ -430,35 +789,15 @@ void Structure::RemoveMember(size_t idx) } -Enumeration::Enumeration(BNEnumeration* e) +void Structure::ReplaceMember(size_t idx, Type* type, const std::string& name) { - m_object = e; + BNReplaceStructureMember(m_object, idx, type->GetObject(), name.c_str()); } -vector<string> Enumeration::GetName() const -{ - vector<string> result; - size_t size; - char** name = BNGetEnumerationName(m_object, &size); - for (size_t i = 0; i < size; i++) - { - result.push_back(name[i]); - BNFreeString(name[i]); - } - delete [] name; - return result; -} - -void Enumeration::SetName(const vector<string>& names) +Enumeration::Enumeration(BNEnumeration* e) { - const char **const nameList = new const char*[names.size()]; - for (size_t i = 0; i < names.size(); i++) - { - nameList[i] = names[i].c_str(); - } - BNSetEnumerationName(m_object, nameList, names.size()); - delete [] nameList; + m_object = e; } @@ -494,6 +833,18 @@ void Enumeration::AddMemberWithValue(const string& name, uint64_t value) } +void Enumeration::RemoveMember(size_t idx) +{ + BNRemoveEnumerationMember(m_object, idx); +} + + +void Enumeration::ReplaceMember(size_t idx, const string& name, uint64_t value) +{ + BNReplaceEnumerationMember(m_object, idx, name.c_str(), value); +} + + bool BinaryNinja::PreprocessSource(const string& source, const string& fileName, string& output, string& errors, const vector<string>& includeDirs) { |
