diff options
71 files changed, 3975 insertions, 907 deletions
@@ -5,6 +5,7 @@ Thumbs.db *.pdb *.ilk *.exp +*.pkl libbinaryninja* binaryninjacore.lib binaryninjacore.dll @@ -20,6 +21,7 @@ api-docs/build/* .env api-docs/source/binaryninja.* bin/ +suite/unit.py # CMake CMakeCache.txt CMakeFiles @@ -30,3 +32,12 @@ CTestTestfile.cmake *.pyc api-docs/source/python.rst api-docs/source/index.rst +.coverage +# Make +generator +plugins/ +python/examples/binaryninja/ +python/plugins/ +python/types/ +suite/binaryninja/ +types/ diff --git a/.gitmodules b/.gitmodules index 1962d2b4..35327f04 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "examples/x86_extension/src/asmx86"] path = examples/x86_extension/src/asmx86 url = https://github.com/Vector35/asmx86.git +[submodule "suite/binaries"] + path = suite/binaries + url = git@github.com:Vector35/BinaryTestCases.git @@ -5,6 +5,7 @@ # LIB := -L$(HOME)/binaryninja/ -lbinaryninjacore #endif +INSTALLPATH := ~/binaryninja TARGETDIR := bin TARGETNAME := libbinaryninjaapi @@ -15,7 +16,8 @@ SOURCES = $(wildcard *$(SRCEXT)) OBJECTS = $(SOURCES:.cpp=.o) json.o -CFLAGS := -c -fPIC -O2 -pipe -std=gnu++11 -Wall -W +CFLAGS := -c -fPIC -O2 -pipe -std=gnu++11 -Wall -W -Wextra -Wshadow +CPPFLAGS := -O2 -std=c++11 -Wall -W -Wextra -Wshadow ifeq ($(UNAME_S),Darwin) CC := $(shell xcrun -f g++) AR := $(shell xcrun -f ar) @@ -30,7 +32,7 @@ all: $(TARGET).a $(TARGET).a: $(OBJECTS) @mkdir -p $(TARGETDIR) $(AR) rcs $@ $^ - + %.o: %.cpp @echo " Compiling... $@ $<" $(CC) $(CFLAGS) $(INC) -c -o $@ $< @@ -38,8 +40,67 @@ $(TARGET).a: $(OBJECTS) json.o: ./json/jsoncpp.cpp ./json/json.h ./json/json-forwards.h $(CC) $(CFLAGS) -I. -c -o $@ $< +install: generate $(TARGET).a + @echo "Installing binaryninja API."; + cp -r python/* $(INSTALLPATH)/python/binaryninja + cp $(TARGET).a $(INSTALLPATH) + @echo "Done."; + +generator: python/generator.cpp $(TARGET).a + @echo "Building generator..."; + $(CC) $(CPPFLAGS) -I . $< -L$(TARGETDIR) -lbinaryninjaapi -L $(INSTALLPATH) -lbinaryninjacore -o $@ + chmod +x $@ + +generate: generator + @echo "Running generator..."; + LD_LIBRARY_PATH=$(INSTALLPATH) ./generator binaryninjacore.h python/_binaryninjacore.py python/enums.py + +python/_binaryninjacore.py: generate + +python/enums.py: generate + +python_test: environment python/_binaryninjacore.py python/enums.py + python2 suite/unit.py + python3 suite/unit.py + +oracle: environment python/_binaryninjacore.py python/enums.py + python3 suite/generator.py + +environment: environment_clean python/_binaryninjacore.py python/enums.py + @echo "Copying over libs..." + cp $(INSTALLPATH)/libbinaryninjacore.so.1 . + cp $(INSTALLPATH)/libbinaryninjacore.so.1 python/ + + @echo "Building 'binaryninja' Packages..." + mkdir -p suite/binaryninja/ + cp -r python/* suite/binaryninja/ + cp -r suite/binaryninja/ python/examples/ + + @echo "Copying Architectures Over..." + cp -r $(INSTALLPATH)/types/ . + cp -r $(INSTALLPATH)/plugins/ . + cp -r $(INSTALLPATH)/types/ python/ + cp -r $(INSTALLPATH)/plugins/ python/ + +environment_clean: + @echo "Removing 'binaryninja' Packages..." + rm -rf suite/binaryninja/ + rm -rf python/examples/binaryninja/ + + @echo "Removing libs..." + rm -f libbinaryninjacore.so.1 + rm -f python/libbinaryninjacore.so.1 + + @echo "Removing Architectures..." + rm -rf types/ + rm -rf plugins/ + rm -rf python/types/ + rm -rf python/plugins/ + clean: @echo " Cleaning..."; - $(RM) -r *.o $(TARGETDIR) + $(RM) -r *.o $(TARGETDIR) generator + +squeaky: clean environment_clean -.PHONY: clean +.PHONY: clean environment_clean squeaky python/_binaryninjacore.py python/enums.py diff --git a/architecture.cpp b/architecture.cpp index a1aaca2d..682cffbc 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -195,8 +195,8 @@ bool Architecture::GetInstructionLowLevelILCallback(void* ctxt, const uint8_t* d size_t* len, BNLowLevelILFunction* il) { Architecture* arch = (Architecture*)ctxt; - LowLevelILFunction func(il); - return arch->GetInstructionLowLevelIL(data, addr, *len, func); + Ref<LowLevelILFunction> func(new LowLevelILFunction(BNNewLowLevelILFunctionReference(il))); + return arch->GetInstructionLowLevelIL(data, addr, *len, *func); } @@ -401,8 +401,8 @@ size_t Architecture::GetFlagWriteLowLevelILCallback(void* ctxt, BNLowLevelILOper uint32_t flag, BNRegisterOrConstant* operands, size_t operandCount, BNLowLevelILFunction* il) { Architecture* arch = (Architecture*)ctxt; - LowLevelILFunction func(il); - return arch->GetFlagWriteLowLevelIL(op, size, flagWriteType, flag, operands, operandCount, func); + Ref<LowLevelILFunction> func(new LowLevelILFunction(BNNewLowLevelILFunctionReference(il))); + return arch->GetFlagWriteLowLevelIL(op, size, flagWriteType, flag, operands, operandCount, *func); } @@ -410,16 +410,16 @@ size_t Architecture::GetFlagConditionLowLevelILCallback(void* ctxt, BNLowLevelIL BNLowLevelILFunction* il) { Architecture* arch = (Architecture*)ctxt; - LowLevelILFunction func(il); - return arch->GetFlagConditionLowLevelIL(cond, semClass, func); + Ref<LowLevelILFunction> func(new LowLevelILFunction(BNNewLowLevelILFunctionReference(il))); + return arch->GetFlagConditionLowLevelIL(cond, semClass, *func); } size_t Architecture::GetSemanticFlagGroupLowLevelILCallback(void* ctxt, uint32_t semGroup, BNLowLevelILFunction* il) { Architecture* arch = (Architecture*)ctxt; - LowLevelILFunction func(il); - return arch->GetSemanticFlagGroupLowLevelIL(semGroup, func); + Ref<LowLevelILFunction> func(new LowLevelILFunction(BNNewLowLevelILFunctionReference(il))); + return arch->GetSemanticFlagGroupLowLevelIL(semGroup, *func); } diff --git a/basicblock.cpp b/basicblock.cpp index 9cf9ed56..9fc8d8aa 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -260,8 +260,8 @@ set<Ref<BasicBlock>> BasicBlock::GetIteratedDominanceFrontier(const set<Ref<Basi delete[] blockSet; set<Ref<BasicBlock>> result; - for (size_t i = 0; i < count; i++) - result.insert(new BasicBlock(BNNewBasicBlockReference(resultBlocks[i]))); + for (size_t k = 0; k < count; k++) + result.insert(new BasicBlock(BNNewBasicBlockReference(resultBlocks[k]))); BNFreeBasicBlockList(resultBlocks, count); return result; diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index f5eede03..b774cf2b 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -344,21 +344,3 @@ string BinaryNinja::GetUniqueIdentifierString() BNFreeString(str); return result; } - - -string BinaryNinja::GetLinuxCADirectory() -{ - char* str = BNGetLinuxCADirectory(); - string result = str; - BNFreeString(str); - return result; -} - - -string BinaryNinja::GetLinuxCABundlePath() -{ - char* str = BNGetLinuxCABundlePath(); - string result = str; - BNFreeString(str); - return result; -} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index dd685d2b..53e0a30d 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1044,6 +1044,25 @@ namespace BinaryNinja void Cancel(); }; + struct ActiveAnalysisInfo + { + Ref<Function> func; + uint64_t analysisTime; + size_t updateCount; + size_t submitCount; + + ActiveAnalysisInfo(Ref<Function> f, uint64_t t, size_t uc, size_t sc) : func(f), analysisTime(t), updateCount(uc), submitCount(sc) + { + } + }; + + struct AnalysisInfo + { + BNAnalysisState state; + uint64_t analysisTime; + std::vector<ActiveAnalysisInfo> activeInfo; + }; + struct DataVariable { DataVariable() { } @@ -1293,6 +1312,7 @@ namespace BinaryNinja Ref<AnalysisCompletionEvent> AddAnalysisCompletionEvent(const std::function<void()>& callback); + AnalysisInfo GetAnalysisInfo(); BNAnalysisProgress GetAnalysisProgress(); Ref<BackgroundTask> GetBackgroundAnalysisTask(); @@ -1373,6 +1393,8 @@ namespace BinaryNinja std::vector<uint8_t> GetRawMetadata(const std::string& key); uint64_t GetUIntMetadata(const std::string& key); + BNAnalysisParameters GetParametersForAnalysis(); + void SetParametersForAnalysis(BNAnalysisParameters params); uint64_t GetMaxFunctionSizeForAnalysis(); void SetMaxFunctionSizeForAnalysis(uint64_t size); }; @@ -2517,6 +2539,7 @@ namespace BinaryNinja bool IsFunctionTooLarge(); bool IsAnalysisSkipped(); + BNAnalysisSkipReason GetAnalysisSkipReason(); BNFunctionAnalysisSkipOverride GetAnalysisSkipOverride(); void SetAnalysisSkipOverride(BNFunctionAnalysisSkipOverride skip); @@ -2837,12 +2860,16 @@ namespace BinaryNinja ExprId Call(ExprId dest, const ILSourceLocation& loc = ILSourceLocation()); ExprId CallStackAdjust(ExprId dest, size_t adjust, const std::map<uint32_t, int32_t>& regStackAdjust, const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCall(ExprId dest, const ILSourceLocation& loc = ILSourceLocation()); ExprId CallSSA(const std::vector<SSARegister>& output, ExprId dest, const std::vector<ExprId>& params, const SSARegister& stack, size_t newMemoryVer, size_t prevMemoryVer, const ILSourceLocation& loc = ILSourceLocation()); ExprId SystemCallSSA(const std::vector<SSARegister>& output, const std::vector<ExprId>& params, const SSARegister& stack, size_t newMemoryVer, size_t prevMemoryVer, const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCallSSA(const std::vector<SSARegister>& output, ExprId dest, const std::vector<ExprId>& params, + const SSARegister& stack, size_t newMemoryVer, size_t prevMemoryVer, + const ILSourceLocation& loc = ILSourceLocation()); ExprId Return(size_t dest, const ILSourceLocation& loc = ILSourceLocation()); ExprId NoReturn(const ILSourceLocation& loc = ILSourceLocation()); ExprId FlagCondition(BNLowLevelILFlagCondition cond, uint32_t semClass = 0, @@ -3156,6 +3183,10 @@ namespace BinaryNinja const ILSourceLocation& loc = ILSourceLocation()); ExprId SyscallUntyped(const std::vector<Variable>& output, const std::vector<Variable>& params, ExprId stack, const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCall(const std::vector<Variable>& output, ExprId dest, const std::vector<ExprId>& params, + const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCallUntyped(const std::vector<Variable>& output, ExprId dest, const std::vector<Variable>& params, + ExprId stack, const ILSourceLocation& loc = ILSourceLocation()); ExprId CallSSA(const std::vector<SSAVariable>& output, ExprId dest, const std::vector<ExprId>& params, size_t newMemVersion, size_t prevMemVersion, const ILSourceLocation& loc = ILSourceLocation()); ExprId CallUntypedSSA(const std::vector<SSAVariable>& output, ExprId dest, @@ -3166,6 +3197,11 @@ namespace BinaryNinja ExprId SyscallUntypedSSA(const std::vector<SSAVariable>& output, const std::vector<SSAVariable>& params, size_t newMemVersion, size_t prevMemVersion, ExprId stack, const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCallSSA(const std::vector<SSAVariable>& output, ExprId dest, const std::vector<ExprId>& params, + size_t newMemVersion, size_t prevMemVersion, const ILSourceLocation& loc = ILSourceLocation()); + ExprId TailCallUntypedSSA(const std::vector<SSAVariable>& output, ExprId dest, + const std::vector<SSAVariable>& params, size_t newMemVersion, size_t prevMemVersion, + ExprId stack, const ILSourceLocation& loc = ILSourceLocation()); ExprId Return(const std::vector<ExprId>& sources, const ILSourceLocation& loc = ILSourceLocation()); ExprId NoReturn(const ILSourceLocation& loc = ILSourceLocation()); ExprId CompareEqual(size_t size, ExprId left, ExprId right, @@ -3679,6 +3715,63 @@ namespace BinaryNinja const std::string& autoTypeSource = ""); }; + // DownloadProvider + class DownloadProvider; + + class DownloadInstance: public CoreRefCountObject<BNDownloadInstance, BNNewDownloadInstanceReference, BNFreeDownloadInstance> + { + protected: + DownloadInstance(DownloadProvider* provider); + DownloadInstance(BNDownloadInstance* instance); + + static void DestroyInstanceCallback(void* ctxt); + static int PerformRequestCallback(void* ctxt, const char* url); + + virtual void DestroyInstance(); + + public: + virtual int PerformRequest(const std::string& url) = 0; + + int PerformRequest(const std::string& url, BNDownloadInstanceOutputCallbacks* callbacks); + + std::string GetError() const; + void SetError(const std::string& error); + }; + + class CoreDownloadInstance: public DownloadInstance + { + public: + CoreDownloadInstance(BNDownloadInstance* instance); + + virtual int PerformRequest(const std::string& url) override; + }; + + class DownloadProvider: public StaticCoreRefCountObject<BNDownloadProvider> + { + std::string m_nameForRegister; + + protected: + DownloadProvider(const std::string& name); + DownloadProvider(BNDownloadProvider* provider); + + static BNDownloadInstance* CreateInstanceCallback(void* ctxt); + + public: + virtual Ref<DownloadInstance> CreateNewInstance() = 0; + + static std::vector<Ref<DownloadProvider>> GetList(); + static Ref<DownloadProvider> GetByName(const std::string& name); + static void Register(DownloadProvider* provider); + }; + + class CoreDownloadProvider: public DownloadProvider + { + public: + CoreDownloadProvider(BNDownloadProvider* provider); + virtual Ref<DownloadInstance> CreateNewInstance() override; + }; + + // Scripting Provider class ScriptingOutputListener { BNScriptingOutputListener m_callbacks; @@ -4062,7 +4155,4 @@ namespace BinaryNinja bool IsArray() const; bool IsKeyValueStore() const; }; - - std::string GetLinuxCADirectory(); - std::string GetLinuxCABundlePath(); } diff --git a/binaryninjacore.h b/binaryninjacore.h index 9359ca0e..a26d1fe6 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -120,6 +120,8 @@ extern "C" struct BNArchitecture; struct BNFunction; struct BNBasicBlock; + struct BNDownloadProvider; + struct BNDownloadInstance; struct BNFlowGraph; struct BNFlowGraphNode; struct BNSymbol; @@ -333,6 +335,7 @@ extern "C" LLIL_JUMP_TO, LLIL_CALL, LLIL_CALL_STACK_ADJUST, + LLIL_TAILCALL, LLIL_RET, LLIL_NORET, LLIL_IF, @@ -404,6 +407,7 @@ extern "C" LLIL_FLAG_BIT_SSA, LLIL_CALL_SSA, LLIL_SYSCALL_SSA, + LLIL_TAILCALL_SSA, LLIL_CALL_PARAM, // Only valid within the LLIL_CALL_SSA, LLIL_SYSCALL_SSA, LLIL_INTRINSIC, LLIL_INTRINSIC_SSA instructions LLIL_CALL_STACK_SSA, // Only valid within the LLIL_CALL_SSA or LLIL_SYSCALL_SSA instructions LLIL_CALL_OUTPUT_SSA, // Only valid within the LLIL_CALL_SSA or LLIL_SYSCALL_SSA instructions @@ -848,8 +852,8 @@ extern "C" MLIL_JUMP_TO, MLIL_CALL, // Not valid in SSA form (see MLIL_CALL_SSA) MLIL_CALL_UNTYPED, // Not valid in SSA form (see MLIL_CALL_UNTYPED_SSA) - MLIL_CALL_OUTPUT, // Only valid within MLIL_CALL or MLIL_SYSCALL family instructions - MLIL_CALL_PARAM, // Only valid within MLIL_CALL or MLIL_SYSCALL family instructions + MLIL_CALL_OUTPUT, // Only valid within MLIL_CALL, MLIL_SYSCALL, MLIL_TAILCALL family instructions + MLIL_CALL_PARAM, // Only valid within MLIL_CALL, MLIL_SYSCALL, MLIL_TAILCALL family instructions MLIL_RET, // Not valid in SSA form (see MLIL_RET_SSA) MLIL_NORET, MLIL_IF, @@ -869,6 +873,8 @@ extern "C" MLIL_ADD_OVERFLOW, MLIL_SYSCALL, // Not valid in SSA form (see MLIL_SYSCALL_SSA) MLIL_SYSCALL_UNTYPED, // Not valid in SSA form (see MLIL_SYSCALL_UNTYPED_SSA) + MLIL_TAILCALL, // Not valid in SSA form (see MLIL_TAILCALL_SSA) + MLIL_TAILCALL_UNTYPED, // Not valid in SSA form (see MLIL_TAILCALL_UNTYPED_SSA) MLIL_INTRINSIC, // Not valid in SSA form (see MLIL_INTRINSIC_SSA) MLIL_FREE_VAR_SLOT, // Not valid in SSA from (see MLIL_FREE_VAR_SLOT_SSA) MLIL_BP, @@ -916,8 +922,10 @@ extern "C" MLIL_CALL_UNTYPED_SSA, MLIL_SYSCALL_SSA, MLIL_SYSCALL_UNTYPED_SSA, - MLIL_CALL_PARAM_SSA, // Only valid within the MLIL_CALL_SSA, MLIL_SYSCALL_SSA family instructions - MLIL_CALL_OUTPUT_SSA, // Only valid within the MLIL_CALL_SSA or MLIL_SYSCALL_SSA family instructions + MLIL_TAILCALL_SSA, + MLIL_TAILCALL_UNTYPED_SSA, + MLIL_CALL_PARAM_SSA, // Only valid within the MLIL_CALL_SSA, MLIL_SYSCALL_SSA, MLIL_TAILCALL_SSA family instructions + MLIL_CALL_OUTPUT_SSA, // Only valid within the MLIL_CALL_SSA or MLIL_SYSCALL_SSA, MLIL_TAILCALL_SSA family instructions MLIL_LOAD_SSA, MLIL_LOAD_STRUCT_SSA, MLIL_STORE_SSA, @@ -1499,7 +1507,24 @@ extern "C" { IdleState, DisassembleState, - AnalyzeState + AnalyzeState, + ExtendedAnalyzeState + }; + + struct BNActiveAnalysisInfo + { + BNFunction* func; + uint64_t analysisTime; + size_t updateCount; + size_t submitCount; + }; + + struct BNAnalysisInfo + { + BNAnalysisState state; + uint64_t analysisTime; + BNActiveAnalysisInfo* activeInfo; + size_t count; }; struct BNAnalysisProgress @@ -1508,6 +1533,36 @@ extern "C" size_t count, total; }; + struct BNAnalysisParameters + { + uint64_t maxAnalysisTime; + uint64_t maxFunctionSize; + uint64_t maxFunctionAnalysisTime; + size_t maxFunctionUpdateCount; + size_t maxFunctionSubmitCount; + }; + + struct BNDownloadInstanceOutputCallbacks + { + uint64_t (*writeCallback)(uint8_t* data, uint64_t len, void* ctxt); + void* writeContext; + bool (*progressCallback)(void* ctxt, uint64_t progress, uint64_t total); + void* progressContext; + }; + + struct BNDownloadInstanceCallbacks + { + void* context; + void (*destroyInstance)(void* ctxt); + int (*performRequest)(void* ctxt, const char* url); + }; + + struct BNDownloadProviderCallbacks + { + void* context; + BNDownloadInstance* (*createInstance)(void* ctxt); + }; + enum BNFindFlag { NoFindFlags = 0, @@ -1781,6 +1836,14 @@ extern "C" BNFlowGraph* (*update)(void* ctxt); }; + enum BNAnalysisSkipReason + { + NoSkipReason, + AlwaysSkipReason, + ExceedFunctionSizeSkipReason, + ExceedFunctionAnalysisTimeSkipReason + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size); @@ -2470,9 +2533,12 @@ extern "C" BINARYNINJACOREAPI bool BNIsFunctionTooLarge(BNFunction* func); BINARYNINJACOREAPI bool BNIsFunctionAnalysisSkipped(BNFunction* func); + BINARYNINJACOREAPI BNAnalysisSkipReason BNGetAnalysisSkipReason(BNFunction* func); BINARYNINJACOREAPI BNFunctionAnalysisSkipOverride BNGetFunctionAnalysisSkipOverride(BNFunction* func); BINARYNINJACOREAPI void BNSetFunctionAnalysisSkipOverride(BNFunction* func, BNFunctionAnalysisSkipOverride skip); + BINARYNINJACOREAPI BNAnalysisParameters BNGetParametersForAnalysis(BNBinaryView* view); + BINARYNINJACOREAPI void BNSetParametersForAnalysis(BNBinaryView* view, BNAnalysisParameters params); BINARYNINJACOREAPI uint64_t BNGetMaxFunctionSizeForAnalysis(BNBinaryView* view); BINARYNINJACOREAPI void BNSetMaxFunctionSizeForAnalysis(BNBinaryView* view, uint64_t size); @@ -2482,6 +2548,8 @@ extern "C" BINARYNINJACOREAPI void BNFreeAnalysisCompletionEvent(BNAnalysisCompletionEvent* event); BINARYNINJACOREAPI void BNCancelAnalysisCompletionEvent(BNAnalysisCompletionEvent* event); + BINARYNINJACOREAPI BNAnalysisInfo* BNGetAnalysisInfo(BNBinaryView* view); + BINARYNINJACOREAPI void BNFreeAnalysisInfo(BNAnalysisInfo* info); BINARYNINJACOREAPI BNAnalysisProgress BNGetAnalysisProgress(BNBinaryView* view); BINARYNINJACOREAPI BNBackgroundTask* BNGetBackgroundAnalysisTask(BNBinaryView* view); @@ -3222,6 +3290,24 @@ extern "C" char*** outVarName, size_t* outVarNameElements); + // Download providers + BINARYNINJACOREAPI BNDownloadProvider* BNRegisterDownloadProvider(const char* name, BNDownloadProviderCallbacks* callbacks); + BINARYNINJACOREAPI BNDownloadProvider** BNGetDownloadProviderList(size_t* count); + BINARYNINJACOREAPI void BNFreeDownloadProviderList(BNDownloadProvider** providers); + BINARYNINJACOREAPI BNDownloadProvider* BNGetDownloadProviderByName(const char* name); + + BINARYNINJACOREAPI char* BNGetDownloadProviderName(BNDownloadProvider* provider); + BINARYNINJACOREAPI BNDownloadInstance* BNCreateDownloadProviderInstance(BNDownloadProvider* provider); + + BINARYNINJACOREAPI BNDownloadInstance* BNInitDownloadInstance(BNDownloadProvider* provider, BNDownloadInstanceCallbacks* callbacks); + BINARYNINJACOREAPI BNDownloadInstance* BNNewDownloadInstanceReference(BNDownloadInstance* instance); + BINARYNINJACOREAPI void BNFreeDownloadInstance(BNDownloadInstance* instance); + BINARYNINJACOREAPI int BNPerformDownloadRequest(BNDownloadInstance* instance, const char* url, BNDownloadInstanceOutputCallbacks* callbacks); + BINARYNINJACOREAPI uint64_t BNWriteDataForDownloadInstance(BNDownloadInstance* instance, uint8_t* data, uint64_t len); + BINARYNINJACOREAPI bool BNNotifyProgressForDownloadInstance(BNDownloadInstance* instance, uint64_t progress, uint64_t total); + BINARYNINJACOREAPI char* BNGetErrorForDownloadInstance(BNDownloadInstance* instance); + BINARYNINJACOREAPI void BNSetErrorForDownloadInstance(BNDownloadInstance* instance, const char* error); + // Scripting providers BINARYNINJACOREAPI BNScriptingProvider* BNRegisterScriptingProvider(const char* name, BNScriptingProviderCallbacks* callbacks); @@ -3508,8 +3594,6 @@ extern "C" BINARYNINJACOREAPI BNMetadata* BNBinaryViewQueryMetadata(BNBinaryView* view, const char* key); BINARYNINJACOREAPI void BNBinaryViewRemoveMetadata(BNBinaryView* view, const char* key); - BINARYNINJACOREAPI char* BNGetLinuxCADirectory(); - BINARYNINJACOREAPI char* BNGetLinuxCABundlePath(); #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 18dd1bef..da516184 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -1014,6 +1014,21 @@ vector<Ref<Function>> BinaryView::GetAnalysisFunctionList() } +AnalysisInfo BinaryView::GetAnalysisInfo() +{ + AnalysisInfo result; + BNAnalysisInfo* info = BNGetAnalysisInfo(m_object); + result.state = info->state; + result.analysisTime = info->analysisTime; + result.activeInfo.reserve(info->count); + for (size_t i = 0; i < info->count; i++) + result.activeInfo.emplace_back(new Function(BNNewFunctionReference(info->activeInfo[i].func)), + info->activeInfo[i].analysisTime, info->activeInfo[i].submitCount, info->activeInfo[i].updateCount); + BNFreeAnalysisInfo(info); + return result; +} + + bool BinaryView::HasFunctions() const { return BNHasFunctions(m_object); @@ -1784,6 +1799,7 @@ bool BinaryView::GetSegmentAt(uint64_t addr, Segment& result) result.dataOffset = segment.dataOffset; result.dataLength = segment.dataLength; result.flags = segment.flags; + result.autoDefined = segment.autoDefined; return true; } @@ -1976,6 +1992,18 @@ uint64_t BinaryView::GetUIntMetadata(const string& key) } +BNAnalysisParameters BinaryView::GetParametersForAnalysis() +{ + return BNGetParametersForAnalysis(m_object); +} + + +void BinaryView::SetParametersForAnalysis(BNAnalysisParameters params) +{ + BNSetParametersForAnalysis(m_object, params); +} + + uint64_t BinaryView::GetMaxFunctionSizeForAnalysis() { return BNGetMaxFunctionSizeForAnalysis(m_object); diff --git a/binaryviewtype.cpp b/binaryviewtype.cpp index e8ef4f53..762b2de7 100644 --- a/binaryviewtype.cpp +++ b/binaryviewtype.cpp @@ -29,6 +29,8 @@ BNBinaryView* BinaryViewType::CreateCallback(void* ctxt, BNBinaryView* data) BinaryViewType* type = (BinaryViewType*)ctxt; Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); Ref<BinaryView> result = type->Create(view); + if (!result) + return nullptr; return BNNewViewReference(result->GetObject()); } diff --git a/docs/about/open-source.md b/docs/about/open-source.md index b0a35f37..be9725d0 100644 --- a/docs/about/open-source.md +++ b/docs/about/open-source.md @@ -22,13 +22,11 @@ The previous tools are used in the generation of our documentation, but are not * Core - [discount] ([discount license] - BSD) - - [libcurl] ([libcurl license] - MIT/X derivative) - [libgit2] ([libgit2 license] - GPLv2 with linking exception) - [libmspack] ([libmspack license] - LGPL, v2) - [llvm] ([llvm license] - BSD-style) - [lzf] ([lzf license] - BSD) - [jemalloc] ([jemalloc license] - BSD) - - [openssl] ([openssl license] - openssl license) - [sqlite] ([sqlite license] - public domain) - [zlib] ([zlib license] - zlib license) @@ -69,8 +67,6 @@ Please note that we offer no support for running Binary Ninja with modified Qt l [discount]: http://www.pell.portland.or.us/~orc/Code/discount/ [doxygen license]: https://github.com/doxygen/doxygen/blob/master/LICENSE [doxygen]: http://www.stack.nl/~dimitri/doxygen/ -[libcurl]: https://curl.haxx.se/ -[libcurl license]: https://curl.haxx.se/docs/copyright.html [libgit2]: https://libgit2.github.com/ [libgit2 license]: https://github.com/libgit2/libgit2/blob/master/COPYING [libmspack]: https://www.cabextract.org.uk/libmspack/ @@ -87,8 +83,6 @@ Please note that we offer no support for running Binary Ninja with modified Qt l [mkdocs]: http://www.mkdocs.org/ [opensans license]: http://www.apache.org/licenses/LICENSE-2.0.html [opensans]: https://www.google.com/fonts/specimen/Open+Sans -[openssl license]: https://www.openssl.org/source/license.html -[openssl]: https://www.openssl.org/ [qt license]: https://www.qt.io/qt-licensing-terms/ [qt]: https://www.qt.io/download/ [sourcecodepro license]: https://github.com/adobe-fonts/source-code-pro/blob/master/LICENSE.txt diff --git a/docs/getting-started.md b/docs/getting-started.md index 073ccd33..7e7e4acb 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -257,18 +257,20 @@ On windows, this is achieved through a separate launcher that loads first and re Settings are stored in the _user_ directory in the file `settings.json`. Each top level object in this file is represents a different plugin. As of build 860 the following settings are available: -|Plugin | Setting | Type | Default | Description | -|------:|-------------------------:|-------------:|-----------------------------------------------:|:----------------------------------------------------------------------------------------------| -| core | linux\_ca\_bundle | string | "" | Certificate authority (.pem or .crt) file to be used for secure downloads | -| core | linux\_ca\_dir | string | "" | Certificate authority directory (for distributions without a CA bundle) | -| ui | activeContent | boolean | True | Allow Binary Ninja to connect to the web to check for updates | -| ui | colorblind | boolean | True | Choose colors that are visible to those with red/green colorblind | -| ui | debug | boolean | False | Enable developer debugging features (Additional views: Lifted IL, and SSA forms) | -| ui | recent-file-limit | integer | 10 | Specify limit for number of recent files | -| pdb | local-store-absolute | string | "" | Absolute path specifying where the pdb symbol store exists on this machine, overrides relative path | -| pdb | local-store-relative | string | "symbols" | Path *relative* to the binaryninja _user_ directory, sepcifying the pdb symbol store | -| pdb | auto-download-pdb | boolean | True | Automatically download pdb files from specified symbol servers | -| pdb | symbol-server-list | list(string) | ["http://msdl.microsoft.com/download/symbols"] | List of servers to query for pdb symbols. | +|Plugin | Setting | Type | Default | Description | +|----------:|-------------------------:|-------------:|-----------------------------------------------:|:----------------------------------------------------------------------------------------------| +| analysis | max-function-size | integer | 65536 | Any functions over this size will not be automatically analyzed and require manual override | +| core | linux\_ca\_bundle | string | "" | Certificate authority (.pem or .crt) file to be used for secure downloads | +| core | linux\_ca\_dir | string | "" | Certificate authority directory (for distributions without a CA bundle) | +| ui | activeContent | boolean | True | Allow Binary Ninja to connect to the web to check for updates | +| ui | colorblind | boolean | True | Choose colors that are visible to those with red/green colorblind | +| ui | debug | boolean | False | Enable developer debugging features (Additional views: Lifted IL, and SSA forms) | +| ui | recent-file-limit | integer | 10 | Specify limit for number of recent files | +| pdb | local-store-absolute | string | "" | Absolute path specifying where the pdb symbol store exists on this machine, overrides relative path | +| pdb | local-store-relative | string | "symbols" | Path *relative* to the binaryninja _user_ directory, sepcifying the pdb symbol store | +| pdb | auto-download-pdb | boolean | True | Automatically download pdb files from specified symbol servers | +| pdb | symbol-server-list | list(string) | ["http://msdl.microsoft.com/download/symbols"] | List of servers to query for pdb symbols. | +| python | interpreter | string | "python27.{dylib,dll,so.1}" | Python interpreter to load if one is not already present when plugins are loaded | Below is an example `settings.json` setting various options: ``` diff --git a/downloadprovider.cpp b/downloadprovider.cpp new file mode 100644 index 00000000..0e3483df --- /dev/null +++ b/downloadprovider.cpp @@ -0,0 +1,135 @@ +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +DownloadInstance::DownloadInstance(DownloadProvider* provider) +{ + BNDownloadInstanceCallbacks cb; + cb.context = this; + cb.destroyInstance = DestroyInstanceCallback; + cb.performRequest = PerformRequestCallback; + m_object = BNInitDownloadInstance(provider->GetObject(), &cb); +} + + +DownloadInstance::DownloadInstance(BNDownloadInstance* instance) +{ + m_object = instance; +} + + +void DownloadInstance::DestroyInstanceCallback(void* ctxt) +{ + DownloadInstance* instance = (DownloadInstance*)ctxt; + instance->DestroyInstance(); +} + + +int DownloadInstance::PerformRequestCallback(void* ctxt, const char* url) +{ + DownloadInstance* instance = (DownloadInstance*)ctxt; + return instance->PerformRequest(url); +} + + +string DownloadInstance::GetError() const +{ + char* str = BNGetErrorForDownloadInstance(m_object); + string result = str; + BNFreeString(str); + return result; +} + + +void DownloadInstance::SetError(const string& error) +{ + BNSetErrorForDownloadInstance(m_object, error.c_str()); +} + + +int DownloadInstance::PerformRequest(const string& url, BNDownloadInstanceOutputCallbacks* callbacks) +{ + return BNPerformDownloadRequest(m_object, url.c_str(), callbacks); +} + + +void DownloadInstance::DestroyInstance() +{ +} + + +CoreDownloadInstance::CoreDownloadInstance(BNDownloadInstance* instance): DownloadInstance(instance) +{ +} + + +int CoreDownloadInstance::PerformRequest(const std::string& url) +{ + (void)url; + return -1; +} + + +DownloadProvider::DownloadProvider(const string& name): m_nameForRegister(name) +{ +} + + +DownloadProvider::DownloadProvider(BNDownloadProvider* provider) +{ + m_object = provider; +} + + +BNDownloadInstance* DownloadProvider::CreateInstanceCallback(void* ctxt) +{ + DownloadProvider* provider = (DownloadProvider*)ctxt; + Ref<DownloadInstance> instance = provider->CreateNewInstance(); + return instance ? BNNewDownloadInstanceReference(instance->GetObject()) : nullptr; +} + + +vector<Ref<DownloadProvider>> DownloadProvider::GetList() +{ + size_t count; + BNDownloadProvider** list = BNGetDownloadProviderList(&count); + vector<Ref<DownloadProvider>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreDownloadProvider(list[i])); + BNFreeDownloadProviderList(list); + return result; +} + + +Ref<DownloadProvider> DownloadProvider::GetByName(const string& name) +{ + BNDownloadProvider* result = BNGetDownloadProviderByName(name.c_str()); + if (!result) + return nullptr; + return new CoreDownloadProvider(result); +} + + +void DownloadProvider::Register(DownloadProvider* provider) +{ + BNDownloadProviderCallbacks cb; + cb.context = provider; + cb.createInstance = CreateInstanceCallback; + provider->m_object = BNRegisterDownloadProvider(provider->m_nameForRegister.c_str(), &cb); +} + + +CoreDownloadProvider::CoreDownloadProvider(BNDownloadProvider* provider): DownloadProvider(provider) +{ +} + + +Ref<DownloadInstance> CoreDownloadProvider::CreateNewInstance() +{ + BNDownloadInstance* result = BNCreateDownloadProviderInstance(m_object); + if (!result) + return nullptr; + return new CoreDownloadInstance(result); +} diff --git a/examples/llil_parser/CMakeLists.txt b/examples/llil_parser/CMakeLists.txt index 6d782109..868a755e 100644 --- a/examples/llil_parser/CMakeLists.txt +++ b/examples/llil_parser/CMakeLists.txt @@ -7,7 +7,7 @@ project(LLIL_Parser) #----------------------------------------------------------------------------- include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) #----------------------------------------------------------------------------- -file( GLOB_RECURSE SRCS *.cpp *.h) +set(SRCS src/llil_parser.cpp) #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #----------------------------------------------------------------------------- @@ -31,7 +31,7 @@ else() CACHE PATH "Binary Ninja user plugins directory") endif() #----------------------------------------------------------------------------- -add_executable (${PROJECT_NAME} ${SRCS} ) +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) diff --git a/examples/llil_parser/src/llil_parser.cpp b/examples/llil_parser/src/llil_parser.cpp index 72ac71bd..9b73d298 100644 --- a/examples/llil_parser/src/llil_parser.cpp +++ b/examples/llil_parser/src/llil_parser.cpp @@ -91,6 +91,8 @@ static void PrintOperation(BNLowLevelILOperation operation) ENUM_PRINTER(LLIL_JUMP) ENUM_PRINTER(LLIL_JUMP_TO) ENUM_PRINTER(LLIL_CALL) + ENUM_PRINTER(LLIL_CALL_STACK_ADJUST) + ENUM_PRINTER(LLIL_TAILCALL) ENUM_PRINTER(LLIL_RET) ENUM_PRINTER(LLIL_NORET) ENUM_PRINTER(LLIL_IF) @@ -126,7 +128,8 @@ static void PrintOperation(BNLowLevelILOperation operation) ENUM_PRINTER(LLIL_FLAG_BIT_SSA) ENUM_PRINTER(LLIL_CALL_SSA) ENUM_PRINTER(LLIL_SYSCALL_SSA) - ENUM_PRINTER(LLIL_CALL_PARAM_SSA) + ENUM_PRINTER(LLIL_TAILCALL_SSA) + ENUM_PRINTER(LLIL_CALL_PARAM) ENUM_PRINTER(LLIL_CALL_STACK_SSA) ENUM_PRINTER(LLIL_CALL_OUTPUT_SSA) ENUM_PRINTER(LLIL_LOAD_SSA) diff --git a/examples/mlil_parser/CMakeLists.txt b/examples/mlil_parser/CMakeLists.txt index 1bf6e3a7..4ec35591 100644 --- a/examples/mlil_parser/CMakeLists.txt +++ b/examples/mlil_parser/CMakeLists.txt @@ -7,7 +7,7 @@ project(MLIL_Parser) #----------------------------------------------------------------------------- include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) #----------------------------------------------------------------------------- -file( GLOB_RECURSE SRCS *.cpp *.h) +set(SRCS src/mlil_parser.cpp) #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #----------------------------------------------------------------------------- @@ -31,7 +31,7 @@ else() CACHE PATH "Binary Ninja user plugins directory") endif() #----------------------------------------------------------------------------- -add_executable (${PROJECT_NAME} ${SRCS} ) +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) diff --git a/examples/mlil_parser/src/mlil_parser.cpp b/examples/mlil_parser/src/mlil_parser.cpp index 8fb762eb..0e621ead 100644 --- a/examples/mlil_parser/src/mlil_parser.cpp +++ b/examples/mlil_parser/src/mlil_parser.cpp @@ -114,6 +114,8 @@ static void PrintOperation(BNMediumLevelILOperation operation) ENUM_PRINTER(MLIL_ADD_OVERFLOW) ENUM_PRINTER(MLIL_SYSCALL) ENUM_PRINTER(MLIL_SYSCALL_UNTYPED) + ENUM_PRINTER(MLIL_TAILCALL) + ENUM_PRINTER(MLIL_TAILCALL_UNTYPED) ENUM_PRINTER(MLIL_BP) ENUM_PRINTER(MLIL_TRAP) ENUM_PRINTER(MLIL_UNDEF) @@ -132,6 +134,8 @@ static void PrintOperation(BNMediumLevelILOperation operation) ENUM_PRINTER(MLIL_CALL_UNTYPED_SSA) ENUM_PRINTER(MLIL_SYSCALL_SSA) ENUM_PRINTER(MLIL_SYSCALL_UNTYPED_SSA) + ENUM_PRINTER(MLIL_TAILCALL_SSA) + ENUM_PRINTER(MLIL_TAILCALL_UNTYPED_SSA) ENUM_PRINTER(MLIL_CALL_PARAM_SSA) ENUM_PRINTER(MLIL_CALL_OUTPUT_SSA) ENUM_PRINTER(MLIL_LOAD_SSA) diff --git a/function.cpp b/function.cpp index 202f6d77..ae6d3a6b 100644 --- a/function.cpp +++ b/function.cpp @@ -1379,6 +1379,12 @@ bool Function::IsAnalysisSkipped() } +BNAnalysisSkipReason Function::GetAnalysisSkipReason() +{ + return BNGetAnalysisSkipReason(m_object); +} + + BNFunctionAnalysisSkipOverride Function::GetAnalysisSkipOverride() { return BNGetFunctionAnalysisSkipOverride(m_object); diff --git a/lowlevelilinstruction.cpp b/lowlevelilinstruction.cpp index 8fb87a9e..5f5dc397 100644 --- a/lowlevelilinstruction.cpp +++ b/lowlevelilinstruction.cpp @@ -148,6 +148,7 @@ unordered_map<BNLowLevelILOperation, vector<LowLevelILOperandUsage>> {LLIL_CALL, {DestExprLowLevelOperandUsage}}, {LLIL_CALL_STACK_ADJUST, {DestExprLowLevelOperandUsage, StackAdjustmentLowLevelOperandUsage, RegisterStackAdjustmentsLowLevelOperandUsage}}, + {LLIL_TAILCALL, {DestExprLowLevelOperandUsage}}, {LLIL_RET, {DestExprLowLevelOperandUsage}}, {LLIL_IF, {ConditionExprLowLevelOperandUsage, TrueTargetLowLevelOperandUsage, FalseTargetLowLevelOperandUsage}}, @@ -161,6 +162,9 @@ unordered_map<BNLowLevelILOperation, vector<LowLevelILOperandUsage>> {LLIL_SYSCALL_SSA, {OutputSSARegistersLowLevelOperandUsage, OutputMemoryVersionLowLevelOperandUsage, StackSSARegisterLowLevelOperandUsage, StackMemoryVersionLowLevelOperandUsage, ParameterExprsLowLevelOperandUsage}}, + {LLIL_TAILCALL_SSA, {OutputSSARegistersLowLevelOperandUsage, OutputMemoryVersionLowLevelOperandUsage, + DestExprLowLevelOperandUsage, StackSSARegisterLowLevelOperandUsage, + StackMemoryVersionLowLevelOperandUsage, ParameterExprsLowLevelOperandUsage}}, {LLIL_REG_PHI, {DestSSARegisterLowLevelOperandUsage, SourceSSARegistersLowLevelOperandUsage}}, {LLIL_REG_STACK_PHI, {DestSSARegisterStackLowLevelOperandUsage, SourceSSARegisterStacksLowLevelOperandUsage}}, {LLIL_FLAG_PHI, {DestSSAFlagLowLevelOperandUsage, SourceSSAFlagsLowLevelOperandUsage}}, @@ -1808,6 +1812,9 @@ void LowLevelILInstruction::VisitExprs(const std::function<bool(const LowLevelIL case LLIL_CALL_STACK_ADJUST: GetDestExpr<LLIL_CALL_STACK_ADJUST>().VisitExprs(func); break; + case LLIL_TAILCALL: + GetDestExpr<LLIL_TAILCALL>().VisitExprs(func); + break; case LLIL_CALL_SSA: GetDestExpr<LLIL_CALL_SSA>().VisitExprs(func); for (auto& i : GetParameterExprs<LLIL_CALL_SSA>()) @@ -1817,6 +1824,11 @@ void LowLevelILInstruction::VisitExprs(const std::function<bool(const LowLevelIL for (auto& i : GetParameterExprs<LLIL_SYSCALL_SSA>()) i.VisitExprs(func); break; + case LLIL_TAILCALL_SSA: + GetDestExpr<LLIL_TAILCALL_SSA>().VisitExprs(func); + for (auto& i : GetParameterExprs<LLIL_TAILCALL_SSA>()) + i.VisitExprs(func); + break; case LLIL_RET: GetDestExpr<LLIL_RET>().VisitExprs(func); break; @@ -2037,6 +2049,8 @@ ExprId LowLevelILInstruction::CopyTo(LowLevelILFunction* dest, case LLIL_CALL_STACK_ADJUST: return dest->CallStackAdjust(subExprHandler(GetDestExpr<LLIL_CALL_STACK_ADJUST>()), GetStackAdjustment<LLIL_CALL_STACK_ADJUST>(), GetRegisterStackAdjustments<LLIL_CALL_STACK_ADJUST>(), *this); + case LLIL_TAILCALL: + return dest->TailCall(subExprHandler(GetDestExpr<LLIL_TAILCALL>()), *this); case LLIL_RET: return dest->Return(subExprHandler(GetDestExpr<LLIL_RET>()), *this); case LLIL_JUMP_TO: @@ -2080,6 +2094,12 @@ ExprId LowLevelILInstruction::CopyTo(LowLevelILFunction* dest, return dest->SystemCallSSA(GetOutputSSARegisters<LLIL_SYSCALL_SSA>(), params, GetStackSSARegister<LLIL_SYSCALL_SSA>(), GetDestMemoryVersion<LLIL_SYSCALL_SSA>(), GetSourceMemoryVersion<LLIL_SYSCALL_SSA>(), *this); + case LLIL_TAILCALL_SSA: + for (auto& i : GetParameterExprs<LLIL_TAILCALL_SSA>()) + params.push_back(subExprHandler(i)); + return dest->TailCallSSA(GetOutputSSARegisters<LLIL_TAILCALL_SSA>(), subExprHandler(GetDestExpr<LLIL_TAILCALL_SSA>()), + params, GetStackSSARegister<LLIL_TAILCALL_SSA>(), GetDestMemoryVersion<LLIL_TAILCALL_SSA>(), + GetSourceMemoryVersion<LLIL_TAILCALL_SSA>(), *this); case LLIL_REG_PHI: return dest->RegisterPhi(GetDestSSARegister<LLIL_REG_PHI>(), GetSourceSSARegisters<LLIL_REG_PHI>(), *this); case LLIL_REG_STACK_PHI: @@ -3157,6 +3177,12 @@ ExprId LowLevelILFunction::CallStackAdjust(ExprId dest, size_t adjust, } +ExprId LowLevelILFunction::TailCall(ExprId dest, const ILSourceLocation& loc) +{ + return AddExprWithLocation(LLIL_TAILCALL, loc, 0, 0, dest); +} + + ExprId LowLevelILFunction::CallSSA(const vector<SSARegister>& output, ExprId dest, const vector<ExprId>& params, const SSARegister& stack, size_t newMemoryVer, size_t prevMemoryVer, const ILSourceLocation& loc) { @@ -3181,6 +3207,18 @@ ExprId LowLevelILFunction::SystemCallSSA(const vector<SSARegister>& output, cons } +ExprId LowLevelILFunction::TailCallSSA(const vector<SSARegister>& output, ExprId dest, const vector<ExprId>& params, + const SSARegister& stack, size_t newMemoryVer, size_t prevMemoryVer, const ILSourceLocation& loc) +{ + return AddExprWithLocation(LLIL_TAILCALL_SSA, loc, 0, 0, + AddExprWithLocation(LLIL_CALL_OUTPUT_SSA, loc, 0, 0, newMemoryVer, + output.size() * 2, AddSSARegisterList(output)), dest, + AddExprWithLocation(LLIL_CALL_STACK_SSA, loc, 0, 0, stack.reg, stack.version, prevMemoryVer), + AddExprWithLocation(LLIL_CALL_PARAM, loc, 0, 0, + params.size(), AddOperandList(params))); +} + + ExprId LowLevelILFunction::Return(size_t dest, const ILSourceLocation& loc) { return AddExprWithLocation(LLIL_RET, loc, 0, 0, dest); diff --git a/lowlevelilinstruction.h b/lowlevelilinstruction.h index 675baa3c..e845bcd0 100644 --- a/lowlevelilinstruction.h +++ b/lowlevelilinstruction.h @@ -1107,6 +1107,10 @@ namespace BinaryNinja size_t GetStackAdjustment() const { return (size_t)GetRawOperandAsInteger(1); } std::map<uint32_t, int32_t> GetRegisterStackAdjustments() const { return GetRawOperandAsRegisterStackAdjustments(2); } }; + template <> struct LowLevelILInstructionAccessor<LLIL_TAILCALL>: public LowLevelILInstructionBase + { + LowLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(0); } + }; template <> struct LowLevelILInstructionAccessor<LLIL_RET>: public LowLevelILInstructionBase { LowLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(0); } @@ -1163,6 +1167,19 @@ namespace BinaryNinja void SetStackSSAVersion(size_t version) { GetRawOperandAsExpr(1).UpdateRawOperand(1, version); } void SetOutputSSARegisters(const std::vector<SSARegister>& regs) { GetRawOperandAsExpr(0).UpdateRawOperandAsSSARegisterList(1, regs); } }; + template <> struct LowLevelILInstructionAccessor<LLIL_TAILCALL_SSA>: public LowLevelILInstructionBase + { + LowLevelILSSARegisterList GetOutputSSARegisters() const { return GetRawOperandAsExpr(0).GetRawOperandAsSSARegisterList(1); } + size_t GetDestMemoryVersion() const { return GetRawOperandAsExpr(0).GetRawOperandAsIndex(0); } + LowLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(1); } + SSARegister GetStackSSARegister() const { return GetRawOperandAsExpr(2).GetRawOperandAsSSARegister(0); } + size_t GetSourceMemoryVersion() const { return GetRawOperandAsExpr(2).GetRawOperandAsIndex(2); } + LowLevelILInstructionList GetParameterExprs() const { return GetRawOperandAsExpr(3).GetRawOperandAsExprList(0); } + void SetDestMemoryVersion(size_t version) { GetRawOperandAsExpr(0).UpdateRawOperand(0, version); } + void SetSourceMemoryVersion(size_t version) { GetRawOperandAsExpr(2).UpdateRawOperand(2, version); } + void SetStackSSAVersion(size_t version) { GetRawOperandAsExpr(2).UpdateRawOperand(1, version); } + void SetOutputSSARegisters(const std::vector<SSARegister>& regs) { GetRawOperandAsExpr(0).UpdateRawOperandAsSSARegisterList(1, regs); } + }; template <> struct LowLevelILInstructionAccessor<LLIL_INTRINSIC>: public LowLevelILInstructionBase { diff --git a/mediumlevelilinstruction.cpp b/mediumlevelilinstruction.cpp index fa66f553..9cf82b08 100644 --- a/mediumlevelilinstruction.cpp +++ b/mediumlevelilinstruction.cpp @@ -129,6 +129,10 @@ unordered_map<BNMediumLevelILOperation, vector<MediumLevelILOperandUsage>> {MLIL_SYSCALL, {OutputVariablesMediumLevelOperandUsage, ParameterExprsMediumLevelOperandUsage}}, {MLIL_SYSCALL_UNTYPED, {OutputVariablesSubExprMediumLevelOperandUsage, ParameterVariablesMediumLevelOperandUsage, StackExprMediumLevelOperandUsage}}, + {MLIL_TAILCALL, {OutputVariablesMediumLevelOperandUsage, DestExprMediumLevelOperandUsage, + ParameterExprsMediumLevelOperandUsage}}, + {MLIL_TAILCALL_UNTYPED, {OutputVariablesSubExprMediumLevelOperandUsage, DestExprMediumLevelOperandUsage, + ParameterVariablesMediumLevelOperandUsage}}, {MLIL_CALL_SSA, {OutputSSAVariablesSubExprMediumLevelOperandUsage, OutputSSAMemoryVersionMediumLevelOperandUsage, DestExprMediumLevelOperandUsage, ParameterExprsMediumLevelOperandUsage, SourceMemoryVersionMediumLevelOperandUsage}}, @@ -142,6 +146,13 @@ unordered_map<BNMediumLevelILOperation, vector<MediumLevelILOperandUsage>> {MLIL_SYSCALL_UNTYPED_SSA, {OutputSSAVariablesSubExprMediumLevelOperandUsage, OutputSSAMemoryVersionMediumLevelOperandUsage, ParameterSSAVariablesMediumLevelOperandUsage, ParameterSSAMemoryVersionMediumLevelOperandUsage, StackExprMediumLevelOperandUsage}}, + {MLIL_TAILCALL_SSA, {OutputSSAVariablesSubExprMediumLevelOperandUsage, + OutputSSAMemoryVersionMediumLevelOperandUsage, DestExprMediumLevelOperandUsage, + ParameterExprsMediumLevelOperandUsage, SourceMemoryVersionMediumLevelOperandUsage}}, + {MLIL_TAILCALL_UNTYPED_SSA, {OutputSSAVariablesSubExprMediumLevelOperandUsage, + OutputSSAMemoryVersionMediumLevelOperandUsage, DestExprMediumLevelOperandUsage, + ParameterSSAVariablesMediumLevelOperandUsage, ParameterSSAMemoryVersionMediumLevelOperandUsage, + StackExprMediumLevelOperandUsage}}, {MLIL_RET, {SourceExprsMediumLevelOperandUsage}}, {MLIL_IF, {ConditionExprMediumLevelOperandUsage, TrueTargetMediumLevelOperandUsage, FalseTargetMediumLevelOperandUsage}}, @@ -1267,6 +1278,22 @@ void MediumLevelILInstruction::VisitExprs(const std::function<bool(const MediumL for (auto& i : GetParameterExprs<MLIL_SYSCALL_SSA>()) i.VisitExprs(func); break; + case MLIL_TAILCALL: + GetDestExpr<MLIL_TAILCALL>().VisitExprs(func); + for (auto& i : GetParameterExprs<MLIL_TAILCALL>()) + i.VisitExprs(func); + break; + case MLIL_TAILCALL_UNTYPED: + GetDestExpr<MLIL_TAILCALL_UNTYPED>().VisitExprs(func); + break; + case MLIL_TAILCALL_SSA: + GetDestExpr<MLIL_TAILCALL_SSA>().VisitExprs(func); + for (auto& i : GetParameterExprs<MLIL_TAILCALL_SSA>()) + i.VisitExprs(func); + break; + case MLIL_TAILCALL_UNTYPED_SSA: + GetDestExpr<MLIL_TAILCALL_UNTYPED_SSA>().VisitExprs(func); + break; case MLIL_RET: for (auto& i : GetSourceExprs<MLIL_RET>()) i.VisitExprs(func); @@ -1504,6 +1531,27 @@ ExprId MediumLevelILInstruction::CopyTo(MediumLevelILFunction* dest, GetDestMemoryVersion<MLIL_SYSCALL_UNTYPED_SSA>(), GetSourceMemoryVersion<MLIL_SYSCALL_UNTYPED_SSA>(), subExprHandler(GetStackExpr<MLIL_SYSCALL_UNTYPED_SSA>()), *this); + case MLIL_TAILCALL: + for (auto& i : GetParameterExprs<MLIL_TAILCALL>()) + params.push_back(subExprHandler(i)); + return dest->TailCall(GetOutputVariables<MLIL_TAILCALL>(), subExprHandler(GetDestExpr<MLIL_TAILCALL>()), + params, *this); + case MLIL_TAILCALL_UNTYPED: + return dest->TailCallUntyped(GetOutputVariables<MLIL_TAILCALL_UNTYPED>(), + subExprHandler(GetDestExpr<MLIL_TAILCALL_UNTYPED>()), GetParameterVariables<MLIL_TAILCALL_UNTYPED>(), + subExprHandler(GetStackExpr<MLIL_TAILCALL_UNTYPED>()), *this); + case MLIL_TAILCALL_SSA: + for (auto& i : GetParameterExprs<MLIL_TAILCALL_SSA>()) + params.push_back(subExprHandler(i)); + return dest->TailCallSSA(GetOutputSSAVariables<MLIL_TAILCALL_SSA>(), subExprHandler(GetDestExpr<MLIL_TAILCALL_SSA>()), + params, GetDestMemoryVersion<MLIL_TAILCALL_SSA>(), GetSourceMemoryVersion<MLIL_TAILCALL_SSA>(), *this); + case MLIL_TAILCALL_UNTYPED_SSA: + return dest->TailCallUntypedSSA(GetOutputSSAVariables<MLIL_TAILCALL_UNTYPED_SSA>(), + subExprHandler(GetDestExpr<MLIL_TAILCALL_UNTYPED_SSA>()), + GetParameterSSAVariables<MLIL_TAILCALL_UNTYPED_SSA>(), + GetDestMemoryVersion<MLIL_TAILCALL_UNTYPED_SSA>(), + GetSourceMemoryVersion<MLIL_TAILCALL_UNTYPED_SSA>(), + subExprHandler(GetStackExpr<MLIL_TAILCALL_UNTYPED_SSA>()), *this); case MLIL_RET: for (auto& i : GetSourceExprs<MLIL_RET>()) params.push_back(subExprHandler(i)); @@ -2483,6 +2531,23 @@ ExprId MediumLevelILFunction::SyscallUntyped(const vector<Variable>& output, con } +ExprId MediumLevelILFunction::TailCall(const vector<Variable>& output, ExprId dest, + const vector<ExprId>& params, const ILSourceLocation& loc) +{ + return AddExprWithLocation(MLIL_TAILCALL, loc, 0, output.size(), AddVariableList(output), dest, + params.size(), AddOperandList(params)); +} + + +ExprId MediumLevelILFunction::TailCallUntyped(const vector<Variable>& output, ExprId dest, + const vector<Variable>& params, ExprId stack, const ILSourceLocation& loc) +{ + return AddExprWithLocation(MLIL_TAILCALL_UNTYPED, loc, 0, + AddExprWithLocation(MLIL_CALL_OUTPUT, loc, 0, output.size(), AddVariableList(output)), dest, + AddExprWithLocation(MLIL_CALL_PARAM, loc, 0, params.size(), AddVariableList(params)), stack); +} + + ExprId MediumLevelILFunction::CallSSA(const vector<SSAVariable>& output, ExprId dest, const vector<ExprId>& params, size_t newMemVersion, size_t prevMemVersion, const ILSourceLocation& loc) { @@ -2527,6 +2592,28 @@ ExprId MediumLevelILFunction::SyscallUntypedSSA(const vector<SSAVariable>& outpu } +ExprId MediumLevelILFunction::TailCallSSA(const vector<SSAVariable>& output, ExprId dest, const vector<ExprId>& params, + size_t newMemVersion, size_t prevMemVersion, const ILSourceLocation& loc) +{ + return AddExprWithLocation(MLIL_TAILCALL_SSA, loc, 0, + AddExprWithLocation(MLIL_CALL_OUTPUT_SSA, loc, 0, newMemVersion, + output.size() * 2, AddSSAVariableList(output)), dest, + params.size(), AddOperandList(params), prevMemVersion); +} + + +ExprId MediumLevelILFunction::TailCallUntypedSSA(const vector<SSAVariable>& output, ExprId dest, + const vector<SSAVariable>& params, size_t newMemVersion, size_t prevMemVersion, + ExprId stack, const ILSourceLocation& loc) +{ + return AddExprWithLocation(MLIL_TAILCALL_UNTYPED_SSA, loc, 0, + AddExprWithLocation(MLIL_CALL_OUTPUT_SSA, loc, 0, newMemVersion, + output.size() * 2, AddSSAVariableList(output)), dest, + AddExprWithLocation(MLIL_CALL_PARAM_SSA, loc, 0, prevMemVersion, + params.size() * 2, AddSSAVariableList(params)), stack); +} + + ExprId MediumLevelILFunction::Return(const vector<ExprId>& sources, const ILSourceLocation& loc) { return AddExprWithLocation(MLIL_RET, loc, 0, sources.size(), AddOperandList(sources)); diff --git a/mediumlevelilinstruction.h b/mediumlevelilinstruction.h index e3167585..b37b8832 100644 --- a/mediumlevelilinstruction.h +++ b/mediumlevelilinstruction.h @@ -839,6 +839,19 @@ namespace BinaryNinja MediumLevelILVariableList GetParameterVariables() const { return GetRawOperandAsExpr(1).GetRawOperandAsVariableList(0); } MediumLevelILInstruction GetStackExpr() const { return GetRawOperandAsExpr(2); } }; + template <> struct MediumLevelILInstructionAccessor<MLIL_TAILCALL>: public MediumLevelILInstructionBase + { + MediumLevelILVariableList GetOutputVariables() const { return GetRawOperandAsVariableList(0); } + MediumLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(2); } + MediumLevelILInstructionList GetParameterExprs() const { return GetRawOperandAsExprList(3); } + }; + template <> struct MediumLevelILInstructionAccessor<MLIL_TAILCALL_UNTYPED>: public MediumLevelILInstructionBase + { + MediumLevelILVariableList GetOutputVariables() const { return GetRawOperandAsExpr(0).GetRawOperandAsVariableList(0); } + MediumLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(1); } + MediumLevelILVariableList GetParameterVariables() const { return GetRawOperandAsExpr(2).GetRawOperandAsVariableList(0); } + MediumLevelILInstruction GetStackExpr() const { return GetRawOperandAsExpr(3); } + }; template <> struct MediumLevelILInstructionAccessor<MLIL_CALL_SSA>: public MediumLevelILInstructionBase { @@ -890,6 +903,32 @@ namespace BinaryNinja void SetOutputSSAVariables(const std::vector<SSAVariable>& vars) { GetRawOperandAsExpr(0).UpdateRawOperandAsSSAVariableList(1, vars); } void SetParameterSSAVariables(const std::vector<SSAVariable>& vars) { GetRawOperandAsExpr(1).UpdateRawOperandAsSSAVariableList(1, vars); } }; + template <> struct MediumLevelILInstructionAccessor<MLIL_TAILCALL_SSA>: public MediumLevelILInstructionBase + { + size_t GetDestMemoryVersion() const { return GetRawOperandAsExpr(0).GetRawOperandAsIndex(0); } + MediumLevelILSSAVariableList GetOutputSSAVariables() const { return GetRawOperandAsExpr(0).GetRawOperandAsSSAVariableList(1); } + MediumLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(1); } + MediumLevelILInstructionList GetParameterExprs() const { return GetRawOperandAsExprList(2); } + size_t GetSourceMemoryVersion() const { return GetRawOperandAsIndex(4); } + void SetDestMemoryVersion(size_t version) { GetRawOperandAsExpr(0).UpdateRawOperand(0, version); } + void SetSourceMemoryVersion(size_t version) { UpdateRawOperand(4, version); } + void SetOutputSSAVariables(const std::vector<SSAVariable>& vars) { GetRawOperandAsExpr(0).UpdateRawOperandAsSSAVariableList(1, vars); } + void SetParameterExprs(const std::vector<MediumLevelILInstruction>& params) { UpdateRawOperandAsExprList(2, params); } + void SetParameterExprs(const std::vector<ExprId>& params) { UpdateRawOperandAsExprList(2, params); } + }; + template <> struct MediumLevelILInstructionAccessor<MLIL_TAILCALL_UNTYPED_SSA>: public MediumLevelILInstructionBase + { + size_t GetDestMemoryVersion() const { return GetRawOperandAsExpr(0).GetRawOperandAsIndex(0); } + MediumLevelILSSAVariableList GetOutputSSAVariables() const { return GetRawOperandAsExpr(0).GetRawOperandAsSSAVariableList(1); } + MediumLevelILInstruction GetDestExpr() const { return GetRawOperandAsExpr(1); } + MediumLevelILSSAVariableList GetParameterSSAVariables() const { return GetRawOperandAsExpr(2).GetRawOperandAsSSAVariableList(1); } + size_t GetSourceMemoryVersion() const { return GetRawOperandAsExpr(2).GetRawOperandAsIndex(0); } + MediumLevelILInstruction GetStackExpr() const { return GetRawOperandAsExpr(3); } + void SetDestMemoryVersion(size_t version) { GetRawOperandAsExpr(0).UpdateRawOperand(0, version); } + void SetSourceMemoryVersion(size_t version) { GetRawOperandAsExpr(2).UpdateRawOperand(0, version); } + void SetOutputSSAVariables(const std::vector<SSAVariable>& vars) { GetRawOperandAsExpr(0).UpdateRawOperandAsSSAVariableList(1, vars); } + void SetParameterSSAVariables(const std::vector<SSAVariable>& vars) { GetRawOperandAsExpr(2).UpdateRawOperandAsSSAVariableList(1, vars); } + }; template <> struct MediumLevelILInstructionAccessor<MLIL_RET>: public MediumLevelILInstructionBase { diff --git a/python/__init__.py b/python/__init__.py index 05ba4c2b..aa7c1ed4 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,41 +19,115 @@ # IN THE SOFTWARE. +from __future__ import absolute_import import atexit import sys +import ctypes from time import gmtime + +# 2-3 compatibility +try: + import builtins # __builtins__ for python2 +except ImportError: + pass +def range(*args): + """ A Python2 and Python3 Compatible Range Generator """ + try: + return xrange(*args) + except NameError: + return builtins.range(*args) + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def cstr(arg): + if isinstance(arg, bytes) or arg is None: + return arg + else: + return arg.encode('charmap') + + +def pyNativeStr(arg): + if isinstance(arg, str): + return arg + else: + return arg.decode('charmap') + + # 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 .mediumlevelil 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 * -from .pluginmanager import * -from .setting import * -from .metadata import * -from .flowgraph import * +import binaryninja._binaryninjacore as core +# __all__ = [ +# "enums", +# "databuffer", +# "filemetadata", +# "fileaccessor", +# "binaryview", +# "transform", +# "architecture", +# "basicblock", +# "function", +# "log", +# "lowlevelil", +# "mediumlevelil", +# "types", +# "functionrecognizer", +# "update", +# "plugin", +# "callingconvention", +# "platform", +# "demangle", +# "mainthread", +# "interaction", +# "lineardisassembly", +# "undoaction", +# "highlight", +# "scriptingprovider", +# "pluginmanager", +# "setting", +# "metadata", +# "flowgraph", +# ] +from binaryninja.enums import * +from binaryninja.databuffer import * +from binaryninja.filemetadata import * +from binaryninja.fileaccessor import * +from binaryninja.binaryview import * +from binaryninja.transform import * +from binaryninja.architecture import * +from binaryninja.basicblock import * +from binaryninja.function import * +from binaryninja.log import * +from binaryninja.lowlevelil import * +from binaryninja.mediumlevelil import * +from binaryninja.types import * +from binaryninja.functionrecognizer import * +from binaryninja.update import * +from binaryninja.plugin import * +from binaryninja.callingconvention import * +from binaryninja.platform import * +from binaryninja.demangle import * +from binaryninja.mainthread import * +from binaryninja.interaction import * +from binaryninja.lineardisassembly import * +from binaryninja.undoaction import * +from binaryninja.highlight import * +from binaryninja.scriptingprovider import * +from binaryninja.downloadprovider import * +from binaryninja.pluginmanager import * +from binaryninja.setting import * +from binaryninja.metadata import * +from binaryninja.flowgraph import * def shutdown(): @@ -138,6 +212,20 @@ class _DestructionCallbackHandler(object): Function._unregister(func) +_plugin_init = False + + +def _init_plugins(): + global _plugin_init + if not _plugin_init: + _plugin_init = True + core.BNInitCorePlugins() + core.BNInitUserPlugins() + core.BNInitRepoPlugins() + if not core.BNIsLicenseValidated(): + raise RuntimeError("License is not valid. Please supply a valid license.") + + _destruct_callbacks = _DestructionCallbackHandler() bundled_plugin_path = core.BNGetBundledPluginDirectory() diff --git a/python/architecture.py b/python/architecture.py index c5f87ec6..aee7c301 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -18,55 +18,60 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import traceback import ctypes import abc # Binary Ninja components -import _binaryninjacore as core -from enums import (Endianness, ImplicitRegisterExtend, BranchType, +from binaryninja import _binaryninjacore as core +from binaryninja.enums import (Endianness, ImplicitRegisterExtend, BranchType, InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) -import startup -import function -import lowlevelil -import callingconvention -import platform -import log -import databuffer -import types +import binaryninja +from binaryninja import log +from binaryninja import lowlevelil +from binaryninja import types +from binaryninja import databuffer +from binaryninja import platform +from binaryninja import callingconvention + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _ArchitectureMetaClass(type): + @property def list(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() archs = core.BNGetArchitectureList(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(CoreArchitecture._from_cache(archs[i])) core.BNFreeArchitectureList(archs) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() archs = core.BNGetArchitectureList(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield CoreArchitecture._from_cache(archs[i]) finally: core.BNFreeArchitectureList(archs) def __getitem__(cls, name): - startup._init_plugins() + binaryninja._init_plugins() arch = core.BNGetArchitectureByName(name) if arch is None: raise KeyError("'%s' is not a valid architecture" % str(name)) return CoreArchitecture._from_cache(arch) def register(cls): - startup._init_plugins() + binaryninja._init_plugins() if cls.name is None: raise ValueError("architecture 'name' is not defined") arch = cls() @@ -80,12 +85,12 @@ class _ArchitectureMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) -class Architecture(object): +class Architecture(with_metaclass(_ArchitectureMetaClass, 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 + ``class Architecture`` has a metaclass with the additional methods ``register``, and supports iteration:: >>> #List the architectures @@ -130,11 +135,10 @@ class Architecture(object): semantic_class_for_flag_write_type = {} reg_stacks = {} intrinsics = {} - __metaclass__ = _ArchitectureMetaClass next_address = 0 def __init__(self): - startup._init_plugins() + binaryninja._init_plugins() if self.__class__.opcode_display_length > self.__class__.max_instr_length: self.__class__.opcode_display_length = self.__class__.max_instr_length @@ -365,11 +369,11 @@ class Architecture(object): for intrinsic in self.__class__.intrinsics.keys(): if intrinsic not in self._intrinsics: info = self.__class__.intrinsics[intrinsic] - for i in xrange(0, len(info.inputs)): + for i in range(0, len(info.inputs)): if isinstance(info.inputs[i], types.Type): - info.inputs[i] = function.IntrinsicInput(info.inputs[i]) + info.inputs[i] = binaryninja.function.IntrinsicInput(info.inputs[i]) elif isinstance(info.inputs[i], tuple): - info.inputs[i] = function.IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) + info.inputs[i] = binaryninja.function.IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) info.index = intrinsic_index self._intrinsics[intrinsic] = intrinsic_index self._intrinsics_by_index[intrinsic_index] = (intrinsic, info) @@ -392,12 +396,17 @@ class Architecture(object): return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + + @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): + for i in range(0, count.value): result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) core.BNFreeRegisterList(regs) return result @@ -408,7 +417,7 @@ class Architecture(object): count = ctypes.c_ulonglong() cc = core.BNGetArchitectureCallingConventions(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) result[obj.name] = obj core.BNFreeCallingConventionList(cc, count) @@ -499,7 +508,7 @@ class Architecture(object): result[0].archTransitionByTargetAddr = info.arch_transition_by_target_addr result[0].branchDelay = info.branch_delay result[0].branchCount = len(info.branches) - for i in xrange(0, len(info.branches)): + for i in range(0, len(info.branches)): if isinstance(info.branches[i].type, str): result[0].branchType[i] = BranchType[info.branches[i].type] else: @@ -525,7 +534,7 @@ class Architecture(object): length[0] = info[1] count[0] = len(tokens) token_buf = (core.BNInstructionTextToken * len(tokens))() - for i in xrange(0, len(tokens)): + for i in range(0, len(tokens)): if isinstance(tokens[i].type, str): token_buf[i].type = InstructionTextTokenType[tokens[i].type] else: @@ -615,10 +624,10 @@ class Architecture(object): def _get_full_width_registers(self, ctxt, count): try: - regs = self._full_width_regs.values() + regs = list(self._full_width_regs.values()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -630,10 +639,10 @@ class Architecture(object): def _get_all_registers(self, ctxt, count): try: - regs = self._regs_by_index.keys() + regs = list(self._regs_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -645,10 +654,10 @@ class Architecture(object): def _get_all_flags(self, ctxt, count): try: - flags = self._flags_by_index.keys() + flags = list(self._flags_by_index.keys()) count[0] = len(flags) flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): + for i in range(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) @@ -660,10 +669,10 @@ class Architecture(object): def _get_all_flag_write_types(self, ctxt, count): try: - write_types = self._flag_write_types_by_index.keys() + write_types = list(self._flag_write_types_by_index.keys()) count[0] = len(write_types) type_buf = (ctypes.c_uint * len(write_types))() - for i in xrange(0, len(write_types)): + for i in range(0, len(write_types)): type_buf[i] = write_types[i] result = ctypes.cast(type_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, type_buf) @@ -675,10 +684,10 @@ class Architecture(object): def _get_all_semantic_flag_classes(self, ctxt, count): try: - sem_classes = self._semantic_flag_classes_by_index.keys() + sem_classes = list(self._semantic_flag_classes_by_index.keys()) count[0] = len(sem_classes) class_buf = (ctypes.c_uint * len(sem_classes))() - for i in xrange(0, len(sem_classes)): + for i in range(0, len(sem_classes)): class_buf[i] = sem_classes[i] result = ctypes.cast(class_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, class_buf) @@ -690,10 +699,10 @@ class Architecture(object): def _get_all_semantic_flag_groups(self, ctxt, count): try: - sem_groups = self._semantic_flag_groups_by_index.keys() + sem_groups = list(self._semantic_flag_groups_by_index.keys()) count[0] = len(sem_groups) group_buf = (ctypes.c_uint * len(sem_groups))() - for i in xrange(0, len(sem_groups)): + for i in range(0, len(sem_groups)): group_buf[i] = sem_groups[i] result = ctypes.cast(group_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, group_buf) @@ -726,7 +735,7 @@ class Architecture(object): flags.append(self._flags[name]) count[0] = len(flags) flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): + for i in range(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) @@ -744,7 +753,7 @@ class Architecture(object): flags = [] count[0] = len(flags) flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): + for i in range(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) @@ -792,7 +801,7 @@ class Architecture(object): flags = [] count[0] = len(flags) flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): + for i in range(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) @@ -819,7 +828,7 @@ class Architecture(object): 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): + for i in range(operand_count): if operands[i].constant: operand_list.append(operands[i].value) elif lowlevelil.LLIL_REG_IS_TEMP(operands[i].reg): @@ -908,7 +917,7 @@ class Architecture(object): try: count[0] = len(self.global_regs) reg_buf = (ctypes.c_uint * len(self.global_regs))() - for i in xrange(0, len(self.global_regs)): + for i in range(0, len(self.global_regs)): reg_buf[i] = self._all_regs[self.global_regs[i]] result = ctypes.cast(reg_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, reg_buf) @@ -929,10 +938,10 @@ class Architecture(object): def _get_all_register_stacks(self, ctxt, count): try: - regs = self._reg_stacks_by_index.keys() + regs = list(self._reg_stacks_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -980,10 +989,10 @@ class Architecture(object): def _get_all_intrinsics(self, ctxt, count): try: - regs = self._intrinsics_by_index.keys() + regs = list(self._intrinsics_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -999,7 +1008,7 @@ class Architecture(object): inputs = self._intrinsics_by_index[intrinsic][1].inputs count[0] = len(inputs) input_buf = (core.BNNameAndType * len(inputs))() - for i in xrange(0, len(inputs)): + for i in range(0, len(inputs)): input_buf[i].name = inputs[i].name input_buf[i].type = core.BNNewTypeReference(inputs[i].type.handle) input_buf[i].typeConfidence = inputs[i].type.confidence @@ -1020,7 +1029,7 @@ class Architecture(object): raise ValueError("freeing name and type list that wasn't allocated") name_and_types = self._pending_name_and_type_lists[buf.value][1] count = self._pending_name_and_type_lists[buf.value][2] - for i in xrange(0, count): + for i in range(0, count): core.BNFreeType(name_and_types[i].type) del self._pending_name_and_type_lists[buf.value] except (ValueError, KeyError): @@ -1032,7 +1041,7 @@ class Architecture(object): outputs = self._intrinsics_by_index[intrinsic][1].outputs count[0] = len(outputs) output_buf = (core.BNTypeWithConfidence * len(outputs))() - for i in xrange(0, len(outputs)): + for i in range(0, len(outputs)): output_buf[i].type = core.BNNewTypeReference(outputs[i].handle) output_buf[i].confidence = outputs[i].confidence result = ctypes.cast(output_buf, ctypes.c_void_p) @@ -1052,7 +1061,7 @@ class Architecture(object): raise ValueError("freeing type list that wasn't allocated") types = self._pending_type_lists[buf.value][1] count = self._pending_type_lists[buf.value][2] - for i in xrange(0, count): + for i in range(0, count): core.BNFreeType(types[i].type) del self._pending_type_lists[buf.value] except (ValueError, KeyError): @@ -1064,7 +1073,6 @@ class Architecture(object): 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)) @@ -1701,7 +1709,7 @@ class Architecture(object): :rtype: LowLevelILExpr index """ operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): + for i in range(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False operand_list[i].reg = self.regs[operands[i]].index @@ -1756,7 +1764,7 @@ class Architecture(object): count = ctypes.c_ulonglong() regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) core.BNFreeRegisterList(regs) return result @@ -2065,15 +2073,15 @@ class CoreArchitecture(Architecture): self._regs_by_index = {} self._full_width_regs = {} self.__dict__["regs"] = {} - for i in xrange(0, count.value): + for i in range(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, + self.regs[name] = binaryninja.function.RegisterInfo(full_width_reg, info.size, info.offset, ImplicitRegisterExtend(info.extend), regs[i]) self._all_regs[name] = regs[i] self._regs_by_index[regs[i]] = name - for i in xrange(0, count.value): + for i in range(0, count.value): info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) if full_width_reg not in self._full_width_regs: @@ -2085,7 +2093,7 @@ class CoreArchitecture(Architecture): self._flags = {} self._flags_by_index = {} self.__dict__["flags"] = [] - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureFlagName(self.handle, flags[i]) self._flags[name] = flags[i] self._flags_by_index[flags[i]] = name @@ -2097,7 +2105,7 @@ class CoreArchitecture(Architecture): self._flag_write_types = {} self._flag_write_types_by_index = {} self.__dict__["flag_write_types"] = [] - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureFlagWriteTypeName(self.handle, write_types[i]) self._flag_write_types[name] = write_types[i] self._flag_write_types_by_index[write_types[i]] = name @@ -2109,7 +2117,7 @@ class CoreArchitecture(Architecture): self._semantic_flag_classes = {} self._semantic_flag_classes_by_index = {} self.__dict__["semantic_flag_classes"] = [] - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureSemanticFlagClassName(self.handle, sem_classes[i]) self._semantic_flag_classes[name] = sem_classes[i] self._semantic_flag_classes_by_index[sem_classes[i]] = name @@ -2121,7 +2129,7 @@ class CoreArchitecture(Architecture): self._semantic_flag_groups = {} self._semantic_flag_groups_by_index = {} self.__dict__["semantic_flag_groups"] = [] - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureSemanticFlagGroupName(self.handle, sem_groups[i]) self._semantic_flag_groups[name] = sem_groups[i] self._semantic_flag_groups_by_index[sem_groups[i]] = name @@ -2140,7 +2148,7 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, 0, count) flag_names = [] - for i in xrange(0, count.value): + for i in range(0, count.value): flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) self.__dict__["flags_required_for_flag_condition"][cond] = flag_names @@ -2153,7 +2161,7 @@ class CoreArchitecture(Architecture): self._semantic_flag_groups[group], count) flag_indexes = [] flag_names = [] - for i in xrange(0, count.value): + for i in range(0, count.value): flag_indexes.append(flags[i]) flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) @@ -2168,7 +2176,7 @@ class CoreArchitecture(Architecture): self._semantic_flag_groups[group], count) class_index_cond = {} class_cond = {} - for i in xrange(0, count.value): + for i in range(0, count.value): class_index_cond[conditions[i].semanticClass] = conditions[i].condition if conditions[i].semanticClass == 0: class_cond[None] = conditions[i].condition @@ -2186,7 +2194,7 @@ class CoreArchitecture(Architecture): self._flag_write_types[write_type], count) flag_indexes = [] flag_names = [] - for i in xrange(0, count.value): + for i in range(0, count.value): flag_indexes.append(flags[i]) flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) @@ -2208,7 +2216,7 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() regs = core.BNGetArchitectureGlobalRegisters(self.handle, count) self.__dict__["global_regs"] = [] - for i in xrange(0, count.value): + for i in range(0, count.value): self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) core.BNFreeRegisterList(regs) @@ -2217,17 +2225,17 @@ class CoreArchitecture(Architecture): self._all_reg_stacks = {} self._reg_stacks_by_index = {} self.__dict__["reg_stacks"] = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureRegisterStackName(self.handle, regs[i]) info = core.BNGetArchitectureRegisterStackInfo(self.handle, regs[i]) storage = [] - for j in xrange(0, info.storageCount): + for j in range(0, info.storageCount): storage.append(core.BNGetArchitectureRegisterName(self.handle, info.firstStorageReg + j)) top_rel = [] - for j in xrange(0, info.topRelativeCount): + for j in range(0, info.topRelativeCount): top_rel.append(core.BNGetArchitectureRegisterName(self.handle, info.firstTopRelativeReg + j)) top = core.BNGetArchitectureRegisterName(self.handle, info.stackTopReg) - self.reg_stacks[name] = function.RegisterStackInfo(storage, top_rel, top, regs[i]) + self.reg_stacks[name] = binaryninja.function.RegisterStackInfo(storage, top_rel, top, regs[i]) self._all_reg_stacks[name] = regs[i] self._reg_stacks_by_index[regs[i]] = name core.BNFreeRegisterList(regs) @@ -2237,23 +2245,23 @@ class CoreArchitecture(Architecture): self._intrinsics = {} self._intrinsics_by_index = {} self.__dict__["intrinsics"] = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = core.BNGetArchitectureIntrinsicName(self.handle, intrinsics[i]) input_count = ctypes.c_ulonglong() inputs = core.BNGetArchitectureIntrinsicInputs(self.handle, intrinsics[i], input_count) input_list = [] - for j in xrange(0, input_count.value): + for j in range(0, input_count.value): input_name = inputs[j].name type_obj = types.Type(core.BNNewTypeReference(inputs[j].type), confidence = inputs[j].typeConfidence) - input_list.append(function.IntrinsicInput(type_obj, input_name)) + input_list.append(binaryninja.function.IntrinsicInput(type_obj, input_name)) core.BNFreeNameAndTypeList(inputs, input_count.value) output_count = ctypes.c_ulonglong() outputs = core.BNGetArchitectureIntrinsicOutputs(self.handle, intrinsics[i], output_count) output_list = [] - for j in xrange(0, output_count.value): + for j in range(0, output_count.value): output_list.append(types.Type(core.BNNewTypeReference(outputs[j].type), confidence = outputs[j].confidence)) core.BNFreeOutputTypeList(outputs, output_count.value) - self.intrinsics[name] = function.IntrinsicInfo(input_list, output_list) + self.intrinsics[name] = binaryninja.function.IntrinsicInfo(input_list, output_list) self._intrinsics[name] = intrinsics[i] self._intrinsics_by_index[intrinsics[i]] = (name, self.intrinsics[name]) core.BNFreeRegisterList(intrinsics) @@ -2285,16 +2293,15 @@ class CoreArchitecture(Architecture): :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 = binaryninja.function.InstructionInfo() result.length = info.length result.arch_transition_by_target_addr = info.archTransitionByTargetAddr result.branch_delay = info.branchDelay - for i in xrange(0, info.branchCount): + for i in range(0, info.branchCount): target = info.branchTarget[i] if info.branchArch[i]: arch = CoreArchitecture._from_cache(info.branchArch[i]) @@ -2313,7 +2320,6 @@ class CoreArchitecture(Architecture): :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) @@ -2323,7 +2329,7 @@ class CoreArchitecture(Architecture): if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): return None, 0 result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -2332,7 +2338,7 @@ class CoreArchitecture(Architecture): context = tokens[i].context confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeInstructionText(tokens, count.value) return result, length.value @@ -2350,7 +2356,6 @@ class CoreArchitecture(Architecture): :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))() @@ -2370,7 +2375,7 @@ class CoreArchitecture(Architecture): """ flag = self.get_flag_index(flag) operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): + for i in range(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False operand_list[i].reg = self.regs[operands[i]].index @@ -2410,8 +2415,8 @@ class CoreArchitecture(Architecture): :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: the bytes for the assembled instructions + :rtype: Python3 - a 'bytes' object; Python2 - a 'str' :Example: >>> arch.assemble("je 10") @@ -2421,8 +2426,11 @@ class CoreArchitecture(Architecture): 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 + raise ValueError("Could not assemble") + if isinstance(str(result), bytes): + return str(result) + else: + return bytes(result) def is_never_branch_patch_available(self, data, addr): """ @@ -2440,7 +2448,6 @@ class CoreArchitecture(Architecture): 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)) @@ -2462,7 +2469,6 @@ class CoreArchitecture(Architecture): 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)) @@ -2483,7 +2489,6 @@ class CoreArchitecture(Architecture): 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)) @@ -2507,7 +2512,6 @@ class CoreArchitecture(Architecture): 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)) @@ -2529,7 +2533,6 @@ class CoreArchitecture(Architecture): 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)) @@ -2549,7 +2552,6 @@ class CoreArchitecture(Architecture): '\\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)): @@ -2576,7 +2578,6 @@ class CoreArchitecture(Architecture): (['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)): @@ -2604,7 +2605,6 @@ class CoreArchitecture(Architecture): (['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)): @@ -2628,7 +2628,6 @@ class CoreArchitecture(Architecture): (['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): @@ -2655,7 +2654,7 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, sem_class, count) flag_names = [] - for i in xrange(0, count.value): + for i in range(0, count.value): flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) return flag_names diff --git a/python/basicblock.py b/python/basicblock.py index 3f615bba..353771d9 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -21,11 +21,13 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType -import architecture -import highlight -import function +import binaryninja +from binaryninja import highlight +from binaryninja import _binaryninjacore as core +from binaryninja.enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType + +# 2-3 compatibility +from binaryninja import range class BasicBlockEdge(object): @@ -87,7 +89,7 @@ class BasicBlock(object): func = core.BNGetBasicBlockFunction(self.handle) if func is None: return None - self._func = function.Function(self.view, func) + self._func =binaryninja.function.Function(self.view, func) return self._func @property @@ -100,7 +102,7 @@ class BasicBlock(object): arch = core.BNGetBasicBlockArchitecture(self.handle) if arch is None: return None - self._arch = architecture.CoreArchitecture._from_cache(arch) + self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch) return self._arch @property @@ -129,7 +131,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong(0) edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): branch_type = BranchType(edges[i].type) if edges[i].target: target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target)) @@ -145,7 +147,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong(0) edges = core.BNGetBasicBlockIncomingEdges(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): branch_type = BranchType(edges[i].type) if edges[i].target: target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target)) @@ -171,7 +173,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominators(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -182,7 +184,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockStrictDominators(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -201,7 +203,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -212,7 +214,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -225,7 +227,7 @@ class BasicBlock(object): @property def disassembly_text(self): """ - ``disassembly_text`` property which returns a list of function.DisassemblyTextLine objects for the current basic block. + ``disassembly_text`` property which returns a list of binaryninja.function.DisassemblyTextLine objects for the current basic block. :Example: >>> current_basic_block.disassembly_text @@ -269,12 +271,12 @@ class BasicBlock(object): if len(blocks) == 0: return [] block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() - for i in xrange(len(blocks)): + for i in range(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): + for i in range(0, count.value): result.append(BasicBlock(blocks[0].view, core.BNNewBasicBlockReference(out_blocks[i]))) core.BNFreeBasicBlockList(out_blocks, count.value) return result @@ -305,6 +307,8 @@ class BasicBlock(object): inst_info = self.arch.get_instruction_info(data, idx) inst_text = self.arch.get_instruction_text(data, idx) + if inst_info is None: + break yield inst_text idx += inst_info.length @@ -313,7 +317,7 @@ class BasicBlock(object): def get_disassembly_text(self, settings=None): """ - ``get_disassembly_text`` returns a list of function.DisassemblyTextLine objects for the current basic block. + ``get_disassembly_text`` returns a list of binaryninja.function.DisassemblyTextLine objects for the current basic block. :param DisassemblySettings settings: (optional) DisassemblySettings object :Example: @@ -328,7 +332,7 @@ class BasicBlock(object): count = ctypes.c_ulonglong() lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(self, 'il_function'): il_instr = self.il_function[lines[i].instrIndex] @@ -336,7 +340,7 @@ class BasicBlock(object): il_instr = None color = highlight.HighlightColor._from_core_struct(lines[i].highlight) tokens = [] - for j in xrange(0, lines[i].count): + for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text value = lines[i].tokens[j].value @@ -345,8 +349,8 @@ class BasicBlock(object): context = lines[i].tokens[j].context confidence = lines[i].tokens[j].confidence address = lines[i].tokens[j].address - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) - result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + tokens.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.DisassemblyTextLine(tokens, addr, il_instr, color)) core.BNFreeDisassemblyTextLines(lines, count.value) return result diff --git a/python/binaryview.py b/python/binaryview.py index ede279ad..d84ed0aa 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -22,26 +22,25 @@ import struct import traceback import ctypes import abc -import threading # Binary Ninja components -import _binaryninjacore as core -from enums import (AnalysisState, SymbolType, InstructionTextTokenType, +from binaryninja import _binaryninjacore as core +from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics) -import function -import startup -import architecture -import platform -import associateddatastore -import fileaccessor -import filemetadata -import log -import databuffer -import basicblock -import types -import lineardisassembly -import metadata -import highlight +import binaryninja +from binaryninja import associateddatastore # required for _BinaryViewAssociatedDataStore +from binaryninja import log +from binaryninja import types +from binaryninja import fileaccessor +from binaryninja import databuffer +from binaryninja import basicblock +from binaryninja import lineardisassembly +from binaryninja import metadata +from binaryninja import highlight + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class BinaryDataNotification(object): @@ -100,21 +99,23 @@ class StringReference(object): @property def value(self): - return self.view.read(self.start, self.length) + return binaryninja.pyNativeStr(self.view.read(self.start, self.length)) def __repr__(self): return "<%s: %#x, len %#x>" % (self.type, self.start, self.length) +_pending_analysis_completion_events = {} class AnalysisCompletionEvent(object): """ The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving callbacks when analysis is complete. The callback runs once. A completion event must be added - for each new analysis in order to be notified of each analysis completion. + for each new analysis in order to be notified of each analysis completion. The + AnalysisCompletionEvent class takes responcibility for keeping track of the object's lifetime. :Example: >>> def on_complete(self): - ... print "Analysis Complete", self.view + ... print("Analysis Complete", self.view) ... >>> evt = AnalysisCompletionEvent(bv, on_complete) >>> @@ -124,11 +125,19 @@ class AnalysisCompletionEvent(object): self.callback = callback self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb) + global _pending_analysis_completion_events + _pending_analysis_completion_events[id(self)] = self def __del__(self): + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] core.BNFreeAnalysisCompletionEvent(self.handle) def _notify(self, ctxt): + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] try: self.callback(self) except: @@ -144,6 +153,30 @@ class AnalysisCompletionEvent(object): """ self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle) + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] + + +class ActiveAnalysisInfo(object): + def __init__(self, func, analysis_time, update_count, submit_count): + self.func = func + self.analysis_time = analysis_time + self.update_count = update_count + self.submit_count = submit_count + + def __repr__(self): + return "<ActiveAnalysisInfo %s, analysis_time %d, update_count %d, submit_count %d>" % (self.func, self.analysis_time, self.update_count, self.submit_count) + + +class AnalysisInfo(object): + def __init__(self, state, analysis_time, active_info): + self.state = AnalysisState(state) + self.analysis_time = analysis_time + self.active_info = active_info + + def __repr__(self): + return "<AnalysisInfo %s, analysis_time %d, active_info %s>" % (self.state, self.analysis_time, self.active_info) class AnalysisProgress(object): @@ -157,6 +190,8 @@ class AnalysisProgress(object): return "Disassembling (%d/%d)" % (self.count, self.total) if self.state == AnalysisState.AnalyzeState: return "Analyzing (%d/%d)" % (self.count, self.total) + if self.state == AnalysisState.ExtendedAnalyzeState: + return "Extended Analysis" return "Idle" def __repr__(self): @@ -220,25 +255,25 @@ class BinaryDataNotificationCallbacks(object): def _function_added(self, ctxt, view, func): try: - self.notify.function_added(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + self.notify.function_added(self.view, binaryninja.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))) + self.notify.function_removed(self.view, binaryninja.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))) + self.notify.function_updated(self.view, binaryninja.function.Function(self.view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) def _function_update_requested(self, ctxt, view, func): try: - self.notify.function_update_requested(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + self.notify.function_update_requested(self.view, binaryninja.function.Function(self.view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) @@ -297,38 +332,38 @@ class BinaryDataNotificationCallbacks(object): class _BinaryViewTypeMetaclass(type): + @property def list(self): """List all BinaryView types (read-only)""" - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetBinaryViewTypes(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(BinaryViewType(types[i])) core.BNFreeBinaryViewTypeList(types) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetBinaryViewTypes(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield BinaryViewType(types[i]) finally: core.BNFreeBinaryViewTypeList(types) def __getitem__(self, value): - startup._init_plugins() + binaryninja._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 +class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): def __init__(self, handle): self.handle = core.handle_of_type(handle, core.BNBinaryViewType) @@ -344,6 +379,11 @@ class BinaryViewType(object): return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + + @property def name(self): """BinaryView name (read-only)""" return core.BNGetBinaryViewTypeName(self.handle) @@ -384,7 +424,7 @@ class BinaryViewType(object): if f is None or f.read(len(sqlite)) != sqlite: return None f.close() - view = filemetadata.FileMetadata().open_existing_database(filename) + view = binaryninja.filemetadata.FileMetadata().open_existing_database(filename) else: view = BinaryView.open(filename) @@ -397,6 +437,9 @@ class BinaryViewType(object): else: bv = cls[available.name].open(filename) + if bv is None: + raise Exception("Unknown Architecture/Architecture Not Found (check plugins folder)") + if update_analysis: bv.update_analysis_and_wait() return bv @@ -412,7 +455,7 @@ class BinaryViewType(object): arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) if arch is None: return None - return architecture.CoreArchitecture._from_cache(arch) + return binaryninja.architecture.CoreArchitecture._from_cache(arch) def register_platform(self, ident, arch, plat): core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle) @@ -424,7 +467,7 @@ class BinaryViewType(object): plat = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) if plat is None: return None - return platform.Platform(None, plat) + return binaryninja.platform.Platform(None, plat) class Segment(object): @@ -566,17 +609,17 @@ class BinaryView(object): 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)) + self.file = binaryninja.filemetadata.FileMetadata(handle=core.BNGetFileForView(handle)) else: self.file = file_metadata elif self.__class__ is BinaryView: - startup._init_plugins() + binaryninja._init_plugins() if file_metadata is None: - file_metadata = filemetadata.FileMetadata() + file_metadata = binaryninja.filemetadata.FileMetadata() self.handle = core.BNCreateBinaryDataView(file_metadata.handle) - self.file = filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) + self.file = binaryninja.filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) else: - startup._init_plugins() + binaryninja._init_plugins() if not self.__class__._registered: raise TypeError("view type not registered") self._cb = core.BNCustomBinaryView() @@ -619,7 +662,7 @@ class BinaryView(object): @classmethod def register(cls): - startup._init_plugins() + binaryninja._init_plugins() if cls.name is None: raise ValueError("view 'name' not defined") if cls.long_name is None: @@ -634,7 +677,7 @@ class BinaryView(object): @classmethod def _create(cls, ctxt, data): try: - file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) if view is None: return None @@ -653,14 +696,14 @@ class BinaryView(object): @classmethod def open(cls, src, file_metadata=None): - startup._init_plugins() + binaryninja._init_plugins() if isinstance(src, fileaccessor.FileAccessor): if file_metadata is None: - file_metadata = filemetadata.FileMetadata() + file_metadata = binaryninja.filemetadata.FileMetadata() view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) else: if file_metadata is None: - file_metadata = filemetadata.FileMetadata(str(src)) + file_metadata = binaryninja.filemetadata.FileMetadata(str(src)) view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) if view is None: return None @@ -669,9 +712,9 @@ class BinaryView(object): @classmethod def new(cls, data=None, file_metadata=None): - startup._init_plugins() + binaryninja._init_plugins() if file_metadata is None: - file_metadata = filemetadata.FileMetadata() + file_metadata = binaryninja.filemetadata.FileMetadata() if data is None: view = core.BNCreateBinaryDataView(file_metadata.handle) else: @@ -755,8 +798,8 @@ class BinaryView(object): 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])) + for i in range(0, count.value): + yield binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i])) finally: core.BNFreeFunctionList(funcs, count.value) @@ -824,7 +867,7 @@ class BinaryView(object): arch = core.BNGetDefaultArchitecture(self.handle) if arch is None: return None - return architecture.CoreArchitecture._from_cache(handle=arch) + return binaryninja.architecture.CoreArchitecture._from_cache(handle=arch) @arch.setter def arch(self, value): @@ -839,7 +882,7 @@ class BinaryView(object): plat = core.BNGetDefaultPlatform(self.handle) if plat is None: return None - return platform.Platform(self.arch, handle=plat) + return binaryninja.platform.Platform(self.arch, handle=plat) @platform.setter def platform(self, value): @@ -874,8 +917,8 @@ class BinaryView(object): 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]))) + for i in range(0, count.value): + result.append(binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) return result @@ -890,7 +933,7 @@ class BinaryView(object): func = core.BNGetAnalysisEntryPoint(self.handle) if func is None: return None - return function.Function(self, func) + return binaryninja.function.Function(self, func) @property def symbols(self): @@ -898,7 +941,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) syms = core.BNGetSymbols(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) result[sym.raw_name] = sym core.BNFreeSymbolList(syms, count.value) @@ -915,7 +958,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) types = core.BNGetBinaryViewTypesForData(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(BinaryViewType(types[i])) core.BNFreeBinaryViewTypeList(types) return result @@ -935,6 +978,20 @@ class BinaryView(object): self.file.saved = value @property + def analysis_info(self): + """Relevant analysis information with list of functions under active analysis (read-only)""" + info_ref = core.BNGetAnalysisInfo(self.handle) + info = info_ref[0] + active_info_list = [] + for i in xrange(0, info.count): + func = binaryninja.function.Function(self, core.BNNewFunctionReference(info.activeInfo[i].func)) + active_info = ActiveAnalysisInfo(func, info.activeInfo[i].analysisTime, info.activeInfo[i].updateCount, info.activeInfo[i].submitCount) + active_info_list.append(active_info) + result = AnalysisInfo(info.state, info.analysisTime, active_info_list) + core.BNFreeAnalysisInfo(info_ref) + return result + + @property def analysis_progress(self): """Status of current analysis (read-only)""" result = core.BNGetAnalysisProgress(self.handle) @@ -951,7 +1008,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) var_list = core.BNGetDataVariables(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): addr = var_list[i].address var_type = types.Type(core.BNNewTypeReference(var_list[i].type), platform = self.platform, confidence = var_list[i].typeConfidence) auto_discovered = var_list[i].autoDiscovered @@ -965,7 +1022,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) type_list = core.BNGetAnalysisTypeList(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self.platform) core.BNFreeTypeList(type_list, count.value) @@ -977,7 +1034,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) segment_list = core.BNGetSegments(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(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, segment_list[i].autoDefined)) core.BNFreeSegmentList(segment_list) @@ -989,7 +1046,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) section_list = core.BNGetSections(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(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, @@ -1003,7 +1060,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) range_list = core.BNGetAllocatedRanges(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(AddressRange(range_list[i].start, range_list[i].end)) core.BNFreeAddressRanges(range_list) return result @@ -1023,7 +1080,15 @@ class BinaryView(object): def global_pointer_value(self): """Discovered value of the global pointer register, if the binary uses one (read-only)""" result = core.BNGetGlobalPointerValue(self.handle) - return function.RegisterValue(self.arch, result.value, confidence = result.confidence) + return binaryninja.function.RegisterValue(self.arch, result.value, confidence = result.confidence) + + @property + def parameters_for_analysis(self): + return core.BNGetParametersForAnalysis(self.handle) + + @parameters_for_analysis.setter + def parameters_for_analysis(self, params): + core.BNSetParametersForAnalysis(self.handle, params) @property def max_function_size_for_analysis(self): @@ -1680,10 +1745,13 @@ class BinaryView(object): """ ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. + Note: Python2 returns a str, but Python3 returns a bytes object. str(DataBufferObject) will + still get you a str in either case. + :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 + :rtype: python2 - str; python3 - bytes :Example: >>> #Opening a x86_64 Mach-O binary @@ -1692,7 +1760,7 @@ class BinaryView(object): \'\\xcf\\xfa\\xed\\xfe\' """ buf = databuffer.DataBuffer(handle=core.BNReadViewBuffer(self.handle, addr, length)) - return str(buf) + return bytes(buf) def write(self, addr, data): """ @@ -1711,6 +1779,8 @@ class BinaryView(object): >>> bv.read(0,4) 'AAAA' """ + if not isinstance(data, bytes): + raise TypeError("Must be bytes") buf = databuffer.DataBuffer(data) return core.BNWriteViewBuffer(self.handle, addr, buf.handle) @@ -1729,6 +1799,8 @@ class BinaryView(object): >>> bv.read(0,8) 'BBBBAAAA' """ + if not isinstance(data, bytes): + raise TypeError("Must be bytes") buf = databuffer.DataBuffer(data) return core.BNInsertViewBuffer(self.handle, addr, buf.handle) @@ -2121,7 +2193,7 @@ class BinaryView(object): func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) if func is None: return None - return function.Function(self, func) + return binaryninja.function.Function(self, func) def get_functions_at(self, addr): """ @@ -2137,8 +2209,8 @@ class BinaryView(object): 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]))) + for i in range(0, count.value): + result.append(binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) return result @@ -2146,7 +2218,7 @@ class BinaryView(object): func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) if func is None: return None - return function.Function(self, func) + return binaryninja.function.Function(self, func) def get_basic_blocks_at(self, addr): """ @@ -2159,7 +2231,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksForAddress(self.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(basicblock.BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -2175,7 +2247,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksStartingAtAddress(self.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(basicblock.BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -2206,17 +2278,17 @@ class BinaryView(object): else: refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): if refs[i].func: - func = function.Function(self, core.BNNewFunctionReference(refs[i].func)) + func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) else: func = None if refs[i].arch: - arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) else: arch = None addr = refs[i].addr - result.append(architecture.ReferenceSource(func, arch, addr)) + result.append(binaryninja.architecture.ReferenceSource(func, arch, addr)) core.BNFreeCodeReferences(refs, count.value) return result @@ -2272,7 +2344,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) syms = core.BNGetSymbolsByName(self.handle, name, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result @@ -2297,7 +2369,7 @@ class BinaryView(object): else: syms = core.BNGetSymbolsInRange(self.handle, start, length, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result @@ -2326,7 +2398,7 @@ class BinaryView(object): else: syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result @@ -2721,9 +2793,11 @@ class BinaryView(object): if start is None: strings = core.BNGetStrings(self.handle, count) else: + if length is None: + length = self.end - start strings = core.BNGetStringsInRange(self.handle, start, length, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(StringReference(self, StringType(strings[i].type), strings[i].start, strings[i].length)) core.BNFreeStringReferenceList(strings) return result @@ -2731,7 +2805,9 @@ class BinaryView(object): 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. + This is helpful when using ``update_analysis`` which does not wait for analysis completion before returning. + + The callee of this function is not resposible for maintaining the lifetime of the returned AnalysisCompletionEvent object. :param callable() callback: A function to be called with no parameters when analysis has completed. :return: An initialized AnalysisCompletionEvent object. @@ -2739,7 +2815,7 @@ class BinaryView(object): :Example: >>> def completionEvent(): - ... print "done" + ... print("done") ... >>> bv.add_analysis_completion_event(completionEvent) <binaryninja.AnalysisCompletionEvent object at 0x10a2c9f10> @@ -2933,7 +3009,7 @@ class BinaryView(object): func = None block = None if pos.function: - func = function.Function(self, pos.function) + func = binaryninja.function.Function(self, pos.function) if pos.block: block = basicblock.BasicBlock(self, pos.block) return lineardisassembly.LinearDisassemblyPosition(func, block, pos.address) @@ -2955,17 +3031,17 @@ class BinaryView(object): lines = api(self.handle, pos_obj, settings, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): func = None block = None if lines[i].function: - func = function.Function(self, core.BNNewFunctionReference(lines[i].function)) + func = binaryninja.function.Function(self, core.BNNewFunctionReference(lines[i].function)) if lines[i].block: block = basicblock.BasicBlock(self, core.BNNewBasicBlockReference(lines[i].block)) color = highlight.HighlightColor._from_core_struct(lines[i].contents.highlight) addr = lines[i].contents.addr tokens = [] - for j in xrange(0, lines[i].contents.count): + for j in range(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 @@ -2974,14 +3050,14 @@ class BinaryView(object): context = lines[i].contents.tokens[j].context confidence = lines[i].contents.tokens[j].confidence address = lines[i].contents.tokens[j].address - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) - contents = function.DisassemblyTextLine(tokens, addr, color = color) + tokens.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + contents = binaryninja.function.DisassemblyTextLine(tokens, addr, color = color) 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) + func = binaryninja.function.Function(self, pos_obj.function) if pos_obj.block: block = basicblock.BasicBlock(self, pos_obj.block) pos.function = func @@ -3048,7 +3124,7 @@ class BinaryView(object): >>> settings = DisassemblySettings() >>> lines = bv.get_linear_disassembly(settings) >>> for line in lines: - ... print line + ... print(line) ... break ... cf fa ed fe 07 00 00 01 ........ @@ -3411,7 +3487,7 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) section_list = core.BNGetSectionsAt(self.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(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, @@ -3431,11 +3507,11 @@ class BinaryView(object): 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] + for i in range(0, len(name_list)): + incoming_names[i] = binaryninja.cstr(name_list[i]) outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) result = [] - for i in xrange(0, len(name_list)): + for i in range(0, len(name_list)): result.append(str(outgoing_names[i])) core.BNFreeStringList(outgoing_names, len(name_list)) return result diff --git a/python/callingconvention.py b/python/callingconvention.py index e3ec7261..6b906184 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -22,13 +22,13 @@ import traceback import ctypes # Binary Ninja components -import _binaryninjacore as core -import architecture -import log -import types -import function -import binaryview -from enums import VariableSourceType +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja import log +from binaryninja.enums import VariableSourceType + +# 2-3 compatibility +from binaryninja import range class CallingConvention(object): @@ -47,7 +47,7 @@ class CallingConvention(object): _registered_calling_conventions = [] - def __init__(self, arch=None, name=None, handle=None, confidence=types.max_confidence): + def __init__(self, arch=None, name=None, handle=None, confidence=binaryninja.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") @@ -75,7 +75,7 @@ class CallingConvention(object): self.__class__._registered_calling_conventions.append(self) else: self.handle = handle - self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(self.handle)) + self.arch = binaryninja.architecture.CoreArchitecture._from_cache(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) @@ -85,7 +85,7 @@ class CallingConvention(object): regs = core.BNGetCallerSavedRegisters(self.handle, count) result = [] arch = self.arch - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs, count.value) self.__dict__["caller_saved_regs"] = result @@ -94,7 +94,7 @@ class CallingConvention(object): regs = core.BNGetIntegerArgumentRegisters(self.handle, count) result = [] arch = self.arch - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs, count.value) self.__dict__["int_arg_regs"] = result @@ -103,7 +103,7 @@ class CallingConvention(object): regs = core.BNGetFloatArgumentRegisters(self.handle, count) result = [] arch = self.arch - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs, count.value) self.__dict__["float_arg_regs"] = result @@ -136,7 +136,7 @@ class CallingConvention(object): regs = core.BNGetImplicitlyDefinedRegisters(self.handle, count) result = [] arch = self.arch - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs, count.value) self.__dict__["implicitly_defined_regs"] = result @@ -161,7 +161,7 @@ class CallingConvention(object): regs = self.__class__.caller_saved_regs count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -176,7 +176,7 @@ class CallingConvention(object): regs = self.__class__.int_arg_regs count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -191,7 +191,7 @@ class CallingConvention(object): regs = self.__class__.float_arg_regs count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -270,7 +270,7 @@ class CallingConvention(object): regs = self.__class__.implicitly_defined_regs count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): + for i in range(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) @@ -282,25 +282,25 @@ class CallingConvention(object): def _get_incoming_reg_value(self, ctxt, reg, func, result): try: - func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + func_obj = binaryninja.function.Function(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) reg_name = self.arch.get_reg_name(reg) api_obj = self.perform_get_incoming_reg_value(reg_name, func_obj)._to_api_object() except: log.log_error(traceback.format_exc()) - api_obj = function.RegisterValue()._to_api_object() + api_obj = binaryninja.function.RegisterValue()._to_api_object() result[0].state = api_obj.state result[0].value = api_obj.value def _get_incoming_flag_value(self, ctxt, reg, func, result): try: - func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + func_obj = binaryninja.function.Function(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) reg_name = self.arch.get_reg_name(reg) api_obj = self.perform_get_incoming_flag_value(reg_name, func_obj)._to_api_object() except: log.log_error(traceback.format_exc()) - api_obj = function.RegisterValue()._to_api_object() + api_obj = binaryninja.function.RegisterValue()._to_api_object() result[0].state = api_obj.state result[0].value = api_obj.value @@ -309,9 +309,9 @@ class CallingConvention(object): if func is None: func_obj = None else: - func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + func_obj = binaryninja.function.Function(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) - in_var_obj = function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) + in_var_obj = binaryninja.function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) out_var = self.perform_get_incoming_var_for_parameter_var(in_var_obj, func_obj) result[0].type = out_var.source_type result[0].index = out_var.index @@ -327,9 +327,9 @@ class CallingConvention(object): if func is None: func_obj = None else: - func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + func_obj = binaryninja.function.Function(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) - in_var_obj = function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) + in_var_obj = binaryninja.function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) out_var = self.perform_get_parameter_var_for_incoming_var(in_var_obj, func_obj) result[0].type = out_var.source_type result[0].index = out_var.index @@ -350,11 +350,11 @@ class CallingConvention(object): reg_stack = self.arch.get_reg_stack_for_reg(reg) if reg_stack is not None: if reg == self.arch.reg_stacks[reg_stack].stack_top_reg: - return function.RegisterValue.constant(0) - return function.RegisterValue() + return binaryninja.function.RegisterValue.constant(0) + return binaryninja.function.RegisterValue() def perform_get_incoming_flag_value(self, reg, func): - return function.RegisterValue() + return binaryninja.function.RegisterValue() def perform_get_incoming_var_for_parameter_var(self, in_var, func): in_buf = core.BNVariable() @@ -365,7 +365,7 @@ class CallingConvention(object): name = None if (func is not None) and (out_var.type == VariableSourceType.RegisterVariableSourceType): name = func.arch.get_reg_name(out_var.storage) - return function.Variable(func, out_var.type, out_var.index, out_var.storage, name) + return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage, name) def perform_get_parameter_var_for_incoming_var(self, in_var, func): in_buf = core.BNVariable() @@ -373,7 +373,7 @@ class CallingConvention(object): in_buf.index = in_var.index in_buf.storage = in_var.storage out_var = core.BNGetDefaultParameterVariableForIncomingVariable(self.handle, in_buf) - return function.Variable(func, out_var.type, out_var.index, out_var.storage) + return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage) def with_confidence(self, confidence): return CallingConvention(self.arch, handle = core.BNNewCallingConventionReference(self.handle), @@ -384,14 +384,14 @@ class CallingConvention(object): func_handle = None if func is not None: func_handle = func.handle - return function.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle)) + return binaryninja.function.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle)) def get_incoming_flag_value(self, flag, func): reg_num = self.arch.get_flag_index(flag) func_handle = None if func is not None: func_handle = func.handle - return function.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_handle)) + return binaryninja.function.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_handle)) def get_incoming_var_for_parameter_var(self, in_var, func): in_buf = core.BNVariable() @@ -406,7 +406,7 @@ class CallingConvention(object): name = None if (func is not None) and (out_var.type == VariableSourceType.RegisterVariableSourceType): name = func.arch.get_reg_name(out_var.storage) - return function.Variable(func, out_var.type, out_var.index, out_var.storage, name) + return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage, name) def get_parameter_var_for_incoming_var(self, in_var, func): in_buf = core.BNVariable() @@ -418,4 +418,4 @@ class CallingConvention(object): else: func_obj = func.handle out_var = core.BNGetParameterVariableForIncomingVariable(self.handle, in_buf, func_obj) - return function.Variable(func, out_var.type, out_var.index, out_var.storage) + return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage) diff --git a/python/databuffer.py b/python/databuffer.py index 3f9e4ce5..2fb5382d 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -21,11 +21,21 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core +from binaryninja import _binaryninjacore as core + +# 2-3 compatibility +from binaryninja import pyNativeStr class DataBuffer(object): def __init__(self, contents="", handle=None): + + # python3 no longer has longs + try: + long + except NameError: + long = int + if handle is not None: self.handle = core.handle_of_type(handle, core.BNDataBuffer) elif isinstance(contents, int) or isinstance(contents, long): @@ -44,7 +54,7 @@ class DataBuffer(object): def __getitem__(self, i): if isinstance(i, tuple): result = "" - source = str(self) + source = bytes(self) for s in i: result += source[s] return result @@ -59,7 +69,7 @@ class DataBuffer(object): ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) return buf.raw else: - return str(self)[i] + return bytes(self)[i] elif i < 0: if i >= -len(self): return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) @@ -79,7 +89,7 @@ class DataBuffer(object): if stop < start: stop = start if len(value) != (stop - start): - data = str(self) + data = bytes(self) data = data[0:start] + value + data[stop:] core.BNSetDataBufferContents(self.handle, data, len(data)) else: @@ -107,22 +117,24 @@ class DataBuffer(object): def __str__(self): buf = ctypes.create_string_buffer(len(self)) ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) - return buf.raw + return pyNativeStr(buf.raw) - def __repr__(self): - return repr(str(self)) + def __bytes__(self): + buf = ctypes.create_string_buffer(len(self)) + ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) + return buf.raw def escape(self): return core.BNDataBufferToEscapedString(self.handle) def unescape(self): - return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) + return DataBuffer(handle=core.BNDecodeEscapedString(bytes(self))) def base64_encode(self): return core.BNDataBufferToBase64(self.handle) def base64_decode(self): - return DataBuffer(handle = core.BNDecodeBase64(str(self))) + return DataBuffer(handle = core.BNDecodeBase64(bytes(self))) def zlib_compress(self): buf = core.BNZlibCompress(self.handle) diff --git a/python/demangle.py b/python/demangle.py index 11673263..2fa1028f 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -21,8 +21,12 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core -import types +from binaryninja import _binaryninjacore as core +from binaryninja import types + +# 2-3 compatibility +from binaryninja import range +from binaryninja import pyNativeStr def get_qualified_name(names): @@ -61,8 +65,8 @@ def demangle_ms(arch, mangled_name): 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]) + for i in range(outSize.value): + names.append(pyNativeStr(outName[i])) core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) return (types.Type(handle), names) return (None, mangled_name) @@ -74,8 +78,8 @@ def demangle_gnu3(arch, mangled_name): 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]) + for i in range(outSize.value): + names.append(pyNativeStr(outName[i])) core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) if not handle: return (None, names) diff --git a/python/downloadprovider.py b/python/downloadprovider.py new file mode 100644 index 00000000..14368ee7 --- /dev/null +++ b/python/downloadprovider.py @@ -0,0 +1,271 @@ +# Copyright (c) 2015-2018 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 abc +import ctypes +import sys +import traceback + +# Binary Ninja Components +import binaryninja._binaryninjacore as core +from binaryninja.setting import Setting +from binaryninja import with_metaclass +from binaryninja import startup +from binaryninja import log + +# 2-3 compatibility +from binaryninja import pyNativeStr + + +class DownloadInstance(object): + def __init__(self, provider, handle = None): + if handle is None: + self._cb = core.BNDownloadInstanceCallbacks() + self._cb.context = 0 + self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) + self._cb.performRequest = self._cb.performRequest.__class__(self._perform_request) + self.handle = core.BNInitDownloadInstance(provider.handle, self._cb) + else: + self.handle = core.handle_of_type(handle, core.BNDownloadInstance) + self._outputCallbacks = None + + def __del__(self): + core.BNFreeDownloadInstance(self.handle) + + def _destroy_instance(self, ctxt): + try: + self.perform_destroy_instance() + except: + log.log_error(traceback.format_exc()) + + def _perform_request(self, ctxt, url): + try: + return self.perform_request(url) + except: + log.log_error(traceback.format_exc()) + return -1 + + @abc.abstractmethod + def perform_destroy_instance(self): + raise NotImplementedError + + @abc.abstractmethod + def perform_request(self, ctxt, url): + raise NotImplementedError + + def perform_request(self, url, callbacks): + return core.BNPerformDownloadRequest(self.handle, url, callbacks) + + +class _DownloadProviderMetaclass(type): + @property + def list(self): + """List all DownloadProvider types (read-only)""" + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetDownloadProviderList(count) + result = [] + for i in xrange(0, count.value): + result.append(DownloadProvider(types[i])) + core.BNFreeDownloadProviderList(types) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetDownloadProviderList(count) + try: + for i in xrange(0, count.value): + yield DownloadProvider(types[i]) + finally: + core.BNFreeDownloadProviderList(types) + + def __getitem__(self, value): + startup._init_plugins() + provider = core.BNGetDownloadProviderByName(str(value)) + if provider is None: + raise KeyError("'%s' is not a valid download provider" % str(value)) + return DownloadProvider(provider) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class DownloadProvider(with_metaclass(_DownloadProviderMetaclass, object)): + 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.BNDownloadProvider) + self.__dict__["name"] = core.BNGetDownloadProviderName(handle) + + def register(self): + self._cb = core.BNDownloadProviderCallbacks() + self._cb.context = 0 + self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) + self.handle = core.BNRegisterDownloadProvider(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.BNNewDownloadInstanceReference(result.handle), ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return None + + def create_instance(self): + result = core.BNCreateDownloadProviderInstance(self.handle) + if result is None: + return None + return DownloadInstance(self, handle = result) + + +if sys.version_info >= (2, 7, 9): + try: + from urllib.request import urlopen, build_opener, install_opener, ProxyHandler + from urllib.error import URLError + except ImportError: + from urllib2 import urlopen, build_opener, install_opener, ProxyHandler, URLError + + class PythonDownloadInstance(DownloadInstance): + def __init__(self, provider): + super(PythonDownloadInstance, self).__init__(provider) + + @abc.abstractmethod + def perform_destroy_instance(self): + pass + + @abc.abstractmethod + def perform_request(self, url): + try: + proxy_setting = Setting('download-client').get_string('https-proxy') + if proxy_setting: + opener = build_opener(ProxyHandler({'https': proxy_setting})) + install_opener(opener) + + r = urlopen(pyNativeStr(url)) + total_size = int(r.headers.get('content-length', 0)) + bytes_sent = 0 + while True: + data = r.read(4096) + if not data: + break + raw_bytes = (ctypes.c_ubyte * len(data)).from_buffer_copy(data) + bytes_wrote = core.BNWriteDataForDownloadInstance(self.handle, raw_bytes, len(raw_bytes)) + if bytes_wrote != len(raw_bytes): + core.BNSetErrorForDownloadInstance(self.handle, "Bytes written mismatch!") + return -1 + bytes_sent = bytes_sent + bytes_wrote + continue_download = core.BNNotifyProgressForDownloadInstance(self.handle, bytes_sent, total_size) + if continue_download is False: + core.BNSetErrorForDownloadInstance(self.handle, "Download aborted!") + return -1 + + if not bytes_sent: + core.BNSetErrorForDownloadInstance(self.handle, "Received no data!") + return -1 + + except URLError as e: + core.BNSetErrorForDownloadInstance(self.handle, e.__class__.__name__) + return -1 + except: + core.BNSetErrorForDownloadInstance(self.handle, "Unknown Exception!") + log.log_error(traceback.format_exc()) + return -1 + + return 0 + + class PythonDownloadProvider(DownloadProvider): + name = "DefaultDownloadProvider" + instance_class = PythonDownloadInstance + + PythonDownloadProvider().register() +else: + try: + import requests + from requests import pyopenssl + class PythonDownloadInstance(DownloadInstance): + def __init__(self, provider): + super(PythonDownloadInstance, self).__init__(provider) + + @abc.abstractmethod + def perform_destroy_instance(self): + pass + + @abc.abstractmethod + def perform_request(self, url): + try: + proxy_setting = Setting('download-client').get_string('https-proxy') + if proxy_setting: + proxies = {"https": proxy_setting} + else: + proxies = None + + r = requests.get(pyNativeStr(url), proxies=proxies) + if not r.ok: + core.BNSetErrorForDownloadInstance(self.handle, "Received error from server") + return -1 + data = r.content + if len(data) == 0: + core.BNSetErrorForDownloadInstance(self.handle, "No data received from server!") + return -1 + raw_bytes = (ctypes.c_ubyte * len(data)).from_buffer_copy(data) + bytes_wrote = core.BNWriteDataForDownloadInstance(self.handle, raw_bytes, len(raw_bytes)) + if bytes_wrote != len(raw_bytes): + core.BNSetErrorForDownloadInstance(self.handle, "Bytes written mismatch!") + return -1 + continue_download = core.BNNotifyProgressForDownloadInstance(self.handle, bytes_wrote, bytes_wrote) + if continue_download is False: + core.BNSetErrorForDownloadInstance(self.handle, "Download aborted!") + return -1 + except requests.RequestException as e: + core.BNSetErrorForDownloadInstance(self.handle, e.__class__.__name__) + return -1 + except: + core.BNSetErrorForDownloadInstance(self.handle, "Unknown Exception!") + log.log_error(traceback.format_exc()) + return -1 + + return 0 + + class PythonDownloadProvider(DownloadProvider): + name = "DefaultDownloadProvider" + instance_class = PythonDownloadInstance + + PythonDownloadProvider().register() + except ImportError: + if sys.platform == "win32": + log.log_error("Provided Python version is too old! Only Python 2.7.10 and above are known to work on Windows!") + else: + log.log_error("On Python versions below 2.7.9, the pip requests[security] package is required for network connectivity!") + log.log_error("On an Ubuntu 14.04 install, the following three commands are sufficient to enable networking for the current user:") + log.log_error(" sudo apt install python-pip") + log.log_error(" python -m pip install pip --upgrade --user") + log.log_error(" python -m pip install requests[security] --upgrade --user") + diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py index 17a96685..05a72ed0 100644 --- a/python/examples/bin_info.py +++ b/python/examples/bin_info.py @@ -20,11 +20,15 @@ # IN THE SOFTWARE. import sys + import binaryninja.log as log from binaryninja.binaryview import BinaryViewType import binaryninja.interaction as interaction from binaryninja.plugin import PluginCommand +# 2-3 compatibility +from binaryninja import range + def get_bininfo(bv): if bv is None: @@ -48,13 +52,13 @@ def get_bininfo(bv): contents += "| Start | Name |\n" contents += "|------:|:-------|\n" - for i in xrange(min(10, len(bv.functions))): + for i in range(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))): + for i in range(min(10, len(bv.strings))): start = bv.strings[i].start length = bv.strings[i].length string = bv.read(start, length) @@ -67,6 +71,6 @@ def display_bininfo(bv): if __name__ == "__main__": - print get_bininfo(None) + 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 a2801511..cbcb86d4 100644 --- a/python/examples/breakpoint.py +++ b/python/examples/breakpoint.py @@ -44,7 +44,7 @@ def write_breakpoint(view, start, length): if bkpt is None: log_error(err) return - view.write(start, bkpt * length / len(bkpt)) + 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 7814c9fd..5bb27824 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -48,15 +48,15 @@ def save_svg(bv, function): def instruction_data_flow(function, address): ''' TODO: Extract data flow information ''' - length = function.view.get_instruction_length(address) - bytes = function.view.read(address, length) + length = binaryninja.function.view.get_instruction_length(address) + bytes = binaryninja.function.view.read(address, length) hex = bytes.encode('hex') padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) return 'Opcode: {bytes}'.format(bytes=padded) def render_svg(function, origname): - graph = function.create_graph() + graph = binaryninja.function.create_graph() graph.layout_and_wait() heightconst = 15 ratio = 0.48 diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py index 419cc188..4ed8dda2 100644 --- a/python/examples/jump_table.py +++ b/python/examples/jump_table.py @@ -43,16 +43,16 @@ def find_jump_table(bv, addr): break jump_addr += info.length if jump_addr >= block.end: - print "Unable to find jump after instruction 0x%x" % addr + print("Unable to find jump after instruction 0x%x" % addr) continue - print "Jump at 0x%x" % jump_addr + print("Jump at 0x%x" % jump_addr) # Collect the branch targets for any tables referenced by the clicked instruction branches = [] for token in tokens: if InstructionTextTokenType(token.type) == InstructionTextTokenType.PossibleAddressToken: # Table addresses will be a "possible address" token tbl = token.value - print "Found possible table at 0x%x" % tbl + print("Found possible table at 0x%x" % tbl) i = 0 while True: # Read the next pointer from the table @@ -66,7 +66,7 @@ def find_jump_table(bv, addr): # If the pointer is within the binary, add it as a destination and continue # to the next entry if (ptr >= bv.start) and (ptr < bv.end): - print "Found destination 0x%x" % ptr + print("Found destination 0x%x" % ptr) branches.append((arch, ptr)) else: # Once a value that is not a pointer is encountered, the jump table is ended diff --git a/python/examples/nds.py b/python/examples/nds.py index 34d8a292..a99303cf 100644 --- a/python/examples/nds.py +++ b/python/examples/nds.py @@ -27,12 +27,15 @@ from binaryninja.log import log_error import struct import traceback +# 2-3 compatibility +from binaryninja import range + def crc16(data): crc = 0xffff for ch in data: crc ^= ord(ch) - for bit in xrange(0, 8): + for bit in range(0, 8): if (crc & 1) == 1: crc = (crc >> 1) ^ 0xa001 else: diff --git a/python/examples/nes.py b/python/examples/nes.py index 00451abd..d3f75cc6 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -31,6 +31,10 @@ from binaryninja.log import log_error from binaryninja.enums import (BranchType, InstructionTextTokenType, LowLevelILOperation, LowLevelILFlagCondition, FlagRole, SegmentFlag, SymbolType) +# 2-3 compatibility +from binaryninja import range + + InstructionNames = [ "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 @@ -631,7 +635,7 @@ class NESView(BinaryView): banks = [] -for i in xrange(0, 32): +for i in range(0, 32): class NESViewBank(NESView): bank = i name = "NES Bank %X" % i diff --git a/python/examples/notification_callbacks.py b/python/examples/notification_callbacks.py index b7c9cde9..02c74db9 100644 --- a/python/examples/notification_callbacks.py +++ b/python/examples/notification_callbacks.py @@ -1,51 +1,74 @@ -from binaryninja import BinaryDataNotification, PluginCommand, log_info +# 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 inspect +from binaryninja import BinaryDataNotification +from binaryninja import PluginCommand + + def reg_notif(view): - demo_notification = DemoNotification(view) - view.register_notification(demo_notification) + demo_notification = DemoNotification(view) + view.register_notification(demo_notification) class DemoNotification(BinaryDataNotification): - def __init__(self, view): - self.view = view + def __init__(self, view): + self.view = view - def data_written(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_written(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def data_inserted(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_inserted(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def data_removed(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_removed(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def function_added(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def function_added(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def function_removed(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def function_removed(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def function_updated(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def function_updated(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def data_var_added(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_var_added(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def data_var_updated(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_var_updated(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def data_var_removed(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def data_var_removed(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def string_found(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def string_found(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def string_removed(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def string_removed(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def type_defined(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def type_defined(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) - def type_undefined(self, *args): - log_info(inspect.stack()[0][3] + str(args)) + def type_undefined(self, *args): + log.log_info(inspect.stack()[0][3] + str(args)) PluginCommand.register("Register Notification", "", reg_notif) diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py index 3e1cab40..c990a6c0 100644 --- a/python/examples/version_switcher.py +++ b/python/examples/version_switcher.py @@ -34,15 +34,15 @@ def load_channel(newchannel): global channel global versions if (channel is not None and newchannel == channel.name): - print "Same channel, not updating." + print("Same channel, not updating.") else: try: - print "Loading channel %s" % newchannel + print("Loading channel %s" % newchannel) channel = UpdateChannel[newchannel] - print "Loading versions..." + print("Loading versions...") versions = channel.versions except Exception: - print "%s is not a valid channel name. Defaulting to " % chandefault + print("%s is not a valid channel name. Defaulting to " % chandefault) channel = UpdateChannel[chandefault] @@ -50,12 +50,12 @@ def select(version): done = False date = datetime.datetime.fromtimestamp(version.time).strftime('%c') while not done: - print "Version:\t%s" % version.version - print "Updated:\t%s" % date - print "Notes:\n\n-----\n%s" % version.notes - print "-----" - print "\t1)\tSwitch to version" - print "\t2)\tMain Menu" + print("Version:\t%s" % version.version) + print("Updated:\t%s" % date) + print("Notes:\n\n-----\n%s" % version.notes) + print("-----") + print("\t1)\tSwitch to version") + print("\t2)\tMain Menu") selection = raw_input('Choice: ') if selection.isdigit(): selection = int(selection) @@ -65,44 +65,44 @@ def select(version): done = True elif (selection == 1): if (version.version == channel.latest_version.version): - print "Requesting update to latest version." + print("Requesting update to latest version.") else: - print "Requesting update to prior version." + print("Requesting update to prior version.") if are_auto_updates_enabled(): - print "Disabling automatic updates." + print("Disabling automatic updates.") set_auto_updates_enabled(False) if (version.version == core_version): - print "Already running %s" % version.version + print("Already running %s" % version.version) else: - print "version.version %s" % version.version - print "core_version %s" % core_version - print "Downloading..." - print version.update() - print "Installing..." + print("version.version %s" % version.version) + print("core_version %s" % core_version) + print("Downloading...") + print(version.update()) + 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" + print("Invalid selection") def list_channels(): done = False - print "\tSelect channel:\n" + print("\tSelect channel:\n") while not done: 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): - print "%s is an invalid choice." % selection + print("%s is an invalid choice." % selection) else: done = True if (selection != len(channel_list) + 1): @@ -118,23 +118,23 @@ def main(): done = False load_channel(chandefault) while not done: - print "\n\tBinary Ninja Version Switcher" - print "\t\tCurrent Channel:\t%s" % channel.name - print "\t\tCurrent Version:\t%s" % core_version - print "\t\tAuto-Updates On:\t%s\n" % are_auto_updates_enabled() + print("\n\tBinary Ninja Version Switcher") + print("\t\tCurrent Channel:\t%s" % channel.name) + 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) - print "\t%d)\t%s" % (len(versions) + 1, "Switch Channel") - print "\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates") - print "\t%d)\t%s" % (len(versions) + 3, "Exit") + print("\t%d)\t%s (%s)" % (index + 1, version.version, date)) + print("\t%d)\t%s" % (len(versions) + 1, "Switch Channel")) + print("\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates")) + print("\t%d)\t%s" % (len(versions) + 3, "Exit")) selection = raw_input('Choice: ') if selection.isdigit(): selection = int(selection) else: selection = 0 if (selection <= 0 or selection > len(versions) + 3): - print "%d is an invalid choice.\n\n" % selection + print("%d is an invalid choice.\n\n" % selection) else: if (selection == len(versions) + 3): done = True diff --git a/python/fileaccessor.py b/python/fileaccessor.py index 2c1f1d19..c2539b39 100644 --- a/python/fileaccessor.py +++ b/python/fileaccessor.py @@ -22,9 +22,7 @@ import traceback import ctypes # Binary Ninja components -import _binaryninjacore as core -import log - +from binaryninja import _binaryninjacore as core class FileAccessor(object): def __init__(self): diff --git a/python/filemetadata.py b/python/filemetadata.py index 4bfc0214..cafe1d67 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -18,16 +18,15 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -import startup -import associateddatastore -import log -import binaryview - +# Binary Ninja Components +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja import associateddatastore #required for _FileMetadataAssociatedDataStore +from binaryninja import log class NavigationHandler(object): def _register(self, handle): @@ -66,12 +65,13 @@ class _FileMetadataAssociatedDataStore(associateddatastore._AssociatedDataStore) 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. """ + + _associated_data = {} + def __init__(self, filename = None, handle = None): """ Instantiates a new FileMetadata class. @@ -82,7 +82,7 @@ class FileMetadata(object): if handle is not None: self.handle = core.handle_of_type(handle, core.BNFileMetadata) else: - startup._init_plugins() + binaryninja._init_plugins() self.handle = core.BNCreateFileMetadata() if filename is not None: core.BNSetFilename(self.handle, str(filename)) @@ -167,7 +167,7 @@ class FileMetadata(object): view = core.BNGetFileViewOfType(self.handle, "Raw") if view is None: return None - return binaryview.BinaryView(file_metadata = self, handle = view) + return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) @property def saved(self): @@ -322,7 +322,7 @@ class FileMetadata(object): lambda ctxt, cur, total: progress_func(cur, total))) if view is None: return None - return binaryview.BinaryView(file_metadata = self, handle = view) + return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) def save_auto_snapshot(self, progress_func = None): if progress_func is None: @@ -341,7 +341,7 @@ class FileMetadata(object): view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) if view is None: return None - return binaryview.BinaryView(file_metadata = self, handle = view) + return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) def __setattr__(self, name, value): try: diff --git a/python/flowgraph.py b/python/flowgraph.py index e6c98597..cf2d8e02 100644 --- a/python/flowgraph.py +++ b/python/flowgraph.py @@ -23,16 +23,19 @@ import threading import traceback # Binary Ninja components -import _binaryninjacore as core -from enums import (BranchType, InstructionTextTokenType, HighlightStandardColor) -import function -import binaryview -import lowlevelil -import mediumlevelil -import basicblock -import log -import interaction -import highlight +import binaryninja +from binaryninja.enums import (BranchType, InstructionTextTokenType, HighlightStandardColor) +from binaryninja import _binaryninjacore as core +from binaryninja import function +from binaryninja import binaryview +from binaryninja import lowlevelil +from binaryninja import mediumlevelil +from binaryninja import basicblock +from binaryninja import log +from binaryninja import highlight + +# 2-3 compatibility +from binaryninja import range class FlowGraphEdge(object): @@ -118,7 +121,7 @@ class FlowGraphNode(object): lines = core.BNGetFlowGraphNodeLines(self.handle, count) block = self.basic_block result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'): il_instr = block.il_function[lines[i].instrIndex] @@ -126,7 +129,7 @@ class FlowGraphNode(object): il_instr = None color = highlight.HighlightColor._from_core_struct(lines[i].highlight) tokens = [] - for j in xrange(0, lines[i].count): + for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text value = lines[i].tokens[j].value @@ -145,7 +148,7 @@ class FlowGraphNode(object): if isinstance(lines, str): lines = lines.split('\n') line_buf = (core.BNDisassemblyTextLine * len(lines))() - for i in xrange(0, len(lines)): + for i in range(0, len(lines)): line = lines[i] if isinstance(line, str): line = function.DisassemblyTextLine([function.InstructionTextToken(InstructionTextTokenType.TextToken, line)]) @@ -170,7 +173,7 @@ class FlowGraphNode(object): line_buf[i].highlight = color._get_core_struct() line_buf[i].count = len(line.tokens) line_buf[i].tokens = (core.BNInstructionTextToken * len(line.tokens))() - for j in xrange(0, len(line.tokens)): + for j in range(0, len(line.tokens)): line_buf[i].tokens[j].type = line.tokens[j].type line_buf[i].tokens[j].text = line.tokens[j].text line_buf[i].tokens[j].value = line.tokens[j].value @@ -187,13 +190,13 @@ class FlowGraphNode(object): count = ctypes.c_ulonglong() edges = core.BNGetFlowGraphNodeOutgoingEdges(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): branch_type = BranchType(edges[i].type) target = edges[i].target if target: target = FlowGraphNode(self.graph, core.BNNewFlowGraphNodeReference(target)) points = [] - for j in xrange(0, edges[i].pointCount): + for j in range(0, edges[i].pointCount): points.append((edges[i].points[j].x, edges[i].points[j].y)) result.append(FlowGraphEdge(branch_type, self, target, points, edges[i].backEdge)) core.BNFreeFlowGraphNodeOutgoingEdgeList(edges, count.value) @@ -235,14 +238,14 @@ class FlowGraphNode(object): lines = core.BNGetFlowGraphNodeLines(self.handle, count) block = self.basic_block try: - for i in xrange(0, count.value): + for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'): il_instr = block.il_function[lines[i].instrIndex] else: il_instr = None tokens = [] - for j in xrange(0, lines[i].count): + for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text value = lines[i].tokens[j].value @@ -353,7 +356,7 @@ class FlowGraph(object): count = ctypes.c_ulonglong() blocks = core.BNGetFlowGraphNodes(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(blocks[i]))) core.BNFreeFlowGraphNodeList(blocks, count.value) return result @@ -451,7 +454,7 @@ class FlowGraph(object): count = ctypes.c_ulonglong() nodes = core.BNGetFlowGraphNodes(self.handle, count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i])) finally: core.BNFreeFlowGraphNodeList(nodes, count.value) @@ -492,7 +495,7 @@ class FlowGraph(object): count = ctypes.c_ulonglong() nodes = core.BNGetFlowGraphNodesInRegion(self.handle, left, top, right, bottom, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i]))) core.BNFreeFlowGraphNodeList(nodes, count.value) return result @@ -507,7 +510,7 @@ class FlowGraph(object): return FlowGraphNode(self, node) def show(self, title): - interaction.show_graph_report(title, self) + binaryninja.interaction.show_graph_report(title, self) def update(self): return None diff --git a/python/function.py b/python/function.py index f64a9677..e840dd5e 100644 --- a/python/function.py +++ b/python/function.py @@ -18,28 +18,25 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import threading import traceback import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import (FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore +from binaryninja import highlight +from binaryninja import log +from binaryninja import types +from binaryninja.enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType, FunctionAnalysisSkipOverride) -import architecture -import platform -import highlight -import associateddatastore -import types -import basicblock -import lowlevelil -import mediumlevelil -import binaryview -import log -import callingconvention -import flowgraph + +# 2-3 compatibility +from binaryninja import range class LookupTableEntry(object): @@ -180,7 +177,7 @@ class PossibleValueSet(object): elif value.state == RegisterValueType.SignedRangeValue: self.offset = value.value self.ranges = [] - for i in xrange(0, value.count): + for i in range(0, value.count): start = value.ranges[i].start end = value.ranges[i].end step = value.ranges[i].step @@ -192,7 +189,7 @@ class PossibleValueSet(object): elif value.state == RegisterValueType.UnsignedRangeValue: self.offset = value.value self.ranges = [] - for i in xrange(0, value.count): + for i in range(0, value.count): start = value.ranges[i].start end = value.ranges[i].end step = value.ranges[i].step @@ -200,15 +197,15 @@ class PossibleValueSet(object): elif value.state == RegisterValueType.LookupTableValue: self.table = [] self.mapping = {} - for i in xrange(0, value.count): + for i in range(0, value.count): from_list = [] - for j in xrange(0, value.table[i].fromCount): + for j in range(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.InSetOfValues) or (value.state == RegisterValueType.NotInSetOfValues): self.values = set() - for i in xrange(0, value.count): + for i in range(0, value.count): self.values.add(value.valueSet[i]) def __repr__(self): @@ -421,7 +418,7 @@ class Function(object): arch = core.BNGetFunctionArchitecture(self.handle) if arch is None: return None - self._arch = architecture.CoreArchitecture._from_cache(arch) + self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch) return self._arch @property @@ -433,7 +430,7 @@ class Function(object): plat = core.BNGetFunctionPlatform(self.handle) if plat is None: return None - self._platform = platform.Platform(None, handle = plat) + self._platform = binaryninja.platform.Platform(None, handle = plat) return self._platform @property @@ -486,8 +483,8 @@ class Function(object): 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]))) + for i in range(0, count.value): + result.append(binaryninja.basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -497,7 +494,7 @@ class Function(object): count = ctypes.c_ulonglong() addrs = core.BNGetCommentedAddresses(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): result[addrs[i]] = self.get_comment_at(addrs[i]) core.BNFreeAddressList(addrs) return result @@ -505,17 +502,17 @@ class Function(object): @property def low_level_il(self): """returns LowLevelILFunction used to represent Function low level IL (read-only)""" - return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) + return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) @property def lifted_il(self): """returns LowLevelILFunction used to represent lifted IL (read-only)""" - return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) + return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) @property def medium_level_il(self): """Function medium level IL (read-only)""" - return mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) + return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) @property def function_type(self): @@ -532,7 +529,7 @@ class Function(object): count = ctypes.c_ulonglong() v = core.BNGetStackLayout(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name, types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence))) result.sort(key = lambda x: x.identifier) @@ -545,7 +542,7 @@ class Function(object): count = ctypes.c_ulonglong() v = core.BNGetFunctionVariables(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name, types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence))) result.sort(key = lambda x: x.identifier) @@ -558,8 +555,8 @@ class Function(object): count = ctypes.c_ulonglong() branches = core.BNGetIndirectBranches(self.handle, count) result = [] - for i in xrange(0, count.value): - result.append(IndirectBranchInfo(architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + for i in range(0, count.value): + result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) core.BNFreeIndirectBranchList(branches) return result @@ -579,7 +576,7 @@ class Function(object): count = ctypes.c_ulonglong() info = core.BNGetFunctionAnalysisPerformanceInfo(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): result[info[i].name] = info[i].seconds core.BNFreeAnalysisPerformanceInfo(info, count.value) return result @@ -613,7 +610,7 @@ class Function(object): """Registers that are used for the return value""" result = core.BNGetFunctionReturnRegisters(self.handle) reg_set = [] - for i in xrange(0, result.count): + for i in range(0, result.count): reg_set.append(self.arch.get_reg_name(result.regs[i])) regs = types.RegisterSet(reg_set, confidence = result.confidence) core.BNFreeRegisterSet(result) @@ -624,7 +621,7 @@ class Function(object): regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) - for i in xrange(0, len(value)): + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) if hasattr(value, 'confidence'): regs.confidence = value.confidence @@ -638,7 +635,7 @@ class Function(object): result = core.BNGetFunctionCallingConvention(self.handle) if not result.convention: return None - return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + return binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) @calling_convention.setter def calling_convention(self, value): @@ -656,7 +653,7 @@ class Function(object): """List of variables for the incoming function parameters""" result = core.BNGetFunctionParameterVariables(self.handle) var_list = [] - for i in xrange(0, result.count): + for i in range(0, result.count): var_list.append(Variable(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage)) confidence = result.confidence core.BNFreeParameterVariables(result) @@ -671,7 +668,7 @@ class Function(object): var_conf = core.BNParameterVariablesWithConfidence() var_conf.vars = (core.BNVariable * len(var_list))() var_conf.count = len(var_list) - for i in xrange(0, len(var_list)): + for i in range(0, len(var_list)): var_conf.vars[i].type = var_list[i].source_type var_conf.vars[i].index = var_list[i].index var_conf.vars[i].storage = var_list[i].storage @@ -721,7 +718,7 @@ class Function(object): count = ctypes.c_ulonglong() adjust = core.BNGetFunctionRegisterStackAdjustments(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = self.arch.get_reg_stack_name(adjust[i].regStack) value = types.RegisterStackAdjustmentWithConfidence(adjust[i].adjustment, confidence = adjust[i].confidence) @@ -749,7 +746,7 @@ class Function(object): """Registers that are modified by this function""" result = core.BNGetFunctionClobberedRegisters(self.handle) reg_set = [] - for i in xrange(0, result.count): + for i in range(0, result.count): reg_set.append(self.arch.get_reg_name(result.regs[i])) regs = types.RegisterSet(reg_set, confidence = result.confidence) core.BNFreeRegisterSet(result) @@ -760,7 +757,7 @@ class Function(object): regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) - for i in xrange(0, len(value)): + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) if hasattr(value, 'confidence'): regs.confidence = value.confidence @@ -829,6 +826,11 @@ class Function(object): """Whether automatic analysis was skipped for this function""" return core.BNIsFunctionAnalysisSkipped(self.handle) + @property + def analysis_skip_reason(self): + """Function analysis skip reason""" + return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle)) + @analysis_skipped.setter def analysis_skipped(self, skip): if skip: @@ -851,14 +853,14 @@ class Function(object): graph = core.BNGetUnresolvedStackAdjustmentGraph(self.handle) if not graph: return None - return flowgraph.CoreFlowGraph(graph) + return binaryninja.flowgraph.CoreFlowGraph(graph) 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])) + for i in range(0, count.value): + yield binaryninja.basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])) finally: core.BNFreeBasicBlockList(blocks, count.value) @@ -928,7 +930,7 @@ class Function(object): count = ctypes.c_ulonglong() exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(exits[i]) core.BNFreeILInstructionList(exits) return result @@ -940,7 +942,7 @@ class Function(object): :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 + :rtype: binaryninja.function.RegisterValue :Example: >>> func.get_reg_value_at(0x400dbe, 'rdi') @@ -960,7 +962,7 @@ class Function(object): :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 + :rtype: binaryninja.function.RegisterValue :Example: >>> func.get_reg_value_after(0x400dbe, 'rdi') @@ -982,7 +984,7 @@ class Function(object): :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 + :rtype: binaryninja.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 @@ -1027,7 +1029,7 @@ class Function(object): count = ctypes.c_ulonglong() regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs) return result @@ -1038,7 +1040,7 @@ class Function(object): count = ctypes.c_ulonglong() regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(arch.get_reg_name(regs[i])) core.BNFreeRegisterList(regs) return result @@ -1049,7 +1051,7 @@ class Function(object): count = ctypes.c_ulonglong() refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): var_type = types.Type(core.BNNewTypeReference(refs[i].type), platform = self.platform, confidence = refs[i].typeConfidence) result.append(StackVariableReference(refs[i].sourceOperand, var_type, refs[i].name, Variable.from_identifier(self, refs[i].varIdentifier, refs[i].name, var_type), @@ -1063,7 +1065,7 @@ class Function(object): count = ctypes.c_ulonglong() refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(ConstantReference(refs[i].value, refs[i].size, refs[i].pointer, refs[i].intermediate)) core.BNFreeConstantReferenceList(refs) return result @@ -1084,7 +1086,7 @@ class Function(object): count = ctypes.c_ulonglong() instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -1094,7 +1096,7 @@ class Function(object): count = ctypes.c_ulonglong() instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -1103,7 +1105,7 @@ class Function(object): count = ctypes.c_ulonglong() flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self.arch._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) return result @@ -1112,7 +1114,7 @@ class Function(object): count = ctypes.c_ulonglong() flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(self.arch._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) return result @@ -1122,7 +1124,7 @@ class Function(object): settings_obj = settings.handle else: settings_obj = None - return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) + return binaryninja.flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) def apply_imported_types(self, sym): core.BNApplyImportedTypes(self.handle, sym.handle) @@ -1134,7 +1136,7 @@ class Function(object): if source_arch is None: source_arch = self.arch branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): + for i in range(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)) @@ -1143,7 +1145,7 @@ class Function(object): if source_arch is None: source_arch = self.arch branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): + for i in range(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)) @@ -1154,8 +1156,8 @@ class Function(object): 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.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + for i in range(0, count.value): + result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) core.BNFreeIndirectBranchList(branches) return result @@ -1165,11 +1167,13 @@ class Function(object): count = ctypes.c_ulonglong(0) lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): tokens = [] - for j in xrange(0, lines[i].count): + for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text + if not isinstance(text, str): + text = text.encode("charmap") value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand @@ -1201,7 +1205,7 @@ class Function(object): regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) - for i in xrange(0, len(value)): + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) if hasattr(value, 'confidence'): regs.confidence = value.confidence @@ -1227,7 +1231,7 @@ class Function(object): var_conf = core.BNParameterVariablesWithConfidence() var_conf.vars = (core.BNVariable * len(var_list))() var_conf.count = len(var_list) - for i in xrange(0, len(var_list)): + for i in range(0, len(var_list)): var_conf.vars[i].type = var_list[i].source_type var_conf.vars[i].index = var_list[i].index var_conf.vars[i].storage = var_list[i].storage @@ -1284,7 +1288,7 @@ class Function(object): regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) - for i in xrange(0, len(value)): + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) if hasattr(value, 'confidence'): regs.confidence = value.confidence @@ -1344,7 +1348,7 @@ class Function(object): block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) if not block: return None - return basicblock.BasicBlock(self._view, handle = block) + return binaryninja.basicblock.BasicBlock(self._view, handle = block) def get_instr_highlight(self, addr, arch=None): """ @@ -1472,11 +1476,11 @@ class Function(object): count = ctypes.c_ulonglong() lines = core.BNGetFunctionTypeTokens(self.handle, settings, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): addr = lines[i].addr color = highlight.HighlightColor._from_core_struct(lines[i].highlight) tokens = [] - for j in xrange(0, lines[i].count): + for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text value = lines[i].tokens[j].value @@ -1568,7 +1572,7 @@ class Function(object): count = ctypes.c_ulonglong() adjust = core.BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): result[arch.get_reg_stack_name(adjust[i].regStack)] = types.RegisterStackAdjustmentWithConfidence( adjust[i].adjustment, confidence = adjust[i].confidence) core.BNFreeRegisterStackAdjustments(adjust) diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py index 4ac304ca..319bff0e 100644 --- a/python/functionrecognizer.py +++ b/python/functionrecognizer.py @@ -21,15 +21,15 @@ import traceback # Binary Ninja components -import _binaryninjacore as core -import function -import filemetadata -import binaryview -import lowlevelil -import log +from binaryninja import _binaryninjacore as core +from binaryninja import function +from binaryninja import filemetadata +from binaryninja import binaryview +from binaryninja import lowlevelil class FunctionRecognizer(object): + _instance = None def __init__(self): diff --git a/python/generator.cpp b/python/generator.cpp index f838b36d..39e45aea 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -117,7 +117,7 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac break; } else if ((type->GetChildType()->GetClass() == IntegerTypeClass) && - (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned())) + (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned())) { if (isReturnType) fprintf(out, "ctypes.POINTER(ctypes.c_byte)"); @@ -173,7 +173,7 @@ int main(int argc, char* argv[]) } bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); - fprintf(stderr, "Errors: %s", errors.c_str()); + fprintf(stderr, "Errors: %s\n", errors.c_str()); if (!ok) return 1; @@ -198,7 +198,9 @@ int main(int argc, char* argv[]) 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"); + fprintf(out, "\traise Exception(\"OS not supported\")\n\n\n"); + + fprintf(out, "from binaryninja import cstr, pyNativeStr\n\n\n"); // Create type objects fprintf(out, "# Type definitions\n"); @@ -211,7 +213,23 @@ int main(int argc, char* argv[]) if (i.second->GetClass() == StructureTypeClass) { fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); - fprintf(out, "\tpass\n"); + + // python uses str's, C uses byte-arrays + bool stringField = false; + for (auto& arg : i.second->GetStructure()->GetMembers()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + fprintf(out, "\t@property\n\tdef %s(self):\n\t\treturn pyNativeStr(self._%s)\n", arg.name.c_str(), arg.name.c_str()); + fprintf(out, "\t@%s.setter\n\tdef %s(self, value):\n\t\tself._%s = cstr(value)\n", arg.name.c_str(), arg.name.c_str(), arg.name.c_str()); + stringField = true; + } + } + + if (!stringField) + fprintf(out, "\tpass\n"); } else if (i.second->GetClass() == EnumerationTypeClass) { @@ -227,7 +245,7 @@ int main(int argc, char* argv[]) } } else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || - (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) + (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) { fprintf(out, "%s = ", name.c_str()); OutputType(out, i.second); @@ -276,7 +294,15 @@ int main(int argc, char* argv[]) fprintf(out, "%s._fields_ = [\n", name.c_str()); for (auto& j : type->GetStructure()->GetMembers()) { - fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); + // To help the python->C wrappers + if ((j.type->GetClass() == PointerTypeClass) && + (j.type->GetChildType()->GetWidth() == 1) && + (j.type->GetChildType()->IsSigned())) + { + fprintf(out, "\t\t(\"_%s\", ", j.name.c_str()); + } + else + fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); OutputType(out, j.type); fprintf(out, "),\n"); } @@ -310,6 +336,22 @@ int main(int argc, char* argv[]) (i.second->GetChildType()->GetChildType()->IsSigned()); // Pointer returns will be automatically wrapped to return None on null pointer bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); + + // From python -> C python3 requires str -> str.encode('charmap') + bool stringArgument = false; + for (auto& arg : i.second->GetParameters()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + stringArgument = true; + break; + } + } + if (name == "BNFreeString") + stringArgument = false; + bool callbackConvention = false; if (name == "BNAllocString") { @@ -321,7 +363,7 @@ int main(int argc, char* argv[]) } string funcName = name; - if (stringResult || pointerResult) + if (stringResult || pointerResult || stringArgument) funcName = string("_") + funcName; fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str()); @@ -349,13 +391,45 @@ int main(int argc, char* argv[]) } fprintf(out, "\t]\n"); } + else + { + // As of writing this, only BNLog's have variable instruction lengths, but in an attempt not to break in the future: + if (funcName.compare(0, 6, "_BNLog") == 0) + { + fprintf(out, "def %s(*args):\n", name.c_str()); + fprintf(out, "\treturn %s(*[cstr(arg) for arg in args])\n\n", funcName.c_str()); + continue; + } + } if (stringResult) { // Emit wrapper to get Python string and free native memory 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"); + if (stringArgument) + { + fprintf(out, "\tresult = %s(", funcName.c_str()); + string stringArgFuncCall = ""; + size_t argN = 0; + for (auto& arg : i.second->GetParameters()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + stringArgFuncCall += "cstr(args[" + to_string(argN) + "]), "; + } + else + stringArgFuncCall += "args[" + to_string(argN) + "], "; + argN++; + } + stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2); + stringArgFuncCall += ")\n"; + fprintf(out, "%s", stringArgFuncCall.c_str()); + } + else + fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n"); fprintf(out, "\tBNFreeString(result)\n"); fprintf(out, "\treturn string\n"); } @@ -363,18 +437,76 @@ int main(int argc, char* argv[]) { // Emit wrapper to return None on null pointer fprintf(out, "def %s(*args):\n", name.c_str()); - fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + if (stringArgument) + { + fprintf(out, "\tresult = %s(", funcName.c_str()); + string stringArgFuncCall = ""; + size_t argN = 0; + for (auto& arg : i.second->GetParameters()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + stringArgFuncCall += "cstr(args[" + to_string(argN) + "]), "; + } + else + stringArgFuncCall += "args[" + to_string(argN) + "], "; + argN++; + } + stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2); + stringArgFuncCall += ")\n"; + fprintf(out, "%s", stringArgFuncCall.c_str()); + } + else + 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"); } + else if (stringArgument) + { + fprintf(out, "def %s(*args):\n", name.c_str()); + { + fprintf(out, "\treturn %s(", funcName.c_str()); + string stringArgFuncCall = ""; + size_t argN = 0; + for (auto& arg : i.second->GetParameters()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + stringArgFuncCall += "cstr(args[" + to_string(argN) + "]), "; + } + else + stringArgFuncCall += "args[" + to_string(argN) + "], "; + argN++; + } + stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2); + stringArgFuncCall += ")\n"; + fprintf(out, "%s", stringArgFuncCall.c_str()); + } + } + fprintf(out, "\n"); } fprintf(out, "\n# Helper functions\n"); fprintf(out, "def handle_of_type(value, 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, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n"); + + // The following method is addapted from python/enum/__init__.py, lines 25-36 + fprintf(out, "\ntry:\n"); + fprintf(out, "\tbasestring\n"); + fprintf(out, "\tunicode\n"); + fprintf(out, "except NameError:\n"); + fprintf(out, "\t# In Python 2 basestring is the ancestor of both str and unicode\n"); + fprintf(out, "\t# in Python 3 it's just str, but was missing in 3.1\n"); + fprintf(out, "\t# In Python 3 unicode no longer exists (it's just str)\n"); + fprintf(out, "\tbasestring = str\n"); + fprintf(out, "\tunicode = str\n"); fprintf(out, "\n# Set path for core plugins\n"); fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); diff --git a/python/highlight.py b/python/highlight.py index 87329202..502caa80 100644 --- a/python/highlight.py +++ b/python/highlight.py @@ -20,8 +20,8 @@ # Binary Ninja components -import _binaryninjacore as core -from enums import HighlightColorStyle, HighlightStandardColor +from binaryninja import _binaryninjacore as core +from binaryninja.enums import HighlightColorStyle, HighlightStandardColor class HighlightColor(object): diff --git a/python/interaction.py b/python/interaction.py index b9aa0c2a..0170e6aa 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -22,11 +22,14 @@ import ctypes import traceback # Binary Ninja components -import _binaryninjacore as core -from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult, ReportType -import binaryview -import log -import flowgraph +from binaryninja import _binaryninjacore as core +from binaryninja.enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult +from binaryninja import binaryview +from binaryninja import log +from binaryninja import flowgraph + +# 2-3 compatibility +from binaryninja import range class LabelField(object): @@ -152,7 +155,11 @@ 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. + in self.result as an index in to the choices array. + + :attr str prompt: prompt to be presented to the user + :attr list(str) choices: list of choices to choose from + """ def __init__(self, prompt, choices): self.prompt = prompt @@ -163,8 +170,8 @@ class ChoiceField(object): 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]) + for i in range(0, len(self.choices)): + choice_buf[i] = self.choices[i].encode('charmap') value.choices = choice_buf value.count = len(self.choices) @@ -349,7 +356,7 @@ class InteractionHandler(object): def _get_choice_input(self, ctxt, result, prompt, title, choice_buf, count): try: choices = [] - for i in xrange(0, count): + for i in range(0, count): choices.append(choice_buf[i]) value = self.get_choice_input(prompt, title, choices) if value is None: @@ -392,7 +399,7 @@ class InteractionHandler(object): def _get_form_input(self, ctxt, fields, count, title): try: field_objs = [] - for i in xrange(0, count): + for i in range(0, count): if fields[i].type == FormInputFieldType.LabelFormField: field_objs.append(LabelField(fields[i].prompt)) elif fields[i].type == FormInputFieldType.SeparatorFormField: @@ -410,7 +417,7 @@ class InteractionHandler(object): field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) elif fields[i].type == FormInputFieldType.ChoiceFormField: choices = [] - for j in xrange(0, fields[i].count): + for j in range(0, fields[i].count): choices.append(fields[i].choices[j]) field_objs.append(ChoiceField(fields[i].prompt, choices)) elif fields[i].type == FormInputFieldType.OpenFileNameFormField: @@ -423,7 +430,7 @@ class InteractionHandler(object): field_objs.append(LabelField(fields[i].prompt)) if not self.get_form_input(field_objs, title): return False - for i in xrange(0, count): + for i in range(0, count): field_objs[i]._fill_core_result(fields[i]) return True except: @@ -783,8 +790,8 @@ def get_choice_input(prompt, title, choices): 0L """ choice_buf = (ctypes.c_char_p * len(choices))() - for i in xrange(0, len(choices)): - choice_buf[i] = str(choices[i]) + for i in range(0, len(choices)): + choice_buf[i] = str(choices[i]).encode('charmap') value = ctypes.c_ulonglong() if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): return None @@ -839,11 +846,9 @@ def get_save_filename_input(prompt, ext="", 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. + ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing a 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. + 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. @@ -896,11 +901,11 @@ def get_form_input(fields, title): 3) Maybe Options 1 >>> True - >>> print tex_f.result, int_f.result, choice_f.result + >>> 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)): + for i in range(0, len(fields)): if isinstance(fields[i], str): LabelField(fields[i])._fill_core_struct(value[i]) elif fields[i] is None: @@ -909,7 +914,7 @@ def get_form_input(fields, title): fields[i]._fill_core_struct(value[i]) if not core.BNGetFormInput(value, len(fields), title): return False - for i in xrange(0, len(fields)): + for i in range(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)) diff --git a/python/log.py b/python/log.py index f7144183..4997b222 100644 --- a/python/log.py +++ b/python/log.py @@ -20,7 +20,8 @@ # Binary Ninja components -import _binaryninjacore as core +from binaryninja import _binaryninjacore as core +from binaryninja.enums import LogLevel _output_to_log = False @@ -54,7 +55,7 @@ def log(level, text): :param str text: message to print :rtype: None """ - core.BNLog(level, "%s", str(text)) + core.BNLog(level, '%s', text) def log_debug(text): @@ -69,7 +70,7 @@ def log_debug(text): >>> log_debug("Hotdogs!") Hotdogs! """ - core.BNLogDebug("%s", str(text)) + core.BNLogDebug('%s', text) def log_info(text): @@ -84,7 +85,7 @@ def log_info(text): Saucisson! >>> """ - core.BNLogInfo("%s", str(text)) + core.BNLogInfo('%s', text) def log_warn(text): @@ -100,7 +101,7 @@ def log_warn(text): Chilidogs! >>> """ - core.BNLogWarn("%s", str(text)) + core.BNLogWarn('%s', text) def log_error(text): @@ -116,7 +117,7 @@ def log_error(text): Spanferkel! >>> """ - core.BNLogError("%s", str(text)) + core.BNLogError('%s', text) def log_alert(text): @@ -132,10 +133,10 @@ def log_alert(text): Kielbasa! >>> """ - core.BNLogAlert("%s", str(text)) + core.BNLogAlert('%s', text) -def log_to_stdout(min_level): +def log_to_stdout(min_level=LogLevel.InfoLog): """ ``log_to_stdout`` redirects minimum log level to standard out. diff --git a/python/lowlevelil.py b/python/lowlevelil.py index ab6170a5..e714eb48 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -19,14 +19,16 @@ # IN THE SOFTWARE. import ctypes +import struct # Binary Ninja components -import _binaryninjacore as core -from .enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType -import function -import basicblock -import mediumlevelil -import struct +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja.enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType +from binaryninja import basicblock #required for LowLevelILBasicBlock + +# 2-3 compatibility +from binaryninja import range class LowLevelILLabel(object): @@ -260,6 +262,7 @@ class LowLevelILInstruction(object): LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], LowLevelILOperation.LLIL_CALL: [("dest", "expr")], LowLevelILOperation.LLIL_CALL_STACK_ADJUST: [("dest", "expr"), ("stack_adjustment", "int"), ("reg_stack_adjustments", "reg_stack_adjust")], + LowLevelILOperation.LLIL_TAILCALL: [("dest", "expr")], LowLevelILOperation.LLIL_RET: [("dest", "expr")], LowLevelILOperation.LLIL_NORET: [], LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], @@ -328,6 +331,7 @@ class LowLevelILInstruction(object): LowLevelILOperation.LLIL_FLAG_BIT_SSA: [("src", "flag_ssa"), ("bit", "int")], LowLevelILOperation.LLIL_CALL_SSA: [("output", "expr"), ("dest", "expr"), ("stack", "expr"), ("param", "expr")], LowLevelILOperation.LLIL_SYSCALL_SSA: [("output", "expr"), ("stack", "expr"), ("param", "expr")], + LowLevelILOperation.LLIL_TAILCALL_SSA: [("output", "expr"), ("dest", "expr"), ("stack", "expr"), ("param", "expr")], LowLevelILOperation.LLIL_CALL_OUTPUT_SSA: [("dest_memory", "int"), ("dest", "reg_ssa_list")], LowLevelILOperation.LLIL_CALL_STACK_SSA: [("src", "reg_ssa"), ("src_memory", "int")], LowLevelILOperation.LLIL_CALL_PARAM: [("src", "expr_list")], @@ -412,7 +416,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value): + for j in range(count.value): value.append(operand_list[j]) core.BNLowLevelILFreeOperandList(operand_list) elif operand_type == "expr_list": @@ -420,7 +424,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value): + for j in range(count.value): value.append(LowLevelILInstruction(func, operand_list[j])) core.BNLowLevelILFreeOperandList(operand_list) elif operand_type == "reg_or_flag_list": @@ -428,7 +432,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value): + for j in range(count.value): if (operand_list[j] & (1 << 32)) != 0: value.append(ILFlag(func.arch, operand_list[j] & 0xffffffff)) else: @@ -439,7 +443,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in range(count.value // 2): reg = operand_list[j * 2] reg_version = operand_list[(j * 2) + 1] value.append(SSARegister(ILRegister(func.arch, reg), reg_version)) @@ -449,7 +453,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in range(count.value // 2): reg_stack = operand_list[j * 2] reg_version = operand_list[(j * 2) + 1] value.append(SSARegisterStack(ILRegisterStack(func.arch, reg_stack), reg_version)) @@ -459,7 +463,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in range(count.value // 2): flag = operand_list[j * 2] flag_version = operand_list[(j * 2) + 1] value.append(SSAFlag(ILFlag(func.arch, flag), flag_version)) @@ -469,7 +473,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in range(count.value // 2): if (operand_list[j * 2] & (1 << 32)) != 0: reg_or_flag = ILFlag(func.arch, operand_list[j * 2] & 0xffffffff) else: @@ -482,7 +486,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = {} - for j in xrange(count.value / 2): + for j in range(count.value // 2): reg_stack = operand_list[j * 2] adjust = operand_list[(j * 2) + 1] if adjust & 0x80000000: @@ -519,7 +523,7 @@ class LowLevelILInstruction(object): self.expr_index, tokens, count): return None result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -528,7 +532,7 @@ class LowLevelILInstruction(object): context = tokens[i].context confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeInstructionText(tokens, count.value) return result @@ -550,7 +554,7 @@ class LowLevelILInstruction(object): expr = self.function.get_medium_level_il_expr_index(self.expr_index) if expr is None: return None - return mediumlevelil.MediumLevelILInstruction(self.function.medium_level_il, expr) + return binaryninja.mediumlevelil.MediumLevelILInstruction(self.function.medium_level_il, expr) @property def mapped_medium_level_il(self): @@ -558,20 +562,20 @@ class LowLevelILInstruction(object): expr = self.function.get_mapped_medium_level_il_expr_index(self.expr_index) if expr is None: return None - return mediumlevelil.MediumLevelILInstruction(self.function.mapped_medium_level_il, expr) + return binaryninja.mediumlevelil.MediumLevelILInstruction(self.function.mapped_medium_level_il, expr) @property def value(self): """Value of expression if constant or a known value (read-only)""" value = core.BNGetLowLevelILExprValue(self.function.handle, self.expr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result @property def possible_values(self): """Possible values of expression using path-sensitive static data flow analysis (read-only)""" value = core.BNGetLowLevelILPossibleExprValues(self.function.handle, self.expr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result @@ -601,74 +605,74 @@ class LowLevelILInstruction(object): def get_reg_value(self, reg): reg = self.function.arch.get_reg_index(reg) value = core.BNGetLowLevelILRegisterValueAtInstruction(self.function.handle, reg, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_reg_value_after(self, reg): reg = self.function.arch.get_reg_index(reg) value = core.BNGetLowLevelILRegisterValueAfterInstruction(self.function.handle, reg, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_possible_reg_values(self, reg): reg = self.function.arch.get_reg_index(reg) value = core.BNGetLowLevelILPossibleRegisterValuesAtInstruction(self.function.handle, reg, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result def get_possible_reg_values_after(self, reg): reg = self.function.arch.get_reg_index(reg) value = core.BNGetLowLevelILPossibleRegisterValuesAfterInstruction(self.function.handle, reg, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result def get_flag_value(self, flag): flag = self.function.arch.get_flag_index(flag) value = core.BNGetLowLevelILFlagValueAtInstruction(self.function.handle, flag, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_flag_value_after(self, flag): flag = self.function.arch.get_flag_index(flag) value = core.BNGetLowLevelILFlagValueAfterInstruction(self.function.handle, flag, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_possible_flag_values(self, flag): flag = self.function.arch.get_flag_index(flag) value = core.BNGetLowLevelILPossibleFlagValuesAtInstruction(self.function.handle, flag, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result def get_possible_flag_values_after(self, flag): flag = self.function.arch.get_flag_index(flag) value = core.BNGetLowLevelILPossibleFlagValuesAfterInstruction(self.function.handle, flag, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result def get_stack_contents(self, offset, size): value = core.BNGetLowLevelILStackContentsAtInstruction(self.function.handle, offset, size, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_stack_contents_after(self, offset, size): value = core.BNGetLowLevelILStackContentsAfterInstruction(self.function.handle, offset, size, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = binaryninja.function.RegisterValue(self.function.arch, value) return result def get_possible_stack_contents(self, offset, size): value = core.BNGetLowLevelILPossibleStackContentsAtInstruction(self.function.handle, offset, size, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result def get_possible_stack_contents_after(self, offset, size): value = core.BNGetLowLevelILPossibleStackContentsAfterInstruction(self.function.handle, offset, size, self.instr_index) - result = function.PossibleValueSet(self.function.arch, value) + result = binaryninja.function.PossibleValueSet(self.function.arch, value) core.BNFreePossibleValueSet(value) return result @@ -692,7 +696,7 @@ class LowLevelILExpr(object): class LowLevelILFunction(object): """ - ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr + ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a binaryninja.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. @@ -775,7 +779,7 @@ class LowLevelILFunction(object): view = None if self.source_function is not None: view = self.source_function.view - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -802,7 +806,7 @@ class LowLevelILFunction(object): result = core.BNGetMediumLevelILForLowLevelIL(self.handle) if not result: return None - return mediumlevelil.MediumLevelILFunction(self.arch, result, self.source_function) + return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, result, self.source_function) @property def mapped_medium_level_il(self): @@ -812,7 +816,7 @@ class LowLevelILFunction(object): result = core.BNGetMappedMediumLevelIL(self.handle) if not result: return None - return mediumlevelil.MediumLevelILFunction(self.arch, result, self.source_function) + return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, result, self.source_function) def __setattr__(self, name, value): try: @@ -842,7 +846,7 @@ class LowLevelILFunction(object): if self.source_function is not None: view = self.source_function.view try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) finally: core.BNFreeBasicBlockList(blocks, count.value) @@ -860,7 +864,7 @@ class LowLevelILFunction(object): def set_indirect_branches(self, branches): branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): + for i in range(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)) @@ -1592,10 +1596,20 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CALL_STACK_ADJUST, dest.index, stack_adjust) + def tailcall(self, dest): + """ + ``tailcall`` returns an expression which jumps (branches) to the expression ``dest`` + + :param LowLevelILExpr dest: the expression to jump to + :return: The expression ``tailcall(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_TAILCALL, 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. + jump that makes the disassembler stop disassembling. :param LowLevelILExpr dest: the expression to jump to :return: The expression ``jump(dest)`` @@ -1797,8 +1811,9 @@ class LowLevelILFunction(object): param_list = [] for param in params: param_list.append(param.index) - return self.expr(LowLevelILOperation.LLIL_INTRINSIC, len(outputs), self.add_operand_list(output_list), - self.arch.get_intrinsic_index(intrinsic), len(params), self.add_operand_list(param_list), flags = flags) + call_param = self.expr(LowLevelILOperation.LLIL_CALL_PARAM, len(params), self.add_operand_list(param_list).index) + return self.expr(LowLevelILOperation.LLIL_INTRINSIC, len(outputs), self.add_operand_list(output_list).index, + self.arch.get_intrinsic_index(intrinsic), call_param.index, flags = flags) def breakpoint(self): """ @@ -2165,7 +2180,7 @@ class LowLevelILFunction(object): :rtype: LowLevelILExpr """ label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() - for i in xrange(len(labels)): + for i in range(len(labels)): label_list[i] = labels[i].handle return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) @@ -2178,7 +2193,7 @@ class LowLevelILFunction(object): :rtype: LowLevelILExpr """ operand_list = (ctypes.c_ulonglong * len(operands))() - for i in xrange(len(operands)): + for i in range(len(operands)): operand_list[i] = operands[i] return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) @@ -2261,7 +2276,7 @@ class LowLevelILFunction(object): count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSARegisterUses(self.handle, reg, reg_ssa.version, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -2271,7 +2286,7 @@ class LowLevelILFunction(object): count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSAFlagUses(self.handle, flag, flag_ssa.version, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -2280,7 +2295,7 @@ class LowLevelILFunction(object): count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSAMemoryUses(self.handle, index, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -2288,13 +2303,13 @@ class LowLevelILFunction(object): def get_ssa_reg_value(self, reg_ssa): reg = self.arch.get_reg_index(reg_ssa.reg) value = core.BNGetLowLevelILSSARegisterValue(self.handle, reg, reg_ssa.version) - result = function.RegisterValue(self.arch, value) + result = binaryninja.function.RegisterValue(self.arch, value) return result def get_ssa_flag_value(self, flag_ssa): flag = self.arch.get_flag_index(flag_ssa.flag) value = core.BNGetLowLevelILSSAFlagValue(self.handle, flag, flag_ssa.version) - result = function.RegisterValue(self.arch, value) + result = binaryninja.function.RegisterValue(self.arch, value) return result def get_medium_level_il_instruction_index(self, instr): @@ -2340,7 +2355,7 @@ class LowLevelILBasicBlock(basicblock.BasicBlock): self.il_function = owner def __iter__(self): - for idx in xrange(self.start, self.end): + for idx in range(self.start, self.end): yield self.il_function[idx] def __getitem__(self, idx): diff --git a/python/mainthread.py b/python/mainthread.py index 3e12bd65..5b0cd53c 100644 --- a/python/mainthread.py +++ b/python/mainthread.py @@ -19,8 +19,8 @@ # IN THE SOFTWARE. # Binary Ninja components -import _binaryninjacore as core -import scriptingprovider +from binaryninja import _binaryninjacore as core +from binaryninja import scriptingprovider def execute_on_main_thread(func): diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index d87b5bf7..cfa8f900 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -19,15 +19,18 @@ # IN THE SOFTWARE. import ctypes +import struct # Binary Ninja components -import _binaryninjacore as core -from .enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDependence -import function -import basicblock -import lowlevelil -import types -import struct +from binaryninja import _binaryninjacore as core +from binaryninja.enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDependence +from binaryninja import basicblock #required for MediumLevelILBasicBlock argument +from binaryninja import function +from binaryninja import types +from binaryninja import lowlevelil + +# 2-3 compatibility +from binaryninja import range class SSAVariable(object): @@ -148,6 +151,8 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_ADD_OVERFLOW: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_SYSCALL: [("output", "var_list"), ("params", "expr_list")], MediumLevelILOperation.MLIL_SYSCALL_UNTYPED: [("output", "expr"), ("params", "expr"), ("stack", "expr")], + MediumLevelILOperation.MLIL_TAILCALL: [("output", "var_list"), ("dest", "expr"), ("params", "expr_list")], + MediumLevelILOperation.MLIL_TAILCALL_UNTYPED: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")], MediumLevelILOperation.MLIL_BP: [], MediumLevelILOperation.MLIL_TRAP: [("vector", "int")], MediumLevelILOperation.MLIL_INTRINSIC: [("output", "var_list"), ("intrinsic", "intrinsic"), ("params", "expr_list")], @@ -193,6 +198,8 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")], MediumLevelILOperation.MLIL_SYSCALL_SSA: [("output", "expr"), ("params", "expr_list"), ("src_memory", "int")], MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA: [("output", "expr"), ("params", "expr"), ("stack", "expr")], + MediumLevelILOperation.MLIL_TAILCALL_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int")], + MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")], MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA: [("dest_memory", "int"), ("dest", "var_ssa_list")], MediumLevelILOperation.MLIL_CALL_PARAM_SSA: [("src_memory", "int"), ("src", "var_ssa_list")], MediumLevelILOperation.MLIL_LOAD_SSA: [("src", "expr"), ("src_memory", "int")], @@ -252,7 +259,7 @@ class MediumLevelILInstruction(object): count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self.expr_index, i, count) value = [] - for j in xrange(count.value): + for j in range(count.value): value.append(operand_list[j]) core.BNMediumLevelILFreeOperandList(operand_list) elif operand_type == "var_list": @@ -260,7 +267,7 @@ class MediumLevelILInstruction(object): operand_list = core.BNMediumLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value): + for j in range(count.value): value.append(function.Variable.from_identifier(self.function.source_function, operand_list[j])) core.BNMediumLevelILFreeOperandList(operand_list) elif operand_type == "var_ssa_list": @@ -268,7 +275,7 @@ class MediumLevelILInstruction(object): operand_list = core.BNMediumLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in range(count.value // 2): var_id = operand_list[j * 2] var_version = operand_list[(j * 2) + 1] value.append(SSAVariable(function.Variable.from_identifier(self.function.source_function, @@ -279,7 +286,7 @@ class MediumLevelILInstruction(object): operand_list = core.BNMediumLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value): + for j in range(count.value): value.append(MediumLevelILInstruction(func, operand_list[j])) core.BNMediumLevelILFreeOperandList(operand_list) self.operands.append(value) @@ -313,7 +320,7 @@ class MediumLevelILInstruction(object): self.expr_index, tokens, count): return None result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -359,7 +366,7 @@ class MediumLevelILInstruction(object): count = ctypes.c_ulonglong() deps = core.BNGetAllMediumLevelILBranchDependence(self.function.handle, self.instr_index, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): result[deps[i].branch] = ILBranchDependence(deps[i].dependence) core.BNFreeILBranchDependenceList(deps) return result @@ -410,11 +417,12 @@ class MediumLevelILInstruction(object): return [self.dest] elif self.operation in [MediumLevelILOperation.MLIL_SET_VAR_SPLIT, MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA]: return [self.high, self.low] - elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL]: + elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL]: return self.output - elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, + elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED, MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, - MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA]: + MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA, + MediumLevelILOperation.MLIL_TAILCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]: return self.output.vars_written elif self.operation in [MediumLevelILOperation.MLIL_CALL_OUTPUT, MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA]: return self.dest @@ -430,14 +438,14 @@ class MediumLevelILInstruction(object): elif self.operation in [MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD, MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD]: return [self.prev] + self.src.vars_read - elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, - MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA]: + elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL, + MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_SSA]: result = [] for param in self.params: result += param.vars_read return result - elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, - MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA]: + elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED, + MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]: return self.params.vars_read elif self.operation in [MediumLevelILOperation.MLIL_CALL_PARAM, MediumLevelILOperation.MLIL_CALL_PARAM_SSA, MediumLevelILOperation.MLIL_VAR_PHI]: @@ -591,7 +599,7 @@ class MediumLevelILExpr(object): class MediumLevelILFunction(object): """ - ``class MediumLevelILFunction`` contains the list of MediumLevelILExpr objects that make up a function. MediumLevelILExpr + ``class MediumLevelILFunction`` contains the list of MediumLevelILExpr objects that make up a binaryninja.function. MediumLevelILExpr objects can be added to the MediumLevelILFunction by calling ``append`` and passing the result of the various class methods which return MediumLevelILExpr objects. """ @@ -642,7 +650,7 @@ class MediumLevelILFunction(object): view = None if self.source_function is not None: view = self.source_function.view - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(MediumLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -699,7 +707,7 @@ class MediumLevelILFunction(object): if self.source_function is not None: view = self.source_function.view try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield MediumLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) finally: core.BNFreeBasicBlockList(blocks, count.value) @@ -770,7 +778,7 @@ class MediumLevelILFunction(object): :rtype: MediumLevelILExpr """ label_list = (ctypes.POINTER(core.BNMediumLevelILLabel) * len(labels))() - for i in xrange(len(labels)): + for i in range(len(labels)): label_list[i] = labels[i].handle return MediumLevelILExpr(core.BNMediumLevelILAddLabelList(self.handle, label_list, len(labels))) @@ -783,7 +791,7 @@ class MediumLevelILFunction(object): :rtype: MediumLevelILExpr """ operand_list = (ctypes.c_ulonglong * len(operands))() - for i in xrange(len(operands)): + for i in range(len(operands)): operand_list[i] = operands[i] return MediumLevelILExpr(core.BNMediumLevelILAddOperandList(self.handle, operand_list, len(operands))) @@ -837,7 +845,7 @@ class MediumLevelILFunction(object): var_data.storage = ssa_var.var.storage instrs = core.BNGetMediumLevelILSSAVarUses(self.handle, var_data, ssa_var.version, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -846,7 +854,7 @@ class MediumLevelILFunction(object): count = ctypes.c_ulonglong() instrs = core.BNGetMediumLevelILSSAMemoryUses(self.handle, version, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -873,7 +881,7 @@ class MediumLevelILFunction(object): var_data.storage = var.storage instrs = core.BNGetMediumLevelILVariableDefinitions(self.handle, var_data, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -886,7 +894,7 @@ class MediumLevelILFunction(object): var_data.storage = var.storage instrs = core.BNGetMediumLevelILVariableUses(self.handle, var_data, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(instrs[i]) core.BNFreeILInstructionList(instrs) return result @@ -931,7 +939,7 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): self.il_function = owner def __iter__(self): - for idx in xrange(self.start, self.end): + for idx in range(self.start, self.end): yield self.il_function[idx] def __getitem__(self, idx): diff --git a/python/metadata.py b/python/metadata.py index 554bbcf4..606e4ceb 100644 --- a/python/metadata.py +++ b/python/metadata.py @@ -19,11 +19,16 @@ # IN THE SOFTWARE. +from __future__ import absolute_import import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import MetadataType +from binaryninja import _binaryninjacore as core +from binaryninja.enums import MetadataType + +# 2-3 compatibility +from binaryninja import range +from binaryninja import pyNativeStr class Metadata(object): @@ -39,7 +44,7 @@ class Metadata(object): self.handle = core.BNCreateMetadataBooleanData(value) elif isinstance(value, str): if raw: - buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value) + buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value.encode('charmap')) self.handle = core.BNCreateMetadataRawData(buffer, len(value)) else: self.handle = core.BNCreateMetadataStringData(value) @@ -137,13 +142,16 @@ class Metadata(object): def __iter__(self): if self.is_array: - for i in xrange(core.BNMetadataSize(self.handle)): + for i in range(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] + for i in range(result.contents.size): + if isinstance(result.contents.keys[i], bytes): + yield str(pyNativeStr(result.contents.keys[i])) + else: + yield result.contents.keys[i] finally: core.BNFreeMetadataValueStore(result) else: @@ -168,13 +176,13 @@ class Metadata(object): def __str__(self): if self.is_string: - return core.BNMetadataGetString(self.handle) + return str(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): + for i in range(length.value): out_list.append(native_list[i]) core.BNFreeMetadataRaw(native_list) return ''.join(chr(a) for a in out_list) diff --git a/python/platform.py b/python/platform.py index a79e3b9a..9a5f70df 100644 --- a/python/platform.py +++ b/python/platform.py @@ -21,42 +21,45 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core -import startup -import architecture -import callingconvention -import types +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja import types + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _PlatformMetaClass(type): + @property def list(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() platforms = core.BNGetPlatformList(count) result = [] - for i in xrange(0, count.value): + for i in range(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() + binaryninja._init_plugins() count = ctypes.c_ulonglong() platforms = core.BNGetPlatformOSList(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(str(platforms[i])) core.BNFreePlatformOSList(platforms, count.value) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() platforms = core.BNGetPlatformList(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield Platform(None, core.BNNewPlatformReference(platforms[i])) finally: core.BNFreePlatformList(platforms, count.value) @@ -68,14 +71,14 @@ class _PlatformMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) def __getitem__(cls, value): - startup._init_plugins() + binaryninja._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() + binaryninja._init_plugins() count = ctypes.c_ulonglong() if os is None: platforms = core.BNGetPlatformList(count) @@ -84,18 +87,17 @@ class _PlatformMetaClass(type): else: platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) core.BNFreePlatformList(platforms, count.value) return result -class Platform(object): +class Platform(with_metaclass(_PlatformMetaClass, 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): @@ -105,7 +107,7 @@ class Platform(object): else: self.handle = handle self.__dict__["name"] = core.BNGetPlatformName(self.handle) - self.arch = architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(self.handle)) + self.arch = binaryninja.architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(self.handle)) def __del__(self): core.BNFreePlatform(self.handle) @@ -121,6 +123,11 @@ class Platform(object): return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + + @property def default_calling_convention(self): """ Default calling convention. @@ -132,7 +139,7 @@ class Platform(object): result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return binaryninja.callingconvention.CallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): @@ -150,7 +157,7 @@ class Platform(object): result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return binaryninja.callingconvention.CallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): @@ -168,7 +175,7 @@ class Platform(object): result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return binaryninja.callingconvention.CallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): @@ -186,7 +193,7 @@ class Platform(object): result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return binaryninja.callingconvention.CallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): @@ -204,7 +211,7 @@ class Platform(object): result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return binaryninja.callingconvention.CallingConvention(handle=result) @system_call_convention.setter def system_call_convention(self, value): @@ -221,8 +228,8 @@ class Platform(object): count = ctypes.c_ulonglong() cc = core.BNGetPlatformCallingConventions(self.handle, count) result = [] - for i in xrange(0, count.value): - result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) + for i in range(0, count.value): + result.append(binaryninja.callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result @@ -232,7 +239,7 @@ class Platform(object): count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformTypes(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) @@ -244,7 +251,7 @@ class Platform(object): count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformVariables(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) @@ -256,7 +263,7 @@ class Platform(object): count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformFunctions(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) @@ -268,7 +275,7 @@ class Platform(object): count = ctypes.c_ulonglong(0) call_list = core.BNGetPlatformSystemCalls(self.handle, count) result = {} - for i in xrange(0, count.value): + for i in range(0, count.value): name = types.QualifiedName._from_core_struct(call_list[i].name) t = types.Type(core.BNNewTypeReference(call_list[i].type), platform = self) result[call_list[i].number] = (name, t) @@ -383,8 +390,8 @@ class Platform(object): 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]) + for i in range(0, len(include_dirs)): + dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, @@ -396,13 +403,13 @@ class Platform(object): type_dict = {} variables = {} functions = {} - for i in xrange(0, parse.typeCount): + for i in range(0, parse.typeCount): name = types.QualifiedName._from_core_struct(parse.types[i].name) type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) - for i in xrange(0, parse.variableCount): + for i in range(0, parse.variableCount): name = types.QualifiedName._from_core_struct(parse.variables[i].name) variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) - for i in xrange(0, parse.functionCount): + for i in range(0, parse.functionCount): name = types.QualifiedName._from_core_struct(parse.functions[i].name) functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) @@ -429,8 +436,8 @@ class Platform(object): >>> """ dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) + for i in range(0, len(include_dirs)): + dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, @@ -442,13 +449,13 @@ class Platform(object): type_dict = {} variables = {} functions = {} - for i in xrange(0, parse.typeCount): + for i in range(0, parse.typeCount): name = types.QualifiedName._from_core_struct(parse.types[i].name) type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) - for i in xrange(0, parse.variableCount): + for i in range(0, parse.variableCount): name = types.QualifiedName._from_core_struct(parse.variables[i].name) variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) - for i in xrange(0, parse.functionCount): + for i in range(0, parse.functionCount): name = types.QualifiedName._from_core_struct(parse.functions[i].name) functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) diff --git a/python/plugin.py b/python/plugin.py index c6cd9fc0..4d9add9c 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -23,15 +23,16 @@ 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 -import lowlevelil -import mediumlevelil +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja.enums import PluginCommandType +from binaryninja import filemetadata +from binaryninja import binaryview +from binaryninja import function + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class PluginCommandContext(object): @@ -46,21 +47,21 @@ class PluginCommandContext(object): class _PluginCommandMetaClass(type): @property def list(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() commands = core.BNGetAllPluginCommands(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(PluginCommand(commands[i])) core.BNFreePluginCommandList(commands) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() commands = core.BNGetAllPluginCommands(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield PluginCommand(commands[i]) finally: core.BNFreePluginCommandList(commands) @@ -72,9 +73,8 @@ class _PluginCommandMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) -class PluginCommand(object): +class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): _registered_commands = [] - __metaclass__ = _PluginCommandMetaClass def __init__(self, cmd): self.command = core.BNPluginCommand() @@ -83,42 +83,47 @@ class PluginCommand(object): self.description = str(cmd.description) self.type = PluginCommandType(cmd.type) + @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + @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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj, addr) except: - log.log_error(traceback.format_exc()) + binaryninja.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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj, addr, length) except: - log.log_error(traceback.format_exc()) + binaryninja.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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.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()) + binaryninja.log.log_error(traceback.format_exc()) @classmethod def _low_level_il_function_action(cls, view, func, action): @@ -126,10 +131,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) @classmethod def _low_level_il_instruction_action(cls, view, func, instr, action): @@ -137,10 +142,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) @classmethod def _medium_level_il_function_action(cls, view, func, action): @@ -148,10 +153,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) @classmethod def _medium_level_il_instruction_action(cls, view, func, instr, action): @@ -159,21 +164,21 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: - log.log_error(traceback.format_exc()) + binaryninja.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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -181,11 +186,11 @@ class PluginCommand(object): 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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj, addr) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -193,11 +198,11 @@ class PluginCommand(object): 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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj, addr, length) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -205,12 +210,12 @@ class PluginCommand(object): 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)) + file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryninja.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()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -221,10 +226,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -235,10 +240,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -249,10 +254,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -263,10 +268,10 @@ class PluginCommand(object): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) return False @classmethod @@ -282,7 +287,7 @@ class PluginCommand(object): .. warning:: Calling ``register`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._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)) @@ -301,7 +306,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_address`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._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)) @@ -320,7 +325,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_range`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._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)) @@ -339,7 +344,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._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)) @@ -358,7 +363,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_low_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_action(view, func, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_is_valid(view, func, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -377,7 +382,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_low_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_action(view, func, instr, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_is_valid(view, func, instr, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -396,7 +401,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_medium_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_action(view, func, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_is_valid(view, func, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -415,7 +420,7 @@ class PluginCommand(object): .. warning:: Calling ``register_for_medium_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_action(view, func, instr, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_is_valid(view, func, instr, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -463,7 +468,7 @@ class PluginCommand(object): elif self.command.type == PluginCommandType.LowLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, lowlevelil.LowLevelILInstruction): + if not isinstance(context.instruction, binaryninja.lowlevelil.LowLevelILInstruction): return False if not self.command.lowLevelILInstructionIsValid: return True @@ -478,7 +483,7 @@ class PluginCommand(object): elif self.command.type == PluginCommandType.MediumLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, mediumlevelil.MediumLevelILInstruction): + if not isinstance(context.instruction, binaryninja.mediumlevelil.MediumLevelILInstruction): return False if not self.command.mediumLevelILInstructionIsValid: return True @@ -546,7 +551,7 @@ class MainThreadActionHandler(object): try: self.add_action(MainThreadAction(action)) except: - log.log_error(traceback.format_exc()) + binaryninja.log.log_error(traceback.format_exc()) def add_action(self, action): pass @@ -559,25 +564,23 @@ class _BackgroundTaskMetaclass(type): 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) + for i in range(0, count.value): + result.append(BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i]))) + core.BNFreeBackgroundTaskList(tasks, count.value) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() tasks = core.BNGetRunningBackgroundTasks(count) try: - for i in xrange(0, count.value): - yield BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i])) + for i in range(0, count.value): + yield BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i])) finally: - core.BNFreeBackgroundTaskList(tasks) - + core.BNFreeBackgroundTaskList(tasks, count.value) -class BackgroundTask(object): - __metaclass__ = _BackgroundTaskMetaclass +class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): if handle is None: self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) @@ -588,6 +591,11 @@ class BackgroundTask(object): core.BNFreeBackgroundTask(self.handle) @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + + @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) diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 2f293577..c7a0c83a 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -21,9 +21,10 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core -from .enums import PluginType, PluginUpdateStatus -import startup +from binaryninja import _binaryninjacore as core + +# 2-3 compatibility +from binaryninja import range class RepoPlugin(object): @@ -31,7 +32,9 @@ class RepoPlugin(object): ``RepoPlugin` is mostly read-only, however you can install/uninstall enable/disable plugins. RepoPlugins are created by parsing the plugins.json in a plugin repository. """ + from binaryninja.enums import PluginType, PluginUpdateStatus def __init__(self, handle): + raise Exception("RepoPlugin temporarily disabled!") self.handle = core.handle_of_type(handle, core.BNRepoPlugin) def __del__(self): @@ -110,7 +113,7 @@ class RepoPlugin(object): result = [] count = ctypes.c_ulonglong(0) plugintypes = core.BNPluginGetPluginTypes(self.handle, count) - for i in xrange(count.value): + for i in range(count.value): result.append(PluginType(plugintypes[i])) core.BNFreePluginTypes(plugintypes) return result @@ -136,6 +139,7 @@ class Repository(object): ``Repository`` is a read-only class. Use RepositoryManager to Enable/Disable/Install/Uninstall plugins. """ def __init__(self, handle): + raise Exception("Repository temporarily disabled!") self.handle = core.handle_of_type(handle, core.BNRepository) def __del__(self): @@ -181,7 +185,7 @@ class Repository(object): pluginlist = [] count = ctypes.c_ulonglong(0) result = core.BNRepositoryGetPlugins(self.handle, count) - for i in xrange(count.value): + for i in range(count.value): pluginlist.append(RepoPlugin(handle=result[i])) core.BNFreeRepositoryPluginList(result, count.value) del result @@ -199,6 +203,7 @@ class RepositoryManager(object): the plugins that are installed/unstalled enabled/disabled """ def __init__(self, handle=None): + raise Exception("RepositoryManager temporarily disabled!") self.handle = core.BNGetRepositoryManager() def __getitem__(self, repo_path): @@ -217,7 +222,7 @@ class RepositoryManager(object): result = [] count = ctypes.c_ulonglong(0) repos = core.BNRepositoryManagerGetRepositories(self.handle, count) - for i in xrange(count.value): + for i in range(count.value): result.append(Repository(handle=repos[i])) core.BNFreeRepositoryManagerRepositoriesList(repos) return result @@ -233,7 +238,7 @@ class RepositoryManager(object): @property def default_repository(self): """Gets the default Repository""" - startup._init_plugins() + binaryninja._init_plugins() return Repository(handle=core.BNRepositoryManagerGetDefaultRepository(self.handle)) def enable_plugin(self, plugin, install=True, repo=None): diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index e7838748..37642ed4 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -26,14 +26,15 @@ 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 +# Binary Ninja components +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja.enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState +from binaryninja import log + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _ThreadActionContext(object): @@ -135,7 +136,7 @@ class ScriptingInstance(object): def _set_current_binary_view(self, ctxt, view): try: if view: - view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + view = binaryninja.binaryview.BinaryView(handle = core.BNNewViewReference(view)) else: view = None self.perform_set_current_binary_view(view) @@ -145,12 +146,12 @@ class ScriptingInstance(object): def _set_current_function(self, ctxt, func): try: if func: - func = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) + func = binaryninja.function.Function(binaryninja.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()) + log.log_error(traceback.format_exc()) def _set_current_basic_block(self, ctxt, block): try: @@ -159,7 +160,7 @@ class ScriptingInstance(object): if func is None: block = None else: - block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) + block = binaryninja.basicblock.BasicBlock(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) core.BNFreeFunction(func) else: block = None @@ -256,30 +257,31 @@ class ScriptingInstance(object): class _ScriptingProviderMetaclass(type): + @property def list(self): """List all ScriptingProvider types (read-only)""" - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetScriptingProviderList(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(ScriptingProvider(types[i])) core.BNFreeScriptingProviderList(types) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetScriptingProviderList(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield ScriptingProvider(types[i]) finally: core.BNFreeScriptingProviderList(types) def __getitem__(self, value): - startup._init_plugins() + binaryninja._init_plugins() provider = core.BNGetScriptingProviderByName(str(value)) if provider is None: raise KeyError("'%s' is not a valid scripting provider" % str(value)) @@ -292,8 +294,7 @@ class _ScriptingProviderMetaclass(type): raise AttributeError("attribute '%s' is read only" % name) -class ScriptingProvider(object): - __metaclass__ = _ScriptingProviderMetaclass +class ScriptingProvider(with_metaclass(_ScriptingProviderMetaclass, object)): name = None instance_class = None @@ -304,6 +305,12 @@ class ScriptingProvider(object): self.handle = core.handle_of_type(handle, core.BNScriptingProvider) self.__dict__["name"] = core.BNGetScriptingProviderName(handle) + @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + + def register(self): self._cb = core.BNScriptingProviderCallbacks() self._cb.context = 0 @@ -484,7 +491,7 @@ class PythonScriptingInstance(ScriptingInstance): self.code = None self.input = "" - self.interpreter.push("from binaryninja import *\n") + self.interpreter.push("from binaryninja import *") def execute(self, code): self.code = code @@ -547,8 +554,8 @@ class PythonScriptingInstance(ScriptingInstance): self.locals["current_llil"] = self.active_func.low_level_il self.locals["current_mlil"] = self.active_func.medium_level_il - for line in code.split("\n"): - self.interpreter.push(line) + for line in code.split(b'\n'): + self.interpreter.push(line.decode('charmap')) if self.locals["here"] != self.active_addr: if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): @@ -650,6 +657,8 @@ 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) +def redirect_stdio(): + sys.stdin = _PythonScriptingInstanceInput(sys.stdin) + sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) + sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) + sys.excepthook = sys.__excepthook__
\ No newline at end of file diff --git a/python/setting.py b/python/setting.py index d58c8955..93f6b72a 100644 --- a/python/setting.py +++ b/python/setting.py @@ -21,7 +21,11 @@ import ctypes # Binary Ninja components -import _binaryninjacore as core +from binaryninja import _binaryninjacore as core + +# 2-3 compatibility +from binaryninja import range +from binaryninja import pyNativeStr class Setting(object): @@ -45,7 +49,7 @@ class Setting(object): default_list[i] = default_value[i] result = core.BNSettingGetIntegerList(self.plugin_name, name, default_list, ctypes.byref(length)) out_list = [] - for i in xrange(length.value): + for i in range(length.value): out_list.append(result[i]) core.BNFreeSettingIntegerList(result) return out_list @@ -55,11 +59,11 @@ class Setting(object): length.value = len(default_value) default_list = (ctypes.c_char_p * len(default_value))() for i in range(len(default_value)): - default_list[i] = default_value[i] + default_list[i] = default_value[i].encode('charmap') result = core.BNSettingGetStringList(self.plugin_name, name, default_list, ctypes.byref(length)) out_list = [] - for i in xrange(length.value): - out_list.append(result[i]) + for i in range(length.value): + out_list.append(pyNativeStr(result[i])) core.BNFreeStringList(result, length) return out_list @@ -100,7 +104,7 @@ class Setting(object): length = ctypes.c_ulonglong() length.value = len(value) default_list = (ctypes.c_longlong * len(value))() - for i in xrange(len(value)): + for i in range(len(value)): default_list[i] = value[i] return core.BNSettingSetIntegerList(self.plugin_name, name, default_list, length, auto_flush) @@ -109,8 +113,8 @@ class Setting(object): length = ctypes.c_ulonglong() length.value = len(value) default_list = (ctypes.c_char_p * len(value))() - for i in xrange(len(value)): - default_list[i] = str(value[i]) + for i in range(len(value)): + default_list[i] = value[i].encode('charmap') return core.BNSettingSetStringList(self.plugin_name, name, default_list, length, auto_flush) @@ -138,4 +142,4 @@ class Setting(object): core.BNSettingRemoveSettingGroup(self.plugin_name, auto_flush) def remove_setting(self, setting, auto_flush=True): - core.BNSettingRemoveSetting(self.plugin_name, setting, auto_flush)
\ No newline at end of file + core.BNSettingRemoveSetting(self.plugin_name, setting, auto_flush) diff --git a/python/startup.py b/python/startup.py index 0abc47cb..04e7b980 100644 --- a/python/startup.py +++ b/python/startup.py @@ -18,18 +18,18 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import _binaryninjacore as core +#from binaryninja import _binaryninjacore as core -_plugin_init = False +#_plugin_init = False -def _init_plugins(): - global _plugin_init - if not _plugin_init: - _plugin_init = True - core.BNInitCorePlugins() - core.BNInitUserPlugins() - core.BNInitRepoPlugins() - if not core.BNIsLicenseValidated(): - raise RuntimeError("License is not valid. Please supply a valid license.") +# def _init_plugins(): +# global _plugin_init +# if not _plugin_init: +# _plugin_init = True +# core.BNInitCorePlugins() +# core.BNInitUserPlugins() +# core.BNInitRepoPlugins() +# 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 index 59d719e7..1734d248 100644 --- a/python/transform.py +++ b/python/transform.py @@ -23,31 +23,35 @@ import ctypes import abc # Binary Ninja components -import _binaryninjacore as core -from enums import TransformType -import startup -import log -import databuffer +import binaryninja +from binaryninja import log +from binaryninja import databuffer +from binaryninja import _binaryninjacore as core +from binaryninja.enums import TransformType + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _TransformMetaClass(type): @property def list(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() xforms = core.BNGetTransformTypeList(count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(Transform(xforms[i])) core.BNFreeTransformTypeList(xforms) return result def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() xforms = core.BNGetTransformTypeList(count) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield Transform(xforms[i]) finally: core.BNFreeTransformTypeList(xforms) @@ -59,14 +63,14 @@ class _TransformMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) def __getitem__(cls, name): - startup._init_plugins() + binaryninja._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() + binaryninja._init_plugins() if cls.name is None: raise ValueError("transform 'name' is not defined") if cls.long_name is None: @@ -90,14 +94,13 @@ class TransformParameter(object): self.fixed_length = fixed_length -class Transform(object): +class Transform(with_metaclass(_TransformMetaClass, 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: @@ -124,7 +127,7 @@ class Transform(object): count = ctypes.c_ulonglong() params = core.BNGetTransformParameterList(self.handle, count) self.parameters = [] - for i in xrange(0, count.value): + for i in range(0, count.value): self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) core.BNFreeTransformParameterList(params, count.value) @@ -145,7 +148,7 @@ class Transform(object): try: count[0] = len(self.parameters) param_buf = (core.BNTransformParameterInfo * len(self.parameters))() - for i in xrange(0, len(self.parameters)): + for i in range(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 @@ -170,7 +173,7 @@ class Transform(object): try: input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) param_map = {} - for i in xrange(0, count): + for i in range(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) @@ -187,7 +190,7 @@ class Transform(object): try: input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) param_map = {} - for i in xrange(0, count): + for i in range(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) @@ -200,6 +203,11 @@ class Transform(object): log.log_error(traceback.format_exc()) return False + @property + def list(self): + """Allow tab completion to discover metaclass list property""" + pass + @abc.abstractmethod def perform_decode(self, data, params): if self.type == TransformType.InvertingTransform: @@ -213,9 +221,9 @@ class Transform(object): def decode(self, input_buf, params = {}): input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() - keys = params.keys() + keys = list(params.keys()) param_buf = (core.BNTransformParameter * len(keys))() - for i in xrange(0, len(keys)): + for i in range(0, len(keys)): data = databuffer.DataBuffer(params[keys[i]]) param_buf[i].name = keys[i] param_buf[i].value = data.handle @@ -226,9 +234,9 @@ class Transform(object): def encode(self, input_buf, params = {}): input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() - keys = params.keys() + keys = list(params.keys()) param_buf = (core.BNTransformParameter * len(keys))() - for i in xrange(0, len(keys)): + for i in range(0, len(keys)): data = databuffer.DataBuffer(params[keys[i]]) param_buf[i].name = keys[i] param_buf[i].value = data.handle diff --git a/python/types.py b/python/types.py index 42ed7ddd..12e7733f 100644 --- a/python/types.py +++ b/python/types.py @@ -18,25 +18,32 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import max_confidence = 255 import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType -import callingconvention -import function +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja.enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType + +# 2-3 compatibility +from binaryninja import range +from binaryninja import pyNativeStr class QualifiedName(object): def __init__(self, name = []): if isinstance(name, str): self.name = [name] + self.byte_name = [name.encode('charmap')] elif isinstance(name, QualifiedName): self.name = name.name + self.byte_name = [n.encode('charmap') for n in name.name] else: - self.name = name + self.name = [pyNativeStr(i) for i in name] + self.byte_name = name def __str__(self): return "::".join(self.name) @@ -98,8 +105,8 @@ class QualifiedName(object): 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] + for i in range(0, len(self.name)): + name_list[i] = self.name[i].encode('charmap') result.name = name_list result.nameCount = len(self.name) return result @@ -107,7 +114,7 @@ class QualifiedName(object): @classmethod def _from_core_struct(cls, name): result = [] - for i in xrange(0, name.nameCount): + for i in range(0, name.nameCount): result.append(name.name[i]) return QualifiedName(result) @@ -292,7 +299,7 @@ class Type(object): result = core.BNGetTypeCallingConvention(self.handle) if not result.convention: return None - return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + return binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) @property def parameters(self): @@ -300,7 +307,7 @@ class Type(object): count = ctypes.c_ulonglong() params = core.BNGetTypeParameters(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): param_type = Type(core.BNNewTypeReference(params[i].type), platform = self.platform, confidence = params[i].typeConfidence) if params[i].defaultLocation: param_location = None @@ -310,7 +317,7 @@ class Type(object): name = self.platform.arch.get_reg_name(params[i].location.storage) elif params[i].location.type == VariableSourceType.StackVariableSourceType: name = "arg_%x" % params[i].location.storage - param_location = function.Variable(None, params[i].location.type, params[i].location.index, + param_location = binaryninja.function.Variable(None, params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type) result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) @@ -344,7 +351,7 @@ class Type(object): return None return Enumeration(result) - @property + @property def named_type_reference(self): """Reference to a named type (read-only)""" result = core.BNGetTypeNamedTypeReference(self.handle) @@ -376,7 +383,7 @@ class Type(object): def __repr__(self): if self.confidence < max_confidence: - return "<type: %s, %d%% confidence>" % (str(self), (self.confidence * 100) / max_confidence) + return "<type: %s, %d%% confidence>" % (str(self), (self.confidence * 100) // max_confidence) return "<type: %s>" % str(self) def get_string_before_name(self): @@ -403,7 +410,7 @@ class Type(object): platform = self.platform.handle tokens = core.BNGetTypeTokens(self.handle, platform, base_confidence, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -412,7 +419,7 @@ class Type(object): context = tokens[i].context confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result @@ -423,7 +430,7 @@ class Type(object): platform = self.platform.handle tokens = core.BNGetTypeTokensBeforeName(self.handle, platform, base_confidence, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -432,7 +439,7 @@ class Type(object): context = tokens[i].context confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result @@ -443,7 +450,7 @@ class Type(object): platform = self.platform.handle tokens = core.BNGetTypeTokensAfterName(self.handle, platform, base_confidence, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) text = tokens[i].text value = tokens[i].value @@ -452,7 +459,7 @@ class Type(object): context = tokens[i].context confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(binaryninja.function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result @@ -574,7 +581,7 @@ class Type(object): :param bool variable_arguments: optional argument for functions that have a variable number of arguments """ param_buf = (core.BNFunctionParameter * len(params))() - for i in xrange(0, len(params)): + for i in range(0, len(params)): if isinstance(params[i], Type): param_buf[i].name = "" param_buf[i].type = params[i].handle @@ -851,7 +858,7 @@ class Structure(object): count = ctypes.c_ulonglong() members = core.BNGetStructureMembers(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type), confidence = members[i].typeConfidence), members[i].name, members[i].offset)) core.BNFreeStructureMemberList(members, count.value) @@ -962,7 +969,7 @@ class Enumeration(object): count = ctypes.c_ulonglong() members = core.BNGetEnumerationMembers(self.handle, count) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) core.BNFreeEnumerationMemberList(members, count.value) return result @@ -1018,8 +1025,8 @@ def preprocess_source(source, filename=None, include_dirs=[]): 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]) + for i in range(0, len(include_dirs)): + dir_buf[i] = include_dirs[i].encode('charmap') output = ctypes.c_char_p() errors = ctypes.c_char_p() result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) diff --git a/python/undoaction.py b/python/undoaction.py index 7e3c76a0..7891bf54 100644 --- a/python/undoaction.py +++ b/python/undoaction.py @@ -23,10 +23,8 @@ import json import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import ActionType -import startup -import log +from binaryninja import _binaryninjacore as core +from binaryninja.enums import ActionType class UndoAction(object): @@ -52,7 +50,7 @@ class UndoAction(object): @classmethod def register(cls): - startup._init_plugins() + binaryninja._init_plugins() if cls.name is None: raise ValueError("undo action 'name' not defined") if cls.action_type is None: diff --git a/python/update.py b/python/update.py index 1eb8ea61..7f5d6f9c 100644 --- a/python/update.py +++ b/python/update.py @@ -22,16 +22,18 @@ import traceback import ctypes # Binary Ninja components -import _binaryninjacore as core -from enums import UpdateResult -import startup -import log +from binaryninja import _binaryninjacore as core +from binaryninja.enums import UpdateResult + +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _UpdateChannelMetaClass(type): @property def list(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() errors = ctypes.c_char_p() channels = core.BNGetUpdateChannels(count, errors) @@ -40,7 +42,7 @@ class _UpdateChannelMetaClass(type): core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise IOError(error_str) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) core.BNFreeUpdateChannelList(channels, count.value) return result @@ -54,7 +56,7 @@ class _UpdateChannelMetaClass(type): return core.BNSetActiveUpdateChannel(value) def __iter__(self): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() errors = ctypes.c_char_p() channels = core.BNGetUpdateChannels(count, errors) @@ -63,7 +65,7 @@ class _UpdateChannelMetaClass(type): core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise IOError(error_str) try: - for i in xrange(0, count.value): + for i in range(0, count.value): yield UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) finally: core.BNFreeUpdateChannelList(channels, count.value) @@ -75,7 +77,7 @@ class _UpdateChannelMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) def __getitem__(cls, name): - startup._init_plugins() + binaryninja._init_plugins() count = ctypes.c_ulonglong() errors = ctypes.c_char_p() channels = core.BNGetUpdateChannels(count, errors) @@ -84,7 +86,7 @@ class _UpdateChannelMetaClass(type): core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise IOError(error_str) result = None - for i in xrange(0, count.value): + for i in range(0, count.value): if channels[i].name == str(name): result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) break @@ -108,9 +110,7 @@ class UpdateProgressCallback(object): log.log_error(traceback.format_exc()) -class UpdateChannel(object): - __metaclass__ = _UpdateChannelMetaClass - +class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): def __init__(self, name, desc, ver): self.name = name self.description = desc @@ -127,7 +127,7 @@ class UpdateChannel(object): core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise IOError(error_str) result = [] - for i in xrange(0, count.value): + for i in range(0, count.value): result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) core.BNFreeUpdateChannelVersionList(versions, count.value) return result @@ -143,7 +143,7 @@ class UpdateChannel(object): core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise IOError(error_str) result = None - for i in xrange(0, count.value): + for i in range(0, count.value): if versions[i].version == self.latest_version_num: result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) break diff --git a/scripts/install_api.py b/scripts/install_api.py index 53679ba5..3cc0ed0d 100755 --- a/scripts/install_api.py +++ b/scripts/install_api.py @@ -80,6 +80,6 @@ else: sys.exit(1) binaryninja_pth_path = os.path.join(install_path, 'binaryninja.pth') -open(binaryninja_pth_path, 'wb').write(api_path) +open(binaryninja_pth_path, 'wb').write(api_path.encode('charmap')) print("Binary Ninja API installed using {}".format(binaryninja_pth_path)) diff --git a/suite/api_test.py b/suite/api_test.py new file mode 100644 index 00000000..740f88e3 --- /dev/null +++ b/suite/api_test.py @@ -0,0 +1,221 @@ +import unittest +import platform +import os +from binaryninja.setting import Setting +from binaryninja.metadata import Metadata +from binaryninja.demangle import demangle_gnu3, get_qualified_name +from binaryninja.architecture import Architecture + + +class SettingsAPI(unittest.TestCase): + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + setting = Setting("test") + setting.remove_setting_group("test") + + def test_bool_settings(self): + setting = Setting("test") + setting.set("bool_test_true", True) + setting.set("bool_test_false", False) + assert not setting.get_bool("bool_test_false"), "bool_test_false failed" + assert setting.get_bool("bool_test_true"), "bool_test_true failed" + assert setting.get_bool("bool_test_default_True", True), "bool_test_default_True failed" + assert not setting.get_bool("bool_test_default_False", False), "bool_test_default_False failed" + + def test_int_settings(self): + setting = Setting("test") + setting.set("int_test1", 0x100) + setting.set("int_test2", 0) + setting.set("int_test3", -1) + assert setting.get_integer("int_test1") == 0x100, "int_test1 failed" + assert setting.get_integer("int_test2") == 0, "int_test2 failed" + assert setting.get_integer("int_test3") == -1, "int_test3 failed" + assert setting.get_integer("int_test_default_1", 1) == 1, "int_test_default_1 failed" + + def test_float_settings(self): + setting = Setting("test") + setting.set("float_test1", 10.5) + setting.set("float_test2", -0.5) + assert setting.get_double("float_test1") == 10.5, "float_test1 failed" + assert setting.get_double("float_test2") == -0.5, "float_test1 failed" + assert setting.get_double("float_test_default", -5.5), "float_test_default failed" + + def test_str_settings(self): + setting = Setting("test") + setting.set("str_test1", "hi") + setting.set("str_test2", "") + setting.set("str_test3", "A" * 1000) + assert setting.get_string("str_test1") == "hi", "str_test1 failed" + assert setting.get_string("str_test2") == "", "str_test2 failed" + assert setting.get_string("str_test3") == "A" * 1000, "str_test3 failed" + assert setting.get_string("str_test_default", "hi") == "hi", "str_test_default failed" + + def test_int_list_settings(self): + setting = Setting("test") + setting.set("int_list_test1", [0x100]) + setting.set("int_list_test2", [1, 2]) + setting.set("int_list_test3", []) + assert setting.get_integer_list("int_list_test1") == [0x100], "int_list_test1 failed" + assert setting.get_integer_list("int_list_test2") == [1, 2], "int_list_test2 failed" + assert setting.get_integer_list("int_list_test3") == [], "int_list_test3 failed" + assert setting.get_integer_list("int_list_test_default", [2, 3]), "int_list_test_default failed" + + def test_str_list_settings(self): + setting = Setting("test") + setting.set("str_list_test1", ["hi"]) + setting.set("str_list_test2", ["hello", "world"]) + setting.set("str_list_test3", []) + assert setting.get_string_list("str_list_test1") == ["hi"], "str_list_test1 failed" + assert setting.get_string_list("str_list_test2") == ["hello", "world"], "str_list_test2 failed" + assert setting.get_string_list("str_list_test3") == [], "str_list_test3 failed" + assert setting.get_string_list("str_list_test_default", ["hi", "there"]), "str_list_test_default failed" + + +class MetaddataAPI(unittest.TestCase): + def test_metadata_basic_types(self): + # Core is tested thoroughly through the C++ unit tests here we focus on the python api side + md = Metadata(1) + assert md.is_integer + assert int(md) == 1 + assert md.value == 1 + + md = Metadata(-1, signed=True) + assert md.is_signed_integer + assert int(md) == -1 + assert md.value == -1 + md = Metadata(1, signed=False) + assert md.is_unsigned_integer + assert int(md) == 1 + md = Metadata(3.14) + assert md.is_float + assert float(md) == 3.14 + assert md.value == 3.14 + + md = Metadata("asdf") + assert md.is_string + assert str(md) == "asdf" + assert len(md) == 4 + assert md.value == "asdf" + + md = Metadata("\x00\x00\x41\x00", raw=True) + assert md.is_raw + assert len(md) == 4 + assert str(md) == "\x00\x00\x41\x00" + assert md.value == "\x00\x00\x41\x00" + + def test_metadata_compound_types(self): + md = Metadata([1, 2, 3]) + assert md.is_array + assert md.value == [1, 2, 3] + assert len(md) == 3 + assert md[0] == 1 + assert md[1] == 2 + assert md[2] == 3 + assert isinstance(list(md), list) + md.remove(0) + assert len(md) == 2 + assert md == [2, 3] + + md = Metadata({"a": 1, "b": 2}) + assert md.is_dict + assert len(md) == 2 + assert md.value == {"a": 1, "b": 2} + assert md["a"] == 1 + assert md["b"] == 2 + md.remove("a") + assert len(md) == 1 + assert md == {"b": 2} + + def test_metadata_equality(self): + assert Metadata(1) == 1 + assert Metadata(1) != 0 + assert Metadata(1) == Metadata(1) + assert Metadata(1) != Metadata(0) + + assert Metadata(3.14) == 3.14 + assert Metadata(3.14) == Metadata(3.14) + assert Metadata(3.14) != 3.1 + assert Metadata(3.14) != Metadata(3.1) + + assert Metadata("asdf") == "asdf" + assert Metadata("asdf") == Metadata("asdf") + assert Metadata("asdf") != "qwer" + assert Metadata("asdf") != Metadata("qwer") + + assert Metadata("as\x00df", raw=True) == "as\x00df" + assert Metadata("as\x00df", raw=True) == Metadata("as\x00df", raw=True) + assert Metadata("as\x00df", raw=True) != "qw\x00er" + assert Metadata("as\x00df", raw=True) != Metadata("qw\x00er", raw=True) + + assert Metadata([1, 2, 3]) == [1, 2, 3] + assert Metadata([1, 2, 3]) == Metadata([1, 2, 3]) + assert Metadata([1, 2, 3]) != [1, 2] + assert Metadata([1, 2, 3]) != Metadata([1, 2]) + + assert Metadata({"a": 1, "b": 2}) == {"a": 1, "b": 2} + assert Metadata({"a": 1, "b": 2}) == Metadata({"a": 1, "b": 2}) + assert Metadata({"a": 1, "b": 2}) != {"a": 1} + assert Metadata({"a": 1, "b": 2}) != Metadata({"a": 1}) + + +class DemanglerTest(unittest.TestCase): + def get_type_string(self, t, n): + out = "" + if t is not None: + out = str(t.get_string_before_name()) + if len(out) > 1 and out[-1] != ' ': + out += " " + out += get_qualified_name(n) + out += str(t.get_string_after_name()) + return out + + def test_demangle_gnu3(self): + tests = ("__ZN15BinaryNinjaCore12BinaryReader5Read8Ev", + "__ZN5QListIP18QAbstractAnimationE18detach_helper_growEii", + "__ZN13QStatePrivate22emitPropertiesAssignedEv", + "__ZN17QtMetaTypePrivate23QMetaTypeFunctionHelperI14QItemSelectionLb1EE9ConstructEPvPKv", + "__ZN18QSharedDataPointerI16QFileInfoPrivateE4dataEv", + "__ZN26QAbstractNativeEventFilterD2Ev", + "__ZN5QListIP14QAbstractStateE3endEv", + "__ZNK15BinaryNinjaCore19ArchitectureWrapper22GetOpcodeDisplayLengthEv", + "__ZN15BinaryNinjaCore17ScriptingInstance19SetCurrentSelectionEyy", + "__ZL32qt_meta_stringdata_QHistoryState", + "__ZN12_GLOBAL__N_114TypeDestructor14DestructorImplI11QStringListLb1EE8DestructEiPv", + "__ZN13QGb18030Codec5_nameEv", + "__ZN5QListIP7QObjectE6detachEv", + "__ZN19QBasicAtomicPointerI9QFreeListI13QMutexPrivateN12_GLOBAL__N_117FreeListConstantsEEE17testAndSetReleaseEPS4_S6_", + "__ZN12QJsonPrivate6Parser12reserveSpaceEi", + "__ZN20QStateMachinePrivate12endMacrostepEb", + "__ZN14QScopedPointerI20QTemporaryDirPrivate21QScopedPointerDeleterIS0_EED2Ev", + "__ZN14QVariantIsNullIN12_GLOBAL__N_115CoreTypesFilterEE8delegateI10QMatrix4x4EEbPKT_", + "__ZN26QAbstractProxyModelPrivateC2Ev", + "__ZNSt3__110__function6__funcIZ26BNWorkerInteractiveEnqueueE4$_16NS_9allocatorIS2_EEFvvEEclEv") + + results = ("int32_t BinaryNinjaCore::BinaryReader::Read8()", + "int32_t QList<QAbstractAnimation*>::detach_helper_grow(int32_t, int32_t)", + "int32_t QStatePrivate::emitPropertiesAssigned()", + "int32_t QtMetaTypePrivate::QMetaTypeFunctionHelper<QItemSelection, true>::Construct(void*, void const*)", + "int32_t QSharedDataPointer<QFileInfoPrivate>::data()", + "void QAbstractNativeEventFilter::~QAbstractNativeEventFilter()", + "int32_t QList<QAbstractState*>::end()", + "int32_t BinaryNinjaCore::ArchitectureWrapper::GetOpcodeDisplayLength() const", + "int32_t BinaryNinjaCore::ScriptingInstance::SetCurrentSelection(uint64_t, uint64_t)", + "qt_meta_stringdata_QHistoryState", + "int32_t (anonymous namespace)::TypeDestructor::DestructorImpl<QStringList, true>::Destruct(int32_t, void*)", + "int32_t QGb18030Codec::_name()", + "int32_t QList<QObject*>::detach()", + "int32_t QBasicAtomicPointer<QFreeList<QMutexPrivate, (anonymous namespace)::FreeListConstants> >::testAndSetRelease(QFreeList<QMutexPrivate, (anonymous namespace)::FreeListConstants>*, QFreeList<QMutexPrivate, (anonymous namespace)::FreeListConstants>*)", + "int32_t QJsonPrivate::Parser::reserveSpace(int32_t)", + "int32_t QStateMachinePrivate::endMacrostep(bool)", + "void QScopedPointer<QTemporaryDirPrivate, QScopedPointerDeleter<QTemporaryDirPrivate> >::~QScopedPointer()", + "bool QVariantIsNull<(anonymous namespace)::CoreTypesFilter>::delegate<QMatrix4x4>(QMatrix4x4 const*)", + "void QAbstractProxyModelPrivate::QAbstractProxyModelPrivate()", + "int32_t std::__1::__function::__func<BNWorkerInteractiveEnqueue::$_16, std::__1::allocator<BNWorkerInteractiveEnqueue::$_16>, void ()>::operator()()") + + for i, test in enumerate(tests): + t, n = demangle_gnu3(Architecture['x86'], test) + assert self.get_type_string(t, n) == results[i] diff --git a/suite/binaries b/suite/binaries new file mode 160000 +Subproject 96d08bbb4fdcf1b88c98165fcfe8474659c38d6 diff --git a/suite/generator.py b/suite/generator.py new file mode 100755 index 00000000..09ee00dc --- /dev/null +++ b/suite/generator.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python2 +import pickle +import sys +import os +import zipfile +from optparse import OptionParser +import testcommon +import time + +unit_test_template = """#!/usr/bin/env python +# This is an auto generated unit test file do not edit directly +import os +import sys +import unittest +import pickle +import zipfile +import difflib +from collections import Counter + +api_suite_path = os.path.join(os.path.dirname(__file__), {4}) +sys.path.append(api_suite_path) +import testcommon +import api_test + +global verbose +verbose = False + + +class TestBinaryNinjaAPI(unittest.TestCase): + # Returns a tuple of: + # bool : Two lists are equal + # string : The string diff + # Args: + # list + # list : (compare list one vs list two) + # string : anything additional wanted to be printed before the string diff + # bool : the ordering of the items in the two lists must be the same + def report(self, oracle, test, firstText='', strictOrdering = False): + stringDiff = "" + + equality = False + if not strictOrdering: + equality = (Counter(oracle) == Counter(test)) + else: + equality = (oracle == test) + + if equality: + return (True, '') + elif not strictOrdering: + try: + for elem in oracle: + test.remove(elem) + oracle.remove(elem) # If it's not in the test, it won't get here! + except ValueError: + pass + + differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) + skipped_lines = 0 + for delta in differ.compare(oracle, test): + if delta[0] == ' ': + skipped_lines += 1 + continue + if skipped_lines > 0: + stringDiff += "<---" + str(skipped_lines) + ' same lines--->\\n' + skipped_lines = 0 + delta = delta.replace(\'\\n\', '') + stringDiff += delta + \'\\n\' + + stringDiffList = stringDiff.split(\'\\n\') + + if len(stringDiffList) > 10: + if not verbose: + stringDiff = \'\\n\'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList[:10]) + stringDiff += \'\\n\\n### And ' + str(len(stringDiffList)) + " more lines, use '-v' to show ###" + elif not verbose: + stringDiff = \'\\n\'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList) + stringDiff = \'\\n\\n\' + firstText + stringDiff + return (equality, stringDiff) + + @classmethod + def setUpClass(self): + self.builder = testcommon.TestBuilder("{3}") + pickle_path = os.path.join(os.path.dirname(__file__), "oracle.pkl") + try: + # Python 2 does not have the encodings option + self.oracle_test_data = pickle.load(open(pickle_path, "rb"), encoding='charmap') + except TypeError: + self.oracle_test_data = pickle.load(open(pickle_path, "rb")) + self.verifybuilder = testcommon.VerifyBuilder("{3}") + + def run_binary_test(self, testfile): + testname = None + with zipfile.ZipFile(os.path.join(api_suite_path, testfile), "r") as zf: + testname = zf.namelist()[0] + zf.extractall(path=api_suite_path) + + pickle_path = os.path.join(os.path.dirname(__file__), testname + ".pkl") + self.assertTrue(pickle_path, "Test pickle doesn't exist") + try: + # Python 2 does not have the encodings option + binary_oracle = pickle.load(open(pickle_path, "rb"), encoding='charmap') + except TypeError: + binary_oracle = pickle.load(open(pickle_path, "rb")) + + test_builder = testcommon.BinaryViewTestBuilder(testname) + for method in test_builder.methods(): + test = getattr(test_builder, method)() + oracle = binary_oracle[method] + if test == oracle: + continue + + result = getattr(test_builder, method).__doc__ + result += ":\\n" + report = self.report(oracle, test, result) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle + os.unlink(os.path.join(api_suite_path, testname)) +{1}{2} + +if __name__ == "__main__": + if len(sys.argv) > 1: + if sys.argv[1] == '-v' or sys.argv[1] == '-V' or sys.argv[1] == '--verbose': + verbose = True + + test_suite = unittest.defaultTestLoader.loadTestsFromModule(api_test) + test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestBinaryNinjaAPI)) + runner = unittest.TextTestRunner(verbosity=2) + runner.run(test_suite) +""" + + +binary_test_string = """ + def test_binary__{0}(self): + self.run_binary_test('{1}') +""" + +test_string = """ + def {0}(self): + oracle = self.oracle_test_data['{0}'] + test = self.builder.{0}() + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle +""" + +verify_string = """ + def {0}(self): + self.assertTrue(self.verifybuilder.{0}(), self.{0}.__doc__) +""" + + +class OracleTestFile: + def __init__(self, filename): + self.f = open(filename + ".pkl", "wb") + self.pkl = pickle.Pickler(self.f, protocol=2) + self.filename = filename + self.oracle_test_data = {} + + def add_entry(self, builder, test_name): + self.oracle_test_data[test_name] = getattr(builder, test_name)() + + def close(self): + self.pkl.dump(self.oracle_test_data) + self.f.close() + + +class UnitTestFile: + def __init__(self, filename, outdir, test_store): + self.filename = filename + self.test_store = test_store + self.outdir = outdir + self.f = open(filename, "wb") + self.template = unit_test_template + self.tests = "" + self.binary_tests = "" + + def close(self): + api_path = os.path.relpath(os.path.dirname(__file__), start=self.outdir) + api_path = os.path.normpath(api_path) + api_path = map(lambda x: '"{0}"'.format(x), api_path.split(os.sep)) + api_path = '{0}'.format(', '.join(api_path)) + self.f.write(self.template.format(self.outdir, self.tests, self.binary_tests, self.test_store, api_path).encode('charmap')) + self.f.close() + + def add_verify(self, test_name): + self.tests += verify_string.format(test_name) + + def add_test(self, test_name): + self.tests += test_string.format(test_name) + + def add_binary_test(self, test_store, binary): + name = binary[len(test_store):].replace(os.path.sep, "_").replace(".", "_") + self.binary_tests += binary_test_string.format(name, binary + ".zip") + + +quiet = False +def myprint(stuff): + if not quiet: + print(stuff) + + +def update_progress(complete, total, description, done=False): + n = 20 + maxdesc = 50 + if total == 0: + total, complete = 10, 10 + if len(description) > maxdesc: + description = description[:maxdesc] + elif len(description) < maxdesc: + description += ' ' * (maxdesc - len(description)) + + if not quiet: + sys.stdout.write('\r[{0}{1}] {2:10.0f}% - {3}'.format('#' * int(n * (float(complete) / total)), ' ' * (n - int(n * (float(complete) / total))), 100 * float(complete) / total, description)) + if done: + sys.stdout.write("\n") + + +class TestStoreError(Exception): + def __init__(self, *args, **kwargs): + Exception.__init__(self, *args, **kwargs) + + +def generate(test_store, outdir, exclude_binaries): + if not os.path.isdir(os.path.join(os.path.dirname(__file__), test_store)): + raise TestStoreError("Specified test store is not a directory") + + unittest = UnitTestFile(os.path.join(outdir, "unit.py"), outdir, test_store) + oracle = OracleTestFile(os.path.join(outdir, "oracle")) + + # Generate the tests that don't involve binaries but do involve oracles + builder = testcommon.TestBuilder(test_store) + tests = builder.methods() + for progress, test_name in enumerate(tests): + update_progress(progress, len(tests), "Generating test data") + oracle.add_entry(builder, test_name) + unittest.add_test(test_name) + update_progress(len(tests), len(tests), "Generating test data", True) + + # Generate the tests that just verify things work as expected + verify = testcommon.VerifyBuilder(test_store) + tests = verify.methods() + for progress, test_name in enumerate(tests): + update_progress(progress, len(tests), "Generating verify data") + unittest.add_verify(test_name) + update_progress(len(tests), len(tests), "Generating verify data", True) + + # Now generate test that involve binaries + allfiles = sorted(testcommon.get_file_list(test_store)) + for progress, testfile in enumerate(allfiles): + oraclefile = None + if testfile.endswith(".pkl"): + continue + elif testfile.endswith(".DS_Store"): + continue + elif testfile.endswith(".zip"): + # We have a zipped binary unzip it so we can rebaseline + with zipfile.ZipFile(testfile, "r") as zf: + zf.extractall(path = os.path.dirname(__file__)) + if not os.path.exists(testfile[:-4]): + print("Error extracting testfile %s from zip: %s" % (testfile[:-4], testfile)) + continue + oraclefile = testfile[:-4] + else: + if os.path.exists(testfile + ".zip"): + # We've got a binary and zip for that binary just skip it + continue + # We have a binary that isn't zipped use it as a new test case + oraclefile = testfile + + oraclefile_rel = os.path.relpath(oraclefile, start=os.path.dirname(__file__)) + + # Now generate the oracle data + update_progress(progress, len(allfiles), oraclefile_rel) + unittest.add_binary_test(test_store, oraclefile_rel) + binary_start_time = time.time() + if exclude_binaries: + continue + test_data = testcommon.BinaryViewTestBuilder(oraclefile_rel) + binary_oracle = OracleTestFile(os.path.join(outdir, oraclefile_rel)) + for method in test_data.methods(): + binary_oracle.add_entry(test_data, method) + binary_oracle.close() + print("{0:.2f}".format(time.time() - binary_start_time)) + + if not os.path.exists(oraclefile + ".zip"): + with zipfile.ZipFile(oraclefile + ".zip", "w") as zf: + zf.write(oraclefile, os.path.relpath(oraclefile, start=os.path.dirname(__file__))) + + os.unlink(oraclefile) + + update_progress(len(allfiles), len(allfiles), "Generating binary unit tests complete", True) + unittest.close() + oracle.close() + + +def main(): + usage = "usage: %prog [-q] [-x] [-o <dir>] [-i <dir>]" + parser = OptionParser(usage=usage) + parser.add_option("-q", "--quiet", + dest="quiet", action="store_true", + default=False, help="Don't print anything") + parser.add_option("-x", "--exclude", + dest="exclude_binary", action="store_true", + default=False, help="Exclude regeneration of binaries") + parser.add_option("-o", "--outputdir", default="suite", + dest="outputdir", action="store", type="string", + help="output directory where the unit.py and oracle.py files will be stored (relative to cwd)") + parser.add_option("-i", "--inputdir", default=os.path.join("binaries", "test_corpus"), + dest="test_store", action="store", type="string", + help="input directory containing the binaries you which to generate unit tests from (relative to this file)") + + options, args = parser.parse_args() + + myprint("[+] INFO: Using test store: %s" % options.test_store) + if len(testcommon.get_file_list(options.test_store)) == 0: + myprint("ERROR: No files in the test store %s" % testcommon.get_file_list(options.test_store)) + sys.exit(1) + + myprint("[+] INFO: Generating test store") + try: + generate(options.test_store, options.outputdir, options.exclude_binary) + except TestStoreError as te: + myprint("[-] ERROR: Failed to generate test store: %s" % te.message) + sys.exit(1) + myprint("[+] SUCCESS: Generating test store") + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/suite/pwnadventurez.nes b/suite/pwnadventurez.nes Binary files differnew file mode 100644 index 00000000..630684e3 --- /dev/null +++ b/suite/pwnadventurez.nes diff --git a/suite/testcommon.py b/suite/testcommon.py new file mode 100644 index 00000000..285c24b5 --- /dev/null +++ b/suite/testcommon.py @@ -0,0 +1,802 @@ +import tempfile +import pickle +import os +import sys +import zipfile +import inspect +import binaryninja as binja +from binaryninja.binaryview import BinaryViewType, BinaryView +from binaryninja.filemetadata import FileMetadata +import subprocess +import re + + +# Dear people from the future: If you're adding tests or debuging an +# issue where python2 and python3 are producing different output +# for the same function and it's a issue of `longs`, run the output +# through this function. If it's a unicode/bytes issue, fix it in +# api/python/ +def fixOutput(outputList): + # Apply regular expression to detect python2 longs + splitList = [] + for elem in outputList: + if isinstance(elem, str): + splitList.append(re.split(r"((?<=[\[ ])0x[\da-f]+L|[\d]+L)", elem)) + else: + splitList.append(elem) + + # Resolve application of regular expression + result = [] + for elem in splitList: + if isinstance(elem, list): + newElem = [] + for item in elem: + if len(item) > 1 and item[-1] == 'L': + newElem.append(item[:-1]) + else: + newElem.append(item) + result.append(''.join(newElem)) + else: + result.append(elem) + return result + + +# Alright so this one is here for Binja functions that output <in set([blah, blah, blah])> +def fixSet(string): + # Apply regular expression + splitList = (re.split(r"((?<=<in set\(\[).*(?=\]\)>))", string)) + if len(splitList) > 1: + return splitList[0] + ', '.join(sorted(splitList[1].split(', '))) + splitList[2] + else: + return string + + +def get_file_list(test_store_rel): + test_store = os.path.join(os.path.dirname(__file__), test_store_rel) + all_files = [] + for root, dir, files in os.walk(test_store): + for file in files: + all_files.append(os.path.join(root, file)) + return all_files + +def remove_low_confidence(type_string): + low_confidence_types = ["int32_t", "void"] + for lct in low_confidence_types: + type_string = type_string.replace(lct + " ", '') # done to resolve confidence ties + return type_string + +class Builder(object): + def __init__(self, test_store): + self.test_store = test_store + # binja.log.log_to_stdout(binja.LogLevel.DebugLog) # Uncomment for more info + + def methods(self): + methodnames = [] + for methodname, method in inspect.getmembers(self, predicate=inspect.ismethod): + if methodname.startswith("test_"): + methodnames.append(methodname) + return methodnames + + def unpackage_file(self, filename): + path = os.path.join(os.path.dirname(__file__), self.test_store, filename) + if not os.path.exists(path): + with zipfile.ZipFile(path + ".zip", "r") as zf: + zf.extractall(path = os.path.dirname(__file__)) + assert os.path.exists(path) + return os.path.relpath(path) + + +class BinaryViewTestBuilder(Builder): + """ The BinaryViewTestBuilder is for test that are verified against a binary. + The tests are first run on your dev machine to base line then run again + on the build machine to verify they are correct. + + - Function that are tests should start with 'test_' + - Function doc string used as 'on error' message + - Should return: list of strings + """ + def __init__(self, filename): + self.filename = os.path.join(os.path.dirname(__file__), filename) + self.bv = BinaryViewType.get_view_of_file(self.filename) + if self.bv is None: + print("%s is not an executable format" % filename) + return + + def test_available_types(self): + """Available types don't match""" + return ["Available Type: " + x.name for x in BinaryView(FileMetadata()).open(self.filename).available_view_types] + + def test_function_starts(self): + """Function starts list doesnt match""" + result = [] + for x in self.bv.functions: + result.append("Function start: " + hex(x.start)) + return fixOutput(result) + + def test_function_symbol_names(self): + """Function.symbol.name list doesnt match""" + result = [] + for x in self.bv.functions: + result.append("Symbol: " + x.symbol.name + ' ' + str(x.symbol.type) + ' ' + hex(x.symbol.address)) + return fixOutput(result) + + def test_function_can_return(self): + """Function.can_return list doesnt match""" + result = [] + for x in self.bv.functions: + result.append("function name: " + x.symbol.name + ' type: ' + str(x.symbol.type) + ' address: ' + hex(x.symbol.address) + ' can_return: ' + str(bool(x.can_return))) + return fixOutput(result) + + def test_function_basic_blocks(self): + """Function basic_block list doesnt match (start, end, has_undetermined_outgoing_edges)""" + bblist = [] + for func in self.bv.functions: + for bb in func.basic_blocks: + bblist.append("basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' undetermined outgoing edges: ' + str(bb.has_undetermined_outgoing_edges)) + for anno in func.get_block_annotations(bb.start): + bblist.append("basic block {} function annotation: ".format(str(bb)) + str(anno)) + bblist.append("basic block {} test get self: ".format(str(bb)) + str(func.get_basic_block_at(bb.start))) + return fixOutput(bblist) + + def test_function_low_il_basic_blocks(self): + """Function low_il_basic_block list doesnt match""" + ilbblist = [] + for func in self.bv.functions: + for bb in func.low_level_il.basic_blocks: + ilbblist.append("LLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing edges: ' + str(len(bb.outgoing_edges))) + return fixOutput(ilbblist) + + def test_function_med_il_basic_blocks(self): + """Function med_il_basic_block list doesn't match""" + ilbblist = [] + for func in self.bv.functions: + for bb in func.medium_level_il.basic_blocks: + ilbblist.append("MLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing_edges: ' + str(len(bb.outgoing_edges))) + return fixOutput(ilbblist) + + def test_symbols(self): + """Symbols list doesn't match""" + return ["Symbol: " + str(i) for i in sorted(self.bv.symbols)] + + def test_strings(self): + """Strings list doesn't match""" + return fixOutput(["String: " + str(x.value) + ' type: ' + str(x.type) + ' at: ' + hex(x.start) for x in self.bv.strings]) + + def test_low_il_instructions(self): + """LLIL instructions produced different output""" + retinfo = [] + for func in self.bv.functions: + for bb in func.low_level_il.basic_blocks: + for ins in bb: + retinfo.append("MLIL: " + str(ins.medium_level_il)) + retinfo.append("Mapped MLIL: " + str(ins.mapped_medium_level_il)) + retinfo.append("Value: " + str(ins.value)) + retinfo.append("Possible Values: " + str(ins.possible_values)) + retinfo.append("Prefix operands: " + str(ins.prefix_operands)) + retinfo.append("Postfix operands: " + str(ins.postfix_operands)) + retinfo.append("SSA form: " + str(ins.ssa_form)) + retinfo.append("Non-SSA form: " + str(ins.non_ssa_form)) + return fixOutput(retinfo) + + def test_low_il_ssa(self): + """LLIL ssa produced different output""" + retinfo = [] + for func in self.bv.functions: + func = func.low_level_il + for reg_name in self.bv.arch.regs: + reg = binja.SSARegister(reg_name, 1) + retinfo.append("Reg {} SSA definition: ".format(reg_name) + str(func.get_ssa_reg_definition(reg))) + retinfo.append("Reg {} SSA uses: ".format(reg_name) + str(func.get_ssa_reg_uses(reg))) + retinfo.append("Reg {} SSA value: ".format(reg_name) + str(func.get_ssa_reg_value(reg))) + for flag_name in self.bv.arch.flags: + flag = binja.SSAFlag(flag_name, 1) + retinfo.append("Flag {} SSA uses: ".format(flag_name) + str(func.get_ssa_flag_uses(flag))) + retinfo.append("Flag {} SSA value: ".format(flag_name) + str(func.get_ssa_flag_value(flag))) + for bb in func.basic_blocks: + for ins in bb: + tempind = func.get_non_ssa_instruction_index(ins.instr_index) + retinfo.append("Non-SSA instruction index: " + str(tempind)) + retinfo.append("SSA instruction index: " + str(func.get_ssa_instruction_index(tempind))) + retinfo.append("MLIL instruction index: " + str(func.get_medium_level_il_instruction_index(ins.instr_index))) + retinfo.append("Mapped MLIL instruction index: " + str(func.get_mapped_medium_level_il_instruction_index(ins.instr_index))) + return fixOutput(retinfo) + + def test_med_il_instructions(self): + """MLIL instructions produced different output""" + retinfo = [] + for func in self.bv.functions: + for bb in func.medium_level_il.basic_blocks: + for ins in bb: + retinfo.append("Expression type: " + str(ins.expr_type)) + retinfo.append("LLIL: " + str(ins.low_level_il)) + retinfo.append("Value: " + str(ins.value)) + retinfo.append("Possible values: " + str(ins.possible_values)) + retinfo.append("Branch dependence: " + str(sorted(ins.branch_dependence.items()))) + + prefixList = [] + for i in ins.prefix_operands: + if isinstance(i, float) and 'e' in str(i): + prefixList.append(str(round(i, 21))) + elif isinstance(i, float): + prefixList.append(str(round(i, 11))) + else: + prefixList.append(str(i)) + retinfo.append("Prefix operands: " + str(sorted(prefixList))) + postfixList = [] + for i in ins.prefix_operands: + if isinstance(i, float) and 'e' in str(i): + postfixList.append(str(round(i, 21))) + elif isinstance(i, float): + postfixList.append(str(round(i, 11))) + else: + postfixList.append(str(i)) + + retinfo.append("Postfix operands: " + str(sorted(postfixList))) + retinfo.append("SSA form: " + str(ins.ssa_form)) + retinfo.append("Non-SSA form" + str(ins.non_ssa_form)) + return fixOutput(retinfo) + + def test_med_il_vars(self): + """Function med_il_vars doesn't match""" + varlist = [] + for func in self.bv.functions: + func = func.medium_level_il + for bb in func.basic_blocks: + for instruction in bb: + instruction = instruction.ssa_form + for var in (instruction.vars_read + instruction.vars_written): + if hasattr(var, "var"): + varlist.append("SSA var definition: " + str(func.get_ssa_var_definition(var))) + varlist.append("SSA var uses: " + str(func.get_ssa_var_uses(var))) + varlist.append("SSA var value: " + str(func.get_ssa_var_value(var))) + varlist.append("SSA var possible values: " + fixSet(str(instruction.get_ssa_var_possible_values(var)))) + varlist.append("SSA var version: " + str(instruction.get_ssa_var_version)) + return fixOutput(varlist) + + def test_function_stack(self): + """Function stack produced different output""" + funcinfo = [] + for func in self.bv.functions: + func.stack_adjustment = func.stack_adjustment + func.reg_stack_adjustments = func.reg_stack_adjustments + func.create_user_stack_var(0, binja.Type.int(4), "testuservar") + func.create_auto_stack_var(4, binja.Type.int(4), "testautovar") + + sl = func.stack_layout + for i in range(len(sl)): + funcinfo.append("Stack position {}: ".format(i) + str(sl[i])) + + funcinfo.append("Stack content sample: " + str(func.get_stack_contents_at(func.start + 0x10, 0, 0x10))) + funcinfo.append("Stack content range sample: " + str(func.get_stack_contents_after(func.start + 0x10, 0, 0x10))) + funcinfo.append("Sample stack var: " + str(func.get_stack_var_at_frame_offset(0, 0))) + func.delete_user_stack_var(0) + func.delete_auto_stack_var(0) + return funcinfo + + def test_function_llil(self): + """Function LLIL produced different output""" + retinfo = [] + for func in self.bv.functions: + for llilbb in func.llil_basic_blocks: + retinfo.append("LLIL basic block: " + str(llilbb)) + for llilins in func.llil_instructions: + retinfo.append("LLIL instruction: " + str(llilins)) + for mlilbb in func.mlil_basic_blocks: + retinfo.append("MLIL basic block: " + str(mlilbb)) + for mlilins in func.mlil_instructions: + retinfo.append("MLIL instruction: " + str(mlilins)) + for ins in func.instructions: + retinfo.append("Instruction: {}: ".format(hex(ins[1])) + ''.join([str(i) for i in ins[0]])) + return fixOutput(retinfo) + + def test_functions_attributes(self): + """Function attributes don't match""" + funcinfo = [] + for func in self.bv.functions: + func.comment = "testcomment " + func.name + func.name = func.name + func.can_return = func.can_return + func.function_type = func.function_type + func.return_type = func.return_type + func.return_regs = func.return_regs + func.calling_convention = func.calling_convention + func.parameter_vars = func.parameter_vars + func.has_variable_arguments = func.has_variable_arguments + func.analysis_skipped = func.analysis_skipped + func.clobbered_regs = func.clobbered_regs + func.set_user_instr_highlight(func.start, binja.highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + func.set_auto_instr_highlight(func.start, binja.highlight.HighlightColor(red=0xff, blue=0xfe, green=0)) + + for var in func.vars: + funcinfo.append("Function {} var: ".format(func.name) + str(var)) + + for branch in func.indirect_branches: + funcinfo.append("Function {} indirect branch: ".format(func.name) + str(branch)) + funcinfo.append("Function {} session data: ".format(func.name) + str(func.session_data)) + funcinfo.append("Function {} analysis perf length: ".format(func.name) + str(len(func.analysis_performance_info))) + for cr in func.clobbered_regs: + funcinfo.append("Function {} clobbered reg: ".format(func.name) + str(cr)) + funcinfo.append("Function {} explicitly defined type: ".format(func.name) + str(func.explicitly_defined_type)) + funcinfo.append("Function {} needs update: ".format(func.name) + str(func.needs_update)) + funcinfo.append("Function {} global pointer value: ".format(func.name) + str(func.global_pointer_value)) + funcinfo.append("Function {} comment: ".format(func.name) + str(func.comment)) + funcinfo.append("Function {} too large: ".format(func.name) + str(func.too_large)) + funcinfo.append("Function {} analysis skipped: ".format(func.name) + str(func.analysis_skipped)) + funcinfo.append("Function {} first ins LLIL: ".format(func.name) + str(func.get_low_level_il_at(func.start))) + funcinfo.append("Function {} LLIL exit test: ".format(func.name) + str(func.get_low_level_il_exits_at(func.start+0x100))) + funcinfo.append("Function {} regs read test: ".format(func.name) + str(func.get_regs_read_by(func.start))) + funcinfo.append("Function {} regs written test: ".format(func.name) + str(func.get_regs_written_by(func.start))) + funcinfo.append("Function {} stack var test: ".format(func.name) + str(func.get_stack_vars_referenced_by(func.start))) + funcinfo.append("Function {} constant reference test: ".format(func.name) + str(func.get_constants_referenced_by(func.start))) + funcinfo.append("Function {} first ins lifted IL: ".format(func.name) + str(func.get_lifted_il_at(func.start))) + funcinfo.append("Function {} flags read by lifted IL ins: ".format(func.name) + str(func.get_flags_read_by_lifted_il_instruction(0))) + funcinfo.append("Function {} flags written by lifted IL ins: ".format(func.name) + str(func.get_flags_written_by_lifted_il_instruction(0))) + funcinfo.append("Function {} create graph: ".format(func.name) + str(func.create_graph())) + funcinfo.append("Function {} indirect branches test: ".format(func.name) + str(func.get_indirect_branches_at(func.start+0x10))) + funcinfo.append("Function {} test instr highlight: ".format(func.name) + str(func.get_instr_highlight(func.start))) + for token in func.get_type_tokens(): + token = str(token) + token = remove_low_confidence(token) + funcinfo.append("Function {} type token: ".format(func.name) + str(token)) + return fixOutput(funcinfo) + + def test_BinaryView(self): + """BinaryView produced different results""" + retinfo = [] + + for type in self.bv.types.items(): + retinfo.append("BV Type: " + str(type)) + for segment in sorted([str(i) for i in self.bv.segments]): + retinfo.append("BV segment: " + str(segment)) + for section in sorted(self.bv.sections): + retinfo.append("BV section: " + str(section)) + for allrange in self.bv.allocated_ranges: + retinfo.append("BV allocated range: " + str(allrange)) + retinfo.append("Session Data: " + str(self.bv.session_data)) + for var in self.bv.data_vars: + retinfo.append("BV data var: " + str(var)) + retinfo.append("BV Entry function: " + str(self.bv.entry_function)) + for i in self.bv: + retinfo.append("BV function: " + str(i)) + retinfo.append("BV entry point: " + hex(self.bv.entry_point)) + retinfo.append("BV start: " + hex(self.bv.start)) + retinfo.append("BV length: " + hex(len(self.bv))) + + return fixOutput(retinfo) + + +class TestBuilder(Builder): + """ The TestBuilder is for tests that need to be checked againsttest_BinaryView + stored oracle data that isn't from a binary. These test are + generated on your local machine then run again on the build + machine to verify correctness. + + - Function that are tests should start with 'test_' + - Function doc string used as 'on error' message + - Should return: list of strings + """ + + def test_BinaryViewType_list(self): + """BinaryViewType list doesnt match""" + return ["BinaryViewType: " + x.name for x in binja.BinaryViewType.list] + + def test_Architecture_list(self): + """Architecture list doesnt match""" + return ["Arch name: " + x.name for x in binja.Architecture.list] + + def test_Assemble(self): + """unexpected assemble result""" + result = [] + # success cases + + strResult = binja.Architecture["x86"].assemble("xor eax, eax") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("x86 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("x86 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["x86_64"].assemble("xor rax, rax") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("x86_64 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("x86_64 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["mips32"].assemble("move $ra, $zero") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("mips32 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("mips32 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["mipsel32"].assemble("move $ra, $zero") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("mipsel32 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("mipsel32 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["armv7"].assemble("str r2, [sp, #-0x4]!") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("armv7 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("armv7 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["aarch64"].assemble("mov x0, x0") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("aarch64 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("aarch64 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["thumb2"].assemble("ldr r4, [r4]") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("thumb2 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("thumb2 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["thumb2eb"].assemble("ldr r4, [r4]") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("thumb2eb assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("thumb2eb assembly: " + repr(str(strResult))) + + # fail cases + try: + strResult = binja.Architecture["x86"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86'") + try: + strResult = binja.Architecture["x86_64"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86_64'") + try: + strResult = binja.Architecture["mips32"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mips32'") + try: + strResult = binja.Architecture["mipsel32"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mipsel32'") + try: + strResult = binja.Architecture["armv7"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'armv7'") + try: + strResult = binja.Architecture["aarch64"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'aarch64'") + try: + strResult = binja.Architecture["thumb2"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2'") + try: + strResult = binja.Architecture["thumb2eb"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2eb'") + return result + + def test_Architecture(self): + """Architecture failure""" + if not os.path.exists(os.path.join(os.path.expanduser("~"), '.binaryninja', 'plugins', 'nes.py')): + return [""] + + retinfo = [] + file_name = os.path.join(self.test_store, "..", "pwnadventurez.nes") + bv = binja.BinaryViewType["NES Bank 0"].open(file_name) + + for i in bv.platform.arch.calling_conventions: + retinfo.append("Custom arch calling convention: " + str(i)) + for i in bv.platform.arch.full_width_regs: + retinfo.append("Custom arch full width reg: " + str(i)) + + reg = binja.RegisterValue() + retinfo.append("Reg entry value: " + str(reg.entry_value(bv.platform.arch, 'x'))) + retinfo.append("Reg constant: " + str(reg.constant(0xfe))) + retinfo.append("Reg constant pointer: " + str(reg.constant_ptr(0xcafebabe))) + retinfo.append("Reg stack frame offset: " + str(reg.stack_frame_offset(0x10))) + retinfo.append("Reg imported address: " + str(reg.imported_address(0xdeadbeef))) + retinfo.append("Reg return address: " + str(reg.return_address())) + + bv.update_analysis_and_wait() + for func in bv.functions: + for bb in func.low_level_il.basic_blocks: + for ins in bb: + retinfo.append("Instruction info: " + str(bv.platform.arch.get_instruction_info(0x10, ins.address))) + retinfo.append("Instruction test: " + str(bv.platform.arch.get_instruction_text(0x10, ins.address))) + retinfo.append("Instruction: " + str(ins)) + return retinfo + + def test_Function(self): + """Function produced different result""" + inttype = binja.Type.int(4) + testfunction = binja.Type.function(inttype, [inttype, inttype, inttype]) + return ["Test_function params: " + str(testfunction.parameters), "Test_function pointer: " + str(testfunction.pointer(binja.Architecture["x86"], testfunction))] + + def test_Struct(self): + """Struct produced different result""" + retinfo = [] + inttype = binja.Type.int(4) + struct = binja.Structure() + struct.a = 1 + struct.insert(0, inttype) + struct.append(inttype) + struct.replace(0, inttype) + struct.remove(1) + for i in struct.members: + retinfo.append("Struct member: " + str(i)) + retinfo.append("Struct width: " + str(struct.width)) + struct.width = 16 + retinfo.append("Struct width after adjustment: " + str(struct.width)) + retinfo.append("Struct alignment: " + str(struct.alignment)) + struct.alignment = 8 + retinfo.append("Struct alignment after adjustment: " + str(struct.alignment)) + retinfo.append("Struct packed: " + str(struct.packed)) + struct.packed = 1 + retinfo.append("Struct packed after adjustment: " + str(struct.packed)) + retinfo.append("Struct type: " + str(struct.type)) + return retinfo + + def test_Enumeration(self): + """Enumeration produced different result""" + retinfo = [] + inttype = binja.Type.int(4) + enum = binja.Enumeration() + enum.a = 1 + enum.append("a", 1) + enum.append("b", 2) + enum.replace(0, "a", 2) + enum.remove(0) + retinfo.append(str(enum)) + retinfo.append(str((enum == enum) and not (enum != enum))) + return retinfo + + def test_Types(self): + """Types produced different result""" + file_name = self.unpackage_file("helloworld") + bv = binja.BinaryViewType.get_view_of_file(file_name) + + preprocessed = binja.preprocess_source(""" + #ifdef nonexistant + int foo = 1; + long long foo1 = 1; + #else + int bar = 2; + long long bar1 = 2; + #endif + """) + source = '\n'.join([i.decode('charmap') for i in preprocessed[0].split(b'\n') if not b'#line' in i and len(i) > 0]) + typelist = bv.platform.parse_types_from_source(source) + inttype = binja.Type.int(4) + + tokens = inttype.get_tokens() + inttype.get_tokens_before_name() + inttype.get_tokens_after_name() + namedtype = binja.NamedTypeReference() + + retinfo = [] + for i in range(len(typelist.variables)): + for j in typelist.variables.popitem(): + retinfo.append("Type: " + str(j)) + retinfo.append("Named Type: " + str(namedtype)) + + retinfo.append("Type equality: " + str((inttype == inttype) and not (inttype != inttype))) + return retinfo + + def test_Plugin_bin_info(self): + """print_syscalls plugin produced different result""" + file_name = self.unpackage_file("helloworld") + bin_info_path = os.path.join(os.path.dirname(__file__), '..', 'python', 'examples', 'bin_info.py') + result = subprocess.Popen(["python", bin_info_path, file_name], stdout=subprocess.PIPE).communicate()[0] + # normalize line endings and path sep + return [line for line in result.replace(b"\\", b"/").replace(b"\r\n", b"\n").decode("charmap").split("\n")] + + def test_linear_disassembly(self): + """linear_disassembly produced different result""" + file_name = self.unpackage_file("helloworld") + bv = binja.BinaryViewType['ELF'].open(file_name) + disass = bv.linear_disassembly + retinfo = [] + for i in disass: + i = str(i) + i = remove_low_confidence(i) + retinfo.append(i) + return retinfo + + def test_partial_register_dataflow(self): + """partial_register_dataflow produced different results""" + file_name = self.unpackage_file("partial_register_dataflow") + result = [] + reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si'] + bv = binja.BinaryViewType.get_view_of_file(file_name) + for func in bv.functions: + llil = func.low_level_il + for i in range(0, llil.__len__()-1): + for x in reg_list: + result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_reg_value(x)).replace('L', '')) + result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_possible_reg_values(x)).replace('L', '')) + result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_reg_value_after(x)).replace('L', '')) + result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_possible_reg_values_after(x)).replace('L', '')) + bv.file.close() + del bv + return result + + + def test_low_il_stack(self): + """LLIL stack produced different output""" + file_name = self.unpackage_file("jumptable_reordered") + bv = binja.BinaryViewType.get_view_of_file(file_name) + reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si'] + flag_list = ['c', 'p', 'a', 'z', 's', 'o'] + retinfo = [] + for func in bv.functions: + for bb in func.low_level_il.basic_blocks: + for ins in bb: + retinfo.append("LLIL first stack element: " + str(ins.get_stack_contents(0,1))) + retinfo.append("LLIL second stack element: " + str(ins.get_stack_contents_after(0,1))) + retinfo.append("LLIL possible first stack element: " + str(ins.get_possible_stack_contents(0,1))) + retinfo.append("LLIL possible second stack element: " + str(ins.get_possible_stack_contents_after(0,1))) + for flag in flag_list: + retinfo.append("LLIL flag {} value at: ".format(flag, hex(ins.address)) + str(ins.get_flag_value(flag))) + retinfo.append("LLIL flag {} value after {}: ".format(flag, hex(ins.address)) + str(ins.get_flag_value_after(flag))) + retinfo.append("LLIL flag {} possible value at {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values(flag))) + retinfo.append("LLIL flag {} possible value after {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values_after(flag))) + return fixOutput(retinfo) + + def test_med_il_stack(self): + """MLIL stack produced different output""" + file_name = self.unpackage_file("jumptable_reordered") + bv = binja.BinaryViewType.get_view_of_file(file_name) + reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si'] + flag_list = ['c', 'p', 'a', 'z', 's', 'o'] + retinfo = [] + for func in bv.functions: + for bb in func.medium_level_il.basic_blocks: + for ins in bb: + retinfo.append("MLIL stack begin var: " + str(ins.get_var_for_stack_location(0))) + retinfo.append("MLIL first stack element: " + str(ins.get_stack_contents(0, 1))) + retinfo.append("MLIL second stack element: " + str(ins.get_stack_contents_after(0, 1))) + retinfo.append("MLIL possible first stack element: " + str(ins.get_possible_stack_contents(0, 1))) + retinfo.append("MLIL possible second stack element: " + str(ins.get_possible_stack_contents_after(0, 1))) + + for reg in reg_list: + retinfo.append("MLIL reg {} var at {}: ".format(reg, hex(ins.address)) + str(ins.get_var_for_reg(reg))) + retinfo.append("MLIL reg {} value at {}: ".format(reg, hex(ins.address)) + str(ins.get_reg_value(reg))) + retinfo.append("MLIL reg {} value after {}: ".format(reg, hex(ins.address)) + str(ins.get_reg_value_after(reg))) + retinfo.append("MLIL reg {} possible value at {}: ".format(reg, hex(ins.address)) + fixSet(str(ins.get_possible_reg_values(reg)))) + retinfo.append("MLIL reg {} possible value after {}: ".format(reg, hex(ins.address)) + fixSet(str(ins.get_possible_reg_values_after(reg)))) + + for flag in flag_list: + retinfo.append("MLIL flag {} value at: ".format(flag, hex(ins.address)) + str(ins.get_flag_value(flag))) + retinfo.append("MLIL flag {} value after {}: ".format(flag, hex(ins.address)) + str(ins.get_flag_value_after(flag))) + retinfo.append("MLIL flag {} possible value at {}: ".format(flag, hex(ins.address)) + fixSet(str(ins.get_possible_flag_values(flag)))) + retinfo.append("MLIL flag {} possible value after {}: ".format(flag, hex(ins.address)) + fixSet(str(ins.get_possible_flag_values(flag)))) + return fixOutput(retinfo) + + def test_events(self): + """Event failure""" + file_name = self.unpackage_file("helloworld") + bv = binja.BinaryViewType['ELF'].open(file_name) + bv.update_analysis_and_wait() + + results = [] + + def simple_complete(self): + results.append("analysis complete") + evt = binja.AnalysisCompletionEvent(bv, simple_complete) + + class NotifyTest(binja.BinaryDataNotification): + def data_written(self, view, offset, length): + results.append("data written: offset {0} length {1}".format(hex(offset), hex(length))) + + def data_inserted(self, view, offset, length): + results.append("data inserted: offset {0} length {1}".format(hex(offset), hex(length))) + + def data_removed(self, view, offset, length): + results.append("data removed: offset {0} length {1}".format(hex(offset), hex(length))) + + def function_added(self, view, func): + results.append("function added: {0}".format(func.name)) + + def function_removed(self, view, func): + results.append("function removed: {0}".format(func.name)) + + def data_var_added(self, view, var): + results.append("data var added: {0}".format(hex(var.address))) + + def data_var_removed(self, view, var): + results.append("data var removed: {0}".format(hex(var.address))) + + def string_found(self, view, string_type, offset, length): + results.append("string found: offset {0} length {1}".format(hex(offset), hex(length))) + + def string_removed(self, view, string_type, offset, length): + results.append("string removed: offset {0} length {1}".format(hex(offset), hex(length))) + + def type_defined(self, view, name, type): + results.append("type defined: {0}".format(name)) + + def type_undefined(self, view, name, type): + results.append("type undefined: {0}".format(name)) + + test = NotifyTest() + bv.register_notification(test) + sacrificial_addr = 0x84fc + + 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.undefine_type(type_id) + + bv.update_analysis_and_wait() + + bv.insert(sacrificial_addr, b"AAAA") + bv.update_analysis_and_wait() + + bv.define_data_var(sacrificial_addr, binja.types.Type.int(4)) + bv.update_analysis_and_wait() + + bv.write(sacrificial_addr, b"BBBB") + bv.update_analysis_and_wait() + + bv.add_function(sacrificial_addr) + bv.update_analysis_and_wait() + + bv.remove_function(bv.get_function_at(sacrificial_addr)) + bv.update_analysis_and_wait() + + bv.undefine_data_var(sacrificial_addr) + bv.update_analysis_and_wait() + + bv.remove(sacrificial_addr, 4) + bv.update_analysis_and_wait() + + bv.unregister_notification(test) + + return fixOutput(sorted(results)) + + +class VerifyBuilder(Builder): + """ The VerifyBuilder is for tests that verify + Binary Ninja against expected output. + + - Function that are tests should start with 'test_' + - Function doc string used as 'on error' message + - Should return: boolean + """ + + def __init__(self, test_store): + super(VerifyBuilder, self).__init__(test_store) + + def get_functions(self, bv): + return [x.start for x in bv.functions] + + def get_comments(self, bv): + return bv.functions[0].comments + + def test_verify_BNDB_round_trip(self): + """Binary Ninja Database output doesn't match its input""" + # This will test Binja's ability to save and restore databases + # By: + # - Creating a binary view + # - Make modification that impact the database + # - Record those modification + # - Save the database + # - Restore the datbase + # - Validate that the modifications are present + file_name = self.unpackage_file("helloworld") + try: + bv = binja.BinaryViewType['ELF'].open(file_name) + bv.update_analysis_and_wait() + # Make some modifications to the binary view + + # Add a comment + bv.functions[0].set_comment(bv.functions[0].start, "Function start") + # Add a new function + bv.add_function(bv.functions[0].start + 4) + temp_name = next(tempfile._get_candidate_names()) + ".bndb" + + comments = self.get_comments(bv) + functions = self.get_functions(bv) + bv.create_database(temp_name) + bv.file.close() + del bv + + bv = binja.FileMetadata(temp_name).open_existing_database(temp_name).get_view_of_type('ELF') + bv.update_analysis_and_wait() + bndb_functions = self.get_functions(bv) + bndb_comments = self.get_comments(bv) + # force windows to close the handle to the bndb that we want to delete + bv.file.close() + del bv + return [str(functions == bndb_functions and comments == bndb_comments)] + finally: + os.unlink(temp_name) diff --git a/themes/dark-blue.theme b/themes/dark-blue.theme new file mode 100644 index 00000000..b0f83598 --- /dev/null +++ b/themes/dark-blue.theme @@ -0,0 +1,108 @@ +{ + "name": "Dark Blue", + "style": "Fusion", + + "//": " Colors are specified as [Red, Green, Blue] values. Colors can be aliased in the 'colors' ", + "//": " section. Colors can also be averaged and mixed together using an RPN like notation: ", + "//": " '+' indicates 'average' - average the next two colors.", + "//": " '~' indicates 'mix' - mix the next two colors with the weight 0->255", + "//": " e.g.", + "//": " ['+', 'red', 'blue'] - average red and blue", + "//": " ['~', 'red', 'blue', 20] - mix a small amount of blue with red", + "//": " ['~', 'red', 'blue', 200] - mix a large amount of blue with red", + "//": " ['+', '~', 'red', 'blue', 20, 'green'] - mix red and blue with weight 20 then average with green", + "//": " The 'palette' section is to specify colors for the QPalette::ColorRole", + + "colors": { + "background": [0, 0, 42], + "backgroundDark": [0, 0, 30], + "backgroundWindow": [0, 0, 80], + "backgroundHighlight": [0, 0, 128], + "content": [224, 224, 224], + "disabled": [144, 144, 144], + "selection": [96, 96, 96], + "selectionLight": [128, 128, 128], + "green": [0, 255, 0], + "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] + }, + + "palette": { + "Window": "backgroundWindow", + "WindowText": "content", + "Base": "backgroundDark", + "AlternateBase": "background", + "ToolTipBase": "backgroundHighlight", + "ToolTipText": "content", + "Text": "content", + "Button": "backgroundHighlight", + "ButtonText": "content", + "BrightText": "yellow", + "Link": "blue", + "Highlight": "blue", + "HighlightedText": "backgroundDark", + "Light": ["+", "backgroundDark", "content"] + }, + + "theme-colors": { + "addressColor": "green", + "modifiedColor": "red", + "insertedColor": "blue", + "notPresentColor": "disabled", + "selectionColor": "selection", + "outlineColor": "content", + "backgroundHighlightDarkColor": "backgroundDark", + "backgroundHighlightLightColor": "backgroundWindow", + "boldBackgroundHighlightDarkColor": "backgroundDark", + "boldBackgroundHighlightLightColor": "selection", + "alphanumericHighlightColor": "blue", + "printableHighlightColor": "lightCyan", + "graphBackgroundDarkColor": "background", + "graphBackgroundLightColor": "background", + "graphNodeDarkColor": "backgroundHighlight", + "graphNodeLightColor": "backgroundHighlight", + "graphNodeOutlineColor": "disabled", + "trueBranchColor": "green", + "falseBranchColor": "red", + "unconditionalBranchColor": "blue", + "altTrueBranchColor": "blue", + "altFalseBranchColor": "orange", + "altUnconditionalBranchColor": "content", + "registerColor": "yellow", + "numberColor": "green", + "codeSymbolColor": "blue", + "dataSymbolColor": "cyan", + "stackVariableColor": ["+", "green", "content"], + "importColor": "orange", + "instructionHighlightColor": "selectionLight", + "tokenHighlightColor": "red", + "annotationColor": "magenta", + "opcodeColor": "disabled", + "linearDisassemblyFunctionHeaderColor": "backgroundHighlight", + "linearDisassemblyBlockColor": "background", + "linearDisassemblyNoteColor": ["~", "+", "backgroundDark", "background", "green", 48], + "linearDisassemblySeparatorColor": "disabled", + "stringColor": "magenta", + "typeNameColor": "orange", + "fieldNameColor": "lightCyan", + "keywordColor": "yellow", + "uncertainColor": "disabled", + "scriptConsoleOutputColor": "content", + "scriptConsoleErrorColor": "red", + "scriptConsoleEchoColor": "disabled", + "blueStandardHighlightColor": "blue", + "greenStandardHighlightColor": "green", + "cyanStandardHighlightColor": "cyan", + "redStandardHighlightColor": "red", + "magentaStandardHighlightColor": "magenta", + "yellowStandardHighlightColor": "yellow", + "orangeStandardHighlightColor": "orange", + "whiteStandardHighlightColor": "content", + "blackStandardHighlightColor": [0,0,0] + } +}
\ No newline at end of file diff --git a/themes/high-contrast.theme b/themes/high-contrast.theme new file mode 100644 index 00000000..ecaf7c7c --- /dev/null +++ b/themes/high-contrast.theme @@ -0,0 +1,107 @@ +{ + "name": "High Contrast 2", + "style": "Fusion", + + "//": "Colors are specified as [Red, Green, Blue] values. Colors can be aliased in the", + "//": "'colors' section. Colors can also be averaged and mixed together using an RPN like notation.", + "//": "'+' indicates 'average' directs the parser to average the next two colors.", + "//": "'~' indicates 'mix' directs the parser to mix the next two colors with the specified weight 0->255", + "//": "e.g.", + "//": " ['+', 'red', 'blue'] - average red and blue", + "//": " ['~', 'red', 'blue', 20] - mix a small amount of blue into red", + "//": " ['+', '~', 'red', 'blue', 20, 'green'] - mix red and blue with weight 20 then average with green", + "//": "The 'palette' section is to specify colors for the QPalette::ColorRole", + + "colors": { + "background": [220, 220, 220], + "backgroundDark": [255, 255, 255], + "backgroundWindow": [240, 240, 240], + "backgroundHighlight": [255, 255, 255], + "content": [31, 31, 31], + "disabled": [122, 122, 122], + "selection": [159, 159, 159], + "selectionLight": [128, 128, 128], + "green": [48, 130, 13], + "red": [191, 38, 36], + "blue": [0, 164, 199], + "cyan": [39, 140, 173], + "lightCyan": [53, 218, 224], + "orange": [224, 124, 53], + "yellow": [141, 141, 45], + "magenta": [32, 54, 53] + }, + + "palette": { + "Window": "backgroundWindow", + "WindowText": "content", + "Base": "backgroundDark", + "AlternateBase": "background", + "ToolTipBase": "backgroundHighlight", + "ToolTipText": "content", + "Text": "content", + "Button": "backgroundHighlight", + "ButtonText": "content", + "BrightText": "magenta", + "Link": "blue", + "Highlight": "blue", + "HighlightedText": "backgroundDark", + "Light": ["+", "backgroundDark", "content"] + }, + + "theme-colors": { + "addressColor": "green", + "modifiedColor": "red", + "insertedColor": "blue", + "notPresentColor": "disabled", + "selectionColor": "selection", + "outlineColor": "content", + "backgroundHighlightDarkColor": "backgroundDark", + "backgroundHighlightLightColor": "backgroundWindow", + "boldBackgroundHighlightDarkColor": "backgroundDark", + "boldBackgroundHighlightLightColor": "selection", + "alphanumericHighlightColor": "blue", + "printableHighlightColor": "lightCyan", + "graphBackgroundDarkColor": "background", + "graphBackgroundLightColor": "background", + "graphNodeDarkColor": "backgroundHighlight", + "graphNodeLightColor": "backgroundHighlight", + "graphNodeOutlineColor": "disabled", + "trueBranchColor": "green", + "falseBranchColor": "red", + "unconditionalBranchColor": "blue", + "altTrueBranchColor": "blue", + "altFalseBranchColor": "orange", + "altUnconditionalBranchColor": "content", + "registerColor": "magenta", + "numberColor": "green", + "codeSymbolColor": "blue", + "dataSymbolColor": "cyan", + "stackVariableColor": ["+", "green", "content"], + "importColor": "orange", + "instructionHighlightColor": "selectionLight", + "tokenHighlightColor": "red", + "annotationColor": "red", + "opcodeColor": "disabled", + "linearDisassemblyFunctionHeaderColor": "backgroundHighlight", + "linearDisassemblyBlockColor": "background", + "linearDisassemblyNoteColor": ["~", "+", "backgroundDark", "background", "green", 48], + "linearDisassemblySeparatorColor": "disabled", + "stringColor": "magenta", + "typeNameColor": "orange", + "fieldNameColor": "lightCyan", + "keywordColor": "yellow", + "uncertainColor": "disabled", + "scriptConsoleOutputColor": "content", + "scriptConsoleErrorColor": "red", + "scriptConsoleEchoColor": "disabled", + "blueStandardHighlightColor": "blue", + "greenStandardHighlightColor": "green", + "cyanStandardHighlightColor": "cyan", + "redStandardHighlightColor": "red", + "magentaStandardHighlightColor": "magenta", + "yellowStandardHighlightColor": "yellow", + "orangeStandardHighlightColor": "orange", + "whiteStandardHighlightColor": "content", + "blackStandardHighlightColor": [0, 0, 0] + } +}
\ No newline at end of file diff --git a/themes/solarized-dark.theme b/themes/solarized-dark.theme new file mode 100644 index 00000000..5d1860f3 --- /dev/null +++ b/themes/solarized-dark.theme @@ -0,0 +1,104 @@ +{ + "name": "Solarized Dark 2", + "style": "Fusion", + + "//": "Colors are specified as [Red, Green, Blue] values. Colors can be aliased in the", + "//": "'colors' section. Colors can also be averaged and mixed together using an RPN like notation.", + "//": "'+' indicates 'average' directs the parser to average the next two colors.", + "//": "'~' indicates 'mix' directs the parser to mix the next two colors with the specified weight 0->255", + "//": "e.g.", + "//": " ['+', 'red', 'blue'] - average red and blue", + "//": " ['~', 'red', 'blue', 20] - mix a small amount of blue into red", + "//": " ['+', '~', 'red', 'blue', 20, 'green'] - mix red and blue with weight 20 then average with green", + "//": "The 'palette' section is to specify colors for the QPalette::ColorRole", + + "colors": { + "background": [0, 43, 54], + "backgroundHighlight": [7, 54, 66], + "content": [131, 148, 150], + "secondary": [88, 110, 117], + "emphasis": [147, 161, 161], + "yellow": [181, 137, 0], + "orange": [203, 75, 22], + "red": [220, 50, 47], + "magenta": [211, 54, 130], + "violet": [108, 113, 196], + "blue": [38, 139, 210], + "cyan": [42, 161, 152], + "green": [133, 153, 0] + }, + + "palette": { + "Window": "background", + "WindowText": "content", + "Base": "background", + "AlternateBase": "backgroundHighlight", + "ToolTipBase": "backgroundHighlight", + "ToolTipText": "content", + "Text": "content", + "Button": "backgroundHighlight", + "ButtonText": "content", + "BrightText": "emphasis", + "Link": "emphasis", + "Highlight": "backgroundHighlight", + "HighlightedText": "content", + "Light": ["+", "background", "content"] + }, + + "theme-colors": { + "addressColor": "secondary", + "modifiedColor": "red", + "insertedColor": "blue", + "notPresentColor": "secondary", + "selectionColor": ["+", "backgroundHighlight", "secondary"], + "outlineColor": "content", + "backgroundHighlightDarkColor": "background", + "backgroundHighlightLightColor": "backgroundHighlight", + "boldBackgroundHighlightDarkColor": "background", + "boldBackgroundHighlightLightColor": ["+", "background", "secondary"], + "alphanumericHighlightColor": "yellow", + "printableHighlightColor": "orange", + "graphBackgroundDarkColor": "background", + "graphBackgroundLightColor": "background", + "graphNodeDarkColor": "backgroundHighlight", + "graphNodeLightColor": "backgroundHighlight", + "graphNodeOutlineColor": "content", + "trueBranchColor": "green", + "falseBranchColor": "red", + "unconditionalBranchColor": "content", + "altTrueBranchColor": "blue", + "altFalseBranchColor": "yellow", + "altUnconditionalBranchColor": "content", + "registerColor": "green", + "numberColor": "cyan", + "codeSymbolColor": "blue", + "dataSymbolColor": "cyan", + "stackVariableColor": "violet", + "importColor": "orange", + "instructionHighlightColor": "secondary", + "tokenHighlightColor": ["+", "secondary", "orange"], + "annotationColor": "magenta", + "opcodeColor": "secondary", + "linearDisassemblyFunctionHeaderColor": "backgroundHighlight", + "linearDisassemblyBlockColor": ["+", "background", "backgroundHighlight"], + "linearDisassemblyNoteColor": ["~", "background", "green", 24], + "linearDisassemblySeparatorColor": [0, 0, 0], + "stringColor": "magenta", + "typeNameColor": "orange", + "fieldNameColor": "cyan", + "keywordColor": "green", + "uncertainColor": "secondary", + "scriptConsoleOutputColor": "content", + "scriptConsoleErrorColor": "red", + "scriptConsoleEchoColor": "secondary", + "blueStandardHighlightColor": "blue", + "greenStandardHighlightColor": "green", + "cyanStandardHighlightColor": "cyan", + "redStandardHighlightColor": "red", + "magentaStandardHighlightColor": "magenta", + "yellowStandardHighlightColor": "yellow", + "orangeStandardHighlightColor": "orange", + "whiteStandardHighlightColor": ["+", "background", [255, 255, 255]], + "blackStandardHighlightColor": "content" + } +}
\ No newline at end of file diff --git a/themes/solarized-light.theme b/themes/solarized-light.theme new file mode 100644 index 00000000..2799892b --- /dev/null +++ b/themes/solarized-light.theme @@ -0,0 +1,104 @@ +{ + "name": "Solarized Light 2", + "style": "Fusion", + + "//": "Colors are specified as [Red, Green, Blue] values. Colors can be aliased in the", + "//": "'colors' section. Colors can also be averaged and mixed together using an RPN like notation.", + "//": "'+' indicates 'average' directs the parser to average the next two colors.", + "//": "'~' indicates 'mix' directs the parser to mix the next two colors with the specified weight 0->255", + "//": "e.g.", + "//": " ['+', 'red', 'blue'] - average red and blue", + "//": " ['~', 'red', 'blue', 20] - mix a small amount of blue into red", + "//": " ['+', '~', 'red', 'blue', 20, 'green'] - mix red and blue with weight 20 then average with green", + "//": "The 'palette' section is to specify colors for the QPalette::ColorRole", + + "colors": { + "background": [253, 246, 227], + "backgroundHighlight": [238, 232, 213], + "content": [101, 123, 131], + "secondary": [147, 161, 161], + "emphasis": [88, 110, 117], + "yellow": [181, 137, 0], + "orange": [203, 75, 22], + "red": [220, 50, 47], + "magenta": [211, 54, 130], + "violet": [108, 113, 196], + "blue": [38, 139, 210], + "cyan": [42, 161, 152], + "green": [133, 153, 0] + }, + + "palette": { + "Window": "background", + "WindowText": "content", + "Base": "background", + "AlternateBase": "backgroundHighlight", + "ToolTipBase": "backgroundHighlight", + "ToolTipText": "content", + "Text": "content", + "Button": "backgroundHighlight", + "ButtonText": "content", + "BrightText": "emphasis", + "Link": "emphasis", + "Highlight": "backgroundHighlight", + "HighlightedText": "content", + "Light": ["+", "background", "content"] + }, + + "theme-colors": { + "addressColor": "secondary", + "modifiedColor": "red", + "insertedColor": "blue", + "notPresentColor": "secondary", + "selectionColor": ["+", "backgroundHighlight", "secondary"], + "outlineColor": "content", + "backgroundHighlightDarkColor": "background", + "backgroundHighlightLightColor": "backgroundHighlight", + "boldBackgroundHighlightDarkColor": "background", + "boldBackgroundHighlightLightColor": ["+", "background", "secondary"], + "alphanumericHighlightColor": "yellow", + "printableHighlightColor": "orange", + "graphBackgroundDarkColor": "background", + "graphBackgroundLightColor": "background", + "graphNodeDarkColor": "backgroundHighlight", + "graphNodeLightColor": "backgroundHighlight", + "graphNodeOutlineColor": "content", + "trueBranchColor": "green", + "falseBranchColor": "red", + "unconditionalBranchColor": "content", + "altTrueBranchColor": "blue", + "altFalseBranchColor": "yellow", + "altUnconditionalBranchColor": "content", + "registerColor": "green", + "numberColor": "cyan", + "codeSymbolColor": "blue", + "dataSymbolColor": "cyan", + "stackVariableColor": "violet", + "importColor": "orange", + "instructionHighlightColor": "secondary", + "tokenHighlightColor": ["+", "secondary", "orange"], + "annotationColor": "magenta", + "opcodeColor": "secondary", + "linearDisassemblyFunctionHeaderColor": "backgroundHighlight", + "linearDisassemblyBlockColor": ["+", "background", "backgroundHighlight"], + "linearDisassemblyNoteColor": ["~", "background", "green", 24], + "linearDisassemblySeparatorColor": [0, 0, 0], + "stringColor": "magenta", + "typeNameColor": "orange", + "fieldNameColor": "cyan", + "keywordColor": "green", + "uncertainColor": "secondary", + "scriptConsoleOutputColor": "content", + "scriptConsoleErrorColor": "red", + "scriptConsoleEchoColor": "secondary", + "blueStandardHighlightColor": "blue", + "greenStandardHighlightColor": "green", + "cyanStandardHighlightColor": "cyan", + "redStandardHighlightColor": "red", + "magentaStandardHighlightColor": "magenta", + "yellowStandardHighlightColor": "yellow", + "orangeStandardHighlightColor": "orange", + "whiteStandardHighlightColor": "content", + "blackStandardHighlightColor": ["+", "background", [0, 0, 0]] + } +}
\ No newline at end of file |
