diff options
| -rw-r--r-- | binaryninjaapi.h | 166 | ||||
| -rw-r--r-- | binaryninjacore.h | 94 | ||||
| -rw-r--r-- | binaryview.cpp | 58 | ||||
| -rw-r--r-- | highlevelil.cpp | 22 | ||||
| -rw-r--r-- | highlevelilinstruction.cpp | 18 | ||||
| -rw-r--r-- | highlevelilinstruction.h | 4 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/binaryview.py | 188 | ||||
| -rw-r--r-- | python/examples/encoded_strings.py | 25 | ||||
| -rw-r--r-- | python/highlevelil.py | 24 | ||||
| -rw-r--r-- | python/stringrecognizer.py | 293 | ||||
| -rw-r--r-- | rust/src/disassembly.rs | 6 | ||||
| -rw-r--r-- | stringrecognizer.cpp | 300 | ||||
| -rw-r--r-- | ui/stringsview.h | 2 |
14 files changed, 1180 insertions, 21 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 96105e66..ecdeabdf 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -4074,6 +4074,7 @@ namespace BinaryNinja { class Segment; class Component; class TypeArchive; + struct DerivedString; /*! @@ -4107,6 +4108,8 @@ namespace BinaryNinja { static void StringFoundCallback(void* ctxt, BNBinaryView* data, BNStringType type, uint64_t offset, size_t len); static void StringRemovedCallback(void* ctxt, BNBinaryView* data, BNStringType type, uint64_t offset, size_t len); + static void DerivedStringFoundCallback(void* ctxt, BNBinaryView* data, BNDerivedString* str); + static void DerivedStringRemovedCallback(void* ctxt, BNBinaryView* data, BNDerivedString* str); static void TypeDefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type); static void TypeUndefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type); static void TypeReferenceChangedCallback(void* ctx, BNBinaryView* data, BNQualifiedName* name, BNType* type); @@ -4203,6 +4206,8 @@ namespace BinaryNinja { UndoEntryTaken = 1ULL << 50, RedoEntryTaken = 1ULL << 51, Rebased = 1ULL << 52, + DerivedStringFound = 1ULL << 53, + DerivedStringRemoved = 1ULL << 54, BinaryDataUpdates = DataWritten | DataInserted | DataRemoved, FunctionLifetime = FunctionAdded | FunctionRemoved, @@ -4213,7 +4218,7 @@ namespace BinaryNinja { TagUpdates = TagLifetime | TagUpdated, SymbolLifetime = SymbolAdded | SymbolRemoved, SymbolUpdates = SymbolLifetime | SymbolUpdated, - StringUpdates = StringFound | StringRemoved, + StringUpdates = StringFound | StringRemoved | DerivedStringFound | DerivedStringRemoved, TypeLifetime = TypeDefined | TypeUndefined, TypeUpdates = TypeLifetime | TypeReferenceChanged | TypeFieldReferenceChanged, SegmentLifetime = SegmentAdded | SegmentRemoved, @@ -4350,6 +4355,16 @@ namespace BinaryNinja { (void)offset; (void)len; } + virtual void OnDerivedStringFound(BinaryView* data, const DerivedString& str) + { + (void)data; + (void)str; + } + virtual void OnDerivedStringRemoved(BinaryView* data, const DerivedString& str) + { + (void)data; + (void)str; + } virtual void OnTypeDefined(BinaryView* data, const QualifiedName& name, Type* type) { (void)data; @@ -4823,7 +4838,7 @@ namespace BinaryNinja { const char* c_str() const; size_t size() const; - BNStringRef* GetObject() { return m_ref; } + BNStringRef* GetObject() const { return m_ref; } bool operator==(const StringRef& other) const { return this->operator std::string_view() == other.operator std::string_view(); } bool operator!=(const StringRef& other) const { return this->operator std::string_view() != other.operator std::string_view(); } @@ -5357,6 +5372,89 @@ namespace BinaryNinja { std::vector<TypeReferenceSource> typeRefs; }; + class CustomStringType: public StaticCoreRefCountObject<BNCustomStringType> + { + public: + CustomStringType(BNCustomStringType* type); + std::string GetName() const; + std::string GetStringPrefix() const; + std::string GetStringPostfix() const; + + static Ref<CustomStringType> Register( + const std::string& name, const std::string& stringPrefix = "", const std::string& stringPostfix = ""); + }; + + struct DerivedStringLocation + { + BNDerivedStringLocationType locationType; + uint64_t addr; + uint64_t len; + + bool operator==(const DerivedStringLocation& other) const + { + if (locationType != other.locationType) + return false; + if (addr != other.addr) + return false; + return len == other.len; + } + + bool operator!=(const DerivedStringLocation& other) const + { + return !(*this == other); + } + + bool operator<(const DerivedStringLocation& other) const + { + if (addr < other.addr) + return true; + if (addr > other.addr) + return false; + if (len < other.len) + return true; + if (len > other.len) + return false; + return locationType < other.locationType; + } + }; + + struct DerivedString + { + StringRef value; + std::optional<DerivedStringLocation> location; + Ref<CustomStringType> customType; + + bool operator==(const DerivedString& other) const + { + if (value != other.value) + return false; + if (location != other.location) + return false; + return customType == other.customType; + } + + bool operator!=(const DerivedString& other) const + { + return !(*this == other); + } + + bool operator<(const DerivedString& other) const + { + if (value < other.value) + return true; + if (other.value < value) + return false; + if (location < other.location) + return true; + if (other.location < location) + return false; + return customType < other.customType; + } + + BNDerivedString ToAPIObject(bool owned) const; + static DerivedString FromAPIObject(BNDerivedString* str, bool owned); + }; + struct QualifiedNameAndType; struct PossibleValueSet; class Metadata; @@ -7132,6 +7230,10 @@ namespace BinaryNinja { */ std::vector<BNStringReference> GetStrings(uint64_t start, uint64_t len); + std::vector<DerivedString> GetDerivedStrings(); + std::vector<ReferenceSource> GetDerivedStringCodeReferences( + const DerivedString& str, std::optional<size_t> maxItems = std::nullopt); + /*! Sets up a call back function to be called when analysis has been completed. This is helpful when using `UpdateAnalysis` which does not wait for analysis completion before returning. @@ -15441,6 +15543,10 @@ namespace BinaryNinja { std::set<Variable> GetVariables(); std::set<Variable> GetAliasedVariables(); std::set<SSAVariable> GetSSAVariables(); + + void SetDerivedStringReferenceForExpr(size_t expr, const DerivedString& str); + void RemoveDerivedStringReferenceForExpr(size_t expr); + std::optional<DerivedString> GetDerivedStringReferenceForExpr(size_t expr); }; struct LineFormatterSettings @@ -21326,6 +21432,62 @@ namespace BinaryNinja { HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence) override; }; + + class StringRecognizer : public StaticCoreRefCountObject<BNStringRecognizer> + { + std::string m_nameForRegister; + + public: + StringRecognizer(const std::string& name); + StringRecognizer(BNStringRecognizer* renderer); + + std::string GetName() const; + + virtual bool IsValidForType(HighLevelILFunction* func, Type* type); + virtual std::optional<DerivedString> RecognizeConstant( + const HighLevelILInstruction& instr, Type* type, int64_t val); + virtual std::optional<DerivedString> RecognizeConstantPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val); + virtual std::optional<DerivedString> RecognizeExternPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val, uint64_t offset); + virtual std::optional<DerivedString> RecognizeImport( + const HighLevelILInstruction& instr, Type* type, int64_t val); + + /*! Registers the string recognizer. + + \param recognizer The string recognizer to register. + */ + static void Register(StringRecognizer* recognizer); + + static Ref<StringRecognizer> GetByName(const std::string& name); + static std::vector<Ref<StringRecognizer>> GetRecognizers(); + + private: + static bool IsValidForTypeCallback(void* ctxt, BNHighLevelILFunction* hlil, BNType* type); + static bool RecognizeConstantCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + static bool RecognizeConstantPointerCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + static bool RecognizeExternPointerCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, + int64_t val, uint64_t offset, BNDerivedString* result); + static bool RecognizeImportCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + }; + + class CoreStringRecognizer : public StringRecognizer + { + public: + CoreStringRecognizer(BNStringRecognizer* recognizer); + bool IsValidForType(HighLevelILFunction* func, Type* type) override; + std::optional<DerivedString> RecognizeConstant( + const HighLevelILInstruction& instr, Type* type, int64_t val) override; + std::optional<DerivedString> RecognizeConstantPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val) override; + std::optional<DerivedString> RecognizeExternPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val, uint64_t offset) override; + std::optional<DerivedString> RecognizeImport( + const HighLevelILInstruction& instr, Type* type, int64_t val) override; + }; } // namespace BinaryNinja diff --git a/binaryninjacore.h b/binaryninjacore.h index 89037998..3e34662c 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 137 +#define BN_CURRENT_CORE_ABI_VERSION 138 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 136 +#define BN_MINIMUM_CORE_ABI_VERSION 138 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -312,6 +312,8 @@ extern "C" typedef struct BNIndirectBranchInfo BNIndirectBranchInfo; typedef struct BNArchitectureAndAddress BNArchitectureAndAddress; typedef struct BNConstantRenderer BNConstantRenderer; + typedef struct BNStringRecognizer BNStringRecognizer; + typedef struct BNCustomStringType BNCustomStringType; typedef struct BNRemoteFileSearchMatch { @@ -468,7 +470,8 @@ extern "C" StringDisplayTokenContext = 10, // For displaying strings which aren't associated with an address ContentCollapsedContext = 11, ContentExpandedContext = 12, - ContentCollapsiblePadding = 13 + ContentCollapsiblePadding = 13, + DerivedStringReferenceTokenContext = 14 } BNInstructionTextTokenContext; typedef enum BNLinearDisassemblyLineType @@ -1639,6 +1642,27 @@ extern "C" size_t nameCount; } BNQualifiedName; + typedef enum BNDerivedStringLocationType + { + DataBackedStringLocation, + CodeStringLocation + } BNDerivedStringLocationType; + + typedef struct BNDerivedStringLocation + { + BNDerivedStringLocationType locationType; + uint64_t addr; + uint64_t len; + } BNDerivedStringLocation; + + typedef struct BNDerivedString + { + BNStringRef* value; + bool locationValid; + BNDerivedStringLocation location; + BNCustomStringType* customType; + } BNDerivedString; + typedef struct BNBinaryDataNotification { void* context; @@ -1664,6 +1688,8 @@ extern "C" void (*symbolUpdated)(void* ctxt, BNBinaryView* view, BNSymbol* sym); void (*stringFound)(void* ctxt, BNBinaryView* view, BNStringType type, uint64_t offset, size_t len); void (*stringRemoved)(void* ctxt, BNBinaryView* view, BNStringType type, uint64_t offset, size_t len); + void (*derivedStringFound)(void* ctxt, BNBinaryView* view, BNDerivedString* str); + void (*derivedStringRemoved)(void* ctxt, BNBinaryView* view, BNDerivedString* str); void (*typeDefined)(void* ctxt, BNBinaryView* view, BNQualifiedName* name, BNType* type); void (*typeUndefined)(void* ctxt, BNBinaryView* view, BNQualifiedName* name, BNType* type); void (*typeReferenceChanged)(void* ctxt, BNBinaryView* view, BNQualifiedName* name, BNType* type); @@ -3826,6 +3852,27 @@ extern "C" BNOperatorPrecedence precedence); } BNCustomConstantRenderer; + typedef struct BNCustomStringRecognizer + { + void* context; + bool (*isValidForType)(void* ctxt, BNHighLevelILFunction* hlil, BNType* type); + bool (*recognizeConstant)( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + bool (*recognizeConstantPointer)( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + bool (*recognizeExternPointer)(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, + uint64_t offset, BNDerivedString* result); + bool (*recognizeImport)( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result); + } BNCustomStringRecognizer; + + typedef struct BNCustomStringTypeInfo + { + char* name; + char* stringPrefix; + char* stringPostfix; + } BNCustomStringTypeInfo; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI char* BNAllocStringWithLength(const char* contents, size_t len); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -5273,6 +5320,11 @@ extern "C" BNBinaryView* view, uint64_t start, uint64_t len, size_t* count); BINARYNINJACOREAPI void BNFreeStringReferenceList(BNStringReference* strings); + BINARYNINJACOREAPI BNDerivedString* BNGetDerivedStrings(BNBinaryView* view, size_t* count); + BINARYNINJACOREAPI BNReferenceSource* BNGetDerivedStringCodeReferences( + BNBinaryView* view, BNDerivedString* str, size_t* count, bool limit, size_t maxItems); + BINARYNINJACOREAPI void BNFreeDerivedStringList(BNDerivedString* strings, size_t count); + BINARYNINJACOREAPI BNVariableNameAndType* BNGetStackLayout(BNFunction* func, size_t* count); BINARYNINJACOREAPI void BNFreeVariableNameAndTypeList(BNVariableNameAndType* vars, size_t count); BINARYNINJACOREAPI void BNCreateAutoStackVariable( @@ -6577,6 +6629,12 @@ extern "C" BINARYNINJACOREAPI bool BNHighLevelILExprEqual( BNHighLevelILFunction* leftFunc, size_t leftExpr, BNHighLevelILFunction* rightFunc, size_t rightExpr); + BINARYNINJACOREAPI void BNSetHighLevelILDerivedStringReferenceForExpr( + BNHighLevelILFunction* func, size_t expr, BNDerivedString* str); + BINARYNINJACOREAPI void BNRemoveHighLevelILDerivedStringReferenceForExpr(BNHighLevelILFunction* func, size_t expr); + BINARYNINJACOREAPI bool BNGetHighLevelILDerivedStringReferenceForExpr( + BNHighLevelILFunction* func, size_t expr, BNDerivedString* out); + // Type Libraries BINARYNINJACOREAPI BNTypeLibrary* BNNewTypeLibrary(BNArchitecture* arch, const char* name); BINARYNINJACOREAPI BNTypeLibrary* BNNewTypeLibraryReference(BNTypeLibrary* lib); @@ -8701,13 +8759,15 @@ extern "C" BINARYNINJACOREAPI BNStringRef* BNDuplicateStringRef(BNStringRef* ref); BINARYNINJACOREAPI const char* BNGetStringRefContents(BNStringRef* ref); BINARYNINJACOREAPI size_t BNGetStringRefSize(BNStringRef* ref); + BINARYNINJACOREAPI BNStringRef* BNCreateStringRef(const char* str); + BINARYNINJACOREAPI BNStringRef* BNCreateStringRefOfLength(const char* str, size_t len); // Constant Renderers BINARYNINJACOREAPI BNConstantRenderer* BNRegisterConstantRenderer( const char* name, BNCustomConstantRenderer* renderer); BINARYNINJACOREAPI BNConstantRenderer* BNGetConstantRendererByName(const char* name); BINARYNINJACOREAPI BNConstantRenderer** BNGetConstantRendererList(size_t* count); - BINARYNINJACOREAPI void BNFreeConstantRendererList(BNLanguageRepresentationFunctionType** renderers); + BINARYNINJACOREAPI void BNFreeConstantRendererList(BNConstantRenderer** renderers); BINARYNINJACOREAPI char* BNGetConstantRendererName(BNConstantRenderer* renderer); BINARYNINJACOREAPI bool BNIsConstantRendererValidForType( BNConstantRenderer* renderer, BNHighLevelILFunction* il, BNType* type); @@ -8718,6 +8778,32 @@ extern "C" BNHighLevelILFunction* il, size_t exprIndex, BNType* type, int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence); + // String recognizers + BINARYNINJACOREAPI BNCustomStringType* BNRegisterCustomStringType(BNCustomStringTypeInfo* info); + BINARYNINJACOREAPI BNCustomStringType* BNGetCustomStringTypeByName(const char* name); + BINARYNINJACOREAPI BNCustomStringType* BNGetCustomStringTypeByID(uint32_t id); + BINARYNINJACOREAPI BNCustomStringType** BNGetCustomStringTypeList(size_t* count); + BINARYNINJACOREAPI void BNFreeCustomStringTypeList(BNCustomStringType** types); + BINARYNINJACOREAPI char* BNGetCustomStringTypeName(BNCustomStringType* type); + BINARYNINJACOREAPI char* BNGetCustomStringTypePrefix(BNCustomStringType* type); + BINARYNINJACOREAPI char* BNGetCustomStringTypePostfix(BNCustomStringType* type); + BINARYNINJACOREAPI BNStringRecognizer* BNRegisterStringRecognizer( + const char* name, BNCustomStringRecognizer* recognizer); + BINARYNINJACOREAPI BNStringRecognizer* BNGetStringRecognizerByName(const char* name); + BINARYNINJACOREAPI BNStringRecognizer** BNGetStringRecognizerList(size_t* count); + BINARYNINJACOREAPI void BNFreeStringRecognizerList(BNStringRecognizer** recognizers); + BINARYNINJACOREAPI char* BNGetStringRecognizerName(BNStringRecognizer* recognizer); + BINARYNINJACOREAPI bool BNIsStringRecognizerValidForType( + BNStringRecognizer* recognizer, BNHighLevelILFunction* il, BNType* type); + BINARYNINJACOREAPI bool BNStringRecognizerRecognizeConstant(BNStringRecognizer* recognizer, + BNHighLevelILFunction* il, size_t exprIndex, BNType* type, int64_t val, BNDerivedString* out); + BINARYNINJACOREAPI bool BNStringRecognizerRecognizeConstantPointer(BNStringRecognizer* recognizer, + BNHighLevelILFunction* il, size_t exprIndex, BNType* type, int64_t val, BNDerivedString* out); + BINARYNINJACOREAPI bool BNStringRecognizerRecognizeExternPointer(BNStringRecognizer* recognizer, + BNHighLevelILFunction* il, size_t exprIndex, BNType* type, int64_t val, uint64_t offset, BNDerivedString* out); + BINARYNINJACOREAPI bool BNStringRecognizerRecognizeImport(BNStringRecognizer* recognizer, BNHighLevelILFunction* il, + size_t exprIndex, BNType* type, int64_t val, BNDerivedString* out); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 292b11ac..3499f2ac 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -222,6 +222,22 @@ void BinaryDataNotification::StringRemovedCallback( } +void BinaryDataNotification::DerivedStringFoundCallback(void* ctxt, BNBinaryView* data, BNDerivedString* str) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + notify->OnDerivedStringFound(view, DerivedString::FromAPIObject(str, false)); +} + + +void BinaryDataNotification::DerivedStringRemovedCallback(void* ctxt, BNBinaryView* data, BNDerivedString* str) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + notify->OnDerivedStringRemoved(view, DerivedString::FromAPIObject(str, false)); +} + + void BinaryDataNotification::TypeDefinedCallback(void* ctxt, BNBinaryView* data, BNQualifiedName* name, BNType* type) { BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; @@ -556,6 +572,8 @@ BinaryDataNotification::BinaryDataNotification() m_callbacks.symbolRemoved = SymbolRemovedCallback; m_callbacks.stringFound = StringFoundCallback; m_callbacks.stringRemoved = StringRemovedCallback; + m_callbacks.derivedStringFound = DerivedStringFoundCallback; + m_callbacks.derivedStringRemoved = DerivedStringRemovedCallback; m_callbacks.typeDefined = TypeDefinedCallback; m_callbacks.typeUndefined = TypeUndefinedCallback; m_callbacks.typeReferenceChanged = TypeReferenceChangedCallback; @@ -615,6 +633,8 @@ BinaryDataNotification::BinaryDataNotification(NotificationTypes notifications) m_callbacks.symbolRemoved = (notifications & NotificationType::SymbolRemoved) ? SymbolRemovedCallback : nullptr; m_callbacks.stringFound = (notifications & NotificationType::StringFound) ? StringFoundCallback : nullptr; m_callbacks.stringRemoved = (notifications & NotificationType::StringRemoved) ? StringRemovedCallback : nullptr; + m_callbacks.derivedStringFound = (notifications & NotificationType::DerivedStringFound) ? DerivedStringFoundCallback : nullptr; + m_callbacks.derivedStringRemoved = (notifications & NotificationType::DerivedStringRemoved) ? DerivedStringRemovedCallback : nullptr; m_callbacks.typeDefined = (notifications & NotificationType::TypeDefined) ? TypeDefinedCallback : nullptr; m_callbacks.typeUndefined = (notifications & NotificationType::TypeUndefined) ? TypeUndefinedCallback : nullptr; m_callbacks.typeReferenceChanged = (notifications & NotificationType::TypeReferenceChanged) ? TypeReferenceChangedCallback : nullptr; @@ -670,7 +690,7 @@ StringRef::StringRef(const std::string& str) StringRef::StringRef(const StringRef& other) { - m_ref = BNDuplicateStringRef(other.m_ref); + m_ref = other.m_ref ? BNDuplicateStringRef(other.m_ref) : nullptr; } @@ -4013,6 +4033,42 @@ vector<BNStringReference> BinaryView::GetStrings(uint64_t start, uint64_t len) } +vector<DerivedString> BinaryView::GetDerivedStrings() +{ + size_t count; + BNDerivedString* strings = BNGetDerivedStrings(m_object, &count); + vector<DerivedString> result; + for (size_t i = 0; i < count; i++) + result.push_back(DerivedString::FromAPIObject(&strings[i], false)); + BNFreeDerivedStringList(strings, count); + return result; +} + + +vector<ReferenceSource> BinaryView::GetDerivedStringCodeReferences( + const DerivedString& str, std::optional<size_t> maxItems) +{ + BNDerivedString derivedStr = str.ToAPIObject(false); + + size_t count; + BNReferenceSource* refs = BNGetDerivedStringCodeReferences( + m_object, &derivedStr, &count, maxItems.has_value(), maxItems.value_or(0)); + vector<ReferenceSource> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + ReferenceSource src; + src.func = new Function(BNNewFunctionReference(refs[i].func)); + src.arch = new CoreArchitecture(refs[i].arch); + src.addr = refs[i].addr; + result.push_back(src); + } + + BNFreeCodeReferences(refs, count); + return result; +} + + // The caller of this function must hold a reference to the returned Ref<AnalysisCompletionEvent>. // Otherwise, it can be freed before the callback is triggered, leading to a crash. Ref<AnalysisCompletionEvent> BinaryView::AddAnalysisCompletionEvent(const function<void()>& callback) diff --git a/highlevelil.cpp b/highlevelil.cpp index 84d74eb3..5f1323eb 100644 --- a/highlevelil.cpp +++ b/highlevelil.cpp @@ -645,6 +645,28 @@ set<SSAVariable> HighLevelILFunction::GetSSAVariables() } +void HighLevelILFunction::SetDerivedStringReferenceForExpr(size_t expr, const DerivedString& str) +{ + BNDerivedString strObj = str.ToAPIObject(false); + BNSetHighLevelILDerivedStringReferenceForExpr(m_object, expr, &strObj); +} + + +void HighLevelILFunction::RemoveDerivedStringReferenceForExpr(size_t expr) +{ + BNRemoveHighLevelILDerivedStringReferenceForExpr(m_object, expr); +} + + +optional<DerivedString> HighLevelILFunction::GetDerivedStringReferenceForExpr(size_t expr) +{ + BNDerivedString str; + if (!BNGetHighLevelILDerivedStringReferenceForExpr(m_object, expr, &str)) + return std::nullopt; + return DerivedString::FromAPIObject(&str, true); +} + + HighLevelILTokenEmitter::CurrentExprGuard::CurrentExprGuard(HighLevelILTokenEmitter& parent, const BNTokenEmitterExpr& expr): m_parent(&parent) { diff --git a/highlevelilinstruction.cpp b/highlevelilinstruction.cpp index 964e1af9..6fe43618 100644 --- a/highlevelilinstruction.cpp +++ b/highlevelilinstruction.cpp @@ -1012,6 +1012,24 @@ void HighLevelILInstructionBase::ClearAttribute(BNILInstructionAttribute attribu } +std::optional<DerivedString> HighLevelILInstructionBase::GetDerivedStringReference() const +{ + return function->GetDerivedStringReferenceForExpr(exprIndex); +} + + +void HighLevelILInstructionBase::SetDerivedStringReference(const DerivedString& str) +{ + function->SetDerivedStringReferenceForExpr(exprIndex, str); +} + + +void HighLevelILInstructionBase::RemoveDerivedStringReference() +{ + function->RemoveDerivedStringReferenceForExpr(exprIndex); +} + + size_t HighLevelILInstructionBase::GetInstructionIndex() const { return function->GetInstructionForExpr(exprIndex); diff --git a/highlevelilinstruction.h b/highlevelilinstruction.h index 2e10c485..9be7d7c5 100644 --- a/highlevelilinstruction.h +++ b/highlevelilinstruction.h @@ -405,6 +405,10 @@ namespace BinaryNinja void SetAttribute(BNILInstructionAttribute attribute, bool state = true); void ClearAttribute(BNILInstructionAttribute attribute); + std::optional<DerivedString> GetDerivedStringReference() const; + void SetDerivedStringReference(const DerivedString& str); + void RemoveDerivedStringReference(); + size_t GetInstructionIndex() const; HighLevelILInstruction GetInstruction() const; diff --git a/python/__init__.py b/python/__init__.py index acf8b293..02703972 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -83,6 +83,7 @@ from .languagerepresentation import * from .lineformatter import * from .renderlayer import * from .constantrenderer import * +from .stringrecognizer import * # We import each of these by name to prevent conflicts between # log.py and the function 'log' which we don't import below from .log import ( diff --git a/python/binaryview.py b/python/binaryview.py index 9e42d8fb..e38c334c 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -45,7 +45,7 @@ from . import decorators from .enums import ( AnalysisState, SymbolType, Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, TypeClass, BinaryViewEventType, FunctionGraphType, TagReferenceType, TagTypeType, RegisterValueType, DisassemblyOption, - RelocationType + RelocationType, DerivedStringLocationType ) from .exceptions import RelocationWriteException, ExternalLinkException @@ -82,6 +82,7 @@ from . import typecontainer from . import externallibrary from . import project from . import undo +from . import stringrecognizer PathType = Union[str, os.PathLike] @@ -216,6 +217,8 @@ class NotificationType(IntFlag): UndoEntryTaken = 1 << 50 RedoEntryTaken = 1 << 51 Rebased = 1 << 52 + DerivedStringFound = 1 << 53 + DerivedStringRemoved = 1 << 54 BinaryDataUpdates = DataWritten | DataInserted | DataRemoved FunctionLifetime = FunctionAdded | FunctionRemoved @@ -226,7 +229,7 @@ class NotificationType(IntFlag): TagUpdates = TagLifetime | TagUpdated SymbolLifetime = SymbolAdded | SymbolRemoved SymbolUpdates = SymbolLifetime | SymbolUpdated - StringUpdates = StringFound | StringRemoved + StringUpdates = StringFound | StringRemoved | DerivedStringFound | DerivedStringRemoved TypeLifetime = TypeDefined | TypeUndefined TypeUpdates = TypeLifetime | TypeReferenceChanged | TypeFieldReferenceChanged SegmentLifetime = SegmentAdded | SegmentRemoved @@ -390,6 +393,12 @@ class BinaryDataNotification: def string_removed(self, view: 'BinaryView', string_type: StringType, offset: int, length: int) -> None: pass + def derived_string_found(self, view: 'BinaryView', string: 'DerivedString') -> None: + pass + + def derived_string_removed(self, view: 'BinaryView', string: 'DerivedString') -> None: + pass + def type_defined(self, view: 'BinaryView', name: '_types.QualifiedName', type: '_types.Type') -> None: pass @@ -518,6 +527,136 @@ class StringReference: return self._view +class StringRef: + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNStringRef) + + def __del__(self): + if core is not None: + core.BNFreeStringRef(self.handle) + + def __bytes__(self): + # Do not call the wrapper BNGetStringRefContents here, it will crash as the generator + # does not understand that this API gives a direct reference to the string + value_ptr = core._BNGetStringRefContents(self.handle) + value_len = core.BNGetStringRefSize(self.handle) + buf = ctypes.create_string_buffer(value_len) + ctypes.memmove(buf, value_ptr, value_len) + return bytes(buf.raw) + + def __str__(self): + return core.pyNativeStr(bytes(self)) + + def __len__(self): + return core.BNGetStringRefSize(self.handle) + + def __repr__(self): + return repr(str(self)) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return bytes(self) == bytes(other) + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __lt__(self, other) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return bytes(self) < bytes(self) + + def __gt__(self, other) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return bytes(self) > bytes(other) + + def __le__(self, other) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return bytes(self) <= bytes(other) + + def __ge__(self, other) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return bytes(self) >= bytes(other) + + def __hash__(self): + return hash(bytes(self)) + + +@dataclass(frozen=True) +class DerivedStringLocation: + location_type: 'DerivedStringLocationType' + address: int + length: int + + +@dataclass(frozen=True) +class DerivedString: + value: 'StringRef' + location: Optional[DerivedStringLocation] + custom_type: Optional[stringrecognizer.CustomStringType] + + def __init__( + self, value: Union['StringRef', str, bytes, bytearray, 'databuffer.DataBuffer'], + location: Optional[DerivedStringLocation], custom_type: Optional[stringrecognizer.CustomStringType] + ): + if isinstance(value, str): + value = value.encode("utf-8") + value = StringRef(core.BNCreateStringRefOfLength(value, len(value))) + elif isinstance(value, bytes): + value = StringRef(core.BNCreateStringRefOfLength(value, len(value))) + elif isinstance(value, bytearray): + value = bytes(value) + value = StringRef(core.BNCreateStringRefOfLength(value, len(value))) + elif isinstance(value, databuffer.DataBuffer): + value = bytes(value) + value = StringRef(core.BNCreateStringRefOfLength(value, len(value))) + elif not isinstance(value, StringRef): + value = str(value).encode("utf-8") + value = StringRef(core.BNCreateStringRefOfLength(value, len(value))) + + object.__setattr__(self, "value", value) + object.__setattr__(self, "location", location) + object.__setattr__(self, "custom_type", custom_type) + + def _to_core_struct(self, owned: bool) -> 'core.BNDerivedString': + result = core.BNDerivedString() + if owned: + result.value = core.BNDuplicateStringRef(self.value.handle) + else: + result.value = self.value.handle + result.locationValid = self.location is not None + if result.locationValid: + result.location.locationType = self.location.location_type + result.location.addr = self.location.address + result.location.len = self.location.length + if self.custom_type is None: + result.customType = None + else: + result.customType = self.custom_type.handle + return result + + @staticmethod + def _from_core_struct(obj: 'core.BNDerivedString', owned: bool) -> 'DerivedString': + if owned: + value = StringRef(obj.value) + else: + value = StringRef(core.BNDuplicateStringRef(obj.value)) + if obj.locationValid: + location = DerivedStringLocation(DerivedStringLocationType(obj.location.locationType), obj.location.addr, obj.location.len) + else: + location = None + if obj.customType: + custom_type = stringrecognizer.CustomStringType(obj.customType) + else: + custom_type = None + return DerivedString(value, location, custom_type) + + class AnalysisCompletionEvent: """ The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving @@ -701,6 +840,8 @@ class BinaryDataNotificationCallbacks: self._cb.symbolUpdated = self._cb.symbolUpdated.__class__(self._symbol_updated) self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) + self._cb.derivedStringFound = self._cb.derivedStringFound.__class__(self._derived_string_found) + self._cb.derivedStringRemoved = self._cb.derivedStringRemoved.__class__(self._derived_string_removed) self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) self._cb.typeReferenceChanged = self._cb.typeReferenceChanged.__class__(self._type_ref_changed) @@ -774,6 +915,10 @@ class BinaryDataNotificationCallbacks: self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) if notify.notifications & NotificationType.StringRemoved: self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) + if notify.notifications & NotificationType.DerivedStringFound: + self._cb.derivedStringFound = self._cb.derivedStringFound.__class__(self._derived_string_found) + if notify.notifications & NotificationType.DerivedStringRemoved: + self._cb.derivedStringRemoved = self._cb.derivedStringRemoved.__class__(self._derived_string_removed) if notify.notifications & NotificationType.TypeDefined: self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) if notify.notifications & NotificationType.TypeUndefined: @@ -1017,6 +1162,18 @@ class BinaryDataNotificationCallbacks: except: log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._string_removed") + def _derived_string_found(self, ctxt, view: core.BNBinaryView, string) -> None: + try: + self._notify.derived_string_found(self._view, DerivedString._from_core_struct(string[0], False)) + except: + log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._derived_string_found") + + def _derived_string_removed(self, ctxt, view: core.BNBinaryView, string) -> None: + try: + self._notify.derived_string_removed(self._view, DerivedString._from_core_struct(string[0], False)) + except: + log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._derived_string_removed") + def _type_defined(self, ctxt, view: core.BNBinaryView, name: str, type_obj: '_types.Type') -> None: try: qualified_name = _types.QualifiedName._from_core_struct(name[0]) @@ -3402,6 +3559,19 @@ class BinaryView: return self.get_strings() @property + def derived_strings(self) -> List['DerivedString']: + count = ctypes.c_ulonglong(0) + strings = core.BNGetDerivedStrings(self.handle, count) + assert strings is not None, "core.BNGetDerivedStrings returned None" + try: + result = [] + for i in range(0, count.value): + result.append(DerivedString._from_core_struct(strings[i], False)) + return result + finally: + core.BNFreeDerivedStringList(strings, count.value) + + @property def saved(self) -> bool: """boolean state of whether or not the file has been saved (read/write)""" return self._file.saved @@ -5818,6 +5988,20 @@ class BinaryView: core.BNFreeTypeReferences(refs, count.value) return result + def get_derived_string_code_refs(self, str: 'DerivedString', max_items: Optional[int] = None) -> Generator['ReferenceSource', None, None]: + count = ctypes.c_ulonglong(0) + has_max_items = max_items is not None + max_items_value = max_items if has_max_items else 0 + core_str = str._to_core_struct(False) + refs = core.BNGetDerivedStringCodeReferences(self.handle, core_str, count, has_max_items, max_items_value) + assert refs is not None, "core.BNGetDerivedStringCodeReferences returned None" + + try: + for i in range(0, count.value): + yield ReferenceSource._from_core_struct(self, refs[i]) + finally: + core.BNFreeCodeReferences(refs, count.value) + def add_data_ref(self, from_addr: int, to_addr: int) -> None: """ ``add_data_ref`` adds an auto data cross-reference (xref) from the address ``from_addr`` to the address ``to_addr``. diff --git a/python/examples/encoded_strings.py b/python/examples/encoded_strings.py index ce7d02db..691d116c 100644 --- a/python/examples/encoded_strings.py +++ b/python/examples/encoded_strings.py @@ -1,8 +1,11 @@ -from binaryninja import (ConstantRenderer, PointerType, InstructionTextToken, InstructionTextTokenType, DataBuffer) +from binaryninja import (StringRecognizer, CustomStringType, DataBuffer, DerivedString, DerivedStringLocation, + DerivedStringLocationType, PointerType, InstructionTextToken, InstructionTextTokenType) +encoded_string_type = CustomStringType.register("Encoded", "", "_enc") -class EncodedStringConstantRenderer(ConstantRenderer): - renderer_name = "encoded_strings" + +class EncodedStringRecognizer(StringRecognizer): + recognizer_name = "encoded_strings" decoders = { "xor_encoded": lambda encoded, key: encoded ^ key, "sub_encoded": lambda encoded, key: (encoded - key) & 0xff, @@ -17,9 +20,9 @@ class EncodedStringConstantRenderer(ConstantRenderer): return True return False - def render_constant_pointer(self, instr, type, val, tokens, settings, precedence): + def recognize_constant_pointer(self, instr, type, val): if not isinstance(type, PointerType): - return False + return None values = None decoder = None @@ -29,9 +32,9 @@ class EncodedStringConstantRenderer(ConstantRenderer): values = bytes.fromhex(type.target.attributes[name]) decoder = self.__class__.decoders[name] except: - return False + return None if values is None or decoder is None: - return False + return None encoded_null = "encoded_null" in type.target.attributes @@ -49,10 +52,8 @@ class EncodedStringConstantRenderer(ConstantRenderer): result += bytes([byte]) i += 1 - tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "\"")) - tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, DataBuffer(result).escape())) - tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "\"_enc")) - return True + loc = DerivedStringLocation(DerivedStringLocationType.DataBackedStringLocation, val, i) + return DerivedString(result, loc, encoded_string_type) -EncodedStringConstantRenderer().register() +EncodedStringRecognizer().register() diff --git a/python/highlevelil.py b/python/highlevelil.py index ce264e19..ef1b3de2 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -41,6 +41,7 @@ from . import highlight from . import flowgraph from . import variable from . import databuffer +from . import stringrecognizer from . import types as _types from .interaction import show_graph_report from .commonil import ( @@ -973,6 +974,13 @@ class HighLevelILInstruction(BaseILInstruction): hash ^= rotl(discriminator, 47) return hash + @property + def derived_string_reference(self) -> Optional['binaryview.DerivedString']: + str = core.BNDerivedString() + if not core.BNGetHighLevelILDerivedStringReferenceForExpr(self.function.handle, self.expr_index, str): + return None + return binaryview.DerivedString._from_core_struct(str, True) + @dataclass(frozen=True, repr=False, eq=False) class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation): @@ -3003,6 +3011,22 @@ class HighLevelILFunction: result |= flag.value core.BNSetHighLevelILExprAttributes(self.handle, expr, result) + def set_derived_string_reference_for_expr(self, expr: InstructionOrExpression, str: 'binaryview.DerivedString'): + if isinstance(expr, HighLevelILInstruction): + expr = expr.expr_index + elif isinstance(expr, int): + expr = ExpressionIndex(expr) + + str_obj = str._to_core_struct(False) + core.BNSetHighLevelILDerivedStringReferenceForExpr(self.handle, expr, str_obj) + + def remove_derived_string_reference_for_expr(self, expr: InstructionOrExpression): + if isinstance(expr, HighLevelILInstruction): + expr = expr.expr_index + elif isinstance(expr, int): + expr = ExpressionIndex(expr) + core.BNRemoveHighLevelILDerivedStringReferenceForExpr(self.handle, expr) + def nop(self, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: """ ``nop`` no operation, this instruction does nothing diff --git a/python/stringrecognizer.py b/python/stringrecognizer.py new file mode 100644 index 00000000..8f503d8b --- /dev/null +++ b/python/stringrecognizer.py @@ -0,0 +1,293 @@ +# Copyright (c) 2015-2025 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +from typing import Optional, Union +from dataclasses import dataclass +import ctypes + +import binaryninja +from . import _binaryninjacore as core +from .log import log_error_for_exception +from . import types +from . import highlevelil +from . import binaryview + + +class _CustomStringTypeMetaClass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetCustomStringTypeList(count) + assert types is not None, "core.BNGetCustomStringTypeList returned None" + try: + for i in range(0, count.value): + yield CustomStringType(handle=types[i]) + finally: + core.BNFreeCustomStringTypeList(types) + + def __getitem__(cls, value): + binaryninja._init_plugins() + string_type = core.BNGetCustomStringTypeByName(str(value)) + if string_type is None: + raise KeyError("'%s' is not a valid type" % str(value)) + return CustomStringType(handle=string_type) + + +class CustomStringType(metaclass=_CustomStringTypeMetaClass): + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNCustomStringType) + + def __str__(self): + return self.name + + def __repr__(self): + return f"<{self.__class__.__name__}: {self.name}>" + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __hash__(self): + return hash(ctypes.addressof(self.handle.contents)) + + @staticmethod + def register(name: str, string_prefix="", string_postfix="") -> 'CustomStringType': + info = core.BNCustomStringTypeInfo() + info.name = name + info.stringPrefix = string_prefix + info.stringPostfix = string_postfix + handle = core.BNRegisterCustomStringType(info) + return CustomStringType(handle) + + @property + def name(self) -> str: + return core.BNGetCustomStringTypeName(self.handle) + + @property + def string_prefix(self) -> str: + return core.BNGetCustomStringTypePrefix(self.handle) + + @property + def string_postfix(self) -> str: + return core.BNGetCustomStringTypePostfix(self.handle) + + +class _StringRecognizerMetaClass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + recognizers = core.BNGetStringRecognizerList(count) + assert recognizers is not None, "core.BNGetStringRecognizerList returned None" + try: + for i in range(0, count.value): + yield CoreStringRecognizer(handle=recognizers[i]) + finally: + core.BNFreeStringRecognizerList(recognizers) + + def __getitem__(cls, value): + binaryninja._init_plugins() + recognizer = core.BNGetStringRecognizerByName(str(value)) + if recognizer is None: + raise KeyError("'%s' is not a valid recognizer" % str(value)) + return CoreStringRecognizer(handle=recognizer) + + +class StringRecognizer(metaclass=_StringRecognizerMetaClass): + _registered_recognizers = [] + recognizer_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNStringRecognizer) + + def register(self): + if self.__class__.recognizer_name is None: + raise ValueError("Recognizer name is missing") + self._cb = core.BNCustomStringRecognizer() + self._cb.context = 0 + if self.is_valid_for_type.__func__ != StringRecognizer.is_valid_for_type: + self._cb.isValidForType = self._cb.isValidForType.__class__(self._is_valid_for_type) + if self.recognize_constant.__func__ != StringRecognizer.recognize_constant: + self._cb.recognizeConstant = self._cb.recognizeConstant.__class__(self._recognize_constant) + if self.recognize_constant_pointer.__func__ != StringRecognizer.recognize_constant_pointer: + self._cb.recognizeConstantPointer = self._cb.recognizeConstantPointer.__class__( + self._recognize_constant_pointer) + if self.recognize_extern_pointer.__func__ != StringRecognizer.recognize_extern_pointer: + self._cb.recognizeExternPointer = self._cb.recognizeExternPointer.__class__(self._recognize_extern_pointer) + if self.recognize_import.__func__ != StringRecognizer.recognize_import: + self._cb.recognizeImport = self._cb.recognizeImport.__class__(self._recognize_import) + self.handle = core.BNRegisterStringRecognizer(self.__class__.recognizer_name, self._cb) + self.__class__._registered_recognizers.append(self) + + def _is_valid_for_type(self, ctxt, hlil, type): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + return self.is_valid_for_type(hlil, type) + except: + log_error_for_exception("Unhandled Python exception in StringRecognizer._is_valid_for_type") + return False + + def _recognize_constant(self, ctxt, hlil, expr, type, val, result): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + ref = self.recognize_constant(instr, type, val) + if ref is None: + return False + result[0] = ref._to_core_struct(True) + return True + except: + log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_constant") + return False + + def _recognize_constant_pointer(self, ctxt, hlil, expr, type, val, result): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + ref = self.recognize_constant_pointer(instr, type, val) + if ref is None: + return False + result[0] = ref._to_core_struct(True) + return True + except: + log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_constant_pointer") + return False + + def _recognize_extern_pointer(self, ctxt, hlil, expr, type, val, offset, result): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + ref = self.recognize_extern_pointer(instr, type, val, offset) + if ref is None: + return False + result[0] = ref._to_core_struct(True) + return True + except: + log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_extern_pointer") + return False + + def _recognize_import(self, ctxt, hlil, expr, type, val, result): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + ref = self.recognize_import(instr, type, val) + if ref is None: + return False + result[0] = ref._to_core_struct(True) + return True + except: + log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_import") + return False + + @property + def name(self) -> str: + if hasattr(self, 'handle'): + return core.BNGetStringRecognizerName(self.handle) + return self.__class__.recognizer_name + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return True + + def recognize_constant( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_constant_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_extern_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, offset: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_import( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + +_recognizer_cache = {} + + +class CoreStringRecognizer(StringRecognizer): + def __init__(self, handle: core.BNStringRecognizer): + super(CoreStringRecognizer, self).__init__(handle=handle) + if type(self) is CoreStringRecognizer: + global _recognizer_cache + _recognizer_cache[ctypes.addressof(handle.contents)] = self + + @classmethod + def _from_cache(cls, handle) -> 'StringRecognizer': + """ + Look up a recognizer from a given BNStringRecognizer handle + :param handle: BNStringRecognizer pointer + :return: Recognizer instance responsible for this handle + """ + global _recognizer_cache + return _recognizer_cache.get(ctypes.addressof(handle.contents)) or cls(handle) + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return core.BNIsStringRecognizerValidForType(self.handle, func.handle, type.handle) + + def recognize_constant( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + string = core.BNDerivedString() + if not core.BNStringRecognizerRecognizeConstant(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string): + return None + return binaryview.DerivedString._from_core_struct(string, True) + + def recognize_constant_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + string = core.BNDerivedString() + if not core.BNStringRecognizerRecognizeConstantPointer(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string): + return None + return binaryview.DerivedString._from_core_struct(string, True) + + def recognize_extern_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, offset: int + ) -> Optional['binaryview.DerivedString']: + string = core.BNDerivedString() + if not core.BNStringRecognizerRecognizeExternPointer(self.handle, instr.function.handle, instr.expr_index, type.handle, val, offset, string): + return None + return binaryview.DerivedString._from_core_struct(string, True) + + def recognize_import( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + string = core.BNDerivedString() + if not core.BNStringRecognizerRecognizeImport(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string): + return None + return binaryview.DerivedString._from_core_struct(string, True) diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index cd5a860d..75bf28fa 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -975,6 +975,8 @@ pub enum InstructionTextTokenContext { Expanded, /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`] CollapsiblePadding, + /// Use only with [`InstructionTextTokenKind::String`] + DerivedStringReference, } impl From<BNInstructionTextTokenContext> for InstructionTextTokenContext { @@ -1002,6 +1004,9 @@ impl From<BNInstructionTextTokenContext> for InstructionTextTokenContext { BNInstructionTextTokenContext::ContentCollapsedContext => Self::Collapsed, BNInstructionTextTokenContext::ContentExpandedContext => Self::Expanded, BNInstructionTextTokenContext::ContentCollapsiblePadding => Self::CollapsiblePadding, + BNInstructionTextTokenContext::DerivedStringReferenceTokenContext => { + Self::DerivedStringReference + } } } } @@ -1023,6 +1028,7 @@ impl From<InstructionTextTokenContext> for BNInstructionTextTokenContext { InstructionTextTokenContext::Collapsed => Self::ContentCollapsedContext, InstructionTextTokenContext::Expanded => Self::ContentExpandedContext, InstructionTextTokenContext::CollapsiblePadding => Self::ContentCollapsiblePadding, + InstructionTextTokenContext::DerivedStringReference => Self::DerivedStringReferenceTokenContext, } } } diff --git a/stringrecognizer.cpp b/stringrecognizer.cpp new file mode 100644 index 00000000..dc6ac2f8 --- /dev/null +++ b/stringrecognizer.cpp @@ -0,0 +1,300 @@ +#include "binaryninjaapi.h" +#include "ffi.h" +#include "highlevelilinstruction.h" + +using namespace BinaryNinja; +using namespace std; + + +BNDerivedString DerivedString::ToAPIObject(bool owned) const +{ + BNDerivedString result; + result.value = owned ? BNDuplicateStringRef(value.GetObject()) : value.GetObject(); + result.locationValid = location.has_value(); + if (location.has_value()) + { + result.location.locationType = location->locationType; + result.location.addr = location->addr; + result.location.len = location->len; + } + else + { + result.location.locationType = DataBackedStringLocation; + result.location.addr = 0; + result.location.len = 0; + } + result.customType = customType ? customType->GetObject() : nullptr; + return result; +} + + +DerivedString DerivedString::FromAPIObject(BNDerivedString* str, bool owned) +{ + DerivedString result; + result.value = StringRef(owned ? str->value : BNDuplicateStringRef(str->value)); + if (str->locationValid) + result.location = {str->location.locationType, str->location.addr, str->location.len}; + result.customType = str->customType ? new CustomStringType(str->customType) : nullptr; + return result; +} + + +CustomStringType::CustomStringType(BNCustomStringType* type) +{ + m_object = type; +} + + +string CustomStringType::GetName() const +{ + char* name = BNGetCustomStringTypeName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + +string CustomStringType::GetStringPrefix() const +{ + char* prefix = BNGetCustomStringTypePrefix(m_object); + string result = prefix; + BNFreeString(prefix); + return result; +} + + +string CustomStringType::GetStringPostfix() const +{ + char* postfix = BNGetCustomStringTypePostfix(m_object); + string result = postfix; + BNFreeString(postfix); + return result; +} + + +Ref<CustomStringType> CustomStringType::Register( + const std::string& name, const std::string& stringPrefix, const std::string& stringPostfix) +{ + BNCustomStringTypeInfo info; + info.name = (char*)name.c_str(); + info.stringPrefix = (char*)stringPrefix.c_str(); + info.stringPostfix = (char*)stringPostfix.c_str(); + BNCustomStringType* result = BNRegisterCustomStringType(&info); + return new CustomStringType(result); +} + + +StringRecognizer::StringRecognizer(const std::string& name): m_nameForRegister(name) +{ + m_object = nullptr; +} + + +StringRecognizer::StringRecognizer(BNStringRecognizer* recognizer) +{ + m_object = recognizer; +} + + +string StringRecognizer::GetName() const +{ + char* name = BNGetStringRecognizerName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + +bool StringRecognizer::IsValidForType(HighLevelILFunction*, Type*) +{ + return true; +} + + +std::optional<DerivedString> StringRecognizer::RecognizeConstant(const HighLevelILInstruction&, Type*, int64_t) +{ + return std::nullopt; +} + + +std::optional<DerivedString> StringRecognizer::RecognizeConstantPointer(const HighLevelILInstruction&, Type*, int64_t) +{ + return std::nullopt; +} + + +std::optional<DerivedString> StringRecognizer::RecognizeExternPointer( + const HighLevelILInstruction&, Type*, int64_t, uint64_t) +{ + return std::nullopt; +} + + +std::optional<DerivedString> StringRecognizer::RecognizeImport(const HighLevelILInstruction&, Type*, int64_t) +{ + return std::nullopt; +} + + +void StringRecognizer::Register(StringRecognizer* recognizer) +{ + BNCustomStringRecognizer callbacks; + callbacks.context = recognizer; + callbacks.isValidForType = IsValidForTypeCallback; + callbacks.recognizeConstant = RecognizeConstantCallback; + callbacks.recognizeConstantPointer = RecognizeConstantPointerCallback; + callbacks.recognizeExternPointer = RecognizeExternPointerCallback; + callbacks.recognizeImport = RecognizeImportCallback; + + recognizer->AddRefForRegistration(); + recognizer->m_object = BNRegisterStringRecognizer(recognizer->m_nameForRegister.c_str(), &callbacks); +} + + +bool StringRecognizer::IsValidForTypeCallback(void* ctxt, BNHighLevelILFunction* hlil, BNType* type) +{ + StringRecognizer* recognizer = (StringRecognizer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + return recognizer->IsValidForType(hlilObj, typeObj); +} + + +bool StringRecognizer::RecognizeConstantCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result) +{ + StringRecognizer* recognizer = (StringRecognizer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + auto str = recognizer->RecognizeConstant(instr, typeObj, val); + if (!str.has_value()) + return false; + *result = str->ToAPIObject(true); + return true; +} + + +bool StringRecognizer::RecognizeConstantPointerCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result) +{ + StringRecognizer* recognizer = (StringRecognizer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + auto str = recognizer->RecognizeConstantPointer(instr, typeObj, val); + if (!str.has_value()) + return false; + *result = str->ToAPIObject(true); + return true; +} + + +bool StringRecognizer::RecognizeExternPointerCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, + BNType* type, int64_t val, uint64_t offset, BNDerivedString* result) +{ + StringRecognizer* recognizer = (StringRecognizer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + auto str = recognizer->RecognizeExternPointer(instr, typeObj, val, offset); + if (!str.has_value()) + return false; + *result = str->ToAPIObject(true); + return true; +} + + +bool StringRecognizer::RecognizeImportCallback( + void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result) +{ + StringRecognizer* recognizer = (StringRecognizer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + auto str = recognizer->RecognizeImport(instr, typeObj, val); + if (!str.has_value()) + return false; + *result = str->ToAPIObject(true); + return true; +} + + +Ref<StringRecognizer> StringRecognizer::GetByName(const std::string& name) +{ + BNStringRecognizer* recognizer = BNGetStringRecognizerByName(name.c_str()); + if (!recognizer) + return nullptr; + return new CoreStringRecognizer(recognizer); +} + + +vector<Ref<StringRecognizer>> StringRecognizer::GetRecognizers() +{ + size_t count = 0; + BNStringRecognizer** recognizers = BNGetStringRecognizerList(&count); + + vector<Ref<StringRecognizer>> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreStringRecognizer(recognizers[i])); + + BNFreeStringRecognizerList(recognizers); + return result; +} + + +CoreStringRecognizer::CoreStringRecognizer(BNStringRecognizer* recognizer): + StringRecognizer(recognizer) +{ +} + + +bool CoreStringRecognizer::IsValidForType(HighLevelILFunction* func, Type* type) +{ + return BNIsStringRecognizerValidForType(m_object, func->GetObject(), type->GetObject()); +} + + +std::optional<DerivedString> CoreStringRecognizer::RecognizeConstant( + const HighLevelILInstruction& instr, Type* type, int64_t val) +{ + BNDerivedString str; + if (!BNStringRecognizerRecognizeConstant(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, &str)) + return std::nullopt; + return DerivedString::FromAPIObject(&str, true); +} + + +std::optional<DerivedString> CoreStringRecognizer::RecognizeConstantPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val) +{ + BNDerivedString str; + if (!BNStringRecognizerRecognizeConstantPointer(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, &str)) + return std::nullopt; + return DerivedString::FromAPIObject(&str, true); +} + + +std::optional<DerivedString> CoreStringRecognizer::RecognizeExternPointer( + const HighLevelILInstruction& instr, Type* type, int64_t val, uint64_t offset) +{ + BNDerivedString str; + if (!BNStringRecognizerRecognizeExternPointer(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, offset, &str)) + return std::nullopt; + return DerivedString::FromAPIObject(&str, true); +} + + +std::optional<DerivedString> CoreStringRecognizer::RecognizeImport( + const HighLevelILInstruction& instr, Type* type, int64_t val) +{ + BNDerivedString str; + if (!BNStringRecognizerRecognizeImport(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, &str)) + return std::nullopt; + return DerivedString::FromAPIObject(&str, true); +} diff --git a/ui/stringsview.h b/ui/stringsview.h index 800e25c5..99c66b79 100644 --- a/ui/stringsview.h +++ b/ui/stringsview.h @@ -79,6 +79,8 @@ class BINARYNINJAUIAPI StringsListModel : public QAbstractItemModel, public Bina virtual void OnStringFound(BinaryNinja::BinaryView* data, BNStringType type, uint64_t offset, size_t len) override; virtual void OnStringRemoved(BinaryNinja::BinaryView* data, BNStringType type, uint64_t offset, size_t len) override; + virtual void OnDerivedStringFound(BinaryNinja::BinaryView* data, const BinaryNinja::DerivedString& str) override; + virtual void OnDerivedStringRemoved(BinaryNinja::BinaryView* data, const BinaryNinja::DerivedString& str) override; void updateStrings(); virtual void sort(int col, Qt::SortOrder order) override; |
