Skip to content

Commit

Permalink
[SharedCache] Add the ability to manually trigger Objective-C processing
Browse files Browse the repository at this point in the history
A problem with processing Objective-C sections at the time a library is loaded is that some of the references within those sections may refer to unload sections. This results in things like selectors not being correctly typed and named.

This commit provides a way for users to manually trigger Objective-C parsing against sections for a specific library or all libraries, via the API. Combined with the previous commit a user can use the API to batch load a number of libraries and skip Objective-C processing for each one and then run it across the entire of the DSC once they are all loaded.

Further improvement would be to provide a way to trigger Objective-C processing through the UI.
  • Loading branch information
WeiN76LQh authored and 0cyn committed Dec 26, 2024
1 parent 2cce2ab commit a924534
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 22 deletions.
38 changes: 38 additions & 0 deletions view/sharedcache/api/python/_sharedcachecore.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,44 @@ def BNDSCViewLoadSectionAtAddress(
return _BNDSCViewLoadSectionAtAddress(cache, name)


# -------------------------------------------------------
# _BNDSCViewProcessAllObjCSections

_BNDSCViewProcessAllObjCSections = core.BNDSCViewProcessAllObjCSections
_BNDSCViewProcessAllObjCSections.restype = None
_BNDSCViewProcessAllObjCSections.argtypes = [
ctypes.POINTER(BNSharedCache),
]


# noinspection PyPep8Naming
def BNDSCViewProcessAllObjCSections(
cache: ctypes.POINTER(BNSharedCache)
) -> None:
return _BNDSCViewProcessAllObjCSections(cache)


# -------------------------------------------------------
# _BNDSCViewProcessObjCSectionsForImageWithInstallName

_BNDSCViewProcessObjCSectionsForImageWithInstallName = core.BNDSCViewProcessObjCSectionsForImageWithInstallName
_BNDSCViewProcessObjCSectionsForImageWithInstallName.restype = None
_BNDSCViewProcessObjCSectionsForImageWithInstallName.argtypes = [
ctypes.POINTER(BNSharedCache),
ctypes.c_char_p,
ctypes.c_bool,
]


# noinspection PyPep8Naming
def BNDSCViewProcessObjCSectionsForImageWithInstallName(
cache: ctypes.POINTER(BNSharedCache),
name: Optional[str],
deallocName: bool
) -> None:
return _BNDSCViewProcessObjCSectionsForImageWithInstallName(cache, cstr(name), deallocName)


# -------------------------------------------------------
# _BNFreeSharedCacheReference

Expand Down
6 changes: 6 additions & 0 deletions view/sharedcache/api/python/sharedcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ def load_section_at_address(self, addr):
def load_image_containing_address(self, addr, skipObjC = False):
return sccore.BNDSCViewLoadImageContainingAddress(self.handle, addr, skipObjC)

def process_objc_sections_for_image_with_install_name(self, installName):
return sccore.BNDSCViewProcessObjCSectionsForImageWithInstallName(self.handle, installName, False)

def process_all_objc_sections(self):
return sccore.BNDSCViewProcessAllObjCSections(self.handle)

@property
def caches(self):
count = ctypes.c_ulonglong()
Expand Down
11 changes: 11 additions & 0 deletions view/sharedcache/api/sharedcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ namespace SharedCacheAPI {
return result;
}

void SharedCache::ProcessObjCSectionsForImageWithInstallName(std::string installName)
{
char* str = BNAllocString(installName.c_str());
BNDSCViewProcessObjCSectionsForImageWithInstallName(m_object, str, true);
}

void SharedCache::ProcessAllObjCSections()
{
BNDSCViewProcessAllObjCSections(m_object);
}

std::vector<DSCMemoryRegion> SharedCache::GetLoadedMemoryRegions()
{
size_t count;
Expand Down
3 changes: 3 additions & 0 deletions view/sharedcache/api/sharedcacheapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ namespace SharedCacheAPI {
bool LoadSectionAtAddress(uint64_t addr);
bool LoadImageContainingAddress(uint64_t addr, bool skipObjC = false);
std::vector<std::string> GetAvailableImages();

void ProcessObjCSectionsForImageWithInstallName(std::string installName);
void ProcessAllObjCSections();

std::vector<DSCSymbol> LoadAllSymbolsAndWait();

Expand Down
3 changes: 3 additions & 0 deletions view/sharedcache/api/sharedcachecore.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ extern "C"
SHAREDCACHE_FFI_API bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name, bool skipObjC);
SHAREDCACHE_FFI_API bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t name);
SHAREDCACHE_FFI_API bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address, bool skipObjC);

SHAREDCACHE_FFI_API void BNDSCViewProcessObjCSectionsForImageWithInstallName(BNSharedCache* cache, char* name, bool deallocName);
SHAREDCACHE_FFI_API void BNDSCViewProcessAllObjCSections(BNSharedCache* cache);

SHAREDCACHE_FFI_API char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address);
SHAREDCACHE_FFI_API char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address);
Expand Down
117 changes: 95 additions & 22 deletions view/sharedcache/core/SharedCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,81 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
return true;
}

static void GetObjCSettings(Ref<BinaryView> view, bool* processObjCMetadata, bool* processCFStrings)
{
auto settings = view->GetLoadSettings(VIEW_NAME);
*processCFStrings = true;
*processObjCMetadata = true;
if (settings && settings->Contains("loader.dsc.processCFStrings"))
*processCFStrings = settings->Get<bool>("loader.dsc.processCFStrings", view);
if (settings && settings->Contains("loader.dsc.processObjC"))
*processObjCMetadata = settings->Get<bool>("loader.dsc.processObjC", view);
}

static void ProcessObjCSectionsForImageWithName(std::string baseName, std::shared_ptr<VM> vm, std::shared_ptr<DSCObjC::DSCObjCProcessor> objc, bool processCFStrings, bool processObjCMetadata, Ref<Logger> logger)
{
try
{
if (processObjCMetadata)
objc->ProcessObjCData(vm, baseName);
if (processCFStrings)
objc->ProcessCFStrings(vm, baseName);
}
catch (const std::exception& ex)
{
logger->LogWarn("Error processing ObjC data for image %s: %s", baseName.c_str(), ex.what());
}
catch (...)
{
logger->LogWarn("Error processing ObjC data for image %s", baseName.c_str());
}
}

void SharedCache::ProcessObjCSectionsForImageWithInstallName(std::string installName)
{
bool processCFStrings;
bool processObjCMetadata;
GetObjCSettings(m_dscView, &processCFStrings, &processObjCMetadata);

if (!processObjCMetadata && !processCFStrings)
return;

auto objc = std::make_shared<DSCObjC::DSCObjCProcessor>(m_dscView, this, false);
auto vm = GetVMMap();

ProcessObjCSectionsForImageWithName(base_name(installName), vm, objc, processCFStrings, processObjCMetadata, m_logger);
}

void SharedCache::ProcessAllObjCSections()
{
bool processCFStrings;
bool processObjCMetadata;
GetObjCSettings(m_dscView, &processCFStrings, &processObjCMetadata);

if (!processObjCMetadata && !processCFStrings)
return;

auto objc = std::make_shared<DSCObjC::DSCObjCProcessor>(m_dscView, this, false);
auto vm = GetVMMap();

std::set<uint64_t> processedImageHeaders;
for (auto region : GetMappedRegions())
{
if (!region.loaded)
continue;

// Don't repeat the same images multiple times
auto header = HeaderForAddress(region.start);
if (!header)
continue;
if (processedImageHeaders.find(header->textBase) != processedImageHeaders.end())
continue;
processedImageHeaders.insert(header->textBase);

ProcessObjCSectionsForImageWithName(header->identifierPrefix, vm, objc, processCFStrings, processObjCMetadata, m_logger);
}
}

bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObjC)
{
auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
Expand Down Expand Up @@ -1839,29 +1914,11 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj

if (!skipObjC)
{
try
{
auto objc = std::make_unique<DSCObjC::DSCObjCProcessor>(m_dscView, this, false);
bool processCFStrings;
bool processObjCMetadata;
GetObjCSettings(m_dscView, &processCFStrings, &processObjCMetadata);

bool processCFStrings = true;
bool processObjCMetadata = true;
if (settings && settings->Contains("loader.dsc.processCFStrings"))
processCFStrings = settings->Get<bool>("loader.dsc.processCFStrings", m_dscView);
if (settings && settings->Contains("loader.dsc.processObjC"))
processObjCMetadata = settings->Get<bool>("loader.dsc.processObjC", m_dscView);
if (processObjCMetadata)
objc->ProcessObjCData(vm, h->identifierPrefix);
if (processCFStrings)
objc->ProcessCFStrings(vm, h->identifierPrefix);
}
catch (const std::exception& ex)
{
m_logger->LogWarn("Error processing ObjC data: %s", ex.what());
}
catch (...)
{
m_logger->LogWarn("Error processing ObjC data");
}
ProcessObjCSectionsForImageWithName(h->identifierPrefix, vm, std::make_shared<DSCObjC::DSCObjCProcessor>(m_dscView, this, false), processCFStrings, processObjCMetadata, m_logger);
}

m_dscView->AddAnalysisOption("linearsweep");
Expand Down Expand Up @@ -3067,6 +3124,22 @@ extern "C"
return false;
}

void BNDSCViewProcessObjCSectionsForImageWithInstallName(BNSharedCache* cache, char* name, bool deallocName)
{
std::string imageName = std::string(name);
if (deallocName)
BNFreeString(name);

if (cache->object)
cache->object->ProcessObjCSectionsForImageWithInstallName(imageName);
}

void BNDSCViewProcessAllObjCSections(BNSharedCache* cache)
{
if (cache->object)
cache->object->ProcessAllObjCSections();
}

char** BNDSCViewGetInstallNames(BNSharedCache* cache, size_t* count)
{
if (cache->object)
Expand Down
2 changes: 2 additions & 0 deletions view/sharedcache/core/SharedCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ namespace SharedCacheCore {
bool LoadImageWithInstallName(std::string installName, bool skipObjC);
bool LoadSectionAtAddress(uint64_t address);
bool LoadImageContainingAddress(uint64_t address, bool skipObjC);
void ProcessObjCSectionsForImageWithInstallName(std::string installName);
void ProcessAllObjCSections();
std::string NameForAddress(uint64_t address);
std::string ImageNameForAddress(uint64_t address);
std::vector<std::string> GetAvailableImages();
Expand Down

0 comments on commit a924534

Please sign in to comment.