Skip to content

[PseudoObjC] Improve how [super ...] calls are displayed #6903

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

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
11 changes: 9 additions & 2 deletions lang/c/pseudoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,11 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
needSeparator = hasBlocks;

// Emit the lines for the statement itself
GetExprTextInternal(*i, tokens, settings, TopLevelOperatorPrecedence, true);
tokens.NewLine();
if (!ShouldSkipStatement(*i))
{
GetExprTextInternal(*i, tokens, settings, TopLevelOperatorPrecedence, true);
tokens.NewLine();
}
}
}();
break;
Expand Down Expand Up @@ -2848,6 +2851,10 @@ string PseudoCFunction::GetAnnotationEndString() const
return " */";
}

bool PseudoCFunction::ShouldSkipStatement(const BinaryNinja::HighLevelILInstruction& instr)
{
return false;
}

PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C")
{
Expand Down
1 change: 1 addition & 0 deletions lang/c/pseudoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
void EndLines(
const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;

virtual bool ShouldSkipStatement(const BinaryNinja::HighLevelILInstruction& instr);
virtual void GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
BNOperatorPrecedence precedence, bool statement);
Expand Down
86 changes: 78 additions & 8 deletions lang/c/pseudoobjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ std::optional<std::pair<uint64_t, Ref<Symbol>>> GetCallTargetInfo(const HighLeve
return std::make_pair(constant, symbol);
}

Ref<Type> TypeResolvingNamedTypeReference(Ref<Type> type, const Function& function)
{
if (!type || !type->IsNamedTypeRefer())
return type;

if (auto resolvedType = function.GetView()->GetTypeByRef(type->GetNamedTypeReference()))
return resolvedType;

return type;
}

struct RuntimeCall
{
enum Type
Expand Down Expand Up @@ -187,6 +198,42 @@ std::optional<RuntimeCall> DetectRewrittenDirectObjCMethodCall(const HighLevelIL
return RuntimeCall {RuntimeCall::MessageSend, constant, true};
}

bool VariableIsObjCSuperStruct(const Variable& variable, Function& function)
{
auto variableName = function.GetVariableName(variable);
if (variableName != "super")
return false;

const auto variableType = TypeResolvingNamedTypeReference(function.GetVariableType(variable), function);
if (!variableType || variableType->GetClass() != StructureTypeClass)
return false;

if (variableType->GetStructureName().GetString() != "objc_super")
return false;

return true;
}

bool IsAssignmentToObjCSuperStructField(const HighLevelILInstruction& assignInstr, Function& function)
{
// Check if this is an assignment to a field of the objc_super struct
// Pattern: HLIL_ASSIGN { dest = HLIL_STRUCT_FIELD { source = HLIL_VAR { super }, }, field = ... }

if (assignInstr.operation != HLIL_ASSIGN)
return false;

const auto destExpr = assignInstr.GetDestExpr();
if (destExpr.operation != HLIL_STRUCT_FIELD)
return false;

const auto sourceExpr = destExpr.GetSourceExpr();
if (sourceExpr.operation != HLIL_VAR)
return false;

auto variable = sourceExpr.GetVariable<HLIL_VAR>();
return VariableIsObjCSuperStruct(variable, function);
}

} // unnamed namespace

PseudoObjCFunction::PseudoObjCFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner,
Expand All @@ -212,7 +259,8 @@ void PseudoObjCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelIL
{
case RuntimeCall::MessageSend:
case RuntimeCall::MessageSendSuper:
if (GetExpr_ObjCMsgSend(objCRuntimeCall->address, objCRuntimeCall->isRewritten, destExpr, tokens, settings, parameterExprs))
if (GetExpr_ObjCMsgSend(objCRuntimeCall->address, objCRuntimeCall->type == RuntimeCall::MessageSendSuper,
objCRuntimeCall->isRewritten, destExpr, tokens, settings, parameterExprs))
{
if (statement)
tokens.AppendSemicolon();
Expand Down Expand Up @@ -259,7 +307,7 @@ void PseudoObjCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelIL
return PseudoCFunction::GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement);
}

bool PseudoObjCFunction::GetExpr_ObjCMsgSend(uint64_t msgSendAddress, bool isRewritten,
bool PseudoObjCFunction::GetExpr_ObjCMsgSend(uint64_t msgSendAddress, bool isSuper, bool isRewritten,
const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings,
const std::vector<HighLevelILInstruction>& parameterExprs)
{
Expand All @@ -278,7 +326,10 @@ bool PseudoObjCFunction::GetExpr_ObjCMsgSend(uint64_t msgSendAddress, bool isRew

tokens.AppendOpenBracket();

GetExprText(parameterExprs[0], tokens, settings);
if (isSuper)
tokens.Append(LocalVariableToken, "super", instr.address);
else
GetExprText(parameterExprs[0], tokens, settings);

for (size_t index = 2; index < parameterExprs.size(); index++)
{
Expand Down Expand Up @@ -355,10 +406,7 @@ void PseudoObjCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruc
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();

auto type = TypeResolvingNamedTypeReference(variable.type, *GetFunction());
if (!type || type->GetClass() != StructureTypeClass)
return PseudoCFunction::GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement);

Expand Down Expand Up @@ -427,7 +475,7 @@ void PseudoObjCFunction::GetExpr_IMPORT(const BinaryNinja::HighLevelILInstructio
BNOperatorPrecedence precedence, bool statement)
{
const auto constant = instr.GetConstant<HLIL_IMPORT>();
auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
auto symbol = GetFunction()->GetView()->GetSymbolByAddress(constant);
const auto symbolType = symbol->GetType();

if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
Expand All @@ -449,6 +497,28 @@ void PseudoObjCFunction::GetExpr_IMPORT(const BinaryNinja::HighLevelILInstructio
PseudoCFunction::GetExpr_IMPORT(instr, tokens, settings, precedence, statement);
}

bool PseudoObjCFunction::ShouldSkipStatement(const BinaryNinja::HighLevelILInstruction& instr)
{
// Skip statements that are compiler-generated artifacts of Objective-C runtime calls
// For now this is limited to the declaration / initialization of the `objc_super` variable
// used for `objc_msgSendSuper` calls.
switch (instr.operation)
{
case HLIL_VAR_DECLARE:
if (VariableIsObjCSuperStruct(instr.GetVariable<HLIL_VAR_DECLARE>(), *GetFunction()))
return true;
break;
case HLIL_ASSIGN:
if (IsAssignmentToObjCSuperStructField(instr, *GetFunction()))
return true;
break;
default:
break;
}

return PseudoCFunction::ShouldSkipStatement(instr);
}


PseudoObjCFunctionType::PseudoObjCFunctionType() : PseudoCFunctionType("Pseudo Objective-C") {}

Expand Down
4 changes: 3 additions & 1 deletion lang/c/pseudoobjc.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ class PseudoObjCFunction : public PseudoCFunction
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
BNOperatorPrecedence precedence, bool statement) override;

bool ShouldSkipStatement(const BinaryNinja::HighLevelILInstruction& instr) override;

private:
bool GetExpr_ObjCMsgSend(uint64_t msgSendAddress, bool isRewritten, const BinaryNinja::HighLevelILInstruction& expr,
bool GetExpr_ObjCMsgSend(uint64_t msgSendAddress, bool isSuper, bool isRewritten, 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,
Expand Down