From bdc48d9c6d55eb57c3aef67336ca67174f48c8f4 Mon Sep 17 00:00:00 2001 From: Alexander Khosrowshahi Date: Mon, 2 Jun 2025 17:35:28 -0400 Subject: [PATCH 1/5] Fix calls to BNFreeString in kernel cache API Amends calls to BNFreeString in kernel cache API that were causing `get_macho_header_for_address`, `get_macho_header_for_image`, `get_image_name_for_address`, and `get_name_for_address` to fail. Fixes https://github.com/Vector35/binaryninja-api/issues/6855 --- view/kernelcache/api/kernelcache.cpp | 10 ++++------ view/kernelcache/api/kernelcacheapi.h | 4 ++-- view/kernelcache/api/kernelcachecore.h | 4 ++-- .../api/python/_kernelcachecore_template.py | 1 + view/kernelcache/api/python/kernelcache.py | 15 +++------------ view/kernelcache/core/KernelCache.cpp | 7 ++----- view/kernelcache/ui/kctriage.cpp | 2 +- 7 files changed, 15 insertions(+), 28 deletions(-) diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp index 86cd8e0d98..7a650660af 100644 --- a/view/kernelcache/api/kernelcache.cpp +++ b/view/kernelcache/api/kernelcache.cpp @@ -20,10 +20,9 @@ namespace KernelCacheAPI { return BNKCViewFastGetImageCount(view->GetObject()); } - bool KernelCache::LoadImageWithInstallName(std::string installName) + bool KernelCache::LoadImageWithInstallName(const std::string& installName) { - char* str = BNAllocString(installName.c_str()); - return BNKCViewLoadImageWithInstallName(m_object, str); + return BNKCViewLoadImageWithInstallName(m_object, installName.c_str()); } bool KernelCache::LoadImageContainingAddress(uint64_t addr) @@ -161,10 +160,9 @@ namespace KernelCacheAPI { return result; } - std::optional KernelCache::GetMachOHeaderForImage(std::string name) + std::optional KernelCache::GetMachOHeaderForImage(const std::string& name) { - char* str = BNAllocString(name.c_str()); - char* outputStr = BNKCViewGetImageHeaderForName(m_object, str); + char* outputStr = BNKCViewGetImageHeaderForName(m_object, name.c_str()); if (outputStr == nullptr) return {}; std::string output = outputStr; diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h index a4462cb075..35c53d78ba 100644 --- a/view/kernelcache/api/kernelcacheapi.h +++ b/view/kernelcache/api/kernelcacheapi.h @@ -254,7 +254,7 @@ namespace KernelCacheAPI { static BNKCViewLoadProgress GetLoadProgress(Ref view); static uint64_t FastGetImageCount(Ref view); - bool LoadImageWithInstallName(std::string installName); + bool LoadImageWithInstallName(const std::string& installName); bool LoadImageContainingAddress(uint64_t addr); std::vector GetAvailableImages(); @@ -268,7 +268,7 @@ namespace KernelCacheAPI { std::vector GetImages(); std::vector GetLoadedImages(); - std::optional GetMachOHeaderForImage(std::string name); + std::optional GetMachOHeaderForImage(const std::string& name); std::optional GetMachOHeaderForAddress(uint64_t address); }; } \ No newline at end of file diff --git a/view/kernelcache/api/kernelcachecore.h b/view/kernelcache/api/kernelcachecore.h index be274a6c77..2f158131d8 100644 --- a/view/kernelcache/api/kernelcachecore.h +++ b/view/kernelcache/api/kernelcachecore.h @@ -106,7 +106,7 @@ extern "C" KERNELCACHE_FFI_API char** BNKCViewGetInstallNames(BNKernelCache* cache, size_t* count); - KERNELCACHE_FFI_API bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, char* name); + KERNELCACHE_FFI_API bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, const char* name); KERNELCACHE_FFI_API bool BNKCViewLoadImageContainingAddress(BNKernelCache* cache, uint64_t address); KERNELCACHE_FFI_API bool BNKCViewIsImageLoaded(BNKernelCache* cache, uint64_t address); @@ -128,7 +128,7 @@ extern "C" KERNELCACHE_FFI_API void BNKCViewFreeLoadedImages(BNKCImage* images, size_t count); KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForAddress(BNKernelCache* cache, uint64_t address); - KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, char* name); + KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, const char* name); #ifdef __cplusplus } diff --git a/view/kernelcache/api/python/_kernelcachecore_template.py b/view/kernelcache/api/python/_kernelcachecore_template.py index ecbcb35db2..cac7eb4299 100644 --- a/view/kernelcache/api/python/_kernelcachecore_template.py +++ b/view/kernelcache/api/python/_kernelcachecore_template.py @@ -8,6 +8,7 @@ core = None core_platform = platform.system() +from binaryninja._binaryninjacore import BNFreeString, BNFreeStringList, BNAllocString from binaryninja import Settings if Settings().get_bool("corePlugins.view.kernelCache"): from binaryninja._binaryninjacore import BNGetBundledPluginDirectory diff --git a/view/kernelcache/api/python/kernelcache.py b/view/kernelcache/api/python/kernelcache.py index 2148f2a8f9..9425ef9c83 100644 --- a/view/kernelcache/api/python/kernelcache.py +++ b/view/kernelcache/api/python/kernelcache.py @@ -207,15 +207,10 @@ def get_macho_header_for_image(self, name: str): """ Return a KernelCacheMachOHeader for the image with the given install name. """ - s = BNAllocString(name) - outputStr = kccore.BNKCViewGetImageHeaderForName(self.handle, s) + outputStr = kccore.BNKCViewGetImageHeaderForName(self.handle, name) if outputStr is None: return None - output = outputStr - BNFreeString(outputStr) - if output == "": - return None - return KernelCacheMachOHeader.LoadFromString(output) + return KernelCacheMachOHeader.LoadFromString(outputStr) def get_macho_header_for_address(self, address: int): """ @@ -224,11 +219,7 @@ def get_macho_header_for_address(self, address: int): outputStr = kccore.BNKCViewGetImageHeaderForAddress(self.handle, address) if outputStr is None: return None - output = outputStr - BNFreeString(outputStr) - if output == "": - return None - return KernelCacheMachOHeader.LoadFromString(output) + return KernelCacheMachOHeader.LoadFromString(outputStr) @property def state(self): diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index be6a4eb64b..6cb3fca6fd 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -2145,11 +2145,9 @@ extern "C" cache->object->ReleaseAPIRef(); } - bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, char* name) + bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, const char* name) { std::string imageName = std::string(name); - // FIXME !!!!!!!! BNFreeString(name); - if (cache->object) return cache->object->LoadImageWithInstallName(imageName); @@ -2321,10 +2319,9 @@ extern "C" return nullptr; } - char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, char* name) + char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, const char* name) { std::string imageName = std::string(name); - BNFreeString(name); if (cache->object) { auto header = cache->object->SerializedImageHeaderForName(imageName); diff --git a/view/kernelcache/ui/kctriage.cpp b/view/kernelcache/ui/kctriage.cpp index 5103bc518f..bac39b5eec 100644 --- a/view/kernelcache/ui/kctriage.cpp +++ b/view/kernelcache/ui/kctriage.cpp @@ -93,7 +93,7 @@ void KCTriageView::loadImagesWithAddr(const std::vector& addresses) { { if (imageLoadTask.IsCancelled()) break; - std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages++, images.size()); + const std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages++, images.size()); imageLoadTask.SetProgressText(newLoad); if (m_cache->LoadImageWithInstallName(imageName)) setImageLoaded(addr); From f4cc0f55ef7c40c7ee554844228af96fb4c902d3 Mon Sep 17 00:00:00 2001 From: Obriv Mostov Date: Thu, 3 Apr 2025 23:35:59 +0700 Subject: [PATCH 2/5] [MachoView] Add support of __init_offsets --- view/macho/machoview.cpp | 73 ++++++++++++++++++++++++++++------------ view/macho/machoview.h | 1 + 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index b832e1e5fe..cb38826ce7 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -448,7 +448,7 @@ MachOHeader MachoView::HeaderForAddress(BinaryView* data, uint64_t address, bool sect.flags, sect.reserved1, sect.reserved2); - if (!strncmp(sect.sectname, "__mod_init_func", 15)) + if (!strncmp(sect.sectname, "__mod_init_func", 15) || !strncmp(sect.sectname, "__init_offsets", 14)) header.moduleInitSections.push_back(sect); if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) header.symbolStubSections.push_back(sect); @@ -551,7 +551,7 @@ MachOHeader MachoView::HeaderForAddress(BinaryView* data, uint64_t address, bool sect.reserved1, sect.reserved2, sect.reserved3); - if (!strncmp(sect.sectname, "__mod_init_func", 15)) + if (!strncmp(sect.sectname, "__mod_init_func", 15) || !strncmp(sect.sectname, "__init_offsets", 14)) header.moduleInitSections.push_back(sect); if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) header.symbolStubSections.push_back(sect); @@ -1640,7 +1640,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ string type; BNSectionSemantics semantics = DefaultSectionSemantics; - switch (header.sections[i].flags & 0xff) + switch (header.sections[i].flags & SECTION_TYPE) { case S_REGULAR: if (header.sections[i].flags & S_ATTR_PURE_INSTRUCTIONS) @@ -1731,6 +1731,10 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS"; break; + case S_INIT_FUNC_OFFSETS: + type = "INIT_FUNC_OFFSETS"; + semantics = ReadOnlyDataSectionSemantics; + break; default: type = "UNKNOWN"; break; @@ -1897,30 +1901,55 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ if (find(threadStarts.begin(), threadStarts.end(), moduleInitSection.offset) != threadStarts.end()) continue; - // The mod_init section contains a list of function pointers called at initialization - // if we don't have a defined entrypoint then use the first one in the list as the entrypoint size_t i = 0; reader.Seek(moduleInitSection.offset); - for (; i < (moduleInitSection.size / m_addressSize); i++) + + if (!strncmp(moduleInitSection.sectname, "__mod_init_func", 15)) + { + // The mod_init section contains a list of function pointers called at initialization + // if we don't have a defined entrypoint then use the first one in the list as the entrypoint + for (; i < (moduleInitSection.size / m_addressSize); i++) + { + uint64_t target = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); + target += m_imageBaseAdjustment; + if (m_header.ident.filetype == MH_FILESET) + { + // FIXME: This isn't a super robust way of detagging, + // should look into xnu source and the tools used to build this cache (if they're public) + // and see if anything better can be done + + // mask out top 8 bits + uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; + // and combine them with bottom 8 of the original entry + target = tag | (target & 0xFFFFFFFF); + } + Ref targetPlatform = GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); + auto name = "mod_init_func_" + to_string(modInitFuncCnt++); + AddEntryPointForAnalysis(targetPlatform, target); + auto symbol = new Symbol(FunctionSymbol, name, target, GlobalBinding); + DefineAutoSymbol(symbol); + } + } + else if (!strncmp(moduleInitSection.sectname, "__init_offsets", 14)) { - uint64_t target = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); - target += m_imageBaseAdjustment; - if (m_header.ident.filetype == MH_FILESET) + // The init_offsets section contains a list of 32-bit RVA offsets to functions called at initialization + // if we don't have a defined entrypoint then use the first one in the list as the entrypoint + for (; i < (moduleInitSection.size / 4); i++) { - // FIXME: This isn't a super robust way of detagging, - // should look into xnu source and the tools used to build this cache (if they're public) - // and see if anything better can be done - - // mask out top 8 bits - uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; - // and combine them with bottom 8 of the original entry - target = tag | (target & 0xFFFFFFFF); + uint64_t target = reader.Read32(); + target += header.textBase; + Ref targetPlatform = GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); + auto name = "mod_init_func_" + to_string(modInitFuncCnt++); + AddEntryPointForAnalysis(targetPlatform, target); + auto symbol = new Symbol(FunctionSymbol, name, target, GlobalBinding); + DefineAutoSymbol(symbol); + + // FIXME: i don't know how to define proper pointer type at this stage of analysis + Ref pointerVar = TypeBuilder::PointerType(4, Type::VoidType()) + .SetPointerBase(RelativeToConstantPointerBaseType, header.textBase) + .Finalize(); + DefineDataVariable(GetStart() + reader.GetOffset() - 4, pointerVar); } - Ref targetPlatform = GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); - auto name = "mod_init_func_" + to_string(modInitFuncCnt++); - AddEntryPointForAnalysis(targetPlatform, target); - auto symbol = new Symbol(FunctionSymbol, name, target, GlobalBinding); - DefineAutoSymbol(symbol); } } diff --git a/view/macho/machoview.h b/view/macho/machoview.h index 3081ff134a..1a71599196 100644 --- a/view/macho/machoview.h +++ b/view/macho/machoview.h @@ -144,6 +144,7 @@ typedef int vm_prot_t; #define S_THREAD_LOCAL_VARIABLES 0x13 #define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 #define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 +#define S_INIT_FUNC_OFFSETS 0x16 //Mach-O Commands #define LC_REQ_DYLD 0x80000000 From beecca23117289f0430b7c134c5d554f1a4274eb Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Thu, 8 May 2025 10:34:13 -0700 Subject: [PATCH 3/5] [ObjC] Add an explicit reference to a method impl from its selector This makes it easier to see the possible message send targets when looking at a call to `objc_msgSend`. --- objectivec/objc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/objectivec/objc.cpp b/objectivec/objc.cpp index a57f2c7319..014d805761 100644 --- a/objectivec/objc.cpp +++ b/objectivec/objc.cpp @@ -955,6 +955,7 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri DefineObjCSymbol(DataSymbol, Type::PointerType(m_data->GetAddressSize(), selType), "selRef_" + method.name, meth.name, true); } + // workflow objc support if (selAddr) m_selToImplementations[selAddr].push_back(meth.imp); @@ -967,6 +968,11 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri method.imp = meth.imp; cls.methodList[cursor] = method; m_localMethods[cursor] = method; + + if (selAddr) + m_data->AddUserDataReference(selAddr, meth.imp); + if (selRefAddr) + m_data->AddUserDataReference(selRefAddr, meth.imp); } catch (...) { From d5c635ecfd1013b033313e41f73d5aeeea02c085 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Fri, 30 May 2025 14:21:14 -0600 Subject: [PATCH 4/5] Add UI action and API to flip conditions during HLIL restructuring --- binaryninjaapi.h | 3 +++ binaryninjacore.h | 7 ++++++- function.cpp | 12 ++++++++++++ python/function.py | 10 ++++++++++ ui/commands.h | 1 + ui/flowgraphwidget.h | 3 +++ ui/linearview.h | 3 +++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 6d5c144cf8..0d4533b580 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -11430,6 +11430,9 @@ namespace BinaryNinja { BNExprFolding GetExprFolding(uint64_t addr); void SetExprFolding(uint64_t addr, BNExprFolding mode); + bool IsConditionInverted(uint64_t addr); + void SetConditionInverted(uint64_t addr, bool invert); + std::map> GetMergedVariables(); void MergeVariables(const Variable& target, const std::set& sources); void UnmergeVariables(const Variable& target, const std::set& sources); diff --git a/binaryninjacore.h b/binaryninjacore.h index 505e10e2f3..9daf70e476 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,7 +37,7 @@ // 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 108 +#define BN_CURRENT_CORE_ABI_VERSION 109 // 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 @@ -1024,6 +1024,9 @@ extern "C" // HLIL expression can be folded into other expressions or has been folded HLILFoldableExpr = 0x100, + + // HLIL condition can be displayed as the inverse + HLILInvertableCondition = 0x200, } BNILInstructionAttribute; typedef enum BNIntrinsicClass @@ -4993,6 +4996,8 @@ extern "C" BNFunction* func, const BNVariable* var, BNDeadStoreElimination mode); BINARYNINJACOREAPI BNExprFolding BNGetExprFolding(BNFunction* func, uint64_t addr); BINARYNINJACOREAPI void BNSetExprFolding(BNFunction* func, uint64_t addr, BNExprFolding mode); + BINARYNINJACOREAPI bool BNIsConditionInverted(BNFunction* func, uint64_t addr); + BINARYNINJACOREAPI void BNSetConditionInverted(BNFunction* func, uint64_t addr, bool invert); BINARYNINJACOREAPI BNMergedVariable* BNGetMergedVariables(BNFunction* func, size_t* count); BINARYNINJACOREAPI void BNFreeMergedVariableList(BNMergedVariable* vars, size_t count); BINARYNINJACOREAPI void BNMergeVariables(BNFunction* func, const BNVariable* target, const BNVariable* sources, diff --git a/function.cpp b/function.cpp index 756c9629c1..fec9e108ce 100644 --- a/function.cpp +++ b/function.cpp @@ -2825,6 +2825,18 @@ void Function::SetExprFolding(uint64_t addr, BNExprFolding mode) } +bool Function::IsConditionInverted(uint64_t addr) +{ + return BNIsConditionInverted(m_object, addr); +} + + +void Function::SetConditionInverted(uint64_t addr, bool invert) +{ + BNSetConditionInverted(m_object, addr, invert); +} + + std::map> Function::GetMergedVariables() { size_t count; diff --git a/python/function.py b/python/function.py index dc7add3a0d..ae3af65b48 100644 --- a/python/function.py +++ b/python/function.py @@ -3466,6 +3466,16 @@ def set_expr_folding(self, addr: Union[int, highlevelil.HighLevelILInstruction], addr = addr.address core.BNSetExprFolding(self.handle, addr, value) + def is_condition_inverted(self, addr: Union[int, highlevelil.HighLevelILInstruction]) -> bool: + if isinstance(addr, highlevelil.HighLevelILInstruction): + addr = addr.address + return core.BNIsConditionInverted(self.handle, addr) + + def set_condition_inverted(self, addr: Union[int, highlevelil.HighLevelILInstruction], invert: bool): + if isinstance(addr, highlevelil.HighLevelILInstruction): + addr = addr.address + core.BNSetConditionInverted(self.handle, addr, invert) + class AdvancedFunctionAnalysisDataRequestor: def __init__(self, func: Optional['Function'] = None): diff --git a/ui/commands.h b/ui/commands.h index 7fb3f5394e..aed0fbf65a 100644 --- a/ui/commands.h +++ b/ui/commands.h @@ -97,6 +97,7 @@ TypeRef GetFunctionType(BinaryViewRef data, TypeRef type); std::optional getFoldableExprAddress( BinaryNinja::HighLevelILFunction* hlil, const HighlightTokenState& highlight); +std::optional getInvertableConditionAddress(BinaryNinja::HighLevelILFunction* hlil, size_t instrIndex); /*! @} diff --git a/ui/flowgraphwidget.h b/ui/flowgraphwidget.h index c7af8aa93c..b69ec38b6b 100644 --- a/ui/flowgraphwidget.h +++ b/ui/flowgraphwidget.h @@ -168,6 +168,8 @@ class BINARYNINJAUIAPI FlowGraphWidget : BNDeadStoreElimination getCurrentVariableDeadStoreElimination(); std::optional getCurrentFoldableExprAddress(); BNExprFolding getCurrentExprFolding(); + std::optional getCurrentInvertableConditionAddress(); + bool getCurrentConditionInverted(); std::optional> getMergeVariablesAtCurrentLocation(); protected: @@ -398,6 +400,7 @@ class BINARYNINJAUIAPI FlowGraphWidget : void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination); void setCurrentExprFolding(BNExprFolding folding); + void toggleConditionInverted(); void splitToNewTabAndNavigateFromCursorPosition(); void splitToNewWindowAndNavigateFromCursorPosition(); void splitToNewPaneAndNavigateFromCursorPosition(); diff --git a/ui/linearview.h b/ui/linearview.h index e5305aa8bd..2ea8e37fcf 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -318,6 +318,8 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub BNDeadStoreElimination getCurrentVariableDeadStoreElimination(); std::optional getCurrentFoldableExprAddress(); BNExprFolding getCurrentExprFolding(); + std::optional getCurrentInvertableConditionAddress(); + bool getCurrentConditionInverted(); void setDataButtonVisible(bool visible); std::optional> getMergeVariablesAtCurrentLocation(); @@ -409,6 +411,7 @@ private Q_SLOTS: void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination); void setCurrentExprFolding(BNExprFolding folding); + void toggleConditionInverted(); Q_SIGNALS: void notifyResizeEvent(int width, int height); From 878d5d734a66aaa4d586ccb2cf3fa18616965b3c Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 3 Jun 2025 13:42:04 -0600 Subject: [PATCH 5/5] Add UI action and API to control early returns in HLIL --- binaryninjaapi.h | 3 +++ binaryninjacore.h | 16 +++++++++++++++- function.cpp | 12 ++++++++++++ python/function.py | 12 +++++++++++- ui/commands.h | 1 + ui/flowgraphwidget.h | 3 +++ ui/linearview.h | 3 +++ 7 files changed, 48 insertions(+), 2 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 0d4533b580..6c9f85142a 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -11433,6 +11433,9 @@ namespace BinaryNinja { bool IsConditionInverted(uint64_t addr); void SetConditionInverted(uint64_t addr, bool invert); + BNEarlyReturn GetEarlyReturn(uint64_t addr); + void SetEarlyReturn(uint64_t addr, BNEarlyReturn mode); + std::map> GetMergedVariables(); void MergeVariables(const Variable& target, const std::set& sources); void UnmergeVariables(const Variable& target, const std::set& sources); diff --git a/binaryninjacore.h b/binaryninjacore.h index 9daf70e476..d36c1767cc 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,7 +37,7 @@ // 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 109 +#define BN_CURRENT_CORE_ABI_VERSION 110 // 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 @@ -1027,6 +1027,9 @@ extern "C" // HLIL condition can be displayed as the inverse HLILInvertableCondition = 0x200, + + // HLIL condition can be rewritten as an early return + HLILEarlyReturnPossible = 0x400, } BNILInstructionAttribute; typedef enum BNIntrinsicClass @@ -3241,6 +3244,15 @@ extern "C" AllowExprFolding } BNExprFolding; + typedef enum BNEarlyReturn + { + DefaultEarlyReturn, + PreventEarlyReturn, + SmallestSideEarlyReturn, + TrueSideEarlyReturn, + FalseSideEarlyReturn + } BNEarlyReturn; + typedef struct BNDebugFunctionInfo { char* shortName; @@ -4998,6 +5010,8 @@ extern "C" BINARYNINJACOREAPI void BNSetExprFolding(BNFunction* func, uint64_t addr, BNExprFolding mode); BINARYNINJACOREAPI bool BNIsConditionInverted(BNFunction* func, uint64_t addr); BINARYNINJACOREAPI void BNSetConditionInverted(BNFunction* func, uint64_t addr, bool invert); + BINARYNINJACOREAPI BNEarlyReturn BNGetEarlyReturn(BNFunction* func, uint64_t addr); + BINARYNINJACOREAPI void BNSetEarlyReturn(BNFunction* func, uint64_t addr, BNEarlyReturn mode); BINARYNINJACOREAPI BNMergedVariable* BNGetMergedVariables(BNFunction* func, size_t* count); BINARYNINJACOREAPI void BNFreeMergedVariableList(BNMergedVariable* vars, size_t count); BINARYNINJACOREAPI void BNMergeVariables(BNFunction* func, const BNVariable* target, const BNVariable* sources, diff --git a/function.cpp b/function.cpp index fec9e108ce..5ce3a8432b 100644 --- a/function.cpp +++ b/function.cpp @@ -2837,6 +2837,18 @@ void Function::SetConditionInverted(uint64_t addr, bool invert) } +BNEarlyReturn Function::GetEarlyReturn(uint64_t addr) +{ + return BNGetEarlyReturn(m_object, addr); +} + + +void Function::SetEarlyReturn(uint64_t addr, BNEarlyReturn mode) +{ + BNSetEarlyReturn(m_object, addr, mode); +} + + std::map> Function::GetMergedVariables() { size_t count; diff --git a/python/function.py b/python/function.py index ae3af65b48..afd9d5b725 100644 --- a/python/function.py +++ b/python/function.py @@ -29,7 +29,7 @@ from .enums import ( AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType, - BuiltinType, ExprFolding + BuiltinType, ExprFolding, EarlyReturn ) from . import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore @@ -3476,6 +3476,16 @@ def set_condition_inverted(self, addr: Union[int, highlevelil.HighLevelILInstruc addr = addr.address core.BNSetConditionInverted(self.handle, addr, invert) + def get_early_return(self, addr: Union[int, highlevelil.HighLevelILInstruction]) -> EarlyReturn: + if isinstance(addr, highlevelil.HighLevelILInstruction): + addr = addr.address + return EarlyReturn(core.BNGetEarlyReturn(self.handle, addr)) + + def set_early_return(self, addr: Union[int, highlevelil.HighLevelILInstruction], value: EarlyReturn): + if isinstance(addr, highlevelil.HighLevelILInstruction): + addr = addr.address + core.BNSetEarlyReturn(self.handle, addr, value) + class AdvancedFunctionAnalysisDataRequestor: def __init__(self, func: Optional['Function'] = None): diff --git a/ui/commands.h b/ui/commands.h index aed0fbf65a..754d99a76f 100644 --- a/ui/commands.h +++ b/ui/commands.h @@ -98,6 +98,7 @@ TypeRef GetFunctionType(BinaryViewRef data, TypeRef type); std::optional getFoldableExprAddress( BinaryNinja::HighLevelILFunction* hlil, const HighlightTokenState& highlight); std::optional getInvertableConditionAddress(BinaryNinja::HighLevelILFunction* hlil, size_t instrIndex); +std::optional getEarlyReturnAddress(BinaryNinja::HighLevelILFunction* hlil, size_t instrIndex); /*! @} diff --git a/ui/flowgraphwidget.h b/ui/flowgraphwidget.h index b69ec38b6b..d988da1209 100644 --- a/ui/flowgraphwidget.h +++ b/ui/flowgraphwidget.h @@ -170,6 +170,8 @@ class BINARYNINJAUIAPI FlowGraphWidget : BNExprFolding getCurrentExprFolding(); std::optional getCurrentInvertableConditionAddress(); bool getCurrentConditionInverted(); + std::optional getCurrentEarlyReturnAddress(); + BNEarlyReturn getCurrentEarlyReturn(); std::optional> getMergeVariablesAtCurrentLocation(); protected: @@ -401,6 +403,7 @@ class BINARYNINJAUIAPI FlowGraphWidget : void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination); void setCurrentExprFolding(BNExprFolding folding); void toggleConditionInverted(); + void setCurrentEarlyReturn(BNEarlyReturn earlyReturn); void splitToNewTabAndNavigateFromCursorPosition(); void splitToNewWindowAndNavigateFromCursorPosition(); void splitToNewPaneAndNavigateFromCursorPosition(); diff --git a/ui/linearview.h b/ui/linearview.h index 2ea8e37fcf..eea40d7fa9 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -320,6 +320,8 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub BNExprFolding getCurrentExprFolding(); std::optional getCurrentInvertableConditionAddress(); bool getCurrentConditionInverted(); + std::optional getCurrentEarlyReturnAddress(); + BNEarlyReturn getCurrentEarlyReturn(); void setDataButtonVisible(bool visible); std::optional> getMergeVariablesAtCurrentLocation(); @@ -412,6 +414,7 @@ private Q_SLOTS: void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination); void setCurrentExprFolding(BNExprFolding folding); void toggleConditionInverted(); + void setCurrentEarlyReturn(BNEarlyReturn earlyReturn); Q_SIGNALS: void notifyResizeEvent(int width, int height);