diff options
| -rw-r--r-- | CMakeLists.txt | 34 | ||||
| -rw-r--r-- | binaryninjaapi.cpp | 12 | ||||
| -rw-r--r-- | binaryninjaapi.h | 31 | ||||
| -rw-r--r-- | binaryninjacore.h | 62 | ||||
| -rw-r--r-- | binaryview.cpp | 22 | ||||
| -rw-r--r-- | examples/llil_parser/CMakeLists.txt | 51 | ||||
| -rw-r--r-- | examples/llil_parser/README.md | 63 | ||||
| -rw-r--r-- | examples/llil_parser/inc/LowLevel_IL_Parser.h | 171 | ||||
| -rw-r--r-- | examples/llil_parser/src/LowLevel_IL_Parser.cpp | 442 | ||||
| -rw-r--r-- | metadata.cpp | 160 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/architecture.py | 8 | ||||
| -rw-r--r-- | python/basicblock.py | 14 | ||||
| -rw-r--r-- | python/binaryview.py | 126 | ||||
| -rw-r--r-- | python/callingconvention.py | 6 | ||||
| -rw-r--r-- | python/examples/angr_plugin.py | 4 | ||||
| -rw-r--r-- | python/function.py | 13 | ||||
| -rw-r--r-- | python/generator.cpp | 11 | ||||
| -rw-r--r-- | python/interaction.py | 257 | ||||
| -rw-r--r-- | python/lowlevelil.py | 5 | ||||
| -rw-r--r-- | python/mediumlevelil.py | 11 | ||||
| -rw-r--r-- | python/metadata.py | 266 | ||||
| -rw-r--r-- | python/platform.py | 12 | ||||
| -rw-r--r-- | python/types.py | 4 |
24 files changed, 1569 insertions, 217 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a635c9f2..85009adc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,37 +2,9 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) project(binaryninja-api) -add_library(binaryninjaapi STATIC - architecture.cpp - backgroundtask.cpp - basicblock.cpp - binaryninjaapi.cpp - binaryreader.cpp - binaryview.cpp - binaryviewtype.cpp - binarywriter.cpp - callingconvention.cpp - databuffer.cpp - demangle.cpp - fileaccessor.cpp - filemetadata.cpp - function.cpp - functiongraph.cpp - functiongraphblock.cpp - functionrecognizer.cpp - interaction.cpp - json/jsoncpp.cpp - log.cpp - lowlevelil.cpp - mainthread.cpp - platform.cpp - plugin.cpp - scriptingprovider.cpp - tempfile.cpp - transform.cpp - type.cpp - update.cpp - ) +file( GLOB SRCS *.cpp json/json.h json/jsoncpp.cpp json/json-forwards.h) + +add_library(binaryninjaapi STATIC ${SRCS}) set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index c425df88..aac92d7c 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -41,11 +41,13 @@ void BinaryNinja::InitUserPlugins() BNInitUserPlugins(); } + void BinaryNinja::InitRepoPlugins() { BNInitRepoPlugins(); } + string BinaryNinja::GetBundledPluginDirectory() { char* path = BNGetBundledPluginDirectory(); @@ -137,6 +139,7 @@ string BinaryNinja::GetVersionString() return result; } + string BinaryNinja::GetProduct() { char* str = BNGetProduct(); @@ -145,6 +148,7 @@ string BinaryNinja::GetProduct() return result; } + string BinaryNinja::GetProductType() { char* str = BNGetProductType(); @@ -153,11 +157,19 @@ string BinaryNinja::GetProductType() return result; } + int BinaryNinja::GetLicenseCount() { return BNGetLicenseCount(); } + +bool BinaryNinja::IsUIEnabled() +{ + return BNIsUIEnabled(); +} + + uint32_t BinaryNinja::GetBuildId() { return BNGetBuildId(); diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d69fec0d..62ebbd04 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -560,6 +560,7 @@ namespace BinaryNinja std::string GetProduct(); std::string GetProductType(); int GetLicenseCount(); + bool IsUIEnabled(); uint32_t GetBuildId(); bool AreAutoUpdatesEnabled(); @@ -1047,7 +1048,7 @@ namespace BinaryNinja \param dest the address to write len number of bytes. \param offset the virtual offset to find and read len bytes from - ....\param len the number of bytes to read from offset and write to dest + \param len the number of bytes to read from offset and write to dest */ virtual size_t PerformRead(void* dest, uint64_t offset, size_t len) { (void)dest; (void)offset; (void)len; return 0; } virtual size_t PerformWrite(uint64_t offset, const void* data, size_t len) { (void)offset; (void)data; (void)len; return 0; } @@ -1172,6 +1173,7 @@ namespace BinaryNinja void RemoveAnalysisFunction(Function* func); void CreateUserFunction(Platform* platform, uint64_t start); void RemoveUserFunction(Function* func); + void UpdateAnalysisAndWait(); void UpdateAnalysis(); void AbortAnalysis(); @@ -1299,8 +1301,9 @@ namespace BinaryNinja std::vector<BNAddressRange> GetAllocatedRanges(); - void StoreMetadata(const std::string& key, Metadata* inValue); - std::unique_ptr<Metadata> QueryMetadata(const std::string& key); + void StoreMetadata(const std::string& key, Ref<Metadata> value); + Ref<Metadata> QueryMetadata(const std::string& key); + void RemoveMetadata(const std::string& key); std::string GetStringMetadata(const std::string& key); std::vector<uint8_t> GetRawMetadata(const std::string& key); uint64_t GetUIntMetadata(const std::string& key); @@ -3114,8 +3117,15 @@ namespace BinaryNinja Metadata(const std::vector<int64_t>& data); Metadata(const std::vector<double>& data); Metadata(const std::vector<uint8_t>& data); + Metadata(const std::vector<Ref<Metadata>>& data); + Metadata(const std::map<std::string, Ref<Metadata>>& data); + Metadata(MetadataType type); virtual ~Metadata() {} + bool operator==(const Metadata& rhs); + Ref<Metadata> operator[](const std::string& key); + Ref<Metadata> operator[](size_t idx); + MetadataType GetType() const; bool GetBoolean() const; std::string GetString() const; @@ -3128,6 +3138,19 @@ namespace BinaryNinja std::vector<int64_t> GetSignedIntegerList() const; std::vector<double> GetDoubleList() const; std::vector<uint8_t> GetRaw() const; + std::vector<Ref<Metadata>> GetArray(); + std::map<std::string, Ref<Metadata>> GetKeyValueStore(); + + //For key-value data only + Ref<Metadata> Get(const std::string& key); + bool SetValueForKey(const std::string& key, Ref<Metadata> data); + void RemoveKey(const std::string& key); + + //For array data only + Ref<Metadata> Get(size_t index); + bool Append(Ref<Metadata> data); + void RemoveIndex(size_t index); + size_t Size() const; bool IsBoolean() const; bool IsString() const; @@ -3140,5 +3163,7 @@ namespace BinaryNinja bool IsSignedIntegerList() const; bool IsDoubleList() const; bool IsRaw() const; + bool IsArray() const; + bool IsKeyValueStore() const; }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index bcd5fbbf..e2eb0700 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1346,6 +1346,13 @@ extern "C" bool pointer, intermediate; }; + struct BNMetadataValueStore + { + size_t size; + char** keys; + BNMetadata** values; + }; + enum BNHighlightColorStyle { StandardHighlightColor = 0, @@ -1525,17 +1532,15 @@ extern "C" enum BNMetadataType { + InvalidDataType, BooleanDataType, StringDataType, UnsignedIntegerDataType, SignedIntegerDataType, DoubleDataType, - BooleanListDataType, - StringListDataType, - UnsignedIntegerListDataType, - SignedIntegerListDataType, - DoubleListDataType, - RawDataType + RawDataType, + KeyValueDataType, + ArrayDataType }; BINARYNINJACOREAPI char* BNAllocString(const char* contents); @@ -1552,6 +1557,7 @@ extern "C" BINARYNINJACOREAPI char* BNGetProduct(void); BINARYNINJACOREAPI char* BNGetProductType(void); BINARYNINJACOREAPI int BNGetLicenseCount(void); + BINARYNINJACOREAPI bool BNIsUIEnabled(void); BINARYNINJACOREAPI void BNRegisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); BINARYNINJACOREAPI void BNUnregisterObjectDestructionCallbacks(BNObjectDestructionCallbacks* callbacks); @@ -1952,6 +1958,7 @@ extern "C" BINARYNINJACOREAPI void BNRemoveAnalysisFunction(BNBinaryView* view, BNFunction* func); BINARYNINJACOREAPI void BNCreateUserFunction(BNBinaryView* view, BNPlatform* platform, uint64_t addr); BINARYNINJACOREAPI void BNRemoveUserFunction(BNBinaryView* view, BNFunction* func); + BINARYNINJACOREAPI void BNUpdateAnalysisAndWait(BNBinaryView* view); BINARYNINJACOREAPI void BNUpdateAnalysis(BNBinaryView* view); BINARYNINJACOREAPI void BNAbortAnalysis(BNBinaryView* view); BINARYNINJACOREAPI bool BNIsFunctionUpdateNeeded(BNFunction* func); @@ -2986,18 +2993,24 @@ extern "C" BINARYNINJACOREAPI BNMetadata* BNCreateMetadataUnsignedIntegerData(uint64_t data); BINARYNINJACOREAPI BNMetadata* BNCreateMetadataSignedIntegerData(int64_t data); BINARYNINJACOREAPI BNMetadata* BNCreateMetadataDoubleData(double data); - BINARYNINJACOREAPI BNMetadata* BNCreateMetadataBooleanListData(const bool* data, size_t size); - BINARYNINJACOREAPI BNMetadata* BNCreateMetadataStringListData(const char** data, size_t size); - BINARYNINJACOREAPI BNMetadata* BNCreateMetadataUnsignedIntegerListData(const uint64_t* data, size_t size); - BINARYNINJACOREAPI BNMetadata* BNCreateMetadataSignedIntegerListData(const int64_t* data, size_t size); - BINARYNINJACOREAPI BNMetadata* BNCreateMetadataDoubleListData(const double* data, size_t size); + BINARYNINJACOREAPI BNMetadata* BNCreateMetadataOfType(BNMetadataType type); BINARYNINJACOREAPI BNMetadata* BNCreateMetadataRawData(const uint8_t* data, size_t size); + BINARYNINJACOREAPI BNMetadata* BNCreateMetadataArray(BNMetadata** data, size_t size); + BINARYNINJACOREAPI BNMetadata* BNCreateMetadataValueStore(const char** keys, BNMetadata** values, size_t size); + + BINARYNINJACOREAPI bool BNMetadataIsEqual(BNMetadata* lhs, BNMetadata* rhs); + + BINARYNINJACOREAPI bool BNMetadataSetValueForKey(BNMetadata* data, const char* key, BNMetadata* md); + BINARYNINJACOREAPI BNMetadata* BNMetadataGetForKey(BNMetadata* data, const char* key); + BINARYNINJACOREAPI bool BNMetadataArrayAppend(BNMetadata* data, BNMetadata* md); + BINARYNINJACOREAPI void BNMetadataRemoveKey(BNMetadata* data, const char* key); + BINARYNINJACOREAPI size_t BNMetadataSize(BNMetadata* data); + BINARYNINJACOREAPI BNMetadata* BNMetadataGetForIndex(BNMetadata* data, size_t index); + BINARYNINJACOREAPI void BNMetadataRemoveIndex(BNMetadata* data, size_t index); + + BINARYNINJACOREAPI void BNFreeMetadataArray(BNMetadata** data); + BINARYNINJACOREAPI void BNFreeMetadataValueStore(BNMetadataValueStore* data); BINARYNINJACOREAPI void BNFreeMetadata(BNMetadata* data); - BINARYNINJACOREAPI void BNFreeMetadataBooleanList(bool* data); - BINARYNINJACOREAPI void BNFreeMetadataStringList(char** data, size_t size); - BINARYNINJACOREAPI void BNFreeMetadataUnsignedIntegerList(uint64_t* data); - BINARYNINJACOREAPI void BNFreeMetadataSignedIntegerList(int64_t* data); - BINARYNINJACOREAPI void BNFreeMetadataDoubleList(double* data); BINARYNINJACOREAPI void BNFreeMetadataRaw(uint8_t* data); // Retrieve Structured Data BINARYNINJACOREAPI bool BNMetadataGetBoolean(BNMetadata* data); @@ -3005,12 +3018,10 @@ extern "C" BINARYNINJACOREAPI uint64_t BNMetadataGetUnsignedInteger(BNMetadata* data); BINARYNINJACOREAPI int64_t BNMetadataGetSignedInteger(BNMetadata* data); BINARYNINJACOREAPI double BNMetadataGetDouble(BNMetadata* data); - BINARYNINJACOREAPI bool* BNMetadataGetBooleanList(BNMetadata* data, size_t* size); - BINARYNINJACOREAPI char** BNMetadataGetStringList(BNMetadata* data, size_t* size); - BINARYNINJACOREAPI uint64_t* BNMetadataGetUnsignedIntegerList(BNMetadata* data, size_t* size); - BINARYNINJACOREAPI int64_t* BNMetadataGetSignedIntegerList(BNMetadata* data, size_t* size); - BINARYNINJACOREAPI double* BNMetadataGetDoubleList(BNMetadata* data, size_t* size); BINARYNINJACOREAPI uint8_t* BNMetadataGetRaw(BNMetadata* data, size_t* size); + BINARYNINJACOREAPI BNMetadata** BNMetadataGetArray(BNMetadata* data, size_t* size); + BINARYNINJACOREAPI BNMetadataValueStore* BNMetadataGetValueStore(BNMetadata* data); + //Query type of Metadata BINARYNINJACOREAPI BNMetadataType BNMetadataGetType(BNMetadata* data); BINARYNINJACOREAPI bool BNMetadataIsBoolean(BNMetadata* data); @@ -3018,16 +3029,15 @@ extern "C" BINARYNINJACOREAPI bool BNMetadataIsUnsignedInteger(BNMetadata* data); BINARYNINJACOREAPI bool BNMetadataIsSignedInteger(BNMetadata* data); BINARYNINJACOREAPI bool BNMetadataIsDouble(BNMetadata* data); - BINARYNINJACOREAPI bool BNMetadataIsBooleanList(BNMetadata* data); - BINARYNINJACOREAPI bool BNMetadataIsStringList(BNMetadata* data); - BINARYNINJACOREAPI bool BNMetadataIsUnsignedIntegerList(BNMetadata* data); - BINARYNINJACOREAPI bool BNMetadataIsSignedIntegerList(BNMetadata* data); - BINARYNINJACOREAPI bool BNMetadataIsDoubleList(BNMetadata* data); BINARYNINJACOREAPI bool BNMetadataIsRaw(BNMetadata* data); + BINARYNINJACOREAPI bool BNMetadataIsArray(BNMetadata* data); + BINARYNINJACOREAPI bool BNMetadataIsKeyValueStore(BNMetadata* data); // Store/Query structured data to/from a BinaryView BINARYNINJACOREAPI void BNBinaryViewStoreMetadata(BNBinaryView* view, const char* key, BNMetadata* value); BINARYNINJACOREAPI BNMetadata* BNBinaryViewQueryMetadata(BNBinaryView* view, const char* key); + BINARYNINJACOREAPI void BNBinaryViewRemoveMetadata(BNBinaryView* view, const char* key); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 03764f85..23a62aec 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -872,6 +872,12 @@ void BinaryView::RemoveUserFunction(Function* func) } +void BinaryView::UpdateAnalysisAndWait() +{ + BNUpdateAnalysisAndWait(m_object); +} + + void BinaryView::UpdateAnalysis() { BNUpdateAnalysis(m_object); @@ -1325,6 +1331,10 @@ uint64_t BinaryView::GetNextDataAfterAddress(uint64_t addr) return BNGetNextDataAfterAddress(m_object, addr); } +uint64_t BinaryView::GetNextDataVariableAfterAddress(uint64_t addr) +{ + return BNGetNextDataVariableAfterAddress(m_object, addr); +} uint64_t BinaryView::GetPreviousFunctionStartBeforeAddress(uint64_t addr) { @@ -1835,20 +1845,24 @@ vector<BNAddressRange> BinaryView::GetAllocatedRanges() } -void BinaryView::StoreMetadata(const std::string& key, Metadata* inValue) +void BinaryView::StoreMetadata(const std::string& key, Ref<Metadata> inValue) { if (!inValue) return; BNBinaryViewStoreMetadata(m_object, key.c_str(), inValue->GetObject()); } -unique_ptr<Metadata> BinaryView::QueryMetadata(const std::string& key) +Ref<Metadata> BinaryView::QueryMetadata(const std::string& key) { BNMetadata* value = BNBinaryViewQueryMetadata(m_object, key.c_str()); if (!value) return nullptr; - auto a = new Metadata(value); - return unique_ptr<Metadata>(a); + return new Metadata(value); +} + +void BinaryView::RemoveMetadata(const std::string& key) +{ + BNBinaryViewRemoveMetadata(m_object, key.c_str()); } string BinaryView::GetStringMetadata(const string& key) diff --git a/examples/llil_parser/CMakeLists.txt b/examples/llil_parser/CMakeLists.txt new file mode 100644 index 00000000..5a0676e0 --- /dev/null +++ b/examples/llil_parser/CMakeLists.txt @@ -0,0 +1,51 @@ +# Mostly copied from https://github.com/Vector35/binaryninja-api/blob/dev/examples/breakpoint/CMakeLists.txt + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +project(LLIL_Parser) + +#----------------------------------------------------------------------------- +include_directories("inc/") +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) +#----------------------------------------------------------------------------- +file( GLOB_RECURSE SRCS *.cpp *.h) +#----------------------------------------------------------------------------- +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +#----------------------------------------------------------------------------- +if(WIN32) + set(BINJA_DIR "C:\\Program Files\\Vector35\\BinaryNinja" + CACHE PATH "Binary Ninja installation directory") + set(BINJA_BIN_DIR "${BINJA_DIR}") + set(BINJA_PLUGINS_DIR "$ENV{APPDATA}/Binary Ninja/plugins" + CACHE PATH "Binary Ninja user plugins directory") +elseif(APPLE) + set(BINJA_DIR "/Applications/Binary Ninja.app" + CACHE PATH "Binary Ninja installation directory") + set(BINJA_BIN_DIR "${BINJA_DIR}/Contents/MacOS") + set(BINJA_PLUGINS_DIR "$ENV{HOME}/Library/Application Support/Binary Ninja/plugins" + CACHE PATH "Binary Ninja user plugins directory") +else() + set(BINJA_DIR "$ENV{HOME}/binaryninja" + CACHE PATH "Binary Ninja installation directory") + set(BINJA_BIN_DIR "${BINJA_DIR}") + set(BINJA_PLUGINS_DIR "$ENV{HOME}/.binaryninja/plugins" + CACHE PATH "Binary Ninja user plugins directory") +endif() +#----------------------------------------------------------------------------- +add_executable (${PROJECT_NAME} ${SRCS} ) +#----------------------------------------------------------------------------- +find_library(BINJA_API_LIBRARY binaryninjaapi + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../bin ${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Release ${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Debug) +find_library(BINJA_CORE_LIBRARY binaryninjacore + HINTS ${BINJA_BIN_DIR}) +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} + ${BINJA_API_LIBRARY} + ${BINJA_CORE_LIBRARY} + ) +#----------------------------------------------------------------------------- +install (TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION Lib + ARCHIVE DESTINATION Lib) + diff --git a/examples/llil_parser/README.md b/examples/llil_parser/README.md new file mode 100644 index 00000000..07532c79 --- /dev/null +++ b/examples/llil_parser/README.md @@ -0,0 +1,63 @@ +LLIL Parser - Binary Ninja C++ API Sample +=== + +> Robert Yates | 22nd June 2017 + +LLIL Parser is a simple example for demonstrating how to use the BinaryNinja C++ API + + + +Example of building under windows from scratch +=== + +* https://cmake.org/ Required for this example +* We will be using Visual Studio 2017 however if want to use a different version simply run the `cmake -G` command to find the alternative name to use in the cmake commands below, be sure to use the Win64 version. + +Note: if you havent installed binary ninja into a default location then you will need to edit the cmake file and also the `std::string get_plugins_directory()` function in the `.cpp` file + +# Building the BinaryNinja API +``` +git clone https://github.com/Vector35/binaryninja-api.git +cd binaryninja +mkdir _build +cd _build +cmake .. -G "Visual Studio 15 2017 Win64" +cmake --build . --config Release +``` + +The objective here is to build the `binaryninjaapi.lib` This will be placed in the `bin` folder + +# Building the C++ Example + +``` +cd ../examples +mkdir _build +cd _build +cmake ../llil_parser -G "Visual Studio 15 2017 Win64" +cmake --build . --config Release +cd Release +copy "c:\Program Files\Vector35\BinaryNinja\binaryninjacore.dll" . +``` + +If you get an error about `BINJA_API_LIBRARY` check the API has built properly and `binaryninjaapi.lib` is located in the `bin` folder in the root folder of the API + +If you get an error about `BINJA_CORE_LIBRARY` then the file C:\Program Files\Vector35\BinaryNinja\binaryninjacore.lib is missing see [Create .lib file from .dll](https://adrianhenke.wordpress.com/2008/12/05/create-lib-file-from-dll/) on details about how to create this lib file from the dll file located in that directory + +> Building under the linux is almost exactly the same however you need not use the `-G` parameter and you build with the `make` command instead of `cmake --build` another important note is that i had to execute `cp ~/binaryninja/libbinaryninjacore.so.1 ~/binaryninja/libbinaryninjacore.so` before linking would work + +Note i do not have access to a MAC so i havent tested this. + +Using the example +=== + +Simply run the compiled executable with a target binary as a parameter and it will parse the LLIL from the first detected function in the target binary. + +The `void LlilParser::analysisInstruction(const BNLowLevelILInstruction& insn)` function is probably the most +function of interest for learning. + +This example is only intended for learning from the source code however if you wish to turn it into something more useful then you could add callbacks in the analysis function to keep track of when certain regs, values occur etc. + +# Disclaimer + +This was mostly figured out by myself and may not be the best way to achieve the intended desire, however i hope it serves as a starting point + diff --git a/examples/llil_parser/inc/LowLevel_IL_Parser.h b/examples/llil_parser/inc/LowLevel_IL_Parser.h new file mode 100644 index 00000000..7b3b6baa --- /dev/null +++ b/examples/llil_parser/inc/LowLevel_IL_Parser.h @@ -0,0 +1,171 @@ +#ifndef __LOWLEVEL_IL_PARSER_H_ +#define __LOWLEVEL_IL_PARSER_H_ + +#include "binaryninjacore.h" +#include "binaryninjaapi.h" +#include <map> + +std::string get_plugins_directory(); +void ShowBanner(); + +using namespace BinaryNinja; + +enum OperandPurpose +{ + kDest, + kSrc, + kConstant, + kLeft, + kRight, + kHi, + kLow, + kTargets, + kCondition, + kVector, + kOutput, + kStack, + kParam, + kDestMemory, + kSrcMemory, + kTrue, + kFalse, + kBit, + kCarry, + kFullReg, +}; + +enum OperandType +{ + kReg, + kExpr, + kFlag, + kIntList, + kInt, + kRegSsa, + kRegSsaList, + kFlagSsa, + kCond, + kFlagSsaList, +}; + +struct BNLowLevelILOperationSyntax +{ + OperandPurpose purpose; + OperandType type; +}; + + +static std::map<BNLowLevelILOperation, std::vector<BNLowLevelILOperationSyntax>> g_llilSyntaxMap = { \ +{ LLIL_NOP,{} }, \ +{ LLIL_SET_REG,{ { kDest, kReg },{ kSrc,kExpr } } }, \ +{ LLIL_SET_REG_SPLIT,{ { kHi, kReg },{ kLow,kReg },{ kSrc,kExpr } } } , \ +{ LLIL_SET_FLAG,{ { kDest, kFlag },{ kSrc,kExpr } } }, \ +{ LLIL_LOAD,{ { kSrc, kExpr } } }, \ +{ LLIL_STORE,{ { kDest, kExpr },{ kSrc,kExpr } } }, \ +{ LLIL_PUSH,{ { kSrc, kExpr } } }, \ +{ LLIL_POP,{} }, \ +{ LLIL_REG,{ { kSrc, kReg } } }, \ +{ LLIL_CONST,{ { kConstant, kInt } } }, \ +{ LLIL_CONST_PTR,{ { kConstant, kInt } } }, \ +{ LLIL_FLAG,{ { kSrc, kFlag } } }, \ +{ LLIL_FLAG_BIT,{ { kSrc, kFlag },{ kBit,kInt } } }, \ +{ LLIL_ADD,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_ADC,{ { kLeft, kExpr },{ kRight,kExpr },{ kCarry,kExpr } } }, \ +{ LLIL_SUB,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_SBB,{ { kLeft, kExpr },{ kRight,kExpr },{ kCarry,kExpr } } }, \ +{ LLIL_AND,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_OR,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_XOR,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_LSL,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_LSR,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_ASR,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_ROL,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_RLC,{ { kLeft, kExpr },{ kRight,kExpr },{ kCarry,kExpr } } }, \ +{ LLIL_ROR,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_RRC,{ { kLeft, kExpr },{ kRight,kExpr },{ kCarry,kExpr } } }, \ +{ LLIL_MUL,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MULU_DP,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MULS_DP,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_DIVU,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_DIVU_DP,{ { kHi, kExpr },{ kLow,kExpr },{ kRight,kExpr } } }, \ +{ LLIL_DIVS,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_DIVS_DP,{ { kHi, kExpr },{ kLow,kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MODU,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MODU_DP,{ { kHi, kExpr },{ kLow,kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MODS,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_MODS_DP,{ { kHi, kExpr },{ kLow,kExpr },{ kRight,kExpr } } }, \ +{ LLIL_NEG,{ { kSrc, kExpr } } }, \ +{ LLIL_NOT,{ { kSrc, kExpr } } }, \ +{ LLIL_SX,{ { kSrc, kExpr } } }, \ +{ LLIL_ZX,{ { kSrc, kExpr } } }, \ +{ LLIL_LOW_PART,{ { kSrc, kExpr } } }, \ +{ LLIL_JUMP,{ { kDest, kExpr } } }, \ +{ LLIL_JUMP_TO,{ { kDest, kExpr },{ kTargets,kIntList } } }, \ +{ LLIL_CALL,{ { kDest, kExpr } } }, \ +{ LLIL_RET,{ { kDest, kExpr } } }, \ +{ LLIL_NORET,{} }, \ +{ LLIL_IF,{ { kCondition, kExpr },{ kTrue,kInt },{ kFalse,kInt } } }, \ +{ LLIL_GOTO,{ { kDest, kInt } } }, \ +{ LLIL_FLAG_COND,{ { kCondition, kCond } } }, \ +{ LLIL_CMP_E,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_NE,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_SLT,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_ULT,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_SLE,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_ULE,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_SGE,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_UGE,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_SGT,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_CMP_UGT,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_TEST_BIT,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_BOOL_TO_INT,{ { kSrc, kExpr } } }, \ +{ LLIL_ADD_OVERFLOW,{ { kLeft, kExpr },{ kRight,kExpr } } }, \ +{ LLIL_SYSCALL,{} }, \ +{ LLIL_BP,{} }, \ +{ LLIL_TRAP,{ { kVector, kInt } } }, \ +{ LLIL_UNDEF,{} }, \ +{ LLIL_UNIMPL,{} }, \ +{ LLIL_UNIMPL_MEM,{ { kSrc, kExpr } } }, \ +{ LLIL_SET_REG_SSA,{ { kDest, kRegSsa },{ kSrc,kExpr } } }, \ +{ LLIL_IF,{ { kFullReg, kRegSsa },{ kDest,kReg },{ kSrc,kExpr } } }, \ +{ LLIL_SET_REG_SPLIT_SSA,{ { kHi, kExpr },{ kLow,kExpr },{ kSrc,kExpr } } }, \ +{ LLIL_REG_SPLIT_DEST_SSA,{ { kDest, kRegSsa } } }, \ +{ LLIL_REG_SSA,{ { kSrc, kRegSsa } } }, \ +{ LLIL_REG_SSA_PARTIAL,{ { kFullReg, kRegSsa },{ kSrc,kReg } } }, \ +{ LLIL_SET_FLAG_SSA,{ { kDest, kFlagSsa },{ kSrc,kExpr } } }, \ +{ LLIL_FLAG_SSA,{ { kSrc, kFlagSsa } } }, \ +{ LLIL_FLAG_BIT_SSA,{ { kSrc, kFlagSsa },{ kBit, kInt } } }, \ +{ LLIL_CALL_SSA,{ { kOutput, kExpr },{ kDest,kExpr },{ kStack,kExpr },{ kParam,kExpr } } }, \ +{ LLIL_SYSCALL_SSA,{ { kOutput, kExpr },{ kStack,kExpr },{ kParam,kExpr } } }, \ +{ LLIL_CALL_OUTPUT_SSA,{ { kDestMemory, kInt },{ kDest, kRegSsaList } } }, \ +{ LLIL_CALL_STACK_SSA,{ { kSrc, kRegSsa },{ kSrcMemory, kInt } } }, \ +{ LLIL_CALL_PARAM_SSA,{ { kSrc, kRegSsaList } } }, \ +{ LLIL_LOAD_SSA,{ { kSrc, kExpr },{ kSrcMemory, kInt } } }, \ +{ LLIL_STORE_SSA,{ { kDest, kExpr },{ kDestMemory,kInt },{ kSrcMemory,kInt },{ kSrc,kExpr } } }, \ +{ LLIL_REG_PHI,{ { kDest, kRegSsa },{ kSrc, kRegSsaList } } }, \ +{ LLIL_FLAG_PHI,{ { kDest, kFlagSsa },{ kSrc, kFlagSsaList } } }, \ +{ LLIL_MEM_PHI,{ { kDestMemory, kInt },{ kSrcMemory, kIntList } } }, \ +}; + + +class LlilParser +{ + +public: + LlilParser(BinaryView *bv); + const std::string getLowLevelILOperationName(const BNLowLevelILOperation id) const; + void decodeIndexInFunction(uint64_t functionAddress, int indexIl); + void decodeWholeFunction(uint64_t functionAddress); + void decodeWholeFunction(BinaryNinja::Function *function); +private: + void showIndent() const; + void analysisInstruction(const BNLowLevelILInstruction& insn); + + BinaryView *m_bv; + std::vector<BinaryNinja::Ref<BinaryNinja::Function>> m_currentFunction; + int m_tabs; + size_t m_currentInstructionId; +}; + + +#endif /* __LOWLEVEL_IL_PARSER_H_ */
\ No newline at end of file diff --git a/examples/llil_parser/src/LowLevel_IL_Parser.cpp b/examples/llil_parser/src/LowLevel_IL_Parser.cpp new file mode 100644 index 00000000..2f61b83a --- /dev/null +++ b/examples/llil_parser/src/LowLevel_IL_Parser.cpp @@ -0,0 +1,442 @@ +/* +LLIL Parser - Binary Ninja C++ API Sample + - Robert Yates - 22/JUN/17 + */ + +#include "LowLevel_IL_Parser.h" +#include <iostream> +#include <sstream> + +int main(int argc, char* argv[]) +{ + + try + { + ShowBanner(); + + + if (argc != 2) + { + printf("Usage: %s <input file>\n", argv[0]); + exit(-1); + } + + std::string inputName = argv[1]; + + SetBundledPluginDirectory(get_plugins_directory()); + InitCorePlugins(); + InitUserPlugins(); + + auto bd = BinaryData(new FileMetadata(), inputName.c_str()); + BinaryView *bv; + + for (auto type : BinaryViewType::GetViewTypes()) + { + if (type->IsTypeValidForData(&bd) && type->GetName() != "Raw") + { + bv = type->Create(&bd); + break; + } + } + + printf("[i] Starting analysis\n"); + bv->UpdateAnalysisAndWait(); + + printf("[i] Analysis done - %zd Functions\n", bv->GetAnalysisFunctionList().size()); + + if (bv->GetAnalysisFunctionList().size() < 1) + throw std::runtime_error("Error no functions found\n"); + + LlilParser myParser(bv); + myParser.decodeWholeFunction(bv->GetAnalysisFunctionList()[0]); + + /* + // Show Single LLIL in function x at index x + myParser.decodeIndexInFunction(0x407930, 0); + + // Decode a whole function by address + myParser.decodeWholeFunction(0x407930); + + // Decode all functions + for (const auto& f : bv->GetAnalysisFunctionList()) + { + // Decode a whole function by BinaryNinja::Function object + myParser.decodeWholeFunction(f); + } + */ + + } + catch (const std::exception& e) + { + printf("An Exception Occured: %s\n", e.what()); + } + + printf("[i] Finished\n"); +} + + + +LlilParser::LlilParser(BinaryView *bv) + : m_bv(bv) +{ + m_currentFunction.clear(); + m_tabs = 0; + m_currentInstructionId = 0; +} + +void LlilParser::showIndent() const +{ + for (int i = 0; i < m_tabs; i++) + printf(" "); +} + +void LlilParser::analysisInstruction(const BNLowLevelILInstruction& insn) +{ + + auto instructionSynatx = g_llilSyntaxMap.find(insn.operation); + BinaryNinja::Ref<BinaryNinja::LowLevelILFunction> llil = m_currentFunction[0]->GetLowLevelIL(); + if (instructionSynatx == g_llilSyntaxMap.end()) + throw std::runtime_error("Error unknown LLIL\n"); + + showIndent(); + printf("Instruction: %s\n", getLowLevelILOperationName(insn.operation).c_str()); + m_tabs += 3; + + int operandId = 0; + for (const auto& operand : instructionSynatx->second) + { + if (operand.type == OperandType::kExpr) + { + // In this case the value in the operands[x] field is a new instruction & expression index value + BNLowLevelILInstruction nextInstruction = (*llil)[insn.operands[operandId]]; + + analysisInstruction(nextInstruction); // recursion begins :) + } + else if (operand.type == OperandType::kReg) + { + // In this case the register id is in the first operands field and we use Arch to translate + showIndent(); + printf("Reg: %s\n", m_bv->GetDefaultArchitecture()->GetRegisterName(static_cast<uint32_t>(insn.operands[0])).c_str()); + m_tabs += 3; + } + else if (operand.type == OperandType::kInt) + { + // In this case the operand is simply a value + showIndent(); + printf("Value: %zX\n", insn.operands[0]); + m_tabs += 3; + } + else if (operand.type == OperandType::kFlag) + { + // In this case the operand is a flag + printf("Flag: %s\n", m_bv->GetDefaultArchitecture()->GetFlagName(static_cast<uint32_t>(insn.operands[0])).c_str()); + m_tabs += 3; + } + else if (operand.type == OperandType::kIntList) + { + // In this case we have an array of llil targets + std::vector<uint64_t> intList = llil->GetOperandList(llil->GetIndexForInstruction(m_currentInstructionId), operandId); + showIndent(); + printf("Target LLIL Indices: "); + for (const auto i : intList) + { + printf("%zd ", i); + } + printf("\n"); + } + else + { + printf("[e] LLIL Parser: Not Handled -> OperandPurpose: %d OperandType: %d\n", operand.purpose, operand.type); + } + + + operandId++; + } + + +} + +void LlilParser::decodeIndexInFunction(uint64_t functionAddress, int indexIl) +{ + + m_currentFunction = m_bv->GetAnalysisFunctionsForAddress(functionAddress); + if (m_currentFunction.size() < 1) + throw std::runtime_error("Error no functions at requested address\n"); + + BinaryNinja::Function *function = m_currentFunction[0]; + BinaryNinja::Ref<BinaryNinja::LowLevelILFunction> llil = function->GetLowLevelIL(); + + m_currentInstructionId = indexIl; + BNLowLevelILInstruction currentInstruction = (*llil)[llil->GetIndexForInstruction(indexIl)]; + + + analysisInstruction(currentInstruction); + m_tabs = 0; + +} + +void LlilParser::decodeWholeFunction(BinaryNinja::Function *function) +{ + m_currentFunction.clear(); + m_currentFunction.push_back(function); + + BinaryNinja::Ref<BinaryNinja::LowLevelILFunction> llil = function->GetLowLevelIL(); + + for (size_t i = 0; i < llil->GetInstructionCount(); i++) + { + + m_currentInstructionId = i; + BNLowLevelILInstruction currentInstruction = (*llil)[llil->GetIndexForInstruction(i)]; + + printf("\n[%zx][%zd]---------------------------------------------------------------------------\n", currentInstruction.address, i); + + analysisInstruction(currentInstruction); + m_tabs = 0; + } + +} + +void LlilParser::decodeWholeFunction(uint64_t functionAddress) +{ + + m_currentFunction = m_bv->GetAnalysisFunctionsForAddress(functionAddress); + if (m_currentFunction.size() < 1) + throw std::runtime_error("Error no functions at requested address or possible invalid BundledPluginDirectory\n"); + + BinaryNinja::Function *function = m_currentFunction[0]; + BinaryNinja::Ref<BinaryNinja::LowLevelILFunction> llil = function->GetLowLevelIL(); + + + + for (size_t i = 0; i < llil->GetInstructionCount(); i++) + { + m_currentInstructionId = i; + BNLowLevelILInstruction currentInstruction = (*llil)[llil->GetIndexForInstruction(i)]; + + printf("\n[%zx][%zd]---------------------------------------------------------------------------\n", currentInstruction.address, i); + + analysisInstruction(currentInstruction); + m_tabs = 0; + } + +} + +void ShowBanner() +{ + + printf (".____ .____ .___.____ __________ \n"); + printf ("| | | | | | | \\______ \\_____ _______ ______ ___________ \n"); + printf ("| | | | | | | | ___/\\__ \\\\_ __ \\/ ___// __ \\_ __ \\\n"); + printf ("| |___| |___| | |___ | | / __ \\| | \\/\\___ \\\\ ___/| | \\/\n"); + printf ("|_______ \\_______ \\___|_______ \\ |____| (____ /__| /____ >\\___ >__| \n"); + printf (" \\/ \\/ \\/ \\/ \\/ \\/ \n"); + printf("====================================================================================\n\n"); + +} + +#ifdef _WIN32 +std::string get_plugins_directory() +{ + return "C:\\Program Files\\Vector35\\BinaryNinja\\plugins\\"; +} +#elif __APPLE__ +std::string get_plugins_directory() +{ + return "/Applications/Binary Ninja.app/Contents/MacOS/plugins/"; +} +#else +std::string get_plugins_directory() +{ + return "~/binaryninja/plugins"; +} +#endif + +const std::string LlilParser::getLowLevelILOperationName(BNLowLevelILOperation id) const +{ + + switch (id) + { + case LLIL_NOP: + return "LLIL_NOP"; + case LLIL_SET_REG: + return "LLIL_SET_REG"; + case LLIL_SET_REG_SPLIT: + return "LLIL_SET_REG_SPLIT"; + case LLIL_SET_FLAG: + return "LLIL_SET_FLAG"; + case LLIL_LOAD: + return "LLIL_LOAD"; + case LLIL_STORE: + return "LLIL_STORE"; + case LLIL_PUSH: + return "LLIL_PUSH"; + case LLIL_POP: + return "LLIL_POP"; + case LLIL_REG: + return "LLIL_REG"; + case LLIL_CONST: + return "LLIL_CONST"; + case LLIL_CONST_PTR: + return "LLIL_CONST_PTR"; + case LLIL_FLAG: + return "LLIL_FLAG"; + case LLIL_FLAG_BIT: + return "LLIL_FLAG_BIT"; + case LLIL_ADD: + return "LLIL_ADD"; + case LLIL_ADC: + return "LLIL_ADC"; + case LLIL_SUB: + return "LLIL_SUB"; + case LLIL_SBB: + return "LLIL_SBB"; + case LLIL_AND: + return "LLIL_AND"; + case LLIL_OR: + return "LLIL_OR"; + case LLIL_XOR: + return "LLIL_XOR"; + case LLIL_LSL: + return "LLIL_LSL"; + case LLIL_LSR: + return "LLIL_LSR"; + case LLIL_ASR: + return "LLIL_ASR"; + case LLIL_ROL: + return "LLIL_ROL"; + case LLIL_RLC: + return "LLIL_RLC"; + case LLIL_ROR: + return "LLIL_ROR"; + case LLIL_RRC: + return "LLIL_RRC"; + case LLIL_MUL: + return "LLIL_MUL"; + case LLIL_MULU_DP: + return "LLIL_MULU_DP"; + case LLIL_MULS_DP: + return "LLIL_MULS_DP"; + case LLIL_DIVU: + return "LLIL_DIVU"; + case LLIL_DIVU_DP: + return "LLIL_DIVU_DP"; + case LLIL_DIVS: + return "LLIL_DIVS"; + case LLIL_DIVS_DP: + return "LLIL_DIVS_DP"; + case LLIL_MODU: + return "LLIL_MODU"; + case LLIL_MODU_DP: + return "LLIL_MODU_DP"; + case LLIL_MODS: + return "LLIL_MODS"; + case LLIL_MODS_DP: + return "LLIL_MODS_DP"; + case LLIL_NEG: + return "LLIL_NEG"; + case LLIL_NOT: + return "LLIL_NOT"; + case LLIL_SX: + return "LLIL_SX"; + case LLIL_ZX: + return "LLIL_ZX"; + case LLIL_LOW_PART: + return "LLIL_LOW_PART"; + case LLIL_JUMP: + return "LLIL_JUMP"; + case LLIL_JUMP_TO: + return "LLIL_JUMP_TO"; + case LLIL_CALL: + return "LLIL_CALL"; + case LLIL_RET: + return "LLIL_RET"; + case LLIL_NORET: + return "LLIL_NORET"; + case LLIL_IF: + return "LLIL_IF"; + case LLIL_GOTO: + return "LLIL_GOTO"; + case LLIL_FLAG_COND: + return "LLIL_FLAG_COND"; + case LLIL_CMP_E: + return "LLIL_CMP_E"; + case LLIL_CMP_NE: + return "LLIL_CMP_NE"; + case LLIL_CMP_SLT: + return "LLIL_CMP_SLT"; + case LLIL_CMP_ULT: + return "LLIL_CMP_ULT"; + case LLIL_CMP_SLE: + return "LLIL_CMP_SLE"; + case LLIL_CMP_ULE: + return "LLIL_CMP_ULE"; + case LLIL_CMP_SGE: + return "LLIL_CMP_SGE"; + case LLIL_CMP_UGE: + return "LLIL_CMP_UGE"; + case LLIL_CMP_SGT: + return "LLIL_CMP_SGT"; + case LLIL_CMP_UGT: + return "LLIL_CMP_UGT"; + case LLIL_TEST_BIT: + return "LLIL_TEST_BIT"; + case LLIL_BOOL_TO_INT: + return "LLIL_BOOL_TO_INT"; + case LLIL_ADD_OVERFLOW: + return "LLIL_ADD_OVERFLOW"; + case LLIL_SYSCALL: + return "LLIL_SYSCALL"; + case LLIL_BP: + return "LLIL_BP"; + case LLIL_TRAP: + return "LLIL_TRAP"; + case LLIL_UNDEF: + return "LLIL_UNDEF"; + case LLIL_UNIMPL: + return "LLIL_UNIMPL"; + case LLIL_UNIMPL_MEM: + return "LLIL_UNIMPL_MEM"; + case LLIL_SET_REG_SSA: + return "LLIL_SET_REG_SSA"; + case LLIL_SET_REG_SSA_PARTIAL: + return "LLIL_SET_REG_SSA_PARTIAL"; + case LLIL_SET_REG_SPLIT_SSA: + return "LLIL_SET_REG_SPLIT_SSA"; + case LLIL_REG_SPLIT_DEST_SSA: + return "LLIL_REG_SPLIT_DEST_SSA"; + case LLIL_REG_SSA: + return "LLIL_REG_SSA"; + case LLIL_REG_SSA_PARTIAL: + return "LLIL_REG_SSA_PARTIAL"; + case LLIL_SET_FLAG_SSA: + return "LLIL_SET_FLAG_SSA"; + case LLIL_FLAG_SSA: + return "LLIL_FLAG_SSA"; + case LLIL_FLAG_BIT_SSA: + return "LLIL_FLAG_BIT_SSA"; + case LLIL_CALL_SSA: + return "LLIL_CALL_SSA"; + case LLIL_SYSCALL_SSA: + return "LLIL_SYSCALL_SSA"; + case LLIL_CALL_PARAM_SSA: + return "LLIL_CALL_PARAM_SSA"; + case LLIL_CALL_STACK_SSA: + return "LLIL_CALL_STACK_SSA"; + case LLIL_CALL_OUTPUT_SSA: + return "LLIL_CALL_OUTPUT_SSA"; + case LLIL_LOAD_SSA: + return "LLIL_LOAD_SSA"; + case LLIL_STORE_SSA: + return "LLIL_STORE_SSA"; + case LLIL_REG_PHI: + return "LLIL_REG_PHI"; + case LLIL_FLAG_PHI: + return "LLIL_FLAG_PHI"; + case LLIL_MEM_PHI: + return "LLIL_MEM_PHI"; + } + + return "Unknown"; + //throw std::runtime_error("GetLowLevelILOperationName Failure"); + +}
\ No newline at end of file diff --git a/metadata.cpp b/metadata.cpp index 3f01f4bb..f9c48b04 100644 --- a/metadata.cpp +++ b/metadata.cpp @@ -33,67 +33,71 @@ Metadata::Metadata(double data) m_object = BNCreateMetadataDoubleData(data); } -Metadata::Metadata(const vector<bool>& data) +Metadata::Metadata(MetadataType type) { - auto input = new bool[data.size()]; + m_object = BNCreateMetadataOfType(type); +} + +Metadata::Metadata(const vector<uint8_t>& data) +{ + auto input = new uint8_t[data.size()]; for (size_t i = 0; i < data.size(); i++) input[i] = data[i]; - m_object = BNCreateMetadataBooleanListData(input, data.size()); + m_object = BNCreateMetadataRawData(input, data.size()); delete[] input; } -Metadata::Metadata(const vector<string>& data) +Metadata::Metadata(const std::vector<Ref<Metadata>>& data) { - char** input = new char*[data.size()]; + BNMetadata** dataList = new BNMetadata*[data.size()]; for (size_t i = 0; i < data.size(); i++) - input[i] = BNAllocString(data[i].c_str()); + dataList[i] = data[i]->m_object; - m_object = BNCreateMetadataStringListData((const char**)input, data.size()); - - for (size_t i = 0; i < data.size(); i++) - BNFreeString(input[i]); - delete[] input; + m_object = BNCreateMetadataArray(dataList, data.size()); } -Metadata::Metadata(const vector<uint64_t>& data) +Metadata::Metadata(const std::map<std::string, Ref<Metadata>>& data) { - auto input = new uint64_t[data.size()]; - for (size_t i = 0; i < data.size(); i++) - input[i] = data[i]; + char** keys = new char*[data.size()]; + BNMetadata** values = new BNMetadata*[data.size()]; - m_object = BNCreateMetadataUnsignedIntegerListData(input, data.size()); - delete[] input; + size_t i = 0; + for (auto &elm : data) + { + keys[i] = BNAllocString(elm.first.c_str()); + values[i++] = elm.second->m_object; + } + m_object = BNCreateMetadataValueStore((const char**)keys, values, data.size()); + for (size_t j = 0; j < data.size(); j++) + BNFreeString(keys[j]); + delete[] keys; + delete[] values; } -Metadata::Metadata(const vector<int64_t>& data) +bool Metadata::operator==(const Metadata& rhs) { - auto input = new int64_t[data.size()]; - for (size_t i = 0; i < data.size(); i++) - input[i] = data[i]; - - m_object = BNCreateMetadataSignedIntegerListData(input, data.size()); - delete[] input; + return BNMetadataIsEqual(m_object, rhs.m_object); } -Metadata::Metadata(const vector<double>& data) +Ref<Metadata> Metadata::operator[](const std::string& key) { - auto input = new double[data.size()]; - for (size_t i = 0; i < data.size(); i++) - input[i] = data[i]; + return new Metadata(BNMetadataGetForKey(m_object, key.c_str())); +} - m_object = BNCreateMetadataDoubleListData(input, data.size()); - delete[] input; +Ref<Metadata> Metadata::operator[](size_t idx) +{ + return new Metadata(BNMetadataGetForIndex(m_object, idx)); } -Metadata::Metadata(const vector<uint8_t>& data) +bool Metadata::SetValueForKey(const string& key, Ref<Metadata> data) { - auto input = new uint8_t[data.size()]; - for (size_t i = 0; i < data.size(); i++) - input[i] = data[i]; + return BNMetadataSetValueForKey(m_object, key.c_str(), data->m_object); +} - m_object = BNCreateMetadataRawData(input, data.size()); - delete[] input; +void Metadata::RemoveKey(const string& key) +{ + return BNMetadataRemoveKey(m_object, key.c_str()); } MetadataType Metadata::GetType() const @@ -126,60 +130,49 @@ double Metadata::GetDouble() const return BNMetadataGetDouble(m_object); } -vector<bool> Metadata::GetBooleanList() const +vector<uint8_t> Metadata::GetRaw() const { size_t outSize; - bool* outList = BNMetadataGetBooleanList(m_object, &outSize); - vector<bool> result(outList, outList + outSize); - BNFreeMetadataBooleanList(outList); + uint8_t* outList = BNMetadataGetRaw(m_object, &outSize); + vector<uint8_t> result(outList, outList + outSize); + BNFreeMetadataRaw(outList); return result; } -vector<string> Metadata::GetStringList() const +vector<Ref<Metadata>> Metadata::GetArray() { - size_t outSize; - char** outList = BNMetadataGetStringList(m_object, &outSize); - vector<string> result; - for (size_t i = 0; i < outSize; i++) - result.push_back(string(outList[i])); - BNFreeMetadataStringList(outList, outSize); + size_t size = 0; + BNMetadata** data = BNMetadataGetArray(m_object, &size); + vector<Ref<Metadata>> result; + for (size_t i = 0; i < size; i++) + result.push_back(new Metadata(data[i])); return result; } -vector<uint64_t> Metadata::GetUnsignedIntegerList() const +map<string, Ref<Metadata>> Metadata::GetKeyValueStore() { - size_t outSize; - uint64_t* outList = BNMetadataGetUnsignedIntegerList(m_object, &outSize); - vector<uint64_t> result(outList, outList + outSize); - BNFreeMetadataUnsignedIntegerList(outList); + BNMetadataValueStore* data = BNMetadataGetValueStore(m_object); + map<string, Ref<Metadata>> result; + for (size_t i = 0; i < data->size; i++) + { + result[data->keys[i]] = new Metadata(data->values[i]); + } return result; } -vector<int64_t> Metadata::GetSignedIntegerList() const +bool Metadata::Append(Ref<Metadata> data) { - size_t outSize; - int64_t* outList = BNMetadataGetSignedIntegerList(m_object, &outSize); - vector<int64_t> result(outList, outList + outSize); - BNFreeMetadataSignedIntegerList(outList); - return result; + return BNMetadataArrayAppend(m_object, data->m_object); } -vector<double> Metadata::GetDoubleList() const +void Metadata::RemoveIndex(size_t index) { - size_t outSize; - double* outList = BNMetadataGetDoubleList(m_object, &outSize); - vector<double> result(outList, outList + outSize); - BNFreeMetadataDoubleList(outList); - return result; + BNMetadataRemoveIndex(m_object, index); } -vector<uint8_t> Metadata::GetRaw() const +size_t Metadata::Size() const { - size_t outSize; - uint8_t* outList = BNMetadataGetRaw(m_object, &outSize); - vector<uint8_t> result(outList, outList + outSize); - BNFreeMetadataRaw(outList); - return result; + return BNMetadataSize(m_object); } bool Metadata::IsBoolean() const @@ -207,32 +200,17 @@ bool Metadata::IsDouble() const return BNMetadataIsDouble(m_object); } -bool Metadata::IsBooleanList() const -{ - return BNMetadataIsBooleanList(m_object); -} - -bool Metadata::IsStringList() const -{ - return BNMetadataIsStringList(m_object); -} - -bool Metadata::IsUnsignedIntegerList() const -{ - return BNMetadataIsUnsignedIntegerList(m_object); -} - -bool Metadata::IsSignedIntegerList() const +bool Metadata::IsRaw() const { - return BNMetadataIsSignedIntegerList(m_object); + return BNMetadataIsRaw(m_object); } -bool Metadata::IsDoubleList() const +bool Metadata::IsArray() const { - return BNMetadataIsDoubleList(m_object); + return BNMetadataIsArray(m_object); } -bool Metadata::IsRaw() const +bool Metadata::IsKeyValueStore() const { - return BNMetadataIsRaw(m_object); + return BNMetadataIsKeyValueStore(m_object); } diff --git a/python/__init__.py b/python/__init__.py index ec25839b..b066e863 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -48,6 +48,7 @@ from .highlight import * from .scriptingprovider import * from .pluginmanager import * from .setting import * +from .metadata import * def shutdown(): diff --git a/python/architecture.py b/python/architecture.py index b9c6fcc9..fa235985 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -361,7 +361,7 @@ class Architecture(object): cc = core.BNGetArchitectureCallingConventions(self.handle, count) result = {} for i in xrange(0, count.value): - obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) result[obj.name] = obj core.BNFreeCallingConventionList(cc, count) return result @@ -898,7 +898,7 @@ class Architecture(object): :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 + :rtype: length of bytes read on success, None on failure """ raise NotImplementedError @@ -1295,7 +1295,7 @@ class Architecture(object): for i in xrange(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]] + operand_list[i].reg = self.regs[operands[i]].index elif isinstance(operands[i], lowlevelil.ILRegister): operand_list[i].constant = False operand_list[i].reg = operands[i].index @@ -1319,7 +1319,7 @@ class Architecture(object): for i in xrange(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]] + operand_list[i].reg = self.regs[operands[i]].index elif isinstance(operands[i], lowlevelil.ILRegister): operand_list[i].constant = False operand_list[i].reg = operands[i].index diff --git a/python/basicblock.py b/python/basicblock.py index c92336c5..4a5caa14 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -48,6 +48,8 @@ class BasicBlock(object): def __init__(self, view, handle): self.view = view self.handle = core.handle_of_type(handle, core.BNBasicBlock) + self._arch = None + self._func = None def __del__(self): core.BNFreeBasicBlock(self.handle) @@ -65,18 +67,26 @@ class BasicBlock(object): @property def function(self): """Basic block function (read-only)""" + if self._func is not None: + return self._func func = core.BNGetBasicBlockFunction(self.handle) if func is None: return None - return function.Function(self.view, func) + self._func = function.Function(self.view, func) + return self._func @property def arch(self): """Basic block architecture (read-only)""" + # The arch for a BasicBlock isn't going to change so just cache + # it the first time we need it + if self._arch is not None: + return self._arch arch = core.BNGetBasicBlockArchitecture(self.handle) if arch is None: return None - return architecture.Architecture(arch) + self._arch = architecture.Architecture(arch) + return self._arch @property def start(self): diff --git a/python/binaryview.py b/python/binaryview.py index 15e5d5da..31fb8d73 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -26,7 +26,8 @@ import threading # Binary Ninja components import _binaryninjacore as core -from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag +from enums import (AnalysisState, SymbolType, InstructionTextTokenType, + Endianness, ModificationStatus, StringType, SegmentFlag) import function import startup import architecture @@ -39,6 +40,7 @@ import databuffer import basicblock import types import lineardisassembly +import metadata class BinaryDataNotification(object): @@ -115,6 +117,10 @@ class AnalysisCompletionEvent(object): pass def cancel(self): + """ + .. warning: This method should only be used when the system is being + shut down and no further analysis should be done afterward. + """ self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle) @@ -1685,11 +1691,25 @@ class BinaryView(object): return core.BNSaveToFilename(self.handle, str(dest)) def register_notification(self, notify): + """ + `register_notification` provides a mechanism for receiving callbacks for various analysis events. A full + list of callbacks can be seen in :py:Class:`BinaryDataNotification`. + + :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`. + :rtype: None + """ cb = BinaryDataNotificationCallbacks(self, notify) cb._register() self.notifications[notify] = cb def unregister_notification(self, notify): + """ + `unregister_notification` unregisters the :py:Class:`BinaryDataNotification` object passed to + `register_notification` + + :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`. + :rtype: None + """ if notify in self.notifications: self.notifications[notify]._unregister() del self.notifications[notify] @@ -1801,28 +1821,7 @@ class BinaryView(object): :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() + core.BNUpdateAnalysisAndWait(self.handle) def abort_analysis(self): """ @@ -1918,11 +1917,27 @@ class BinaryView(object): return None return DataVariable(var.address, types.Type(var.type, confidence = var.typeConfidence), var.autoDiscovered) + def get_functions_containing(self, addr): + """ + ``get_functions_containing`` returns a list of functions which contain the given address or None on failure. + + :param int addr: virtual address to query. + :rtype: list of Function objects or None + """ + basic_blocks = self.get_basic_blocks_at(addr) + if len(basic_blocks) == 0: + return None + + result = [] + for block in basic_blocks: + result.append(block.function) + return result + def get_function_at(self, addr, plat=None): """ - ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: + ``get_function_at`` gets a Function object for the function that starts at virtual address ``addr``: - :param int addr: virtual address of the desired function + :param int addr: starting 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 @@ -3250,6 +3265,67 @@ class BinaryView(object): core.BNFreeStringList(outgoing_names, len(name_list)) return result + def query_metadata(self, key): + """ + `query_metadata` retrieves a metadata associated with the given key stored in the current BinaryView. + + :param string key: key to query + :rtype: metadata associated with the key + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.query_metadata("integer") + 1337L + >>> bv.store_metadata("list", [1,2,3]) + >>> bv.query_metadata("list") + [1L, 2L, 3L] + >>> bv.store_metadata("string", "my_data") + >>> bv.query_metadata("string") + 'my_data' + """ + md_handle = core.BNBinaryViewQueryMetadata(self.handle, key) + if md_handle is None: + raise KeyError(key) + return metadata.Metadata(handle=md_handle).value + + def store_metadata(self, key, md): + """ + `store_metadata` stores an object for the given key in the current BinaryView. Objects stored using + `store_metadata` can be retrieved when the database is reopend. Objects stored are not arbitrary python + objects! The values stored must be able to be held in a Metadata object. See :py:class:`Metadata` + for more information. Python objects could obviously be serialized using pickle but this intentionally + a task left to the user since there is the potential security issues. + + :param string key: key value to associate the Metadata object with + :param Varies md: object to store. + :rtype: None + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.query_metadata("integer") + 1337L + >>> bv.store_metadata("list", [1,2,3]) + >>> bv.query_metadata("list") + [1L, 2L, 3L] + >>> bv.store_metadata("string", "my_data") + >>> bv.query_metadata("string") + 'my_data' + """ + core.BNBinaryViewStoreMetadata(self.handle, key, metadata.Metadata(md).handle) + + def remove_metadata(self, key): + """ + `remove_metadata` removes the metadata associated with key from the current BinaryView. + + :param string key: key associated with metadata to remove from the BinaryView + :rtype: None + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.remove_metadata("integer") + """ + core.BNBinaryViewRemoveMetadata(self.handle, key) + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) diff --git a/python/callingconvention.py b/python/callingconvention.py index db473c53..21c8c95a 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -41,8 +41,10 @@ class CallingConvention(object): _registered_calling_conventions = [] - def __init__(self, arch, handle = None, confidence = types.max_confidence): + def __init__(self, arch=None, name=None, handle=None, confidence=types.max_confidence): if handle is None: + if arch is None or name is None: + raise ValueError("Must specify either handle or architecture and name") self.arch = arch self._pending_reg_lists = {} self._cb = core.BNCustomCallingConvention() @@ -56,7 +58,7 @@ class CallingConvention(object): 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.handle = core.BNCreateCallingConvention(arch.handle, name, self._cb) self.__class__._registered_calling_conventions.append(self) else: self.handle = handle diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py index c84373be..26f8040c 100644 --- a/python/examples/angr_plugin.py +++ b/python/examples/angr_plugin.py @@ -42,7 +42,7 @@ 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 +from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet, MessageBoxIcon # Disable warning logs as they show up as errors in the UI logging.disable(logging.WARNING) @@ -137,7 +137,7 @@ 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.", MessageBoxButtonSet.OKButtonSet, MessageBoxButtonSet.ErrorIcon) + "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxIcon.ErrorIcon) return # Start a solver thread for the path associated with the view diff --git a/python/function.py b/python/function.py index c3310b6c..f0b6faee 100644 --- a/python/function.py +++ b/python/function.py @@ -206,6 +206,12 @@ class Variable(object): def __str__(self): return self.name + def __eq__(self, other): + return self.identifier == other.identifier + + def __hash__(self): + return hash(self.identifier) + class ConstantReference(object): def __init__(self, val, size, ptr, intermediate): @@ -261,6 +267,9 @@ class Function(object): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def __hash__(self): + return hash((self.start, self.arch.name, self.platform.name)) + @classmethod def _unregister(cls, func): handle = ctypes.cast(func, ctypes.c_void_p) @@ -471,7 +480,11 @@ class Function(object): def get_comment_at(self, addr): return core.BNGetCommentForAddress(self.handle, addr) + def set_comment_at(self, addr, comment): + core.BNSetCommentForAddress(self.handle, addr, comment) + def set_comment(self, addr, comment): + """Deprecated""" core.BNSetCommentForAddress(self.handle, addr, comment) def get_low_level_il_at(self, addr, arch=None): diff --git a/python/generator.cpp b/python/generator.cpp index 6f19db66..554f82cd 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -165,8 +165,15 @@ int main(int argc, char* argv[]) // Parse API header to get type and function information 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()); + auto arch = Architecture::GetByName("generator"); + if (!arch) + { + printf("ERROR: License file validation failed (most likely)\n"); + return 1; + } + + bool ok = arch->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); + fprintf(stderr, "Errors: %s", errors.c_str()); if (!ok) return 1; diff --git a/python/interaction.py b/python/interaction.py index 60607692..979549f5 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -29,6 +29,9 @@ import log class LabelField(object): + """ + ``LabelField`` adds a text label to the display. + """ def __init__(self, text): self.text = text @@ -44,6 +47,9 @@ class LabelField(object): class SeparatorField(object): + """ + ``SeparatorField`` adds vertical separation to the display. + """ def _fill_core_struct(self, value): value.type = FormInputFieldType.SeparatorFormField @@ -55,6 +61,9 @@ class SeparatorField(object): class TextLineField(object): + """ + ``TextLineField`` Adds prompt for text string input. Result is stored in self.result as a string on completion. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -71,6 +80,10 @@ class TextLineField(object): class MultilineTextField(object): + """ + ``MultilineTextField`` add multi-line text string input field. Result is stored in self.result + as a string. This option is not supported on the command line. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -87,6 +100,9 @@ class MultilineTextField(object): class IntegerField(object): + """ + ``IntegerField`` add prompt for integer. Result is stored in self.result as an int. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -103,7 +119,15 @@ class IntegerField(object): class AddressField(object): - def __init__(self, prompt, view = None, current_address = 0): + """ + ``AddressField`` prompts the user for an address. By passing the optional view and current_address parameters + offsets can be used instead of just an address. Th reslut is stored as in int in self.result. + + Note: This API currenlty functions differently on the command line, as the view and current_address are + disregarded. Additionally where as in the ui the result defaults to hexidecimal on the command line 0x must be + specified. + """ + def __init__(self, prompt, view=None, current_address=0): self.prompt = prompt self.view = view self.current_address = current_address @@ -125,6 +149,10 @@ class AddressField(object): class ChoiceField(object): + """ + ``ChoiceField`` prompts the user to choose from the list of strings provided in ``choices``. Result is stored + in self.result as an index in to the coices array. + """ def __init__(self, prompt, choices): self.prompt = prompt self.choices = choices @@ -147,7 +175,10 @@ class ChoiceField(object): class OpenFileNameField(object): - def __init__(self, prompt, ext = ""): + """ + ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a string. + """ + def __init__(self, prompt, ext=""): self.prompt = prompt self.ext = ext self.result = None @@ -165,7 +196,10 @@ class OpenFileNameField(object): class SaveFileNameField(object): - def __init__(self, prompt, ext = "", default_name = ""): + """ + ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a string. + """ + def __init__(self, prompt, ext="", default_name=""): self.prompt = prompt self.ext = ext self.default_name = default_name @@ -185,13 +219,17 @@ class SaveFileNameField(object): class DirectoryNameField(object): - def __init__(self, prompt, default_name = ""): + """ + ``DirectoryNameField`` prompts the user to specify a directory name to open. Result is stored in self.result as + a string. + """ + 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.type = FormInputFieldType.DirectoryNameFormField value.prompt = self.prompt value.defaultName = self.default_name @@ -353,14 +391,14 @@ class InteractionHandler(object): 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]) + for j in xrange(0, fields[i].count): + choices.append(fields[i].choices[j]) 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: + elif fields[i].type == FormInputFieldType.DirectoryNameFormField: field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName)) else: field_objs.append(LabelField(fields[i].prompt)) @@ -424,22 +462,86 @@ class InteractionHandler(object): def markdown_to_html(contents): + """ + ``markdown_to_html`` converts the provided markdown to HTML. + + :param string contents: Markdown contents to convert to HTML. + :rtype: string + :Example: + >>> markdown_to_html("##Yay") + '<h2>Yay</h2>' + """ return core.BNMarkdownToHTML(contents) def show_plain_text_report(title, contents): + """ + ``show_plain_text_report`` displays contents to the user in the UI or on the command line. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str title: title to display in the UI popup. + :param str contents: plain text contents to display + :rtype: None + :Example: + >>> show_plain_text_report("title", "contents") + contents + """ core.BNShowPlainTextReport(None, title, contents) -def show_markdown_report(title, contents, plaintext = ""): +def show_markdown_report(title, contents, plaintext=""): + """ + ``show_markdown_report`` displays the markdown contents in UI applications and plaintext in command line + applications. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str contents: markdown contents to display + :param str plaintext: Plain text version to display (used on the command line) + :rtype: None + :Example: + >>> show_markdown_report("title", "##Contents", "Plain text contents") + Plain text contents + """ core.BNShowMarkdownReport(None, title, contents, plaintext) -def show_html_report(title, contents, plaintext = ""): +def show_html_report(title, contents, plaintext=""): + """ + ``show_html_report`` displays the html contents in UI applications and plaintext in command line + applications. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str contents: HTML contents to display + :param str plaintext: Plain text version to display (used on the command line) + :rtype: None + :Example" + >>> show_html_report("title", "<h1>Contents</h1>", "Plain text contents") + Plain text contents + """ core.BNShowHTMLReport(None, title, contents, plaintext) def get_text_line_input(prompt, title): + """ + ``get_text_line_input`` prompts the user to input a string with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: string containing the input without trailing newline character. + :Example: + >>> get_text_line_input("PROMPT>", "getinfo") + PROMPT> Input! + 'Input!' + """ value = ctypes.c_char_p() if not core.BNGetTextLineInput(value, prompt, title): return None @@ -449,6 +551,20 @@ def get_text_line_input(prompt, title): def get_int_input(prompt, title): + """ + ``get_int_input`` prompts the user to input a integer with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: integer value input by the user. + :Example: + >>> get_int_input("PROMPT>", "getinfo") + PROMPT> 10 + 10 + """ value = ctypes.c_longlong() if not core.BNGetIntegerInput(value, prompt, title): return None @@ -456,6 +572,20 @@ def get_int_input(prompt, title): def get_address_input(prompt, title): + """ + ``get_address_input`` prompts the user for an address with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: integer value input by the user. + :Example: + >>> get_address_input("PROMPT>", "getinfo") + PROMPT> 10 + 10L + """ value = ctypes.c_ulonglong() if not core.BNGetAddressInput(value, prompt, title, None, 0): return None @@ -463,6 +593,25 @@ def get_address_input(prompt, title): def get_choice_input(prompt, title, choices): + """ + ``get_choice_input`` prompts the user to select the one of the provided choices. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses a combo box. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :param list choices: A list of strings for the user to choose from. + :rtype: integer array index of the selected option + :Example: + >>> get_choice_input("PROMPT>", "choices", ["Yes", "No", "Maybe"]) + choices + 1) Yes + 2) No + 3) Maybe + PROMPT> 1 + 0L + """ choice_buf = (ctypes.c_char_p * len(choices))() for i in xrange(0, len(choices)): choice_buf[i] = str(choices[i]) @@ -472,7 +621,20 @@ def get_choice_input(prompt, title, choices): return value.value -def get_open_filename_input(prompt, ext = ""): +def get_open_filename_input(prompt, ext=""): + """ + ``get_open_filename_input`` prompts the user for a file name to open. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str ext: Optional, file extension + :Example: + >>> get_open_filename_input("filename:", "exe") + filename: foo.exe + 'foo.exe' + """ value = ctypes.c_char_p() if not core.BNGetOpenFileNameInput(value, prompt, ext): return None @@ -481,7 +643,22 @@ def get_open_filename_input(prompt, ext = ""): return result -def get_save_filename_input(prompt, ext = "", default_name = ""): +def get_save_filename_input(prompt, ext="", default_name=""): + """ + ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and + default_name. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str ext: Optional, file extension + :param str default_name: Optional, default file name. + :Example: + >>> get_save_filename_input("filename:", "exe", "foo.exe") + filename: foo.exe + 'foo.exe' + """ value = ctypes.c_char_p() if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): return None @@ -490,7 +667,22 @@ def get_save_filename_input(prompt, ext = "", default_name = ""): return result -def get_directory_name_input(prompt, default_name = ""): +def get_directory_name_input(prompt, default_name=""): + """ + ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing and + default_name. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str default_name: Optional, default directory name. + :rtype: str + :Example: + >>> get_directory_name_input("prompt") + prompt dirname + 'dirname' + """ value = ctypes.c_char_p() if not core.BNGetDirectoryNameInput(value, prompt, default_name): return None @@ -500,6 +692,43 @@ def get_directory_name_input(prompt, default_name = ""): def get_form_input(fields, title): + """ + ``get_from_input`` Prompts the user for a set of inputs specified in ``fields`` with given title. + The fields parameter is a list which can contain the following types: + - str - an alias for LabelField + - None - an alias for SeparatorField + - LabelField - Text output + - SeparatorField - Vertical spacing + - TextLineField - Prompt for a string value + - MultilineTextField - Prompt for multi-line string value + - IntegerField - Prompt for an integer + - AddressField - Prompt for an address + - ChoiceField - Prompt for a choice from provided options + - OpenFileNameField - Prompt for file to open + - SaveFileNameField - Prompt for file to save to + - DirectoryNameField - Prompt for directory name + This API is flexible and works both in the UI via a popup dialog and on the command line. + :params list fields: A list containing of the above specified classes, strings or None + :params str title: The title of the popup dialog. + :Example: + + >>> int_f = IntegerField("Specify Integer") + >>> tex_f = TextLineField("Specify name") + >>> choice_f = ChoiceField("Options", ["Yes", "No", "Maybe"]) + >>> get_form_input(["Get Data", None, int_f, tex_f, choice_f], "The options") + Get Data + + Specify Integer 1337 + Specify name Peter + The options + 1) Yes + 2) No + 3) Maybe + Options 1 + >>> True + >>> print tex_f.result, int_f.result, choice_f.result + Peter 1337 0 + """ value = (core.BNFormInputField * len(fields))() for i in xrange(0, len(fields)): if isinstance(fields[i], str): @@ -517,7 +746,7 @@ def get_form_input(fields, title): return True -def show_message_box(title, text, buttons = MessageBoxButtonSet.OKButtonSet, icon = MessageBoxIcon.InformationIcon): +def show_message_box(title, text, buttons=MessageBoxButtonSet.OKButtonSet, icon=MessageBoxIcon.InformationIcon): """ ``show_message_box`` Displays a configurable message box in the UI, or prompts on the console as appropriate retrieves a list of all Symbol objects of the provided symbol type in the optionally diff --git a/python/lowlevelil.py b/python/lowlevelil.py index 08ca04e6..9f532e6f 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -731,17 +731,18 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size) - def store(self, size, addr, value): + def store(self, size, addr, value, flags=None): """ ``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 + :param str flags: which flags are set by this operation :return: The expression ``[addr].size = value`` :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size) + return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size, flags=flags) def push(self, size, value): """ diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index 3377ab6a..eefbe787 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -37,6 +37,15 @@ class SSAVariable(object): def __repr__(self): return "<ssa %s version %d>" % (repr(self.var), self.version) + def __eq__(self, other): + return ( + (self.var.identifier, self.version) == + (other.var.identifier, other.version) + ) + + def __hash__(self): + return hash((self.var.identifier, self.version)) + class MediumLevelILLabel(object): def __init__(self, handle = None): @@ -142,7 +151,7 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_UNIMPL_MEM: [("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_SSA: [("dest", "var_ssa"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")], - MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "expr"), ("low", "expr"), ("src", "expr")], + MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "ssa_var"), ("low", "ssa_var"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_ALIASED: [("prev", "var_ssa_dest_and_src"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")], MediumLevelILOperation.MLIL_VAR_SSA: [("src", "var_ssa")], diff --git a/python/metadata.py b/python/metadata.py new file mode 100644 index 00000000..554bbcf4 --- /dev/null +++ b/python/metadata.py @@ -0,0 +1,266 @@ +# Copyright (c) 2015-2017 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 MetadataType + + +class Metadata(object): + def __init__(self, value=None, signed=None, raw=None, handle=None): + if handle is not None: + self.handle = handle + elif isinstance(value, int): + if signed: + self.handle = core.BNCreateMetadataSignedIntegerData(value) + else: + self.handle = core.BNCreateMetadataUnsignedIntegerData(value) + elif isinstance(value, bool): + self.handle = core.BNCreateMetadataBooleanData(value) + elif isinstance(value, str): + if raw: + buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value) + self.handle = core.BNCreateMetadataRawData(buffer, len(value)) + else: + self.handle = core.BNCreateMetadataStringData(value) + elif isinstance(value, float): + self.handle = core.BNCreateMetadataDoubleData(value) + elif isinstance(value, list): + self.handle = core.BNCreateMetadataOfType(MetadataType.ArrayDataType) + for elm in value: + md = Metadata(elm, signed, raw) + core.BNMetadataArrayAppend(self.handle, md.handle) + elif isinstance(value, dict): + self.handle = core.BNCreateMetadataOfType(MetadataType.KeyValueDataType) + for elm in value: + md = Metadata(value[elm], signed, raw) + core.BNMetadataSetValueForKey(self.handle, str(elm), md.handle) + else: + raise ValueError("List doesn't not contain type of: int, bool, str, float, list, dict") + + @property + def value(self): + if self.is_integer: + return int(self) + elif self.is_string or self.is_raw: + return str(self) + elif self.is_float: + return float(self) + elif self.is_boolean: + return bool(self) + elif self.is_array: + return list(self) + elif self.is_dict: + return self.get_dict() + raise TypeError() + + def get_dict(self): + if not self.is_dict: + raise TypeError() + result = {} + for key in self: + result[key] = self[key] + return result + + @property + def type(self): + return MetadataType(core.BNMetadataGetType(self.handle)) + + @property + def is_integer(self): + return self.is_signed_integer or self.is_unsigned_integer + + @property + def is_signed_integer(self): + return core.BNMetadataIsSignedInteger(self.handle) + + @property + def is_unsigned_integer(self): + return core.BNMetadataIsUnsignedInteger(self.handle) + + @property + def is_float(self): + return core.BNMetadataIsDouble(self.handle) + + @property + def is_boolean(self): + return core.BNMetadataIsBoolean(self.handle) + + @property + def is_string(self): + return core.BNMetadataIsString(self.handle) + + @property + def is_raw(self): + return core.BNMetadataIsRaw(self.handle) + + @property + def is_array(self): + return core.BNMetadataIsArray(self.handle) + + @property + def is_dict(self): + return core.BNMetadataIsKeyValueStore(self.handle) + + def remove(self, key_or_index): + if isinstance(key_or_index, str) and self.is_dict: + core.BNMetadataRemoveKey(self.handle, key_or_index) + elif isinstance(key_or_index, int) and self.is_array: + core.BNMetadataRemoveIndex(self.handle, key_or_index) + else: + raise TypeError("remove only valid for dict and array objects") + + def __len__(self): + if self.is_array or self.is_dict or self.is_string or self.is_raw: + return core.BNMetadataSize(self.handle) + raise Exception("Metadata object doesn't support len()") + + def __iter__(self): + if self.is_array: + for i in xrange(core.BNMetadataSize(self.handle)): + yield Metadata(handle=core.BNMetadataGetForIndex(self.handle, i)).value + elif self.is_dict: + result = core.BNMetadataGetValueStore(self.handle) + try: + for i in xrange(result.contents.size): + yield result.contents.keys[i] + finally: + core.BNFreeMetadataValueStore(result) + else: + raise Exception("Metadata object doesn't support iteration") + + def __getitem__(self, value): + if self.is_array: + if not isinstance(value, int): + raise ValueError("Metadata object only supports integers for indexing") + if value >= len(self): + raise IndexError("Index value out of range") + return Metadata(handle=core.BNMetadataGetForIndex(self.handle, value)).value + if self.is_dict: + if not isinstance(value, str): + raise ValueError("Metadata object only supports strings for indexing") + handle = core.BNMetadataGetForKey(self.handle, value) + if handle is None: + raise KeyError(value) + return Metadata(handle=handle).value + + raise NotImplementedError("Metadata object doesn't support indexing") + + def __str__(self): + if self.is_string: + return core.BNMetadataGetString(self.handle) + if self.is_raw: + length = ctypes.c_ulonglong() + length.value = 0 + native_list = core.BNMetadataGetRaw(self.handle, ctypes.byref(length)) + out_list = [] + for i in xrange(length.value): + out_list.append(native_list[i]) + core.BNFreeMetadataRaw(native_list) + return ''.join(chr(a) for a in out_list) + + raise ValueError("Metadata object not a string or raw type") + + def __int__(self): + if self.is_signed_integer: + return core.BNMetadataGetSignedInteger(self.handle) + if self.is_unsigned_integer: + return core.BNMetadataGetUnsignedInteger(self.handle) + + raise ValueError("Metadata object not of integer type") + + def __float__(self): + if not self.is_float: + raise ValueError("Metadata object is not float type") + return core.BNMetadataGetDouble(self.handle) + + def __nonzero__(self): + if not self.is_boolean: + raise ValueError("Metadata object is not boolean type") + return core.BNMetadataGetBoolean(self.handle) + + def __eq__(self, other): + if isinstance(other, int) and self.is_integer: + return int(self) == other + elif isinstance(other, str) and (self.is_string or self.is_raw): + return str(self) == other + elif isinstance(other, float) and self.is_float: + return float(self) == other + elif isinstance(other, bool) and self.is_boolean: + return bool(self) == other + elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)): + if len(self) != len(other): + return False + for a, b in zip(self, other): + if a != b: + return False + return True + elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)): + if len(self) != len(other): + return False + for a, b in zip(self, other): + if a != b or self[a] != other[b]: + return False + return True + elif isinstance(other, Metadata) and self.is_integer and other.is_integer: + return int(self) == int(other) + elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw): + return str(self) == str(other) + elif isinstance(other, Metadata) and self.is_float and other.is_float: + return float(self) == float(other) + elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean: + return bool(self) == bool(other) + raise NotImplementedError() + + def __ne__(self, other): + if isinstance(other, int) and self.is_integer: + return int(self) != other + elif isinstance(other, str) and (self.is_string or self.is_raw): + return str(self) != other + elif isinstance(other, float) and self.is_float: + return float(self) != other + elif isinstance(other, bool): + return bool(self) != other + elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)): + if len(self) != len(other): + return True + areEqual = True + for a, b in zip(self, other): + if a != b: + areEqual = False + return not areEqual + elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)): + if len(self) != len(other): + return True + for a, b in zip(self, other): + if a != b or self[a] != other[b]: + return True + return False + elif isinstance(other, Metadata) and self.is_integer and other.is_integer: + return int(self) != int(other) + elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw): + return str(self) != str(other) + elif isinstance(other, Metadata) and self.is_float and other.is_float: + return float(self) != float(other) + elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean: + return bool(self) != bool(other) diff --git a/python/platform.py b/python/platform.py index 9ba7625f..1c2fdcd3 100644 --- a/python/platform.py +++ b/python/platform.py @@ -132,7 +132,7 @@ class Platform(object): result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): @@ -150,7 +150,7 @@ class Platform(object): result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): @@ -168,7 +168,7 @@ class Platform(object): result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): @@ -186,7 +186,7 @@ class Platform(object): result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): @@ -204,7 +204,7 @@ class Platform(object): result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @system_call_convention.setter def system_call_convention(self, value): @@ -222,7 +222,7 @@ class Platform(object): cc = core.BNGetPlatformCallingConventions(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) + result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result diff --git a/python/types.py b/python/types.py index ab3fb337..47d99bee 100644 --- a/python/types.py +++ b/python/types.py @@ -279,7 +279,7 @@ class Type(object): result = core.BNGetTypeCallingConvention(self.handle) if not result.convention: return None - return callingconvention.CallingConvention(None, result, confidence = result.confidence) + return callingconvention.CallingConvention(None, handle = result, confidence = result.confidence) @property def parameters(self): @@ -854,7 +854,7 @@ class TypeParserResult(object): self.functions = functions def __repr__(self): - return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) + return "<types: %s, variables: %s, functions: %s>" % (self.types, self.variables, self.functions) def preprocess_source(source, filename=None, include_dirs=[]): |
