Skip to content
This repository was archived by the owner on Jul 11, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_subdirectory(Vendor/BinaryNinjaAPI)
# Core library -----------------------------------------------------------------

set(CORE_SOURCE
Include/ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h
Include/ObjectiveNinjaCore/Analyzers/CFStringAnalyzer.h
Include/ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h
Include/ObjectiveNinjaCore/Analyzers/SelectorAnalyzer.h
Expand All @@ -36,6 +37,7 @@ set(CORE_SOURCE
Include/ObjectiveNinjaCore/AnalysisProvider.h
Include/ObjectiveNinjaCore/Analyzer.h
Include/ObjectiveNinjaCore/TypeParser.h
Core/Analyzers/CategoryAnalyzer.cpp
Core/Analyzers/CFStringAnalyzer.cpp
Core/Analyzers/ClassAnalyzer.cpp
Core/Analyzers/SelectorAnalyzer.cpp
Expand Down
2 changes: 2 additions & 0 deletions Core/AnalysisProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <ObjectiveNinjaCore/Analyzers/CFStringAnalyzer.h>
#include <ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h>
#include <ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h>
#include <ObjectiveNinjaCore/Analyzers/SelectorAnalyzer.h>

namespace ObjectiveNinja {
Expand All @@ -20,6 +21,7 @@ SharedAnalysisInfo AnalysisProvider::infoForFile(SharedAbstractFile file)
std::vector<std::unique_ptr<ObjectiveNinja::Analyzer>> analyzers;
analyzers.emplace_back(new SelectorAnalyzer(info, file));
analyzers.emplace_back(new ClassAnalyzer(info, file));
analyzers.emplace_back(new CategoryAnalyzer(info, file));
analyzers.emplace_back(new CFStringAnalyzer(info, file));

for (const auto& analyzer : analyzers)
Expand Down
81 changes: 81 additions & 0 deletions Core/Analyzers/CategoryAnalyzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include <ObjectiveNinjaCore/Analyzers/ClassAnalyzer.h>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is also missing a copyright header

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary #include here

#include <ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h>

#include <ObjectiveNinjaCore/TypeParser.h>
Copy link
Owner

@jonpalmisc jonpalmisc May 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also unnecessary to #include <ObjectiveNinjaCore/TypeParser.h> (although ClassAnalyzer.cpp makes this mistake too, not your fault)


using namespace ObjectiveNinja;


CategoryAnalyzer::CategoryAnalyzer(SharedAnalysisInfo info,
SharedAbstractFile file)
: Analyzer(std::move(info), std::move(file))
{
}

MethodListInfo CategoryAnalyzer::analyzeMethodList(uint64_t address)
{
MethodListInfo mli;
mli.address = address;
mli.flags = m_file->readInt(mli.address);

auto methodCount = m_file->readInt(mli.address + 0x4);
auto methodSize = mli.hasRelativeOffsets() ? 12 : 24;

for (unsigned i = 0; i < methodCount; ++i) {
MethodInfo mi;
mi.address = mli.address + 8 + (i * methodSize);

m_file->seek(mi.address);

if (mli.hasRelativeOffsets()) {
mi.nameAddress = mi.address + static_cast<int32_t>(m_file->readInt());
mi.typeAddress = mi.address + 4 + static_cast<int32_t>(m_file->readInt());
mi.implAddress = mi.address + 8 + static_cast<int32_t>(m_file->readInt());
} else {
mi.nameAddress = arp(m_file->readLong());
mi.typeAddress = arp(m_file->readLong());
mi.implAddress = arp(m_file->readLong());
}

if (!mli.hasRelativeOffsets() || mli.hasDirectSelectors()) {
mi.selector = m_file->readStringAt(mi.nameAddress);
} else {
auto selectorNamePointer = arp(m_file->readLong(mi.nameAddress));
mi.selector = m_file->readStringAt(selectorNamePointer);
}

mi.type = m_file->readStringAt(mi.typeAddress);

m_info->methodImpls[mi.nameAddress] = mi.implAddress;

mli.methods.emplace_back(mi);
}

return mli;
}

void CategoryAnalyzer::run()
{
const auto sectionStart = m_file->sectionStart("__objc_catlist");
const auto sectionEnd = m_file->sectionEnd("__objc_catlist");
if (sectionStart == 0 || sectionEnd == 0)
return;

for (auto address = sectionStart; address < sectionEnd; address += 8) {
CategoryInfo ci;
ci.listPointer = address;
ci.address = arp(m_file->readLong(address));
ci.nameAddress = arp(m_file->readLong(ci.address));
ci.name = m_file->readStringAt(ci.nameAddress);
ci.instanceMethodListAddress = arp(m_file->readLong(ci.address + 0x10));
ci.classMethodListAddress = arp(m_file->readLong(ci.address + 0x18));

if (ci.instanceMethodListAddress)
ci.instanceMethods = analyzeMethodList(ci.instanceMethodListAddress);

if (ci.classMethodListAddress)
ci.classMethods = analyzeMethodList(ci.classMethodListAddress);

m_info->categories.emplace_back(ci);
}
}
20 changes: 20 additions & 0 deletions Include/ObjectiveNinjaCore/AnalysisInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ struct ClassInfo {
uint64_t methodListAddress {};
};

/**
* A description of an Objective-C category.
*/
struct CategoryInfo {
uint64_t address {};
std::string name {};
MethodListInfo instanceMethods {};
MethodListInfo classMethods {};

uint64_t listPointer {};
uint64_t nameAddress {};
uint64_t instanceMethodListAddress {};
uint64_t classMethodListAddress {};
};

/**
* Analysis info storage.
*
Expand All @@ -106,6 +121,7 @@ struct AnalysisInfo {
std::unordered_map<uint64_t, SharedSelectorRefInfo> selectorRefsByKey {};

std::vector<ClassInfo> classes {};
std::vector<CategoryInfo> categories {};
std::unordered_map<uint64_t, uint64_t> methodImpls;

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

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ClassInfo, listPointer, address, dataAddress,
nameAddress, name, methodListAddress, methodList)

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CategoryInfo, listPointer, address, nameAddress,
name, instanceMethodListAddress, instanceMethods, classMethodListAddress,
classMethods)
}
29 changes: 29 additions & 0 deletions Include/ObjectiveNinjaCore/Analyzers/CategoryAnalyzer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 Fabian Freyer. All rights reserved.
*
* Use of this source code is governed by the BSD 3-Clause license; the full
* terms of the license can be found in the LICENSE.txt file.
*/

#pragma once

#include <ObjectiveNinjaCore/Analyzer.h>

namespace ObjectiveNinja {

/**
* Analyzer for extracting Objective-C class information.
*/
class CategoryAnalyzer : public Analyzer {
/**
* Analyze a method list.
*/
MethodListInfo analyzeMethodList(uint64_t);

public:
CategoryAnalyzer(SharedAnalysisInfo, SharedAbstractFile);

void run() override;
};

}
9 changes: 9 additions & 0 deletions Plugin/CustomTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ struct objc_class_t {
void* vtable;
const fptr_t data;
};

struct objc_category_t {
const char* name;
void* class;
const tptr_t instance_methods;
const tptr_t class_methods;
const tptr_t protocols;
const tptr_t instance_properties;
}
)";

namespace CustomTypes {
Expand Down
1 change: 1 addition & 0 deletions Plugin/CustomTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const std::string Method = "objc_method_t";
const std::string MethodListEntry = "objc_method_entry_t";
const std::string Class = "objc_class_t";
const std::string ClassRO = "objc_class_ro_t";
const std::string Category = "objc_category_t";

/**
* Define all Objective-C-related types for a view.
Expand Down
57 changes: 57 additions & 0 deletions Plugin/InfoHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
BinaryReader reader(bv);

auto taggedPointerType = namedType(bv, CustomTypes::TaggedPointer);
auto categoryType = namedType(bv, CustomTypes::Category);
auto cfStringType = namedType(bv, CustomTypes::CFString);
auto classType = namedType(bv, CustomTypes::Class);
auto classDataType = namedType(bv, CustomTypes::ClassRO);
Expand Down Expand Up @@ -198,6 +199,62 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
defineSymbol(bv, ci.methodListAddress, ci.name, "ml_");
}

// Create data variables and symbols for the analyzed Categories.
for (const auto& ci : info->categories) {
defineVariable(bv, ci.listPointer, taggedPointerType);
defineVariable(bv, ci.address, categoryType);

defineSymbol(bv, ci.listPointer, ci.name, "catp_");
defineSymbol(bv, ci.address, ci.name, "cat_");

defineReference(bv, ci.listPointer, ci.address);

if (ci.instanceMethods.address && !ci.instanceMethods.methods.empty()) {
auto methodType = ci.instanceMethods.hasRelativeOffsets()
? bv->GetTypeByName(CustomTypes::MethodListEntry)
: bv->GetTypeByName(CustomTypes::Method);

// Create data variables for each method in the method list.
for (const auto& mi : ci.instanceMethods.methods) {
defineVariable(bv, mi.address, methodType);
defineSymbol(bv, mi.address, sanitizeSelector(mi.selector), "mt_");
defineVariable(bv, mi.typeAddress, stringType(mi.type.size()));

defineReference(bv, ci.instanceMethods.address, mi.address);
defineReference(bv, mi.address, mi.nameAddress);
defineReference(bv, mi.address, mi.typeAddress);
defineReference(bv, mi.address, mi.implAddress);
}

// Create a data variable and symbol for the method list header.
defineVariable(bv, ci.instanceMethodListAddress, methodListType);
defineSymbol(bv, ci.instanceMethodListAddress, ci.name, "mli_");
}


if (ci.classMethods.address && !ci.classMethods.methods.empty()) {
auto methodType = ci.classMethods.hasRelativeOffsets()
? bv->GetTypeByName(CustomTypes::MethodListEntry)
: bv->GetTypeByName(CustomTypes::Method);

// Create data variables for each method in the method list.
for (const auto& mi : ci.classMethods.methods) {
defineVariable(bv, mi.address, methodType);
defineSymbol(bv, mi.address, sanitizeSelector(mi.selector), "mt_");
defineVariable(bv, mi.typeAddress, stringType(mi.type.size()));

defineReference(bv, ci.classMethods.address, mi.address);
defineReference(bv, mi.address, mi.nameAddress);
defineReference(bv, mi.address, mi.typeAddress);
defineReference(bv, mi.address, mi.implAddress);
}

// Create a data variable and symbol for the method list header.
defineVariable(bv, ci.classMethodListAddress, methodListType);
defineSymbol(bv, ci.classMethodListAddress, ci.name, "mlc_");
}
}

bv->CommitUndoActions();
bv->UpdateAnalysis();
}