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

Commit e305083

Browse files
committed
Move msgSend func lookups to a cache class
1 parent fdc174d commit e305083

File tree

7 files changed

+105
-67
lines changed

7 files changed

+105
-67
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ set(PLUGIN_SOURCE
4646
GlobalState.cpp
4747
InfoHandler.h
4848
InfoHandler.cpp
49+
MessageHandler.cpp
50+
MessageHandler.h
4951
Plugin.cpp
5052
Workflow.h
5153
Workflow.cpp)

GlobalState.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@
1111
#include <unordered_map>
1212

1313
static std::unordered_map<BinaryViewID, SharedAnalysisInfo> g_analysisRecords;
14+
static std::unordered_map<BinaryViewID, MessageHandler*> g_messageHandlers;
1415
static std::set<BinaryViewID> g_ignoredViews;
1516

17+
MessageHandler* GlobalState::messageHandler(BinaryViewRef bv)
18+
{
19+
if (auto messageHandler = g_messageHandlers.find(id(bv)); messageHandler != g_messageHandlers.end()) {
20+
return messageHandler->second;
21+
} else {
22+
auto newMessageHandler = new MessageHandler(bv);
23+
g_messageHandlers[id(bv)] = newMessageHandler;
24+
return newMessageHandler;
25+
}
26+
}
27+
1628
BinaryViewID GlobalState::id(BinaryViewRef bv)
1729
{
1830
return bv->GetFile()->GetSessionId();

GlobalState.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "BinaryNinja.h"
1111

1212
#include "Core/AnalysisInfo.h"
13+
#include "MessageHandler.h"
1314

1415
using SharedAnalysisInfo = std::shared_ptr<ObjectiveNinja::AnalysisInfo>;
1516

@@ -38,6 +39,10 @@ class GlobalState {
3839
*/
3940
static SharedAnalysisInfo analysisInfo(BinaryViewRef);
4041

42+
/**
43+
* Get ObjC Message Handler for a view
44+
*/
45+
static MessageHandler* messageHandler(BinaryViewRef);
4146
/**
4247
* Store analysis info for a view.
4348
*/

MessageHandler.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "MessageHandler.h"
2+
3+
using namespace BinaryNinja;
4+
5+
MessageHandler::MessageHandler(Ref<BinaryView> data)
6+
{
7+
m_msgSendFunctions = findMsgSendFunctions(data);
8+
}
9+
10+
std::set<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
11+
{
12+
std::set<uint64_t> results;
13+
14+
const auto authStubsSection = data->GetSectionByName("__auth_stubs");
15+
const auto stubsSection = data->GetSectionByName("__stubs");
16+
const auto authGotSection = data->GetSectionByName("__auth_got");
17+
const auto gotSection = data->GetSectionByName("__got");
18+
const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr");
19+
20+
// Shorthand to check if a symbol lies in a given section.
21+
auto sectionContains = [](Ref<Section> section, Ref<Symbol> symbol) {
22+
const auto start = section->GetStart();
23+
const auto length = section->GetLength();
24+
const auto address = symbol->GetAddress();
25+
26+
return (uint64_t)(address - start) <= length;
27+
};
28+
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.
46+
const auto candidates = data->GetSymbolsByName("_objc_msgSend");
47+
for (const auto& c : candidates) {
48+
if ((authStubsSection && sectionContains(authStubsSection, c))
49+
|| (stubsSection && sectionContains(stubsSection, c))
50+
|| (authGotSection && sectionContains(authGotSection, c))
51+
|| (gotSection && sectionContains(gotSection, c))
52+
|| (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) {
53+
results.insert(c->GetAddress());
54+
}
55+
}
56+
57+
return results;
58+
}
59+
60+
bool MessageHandler::isMessageSend(uint64_t functionAddress)
61+
{
62+
return m_msgSendFunctions.count(functionAddress);
63+
}

MessageHandler.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include <binaryninjaapi.h>
4+
5+
class MessageHandler {
6+
7+
std::set<uint64_t> m_msgSendFunctions;
8+
static std::set<uint64_t> findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
9+
10+
public:
11+
MessageHandler(BinaryNinja::Ref<BinaryNinja::BinaryView> data);
12+
13+
std::set<uint64_t> getMessageSendFunctions() const { return m_msgSendFunctions; }
14+
bool hasMessageSendFunctions() const { return m_msgSendFunctions.size() != 0; }
15+
bool isMessageSend(uint64_t);
16+
};

Workflow.cpp

Lines changed: 7 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -26,55 +26,6 @@ static std::mutex g_initialAnalysisMutex;
2626
using SectionRef = BinaryNinja::Ref<BinaryNinja::Section>;
2727
using SymbolRef = BinaryNinja::Ref<BinaryNinja::Symbol>;
2828

29-
std::set<uint64_t> Workflow::findMsgSendFunctions(BinaryViewRef bv)
30-
{
31-
std::set<uint64_t> results;
32-
33-
const auto authStubsSection = bv->GetSectionByName("__auth_stubs");
34-
const auto stubsSection = bv->GetSectionByName("__stubs");
35-
const auto authGotSection = bv->GetSectionByName("__auth_got");
36-
const auto gotSection = bv->GetSectionByName("__got");
37-
const auto laSymbolPtrSection = bv->GetSectionByName("__la_symbol_ptr");
38-
39-
// Shorthand to check if a symbol lies in a given section.
40-
auto sectionContains = [](SectionRef section, SymbolRef symbol) {
41-
const auto start = section->GetStart();
42-
const auto length = section->GetLength();
43-
const auto address = symbol->GetAddress();
44-
45-
return (uint64_t)(address - start) <= length;
46-
};
47-
48-
// There can be multiple `_objc_msgSend` symbols in the same binary; there
49-
// may even be lots. Some of them are valid, others aren't. In order of
50-
// preference, `_objc_msgSend` symbols in the following sections are
51-
// preferred:
52-
//
53-
// 1. __auth_stubs
54-
// 2. __stubs
55-
// 3. __auth_got
56-
// 4. __got
57-
// ?. __la_symbol_ptr
58-
//
59-
// There is often an `_objc_msgSend` symbol that is a stub function, found
60-
// in the `__stubs` section, which will come with an imported symbol of the
61-
// same name in the `__got` section. Not all `__objc_msgSend` calls will be
62-
// routed through the stub function, making it important to make note of
63-
// both symbols' addresses. Furthermore, on ARM64, the `__auth{stubs,got}`
64-
// sections are preferred over their unauthenticated counterparts.
65-
const auto candidates = bv->GetSymbolsByName("_objc_msgSend");
66-
for (const auto& c : candidates) {
67-
if ((authStubsSection && sectionContains(authStubsSection, c))
68-
|| (stubsSection && sectionContains(stubsSection, c))
69-
|| (authGotSection && sectionContains(authGotSection, c))
70-
|| (gotSection && sectionContains(gotSection, c))
71-
|| (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c)))
72-
results.insert(c->GetAddress());
73-
}
74-
75-
return results;
76-
}
77-
7829
void Workflow::rewriteMethodCall(LLILFunctionRef ssa, size_t insnIndex)
7930
{
8031
const auto bv = ssa->GetFunction()->GetView();
@@ -187,6 +138,7 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
187138
CustomTypes::defineAll(bv);
188139
CFStringArchitectureHook* currentHook = new CFStringArchitectureHook(bv->GetDefaultArchitecture());
189140
bv->GetDefaultArchitecture()->Register(currentHook);
141+
auto messageHandler = GlobalState::messageHandler(bv);
190142

191143
try {
192144
auto file = std::make_shared<ObjectiveNinja::BinaryViewFile>(bv);
@@ -200,7 +152,7 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
200152

201153
InfoHandler::applyInfoToView(info, bv);
202154

203-
const auto msgSendFunctions = findMsgSendFunctions(bv);
155+
const auto msgSendFunctions = messageHandler->getMessageSendFunctions();
204156
for (auto addr : msgSendFunctions)
205157
{
206158
BinaryNinja::QualifiedNameAndType nameAndType;
@@ -237,12 +189,8 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
237189
}
238190
}
239191

240-
// Try to find the `objc_msgSend` functions(s), abort activity if missing.
241-
//
242-
// TODO: These results should be cached somehow as it can't be efficient to
243-
// repeatedly search for all the usable function addresses.
244-
const auto msgSendFunctions = findMsgSendFunctions(bv);
245-
if (msgSendFunctions.empty()) {
192+
auto messageHandler = GlobalState::messageHandler(bv);
193+
if (!messageHandler->hasMessageSendFunctions()) {
246194
log->LogError("Cannot perform Objective-C IL cleanup; no objc_msgSend candidates found");
247195
GlobalState::addIgnoredView(bv);
248196
return;
@@ -259,14 +207,14 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
259207
return;
260208
}
261209

262-
const auto rewriteIfEligible = [bv, msgSendFunctions, ssa](size_t insnIndex) {
210+
const auto rewriteIfEligible = [bv, messageHandler, ssa](size_t insnIndex) {
263211
auto insn = ssa->GetInstruction(insnIndex);
264212

265213
if (insn.operation == LLIL_CALL_SSA)
266214
{
267215
// Filter out calls that aren't to `objc_msgSend`.
268216
auto callExpr = insn.GetDestExpr<LLIL_CALL_SSA>();
269-
if (!msgSendFunctions.count(callExpr.GetValue().value))
217+
if (!messageHandler->isMessageSend(callExpr.GetValue().value))
270218
return;
271219

272220
// By convention, the selector is the second argument to `objc_msgSend`,
@@ -279,6 +227,7 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
279227
return;
280228

281229
rewriteMethodCall(ssa, insnIndex);
230+
282231
}
283232
else if (insn.operation == LLIL_SET_REG_SSA)
284233
{

Workflow.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,6 @@ constexpr auto ResolveMethodCalls = "core.function.objectiveC.resolveMethodCalls
2323
*/
2424
class Workflow {
2525

26-
/**
27-
* Get the addresses of all usable `_objc_msgSend` functions.
28-
*
29-
* Not all `_objc_msgSend` functions in the binary are usable, and there
30-
* may also be more than one usable candidate. For additional details and
31-
* specifics, see this function's implementation.
32-
*/
33-
static std::set<uint64_t> findMsgSendFunctions(BinaryViewRef);
34-
3526
/**
3627
* Attempt to rewrite the `objc_msgSend` call at `insnIndex` with a direct
3728
* call to the requested method's implementation.

0 commit comments

Comments
 (0)