Skip to content
This repository was archived by the owner on Jul 11, 2022. It is now read-only.

Commit dd9d8dd

Browse files
committed
Initial support for analyzing categories.
1 parent fb414df commit dd9d8dd

File tree

8 files changed

+201
-0
lines changed

8 files changed

+201
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_subdirectory(Vendor/BinaryNinjaAPI)
2626
# Core library -----------------------------------------------------------------
2727

2828
set(CORE_SOURCE
29+
Include/ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h
2930
Include/ObjectiveNinjaCore/Analyzers/CFStringAnalyzer.h
3031
Include/ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h
3132
Include/ObjectiveNinjaCore/Analyzers/SelectorAnalyzer.h
@@ -36,6 +37,7 @@ set(CORE_SOURCE
3637
Include/ObjectiveNinjaCore/AnalysisProvider.h
3738
Include/ObjectiveNinjaCore/Analyzer.h
3839
Include/ObjectiveNinjaCore/TypeParser.h
40+
Core/Analyzers/CategoryAnalyzer.cpp
3941
Core/Analyzers/CFStringAnalyzer.cpp
4042
Core/Analyzers/ClassAnalyzer.cpp
4143
Core/Analyzers/SelectorAnalyzer.cpp

Core/AnalysisProvider.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <ObjectiveNinjaCore/Analyzers/CFStringAnalyzer.h>
1111
#include <ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h>
12+
#include <ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h>
1213
#include <ObjectiveNinjaCore/Analyzers/SelectorAnalyzer.h>
1314

1415
namespace ObjectiveNinja {
@@ -20,6 +21,7 @@ SharedAnalysisInfo AnalysisProvider::infoForFile(SharedAbstractFile file)
2021
std::vector<std::unique_ptr<ObjectiveNinja::Analyzer>> analyzers;
2122
analyzers.emplace_back(new SelectorAnalyzer(info, file));
2223
analyzers.emplace_back(new ClassAnalyzer(info, file));
24+
analyzers.emplace_back(new CategoryAnalyzer(info, file));
2325
analyzers.emplace_back(new CFStringAnalyzer(info, file));
2426

2527
for (const auto& analyzer : analyzers)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include <ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h>
2+
#include <ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h>
3+
4+
#include <ObjectiveNinjaCore/TypeParser.h>
5+
6+
using namespace ObjectiveNinja;
7+
8+
9+
CategoryAnalyzer::CategoryAnalyzer(SharedAnalysisInfo info,
10+
SharedAbstractFile file)
11+
: Analyzer(std::move(info), std::move(file))
12+
{
13+
}
14+
15+
MethodListInfo CategoryAnalyzer::analyzeMethodList(uint64_t address)
16+
{
17+
MethodListInfo mli;
18+
mli.address = address;
19+
mli.flags = m_file->readInt(mli.address);
20+
21+
auto methodCount = m_file->readInt(mli.address + 0x4);
22+
auto methodSize = mli.hasRelativeOffsets() ? 12 : 24;
23+
24+
for (unsigned i = 0; i < methodCount; ++i) {
25+
MethodInfo mi;
26+
mi.address = mli.address + 8 + (i * methodSize);
27+
28+
m_file->seek(mi.address);
29+
30+
if (mli.hasRelativeOffsets()) {
31+
mi.nameAddress = mi.address + static_cast<int32_t>(m_file->readInt());
32+
mi.typeAddress = mi.address + 4 + static_cast<int32_t>(m_file->readInt());
33+
mi.implAddress = mi.address + 8 + static_cast<int32_t>(m_file->readInt());
34+
} else {
35+
mi.nameAddress = arp(m_file->readLong());
36+
mi.typeAddress = arp(m_file->readLong());
37+
mi.implAddress = arp(m_file->readLong());
38+
}
39+
40+
if (!mli.hasRelativeOffsets() || mli.hasDirectSelectors()) {
41+
mi.selector = m_file->readStringAt(mi.nameAddress);
42+
} else {
43+
auto selectorNamePointer = arp(m_file->readLong(mi.nameAddress));
44+
mi.selector = m_file->readStringAt(selectorNamePointer);
45+
}
46+
47+
mi.type = m_file->readStringAt(mi.typeAddress);
48+
49+
m_info->methodImpls[mi.nameAddress] = mi.implAddress;
50+
51+
mli.methods.emplace_back(mi);
52+
}
53+
54+
return mli;
55+
}
56+
57+
void CategoryAnalyzer::run()
58+
{
59+
const auto sectionStart = m_file->sectionStart("__objc_catlist");
60+
const auto sectionEnd = m_file->sectionEnd("__objc_catlist");
61+
if (sectionStart == 0 || sectionEnd == 0)
62+
return;
63+
64+
for (auto address = sectionStart; address < sectionEnd; address += 8) {
65+
CategoryInfo ci;
66+
ci.listPointer = address;
67+
ci.address = arp(m_file->readLong(address));
68+
ci.nameAddress = arp(m_file->readLong(ci.address));
69+
ci.name = m_file->readStringAt(ci.nameAddress);
70+
ci.instanceMethodListAddress = arp(m_file->readLong(ci.address + 0x10));
71+
ci.classMethodListAddress = arp(m_file->readLong(ci.address + 0x18));
72+
73+
if (ci.instanceMethodListAddress)
74+
ci.instanceMethods = analyzeMethodList(ci.instanceMethodListAddress);
75+
76+
if (ci.classMethodListAddress)
77+
ci.classMethods = analyzeMethodList(ci.classMethodListAddress);
78+
79+
m_info->categories.emplace_back(ci);
80+
}
81+
}

Include/ObjectiveNinjaCore/AnalysisInfo.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,21 @@ struct ClassInfo {
9292
uint64_t methodListAddress {};
9393
};
9494

95+
/**
96+
* A description of an Objective-C category.
97+
*/
98+
struct CategoryInfo {
99+
uint64_t address {};
100+
std::string name {};
101+
MethodListInfo instanceMethods {};
102+
MethodListInfo classMethods {};
103+
104+
uint64_t listPointer {};
105+
uint64_t nameAddress {};
106+
uint64_t instanceMethodListAddress {};
107+
uint64_t classMethodListAddress {};
108+
};
109+
95110
/**
96111
* Analysis info storage.
97112
*
@@ -106,6 +121,7 @@ struct AnalysisInfo {
106121
std::unordered_map<uint64_t, SharedSelectorRefInfo> selectorRefsByKey {};
107122

108123
std::vector<ClassInfo> classes {};
124+
std::vector<CategoryInfo> categories {};
109125
std::unordered_map<uint64_t, uint64_t> methodImpls;
110126

111127
std::string dump() const;
@@ -123,4 +139,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MethodListInfo, address, flags, methods)
123139

124140
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ClassInfo, listPointer, address, dataAddress,
125141
nameAddress, name, methodListAddress, methodList)
142+
143+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CategoryInfo, listPointer, address, nameAddress,
144+
name, instanceMethodListAddress, instanceMethods, classMethodListAddress,
145+
classMethods)
126146
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2022 Fabian Freyer. All rights reserved.
3+
*
4+
* Use of this source code is governed by the BSD 3-Clause license; the full
5+
* terms of the license can be found in the LICENSE.txt file.
6+
*/
7+
8+
#pragma once
9+
10+
#include <ObjectiveNinjaCore/Analyzer.h>
11+
12+
namespace ObjectiveNinja {
13+
14+
/**
15+
* Analyzer for extracting Objective-C class information.
16+
*/
17+
class CategoryAnalyzer : public Analyzer {
18+
/**
19+
* Analyze a method list.
20+
*/
21+
MethodListInfo analyzeMethodList(uint64_t);
22+
23+
public:
24+
CategoryAnalyzer(SharedAnalysisInfo, SharedAbstractFile);
25+
26+
void run() override;
27+
};
28+
29+
}

Plugin/CustomTypes.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ struct objc_class_t {
7373
void* vtable;
7474
const fptr_t data;
7575
};
76+
77+
struct objc_category_t {
78+
const char* name;
79+
void* class;
80+
const tptr_t instance_methods;
81+
const tptr_t class_methods;
82+
const tptr_t protocols;
83+
const tptr_t instance_properties;
84+
}
7685
)";
7786

7887
namespace CustomTypes {

Plugin/CustomTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const std::string Method = "objc_method_t";
2828
const std::string MethodListEntry = "objc_method_entry_t";
2929
const std::string Class = "objc_class_t";
3030
const std::string ClassRO = "objc_class_ro_t";
31+
const std::string Category = "objc_category_t";
3132

3233
/**
3334
* Define all Objective-C-related types for a view.

Plugin/InfoHandler.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
125125
BinaryReader reader(bv);
126126

127127
auto taggedPointerType = namedType(bv, CustomTypes::TaggedPointer);
128+
auto categoryType = namedType(bv, CustomTypes::Category);
128129
auto cfStringType = namedType(bv, CustomTypes::CFString);
129130
auto classType = namedType(bv, CustomTypes::Class);
130131
auto classDataType = namedType(bv, CustomTypes::ClassRO);
@@ -198,6 +199,62 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
198199
defineSymbol(bv, ci.methodListAddress, ci.name, "ml_");
199200
}
200201

202+
// Create data variables and symbols for the analyzed Categories.
203+
for (const auto& ci : info->categories) {
204+
defineVariable(bv, ci.listPointer, taggedPointerType);
205+
defineVariable(bv, ci.address, categoryType);
206+
207+
defineSymbol(bv, ci.listPointer, ci.name, "catp_");
208+
defineSymbol(bv, ci.address, ci.name, "cat_");
209+
210+
defineReference(bv, ci.listPointer, ci.address);
211+
212+
if (ci.instanceMethods.address && !ci.instanceMethods.methods.empty()) {
213+
auto methodType = ci.instanceMethods.hasRelativeOffsets()
214+
? bv->GetTypeByName(CustomTypes::MethodListEntry)
215+
: bv->GetTypeByName(CustomTypes::Method);
216+
217+
// Create data variables for each method in the method list.
218+
for (const auto& mi : ci.instanceMethods.methods) {
219+
defineVariable(bv, mi.address, methodType);
220+
defineSymbol(bv, mi.address, sanitizeSelector(mi.selector), "mt_");
221+
defineVariable(bv, mi.typeAddress, stringType(mi.type.size()));
222+
223+
defineReference(bv, ci.instanceMethods.address, mi.address);
224+
defineReference(bv, mi.address, mi.nameAddress);
225+
defineReference(bv, mi.address, mi.typeAddress);
226+
defineReference(bv, mi.address, mi.implAddress);
227+
}
228+
229+
// Create a data variable and symbol for the method list header.
230+
defineVariable(bv, ci.instanceMethodListAddress, methodListType);
231+
defineSymbol(bv, ci.instanceMethodListAddress, ci.name, "mli_");
232+
}
233+
234+
235+
if (ci.classMethods.address && !ci.classMethods.methods.empty()) {
236+
auto methodType = ci.classMethods.hasRelativeOffsets()
237+
? bv->GetTypeByName(CustomTypes::MethodListEntry)
238+
: bv->GetTypeByName(CustomTypes::Method);
239+
240+
// Create data variables for each method in the method list.
241+
for (const auto& mi : ci.classMethods.methods) {
242+
defineVariable(bv, mi.address, methodType);
243+
defineSymbol(bv, mi.address, sanitizeSelector(mi.selector), "mt_");
244+
defineVariable(bv, mi.typeAddress, stringType(mi.type.size()));
245+
246+
defineReference(bv, ci.classMethods.address, mi.address);
247+
defineReference(bv, mi.address, mi.nameAddress);
248+
defineReference(bv, mi.address, mi.typeAddress);
249+
defineReference(bv, mi.address, mi.implAddress);
250+
}
251+
252+
// Create a data variable and symbol for the method list header.
253+
defineVariable(bv, ci.classMethodListAddress, methodListType);
254+
defineSymbol(bv, ci.classMethodListAddress, ci.name, "mlc_");
255+
}
256+
}
257+
201258
bv->CommitUndoActions();
202259
bv->UpdateAnalysis();
203260
}

0 commit comments

Comments
 (0)