Skip to content

Commit

Permalink
Remove ARC-related Obj-C Code from decompilation
Browse files Browse the repository at this point in the history
  • Loading branch information
0cyn committed Jan 24, 2023
1 parent e305083 commit a37fa39
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 55 deletions.
170 changes: 148 additions & 22 deletions MessageHandler.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,87 @@
#include "MessageHandler.h"
#include "mediumlevelilinstruction.h"

using namespace BinaryNinja;

const std::set<std::string> arcFunctionNames = {
"_objc_retain",
"_objc_release",
"_objc_autorelease",
"_objc_autoreleaseReturnValue",
"_objc_retainAutoreleasedReturnValue",
"_objc_unsafeClaimAutoreleasedReturnValue"
};

MessageHandler::MessageHandler(Ref<BinaryView> 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<std::mutex> 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::Ref<BinaryNinja::Type>>(
BinaryNinja::Type::PointerType(m_data->GetAddressSize(), BinaryNinja::Type::VoidType(),
0));

std::vector<BinaryNinja::FunctionParameter> 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<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
{
std::unique_lock<std::mutex> lock(m_stubMutex);
std::set<uint64_t> 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");
Expand All @@ -26,38 +95,95 @@ std::set<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryN
return (uint64_t)(address - start) <= length;
};

// There can be multiple `_objc_msgSend` symbols in the same binary; there
// may even be lots. Some of them are valid, others aren't. In order of
// preference, `_objc_msgSend` symbols in the following sections are
// preferred:
//
// 1. __auth_stubs
// 2. __stubs
// 3. __auth_got
// 4. __got
// ?. __la_symbol_ptr
//
// There is often an `_objc_msgSend` symbol that is a stub function, found
// in the `__stubs` section, which will come with an imported symbol of the
// same name in the `__got` section. Not all `__objc_msgSend` calls will be
// routed through the stub function, making it important to make note of
// both symbols' addresses. Furthermore, on ARM64, the `__auth{stubs,got}`
// sections are preferred over their unauthenticated counterparts.
const auto candidates = data->GetSymbolsByName("_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<uint64_t> MessageHandler::findARCFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
{
std::unique_lock<std::mutex> lock(m_stubMutex);
std::set<uint64_t> 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> section, Ref<Symbol> symbol) {
const auto start = section->GetStart();
const auto length = section->GetLength();
const auto address = symbol->GetAddress();

return (uint64_t)(address - start) <= length;
};

std::vector<BinaryNinja::Ref<BinaryNinja::Symbol>> candidates;
std::vector<BinaryNinja::Ref<BinaryNinja::Symbol>> 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<std::mutex> lock(m_stubMutex);
return m_msgSendFunctions.count(functionAddress);
}

bool MessageHandler::isARCFunction(uint64_t functionAddress)
{
std::unique_lock<std::mutex> 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;
}
28 changes: 26 additions & 2 deletions MessageHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@

#include <binaryninjaapi.h>

class MessageHandler {
class MessageHandler : public BinaryNinja::BinaryDataNotification {

BinaryNinja::Ref<BinaryNinja::BinaryView> m_data;

BinaryNinja::Ref<BinaryNinja::Section> m_authStubsSection;
BinaryNinja::Ref<BinaryNinja::Section> m_stubsSection;

bool m_readyForRealAnalysisPass = false;
std::mutex m_stubMutex;
std::set<std::string> m_presentRequiredStubs;
std::set<std::string> m_locatedRequiredStubs;

std::set<uint64_t> m_reAnalysisRequiredFunctions;

std::set<uint64_t> m_msgSendFunctions;
static std::set<uint64_t> findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
std::set<uint64_t> m_checkedNonMsgSendFunctions;
std::set<uint64_t> m_arcFunctions;
std::set<uint64_t> m_checkedNonARCFunctions;

std::set<uint64_t> findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
std::set<uint64_t> findARCFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);

virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override;

public:
MessageHandler(BinaryNinja::Ref<BinaryNinja::BinaryView> data);

void functionWasAnalyzed(uint64_t addr);

std::set<uint64_t> 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);
};
71 changes: 40 additions & 31 deletions Workflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);

Expand All @@ -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.
Expand Down Expand Up @@ -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<ObjectiveNinja::BinaryViewFile>(bv);
Expand All @@ -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<BinaryNinja::QualifiedName> typesAllowRedefinition;

// void *
auto retType = BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>>(
BinaryNinja::Type::PointerType(bv->GetAddressSize(),BinaryNinja::Type::VoidType(),
BinaryNinja::Type::PointerType(bv->GetAddressSize(), BinaryNinja::Type::VoidType(),
0));

std::vector<BinaryNinja::FunctionParameter> 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));
Expand All @@ -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);
Expand All @@ -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<LLIL_CALL_SSA>();
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<LLIL_CALL_SSA>();
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<LLIL_SET_REG_SSA>();
auto addr = sourceExpr.GetValue().value;
BinaryNinja::DataVariable var;
Expand Down

0 comments on commit a37fa39

Please sign in to comment.