Skip to content

Commit 99ca066

Browse files
committed
Parse sections containing Objective-C constants
This adds support for the `__objc_arrayobj`, `_objc_dictobj`, `__objc_intobj`, `__objc_floatobj`, `__objc_doubleobj` and `__objc_dateobj` sections that contain Objective-C constants. These are emitted by Apple's versions of Clang for `const` literals, amongst other things.
1 parent 05e3335 commit 99ca066

File tree

4 files changed

+295
-4
lines changed

4 files changed

+295
-4
lines changed

objectivec/objc.cpp

+278-1
Original file line numberDiff line numberDiff line change
@@ -1459,8 +1459,17 @@ void ObjCProcessor::ProcessObjCData(std::optional<std::string> imageName)
14591459
m_relocationPointerRewrites.clear();
14601460
}
14611461

1462+
void ObjCProcessor::ProcessObjCLiterals(std::optional<std::string> imageName)
1463+
{
1464+
ProcessCFStrings(imageName);
1465+
ProcessNSConstantArrays(imageName);
1466+
ProcessNSConstantDictionaries(imageName);
1467+
ProcessNSConstantIntegerNumbers(imageName);
1468+
ProcessNSConstantFloatingPointNumbers(imageName);
1469+
ProcessNSConstantDatas(imageName);
1470+
}
14621471

1463-
void ObjCProcessor::ProcessCFStrings(std::optional<std::string> imageName)
1472+
void ObjCProcessor::ProcessCFStrings(const std::optional<std::string>& imageName)
14641473
{
14651474
m_symbolQueue = new SymbolQueue();
14661475
uint64_t ptrSize = m_data->GetAddressSize();
@@ -1577,6 +1586,274 @@ void ObjCProcessor::ProcessCFStrings(std::optional<std::string> imageName)
15771586
delete m_symbolQueue;
15781587
}
15791588

1589+
void ObjCProcessor::ProcessNSConstantArrays(const std::optional<std::string>& imageName)
1590+
{
1591+
m_symbolQueue = new SymbolQueue();
1592+
uint64_t ptrSize = m_data->GetAddressSize();
1593+
1594+
auto idType = Type::NamedType(m_data, m_typeNames.id);
1595+
StructureBuilder nsConstantArrayBuilder;
1596+
nsConstantArrayBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1597+
nsConstantArrayBuilder.AddMember(Type::IntegerType(ptrSize, false), "count");
1598+
nsConstantArrayBuilder.AddMember(Type::PointerType(ptrSize, idType), "objects");
1599+
auto type = finalizeStructureBuilder(m_data, nsConstantArrayBuilder, "__NSConstantArray");
1600+
m_typeNames.nsConstantArray = type.first;
1601+
1602+
auto reader = GetReader();
1603+
if (auto arrays = GetSectionForImage(imageName, "__objc_arrayobj"))
1604+
{
1605+
auto start = arrays->GetStart();
1606+
auto end = arrays->GetEnd();
1607+
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantArray)->GetWidth();
1608+
m_data->BeginBulkModifySymbols();
1609+
for (view_ptr_t i = start; i < end; i += typeWidth)
1610+
{
1611+
reader->Seek(i + ptrSize);
1612+
uint64_t count = reader->ReadPointer();
1613+
auto dataLoc = ReadPointerAccountingForRelocations(reader.get());
1614+
DefineObjCSymbol(
1615+
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsarray_{:x}_data", i), dataLoc, true);
1616+
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantArray),
1617+
fmt::format("nsarray_{:x}", i), i, true);
1618+
}
1619+
auto id = m_data->BeginUndoActions();
1620+
m_symbolQueue->Process();
1621+
m_data->EndBulkModifySymbols();
1622+
m_data->ForgetUndoActions(id);
1623+
}
1624+
delete m_symbolQueue;
1625+
}
1626+
1627+
void ObjCProcessor::ProcessNSConstantDictionaries(const std::optional<std::string>& imageName)
1628+
{
1629+
m_symbolQueue = new SymbolQueue();
1630+
uint64_t ptrSize = m_data->GetAddressSize();
1631+
1632+
auto idType = Type::NamedType(m_data, m_typeNames.id);
1633+
StructureBuilder nsConstantDictionaryBuilder;
1634+
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1635+
nsConstantDictionaryBuilder.AddMember(Type::IntegerType(ptrSize, false), "options");
1636+
nsConstantDictionaryBuilder.AddMember(Type::IntegerType(ptrSize, false), "count");
1637+
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, idType), "keys");
1638+
nsConstantDictionaryBuilder.AddMember(Type::PointerType(ptrSize, idType), "objects");
1639+
auto type = finalizeStructureBuilder(m_data, nsConstantDictionaryBuilder, "__NSConstantDictionary");
1640+
m_typeNames.nsConstantDictionary = type.first;
1641+
1642+
auto reader = GetReader();
1643+
if (auto dicts = GetSectionForImage(imageName, "__objc_dictobj"))
1644+
{
1645+
auto start = dicts->GetStart();
1646+
auto end = dicts->GetEnd();
1647+
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantDictionary)->GetWidth();
1648+
m_data->BeginBulkModifySymbols();
1649+
for (view_ptr_t i = start; i < end; i += typeWidth)
1650+
{
1651+
reader->Seek(i + (ptrSize * 2));
1652+
// TODO: Do we need to do anything with `options`? It appears to always be 1.
1653+
uint64_t count = reader->ReadPointer();
1654+
auto keysLoc = ReadPointerAccountingForRelocations(reader.get());
1655+
auto objectsLoc = ReadPointerAccountingForRelocations(reader.get());
1656+
DefineObjCSymbol(
1657+
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsdict_{:x}_keys", i), keysLoc, true);
1658+
DefineObjCSymbol(
1659+
DataSymbol, Type::ArrayType(idType, count), fmt::format("nsdict_{:x}_objects", i), objectsLoc, true);
1660+
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantDictionary),
1661+
fmt::format("nsdict_{:x}", i), i, true);
1662+
}
1663+
auto id = m_data->BeginUndoActions();
1664+
m_symbolQueue->Process();
1665+
m_data->EndBulkModifySymbols();
1666+
m_data->ForgetUndoActions(id);
1667+
}
1668+
delete m_symbolQueue;
1669+
}
1670+
1671+
void ObjCProcessor::ProcessNSConstantIntegerNumbers(const std::optional<std::string>& imageName)
1672+
{
1673+
m_symbolQueue = new SymbolQueue();
1674+
uint64_t ptrSize = m_data->GetAddressSize();
1675+
1676+
StructureBuilder nsConstantIntegerNumberBuilder;
1677+
nsConstantIntegerNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1678+
nsConstantIntegerNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "encoding");
1679+
nsConstantIntegerNumberBuilder.AddMember(Type::IntegerType(ptrSize, true), "value");
1680+
auto type = finalizeStructureBuilder(m_data, nsConstantIntegerNumberBuilder, "__NSConstantIntegerNumber");
1681+
m_typeNames.nsConstantIntegerNumber = type.first;
1682+
1683+
auto reader = GetReader();
1684+
if (auto numbers = GetSectionForImage(imageName, "__objc_intobj"))
1685+
{
1686+
auto start = numbers->GetStart();
1687+
auto end = numbers->GetEnd();
1688+
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber)->GetWidth();
1689+
m_data->BeginBulkModifySymbols();
1690+
for (view_ptr_t i = start; i < end; i += typeWidth)
1691+
{
1692+
reader->Seek(i + ptrSize);
1693+
uint64_t encodingLoc = ReadPointerAccountingForRelocations(reader.get());
1694+
uint64_t value = reader->Read64();
1695+
reader->Seek(encodingLoc);
1696+
uint8_t encoding = reader->Read8();
1697+
1698+
switch (encoding)
1699+
{
1700+
case 'c':
1701+
case 's':
1702+
case 'i':
1703+
case 'l':
1704+
case 'q':
1705+
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber),
1706+
fmt::format("nsint_{:x}_{}", i, (int64_t)value), i, true);
1707+
break;
1708+
case 'C':
1709+
case 'S':
1710+
case 'I':
1711+
case 'L':
1712+
case 'Q':
1713+
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantIntegerNumber),
1714+
fmt::format("nsint_{:x}_{}", i, value), i, true);
1715+
break;
1716+
default:
1717+
m_logger->LogWarn("Unknown type encoding '%c' in number literal object at %p", encoding, i);
1718+
continue;
1719+
}
1720+
}
1721+
auto id = m_data->BeginUndoActions();
1722+
m_symbolQueue->Process();
1723+
m_data->EndBulkModifySymbols();
1724+
m_data->ForgetUndoActions(id);
1725+
}
1726+
delete m_symbolQueue;
1727+
}
1728+
1729+
void ObjCProcessor::ProcessNSConstantFloatingPointNumbers(const std::optional<std::string>& imageName)
1730+
{
1731+
uint64_t ptrSize = m_data->GetAddressSize();
1732+
1733+
StructureBuilder nsConstantFloatNumberBuilder;
1734+
nsConstantFloatNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1735+
nsConstantFloatNumberBuilder.AddMember(Type::FloatType(4), "value");
1736+
auto type = finalizeStructureBuilder(m_data, nsConstantFloatNumberBuilder, "__NSConstantFloatNumber");
1737+
m_typeNames.nsConstantFloatNumber = type.first;
1738+
1739+
StructureBuilder nsConstantDoubleNumberBuilder;
1740+
nsConstantDoubleNumberBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1741+
nsConstantDoubleNumberBuilder.AddMember(Type::FloatType(8), "value");
1742+
type = finalizeStructureBuilder(m_data, nsConstantDoubleNumberBuilder, "__NSConstantDoubleNumber");
1743+
m_typeNames.nsConstantDoubleNumber = type.first;
1744+
1745+
StructureBuilder nsConstantDateBuilder;
1746+
nsConstantDateBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1747+
nsConstantDateBuilder.AddMember(Type::FloatType(8), "ti");
1748+
type = finalizeStructureBuilder(m_data, nsConstantDateBuilder, "__NSConstantDate");
1749+
m_typeNames.nsConstantDate = type.first;
1750+
1751+
enum SectionType
1752+
{
1753+
Float,
1754+
Double,
1755+
Date,
1756+
};
1757+
1758+
constexpr std::pair<std::string_view, SectionType> sections[] = {
1759+
{"__objc_floatobj", Float},
1760+
{"__objc_doubleobj", Double},
1761+
{"__objc_dateobj", Date},
1762+
};
1763+
1764+
auto reader = GetReader();
1765+
for (auto& [sectionName, sectionType] : sections)
1766+
{
1767+
auto numbers = GetSectionForImage(imageName, sectionName.data());
1768+
if (!numbers)
1769+
continue;
1770+
1771+
m_symbolQueue = new SymbolQueue();
1772+
auto start = numbers->GetStart();
1773+
auto end = numbers->GetEnd();
1774+
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantDoubleNumber)->GetWidth();
1775+
m_data->BeginBulkModifySymbols();
1776+
for (view_ptr_t i = start; i < end; i += typeWidth)
1777+
{
1778+
reader->Seek(i + ptrSize);
1779+
1780+
QualifiedName* typeName = nullptr;
1781+
std::string name;
1782+
1783+
switch (sectionType)
1784+
{
1785+
case Float:
1786+
{
1787+
float value = 0;
1788+
reader->Read(&value, sizeof(value));
1789+
name = fmt::format("nsfloat_{:x}_{}", i, value);
1790+
typeName = &m_typeNames.nsConstantFloatNumber;
1791+
break;
1792+
}
1793+
case Double:
1794+
{
1795+
double value = 0;
1796+
reader->Read(&value, sizeof(value));
1797+
name = fmt::format("nsdouble_{:x}_{}", i, value);
1798+
typeName = &m_typeNames.nsConstantDoubleNumber;
1799+
break;
1800+
}
1801+
case Date:
1802+
{
1803+
double value = 0;
1804+
reader->Read(&value, sizeof(value));
1805+
name = fmt::format("nsdate_{:x}_{}", i, value);
1806+
typeName = &m_typeNames.nsConstantDate;
1807+
break;
1808+
}
1809+
}
1810+
DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, *typeName), name, i, true);
1811+
}
1812+
auto id = m_data->BeginUndoActions();
1813+
m_symbolQueue->Process();
1814+
m_data->EndBulkModifySymbols();
1815+
m_data->ForgetUndoActions(id);
1816+
delete m_symbolQueue;
1817+
}
1818+
}
1819+
1820+
void ObjCProcessor::ProcessNSConstantDatas(const std::optional<std::string>& imageName)
1821+
{
1822+
m_symbolQueue = new SymbolQueue();
1823+
uint64_t ptrSize = m_data->GetAddressSize();
1824+
1825+
StructureBuilder nsConstantDataBuilder;
1826+
nsConstantDataBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
1827+
nsConstantDataBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
1828+
nsConstantDataBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, false)), "bytes");
1829+
auto type = finalizeStructureBuilder(m_data, nsConstantDataBuilder, "__NSConstantData");
1830+
m_typeNames.nsConstantData = type.first;
1831+
1832+
auto reader = GetReader();
1833+
if (auto datas = GetSectionForImage(imageName, "__objc_dataobj"))
1834+
{
1835+
auto start = datas->GetStart();
1836+
auto end = datas->GetEnd();
1837+
auto typeWidth = Type::NamedType(m_data, m_typeNames.nsConstantData)->GetWidth();
1838+
m_data->BeginBulkModifySymbols();
1839+
for (view_ptr_t i = start; i < end; i += typeWidth)
1840+
{
1841+
reader->Seek(i + ptrSize);
1842+
uint64_t length = reader->ReadPointer();
1843+
auto dataLoc = ReadPointerAccountingForRelocations(reader.get());
1844+
DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, false), length),
1845+
fmt::format("nsdata_{:x}_data", i), dataLoc, true);
1846+
DefineObjCSymbol(
1847+
DataSymbol, Type::NamedType(m_data, m_typeNames.nsConstantData), fmt::format("nsdata_{:x}", i), i, true);
1848+
}
1849+
auto id = m_data->BeginUndoActions();
1850+
m_symbolQueue->Process();
1851+
m_data->EndBulkModifySymbols();
1852+
m_data->ForgetUndoActions(id);
1853+
}
1854+
delete m_symbolQueue;
1855+
}
1856+
15801857
void ObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
15811858
{
15821859
m_relocationPointerRewrites[location] = rewrite;

objectivec/objc.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@ namespace BinaryNinja {
275275
QualifiedName protocolList;
276276
QualifiedName ivar;
277277
QualifiedName ivarList;
278+
QualifiedName nsConstantArray;
279+
QualifiedName nsConstantDictionary;
280+
QualifiedName nsConstantDoubleNumber;
281+
QualifiedName nsConstantFloatNumber;
282+
QualifiedName nsConstantIntegerNumber;
283+
QualifiedName nsConstantDate;
284+
QualifiedName nsConstantData;
278285
} m_typeNames;
279286

280287
bool m_isBackedByDatabase;
@@ -314,6 +321,13 @@ namespace BinaryNinja {
314321
bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
315322
void ApplyMethodTypes(Class& cls);
316323

324+
void ProcessCFStrings(const std::optional<std::string>& imageName);
325+
void ProcessNSConstantArrays(const std::optional<std::string>& imageName);
326+
void ProcessNSConstantDictionaries(const std::optional<std::string>& imageName);
327+
void ProcessNSConstantIntegerNumbers(const std::optional<std::string>& imageName);
328+
void ProcessNSConstantFloatingPointNumbers(const std::optional<std::string>& imageName);
329+
void ProcessNSConstantDatas(const std::optional<std::string>& imageName);
330+
317331
Ref<Section> GetSectionForImage(std::optional<std::string> imageName, const char* sectionName);
318332
void PostProcessObjCSections(ObjCReader* reader, std::optional<std::string> imageName);
319333

@@ -333,7 +347,7 @@ namespace BinaryNinja {
333347

334348
ObjCProcessor(BinaryView* data, const char* loggerName, bool isBackedByDatabase, bool skipClassBaseProtocols = false);
335349
void ProcessObjCData(std::optional<std::string> imageName);
336-
void ProcessCFStrings(std::optional<std::string> imageName);
350+
void ProcessObjCLiterals(std::optional<std::string> imageName);
337351
void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
338352
};
339353
}

view/macho/machoview.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2336,7 +2336,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
23362336
if (parseCFStrings)
23372337
{
23382338
try {
2339-
m_objcProcessor->ProcessCFStrings(std::nullopt);
2339+
m_objcProcessor->ProcessObjCLiterals(std::nullopt);
23402340
}
23412341
catch (std::exception& ex)
23422342
{

view/sharedcache/core/SharedCacheController.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ bool SharedCacheController::ApplyImage(BinaryView& view, const CacheImage& image
224224
if (m_processObjC)
225225
objcProcessor.ProcessObjCData(image.GetName());
226226
if (m_processCFStrings)
227-
objcProcessor.ProcessCFStrings(image.GetName());
227+
objcProcessor.ProcessObjCLiterals(image.GetName());
228228
}
229229
catch (std::exception& e)
230230
{

0 commit comments

Comments
 (0)