summaryrefslogtreecommitdiff
path: root/lang
diff options
context:
space:
mode:
Diffstat (limited to 'lang')
-rw-r--r--lang/c/plugin.cpp30
-rw-r--r--lang/c/pseudoc.cpp192
-rw-r--r--lang/c/pseudoc.h13
-rw-r--r--lang/c/pseudoobjc.cpp421
-rw-r--r--lang/c/pseudoobjc.h44
5 files changed, 596 insertions, 104 deletions
diff --git a/lang/c/plugin.cpp b/lang/c/plugin.cpp
new file mode 100644
index 00000000..721cfc19
--- /dev/null
+++ b/lang/c/plugin.cpp
@@ -0,0 +1,30 @@
+#include "binaryninjaapi.h"
+#include "pseudoc.h"
+#include "pseudoobjc.h"
+
+using namespace BinaryNinja;
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+#ifndef DEMO_EDITION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ }
+#endif
+
+#ifdef DEMO_EDITION
+ bool PseudoCPluginInit()
+#else
+ BINARYNINJAPLUGIN bool CorePluginInit()
+#endif
+ {
+ LanguageRepresentationFunctionType* type = new PseudoCFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+
+ type = new PseudoObjCFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+ return true;
+ }
+}
diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp
index 7046c6d0..fdc044fc 100644
--- a/lang/c/pseudoc.cpp
+++ b/lang/c/pseudoc.cpp
@@ -976,73 +976,11 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
break;
case HLIL_CALL:
- [&]() {
- const auto destExpr = instr.GetDestExpr<HLIL_CALL>();
- const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>();
-
- GetExprTextInternal(destExpr, tokens, settings, MemberAndFunctionOperatorPrecedence);
- tokens.AppendOpenParen();
-
- vector<FunctionParameter> namedParams;
- Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType();
- if (functionType && (functionType->GetClass() == PointerTypeClass)
- && (functionType->GetChildType()->GetClass() == FunctionTypeClass))
- namedParams = functionType->GetChildType()->GetParameters();
-
- for (size_t index{}; index < parameterExprs.size(); index++)
- {
- const auto& parameterExpr = parameterExprs[index];
- if (index != 0) tokens.Append(TextToken, ", ");
-
- // If the type of the parameter is known to be a pointer to a string, then we directly render it as a
- // string, regardless of its length
- bool renderedAsString = false;
- if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR)
- {
- auto exprType = namedParams[index].type;
- if (exprType && (exprType->GetClass() == PointerTypeClass))
- {
- if (auto child = exprType->GetChildType(); child)
- {
- if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1)
- || child->IsWideChar())
- {
- tokens.AppendPointerTextToken(parameterExprs[index],
- parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
- precedence, true);
- renderedAsString = true;
- }
- }
- }
- }
-
- if (!renderedAsString)
- GetExprText(parameterExpr, tokens, settings);
- }
- tokens.AppendCloseParen();
- if (statement)
- tokens.AppendSemicolon();
- }();
+ GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement);
break;
case HLIL_IMPORT:
- [&]() {
- const auto constant = instr.GetConstant<HLIL_IMPORT>();
- auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
- const auto symbolType = symbol->GetType();
-
- if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
- {
- symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant);
- const auto symbolShortName = symbol->GetShortName();
- tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand);
- return;
- }
-
- tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence);
- if (statement)
- tokens.AppendSemicolon();
- }();
+ GetExpr_IMPORT(instr, tokens, settings, precedence, statement);
break;
case HLIL_ARRAY_INDEX:
@@ -1288,12 +1226,7 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
break;
case HLIL_CONST_PTR:
- [&]() {
- tokens.AppendPointerTextToken(
- instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
- if (statement)
- tokens.AppendSemicolon();
- }();
+ GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);
break;
case HLIL_VAR:
@@ -1766,17 +1699,8 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
tokens.Append(AnnotationToken, "/* tailcall */");
tokens.NewLine();
tokens.Append(KeywordToken, "return ");
- GetExprTextInternal(destExpr, tokens, settings, MemberAndFunctionOperatorPrecedence);
- tokens.AppendOpenParen();
- for (size_t index{}; index < parameterExprs.size(); index++)
- {
- const auto& parameterExpr = parameterExprs[index];
- if (index != 0) tokens.Append(TextToken, ", ");
- GetExprTextInternal(parameterExpr, tokens, settings);
- }
- tokens.AppendCloseParen();
- if (statement)
- tokens.AppendSemicolon();
+
+ GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement);
}();
break;
@@ -2829,6 +2753,88 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
}
}
+void PseudoCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ const auto destExpr = instr.GetDestExpr();
+ const auto parameterExprs = instr.GetParameterExprs();
+
+ vector<FunctionParameter> namedParams;
+ Ref<Type> functionType = destExpr.GetType();
+ if (functionType && (functionType->GetClass() == PointerTypeClass)
+ && (functionType->GetChildType()->GetClass() == FunctionTypeClass))
+ namedParams = functionType->GetChildType()->GetParameters();
+
+ GetExprTextInternal(destExpr, tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+
+ for (size_t index {}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0)
+ tokens.Append(TextToken, ", ");
+
+ // If the type of the parameter is known to be a pointer to a string, then we directly render it as a
+ // string, regardless of its length
+ bool renderedAsString = false;
+ if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR)
+ {
+ auto exprType = namedParams[index].type;
+ if (exprType && (exprType->GetClass() == PointerTypeClass))
+ {
+ if (auto child = exprType->GetChildType(); child)
+ {
+ if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1) || child->IsWideChar())
+ {
+ tokens.AppendPointerTextToken(parameterExprs[index],
+ parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
+ precedence, true);
+ renderedAsString = true;
+ }
+ }
+ }
+ }
+
+ if (!renderedAsString)
+ GetExprText(parameterExpr, tokens, settings);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+}
+
+void PseudoCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ auto constant = instr.GetConstant<HLIL_CONST_PTR>();
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+}
+
+void PseudoCFunction::GetExpr_IMPORT(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
+ const auto symbolType = symbol->GetType();
+
+ if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
+ {
+ symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant);
+ const auto symbolShortName = symbol->GetShortName();
+ tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand);
+ return;
+ }
+
+ tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+}
string PseudoCFunction::GetAnnotationStartString() const
{
@@ -2848,32 +2854,10 @@ PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("
{
}
+PseudoCFunctionType::PseudoCFunctionType(const string& name) : LanguageRepresentationFunctionType(name) {}
Ref<LanguageRepresentationFunction> PseudoCFunctionType::Create(Architecture* arch, Function* owner,
HighLevelILFunction* highLevelILFunction)
{
return new PseudoCFunction(this, arch, owner, highLevelILFunction);
}
-
-
-extern "C"
-{
- BN_DECLARE_CORE_ABI_VERSION
-
-#ifndef DEMO_EDITION
- BINARYNINJAPLUGIN void CorePluginDependencies()
- {
- }
-#endif
-
-#ifdef DEMO_EDITION
- bool PseudoCPluginInit()
-#else
- BINARYNINJAPLUGIN bool CorePluginInit()
-#endif
- {
- LanguageRepresentationFunctionType* type = new PseudoCFunctionType();
- LanguageRepresentationFunctionType::Register(type);
- return true;
- }
-}
diff --git a/lang/c/pseudoc.h b/lang/c/pseudoc.h
index a3b03a88..66178432 100644
--- a/lang/c/pseudoc.h
+++ b/lang/c/pseudoc.h
@@ -52,6 +52,16 @@ protected:
void EndLines(
const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ virtual void GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement);
+ virtual void GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement);
+ virtual void GetExpr_IMPORT(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement);
+
public:
PseudoCFunction(BinaryNinja::LanguageRepresentationFunctionType* type, BinaryNinja::Architecture* arch,
BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
@@ -66,4 +76,7 @@ public:
PseudoCFunctionType();
BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+
+protected:
+ PseudoCFunctionType(const std::string& name);
};
diff --git a/lang/c/pseudoobjc.cpp b/lang/c/pseudoobjc.cpp
new file mode 100644
index 00000000..eccf10f1
--- /dev/null
+++ b/lang/c/pseudoobjc.cpp
@@ -0,0 +1,421 @@
+#include "pseudoobjc.h"
+
+#include "binaryninjaapi.h"
+#include "highlevelilinstruction.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace BinaryNinja;
+
+namespace {
+
+bool ParameterIsString(const HighLevelILInstruction& expr)
+{
+ if (expr.operation != HLIL_CONST_PTR)
+ return false;
+
+ auto exprType = expr.GetType();
+ if (!exprType || exprType->GetClass() != PointerTypeClass)
+ return false;
+
+ if (auto child = exprType->GetChildType(); child)
+ {
+ child = child->IsArray() ? child->GetChildType() : child;
+ return child->IsInteger() && child->IsSigned() && child->GetWidth() == 1;
+ }
+ return false;
+}
+
+struct SelectorReference
+{
+ std::string name;
+ uint64_t address;
+};
+
+std::optional<SelectorReference> GetSelectorFromParameter(
+ const HighLevelILInstruction& expr, const Function& function)
+{
+ if (expr.operation != HLIL_CONST_PTR)
+ return std::nullopt;
+
+ if (!ParameterIsString(expr))
+ return std::nullopt;
+
+ uint64_t constant = expr.GetConstant<HLIL_CONST_PTR>();
+ std::string string;
+ auto stringType = function.GetView()->CheckForStringAnnotationType(constant, string, true, true, 1);
+
+ if (!stringType || (stringType != AsciiString && stringType != Utf8String))
+ return std::nullopt;
+
+ return SelectorReference {string, constant};
+}
+
+void SplitSelector(const std::string& selector, std::vector<std::string>& tokens)
+{
+ std::stringstream ss(selector);
+ std::string token;
+ while (std::getline(ss, token, ':'))
+ tokens.push_back(token);
+}
+
+struct RuntimeCall
+{
+ enum Type
+ {
+ MessageSend,
+ MessageSendSuper,
+ Alloc,
+ AllocInit,
+ New,
+ Retain,
+ Release,
+ Autorelease,
+ RetainAutorelease,
+ Class,
+ };
+
+ Type type;
+ uint64_t address;
+};
+
+constexpr std::array RUNTIME_CALLS = {
+ std::make_pair("_objc_alloc_init", RuntimeCall::AllocInit),
+ std::make_pair("_objc_alloc", RuntimeCall::Alloc),
+ std::make_pair("_objc_autorelease", RuntimeCall::Autorelease),
+ std::make_pair("_objc_autoreleaseReturnValue", RuntimeCall::Autorelease),
+ std::make_pair("_objc_msgSend", RuntimeCall::MessageSend),
+ std::make_pair("_objc_msgSendSuper", RuntimeCall::MessageSendSuper),
+ std::make_pair("_objc_msgSendSuper2", RuntimeCall::MessageSendSuper),
+ std::make_pair("_objc_opt_class", RuntimeCall::Class),
+ std::make_pair("_objc_opt_new", RuntimeCall::New),
+ std::make_pair("_objc_release", RuntimeCall::Release),
+ std::make_pair("_objc_retain", RuntimeCall::Retain),
+ std::make_pair("_objc_retainAutoreleasedReturnValue", RuntimeCall::Retain),
+ std::make_pair("_objc_retainAutoreleaseReturnValue", RuntimeCall::RetainAutorelease),
+ std::make_pair("_objc_retainBlock", RuntimeCall::Retain),
+ std::make_pair("j__objc_alloc_init", RuntimeCall::AllocInit),
+ std::make_pair("j__objc_alloc", RuntimeCall::Alloc),
+ std::make_pair("j__objc_autorelease", RuntimeCall::Autorelease),
+ std::make_pair("j__objc_autoreleaseReturnValue", RuntimeCall::Autorelease),
+ std::make_pair("j__objc_msgSend", RuntimeCall::MessageSend),
+ std::make_pair("j__objc_msgSendSuper", RuntimeCall::MessageSendSuper),
+ std::make_pair("j__objc_msgSendSuper2", RuntimeCall::MessageSendSuper),
+ std::make_pair("j__objc_opt_class", RuntimeCall::Class),
+ std::make_pair("j__objc_opt_new", RuntimeCall::New),
+ std::make_pair("j__objc_release", RuntimeCall::Release),
+ std::make_pair("j__objc_retain", RuntimeCall::Retain),
+ std::make_pair("j__objc_retainAutoreleasedReturnValue", RuntimeCall::Retain),
+ std::make_pair("j__objc_retainAutoreleaseReturnValue", RuntimeCall::RetainAutorelease),
+ std::make_pair("j__objc_retainBlock", RuntimeCall::Retain),
+};
+
+std::optional<RuntimeCall> DetectObjCRuntimeCall(const HighLevelILInstruction& callTarget,
+ const std::vector<HighLevelILInstruction>& parameterExprs, const Function& function)
+{
+ uint64_t constant = 0;
+ Ref<Symbol> symbol;
+
+ switch (callTarget.operation)
+ {
+ case HLIL_CONST_PTR:
+ {
+ constant = callTarget.GetConstant<HLIL_CONST_PTR>();
+ symbol = function.GetView()->GetSymbolByAddress(constant);
+ break;
+ }
+ case HLIL_IMPORT:
+ {
+ constant = callTarget.GetConstant<HLIL_IMPORT>();
+ auto importAddressSymbol = function.GetView()->GetSymbolByAddress(constant);
+ if (!importAddressSymbol)
+ return std::nullopt;
+
+ const auto symbolType = importAddressSymbol->GetType();
+ if (symbolType != ImportedDataSymbol && symbolType != ImportAddressSymbol)
+ return std::nullopt;
+
+ symbol = Symbol::ImportedFunctionFromImportAddressSymbol(importAddressSymbol, constant);
+ }
+ default:
+ break;
+ }
+
+ if (!symbol)
+ return std::nullopt;
+
+ const auto symbolShortName = symbol->GetShortName();
+ auto it = std::find_if(RUNTIME_CALLS.begin(), RUNTIME_CALLS.end(), [&](const auto& pair) {
+ return pair.first == symbolShortName;
+ });
+ if (it == RUNTIME_CALLS.end())
+ return std::nullopt;
+
+ return RuntimeCall {it->second, constant};
+}
+
+} // unnamed namespace
+
+PseudoObjCFunction::PseudoObjCFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner,
+ HighLevelILFunction* highLevelILFunction) : PseudoCFunction(type, arch, owner, highLevelILFunction)
+{}
+
+void PseudoObjCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ const auto destExpr = instr.GetDestExpr();
+ const auto parameterExprs = instr.GetParameterExprs();
+
+ auto objCRuntimeCall = DetectObjCRuntimeCall(destExpr, parameterExprs, *GetFunction());
+ if (!objCRuntimeCall)
+ return PseudoCFunction::GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement);
+
+ std::vector<std::string_view> runtimeCallTokens;
+ switch (objCRuntimeCall->type)
+ {
+ case RuntimeCall::MessageSend:
+ case RuntimeCall::MessageSendSuper:
+ if (GetExpr_ObjCMsgSend(destExpr, tokens, settings, parameterExprs))
+ {
+ if (statement)
+ tokens.AppendSemicolon();
+ return;
+ }
+ break;
+ case RuntimeCall::Alloc:
+ runtimeCallTokens = {"alloc"};
+ break;
+ case RuntimeCall::AllocInit:
+ runtimeCallTokens = {"alloc", "init"};
+ break;
+ case RuntimeCall::New:
+ runtimeCallTokens = {"new"};
+ break;
+ case RuntimeCall::Retain:
+ runtimeCallTokens = {"retain"};
+ break;
+ case RuntimeCall::Release:
+ runtimeCallTokens = {"release"};
+ break;
+ case RuntimeCall::Autorelease:
+ runtimeCallTokens = {"autorelease"};
+ break;
+ case RuntimeCall::RetainAutorelease:
+ runtimeCallTokens = {"retain", "autorelease"};
+ break;
+ case RuntimeCall::Class:
+ runtimeCallTokens = {"class"};
+ break;
+ default:
+ break;
+ }
+
+ if (runtimeCallTokens.size()
+ && GetExpr_GenericObjCRuntimeCall(
+ objCRuntimeCall->address, instr, tokens, settings, parameterExprs, runtimeCallTokens))
+ {
+ if (statement)
+ tokens.AppendSemicolon();
+ return;
+ }
+
+ return PseudoCFunction::GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement);
+}
+
+bool PseudoObjCFunction::GetExpr_ObjCMsgSend(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, const std::vector<HighLevelILInstruction>& parameterExprs)
+{
+ if (parameterExprs.size() < 2)
+ return false;
+
+ auto maybeSelector = GetSelectorFromParameter(parameterExprs[1], *GetFunction());
+ if (!maybeSelector)
+ return false;
+
+ auto [selector, selectorAddress] = maybeSelector.value();
+ std::vector<std::string> selectorTokens {2};
+ SplitSelector(selector, selectorTokens);
+
+ tokens.AppendOpenBracket();
+
+ GetExprText(parameterExprs[0], tokens, settings);
+
+ for (size_t index = 2; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ tokens.Append(TextToken, " ");
+ if (index < selectorTokens.size())
+ {
+ tokens.Append(
+ DataSymbolToken, StringReferenceTokenContext, selectorTokens[index], instr.address, selectorAddress);
+ tokens.Append(TextToken, ":");
+ }
+ else
+ {
+ tokens.Append(TextToken, ", ");
+ }
+ GetExprText(parameterExpr, tokens, settings);
+ }
+ if (selectorTokens.size() > parameterExprs.size())
+ {
+ tokens.Append(TextToken, " ");
+ for (size_t index = parameterExprs.size(); index < selectorTokens.size(); index++)
+ {
+ tokens.Append(
+ DataSymbolToken, StringReferenceTokenContext, selectorTokens[index], instr.address, selectorAddress);
+ if (index != selectorTokens.size() - 1 || selector.back() == ':')
+ tokens.Append(TextToken, ":");
+ }
+ }
+ tokens.AppendCloseBracket();
+ return true;
+}
+
+bool PseudoObjCFunction::GetExpr_GenericObjCRuntimeCall(uint64_t address, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& tokens, DisassemblySettings* settings,
+ const std::vector<HighLevelILInstruction>& parameterExprs, const std::vector<std::string_view>& selectorTokens)
+{
+ if (parameterExprs.size() < 1)
+ return false;
+
+ for (auto _ : selectorTokens)
+ tokens.AppendOpenBracket();
+
+ GetExprText(parameterExprs[0], tokens, settings);
+ for (auto& token : selectorTokens)
+ {
+ tokens.Append(TextToken, " ");
+ tokens.Append(CodeSymbolToken, StringReferenceTokenContext, std::string(token), instr.address, address);
+ tokens.AppendCloseBracket();
+ }
+ return true;
+}
+
+void PseudoObjCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ uint64_t constant = instr.GetConstant<HLIL_CONST_PTR>();
+ auto symbol = GetFunction()->GetView()->GetSymbolByAddress(constant);
+ if (!symbol)
+ return PseudoCFunction::GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);
+
+ auto shortName = symbol->GetShortName();
+
+ // Match class references based only on the symbol name as the class metadata may be imported
+ // from a different image.
+ if (shortName.rfind("_OBJC_CLASS_$_", 0) == 0 || shortName.rfind("cls_", 0) == 0)
+ {
+ if (GetExpr_OBJC_CLASS(*symbol, constant, instr, tokens, settings, precedence, statement))
+ return;
+ }
+
+ DataVariable variable {};
+ auto hasVariable = GetFunction()->GetView()->GetDataVariableAtAddress(constant, variable);
+ if (!hasVariable)
+ return PseudoCFunction::GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);
+
+ auto type = variable.type->IsNamedTypeRefer() ?
+ GetFunction()->GetView()->GetTypeByRef(variable.type->GetNamedTypeReference()) :
+ variable.type.GetValue();
+
+ if (type->GetClass() != StructureTypeClass)
+ return PseudoCFunction::GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);
+
+ auto structureName = type->GetStructureName().GetString();
+ if (structureName == "__NSConstantString")
+ {
+ if (GetExpr_NSConstantString(type, constant, instr, tokens, settings, precedence, statement))
+ return;
+ }
+
+ PseudoCFunction::GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);
+}
+
+bool PseudoObjCFunction::GetExpr_OBJC_CLASS(const Symbol& symbol, uint64_t constant,
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement)
+{
+ auto shortName = symbol.GetShortName();
+ std::string className;
+ if (shortName.rfind("_OBJC_CLASS_$_", 0) == 0)
+ className = shortName.substr(14);
+ else if (shortName.rfind("cls_", 0) == 0)
+ className = shortName.substr(4);
+
+ if (className.empty())
+ return false;
+
+ tokens.Append(DataSymbolToken, ConstDataTokenContext, className, instr.address, constant);
+ if (statement)
+ tokens.AppendSemicolon();
+
+ return true;
+}
+
+bool PseudoObjCFunction::GetExpr_NSConstantString(Ref<Type> type, uint64_t constant,
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement)
+{
+ StructureMember dataMember;
+ bool hasDataField = type->GetStructure()->GetMemberByName("data", dataMember);
+ if (!hasDataField)
+ return false;
+
+ uint64_t dataPointer = 0;
+ if (!GetFunction()->GetView()->Read(
+ &dataPointer, constant + dataMember.offset, GetFunction()->GetView()->GetAddressSize()))
+ return false;
+
+ std::string stringValue;
+ if (!GetFunction()->GetView()->CheckForStringAnnotationType(dataPointer, stringValue, true, true, 1))
+ return false;
+
+ // TODO: Ideally this'd be part of the same token as the quotes for the string literal.
+ // Sometimes the view ends up wrapping between the @ and the quote.
+ tokens.Append(TextToken, "@");
+ tokens.AppendConstantTextToken(
+ instr, dataPointer, GetFunction()->GetView()->GetAddressSize(), settings, MemberAndFunctionOperatorPrecedence);
+ if (statement)
+ tokens.AppendSemicolon();
+
+ return true;
+}
+
+void PseudoObjCFunction::GetExpr_IMPORT(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement)
+{
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
+ const auto symbolType = symbol->GetType();
+
+ if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
+ {
+ symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant);
+ const auto symbolShortName = symbol->GetShortName();
+ if (symbolShortName.rfind("_OBJC_CLASS_$_", 0) == 0)
+ {
+ tokens.Append(IndirectImportToken, ConstDataTokenContext, symbolShortName.substr(14), instr.address,
+ constant);
+ if (statement)
+ tokens.AppendSemicolon();
+ return;
+ }
+ tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand);
+ return;
+ }
+
+ PseudoCFunction::GetExpr_IMPORT(instr, tokens, settings, precedence, statement);
+}
+
+
+PseudoObjCFunctionType::PseudoObjCFunctionType() : PseudoCFunctionType("Pseudo Objective-C") {}
+
+Ref<LanguageRepresentationFunction> PseudoObjCFunctionType::Create(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction)
+{
+ return new PseudoObjCFunction(this, arch, owner, highLevelILFunction);
+}
diff --git a/lang/c/pseudoobjc.h b/lang/c/pseudoobjc.h
new file mode 100644
index 00000000..4b87c7d7
--- /dev/null
+++ b/lang/c/pseudoobjc.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "pseudoc.h"
+
+#include "binaryninjaapi.h"
+
+class PseudoObjCFunction : public PseudoCFunction
+{
+public:
+ PseudoObjCFunction(BinaryNinja::LanguageRepresentationFunctionType* type, BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
+
+protected:
+ void GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement) override;
+ void GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement) override;
+ void GetExpr_IMPORT(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNOperatorPrecedence precedence, bool statement) override;
+
+private:
+ bool GetExpr_ObjCMsgSend(const BinaryNinja::HighLevelILInstruction& expr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ const std::vector<BinaryNinja::HighLevelILInstruction>& parameterExprs);
+ bool GetExpr_GenericObjCRuntimeCall(uint64_t address, const BinaryNinja::HighLevelILInstruction& expr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
+ const std::vector<BinaryNinja::HighLevelILInstruction>& parameterExprs, const std::vector<std::string_view>& selectorTokens);
+ bool GetExpr_OBJC_CLASS(const BinaryNinja::Symbol& symbol, uint64_t constant,
+ const BinaryNinja::HighLevelILInstruction& expr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement);
+ bool GetExpr_NSConstantString(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t constant,
+ const BinaryNinja::HighLevelILInstruction& expr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement);
+};
+
+class PseudoObjCFunctionType : public PseudoCFunctionType {
+public:
+ PseudoObjCFunctionType();
+ BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+};