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

Commit fdc174d

Browse files
committed
Inline CFStrings
1 parent a8dd9e3 commit fdc174d

5 files changed

Lines changed: 117 additions & 19 deletions

File tree

ArchitectureHooks.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "ArchitectureHooks.h"
2+
3+
using namespace BinaryNinja;
4+
5+
std::string CFStringArchitectureHook::GetIntrinsicName(uint32_t intrinsic)
6+
{
7+
if (intrinsic == CFSTRIntrinsicIndex)
8+
return "CFSTR";
9+
10+
return ArchitectureHook::GetIntrinsicName(intrinsic);
11+
}
12+
13+
std::vector<uint32_t> CFStringArchitectureHook::GetAllIntrinsics()
14+
{
15+
auto parent = ArchitectureHook::GetAllIntrinsics();
16+
parent.push_back(CFSTRIntrinsicIndex);
17+
return parent;
18+
}
19+
20+
std::vector<NameAndType> CFStringArchitectureHook::GetIntrinsicInputs(uint32_t intrinsic)
21+
{
22+
if (intrinsic != CFSTRIntrinsicIndex)
23+
return ArchitectureHook::GetIntrinsicInputs(intrinsic);
24+
25+
return { NameAndType(Type::PointerType(ArchitectureHook::GetAddressSize(), Type::IntegerType(1, false))) };
26+
}
27+
28+
std::vector<Confidence<BinaryNinja::Ref<Type>>> CFStringArchitectureHook::GetIntrinsicOutputs(uint32_t intrinsic)
29+
{
30+
if (intrinsic != CFSTRIntrinsicIndex)
31+
return ArchitectureHook::GetIntrinsicOutputs(intrinsic);
32+
33+
return { Type::PointerType(ArchitectureHook::GetAddressSize(), Type::IntegerType(1, false)) };
34+
}

ArchitectureHooks.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include <binaryninjaapi.h>
4+
5+
constexpr uint32_t CFSTRIntrinsicIndex = UINT32_MAX - 64;
6+
7+
class CFStringArchitectureHook : public BinaryNinja::ArchitectureHook
8+
{
9+
virtual std::string GetIntrinsicName(uint32_t intrinsic) override;
10+
virtual std::vector<uint32_t> GetAllIntrinsics() override;
11+
virtual std::vector<BinaryNinja::NameAndType> GetIntrinsicInputs(uint32_t intrinsic) override;
12+
virtual std::vector<BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>>> GetIntrinsicOutputs(uint32_t intrinsic) override;
13+
14+
public:
15+
CFStringArchitectureHook(BinaryNinja::Ref<BinaryNinja::Architecture> base)
16+
: BinaryNinja::ArchitectureHook(base) { };
17+
};

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ set(PLUGIN_SOURCE
3434
Core/AnalysisProvider.cpp
3535
Core/Analyzer.cpp
3636
Core/TypeParser.cpp
37+
ArchitectureHooks.cpp
38+
ArchitectureHooks.h
3739
Commands.h
3840
Commands.cpp
3941
CustomTypes.h

Workflow.cpp

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "GlobalState.h"
1313
#include "InfoHandler.h"
1414
#include "Performance.h"
15+
#include "ArchitectureHooks.h"
1516

1617
#include "Core/AnalysisProvider.h"
1718
#include "Core/BinaryViewFile.h"
@@ -119,6 +120,31 @@ void Workflow::rewriteMethodCall(LLILFunctionRef ssa, size_t insnIndex)
119120
llil->GenerateSSAForm();
120121
}
121122

123+
void Workflow::rewriteCFString(LLILFunctionRef ssa, size_t insnIndex)
124+
{
125+
const auto bv = ssa->GetFunction()->GetView();
126+
const auto llil = ssa->GetNonSSAForm();
127+
const auto insn = ssa->GetInstruction(insnIndex);
128+
const auto llilIndex = ssa->GetNonSSAInstructionIndex(insnIndex);
129+
auto llilInsn = llil->GetInstruction(llilIndex);
130+
131+
auto sourceExpr = insn.GetSourceExpr<LLIL_SET_REG_SSA>();
132+
auto destRegister = llilInsn.GetDestRegister();
133+
134+
auto addr = sourceExpr.GetValue().value;
135+
auto stringPointer = addr + 0x10;
136+
uint64_t dest;
137+
bv->Read(&dest, stringPointer, bv->GetDefaultArchitecture()->GetAddressSize());
138+
139+
auto targetPointer = llil->ConstPointer(bv->GetAddressSize(), dest, llilInsn);
140+
auto cfstrCall = llil->Intrinsic({ BinaryNinja::RegisterOrFlag(0, destRegister) }, CFSTRIntrinsicIndex, {targetPointer}, 0, llilInsn);
141+
142+
llilInsn.Replace(cfstrCall);
143+
144+
llil->GenerateSSAForm();
145+
llil->Finalize();
146+
}
147+
122148
void Workflow::inlineMethodCalls(AnalysisContextRef ac)
123149
{
124150
const auto func = ac->GetFunction();
@@ -159,6 +185,8 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
159185
if (!GlobalState::hasAnalysisInfo(bv)) {
160186
SharedAnalysisInfo info;
161187
CustomTypes::defineAll(bv);
188+
CFStringArchitectureHook* currentHook = new CFStringArchitectureHook(bv->GetDefaultArchitecture());
189+
bv->GetDefaultArchitecture()->Register(currentHook);
162190

163191
try {
164192
auto file = std::make_shared<ObjectiveNinja::BinaryViewFile>(bv);
@@ -231,27 +259,37 @@ void Workflow::inlineMethodCalls(AnalysisContextRef ac)
231259
return;
232260
}
233261

234-
const auto rewriteIfEligible = [msgSendFunctions, ssa](size_t insnIndex) {
262+
const auto rewriteIfEligible = [bv, msgSendFunctions, ssa](size_t insnIndex) {
235263
auto insn = ssa->GetInstruction(insnIndex);
236264

237-
if (insn.operation != LLIL_CALL_SSA)
238-
return;
239-
240-
// Filter out calls that aren't to `objc_msgSend`.
241-
auto callExpr = insn.GetDestExpr<LLIL_CALL_SSA>();
242-
if (!msgSendFunctions.count(callExpr.GetValue().value))
243-
return;
244-
245-
// By convention, the selector is the second argument to `objc_msgSend`,
246-
// therefore two parameters are required for a proper rewrite; abort if
247-
// this condition is not met.
248-
auto params = insn.GetParameterExprs<LLIL_CALL_SSA>();
249-
if (params.size() < 2
250-
|| params[0].operation != LLIL_REG_SSA
251-
|| params[1].operation != LLIL_REG_SSA)
252-
return;
253-
254-
rewriteMethodCall(ssa, insnIndex);
265+
if (insn.operation == LLIL_CALL_SSA)
266+
{
267+
// Filter out calls that aren't to `objc_msgSend`.
268+
auto callExpr = insn.GetDestExpr<LLIL_CALL_SSA>();
269+
if (!msgSendFunctions.count(callExpr.GetValue().value))
270+
return;
271+
272+
// By convention, the selector is the second argument to `objc_msgSend`,
273+
// therefore two parameters are required for a proper rewrite; abort if
274+
// this condition is not met.
275+
auto params = insn.GetParameterExprs<LLIL_CALL_SSA>();
276+
if (params.size() < 2
277+
|| params[0].operation != LLIL_REG_SSA
278+
|| params[1].operation != LLIL_REG_SSA)
279+
return;
280+
281+
rewriteMethodCall(ssa, insnIndex);
282+
}
283+
else if (insn.operation == LLIL_SET_REG_SSA)
284+
{
285+
auto sourceExpr = insn.GetSourceExpr<LLIL_SET_REG_SSA>();
286+
auto addr = sourceExpr.GetValue().value;
287+
BinaryNinja::DataVariable var;
288+
if (!bv->GetDataVariableAtAddress(addr, var) || var.type->GetString() != "struct CFString")
289+
return;
290+
291+
rewriteCFString(ssa, insnIndex);
292+
}
255293
};
256294

257295
for (const auto& block : ssa->GetBasicBlocks())

Workflow.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ class Workflow {
4040
*/
4141
static void rewriteMethodCall(LLILFunctionRef, size_t insnIndex);
4242

43+
/**
44+
* Rewrite a CFString reference to a direct string reference and matching CFSTR intrinsic call.
45+
*
46+
* @param insnIndex The index of the `LLIL_CALL` instruction to rewrite
47+
*/
48+
static void rewriteCFString(LLILFunctionRef, size_t insnIndex);
49+
4350
public:
4451
/**
4552
* Attempt to inline all `objc_msgSend` calls in the given analysis context.

0 commit comments

Comments
 (0)