Skip to content

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

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

Merged
merged 2 commits into from
Jun 5, 2025
Merged
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
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.rewriteMessageSendTarget",
R"({
"title" : "Rewrite objc_msgSend calls in IL",
"type" : "boolean",
"default" : false,
"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.rewriteMessageSendTarget", bv))
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