diff --git a/MessageHandler.cpp b/MessageHandler.cpp index 5c3422b..2909452 100644 --- a/MessageHandler.cpp +++ b/MessageHandler.cpp @@ -2,17 +2,90 @@ using namespace BinaryNinja; +const std::set arcFunctionNames = { + "_objc_retain", + "_objc_release", + "_objc_autorelease", + "_objc_autoreleaseReturnValue", + "_objc_retainAutoreleasedReturnValue", + "_objc_unsafeClaimAutoreleasedReturnValue" +}; + MessageHandler::MessageHandler(Ref data) + : m_data(data) { + m_shouldCleanupARCCode = BinaryNinja::Settings::Instance()->Get("workflows.objectiveC.cleanupARCCode"); + + std::unique_lock lock(m_stubMutex); + + m_authStubsSection = data->GetSectionByName("__auth_stubs"); + m_stubsSection = data->GetSectionByName("__stubs"); + + if (!m_authStubsSection && !m_stubsSection) + m_readyForRealAnalysisPass = true; + else + data->RegisterNotification(this); + m_msgSendFunctions = findMsgSendFunctions(data); + m_arcFunctions = findARCFunctions(data); +} + +void MessageHandler::OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) +{ + std::unique_lock lock(m_stubMutex); + + if (m_readyForRealAnalysisPass) + return; + if (sym->GetType() != ImportedFunctionSymbol) + return; + if (!m_presentRequiredStubs.count(sym->GetShortName())) + return; + + m_locatedRequiredStubs.insert(sym->GetShortName()); + if (arcFunctionNames.count(sym->GetShortName())) + m_arcFunctions.insert(sym->GetAddress()); + else if (sym->GetShortName() == "_objc_msgSend") { + auto f = m_data->GetAnalysisFunction(m_data->GetDefaultPlatform(), sym->GetAddress()); + if (!f) + return; + auto retType = BinaryNinja::Confidence>( + BinaryNinja::Type::PointerType(m_data->GetAddressSize(), BinaryNinja::Type::VoidType(), + 0)); + + std::vector params; + auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention(); + + params.push_back({ "self", + BinaryNinja::Type::NamedType(m_data, { "id" }), + true, + BinaryNinja::Variable() }); + params.push_back({ "sel", + BinaryNinja::Type::PointerType(m_data->GetAddressSize(), BinaryNinja::Type::IntegerType(1, false)), + true, + BinaryNinja::Variable() }); + + auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params, true); + f->SetUserType(funcType); + + m_msgSendFunctions.insert(f->GetStart()); + } + + if (m_locatedRequiredStubs.size() == m_presentRequiredStubs.size()) { + m_readyForRealAnalysisPass = true; + + for (auto fAddr : m_reAnalysisRequiredFunctions) + for (auto f : m_data->GetAnalysisFunctionsForAddress(fAddr)) + f->Reanalyze(); + + m_data->UnregisterNotification(this); + } } std::set MessageHandler::findMsgSendFunctions(BinaryNinja::Ref data) { - std::set results; + std::unique_lock lock(m_stubMutex); - const auto authStubsSection = data->GetSectionByName("__auth_stubs"); - const auto stubsSection = data->GetSectionByName("__stubs"); + std::set results; const auto authGotSection = data->GetSectionByName("__auth_got"); const auto gotSection = data->GetSectionByName("__got"); const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr"); @@ -26,30 +99,60 @@ std::set MessageHandler::findMsgSendFunctions(BinaryNinja::RefGetSymbolsByName("_objc_msgSend"); for (const auto& c : candidates) { - if ((authStubsSection && sectionContains(authStubsSection, c)) - || (stubsSection && sectionContains(stubsSection, c)) - || (authGotSection && sectionContains(authGotSection, c)) + if ((authGotSection && sectionContains(authGotSection, c)) + || (gotSection && sectionContains(gotSection, c)) + || (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) { + results.insert(c->GetAddress()); + } + if ((m_authStubsSection && sectionContains(m_authStubsSection, c)) + || (m_stubsSection && sectionContains(m_stubsSection, c))) { + results.insert(c->GetAddress()); + m_locatedRequiredStubs.insert(c->GetShortName()); + } + } + m_presentRequiredStubs.insert("_objc_msgSend"); + + return results; +} + +std::set MessageHandler::findARCFunctions(BinaryNinja::Ref data) +{ + std::unique_lock lock(m_stubMutex); + + std::set results; + const auto authGotSection = data->GetSectionByName("__auth_got"); + const auto gotSection = data->GetSectionByName("__got"); + const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr"); + + // Shorthand to check if a symbol lies in a given section. + auto sectionContains = [](Ref
section, Ref symbol) { + const auto start = section->GetStart(); + const auto length = section->GetLength(); + const auto address = symbol->GetAddress(); + + return (uint64_t)(address - start) <= length; + }; + + std::vector> candidates; + std::vector> next; + for (auto name : arcFunctionNames) { + next = data->GetSymbolsByName(name); + candidates.insert(candidates.end(), next.begin(), next.end()); + } + for (auto& c : candidates) { + if ((authGotSection && sectionContains(authGotSection, c)) || (gotSection && sectionContains(gotSection, c)) || (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) { + { + results.insert(c->GetAddress()); + m_presentRequiredStubs.insert(c->GetShortName()); + } + } + if ((m_authStubsSection && sectionContains(m_authStubsSection, c)) + || (m_stubsSection && sectionContains(m_stubsSection, c))) { + m_locatedRequiredStubs.insert(c->GetShortName()); results.insert(c->GetAddress()); } } @@ -57,7 +160,34 @@ std::set MessageHandler::findMsgSendFunctions(BinaryNinja::Ref lock(m_reAnalysisRequiredFunctionsMutex); + m_reAnalysisRequiredFunctions.insert(addr); + } +} + bool MessageHandler::isMessageSend(uint64_t functionAddress) { + std::unique_lock lock(m_stubMutex); return m_msgSendFunctions.count(functionAddress); } + +bool MessageHandler::isARCFunction(uint64_t functionAddress) +{ + std::unique_lock lock(m_stubMutex); + return m_arcFunctions.count(functionAddress); +} + +bool MessageHandler::isFunctionLocatedInStubSection(uint64_t addr) +{ + if (m_stubsSection && m_stubsSection->GetStart() < addr + && m_stubsSection->GetStart() + m_stubsSection->GetLength() > addr) + return true; + else if (m_authStubsSection && m_authStubsSection->GetStart() < addr + && m_authStubsSection->GetStart() + m_authStubsSection->GetLength() > addr) + return true; + + return false; +} \ No newline at end of file diff --git a/MessageHandler.h b/MessageHandler.h index 8826b09..abd790a 100644 --- a/MessageHandler.h +++ b/MessageHandler.h @@ -2,15 +2,43 @@ #include -class MessageHandler { +class MessageHandler : public BinaryNinja::BinaryDataNotification { + + BinaryNinja::Ref m_data; + bool m_shouldCleanupARCCode; + + BinaryNinja::Ref m_authStubsSection; + BinaryNinja::Ref m_stubsSection; + + bool m_readyForRealAnalysisPass = false; + std::recursive_mutex m_stubMutex; + std::set m_presentRequiredStubs; + std::set m_locatedRequiredStubs; + + std::mutex m_reAnalysisRequiredFunctionsMutex; + std::set m_reAnalysisRequiredFunctions; std::set m_msgSendFunctions; - static std::set findMsgSendFunctions(BinaryNinja::Ref data); + std::set m_checkedNonMsgSendFunctions; + std::set m_arcFunctions; + std::set m_checkedNonARCFunctions; + + std::set findMsgSendFunctions(BinaryNinja::Ref data); + std::set findARCFunctions(BinaryNinja::Ref data); + + virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; public: MessageHandler(BinaryNinja::Ref data); + void functionWasAnalyzed(uint64_t addr); + + bool ShouldCleanupARCCode() const { return m_shouldCleanupARCCode; } + std::set getMessageSendFunctions() const { return m_msgSendFunctions; } bool hasMessageSendFunctions() const { return m_msgSendFunctions.size() != 0; } bool isMessageSend(uint64_t); + bool isARCFunction(uint64_t); + + bool isFunctionLocatedInStubSection(uint64_t); }; diff --git a/Plugin.cpp b/Plugin.cpp index ba04fda..1638293 100644 --- a/Plugin.cpp +++ b/Plugin.cpp @@ -31,6 +31,18 @@ BINARYNINJAPLUGIN bool CorePluginInit() Workflow::registerActivities(); Commands::registerCommands(); + BinaryNinja::Ref settings = BinaryNinja::Settings::Instance(); + settings->RegisterGroup("objc", "Objective-C"); + + settings->RegisterSetting("workflows.objectiveC.cleanupARCCode", + R"({ + "title" : "Hide ARC Calls", + "type" : "boolean", + "default" : true, + "description" : "Remove ARC-related code, e.g. calls to _objc_release, _objc_retain, and other ARC functions, in ILs" + })"); + + std::vector> targets = { BinaryNinja::Architecture::GetByName("aarch64"), BinaryNinja::Architecture::GetByName("x86_64") diff --git a/Workflow.cpp b/Workflow.cpp index 4fb2932..5839706 100644 --- a/Workflow.cpp +++ b/Workflow.cpp @@ -7,12 +7,12 @@ #include "Workflow.h" +#include "ArchitectureHooks.h" #include "Constants.h" #include "CustomTypes.h" #include "GlobalState.h" #include "InfoHandler.h" #include "Performance.h" -#include "ArchitectureHooks.h" #include "Core/AnalysisProvider.h" #include "Core/BinaryViewFile.h" @@ -88,7 +88,7 @@ void Workflow::rewriteCFString(LLILFunctionRef ssa, size_t insnIndex) bv->Read(&dest, stringPointer, bv->GetDefaultArchitecture()->GetAddressSize()); auto targetPointer = llil->ConstPointer(bv->GetAddressSize(), dest, llilInsn); - auto cfstrCall = llil->Intrinsic({ BinaryNinja::RegisterOrFlag(0, destRegister) }, CFSTRIntrinsicIndex, {targetPointer}, 0, llilInsn); + auto cfstrCall = llil->Intrinsic({ BinaryNinja::RegisterOrFlag(0, destRegister) }, CFSTRIntrinsicIndex, { targetPointer }, 0, llilInsn); llilInsn.Replace(cfstrCall); @@ -151,28 +151,27 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) InfoHandler::applyInfoToView(info, bv); const auto msgSendFunctions = messageHandler->getMessageSendFunctions(); - for (auto addr : msgSendFunctions) - { + for (auto addr : msgSendFunctions) { BinaryNinja::QualifiedNameAndType nameAndType; std::string errors; std::set typesAllowRedefinition; // void * auto retType = BinaryNinja::Confidence>( - BinaryNinja::Type::PointerType(bv->GetAddressSize(),BinaryNinja::Type::VoidType(), + BinaryNinja::Type::PointerType(bv->GetAddressSize(), BinaryNinja::Type::VoidType(), 0)); std::vector params; auto cc = bv->GetDefaultPlatform()->GetDefaultCallingConvention(); - params.push_back({"self", - BinaryNinja::Type::NamedType(bv, {"id"}), + params.push_back({ "self", + BinaryNinja::Type::NamedType(bv, { "id" }), true, - BinaryNinja::Variable()}); - params.push_back({"sel", + BinaryNinja::Variable() }); + params.push_back({ "sel", BinaryNinja::Type::PointerType(bv->GetAddressSize(), BinaryNinja::Type::IntegerType(1, false)), true, - BinaryNinja::Variable()}); + BinaryNinja::Variable() }); auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params, true); bv->DefineDataVariable(addr, BinaryNinja::Type::PointerType(bv->GetDefaultArchitecture(), funcType)); @@ -186,8 +185,8 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) GlobalState::storeAnalysisInfo(bv, info); } } - auto messageHandler = GlobalState::messageHandler(bv); + if (!messageHandler->hasMessageSendFunctions()) { log->LogError("Cannot perform Objective-C IL cleanup; no objc_msgSend candidates found"); GlobalState::addIgnoredView(bv); @@ -205,30 +204,40 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) return; } - const auto rewriteIfEligible = [bv, messageHandler, ssa](size_t insnIndex) { + const auto rewriteIfEligible = [bv, llil, messageHandler, ssa](size_t insnIndex) { auto insn = ssa->GetInstruction(insnIndex); - if (insn.operation == LLIL_CALL_SSA) - { + if (insn.operation == LLIL_CALL_SSA + || (insn.operation == LLIL_JUMP && insnIndex == ssa->GetInstructionCount() - 1) + || insn.operation == LLIL_TAILCALL_SSA) { // Filter out calls that aren't to `objc_msgSend`. - auto callExpr = insn.GetDestExpr(); - if (!messageHandler->isMessageSend(callExpr.GetValue().value)) - return; + auto callExpr = insn.GetDestExpr(); + if (insn.operation == LLIL_CALL_SSA + && messageHandler->isMessageSend(callExpr.GetValue().value)) { + auto params = insn.GetParameterExprs(); + if (params.size() >= 2 + && params[0].operation == LLIL_REG_SSA + && params[1].operation == LLIL_REG_SSA) + rewriteMethodCall(ssa, insnIndex); + } else if (messageHandler->isARCFunction(callExpr.GetValue().value) && messageHandler->ShouldCleanupARCCode()) { + auto nonSSAIdx = ssa->GetNonSSAInstructionIndex(insnIndex); + auto targetInsn = llil->GetInstruction(nonSSAIdx); + if (insn.operation == LLIL_CALL_SSA) + targetInsn.Replace(llil->Nop(targetInsn)); + else // Other two categories. TailCall or Jump that is last instruction (so, tailcall...) + { + auto lr = llil->GetArchitecture()->GetLinkRegister(); + auto lrInfo = llil->GetArchitecture()->GetRegisterInfo(lr); - // By convention, the selector is the second argument to `objc_msgSend`, - // therefore two parameters are required for a proper rewrite; abort if - // this condition is not met. - auto params = insn.GetParameterExprs(); - if (params.size() < 2 - || params[0].operation != LLIL_REG_SSA - || params[1].operation != LLIL_REG_SSA) + targetInsn.Replace(llil->Return(llil->Register(lrInfo.size, lr, targetInsn), targetInsn)); + } + llil->GenerateSSAForm(); + llil->Finalize(); return; - - rewriteMethodCall(ssa, insnIndex); - - } - else if (insn.operation == LLIL_SET_REG_SSA) - { + } + if (messageHandler->isFunctionLocatedInStubSection(callExpr.GetValue().value)) + messageHandler->functionWasAnalyzed(llil->GetFunction()->GetStart()); + } else if (insn.operation == LLIL_SET_REG_SSA) { auto sourceExpr = insn.GetSourceExpr(); auto addr = sourceExpr.GetValue().value; BinaryNinja::DataVariable var;