Skip to content

[ObjC] Parse sections containing Objective-C constants #6559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
277 changes: 277 additions & 0 deletions objectivec/objc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,15 @@ void ObjCProcessor::ProcessObjCData()
m_relocationPointerRewrites.clear();
}

void ObjCProcessor::ProcessObjCLiterals()
{
ProcessCFStrings();
ProcessNSConstantArrays();
ProcessNSConstantDictionaries();
ProcessNSConstantIntegerNumbers();
ProcessNSConstantFloatingPointNumbers();
ProcessNSConstantDatas();
}

void ObjCProcessor::ProcessCFStrings()
{
Expand Down Expand Up @@ -1570,6 +1579,274 @@ void ObjCProcessor::ProcessCFStrings()
delete m_symbolQueue;
}

void ObjCProcessor::ProcessNSConstantArrays()
{
m_symbolQueue = new SymbolQueue();
uint64_t ptrSize = m_data->GetAddressSize();

auto idType = Type::NamedType(m_data, m_typeNames.id);
StructureBuilder nsConstantArrayBuilder;
nsConstantArrayBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantArrayBuilder.AddMember(Type::IntegerType(ptrSize, false), "count");
nsConstantArrayBuilder.AddMember(Type::PointerType(ptrSize, idType), "objects");
auto type = finalizeStructureBuilder(m_data, nsConstantArrayBuilder, "__NSConstantArray");
m_typeNames.nsConstantArray = type.first;

auto reader = GetReader();
if (auto arrays = GetSectionWithName("__objc_arrayobj"))
{
auto start = arrays->GetStart();
auto end = arrays->GetEnd();
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantArray)->GetWidth();
m_data->BeginBulkModifySymbols();
for (view_ptr_t i = start; i < end; i += typeWidth)
{
reader->Seek(i + ptrSize);
uint64_t count = reader->ReadPointer();
auto dataLoc = ReadPointerAccountingForRelocations(reader.get());
DefineObjCSymbol(
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsarray_{:x}_data", i), dataLoc, true);
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantArray),
fmt::format("nsarray_{:x}", i), i, true);
}
auto id = m_data->BeginUndoActions();
m_symbolQueue->Process();
m_data->EndBulkModifySymbols();
m_data->ForgetUndoActions(id);
}
delete m_symbolQueue;
}

void ObjCProcessor::ProcessNSConstantDictionaries()
{
m_symbolQueue = new SymbolQueue();
uint64_t ptrSize = m_data->GetAddressSize();

auto idType = Type::NamedType(m_data, m_typeNames.id);
StructureBuilder nsConstantDictionaryBuilder;
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantDictionaryBuilder.AddMember(Type::IntegerType(ptrSize, false), "options");
nsConstantDictionaryBuilder.AddMember(Type::IntegerType(ptrSize, false), "count");
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, idType), "keys");
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, idType), "objects");
auto type = finalizeStructureBuilder(m_data, nsConstantDictionaryBuilder, "__NSConstantDictionary");
m_typeNames.nsConstantDictionary = type.first;

auto reader = GetReader();
if (auto dicts = GetSectionWithName("__objc_dictobj"))
{
auto start = dicts->GetStart();
auto end = dicts->GetEnd();
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantDictionary)->GetWidth();
m_data->BeginBulkModifySymbols();
for (view_ptr_t i = start; i < end; i += typeWidth)
{
reader->Seek(i + (ptrSize * 2));
// TODO: Do we need to do anything with `options`? It appears to always be 1.
uint64_t count = reader->ReadPointer();
auto keysLoc = ReadPointerAccountingForRelocations(reader.get());
auto objectsLoc = ReadPointerAccountingForRelocations(reader.get());
DefineObjCSymbol(
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsdict_{:x}_keys", i), keysLoc, true);
DefineObjCSymbol(
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsdict_{:x}_objects", i), objectsLoc, true);
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantDictionary),
fmt::format("nsdict_{:x}", i), i, true);
}
auto id = m_data->BeginUndoActions();
m_symbolQueue->Process();
m_data->EndBulkModifySymbols();
m_data->ForgetUndoActions(id);
}
delete m_symbolQueue;
}

void ObjCProcessor::ProcessNSConstantIntegerNumbers()
{
m_symbolQueue = new SymbolQueue();
uint64_t ptrSize = m_data->GetAddressSize();

StructureBuilder nsConstantIntegerNumberBuilder;
nsConstantIntegerNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantIntegerNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "encoding");
nsConstantIntegerNumberBuilder.AddMember(Type::IntegerType(ptrSize, true), "value");
auto type = finalizeStructureBuilder(m_data, nsConstantIntegerNumberBuilder, "__NSConstantIntegerNumber");
m_typeNames.nsConstantIntegerNumber = type.first;

auto reader = GetReader();
if (auto numbers = GetSectionWithName("__objc_intobj"))
{
auto start = numbers->GetStart();
auto end = numbers->GetEnd();
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber)->GetWidth();
m_data->BeginBulkModifySymbols();
for (view_ptr_t i = start; i < end; i += typeWidth)
{
reader->Seek(i + ptrSize);
uint64_t encodingLoc = ReadPointerAccountingForRelocations(reader.get());
uint64_t value = reader->Read64();
reader->Seek(encodingLoc);
uint8_t encoding = reader->Read8();

switch (encoding)
{
case 'c':
case 's':
case 'i':
case 'l':
case 'q':
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber),
fmt::format("nsint_{:x}_{}", i, (int64_t)value), i, true);
break;
case 'C':
case 'S':
case 'I':
case 'L':
case 'Q':
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber),
fmt::format("nsint_{:x}_{}", i, value), i, true);
break;
default:
m_logger->LogWarn("Unknown type encoding '%c' in number literal object at %p", encoding, i);
continue;
}
}
auto id = m_data->BeginUndoActions();
m_symbolQueue->Process();
m_data->EndBulkModifySymbols();
m_data->ForgetUndoActions(id);
}
delete m_symbolQueue;
}

void ObjCProcessor::ProcessNSConstantFloatingPointNumbers()
{
uint64_t ptrSize = m_data->GetAddressSize();

StructureBuilder nsConstantFloatNumberBuilder;
nsConstantFloatNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantFloatNumberBuilder.AddMember(Type::FloatType(4), "value");
auto type = finalizeStructureBuilder(m_data, nsConstantFloatNumberBuilder, "__NSConstantFloatNumber");
m_typeNames.nsConstantFloatNumber = type.first;

StructureBuilder nsConstantDoubleNumberBuilder;
nsConstantDoubleNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantDoubleNumberBuilder.AddMember(Type::FloatType(8), "value");
type = finalizeStructureBuilder(m_data, nsConstantDoubleNumberBuilder, "__NSConstantDoubleNumber");
m_typeNames.nsConstantDoubleNumber = type.first;

StructureBuilder nsConstantDateBuilder;
nsConstantDateBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantDateBuilder.AddMember(Type::FloatType(8), "ti");
type = finalizeStructureBuilder(m_data, nsConstantDateBuilder, "__NSConstantDate");
m_typeNames.nsConstantDate = type.first;

enum SectionType
{
Float,
Double,
Date,
};

constexpr std::pair<std::string_view, SectionType> sections[] = {
{"__objc_floatobj", Float},
{"__objc_doubleobj", Double},
{"__objc_dateobj", Date},
};

auto reader = GetReader();
for (auto& [sectionName, sectionType] : sections)
{
auto numbers = GetSectionWithName(sectionName.data());
if (!numbers)
continue;

m_symbolQueue = new SymbolQueue();
auto start = numbers->GetStart();
auto end = numbers->GetEnd();
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantDoubleNumber)->GetWidth();
m_data->BeginBulkModifySymbols();
for (view_ptr_t i = start; i < end; i += typeWidth)
{
reader->Seek(i + ptrSize);

QualifiedName* typeName = nullptr;
std::string name;

switch (sectionType)
{
case Float:
{
float value = 0;
reader->Read(&value, sizeof(value));
name = fmt::format("nsfloat_{:x}_{}", i, value);
typeName = &m_typeNames.nsConstantFloatNumber;
break;
}
case Double:
{
double value = 0;
reader->Read(&value, sizeof(value));
name = fmt::format("nsdouble_{:x}_{}", i, value);
typeName = &m_typeNames.nsConstantDoubleNumber;
break;
}
case Date:
{
double value = 0;
reader->Read(&value, sizeof(value));
name = fmt::format("nsdate_{:x}_{}", i, value);
typeName = &m_typeNames.nsConstantDate;
break;
}
}
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, *typeName), name, i, true);
}
auto id = m_data->BeginUndoActions();
m_symbolQueue->Process();
m_data->EndBulkModifySymbols();
m_data->ForgetUndoActions(id);
delete m_symbolQueue;
}
}

void ObjCProcessor::ProcessNSConstantDatas()
{
m_symbolQueue = new SymbolQueue();
uint64_t ptrSize = m_data->GetAddressSize();

StructureBuilder nsConstantDataBuilder;
nsConstantDataBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
nsConstantDataBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
nsConstantDataBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, false)), "bytes");
auto type = finalizeStructureBuilder(m_data, nsConstantDataBuilder, "__NSConstantData");
m_typeNames.nsConstantData = type.first;

auto reader = GetReader();
if (auto datas = GetSectionWithName("__objc_dataobj"))
{
auto start = datas->GetStart();
auto end = datas->GetEnd();
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantData)->GetWidth();
m_data->BeginBulkModifySymbols();
for (view_ptr_t i = start; i < end; i += typeWidth)
{
reader->Seek(i + ptrSize);
uint64_t length = reader->ReadPointer();
auto dataLoc = ReadPointerAccountingForRelocations(reader.get());
DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, false), length),
fmt::format("nsdata_{:x}_data", i), dataLoc, true);
DefineObjCSymbol(
DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantData), fmt::format("nsdata_{:x}", i), i, true);
}
auto id = m_data->BeginUndoActions();
m_symbolQueue->Process();
m_data->EndBulkModifySymbols();
m_data->ForgetUndoActions(id);
}
delete m_symbolQueue;
}

void ObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
{
m_relocationPointerRewrites[location] = rewrite;
Expand Down
17 changes: 15 additions & 2 deletions objectivec/objc.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ namespace BinaryNinja {
QualifiedName protocolList;
QualifiedName ivar;
QualifiedName ivarList;
QualifiedName nsConstantArray;
QualifiedName nsConstantDictionary;
QualifiedName nsConstantDoubleNumber;
QualifiedName nsConstantFloatNumber;
QualifiedName nsConstantIntegerNumber;
QualifiedName nsConstantDate;
QualifiedName nsConstantData;
} m_typeNames;

bool m_isBackedByDatabase;
Expand Down Expand Up @@ -314,6 +321,13 @@ namespace BinaryNinja {
bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
void ApplyMethodTypes(Class& cls);

void ProcessCFStrings();
void ProcessNSConstantArrays();
void ProcessNSConstantDictionaries();
void ProcessNSConstantIntegerNumbers();
void ProcessNSConstantFloatingPointNumbers();
void ProcessNSConstantDatas();

void PostProcessObjCSections(ObjCReader* reader);

protected:
Expand All @@ -332,9 +346,8 @@ namespace BinaryNinja {
virtual ~ObjCProcessor() = default;

ObjCProcessor(BinaryView* data, const char* loggerName, bool isBackedByDatabase, bool skipClassBaseProtocols = false);
// TODO: Instead of passing in image name the processor must be given section refs in a structure that outlines all objc sections.
void ProcessObjCData();
void ProcessCFStrings();
void ProcessObjCLiterals();
void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
};
}
Expand Down
2 changes: 1 addition & 1 deletion view/macho/machoview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2336,7 +2336,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
if (parseCFStrings)
{
try {
m_objcProcessor->ProcessCFStrings();
m_objcProcessor->ProcessObjCLiterals();
}
catch (std::exception& ex)
{
Expand Down
2 changes: 1 addition & 1 deletion view/sharedcache/core/SharedCacheController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ bool SharedCacheController::ApplyImage(BinaryView& view, const CacheImage& image
if (m_processObjC)
objcProcessor.ProcessObjCData();
if (m_processCFStrings)
objcProcessor.ProcessCFStrings();
objcProcessor.ProcessObjCLiterals();
}
catch (std::exception& e)
{
Expand Down