Skip to content
This repository was archived by the owner on Jun 25, 2025. It is now read-only.

Commit a37fa39

Browse files
committed
Remove ARC-related Obj-C Code from decompilation
1 parent e305083 commit a37fa39

File tree

3 files changed

+214
-55
lines changed

3 files changed

+214
-55
lines changed

MessageHandler.cpp

Lines changed: 148 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,87 @@
11
#include "MessageHandler.h"
2+
#include "mediumlevelilinstruction.h"
23

34
using namespace BinaryNinja;
45

6+
const std::set<std::string> arcFunctionNames = {
7+
"_objc_retain",
8+
"_objc_release",
9+
"_objc_autorelease",
10+
"_objc_autoreleaseReturnValue",
11+
"_objc_retainAutoreleasedReturnValue",
12+
"_objc_unsafeClaimAutoreleasedReturnValue"
13+
};
14+
515
MessageHandler::MessageHandler(Ref<BinaryView> data)
16+
: m_data(data)
617
{
18+
m_authStubsSection = data->GetSectionByName("__auth_stubs");
19+
m_stubsSection = data->GetSectionByName("__stubs");
20+
721
m_msgSendFunctions = findMsgSendFunctions(data);
22+
m_arcFunctions = findARCFunctions(data);
23+
24+
if (!m_authStubsSection && !m_stubsSection)
25+
m_readyForRealAnalysisPass = true;
26+
else
27+
data->RegisterNotification(this);
28+
}
29+
30+
void MessageHandler::OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym)
31+
{
32+
std::unique_lock<std::mutex> lock(m_stubMutex);
33+
34+
if (m_readyForRealAnalysisPass)
35+
return;
36+
if (sym->GetType() != ImportedFunctionSymbol)
37+
return;
38+
if (!m_presentRequiredStubs.count(sym->GetShortName()))
39+
return;
40+
41+
m_locatedRequiredStubs.insert(sym->GetShortName());
42+
if (arcFunctionNames.count(sym->GetShortName()))
43+
m_arcFunctions.insert(sym->GetAddress());
44+
else if (sym->GetShortName() == "_objc_msgSend") {
45+
auto f = m_data->GetAnalysisFunction(m_data->GetDefaultPlatform(), sym->GetAddress());
46+
if (!f)
47+
return;
48+
auto retType = BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>>(
49+
BinaryNinja::Type::PointerType(m_data->GetAddressSize(), BinaryNinja::Type::VoidType(),
50+
0));
51+
52+
std::vector<BinaryNinja::FunctionParameter> params;
53+
auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention();
54+
55+
params.push_back({ "self",
56+
BinaryNinja::Type::NamedType(m_data, { "id" }),
57+
true,
58+
BinaryNinja::Variable() });
59+
params.push_back({ "sel",
60+
BinaryNinja::Type::PointerType(m_data->GetAddressSize(), BinaryNinja::Type::IntegerType(1, false)),
61+
true,
62+
BinaryNinja::Variable() });
63+
64+
auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params, true);
65+
f->SetUserType(funcType);
66+
67+
m_msgSendFunctions.insert(f->GetStart());
68+
}
69+
70+
if (m_locatedRequiredStubs.size() == m_presentRequiredStubs.size()) {
71+
m_readyForRealAnalysisPass = true;
72+
73+
for (auto fAddr : m_reAnalysisRequiredFunctions)
74+
for (auto f : m_data->GetAnalysisFunctionsForAddress(fAddr))
75+
f->Reanalyze();
76+
77+
m_data->UnregisterNotification(this);
78+
}
879
}
980

1081
std::set<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
1182
{
83+
std::unique_lock<std::mutex> lock(m_stubMutex);
1284
std::set<uint64_t> results;
13-
14-
const auto authStubsSection = data->GetSectionByName("__auth_stubs");
15-
const auto stubsSection = data->GetSectionByName("__stubs");
1685
const auto authGotSection = data->GetSectionByName("__auth_got");
1786
const auto gotSection = data->GetSectionByName("__got");
1887
const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr");
@@ -26,38 +95,95 @@ std::set<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryN
2695
return (uint64_t)(address - start) <= length;
2796
};
2897

29-
// There can be multiple `_objc_msgSend` symbols in the same binary; there
30-
// may even be lots. Some of them are valid, others aren't. In order of
31-
// preference, `_objc_msgSend` symbols in the following sections are
32-
// preferred:
33-
//
34-
// 1. __auth_stubs
35-
// 2. __stubs
36-
// 3. __auth_got
37-
// 4. __got
38-
// ?. __la_symbol_ptr
39-
//
40-
// There is often an `_objc_msgSend` symbol that is a stub function, found
41-
// in the `__stubs` section, which will come with an imported symbol of the
42-
// same name in the `__got` section. Not all `__objc_msgSend` calls will be
43-
// routed through the stub function, making it important to make note of
44-
// both symbols' addresses. Furthermore, on ARM64, the `__auth{stubs,got}`
45-
// sections are preferred over their unauthenticated counterparts.
4698
const auto candidates = data->GetSymbolsByName("_objc_msgSend");
4799
for (const auto& c : candidates) {
48-
if ((authStubsSection && sectionContains(authStubsSection, c))
49-
|| (stubsSection && sectionContains(stubsSection, c))
100+
if ((m_stubsSection && sectionContains(m_stubsSection, c))
50101
|| (authGotSection && sectionContains(authGotSection, c))
51102
|| (gotSection && sectionContains(gotSection, c))
52103
|| (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) {
53104
results.insert(c->GetAddress());
54105
}
106+
if ((m_authStubsSection && sectionContains(m_authStubsSection, c))
107+
|| (m_stubsSection && sectionContains(gotSection, c))) {
108+
results.insert(c->GetAddress());
109+
m_locatedRequiredStubs.insert(c->GetShortName());
110+
}
55111
}
112+
m_presentRequiredStubs.insert("_objc_msgSend");
56113

57114
return results;
58115
}
59116

117+
std::set<uint64_t> MessageHandler::findARCFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
118+
{
119+
std::unique_lock<std::mutex> lock(m_stubMutex);
120+
std::set<uint64_t> results;
121+
const auto authStubsSection = data->GetSectionByName("__auth_stubs");
122+
const auto stubsSection = data->GetSectionByName("__stubs");
123+
const auto authGotSection = data->GetSectionByName("__auth_got");
124+
const auto gotSection = data->GetSectionByName("__got");
125+
const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr");
126+
127+
// Shorthand to check if a symbol lies in a given section.
128+
auto sectionContains = [](Ref<Section> section, Ref<Symbol> symbol) {
129+
const auto start = section->GetStart();
130+
const auto length = section->GetLength();
131+
const auto address = symbol->GetAddress();
132+
133+
return (uint64_t)(address - start) <= length;
134+
};
135+
136+
std::vector<BinaryNinja::Ref<BinaryNinja::Symbol>> candidates;
137+
std::vector<BinaryNinja::Ref<BinaryNinja::Symbol>> next;
138+
for (auto name : arcFunctionNames) {
139+
next = data->GetSymbolsByName(name);
140+
candidates.insert(candidates.end(), next.begin(), next.end());
141+
}
142+
for (auto& c : candidates) {
143+
if ((authGotSection && sectionContains(authGotSection, c))
144+
|| (gotSection && sectionContains(gotSection, c))
145+
|| (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) {
146+
{
147+
results.insert(c->GetAddress());
148+
m_presentRequiredStubs.insert(c->GetShortName());
149+
}
150+
}
151+
if ((m_authStubsSection && sectionContains(m_authStubsSection, c))
152+
|| (m_stubsSection && sectionContains(m_stubsSection, c))) {
153+
m_locatedRequiredStubs.insert(c->GetShortName());
154+
results.insert(c->GetAddress());
155+
}
156+
}
157+
158+
return results;
159+
}
160+
161+
void MessageHandler::functionWasAnalyzed(uint64_t addr)
162+
{
163+
if (!m_readyForRealAnalysisPass)
164+
m_reAnalysisRequiredFunctions.insert(addr);
165+
}
166+
60167
bool MessageHandler::isMessageSend(uint64_t functionAddress)
61168
{
169+
std::unique_lock<std::mutex> lock(m_stubMutex);
62170
return m_msgSendFunctions.count(functionAddress);
63171
}
172+
173+
bool MessageHandler::isARCFunction(uint64_t functionAddress)
174+
{
175+
std::unique_lock<std::mutex> lock(m_stubMutex);
176+
return m_arcFunctions.count(functionAddress);
177+
}
178+
179+
bool MessageHandler::isFunctionLocatedInStubSection(uint64_t addr)
180+
{
181+
if (m_stubsSection && m_stubsSection->GetStart() < addr
182+
&& m_stubsSection->GetStart() + m_stubsSection->GetLength() > addr)
183+
return true;
184+
else if (m_authStubsSection && m_authStubsSection->GetStart() < addr
185+
&& m_authStubsSection->GetStart() + m_authStubsSection->GetLength() > addr)
186+
return true;
187+
188+
return false;
189+
}

MessageHandler.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,39 @@
22

33
#include <binaryninjaapi.h>
44

5-
class MessageHandler {
5+
class MessageHandler : public BinaryNinja::BinaryDataNotification {
6+
7+
BinaryNinja::Ref<BinaryNinja::BinaryView> m_data;
8+
9+
BinaryNinja::Ref<BinaryNinja::Section> m_authStubsSection;
10+
BinaryNinja::Ref<BinaryNinja::Section> m_stubsSection;
11+
12+
bool m_readyForRealAnalysisPass = false;
13+
std::mutex m_stubMutex;
14+
std::set<std::string> m_presentRequiredStubs;
15+
std::set<std::string> m_locatedRequiredStubs;
16+
17+
std::set<uint64_t> m_reAnalysisRequiredFunctions;
618

719
std::set<uint64_t> m_msgSendFunctions;
8-
static std::set<uint64_t> findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
20+
std::set<uint64_t> m_checkedNonMsgSendFunctions;
21+
std::set<uint64_t> m_arcFunctions;
22+
std::set<uint64_t> m_checkedNonARCFunctions;
23+
24+
std::set<uint64_t> findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
25+
std::set<uint64_t> findARCFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
26+
27+
virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override;
928

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

32+
void functionWasAnalyzed(uint64_t addr);
33+
1334
std::set<uint64_t> getMessageSendFunctions() const { return m_msgSendFunctions; }
1435
bool hasMessageSendFunctions() const { return m_msgSendFunctions.size() != 0; }
1536
bool isMessageSend(uint64_t);
37+
bool isARCFunction(uint64_t);
38+
39+
bool isFunctionLocatedInStubSection(uint64_t);
1640
};

Workflow.cpp

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77

88
#include "Workflow.h"
99

10+
#include "ArchitectureHooks.h"
1011
#include "Constants.h"
1112
#include "CustomTypes.h"
1213
#include "GlobalState.h"
1314
#include "InfoHandler.h"
1415
#include "Performance.h"
15-
#include "ArchitectureHooks.h"
1616

1717
#include "Core/AnalysisProvider.h"
1818
#include "Core/BinaryViewFile.h"
@@ -88,7 +88,7 @@ void Workflow::rewriteCFString(LLILFunctionRef ssa, size_t insnIndex)
8888
bv->Read(&dest, stringPointer, bv->GetDefaultArchitecture()->GetAddressSize());
8989

9090
auto targetPointer = llil->ConstPointer(bv->GetAddressSize(), dest, llilInsn);
91-
auto cfstrCall = llil->Intrinsic({ BinaryNinja::RegisterOrFlag(0, destRegister) }, CFSTRIntrinsicIndex, {targetPointer}, 0, llilInsn);
91+
auto cfstrCall = llil->Intrinsic({ BinaryNinja::RegisterOrFlag(0, destRegister) }, CFSTRIntrinsicIndex, { targetPointer }, 0, llilInsn);
9292

9393
llilInsn.Replace(cfstrCall);
9494

@@ -105,6 +105,8 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
105105
if (GlobalState::viewIsIgnored(bv))
106106
return;
107107

108+
auto messageHandler = GlobalState::messageHandler(bv);
109+
108110
const auto log = BinaryNinja::LogRegistry::GetLogger(PluginLoggerName);
109111

110112
// Ignore the view if it has an unsupported architecture.
@@ -138,7 +140,6 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
138140
CustomTypes::defineAll(bv);
139141
CFStringArchitectureHook* currentHook = new CFStringArchitectureHook(bv->GetDefaultArchitecture());
140142
bv->GetDefaultArchitecture()->Register(currentHook);
141-
auto messageHandler = GlobalState::messageHandler(bv);
142143

143144
try {
144145
auto file = std::make_shared<ObjectiveNinja::BinaryViewFile>(bv);
@@ -153,28 +154,27 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
153154
InfoHandler::applyInfoToView(info, bv);
154155

155156
const auto msgSendFunctions = messageHandler->getMessageSendFunctions();
156-
for (auto addr : msgSendFunctions)
157-
{
157+
for (auto addr : msgSendFunctions) {
158158
BinaryNinja::QualifiedNameAndType nameAndType;
159159
std::string errors;
160160
std::set<BinaryNinja::QualifiedName> typesAllowRedefinition;
161161

162162
// void *
163163
auto retType = BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>>(
164-
BinaryNinja::Type::PointerType(bv->GetAddressSize(),BinaryNinja::Type::VoidType(),
164+
BinaryNinja::Type::PointerType(bv->GetAddressSize(), BinaryNinja::Type::VoidType(),
165165
0));
166166

167167
std::vector<BinaryNinja::FunctionParameter> params;
168168
auto cc = bv->GetDefaultPlatform()->GetDefaultCallingConvention();
169169

170-
params.push_back({"self",
171-
BinaryNinja::Type::NamedType(bv, {"id"}),
170+
params.push_back({ "self",
171+
BinaryNinja::Type::NamedType(bv, { "id" }),
172172
true,
173-
BinaryNinja::Variable()});
174-
params.push_back({"sel",
173+
BinaryNinja::Variable() });
174+
params.push_back({ "sel",
175175
BinaryNinja::Type::PointerType(bv->GetAddressSize(), BinaryNinja::Type::IntegerType(1, false)),
176176
true,
177-
BinaryNinja::Variable()});
177+
BinaryNinja::Variable() });
178178

179179
auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params, true);
180180
bv->DefineDataVariable(addr, BinaryNinja::Type::PointerType(bv->GetDefaultArchitecture(), funcType));
@@ -189,7 +189,6 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
189189
}
190190
}
191191

192-
auto messageHandler = GlobalState::messageHandler(bv);
193192
if (!messageHandler->hasMessageSendFunctions()) {
194193
log->LogError("Cannot perform Objective-C IL cleanup; no objc_msgSend candidates found");
195194
GlobalState::addIgnoredView(bv);
@@ -207,30 +206,40 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
207206
return;
208207
}
209208

210-
const auto rewriteIfEligible = [bv, messageHandler, ssa](size_t insnIndex) {
209+
const auto rewriteIfEligible = [bv, llil, messageHandler, ssa](size_t insnIndex) {
211210
auto insn = ssa->GetInstruction(insnIndex);
212211

213-
if (insn.operation == LLIL_CALL_SSA)
214-
{
212+
if (insn.operation == LLIL_CALL_SSA
213+
|| (insn.operation == LLIL_JUMP && insnIndex == ssa->GetInstructionCount() - 1)
214+
|| insn.operation == LLIL_TAILCALL_SSA) {
215215
// Filter out calls that aren't to `objc_msgSend`.
216-
auto callExpr = insn.GetDestExpr<LLIL_CALL_SSA>();
217-
if (!messageHandler->isMessageSend(callExpr.GetValue().value))
218-
return;
216+
auto callExpr = insn.GetDestExpr();
217+
if (insn.operation == LLIL_CALL_SSA
218+
&& messageHandler->isMessageSend(callExpr.GetValue().value)) {
219+
auto params = insn.GetParameterExprs();
220+
if (params.size() >= 2
221+
&& params[0].operation == LLIL_REG_SSA
222+
&& params[1].operation == LLIL_REG_SSA)
223+
rewriteMethodCall(ssa, insnIndex);
224+
} else if (messageHandler->isARCFunction(callExpr.GetValue().value)) {
225+
auto nonSSAIdx = ssa->GetNonSSAInstructionIndex(insnIndex);
226+
auto targetInsn = llil->GetInstruction(nonSSAIdx);
227+
if (insn.operation == LLIL_CALL_SSA)
228+
targetInsn.Replace(llil->Nop(targetInsn));
229+
else // Other two categories. TailCall or Jump that is last instruction (so, tailcall...)
230+
{
231+
auto lr = llil->GetArchitecture()->GetLinkRegister();
232+
auto lrInfo = llil->GetArchitecture()->GetRegisterInfo(lr);
219233

220-
// By convention, the selector is the second argument to `objc_msgSend`,
221-
// therefore two parameters are required for a proper rewrite; abort if
222-
// this condition is not met.
223-
auto params = insn.GetParameterExprs<LLIL_CALL_SSA>();
224-
if (params.size() < 2
225-
|| params[0].operation != LLIL_REG_SSA
226-
|| params[1].operation != LLIL_REG_SSA)
234+
targetInsn.Replace(llil->Return(llil->Register(lrInfo.size, lr, targetInsn), targetInsn));
235+
}
236+
llil->GenerateSSAForm();
237+
llil->Finalize();
227238
return;
228-
229-
rewriteMethodCall(ssa, insnIndex);
230-
231-
}
232-
else if (insn.operation == LLIL_SET_REG_SSA)
233-
{
239+
}
240+
if (messageHandler->isFunctionLocatedInStubSection(callExpr.GetValue().value))
241+
messageHandler->functionWasAnalyzed(llil->GetFunction()->GetStart());
242+
} else if (insn.operation == LLIL_SET_REG_SSA) {
234243
auto sourceExpr = insn.GetSourceExpr<LLIL_SET_REG_SSA>();
235244
auto addr = sourceExpr.GetValue().value;
236245
BinaryNinja::DataVariable var;

0 commit comments

Comments
 (0)