diff --git a/MessageHandler.cpp b/MessageHandler.cpp index 5c3422b..7cd0a3f 100644 --- a/MessageHandler.cpp +++ b/MessageHandler.cpp @@ -1,18 +1,87 @@ #include "MessageHandler.h" +#include "mediumlevelilinstruction.h" 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_authStubsSection = data->GetSectionByName("__auth_stubs"); + m_stubsSection = data->GetSectionByName("__stubs"); + m_msgSendFunctions = findMsgSendFunctions(data); + m_arcFunctions = findARCFunctions(data); + + if (!m_authStubsSection && !m_stubsSection) + m_readyForRealAnalysisPass = true; + else + data->RegisterNotification(this); +} + +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::unique_lock lock(m_stubMutex); std::set results; - - const auto authStubsSection = data->GetSectionByName("__auth_stubs"); - const auto stubsSection = data->GetSectionByName("__stubs"); const auto authGotSection = data->GetSectionByName("__auth_got"); const auto gotSection = data->GetSectionByName("__got"); const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr"); @@ -26,38 +95,95 @@ std::set MessageHandler::findMsgSendFunctions(BinaryNinja::RefGetSymbolsByName("_objc_msgSend"); for (const auto& c : candidates) { - if ((authStubsSection && sectionContains(authStubsSection, c)) - || (stubsSection && sectionContains(stubsSection, c)) + if ((m_stubsSection && sectionContains(m_stubsSection, c)) || (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(gotSection, 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 authStubsSection = data->GetSectionByName("__auth_stubs"); + const auto stubsSection = data->GetSectionByName("__stubs"); + 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()); + } + } + + return results; +} + +void MessageHandler::functionWasAnalyzed(uint64_t addr) +{ + if (!m_readyForRealAnalysisPass) + 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..b128699 100644 --- a/MessageHandler.h +++ b/MessageHandler.h @@ -2,15 +2,39 @@ #include -class MessageHandler { +class MessageHandler : public BinaryNinja::BinaryDataNotification { + + BinaryNinja::Ref m_data; + + BinaryNinja::Ref m_authStubsSection; + BinaryNinja::Ref m_stubsSection; + + bool m_readyForRealAnalysisPass = false; + std::mutex m_stubMutex; + std::set m_presentRequiredStubs; + std::set m_locatedRequiredStubs; + + 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); + 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/Workflow.cpp b/Workflow.cpp index 0feda46..035ecc4 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); @@ -105,6 +105,8 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) if (GlobalState::viewIsIgnored(bv)) return; + auto messageHandler = GlobalState::messageHandler(bv); + const auto log = BinaryNinja::LogRegistry::GetLogger(PluginLoggerName); // Ignore the view if it has an unsupported architecture. @@ -138,7 +140,6 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) CustomTypes::defineAll(bv); CFStringArchitectureHook* currentHook = new CFStringArchitectureHook(bv->GetDefaultArchitecture()); bv->GetDefaultArchitecture()->Register(currentHook); - auto messageHandler = GlobalState::messageHandler(bv); try { auto file = std::make_shared(bv); @@ -153,28 +154,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)); @@ -189,7 +189,6 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac) } } - auto messageHandler = GlobalState::messageHandler(bv); if (!messageHandler->hasMessageSendFunctions()) { log->LogError("Cannot perform Objective-C IL cleanup; no objc_msgSend candidates found"); GlobalState::addIgnoredView(bv); @@ -207,30 +206,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)) { + 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;