Skip to content

Commit

Permalink
- Process Ivars, Create custom types for ObjC Classes
Browse files Browse the repository at this point in the history
- Fix an issue where pointer typestrings weren't parsed correctly
  • Loading branch information
0cyn committed Nov 10, 2022
1 parent 992c374 commit c86cf56
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 7 deletions.
9 changes: 9 additions & 0 deletions Core/AnalysisInfo.cpp
Expand Up @@ -41,6 +41,15 @@ bool MethodListInfo::hasDirectSelectors() const
return (flags & FlagsMask) & 0x40000000;
}

std::string IvarInfo::decodedTypeToken() const
{
std::vector<std::string> encodedTypes = TypeParser::parseEncodedType(type);
if (encodedTypes.size() > 0)
return encodedTypes.front();
else
return {};
}

std::string AnalysisInfo::dump() const
{
return "<unimplemented>";
Expand Down
34 changes: 34 additions & 0 deletions Core/AnalysisInfo.h
Expand Up @@ -80,6 +80,38 @@ struct MethodListInfo {
bool hasDirectSelectors() const;
};

/**
* A description of an Objective-C instance variable (ivar).
*/
struct IvarInfo
{
uint64_t address = {};

uint32_t offset;
std::string name;
std::string type;

uint64_t offsetAddress {};
uint64_t nameAddress {};
uint64_t typeAddress {};
uint32_t size {};

/**
* Get the instance variable's type as a C-style token.
*/
std::string decodedTypeToken() const;
};

/**
* A description of an Objective-C instance variable list.
*/
struct IvarListInfo {
uint64_t address {};

uint32_t count {};
std::vector<IvarInfo> ivars {};
};

/**
* A description of an Objective-C class.
*/
Expand All @@ -88,11 +120,13 @@ struct ClassInfo {

std::string name {};
MethodListInfo methodList {};
IvarListInfo ivarList {};

uint64_t listPointer {};
uint64_t dataAddress {};
uint64_t nameAddress {};
uint64_t methodListAddress {};
uint64_t ivarListAddress {};
};

struct ClassRefInfo {
Expand Down
37 changes: 37 additions & 0 deletions Core/Analyzers/ClassAnalyzer.cpp
Expand Up @@ -57,6 +57,39 @@ MethodListInfo ClassAnalyzer::analyzeMethodList(uint64_t address)
return mli;
}

IvarListInfo ClassAnalyzer::analyzeIvarList(uint64_t address)
{
IvarListInfo ili;
ili.address = address;
auto ivarCount = m_file->readInt(ili.address + 4);

ili.ivars.reserve(ivarCount);

auto ivarSize = 32; // (Pointer Size * 3) + 8

for (unsigned i = 0; i < ivarCount; ++i)
{
IvarInfo ii;
ii.address = ili.address + 8 + (i * ivarSize);

m_file->seek(ii.address);

ii.offsetAddress = arp(m_file->readLong());
ii.nameAddress = arp(m_file->readLong());
ii.typeAddress = arp(m_file->readLong());
m_file->readInt();
ii.size = m_file->readInt();

ii.offset = m_file->readInt(ii.offsetAddress);
ii.name = m_file->readStringAt(ii.nameAddress);
ii.type = m_file->readStringAt(ii.typeAddress);

ili.ivars.push_back(ii);
}

return ili;
}

void ClassAnalyzer::run()
{
const auto sectionStart = m_file->sectionStart("__objc_classlist");
Expand All @@ -82,6 +115,10 @@ void ClassAnalyzer::run()
if (ci.methodListAddress)
ci.methodList = analyzeMethodList(ci.methodListAddress);

ci.ivarListAddress = arp(m_file->readLong(ci.dataAddress + 0x30));
if (ci.ivarListAddress)
ci.ivarList = analyzeIvarList(ci.ivarListAddress);

m_info->classes.emplace_back(ci);
}
}
5 changes: 5 additions & 0 deletions Core/Analyzers/ClassAnalyzer.h
Expand Up @@ -20,6 +20,11 @@ class ClassAnalyzer : public Analyzer {
*/
MethodListInfo analyzeMethodList(uint64_t);

/**
* Analyze an ivar list.
*/
IvarListInfo analyzeIvarList(uint64_t);

public:
ClassAnalyzer(SharedAnalysisInfo, SharedAbstractFile);

Expand Down
17 changes: 15 additions & 2 deletions Core/TypeParser.cpp
Expand Up @@ -42,17 +42,28 @@ static const std::map<char, std::string> TypeEncodingMap = {
std::vector<std::string> TypeParser::parseEncodedType(const std::string& encodedType)
{
std::vector<std::string> result;
int pointerDepth = 0;

for (size_t i = 0; i < encodedType.size(); ++i) {
char c = encodedType[i];

// K: For example, ^@ is a single type, "id*".
if (c == '^') {
pointerDepth++;
continue;
}

// Argument frame size and offset specifiers aren't relevant here; they
// should just be skipped.
if (std::isdigit(c))
continue;

if (TypeEncodingMap.count(c)) {
result.emplace_back(TypeEncodingMap.at(c));
if (auto it = TypeEncodingMap.find(c); it != TypeEncodingMap.end()) {
std::string encoding = it->second;
for (int j = pointerDepth; j > 0; j--)
encoding += "*";
pointerDepth = 0;
result.emplace_back(encoding);
continue;
}

Expand All @@ -62,6 +73,7 @@ std::vector<std::string> TypeParser::parseEncodedType(const std::string& encoded
i++;

// TODO: Emit real type names.
pointerDepth = 0;
result.emplace_back("void*");
continue;
}
Expand All @@ -80,6 +92,7 @@ std::vector<std::string> TypeParser::parseEncodedType(const std::string& encoded
}

// TODO: Emit real struct types.
pointerDepth = 0;
result.emplace_back("void*");
continue;
}
Expand Down
2 changes: 2 additions & 0 deletions CustomTypes.h
Expand Up @@ -26,6 +26,8 @@ const std::string CFString = "CFString";
const std::string MethodList = "objc_method_list_t";
const std::string Method = "objc_method_t";
const std::string MethodListEntry = "objc_method_entry_t";
const std::string IvarList = "objc_ivar_list_t";
const std::string Ivar = "objc_ivar_t";
const std::string Class = "objc_class_t";
const std::string ClassRO = "objc_class_ro_t";

Expand Down
59 changes: 55 additions & 4 deletions InfoHandler.cpp
Expand Up @@ -70,7 +70,7 @@ void InfoHandler::defineReference(BinaryViewRef bv, uint64_t from, uint64_t to)
}

void InfoHandler::applyMethodType(BinaryViewRef bv, const ObjectiveNinja::ClassInfo& ci,
const ObjectiveNinja::MethodInfo& mi)
const BinaryNinja::QualifiedName& classTypeName, const ObjectiveNinja::MethodInfo& mi)
{
auto selectorTokens = mi.selectorTokens();
auto typeTokens = mi.decodedTypeTokens();
Expand All @@ -84,7 +84,7 @@ void InfoHandler::applyMethodType(BinaryViewRef bv, const ObjectiveNinja::ClassI
}

// Shorthand for formatting an individual "part" of the type signature.
auto partForIndex = [selectorTokens, typeTokens](size_t i) {
auto partForIndex = [selectorTokens, typeTokens, classTypeName](size_t i) {
std::string argName;

// Indices 0, 1, and 2 are the function return type, self parameter, and
Expand All @@ -99,7 +99,11 @@ void InfoHandler::applyMethodType(BinaryViewRef bv, const ObjectiveNinja::ClassI
else if (i - 3 < selectorTokens.size())
argName = selectorTokens[i - 3];

return typeTokens[i] + " " + argName;
// Inject our custom class type only if one was provided.
if (i == 1 && !classTypeName.IsEmpty())
return classTypeName.GetString() + " " + argName;
else
return typeTokens[i] + " " + argName;
};

// Build the type string for the method.
Expand Down Expand Up @@ -141,6 +145,39 @@ void InfoHandler::applyMethodType(BinaryViewRef bv, const ObjectiveNinja::ClassI
defineSymbol(bv, mi.implAddress, name, "", FunctionSymbol);
}

QualifiedName InfoHandler::createClassType(BinaryViewRef bv, const ObjectiveNinja::ClassInfo& info, const ObjectiveNinja::IvarListInfo& vi)
{
StructureBuilder classTypeBuilder;
for (const auto& ivar : vi.ivars)
{
auto encodedType = ivar.decodedTypeToken();
if (encodedType.empty())
{
BinaryNinja::LogWarn("Failed to process ivar type %s", ivar.type.c_str());
}
std::string errors;
TypeParserResult tpResult;
auto ok = bv->ParseTypesFromSource(encodedType + " _;", {}, {}, tpResult, errors);
if (ok && !tpResult.variables.empty())
{
classTypeBuilder.AddMemberAtOffset(tpResult.variables[0].type, ivar.name, ivar.offset);
}
}
auto classTypeStruct = classTypeBuilder.Finalize();
QualifiedName classTypeName = "class_" + std::string(info.name);
std::string classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
Ref<Type> classType = Type::StructureType(classTypeStruct);
QualifiedName classQualName = bv->DefineType(classTypeId, classTypeName, classType);

std::string typeDefType = "typedef struct " + classTypeName.GetString() + "* " + info.name + ";";
std::string errors;
TypeParserResult tpResult;
auto ok = bv->ParseTypesFromSource(typeDefType, {}, {}, tpResult, errors);
bv->DefineUserType(info.name, tpResult.types[0].type);

return info.name;
}

void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
{
auto start = Performance::now();
Expand All @@ -154,6 +191,8 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
auto classType = namedType(bv, CustomTypes::Class);
auto classDataType = namedType(bv, CustomTypes::ClassRO);
auto methodListType = namedType(bv, CustomTypes::MethodList);
auto ivarListType = namedType(bv, CustomTypes::IvarList);
auto ivarType = namedType(bv, CustomTypes::Ivar);

// Create data variables and symbols for all CFString instances.
for (const auto& csi : info->cfStrings) {
Expand Down Expand Up @@ -202,6 +241,8 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
defineReference(bv, ci.dataAddress, ci.nameAddress);
defineReference(bv, ci.dataAddress, ci.methodListAddress);

auto methodSelfType = createClassType(bv, ci, ci.ivarList);

if (ci.methodList.address == 0 || ci.methodList.methods.empty())
continue;

Expand All @@ -222,7 +263,17 @@ void InfoHandler::applyInfoToView(SharedAnalysisInfo info, BinaryViewRef bv)
defineReference(bv, mi.address, mi.typeAddress);
defineReference(bv, mi.address, mi.implAddress);

applyMethodType(bv, ci, mi);
applyMethodType(bv, ci, methodSelfType, mi);
}

if (ci.ivarListAddress != 0) {
defineVariable(bv, ci.ivarListAddress, ivarListType);
defineSymbol(bv, ci.ivarListAddress, ci.name, "vl_");

for (const auto& ii : ci.ivarList.ivars) {
defineVariable(bv, ii.address, ivarType);
defineSymbol(bv, ii.address, ii.name, "iv_");
}
}

// Create a data variable and symbol for the method list header.
Expand Down
5 changes: 4 additions & 1 deletion InfoHandler.h
Expand Up @@ -64,7 +64,10 @@ class InfoHandler {
* Create a symbol and apply return/argument types for a method.
*/
static void applyMethodType(BinaryViewRef, const ObjectiveNinja::ClassInfo&,
const ObjectiveNinja::MethodInfo&);
const BinaryNinja::QualifiedName& classTypeName, const ObjectiveNinja::MethodInfo&);

static BinaryNinja::QualifiedName createClassType(BinaryViewRef,
const ObjectiveNinja::ClassInfo&, const ObjectiveNinja::IvarListInfo&);

public:
/**
Expand Down

0 comments on commit c86cf56

Please sign in to comment.