diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 6d5c144cf8..6c9f85142a 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -11430,6 +11430,12 @@ 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); + + 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 505e10e2f3..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 108 +#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 @@ -1024,6 +1024,12 @@ 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, + + // HLIL condition can be rewritten as an early return + HLILEarlyReturnPossible = 0x400, } BNILInstructionAttribute; typedef enum BNIntrinsicClass @@ -3238,6 +3244,15 @@ extern "C" AllowExprFolding } BNExprFolding; + typedef enum BNEarlyReturn + { + DefaultEarlyReturn, + PreventEarlyReturn, + SmallestSideEarlyReturn, + TrueSideEarlyReturn, + FalseSideEarlyReturn + } BNEarlyReturn; + typedef struct BNDebugFunctionInfo { char* shortName; @@ -4993,6 +5008,10 @@ 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 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 756c9629c1..5ce3a8432b 100644 --- a/function.cpp +++ b/function.cpp @@ -2825,6 +2825,30 @@ 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); +} + + +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/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 (...) { diff --git a/python/function.py b/python/function.py index dc7add3a0d..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 @@ -3466,6 +3466,26 @@ 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) + + 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 7fb3f5394e..754d99a76f 100644 --- a/ui/commands.h +++ b/ui/commands.h @@ -97,6 +97,8 @@ 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 c7af8aa93c..d988da1209 100644 --- a/ui/flowgraphwidget.h +++ b/ui/flowgraphwidget.h @@ -168,6 +168,10 @@ class BINARYNINJAUIAPI FlowGraphWidget : BNDeadStoreElimination getCurrentVariableDeadStoreElimination(); std::optional getCurrentFoldableExprAddress(); BNExprFolding getCurrentExprFolding(); + std::optional getCurrentInvertableConditionAddress(); + bool getCurrentConditionInverted(); + std::optional getCurrentEarlyReturnAddress(); + BNEarlyReturn getCurrentEarlyReturn(); std::optional> getMergeVariablesAtCurrentLocation(); protected: @@ -398,6 +402,8 @@ 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 e5305aa8bd..eea40d7fa9 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -318,6 +318,10 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub BNDeadStoreElimination getCurrentVariableDeadStoreElimination(); std::optional getCurrentFoldableExprAddress(); BNExprFolding getCurrentExprFolding(); + std::optional getCurrentInvertableConditionAddress(); + bool getCurrentConditionInverted(); + std::optional getCurrentEarlyReturnAddress(); + BNEarlyReturn getCurrentEarlyReturn(); void setDataButtonVisible(bool visible); std::optional> getMergeVariablesAtCurrentLocation(); @@ -409,6 +413,8 @@ 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); 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); 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