From 721e5ec0ba32f61e03912262259bb1b6d8a76df2 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Wed, 28 Aug 2024 13:19:25 +0800 Subject: [PATCH] Add a debugger info sidebar widget to display relevant info during debugging --- api/debuggerapi.h | 8 + api/debuggercontroller.cpp | 31 + api/ffi.h | 14 + core/debuggercontroller.cpp | 1128 ++++++++++++++++++++++++++++++++++- core/debuggercontroller.h | 15 + core/ffi.cpp | 37 ++ ui/debuggerinfowidget.cpp | 935 +++++++++++++++++++++++++++++ ui/debuggerinfowidget.h | 178 ++++++ ui/ui.cpp | 2 + 9 files changed, 2347 insertions(+), 1 deletion(-) create mode 100644 ui/debuggerinfowidget.cpp create mode 100644 ui/debuggerinfowidget.h diff --git a/api/debuggerapi.h b/api/debuggerapi.h index 9fe89cf..9ab6022 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -598,6 +598,14 @@ namespace BinaryNinjaDebuggerAPI { bool ReAddDebuggerMemoryRegion(); uint64_t GetViewFileSegmentsStart(); + + bool ComputeExprValue(const Ref& func, const LowLevelILInstruction& expr, + uint64_t & value); + bool ComputeExprValue(const Ref& func, const MediumLevelILInstruction& expr, + uint64_t & value); + bool ComputeExprValue(const Ref& func, const HighLevelILInstruction& expr, + uint64_t & value); + bool GetVariableValue(Variable& var, uint64_t address, size_t size, uint64_t& value); }; diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index f9090d2..bcc9447 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -15,6 +15,9 @@ limitations under the License. */ #include "debuggerapi.h" +#include "lowlevelilinstruction.h" +#include "mediumlevelilinstruction.h" +#include "highlevelilinstruction.h" using namespace BinaryNinja; using namespace BinaryNinjaDebuggerAPI; @@ -927,3 +930,31 @@ uint64_t DebuggerController::GetViewFileSegmentsStart() { return BNDebuggerGetViewFileSegmentsStart(m_object); } + + +bool DebuggerController::ComputeExprValue(const Ref& func, + const BinaryNinja::LowLevelILInstruction &expr, uint64_t &value) +{ + return BNDebuggerComputeLLILExprValue(m_object, func->GetObject(), expr.exprIndex, value); +} + + +bool DebuggerController::ComputeExprValue(const Ref& func, + const BinaryNinja::MediumLevelILInstruction &expr, uint64_t &value) +{ + return BNDebuggerComputeMLILExprValue(m_object, func->GetObject(), expr.exprIndex, value); +} + + +bool DebuggerController::ComputeExprValue(const Ref& func, + const BinaryNinja::HighLevelILInstruction &expr, uint64_t &value) +{ + return BNDebuggerComputeHLILExprValue(m_object, func->GetObject(), expr.exprIndex, value); +} + + +bool DebuggerController::GetVariableValue(BinaryNinja::Variable &var, uint64_t address, size_t size, + uint64_t &value) +{ + return BNDebuggerGetVariableValue(m_object, &var, address, size, value); +} diff --git a/api/ffi.h b/api/ffi.h index 6aea4fc..ecd85c7 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -53,6 +53,10 @@ extern "C" typedef struct BNArchitecture BNArchitecture; typedef struct BNDataBuffer BNDataBuffer; typedef struct BNMetadata BNMetadata; + typedef struct BNLowLevelILFunction BNLowLevelILFunction; + typedef struct BNMediumLevelILFunction BNMediumLevelILFunction; + typedef struct BNHighLevelILFunction BNHighLevelILFunction; + typedef struct BNVariable BNVariable; // When `ffi.h` gets parsed by clang type parser, the binaryninjacore.h is NOT included so this enum will become not // defined. As a workaround, I duplicate its definition here. When the code gets compiled, the `BN_TYPE_PARSER` is @@ -516,6 +520,16 @@ extern "C" DEBUGGER_FFI_API bool BNDebuggerSetAdapterProperty( BNDebuggerController* controller, const char* name, BNMetadata* value); + // Compute expression values + DEBUGGER_FFI_API bool BNDebuggerComputeLLILExprValue(BNDebuggerController* controller, + BNLowLevelILFunction* function, size_t expr, uint64_t& value); + DEBUGGER_FFI_API bool BNDebuggerComputeMLILExprValue(BNDebuggerController* controller, + BNMediumLevelILFunction* function, size_t expr, uint64_t& value); + DEBUGGER_FFI_API bool BNDebuggerComputeHLILExprValue(BNDebuggerController* controller, + BNHighLevelILFunction* function, size_t expr, uint64_t& value); + DEBUGGER_FFI_API bool BNDebuggerGetVariableValue(BNDebuggerController* controller, + BNVariable* variable, uint64_t address, size_t size, uint64_t& value); + #ifdef __cplusplus } #endif diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index f73e573..76c76c9 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -2451,7 +2451,7 @@ static std::string CheckForLiteralString(uint64_t address) } if (ok) - return result; + return BinaryNinja::EscapeString(result); return ""; } @@ -2567,3 +2567,1129 @@ bool DebuggerController::ReAddDebuggerMemoryRegion() GetData()->SetFunctionAnalysisUpdateDisabled(false); return ret; } + + + +// TODO: these 3 functions should be moved to the BinaryNinjaAPI namespace for wider audiences +static int64_t MaskToSize(int64_t value, size_t size) +{ + if (size >= 8) + return value; + if (size == 0) + return value & 1; + return value & ((1LL << (size * 8)) - 1); +} + + +static int64_t ZeroExtend(int64_t value, size_t sourceSize, size_t destSize) +{ + if (destSize <= sourceSize) + return MaskToSize(value, destSize); + return MaskToSize(value & ((1LL << (sourceSize * 8)) - 1), destSize); +} + + +static int64_t SignExtend(int64_t value, size_t sourceSize, size_t destSize) +{ + if (destSize <= sourceSize) + return MaskToSize(value, destSize); + if (value & (1LL << ((sourceSize * 8) - 1))) + return MaskToSize(value | (~((1LL << (sourceSize * 8)) - 1)), destSize); + else + return MaskToSize(value & ((1LL << (sourceSize * 8)) - 1), destSize); +} + + +static inline uint64_t GetActualShift(uint64_t value, size_t instrSize) +{ + if (instrSize <= 4) + return value & 0b11111; + else + return value & 0b111111; +} + + +bool DebuggerController::ComputeExprValueAPI(const BinaryNinja::LowLevelILInstruction &instr, uint64_t& value) +{ + // We only want to do this check once before the recursion + if (!m_state->IsConnected() || m_state->IsRunning()) + return false; + + return ComputeExprValue(instr, value); +} + + +bool DebuggerController::ComputeExprValue(const LowLevelILInstruction &instr, uint64_t& value) +{ + if (instr.size > 8) + return false; + + uint64_t left, right; + + int64_t sizeMask = -1; + if (instr.size > 0 && instr.size < 8) + sizeMask = (1LL << (instr.size * 8)) - 1; + + switch (instr.operation) + { + case LLIL_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case LLIL_CONST_PTR: + value = instr.GetConstant() & sizeMask; + return true; + case LLIL_FLOAT_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case LLIL_REG: + { + auto reg = instr.GetSourceRegister(); + if (LLIL_REG_IS_TEMP(reg)) + return false; + + auto name = GetData()->GetDefaultArchitecture()->GetRegisterName(reg); + // TODO: what if the name reported by the adapter is different from that in the architecture? + // GetRegisterValue should return if the value can be retrieved + + // Cheat for arm64 + if (name == "x29") name = "fp"; + + value = GetRegisterValue(name) & sizeMask; + return true; + } + case LLIL_ADD: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left + right; + value &= sizeMask; + return true; + case LLIL_SUB: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left - right; + value &= sizeMask; + return true; + case LLIL_LOAD: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + auto buffer = ReadMemory(left, instr.size); + if (buffer.GetLength() != instr.size) + return false; + + switch (instr.size) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + case LLIL_STORE: + { + if (!ComputeExprValue(instr.GetDestExpr(), left)) + return false; + auto buffer = ReadMemory(left, instr.size); + if (buffer.GetLength() != instr.size) + return false; + + switch (instr.size) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + case LLIL_LSL: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left << GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case LLIL_LSR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case LLIL_ASR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + if (left & (1LL << ((instr.size * 8) - 1))) + left |= ~sizeMask; + else + left &= sizeMask; + value = ((int64_t)left) >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case LLIL_XOR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left ^ right; + value &= sizeMask; + return true; + } + case LLIL_AND: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left & right; + value &= sizeMask; + return true; + } + case LLIL_OR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left | right; + value &= sizeMask; + return true; + } + case LLIL_NEG: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = -left; + value &= sizeMask; + return true; + } + case LLIL_NOT: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ~left; + value &= sizeMask; + return true; + } + case LLIL_SX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = SignExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case LLIL_ZX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ZeroExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case LLIL_PUSH: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value &= sizeMask; + return true; + } + case LLIL_POP: + { + auto stackPointer = GetState()->StackPointer(); + auto buffer = ReadMemory(stackPointer, instr.size); + if (buffer.GetLength() != instr.size) + return false; + + switch (instr.size) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + case LLIL_CMP_E: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_NE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_SLT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_ULT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_SLE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_ULE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_SGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_UGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_SGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case LLIL_CMP_UGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + default: + break; + } + return false; +} + + +uint64_t DebuggerController::GetValueFromComparison(const BNLowLevelILOperation op, uint64_t left, uint64_t right, + size_t size) +{ + switch (op) + { + case LLIL_CMP_E: + return left == right; + break; + case LLIL_CMP_NE: + return left != right; + break; + case LLIL_CMP_SLT: + return SignExtend(left, size, 8) < SignExtend(right, size, 8); + break; + case LLIL_CMP_ULT: + return (uint64_t)(left) < (uint64_t)(right); + break; + case LLIL_CMP_SLE: + return SignExtend(left, size, 8) <= SignExtend(right, size, 8); + break; + case LLIL_CMP_ULE: + return (uint64_t)(left) <= (uint64_t)(right); + break; + case LLIL_CMP_SGE: + return SignExtend(left, size, 8) >= SignExtend(right, size, 8); + break; + case LLIL_CMP_UGE: + return (uint64_t)(left) >= (uint64_t)(right); + break; + case LLIL_CMP_SGT: + return SignExtend(left, size, 8) > SignExtend(right, size, 8); + break; + case LLIL_CMP_UGT: + return (uint64_t)(left) > (uint64_t)(right); + break; + default: + break; + } + return -1; +} + + +uint64_t DebuggerController::GetValueFromComparison(const BNMediumLevelILOperation op, uint64_t left, uint64_t right, + size_t size) +{ + switch (op) + { + case MLIL_CMP_E: + return left == right; + break; + case MLIL_CMP_NE: + return left != right; + break; + case MLIL_CMP_SLT: + return SignExtend(left, size, 8) < SignExtend(right, size, 8); + break; + case MLIL_CMP_ULT: + return (uint64_t)(left) < (uint64_t)(right); + break; + case MLIL_CMP_SLE: + return SignExtend(left, size, 8) <= SignExtend(right, size, 8); + break; + case MLIL_CMP_ULE: + return (uint64_t)(left) <= (uint64_t)(right); + break; + case MLIL_CMP_SGE: + return SignExtend(left, size, 8) >= SignExtend(right, size, 8); + break; + case MLIL_CMP_UGE: + return (uint64_t)(left) >= (uint64_t)(right); + break; + case MLIL_CMP_SGT: + return SignExtend(left, size, 8) > SignExtend(right, size, 8); + break; + case MLIL_CMP_UGT: + return (uint64_t)(left) > (uint64_t)(right); + break; + default: + break; + } + return -1; +} + + +uint64_t DebuggerController::GetValueFromComparison(const BNHighLevelILOperation op, uint64_t left, uint64_t right, + size_t size) +{ + switch (op) + { + case HLIL_CMP_E: + return left == right; + break; + case HLIL_CMP_NE: + return left != right; + break; + case HLIL_CMP_SLT: + return SignExtend(left, size, 8) < SignExtend(right, size, 8); + break; + case HLIL_CMP_ULT: + return (uint64_t)(left) < (uint64_t)(right); + break; + case HLIL_CMP_SLE: + return SignExtend(left, size, 8) <= SignExtend(right, size, 8); + break; + case HLIL_CMP_ULE: + return (uint64_t)(left) <= (uint64_t)(right); + break; + case HLIL_CMP_SGE: + return SignExtend(left, size, 8) >= SignExtend(right, size, 8); + break; + case HLIL_CMP_UGE: + return (uint64_t)(left) >= (uint64_t)(right); + break; + case HLIL_CMP_SGT: + return SignExtend(left, size, 8) > SignExtend(right, size, 8); + break; + case HLIL_CMP_UGT: + return (uint64_t)(left) > (uint64_t)(right); + break; + default: + break; + } + return -1; +} + + +bool DebuggerController::ComputeExprValueAPI(const BinaryNinja::MediumLevelILInstruction &instr, uint64_t& value) +{ + // We only want to do this check once before the recursion + if (!m_state->IsConnected() || m_state->IsRunning()) + return false; + + return ComputeExprValue(instr, value); +} + + +bool DebuggerController::ComputeExprValue(const MediumLevelILInstruction &instr, uint64_t& value) +{ + if (instr.size > 8) + return false; + + uint64_t left, right; + + int64_t sizeMask = -1; + if (instr.size > 0 && instr.size < 8) + sizeMask = (1LL << (instr.size * 8)) - 1; + + switch (instr.operation) + { + case MLIL_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case MLIL_CONST_PTR: + value = instr.GetConstant() & sizeMask; + return true; + case MLIL_FLOAT_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case MLIL_VAR: + { + const auto var = instr.GetSourceVariable(); + return GetVariableValue(var, instr.address, instr.size, value); + break; + } + case MLIL_ADD: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left + right; + value &= sizeMask; + return true; + case MLIL_SUB: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left - right; + value &= sizeMask; + return true; + case MLIL_LOAD: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + auto buffer = ReadMemory(left, instr.size); + if (buffer.GetLength() != instr.size) + return false; + + switch (instr.size) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + case MLIL_STORE: + { + if (!ComputeExprValue(instr.GetDestExpr(), left)) + return false; + auto buffer = ReadMemory(left, instr.size); + if (buffer.GetLength() != instr.size) + return false; + + switch (instr.size) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + case MLIL_LSL: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left << GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case MLIL_LSR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case MLIL_ASR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + if (left & (1LL << ((instr.size * 8) - 1))) + left |= ~sizeMask; + else + left &= sizeMask; + value = ((int64_t)left) >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case MLIL_XOR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left ^ right; + value &= sizeMask; + return true; + } + case MLIL_AND: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left & right; + value &= sizeMask; + return true; + } + case MLIL_OR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left | right; + value &= sizeMask; + return true; + } + case MLIL_NEG: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = -left; + value &= sizeMask; + return true; + } + case MLIL_NOT: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ~left; + value &= sizeMask; + return true; + } + case MLIL_SX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = SignExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case MLIL_ZX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ZeroExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case MLIL_CMP_E: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_NE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_SLT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_ULT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_SLE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_ULE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_SGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_UGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_SGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case MLIL_CMP_UGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + default: + return false; + } +} + + +bool DebuggerController::GetVariableValueAPI(const Variable& var, uint64_t address, size_t size, uint64_t& value) +{ + // We only want to do this check once before the recursion + if (!m_state->IsConnected() || m_state->IsRunning()) + return false; + + return GetVariableValue(var, address, size, value); +} + + +bool DebuggerController::GetVariableValue(const Variable& var, uint64_t address, size_t size, uint64_t &value) +{ + int64_t sizeMask = -1; + if (size > 0 && size < 8) + sizeMask = (1LL << (size * 8)) - 1; + + if (var.type == RegisterVariableSourceType) + { + auto reg = var.storage; + if (LLIL_REG_IS_TEMP(reg)) + return false; + + auto name = GetData()->GetDefaultArchitecture()->GetRegisterName(reg); + // TODO: what if the name reported by the adapter is different from that in the architecture? + // GetRegisterValue should return if the value can be retrieved + + // Cheat for arm64 + if (name == "x29") name = "fp"; + + value = GetRegisterValue(name) & sizeMask; + return true; + } + else if (var.type == StackVariableSourceType) + { + auto stack = m_state->StackPointer(); + auto ip = m_state->IP(); + auto funcs = GetData()->GetAnalysisFunctionsContainingAddress(address); + if (funcs.empty()) + return false; + auto func = funcs[0]; + if (!func) + return false; + auto arch = GetData()->GetDefaultArchitecture(); + if (!arch) + return false; + auto stackReg = arch->GetStackPointerRegister(); + auto stackValue = func->GetRegisterValueAtInstruction(arch, ip, stackReg); + if (stackValue.state != StackFrameOffset) + return false; + auto stackAtFuncEntry = stack - stackValue.value; + auto addrOfVar = stackAtFuncEntry + var.storage; + + auto type = func->GetVariableType(var); + if (!type) + return false; + + auto width = type->GetWidth(); + if (width > 8) + return false; + + auto buffer = ReadMemory(addrOfVar, width); + if (buffer.GetLength() != width) + return false; + + switch (width) + { + case 1: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 2: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 4: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + case 8: + value = *reinterpret_cast(buffer.GetData()); + value &= sizeMask; + return true; + default: + return false; + } + } + + return false; +} + + +bool DebuggerController::ComputeExprValueAPI(const BinaryNinja::HighLevelILInstruction &instr, uint64_t& value) +{ + // We only want to do this check once before the recursion + if (!m_state->IsConnected() || m_state->IsRunning()) + return false; + + return ComputeExprValue(instr, value); +} + + +bool DebuggerController::ComputeExprValue(const HighLevelILInstruction &instr, uint64_t& value) +{ + if (instr.size > 8) + return false; + + uint64_t left, right; + + int64_t sizeMask = -1; + if (instr.size > 0 && instr.size < 8) + sizeMask = (1LL << (instr.size * 8)) - 1; + + switch (instr.operation) + { + case HLIL_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case HLIL_CONST_PTR: + value = instr.GetConstant() & sizeMask; + return true; + case HLIL_FLOAT_CONST: + value = instr.GetConstant() & sizeMask; + return true; + case HLIL_VAR: + { + const auto var = instr.GetVariable(); + return GetVariableValue(var, instr.address, instr.size, value); + break; + } + case HLIL_ADD: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left + right; + value &= sizeMask; + return true; + case HLIL_SUB: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left - right; + value &= sizeMask; + return true; + case HLIL_LSL: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left << GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case HLIL_LSR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case HLIL_ASR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + if (left & (1LL << ((instr.size * 8) - 1))) + left |= ~sizeMask; + else + left &= sizeMask; + value = ((int64_t)left) >> GetActualShift(right, instr.size); + value &= sizeMask; + return true; + } + case HLIL_XOR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left ^ right; + value &= sizeMask; + return true; + } + case HLIL_AND: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left & right; + value &= sizeMask; + return true; + } + case HLIL_OR: + { + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = left | right; + value &= sizeMask; + return true; + } + case HLIL_NEG: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = -left; + value &= sizeMask; + return true; + } + case HLIL_NOT: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ~left; + value &= sizeMask; + return true; + } + case HLIL_SX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = SignExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case HLIL_ZX: + { + if (!ComputeExprValue(instr.GetSourceExpr(), left)) + return false; + value = ZeroExtend(left, instr.GetSourceExpr().size, instr.size); + return true; + } + case HLIL_CMP_E: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_NE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_SLT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_ULT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_SLE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_ULE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_SGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_UGE: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_SGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + case HLIL_CMP_UGT: + if (!ComputeExprValue(instr.GetLeftExpr(), left)) + return false; + if (!ComputeExprValue(instr.GetRightExpr(), right)) + return false; + value = GetValueFromComparison(instr.operation, left, right, instr.size); + return true; + + default: + return false; + } +} diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index a660b2e..9d029e8 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -315,5 +315,20 @@ namespace BinaryNinjaDebugger { bool ReAddDebuggerMemoryRegion(); uint64_t GetViewFileSegmentsStart() { return m_viewStart; } + + bool ComputeExprValueAPI(const LowLevelILInstruction& instr, uint64_t& value); + bool ComputeExprValue(const LowLevelILInstruction& instr, uint64_t& value); + uint64_t GetValueFromComparison(const BNLowLevelILOperation op, uint64_t left, uint64_t right, size_t size); + + bool ComputeExprValueAPI(const MediumLevelILInstruction& instr, uint64_t& value); + bool ComputeExprValue(const MediumLevelILInstruction& instr, uint64_t& value); + uint64_t GetValueFromComparison(const BNMediumLevelILOperation op, uint64_t left, uint64_t right, size_t size); + + bool ComputeExprValueAPI(const HighLevelILInstruction& instr, uint64_t& value); + bool ComputeExprValue(const HighLevelILInstruction& instr, uint64_t& value); + uint64_t GetValueFromComparison(const BNHighLevelILOperation op, uint64_t left, uint64_t right, size_t size); + + bool GetVariableValueAPI(const Variable& var, uint64_t address, size_t size, uint64_t& value); + bool GetVariableValue(const Variable& var, uint64_t address, size_t size, uint64_t& value); }; }; // namespace BinaryNinjaDebugger diff --git a/core/ffi.cpp b/core/ffi.cpp index f7e78b2..399a0ce 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -15,6 +15,9 @@ limitations under the License. */ #include "binaryninjaapi.h" +#include "lowlevelilinstruction.h" +#include "mediumlevelilinstruction.h" +#include "highlevelilinstruction.h" #include "debuggercontroller.h" #include "debuggercommon.h" #include "../api/ffi.h" @@ -1063,3 +1066,37 @@ uint64_t BNDebuggerGetViewFileSegmentsStart(BNDebuggerController* controller) { return controller->object->GetViewFileSegmentsStart(); } + + +bool BNDebuggerComputeLLILExprValue(BNDebuggerController* controller, BNLowLevelILFunction* function, size_t expr, + uint64_t& value) +{ + Ref llil = new LowLevelILFunction(BNNewLowLevelILFunctionReference(function)); + auto instr = llil->GetExpr(expr); + return controller->object->ComputeExprValueAPI(instr, value); +} + + +bool BNDebuggerComputeMLILExprValue(BNDebuggerController* controller, BNMediumLevelILFunction* function, size_t expr, + uint64_t& value) +{ + Ref mlil = new MediumLevelILFunction(BNNewMediumLevelILFunctionReference(function)); + auto instr = mlil->GetExpr(expr); + return controller->object->ComputeExprValueAPI(instr, value); +} + + +bool BNDebuggerComputeHLILExprValue(BNDebuggerController* controller, BNHighLevelILFunction* function, size_t expr, + uint64_t& value) +{ + Ref hlil = new HighLevelILFunction(BNNewHighLevelILFunctionReference(function)); + auto instr = hlil->GetExpr(expr); + return controller->object->ComputeExprValueAPI(instr, value); +} + + +bool BNDebuggerGetVariableValue(BNDebuggerController* controller, BNVariable* variable, uint64_t address, size_t size, + uint64_t& value) +{ + return controller->object->GetVariableValue(*variable, address, size, value); +} diff --git a/ui/debuggerinfowidget.cpp b/ui/debuggerinfowidget.cpp new file mode 100644 index 0000000..2c7e3c4 --- /dev/null +++ b/ui/debuggerinfowidget.cpp @@ -0,0 +1,935 @@ +/* +Copyright 2020-2024 Vector 35 Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include "ui.h" +#include "debuggerinfowidget.h" +#include "lowlevelilinstruction.h" +#include "mediumlevelilinstruction.h" +#include "highlevelilinstruction.h" +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +DebugInfoSidebarWidget::DebugInfoSidebarWidget(BinaryViewRef data): SidebarWidget("Debugger Info"), m_data(data) +{ + m_debugger = DebuggerController::GetController(data); + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + + m_entryList = new DebuggerInfoTable(data); + layout->addWidget(m_entryList); + + setLayout(layout); +} + + +std::vector DebuggerInfoTable::getInfoForLLILCalls(LowLevelILFunctionRef llil, + const LowLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != LLIL_CALL && instr.operation != LLIL_TAILCALL) + return result; + + auto dest = instr.GetDestExpr(); + if (dest.operation != LLIL_CONST_PTR && dest.operation != LLIL_CONST) + return result; + + auto callTarget = dest.GetConstant(); + auto functions = m_data->GetAnalysisFunctionsForAddress(callTarget); + if (functions.empty()) + return result; + + auto func = functions[0]; + if (!func) + return result; + + auto arch = func->GetArchitecture(); + if (!arch) + return result; + + for (const auto& param: func->GetParameterVariables().GetValue()) + { + switch (param.type) + { + case RegisterVariableSourceType: + { + auto paramName = func->GetVariableName(param); + auto reg = param.storage; + auto regName = arch->GetRegisterName(reg); + auto value = m_debugger->GetRegisterValue(regName); + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(RegisterToken, regName); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case StackVariableSourceType: + { + auto offset = param.storage; + // Account for the return address on the stack for x64/x86_64, not sure if we should do it for other arch + offset -= arch->GetAddressSize(); + auto realOffset = offset + m_debugger->StackPointer(); + + BinaryReader reader(m_data); + reader.Seek(realOffset); + auto value = reader.ReadPointer(); + auto hints = m_debugger->GetAddressInformation(value); + + auto paramName = func->GetVariableName(param); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + + auto stackReg = arch->GetStackPointerRegister(); + auto stackRegName = arch->GetRegisterName(stackReg); + tokens.emplace_back(RegisterToken, stackRegName); + if (offset != 0) + { + tokens.emplace_back(TextToken, " + "); + char buf[64] = {0}; + snprintf(buf, sizeof(buf), "%#llx", offset); + tokens.emplace_back(IntegerToken, buf, offset); + } + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case FlagVariableSourceType: + break; + } + } + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForLLILConditions(LowLevelILFunctionRef llil, + const LowLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != LLIL_IF) + return result; + + auto func = llil->GetFunction(); + auto condition = instr.GetConditionExpr(); + uint64_t value; + if (!m_debugger->ComputeExprValue(llil, condition, value)) + return result; + + // The value of a conditional expression must be 0 or 1 if it can be evaluated + if ((value != 1) && (value != 0)) + return result; + + std::vector tokens; + if (!llil->GetExprText(func->GetArchitecture(), condition.exprIndex, tokens)) + return result; + + auto trueBranch = instr.GetTrueTarget(); + auto falseBranch = instr.GetFalseTarget(); + auto targetIL = value == 1 ? trueBranch : falseBranch; + auto il = llil->GetInstruction(targetIL); + auto targetAddr = il.address; + + string hints = fmt::format("Branch to {} @ {:#x}", targetIL, targetAddr); + result.emplace_back(tokens, value, hints, instr.instructionIndex, instr.exprIndex, instr.address); + return result; +} + + +std::vector DebuggerInfoTable::getInfoForLLIL(LowLevelILFunctionRef llil, const LowLevelILInstruction &instr) +{ + std::vector result; + auto func = llil->GetFunction(); + for (const auto operand: instr.GetOperands()) + { + switch (operand.GetType()) + { + case ExprLowLevelOperand: + { + uint64_t value; + if (!m_debugger->ComputeExprValue(llil, operand.GetExpr(), value)) + continue; + std::vector tokens; + if (!llil->GetExprText(func->GetArchitecture(), operand.GetExpr().exprIndex, tokens)) + continue; + auto hints = m_debugger->GetAddressInformation(value); + result.emplace_back(tokens, value, hints, instr.instructionIndex, operand.GetExpr().exprIndex, instr.address); + break; + } + case RegisterLowLevelOperand: + { + auto reg = operand.GetRegister(); + if (LLIL_REG_IS_TEMP(reg)) + break; + auto name = func->GetArchitecture()->GetRegisterName(reg); + auto value = m_debugger->GetRegisterValue(name); + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + tokens.emplace_back(RegisterToken, name); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + default: + break; + } + } + + // Display the info of the function arguments if the current LLIL is a call instruction + auto lines = getInfoForLLILCalls(llil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + // Display the info of the conditional expressions + lines = getInfoForLLILConditions(llil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForMLIL(MediumLevelILFunctionRef mlil, + const MediumLevelILInstruction& instr) +{ + std::vector result; + auto func = mlil->GetFunction(); + for (const auto operand: instr.GetOperands()) + { + switch (operand.GetType()) + { + case ExprMediumLevelOperand: + { + uint64_t value; + if (!m_debugger->ComputeExprValue(mlil, operand.GetExpr(), value)) + continue; + std::vector tokens; + if (!mlil->GetExprText(func->GetArchitecture(), operand.GetExpr().exprIndex, tokens)) + continue; + auto hints = m_debugger->GetAddressInformation(value); + result.emplace_back(tokens, value, hints, instr.instructionIndex, operand.GetExpr().exprIndex, instr.address); + break; + } + case VariableMediumLevelOperand: + { + uint64_t value; + auto var = operand.GetVariable(); + if (!m_debugger->GetVariableValue(var, instr.address, instr.size, value)) + break; + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + auto name = func->GetVariableName(var); + tokens.emplace_back(LocalVariableToken, name); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + default: + break; + } + } + + // Display the info of the function arguments if the current MLIL is a call instruction + auto lines = getInfoForMLILCalls(mlil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + // Display the info of the conditional expressions + lines = getInfoForMLILConditions(mlil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForMLILCalls(MediumLevelILFunctionRef mlil, + const MediumLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != MLIL_CALL && instr.operation != MLIL_TAILCALL) + return result; + + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_CONST_PTR && dest.operation != MLIL_CONST) + return result; + + auto callTarget = dest.GetConstant(); + auto functions = m_data->GetAnalysisFunctionsForAddress(callTarget); + if (functions.empty()) + return result; + + auto func = functions[0]; + if (!func) + return result; + + auto arch = func->GetArchitecture(); + if (!arch) + return result; + + for (const auto& param: func->GetParameterVariables().GetValue()) + { + switch (param.type) + { + case RegisterVariableSourceType: + { + auto paramName = func->GetVariableName(param); + auto reg = param.storage; + auto regName = arch->GetRegisterName(reg); + auto value = m_debugger->GetRegisterValue(regName); + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(RegisterToken, regName); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case StackVariableSourceType: + { + auto offset = param.storage; + // Account for the return address on the stack for x64/x86_64, not sure if we should do it for other arch + offset -= arch->GetAddressSize(); + auto realOffset = offset + m_debugger->StackPointer(); + + BinaryReader reader(m_data); + reader.Seek(realOffset); + auto value = reader.ReadPointer(); + auto hints = m_debugger->GetAddressInformation(value); + + auto paramName = func->GetVariableName(param); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + + auto stackReg = arch->GetStackPointerRegister(); + auto stackRegName = arch->GetRegisterName(stackReg); + tokens.emplace_back(RegisterToken, stackRegName); + if (offset != 0) + { + tokens.emplace_back(TextToken, " + "); + char buf[64] = {0}; + snprintf(buf, sizeof(buf), "%#llx", offset); + tokens.emplace_back(IntegerToken, buf, offset); + } + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case FlagVariableSourceType: + break; + } + } + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForMLILConditions(MediumLevelILFunctionRef mlil, + const MediumLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != MLIL_IF) + return result; + + auto func = mlil->GetFunction(); + auto condition = instr.GetConditionExpr(); + uint64_t value; + if (!m_debugger->ComputeExprValue(mlil, condition, value)) + return result; + + // The value of a conditional expression must be 0 or 1 if it can be evaluated + if ((value != 1) && (value != 0)) + return result; + + std::vector tokens; + if (!mlil->GetExprText(func->GetArchitecture(), condition.exprIndex, tokens)) + return result; + + auto trueBranch = instr.GetTrueTarget(); + auto falseBranch = instr.GetFalseTarget(); + auto targetIL = value == 1 ? trueBranch : falseBranch; + auto il = mlil->GetInstruction(targetIL); + auto targetAddr = il.address; + + string hints = fmt::format("Branch to {} @ {:#x}", targetIL, targetAddr); + result.emplace_back(tokens, value, hints, instr.instructionIndex, instr.exprIndex, instr.address); + return result; +} + + +std::vector DebuggerInfoTable::getInfoForHLIL(HighLevelILFunctionRef hlil, + const HighLevelILInstruction& instr) +{ + std::vector result; + auto func = hlil->GetFunction(); + for (const auto operand: instr.GetOperands()) + { + switch (operand.GetType()) + { + case ExprHighLevelOperand: + { + uint64_t value; + if (!m_debugger->ComputeExprValue(hlil, operand.GetExpr(), value)) + continue; + std::vector lines = hlil->GetExprText(operand.GetExpr().exprIndex); + if (lines.empty()) + continue; + std::vector tokens; + for (const auto& line: lines) + tokens.insert(tokens.end(), line.tokens.begin(), line.tokens.end()); + if (tokens.empty()) + continue; + + auto hints = m_debugger->GetAddressInformation(value); + result.emplace_back(tokens, value, hints, instr.instructionIndex, operand.GetExpr().exprIndex, instr.address); + break; + } + case VariableHighLevelOperand: + { + uint64_t value; + auto var = operand.GetVariable(); + if (!m_debugger->GetVariableValue(var, instr.address, instr.size, value)) + break; + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + auto name = func->GetVariableName(var); + tokens.emplace_back(LocalVariableToken, name); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + default: + break; + } + } + + // Display the info of the function arguments if the current HLIL is a call instruction + auto lines = getInfoForHLILCalls(hlil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + // Display the info of the conditional expressions + lines = getInfoForHLILConditions(hlil, instr); + if (!lines.empty()) + result.insert(result.end(), lines.begin(), lines.end()); + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForHLILCalls(HighLevelILFunctionRef hlil, + const HighLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != HLIL_CALL && instr.operation != HLIL_TAILCALL) + return result; + + auto dest = instr.GetDestExpr(); + if (dest.operation != HLIL_CONST_PTR && dest.operation != HLIL_CONST) + return result; + + auto callTarget = dest.GetConstant(); + auto functions = m_data->GetAnalysisFunctionsForAddress(callTarget); + if (functions.empty()) + return result; + + auto func = functions[0]; + if (!func) + return result; + + auto arch = func->GetArchitecture(); + if (!arch) + return result; + + for (const auto& param: func->GetParameterVariables().GetValue()) + { + switch (param.type) + { + case RegisterVariableSourceType: + { + auto paramName = func->GetVariableName(param); + auto reg = param.storage; + auto regName = arch->GetRegisterName(reg); + auto value = m_debugger->GetRegisterValue(regName); + auto hints = m_debugger->GetAddressInformation(value); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(RegisterToken, regName); + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case StackVariableSourceType: + { + auto offset = param.storage; + // Account for the return address on the stack for x64/x86_64, not sure if we should do it for other arch + offset -= arch->GetAddressSize(); + auto realOffset = offset + m_debugger->StackPointer(); + + BinaryReader reader(m_data); + reader.Seek(realOffset); + auto value = reader.ReadPointer(); + auto hints = m_debugger->GetAddressInformation(value); + + auto paramName = func->GetVariableName(param); + std::vector tokens; + tokens.emplace_back(LocalVariableToken, paramName); + tokens.emplace_back(TextToken, " @ "); + + auto stackReg = arch->GetStackPointerRegister(); + auto stackRegName = arch->GetRegisterName(stackReg); + tokens.emplace_back(RegisterToken, stackRegName); + if (offset != 0) + { + tokens.emplace_back(TextToken, " + "); + char buf[64] = {0}; + snprintf(buf, sizeof(buf), "%#llx", offset); + tokens.emplace_back(IntegerToken, buf, offset); + } + result.emplace_back(tokens, value, hints, instr.instructionIndex, BN_INVALID_EXPR, instr.address); + break; + } + case FlagVariableSourceType: + break; + } + } + + return result; +} + + +std::vector DebuggerInfoTable::getInfoForHLILConditions(HighLevelILFunctionRef hlil, + const HighLevelILInstruction &instr) +{ + std::vector result; + if (instr.operation != HLIL_IF) + return result; + + auto func = hlil->GetFunction(); + auto condition = instr.GetConditionExpr(); + uint64_t value; + if (!m_debugger->ComputeExprValue(hlil, condition, value)) + return result; + + // The value of a conditional expression must be 0 or 1 if it can be evaluated + if ((value != 1) && (value != 0)) + return result; + + std::vector lines = hlil->GetExprText(condition.exprIndex); + if (lines.empty()) + return result; + std::vector tokens; + for (const auto& line: lines) + tokens.insert(tokens.end(), line.tokens.begin(), line.tokens.end()); + if (tokens.empty()) + return result; + + auto trueBranch = instr.GetTrueExpr(); + auto falseBranch = instr.GetFalseExpr(); + auto targetIL = value == 1 ? trueBranch : falseBranch; + string hints = fmt::format("Branch to {} @ {:#x}", targetIL.instructionIndex, targetIL.address); + result.emplace_back(tokens, value, hints, instr.instructionIndex, instr.exprIndex, instr.address); + return result; +} + + +vector DebuggerInfoTable::getILInfoEntries(const ViewLocation &location) +{ + vector result; + if (!m_debugger->IsConnected()) + return result; + + switch (location.getILViewType()) + { + case NormalFunctionGraph: + { + auto func = location.getFunction(); + if (!func) + break; + auto addr = location.getOffset(); + auto llil = func->GetLowLevelILIfAvailable(); + if (!llil) + break; + auto llils = func->GetLowLevelILInstructionsForAddress(func->GetArchitecture(), addr); + for (const auto index: llils) + { + auto instr = llil->GetInstruction(index); + auto entries = getInfoForLLIL(llil, instr); + result.insert(result.end(), entries.begin(), entries.end()); + } + break; + } + case LowLevelILFunctionGraph: + { + auto func = location.getFunction(); + if (!func) + break; + auto llil = func->GetLowLevelILIfAvailable(); + if (!llil) + break; + if (location.getInstrIndex() == BN_INVALID_EXPR) + break; + auto instr = llil->GetInstruction(location.getInstrIndex()); + auto entries = getInfoForLLIL(llil, instr); + result.insert(result.end(), entries.begin(), entries.end()); + break; + } + case MediumLevelILFunctionGraph: + { + auto func = location.getFunction(); + if (!func) + break; + auto mlil = func->GetMediumLevelILIfAvailable(); + if (!mlil) + break; + if (location.getInstrIndex() == BN_INVALID_EXPR) + break; + auto instr = mlil->GetInstruction(location.getInstrIndex()); + auto entries = getInfoForMLIL(mlil, instr); + result.insert(result.end(), entries.begin(), entries.end()); + break; + } + case HighLevelILFunctionGraph: + { + auto func = location.getFunction(); + if (!func) + break; + auto hlil = func->GetHighLevelILIfAvailable(); + if (!hlil) + break; + if (location.getInstrIndex() == BN_INVALID_EXPR) + break; + auto instr = hlil->GetInstruction(location.getInstrIndex()); + auto entries = getInfoForHLIL(hlil, instr); + result.insert(result.end(), entries.begin(), entries.end()); + break; + } + default: + break; + } + + return result; +} + + +void DebugInfoSidebarWidget::notifyViewLocationChanged(View* view, const ViewLocation& location) +{ + m_entryList->updateContents(location); +} + + +DebugInfoSidebarWidget::~DebugInfoSidebarWidget() +{ + +} + + +void DebugInfoSidebarWidget::notifyFontChanged() +{ + m_entryList->updateFonts(); +} + + +DebuggerInfoEntryItemDelegate::DebuggerInfoEntryItemDelegate(QWidget* parent): m_render(parent) +{ + updateFonts(); +} + + +void DebuggerInfoEntryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + // Draw the item background, highlighting it if selected. + bool selected = (option.state & QStyle::State_Selected) != 0; + if (selected) + painter->setBrush(getThemeColor(SelectionColor)); + else + painter->setBrush(option.backgroundBrush); + + auto* entry = qvariant_cast(index.data(Qt::DisplayRole)); + if (!entry) + { + QStyledItemDelegate::paint(painter, option, index); + return; + } + + painter->setPen(Qt::NoPen); + painter->drawRect(option.rect); + + painter->setPen(option.palette.text().color()); + painter->setFont(m_font); + + QRect textRect = option.rect; +// textRect.setLeft(textRect.left() + 8); + + switch (index.column()) + { + case ExprColumn: + { + HighlightTokenState highlight; + m_render.drawDisassemblyLine(*painter, textRect.left(), textRect.top(), entry->tokens, highlight); + break; + } + case ValueColumn: + painter->setPen(getThemeColor(AddressColor)); + painter->drawText(textRect, "0x" + QString::number(entry->value, 16)); + break; + case HintColumn: + painter->setPen(getThemeColor(StringColor)); + painter->drawText(textRect, QString::fromStdString(entry->hints)); + break; + default: + break; + } + +} + + +void DebuggerInfoEntryItemDelegate::updateFonts() +{ + // Get font and compute character sizes + m_font = getMonospaceFont(dynamic_cast(parent())); + m_font.setKerning(false); + m_baseline = (int)QFontMetricsF(m_font).ascent(); + m_charWidth = getFontWidthAndAdjustSpacing(m_font); + m_charHeight = (int)(QFontMetricsF(m_font).height() + getExtraFontSpacing()); + m_charOffset = getFontVerticalOffset(); +} + + +QSize DebuggerInfoEntryItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& idx) const +{ + auto totalWidth = (idx.data(Qt::SizeHintRole).toInt() + 2) * m_charWidth + 4; + return QSize(totalWidth, m_charHeight + 2); +} + + +DebuggerInfoEntryItemModel::DebuggerInfoEntryItemModel(QWidget *parent, BinaryViewRef data) +{ + +} + + +DebuggerInfoEntryItemModel::~DebuggerInfoEntryItemModel() +{ + +} + + +QModelIndex DebuggerInfoEntryItemModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || (size_t)row >= m_infoEntries.size() || column >= columnCount()) + { + return QModelIndex(); + } + + return createIndex(row, column, (void*)&m_infoEntries[row]); +} + + +QModelIndex DebuggerInfoEntryItemModel::parent(const QModelIndex &child) const +{ + return {}; +} + + +int DebuggerInfoEntryItemModel::rowCount(const QModelIndex &parent) const +{ + return m_infoEntries.size(); +} + + +int DebuggerInfoEntryItemModel::columnCount(const QModelIndex &parent) const +{ + return 3; +} + + +QVariant DebuggerInfoEntryItemModel::data(const QModelIndex &index, int role) const +{ + if (index.column() >= columnCount() || (size_t)index.row() >= m_infoEntries.size()) + return QVariant(); + + DebuggerInfoEntry* item = static_cast(index.internalPointer()); + if (!item) + return QVariant(); + + QVariant result; + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case ExprColumn: + case ValueColumn: + case HintColumn: + result.setValue(item); + break; + default: + break; + } + } + else if (role == Qt::SizeHintRole) + { + switch (index.column()) + { + case ExprColumn: + { + std::string expr; + for (const auto& token: item->tokens) + expr += token.text; + + result.setValue(expr.size()); + break; + } + case ValueColumn: + { + auto str = "0x" + QString::number(item->value, 16); + result.setValue(str.size()); + break; + } + case HintColumn: + result.setValue(item->hints.size()); + break; + default: + break; + } + } + + return result; +} + + +void DebuggerInfoEntryItemModel::updateRows(std::vector& newRows) +{ + beginResetModel(); + m_infoEntries = newRows; + endResetModel(); +} + + +QVariant DebuggerInfoEntryItemModel::headerData(int column, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Vertical) + return QVariant(); + + switch (column) + { + case ExprColumn: + return "Expr"; + case ValueColumn: + return "Value"; + case HintColumn: + return "Hint"; + } + return QVariant(); +} + + +DebuggerInfoEntry DebuggerInfoEntryItemModel::getRow(int row) const +{ + if ((size_t)row >= m_infoEntries.size()) + throw std::runtime_error("row index out-of-bound"); + + return m_infoEntries[row]; +} + + +DebuggerInfoTable::DebuggerInfoTable(BinaryViewRef data): m_data(data) +{ + m_debugger = DebuggerController::GetController(data); + + m_model = new DebuggerInfoEntryItemModel(this, data); + m_itemDelegate = new DebuggerInfoEntryItemDelegate(this); + + setModel(m_model); + setSelectionMode(QListView::SingleSelection); + setSelectionBehavior(QListView::SelectRows); + setEditTriggers(QListView::NoEditTriggers); + setDragEnabled(false); + setDragDropMode(QListView::NoDragDrop); + setItemDelegate(m_itemDelegate); + + horizontalHeader()->setStretchLastSection(true); + + connect(this, &QTableView::doubleClicked, this, &DebuggerInfoTable::onDoubleClicked); +} + + +void DebuggerInfoTable::updateContents(const ViewLocation &location) +{ + if (!location.isValid() || !location.getFunction()) + return; + + auto info = getILInfoEntries(location); + m_model->updateRows(info); + updateColumnWidths(); +} + + +void DebuggerInfoTable::updateColumnWidths() +{ + resizeColumnToContents(ExprColumn); + resizeColumnToContents(ValueColumn); + resizeColumnToContents(HintColumn); +} + + +void DebuggerInfoTable::updateFonts() +{ + m_itemDelegate->updateFonts(); +} + + +void DebuggerInfoTable::onDoubleClicked() +{ + QModelIndexList sel = selectionModel()->selectedIndexes(); + if (sel.empty()) + return; + + auto info = m_model->getRow(sel[0].row()); + uint64_t value = info.value; + + UIContext* context = UIContext::contextForWidget(this); + if (!context) + return; + + ViewFrame* frame = context->getCurrentViewFrame(); + if (!frame) + return; + + if (m_debugger->GetData()) + frame->navigate(m_debugger->GetData(), value, true, true); +} + + +DebugInfoWidgetType::DebugInfoWidgetType(): + SidebarWidgetType(QIcon(":/debugger/debugger").pixmap(QSize(64, 64)).toImage(), "Debugger Info") +{ +} + + +SidebarWidget* DebugInfoWidgetType::createWidget(ViewFrame*, BinaryViewRef data) +{ + return new DebugInfoSidebarWidget(data); +} diff --git a/ui/debuggerinfowidget.h b/ui/debuggerinfowidget.h new file mode 100644 index 0000000..035abd2 --- /dev/null +++ b/ui/debuggerinfowidget.h @@ -0,0 +1,178 @@ +/* +Copyright 2020-2024 Vector 35 Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "inttypes.h" +#include "binaryninjaapi.h" +#include "dockhandler.h" +#include "viewframe.h" +#include "fontsettings.h" +#include "theme.h" +#include "render.h" +//#include "globalarea.h" +#include "debuggerapi.h" + + +using namespace BinaryNinjaDebuggerAPI; +using namespace BinaryNinja; +using namespace std; + + +enum ColumnHeaders +{ + ExprColumn, + ValueColumn, + HintColumn, +}; + + +struct DebuggerInfoEntry +{ + std::vector tokens; + uint64_t value; + std::string hints; + size_t instrIndex; + size_t operandIndex; + uint64_t address; + + DebuggerInfoEntry(const std::vector& t, uint64_t v, const std::string& h, size_t i, size_t o, + uint64_t a): tokens(t), value(v), hints(h), instrIndex(i), operandIndex(o), address(a) + {} +}; + + +class DebuggerInfoEntryItemModel : public QAbstractTableModel +{ + std::vector m_infoEntries; + + FileMetadataRef m_file; + std::vector m_views; + QSettings m_settings; + +public: + DebuggerInfoEntryItemModel(QWidget* parent, BinaryViewRef data); + ~DebuggerInfoEntryItemModel(); + + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& child) const override; + + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + void updateRows(std::vector& newRows); + virtual QVariant headerData(int column, Qt::Orientation orientation, int role) const override; + DebuggerInfoEntry getRow(int row) const; +}; + + +class DebuggerInfoEntryItemDelegate : public QStyledItemDelegate +{ +Q_OBJECT + + QFont m_font; + int m_baseline, m_charWidth, m_charHeight, m_charOffset; + + RenderContext m_render; + +public: + DebuggerInfoEntryItemDelegate(QWidget* parent = nullptr); + + void updateFonts(); + + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& idx) const override; + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; +}; + + +class DebuggerInfoTable : public QTableView +{ +Q_OBJECT; + + DebuggerInfoEntryItemModel* m_model; + DebuggerInfoEntryItemDelegate* m_itemDelegate; + + BinaryViewRef m_data; + DebuggerControllerRef m_debugger; + + std::vector getILInfoEntries(const ViewLocation& location); + std::vector getInfoForLLIL(LowLevelILFunctionRef llil, const LowLevelILInstruction& instr); + std::vector getInfoForLLILCalls(LowLevelILFunctionRef llil, const LowLevelILInstruction& instr); + std::vector getInfoForLLILConditions(LowLevelILFunctionRef llil, const LowLevelILInstruction& instr); + + std::vector getInfoForMLIL(MediumLevelILFunctionRef mlil, const MediumLevelILInstruction& instr); + std::vector getInfoForMLILCalls(MediumLevelILFunctionRef mlil, const MediumLevelILInstruction& instr); + std::vector getInfoForMLILConditions(MediumLevelILFunctionRef mlil, const MediumLevelILInstruction& instr); + + std::vector getInfoForHLIL(HighLevelILFunctionRef hlil, const HighLevelILInstruction& instr); + std::vector getInfoForHLILCalls(HighLevelILFunctionRef hlil, const HighLevelILInstruction& instr); + std::vector getInfoForHLILConditions(HighLevelILFunctionRef hlil, const HighLevelILInstruction& instr); + + void updateColumnWidths(); + +private slots: + void onDoubleClicked(); + +public: + DebuggerInfoTable(BinaryViewRef data); + void updateFonts(); + + void updateContents(const ViewLocation& location); +}; + + +class DebugInfoSidebarWidget : public SidebarWidget +{ +Q_OBJECT + DebuggerInfoTable* m_entryList; + + QWidget* m_header; + BinaryViewRef m_data; + DebuggerControllerRef m_debugger; + +// virtual void contextMenuEvent(QContextMenuEvent*) override; + + virtual void notifyViewLocationChanged(View* /*view*/, const ViewLocation& /*viewLocation*/) override; + + void itemDoubleClicked(const QModelIndex& index); + void scrollBarValueChanged(int value); + void scrollBarRangeChanged(int min, int max); + + void resetToSelectedEntry(std::function progress); + +public: + DebugInfoSidebarWidget(BinaryViewRef data); + ~DebugInfoSidebarWidget(); + void notifyFontChanged() override; +// QWidget* headerWidget() override { return m_header; } +}; + + +class DebugInfoWidgetType : public SidebarWidgetType +{ +public: + DebugInfoWidgetType(); + SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; + SidebarWidgetLocation defaultLocation() const override { return SidebarWidgetLocation::RightBottom; } + SidebarContextSensitivity contextSensitivity() const override { return PerViewTypeSidebarContext; } +// bool hideIfNoContent() const override { return true; } +}; diff --git a/ui/ui.cpp b/ui/ui.cpp index 62b918a..c6ea92f 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -40,6 +40,7 @@ limitations under the License. #include "progresstask.h" #include "attachprocess.h" #include "progresstask.h" +#include "debuggerinfowidget.h" #ifdef WIN32 #include "ttdrecord.h" @@ -1428,6 +1429,7 @@ void GlobalDebuggerUI::InitializeUI() Sidebar::addSidebarWidgetType(new DebuggerWidgetType(QImage(":/debugger/debugger"), "Debugger")); Sidebar::addSidebarWidgetType(new DebugModulesSidebarWidgetType()); Sidebar::addSidebarWidgetType(new ThreadFramesSidebarWidgetType()); + Sidebar::addSidebarWidgetType(new DebugInfoWidgetType()); }