Skip to content
This repository was archived by the owner on Jun 25, 2025. It is now read-only.

Add a setting to opt out of rewriting message sends with visible implementations #70

Merged
merged 2 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions Plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ BINARYNINJAPLUGIN bool CorePluginInit()

BinaryNinja::LogRegistry::CreateLogger(PluginLoggerName);

auto settings = BinaryNinja::Settings::Instance();
settings->RegisterSetting("core.function.objectiveC.assumeMessageSendTarget",
R"({
"title" : "Rewrite objc_msgSend calls to first visible implementation",
"type" : "boolean",
"default" : true,
"description" : "Message sends of selectors with any visible implementation are replaced with a direct call to the first visible implementation. Note that this can produce false positives if the selector is implemented by more than one class, or shares a name with a method from a system framework."
})");

return true;
}
}
10 changes: 6 additions & 4 deletions Workflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ bool Workflow::rewriteMethodCall(LLILFunctionRef ssa, size_t insnIndex)
ssa->GetFunction()->SetAutoCallTypeAdjustment(ssa->GetFunction()->GetArchitecture(), insn.address, {funcType, BN_DEFAULT_CONFIDENCE});
// --

if (!BinaryNinja::Settings::Instance()->Get<bool>("core.function.objectiveC.assumeMessageSendTarget"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed this in a force-push; typically with settings like this you might want to change per-view, you can pass the BinaryView as the second argument in Get, which takes the user's Open with Options overrides into account.

return false;

// Check the analysis info for a selector reference corresponding to the
// current selector. It is possible no such selector reference exists, for
Expand All @@ -158,6 +160,10 @@ bool Workflow::rewriteMethodCall(LLILFunctionRef ssa, size_t insnIndex)
const auto info = GlobalState::analysisInfo(bv);
if (!info)
return false;

// Attempt to look up the implementation for the given selector, first by
// using the raw selector, then by the address of the selector reference. If
// the lookup fails in both cases, abort.
std::vector<uint64_t> imps;
if (const auto& it = info->selRefToImp.find(rawSelector); it != info->selRefToImp.end())
imps = it->second;
Expand All @@ -167,10 +173,6 @@ bool Workflow::rewriteMethodCall(LLILFunctionRef ssa, size_t insnIndex)
if (imps.empty())
return false;

// Attempt to look up the implementation for the given selector, first by
// using the raw selector, then by the address of the selector reference. If
// the lookup fails in both cases, abort.

// k: This is the same behavior as before, however it is more apparent now by implementation
// that we are effectively just guessing which method this hits. This has _obvious_ drawbacks,
// but until we have more robust typing and objective-c type libraries, fixing this would
Expand Down