Skip to content

Commit 9a3859f

Browse files
committed
[ObjC] Add support for method type strings that are pointers relative to selector base address
These show up in iOS 27 shared caches.
1 parent 1f509da commit 9a3859f

4 files changed

Lines changed: 54 additions & 14 deletions

File tree

objectivec/objc.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -918,8 +918,11 @@ void ObjCProcessor::LoadProtocols(ObjCReader* reader, Ref<Section> listSection)
918918
}
919919
}
920920

921-
void ObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth)
921+
void ObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth, bool typesAreOffsetsFromSelectorBase)
922922
{
923+
// `typesAreOffsetsFromSelectorBase` is only relevant for shared caches
924+
(void)typesAreOffsetsFromSelectorBase;
925+
923926
uint64_t offset = reader->GetOffset();
924927
meth.name = offset + reader->ReadS32();
925928

@@ -980,15 +983,16 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri
980983
uint64_t pointerSize = m_data->GetAddressSize();
981984
bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000;
982985
bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000;
986+
bool typesAreOffsetsFromSelectorBase = (head.entsizeAndFlags & 0xFFFF0000) & 0x20000000;
983987
auto methodSize = relativeOffsets ? 12 : pointerSize * 3;
984988
DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + std::string(name), start, true);
985989

986990
for (unsigned i = 0; i < head.count; i++)
987991
{
992+
auto cursor = start + sizeof(method_list_t) + (i * methodSize);
988993
try
989994
{
990995
Method method;
991-
auto cursor = start + sizeof(method_list_t) + (i * methodSize);
992996
reader->Seek(cursor);
993997
method_t meth;
994998
// workflow_objc support
@@ -997,7 +1001,7 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri
9971001
// --
9981002
if (relativeOffsets)
9991003
{
1000-
GetRelativeMethod(reader, meth);
1004+
GetRelativeMethod(reader, meth, typesAreOffsetsFromSelectorBase);
10011005
}
10021006
else
10031007
{
@@ -1050,8 +1054,12 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri
10501054
m_selRefToImplementations[selRefAddr].push_back(meth.imp);
10511055
// --
10521056

1053-
DefineObjCSymbol(DataSymbol, relativeOffsets ? m_typeNames.methodEntry : m_typeNames.method,
1054-
"method_" + method.name, cursor, true);
1057+
QualifiedName methodTypeName = m_typeNames.method;
1058+
if (relativeOffsets)
1059+
methodTypeName = typesAreOffsetsFromSelectorBase && !m_typeNames.methodEntryTypeOffsets.IsEmpty()
1060+
? m_typeNames.methodEntryTypeOffsets
1061+
: m_typeNames.methodEntry;
1062+
DefineObjCSymbol(DataSymbol, methodTypeName, "method_" + method.name, cursor, true);
10551063
method.imp = meth.imp;
10561064
cls.methodList[cursor] = method;
10571065
m_localMethods[cursor] = method;
@@ -1061,10 +1069,14 @@ void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::stri
10611069
if (selRefAddr)
10621070
m_data->AddDataReference(selRefAddr, meth.imp);
10631071
}
1072+
catch (const std::exception& ex)
1073+
{
1074+
m_logger->LogErrorF(
1075+
"Failed to process a method at offset {:#x} in method list \"{}\": {}", cursor, name, ex.what());
1076+
}
10641077
catch (...)
10651078
{
1066-
m_logger->LogError(
1067-
"Failed to process a method at offset 0x%llx", start + sizeof(method_list_t) + (i * methodSize));
1079+
m_logger->LogErrorF("Failed to process a method at offset {:#x} in method list \"{}\"", cursor, name);
10681080
}
10691081
}
10701082
}
@@ -1512,6 +1524,23 @@ void ObjCProcessor::ProcessObjCData()
15121524
auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t");
15131525
m_typeNames.methodEntry = type.first;
15141526

1527+
// Shared caches built with type offsets store the `types` field as an offset from the same base address as
1528+
// relative selectors. That base address is only known for shared caches, so the struct is only defined for them.
1529+
if (relativeSelectorBaseOffset)
1530+
{
1531+
auto relativeTypesPtrName = defineTypedef(m_data, {"rel_types"},
1532+
TypeBuilder::PointerType(4, Type::PointerType(addrSize, Type::IntegerType(1, false)))
1533+
.SetPointerBase(RelativeToConstantPointerBaseType, relativeSelectorBaseOffset)
1534+
.Finalize());
1535+
1536+
StructureBuilder methodEntryTypeOffsets;
1537+
methodEntryTypeOffsets.AddMember(Type::NamedType(m_data, relativeSelectorPtrName), "name");
1538+
methodEntryTypeOffsets.AddMember(Type::NamedType(m_data, relativeTypesPtrName), "types");
1539+
methodEntryTypeOffsets.AddMember(Type::NamedType(m_data, relativeIMPPtrName), "imp");
1540+
type = finalizeStructureBuilder(m_data, methodEntryTypeOffsets, "objc_method_entry_type_offsets_t");
1541+
m_typeNames.methodEntryTypeOffsets = type.first;
1542+
}
1543+
15151544
StructureBuilder method;
15161545
method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
15171546
method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "types");

objectivec/objc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ namespace BinaryNinja {
268268
QualifiedName imageInfoSwiftVersion;
269269
QualifiedName imageInfo;
270270
QualifiedName methodEntry;
271+
QualifiedName methodEntryTypeOffsets;
271272
QualifiedName method;
272273
QualifiedName methodList;
273274
QualifiedName classRO;
@@ -337,7 +338,7 @@ namespace BinaryNinja {
337338
Ref<Logger> m_logger;
338339

339340
virtual uint64_t GetObjCRelativeMethodBaseAddress(ObjCReader* reader);
340-
virtual void GetRelativeMethod(ObjCReader* reader, method_t& meth);
341+
virtual void GetRelativeMethod(ObjCReader* reader, method_t& meth, bool typesAreOffsetsFromSelectorBase);
341342
virtual std::shared_ptr<ObjCReader> GetReader() = 0;
342343
// Because an objective-c processor might have access to other non-view symbols that we want to retrieve.
343344
// By default, this will just get symbol at the address in the view.

view/sharedcache/core/ObjC.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,30 @@ std::shared_ptr<ObjCReader> SharedCacheObjCProcessor::GetReader()
9292
return std::make_shared<SharedCacheObjCReader>(reader);
9393
}
9494

95-
void SharedCacheObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth)
95+
void SharedCacheObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth, bool typesAreOffsetsFromSelectorBase)
9696
{
9797
if (m_customRelativeMethodSelectorBase.has_value())
9898
{
9999
meth.name = m_customRelativeMethodSelectorBase.value() + reader->ReadS32();
100100

101-
uint64_t offset = reader->GetOffset();
102-
meth.types = offset + reader->ReadS32();
101+
// `typesAreOffsetsFromSelectorBase` indicates that `types` is an unsigned offset into the cache's
102+
// deduplicated string pool, relative to the same base address as relative method selectors.
103+
if (typesAreOffsetsFromSelectorBase)
104+
{
105+
meth.types = m_customRelativeMethodSelectorBase.value() + reader->Read32();
106+
}
107+
else
108+
{
109+
uint64_t offset = reader->GetOffset();
110+
meth.types = offset + reader->ReadS32();
111+
}
103112

104-
offset += sizeof(int32_t);
113+
uint64_t offset = reader->GetOffset();
105114
meth.imp = offset + reader->ReadS32();
106115
}
107116
else
108117
{
109-
ObjCProcessor::GetRelativeMethod(reader, meth);
118+
ObjCProcessor::GetRelativeMethod(reader, meth, typesAreOffsetsFromSelectorBase);
110119
}
111120
}
112121

view/sharedcache/core/ObjC.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ namespace DSCObjC {
7878

7979
std::shared_ptr<BinaryNinja::ObjCReader> GetReader() override;
8080

81-
void GetRelativeMethod(BinaryNinja::ObjCReader* reader, BinaryNinja::method_t& meth) override;
81+
void GetRelativeMethod(BinaryNinja::ObjCReader* reader, BinaryNinja::method_t& meth,
82+
bool typesAreOffsetsFromSelectorBase) override;
8283

8384
BinaryNinja::Ref<BinaryNinja::Symbol> GetSymbol(uint64_t address) override;
8485

0 commit comments

Comments
 (0)