diff --git a/ui/debuggerinfowidget.cpp b/ui/debuggerinfowidget.cpp index 6752ea2c..dd333c45 100644 --- a/ui/debuggerinfowidget.cpp +++ b/ui/debuggerinfowidget.cpp @@ -19,12 +19,16 @@ limitations under the License. #include #include #include +#include +#include +#include #include "ui.h" #include "debuggerinfowidget.h" #include "lowlevelilinstruction.h" #include "mediumlevelilinstruction.h" #include "highlevelilinstruction.h" #include "binaryninjaapi.h" +#include "fmt/format.h" using namespace BinaryNinja; using namespace std; @@ -551,6 +555,103 @@ std::vector DebuggerInfoTable::getInfoForHLILConditions(HighL } +std::vector DebuggerInfoTable::getStackInfo(const ViewLocation& location) +{ + std::vector result; + + if (!m_debugger->IsConnected()) + return result; + + auto func = location.getFunction(); + if (!func) + return result; + + auto arch = func->GetArchitecture(); + if (!arch) + return result; + + uint64_t stackPointer = m_debugger->StackPointer(); + size_t addressSize = arch->GetAddressSize(); + + // Get stack register name for display + auto stackReg = arch->GetStackPointerRegister(); + auto stackRegName = arch->GetRegisterName(stackReg); + + // Read stack contents - show configurable number of entries + BinaryReader reader(m_data); + for (int i = 0; i < m_stackEntryCount; i++) + { + ptrdiff_t offset = i * addressSize; + uint64_t stackAddress = stackPointer + offset; + + try + { + reader.Seek(stackAddress); + uint64_t value = 0; + + switch (addressSize) + { + case 1: + value = reader.Read8(); + break; + case 2: + value = reader.Read16(); + break; + case 4: + value = reader.Read32(); + break; + case 8: + value = reader.Read64(); + break; + default: + continue; + } + + // Create tokens for stack entry display + std::vector tokens; + tokens.emplace_back(RegisterToken, stackRegName); + if (offset != 0) + { + tokens.emplace_back(TextToken, " + "); + tokens.emplace_back(IntegerToken, fmt::format("0x{:x}", offset), offset); + } + + // Get hint information using the existing API + std::string hint = m_debugger->GetAddressInformation(value); + + // Check if this looks like a return address by checking if it's in a function + // and the previous instruction is a call + if (hint.empty() && value != 0) + { + auto targetFunc = m_data->GetAnalysisFunction(m_data->GetDefaultPlatform(), value); + if (targetFunc) + { + // Check if the previous address contains a call instruction + auto prevAddr = value - 1; // Rough approximation + auto callingFunc = m_data->GetAnalysisFunction(m_data->GetDefaultPlatform(), prevAddr); + if (callingFunc) + { + hint = fmt::format("Return address to {}", targetFunc->GetSymbol() ? + targetFunc->GetSymbol()->GetShortName() : + fmt::format("func_{:x}", targetFunc->GetStart())); + } + } + } + + // Create stack entry with storage address and stack flag + result.emplace_back(tokens, value, hint, BN_INVALID_EXPR, BN_INVALID_EXPR, stackAddress, stackAddress, true); + } + catch (const std::exception&) + { + // Skip this entry if we can't read it + continue; + } + } + + return result; +} + + vector DebuggerInfoTable::getILInfoEntries(const ViewLocation &location) { vector result; @@ -627,6 +728,10 @@ vector DebuggerInfoTable::getILInfoEntries(const ViewLocation break; } + // Add stack information + auto stackEntries = getStackInfo(location); + result.insert(result.end(), stackEntries.begin(), stackEntries.end()); + return result; } @@ -689,6 +794,14 @@ void DebuggerInfoEntryItemDelegate::paint(QPainter *painter, const QStyleOptionV m_render.drawDisassemblyLine(*painter, textRect.left(), textRect.top(), entry->tokens, highlight); break; } + case StorageColumn: + if (entry->isStackEntry) + { + painter->setPen(getThemeColor(AddressColor)); + painter->drawText(textRect, QString::asprintf("0x%llx", entry->storageAddress)); + } + // Draw nothing for non-stack entries (empty column) + break; case ValueColumn: painter->setPen(getThemeColor(AddressColor)); painter->drawText(textRect, QString::fromStdString("0x") + QString::fromStdString(intx::hex(entry->value))); @@ -760,7 +873,7 @@ int DebuggerInfoEntryItemModel::rowCount(const QModelIndex &parent) const int DebuggerInfoEntryItemModel::columnCount(const QModelIndex &parent) const { - return 3; + return 4; // Added StorageColumn } @@ -781,6 +894,7 @@ QVariant DebuggerInfoEntryItemModel::data(const QModelIndex &index, int role) co case ExprColumn: case ValueColumn: case HintColumn: + case StorageColumn: result.setValue(item); break; default: @@ -800,6 +914,19 @@ QVariant DebuggerInfoEntryItemModel::data(const QModelIndex &index, int role) co result.setValue(expr.size()); break; } + case StorageColumn: + { + if (item->isStackEntry) + { + auto str = QString::asprintf("0x%llx", item->storageAddress); + result.setValue(str.size()); + } + else + { + result.setValue(0); // Empty for non-stack entries + } + break; + } case ValueColumn: { auto str = QString::fromStdString("0x") + QString::fromStdString(intx::hex(item->value)); @@ -838,6 +965,8 @@ QVariant DebuggerInfoEntryItemModel::headerData(int column, Qt::Orientation orie { case ExprColumn: return "Expr"; + case StorageColumn: + return "Storage"; case ValueColumn: return "Value"; case HintColumn: @@ -856,7 +985,7 @@ DebuggerInfoEntry DebuggerInfoEntryItemModel::getRow(int row) const } -DebuggerInfoTable::DebuggerInfoTable(BinaryViewRef data): m_data(data) +DebuggerInfoTable::DebuggerInfoTable(BinaryViewRef data): m_data(data), m_stackEntryCount(16) { m_debugger = DebuggerController::GetController(data); @@ -882,6 +1011,7 @@ void DebuggerInfoTable::updateContents(const ViewLocation &location) if (!location.isValid() || !location.getFunction()) return; + m_currentLocation = location; // Store for context menu updates auto info = getILInfoEntries(location); m_model->updateRows(info); updateColumnWidths(); @@ -891,6 +1021,7 @@ void DebuggerInfoTable::updateContents(const ViewLocation &location) void DebuggerInfoTable::updateColumnWidths() { resizeColumnToContents(ExprColumn); + resizeColumnToContents(StorageColumn); resizeColumnToContents(ValueColumn); resizeColumnToContents(HintColumn); } @@ -902,14 +1033,34 @@ void DebuggerInfoTable::updateFonts() } -void DebuggerInfoTable::onDoubleClicked() +void DebuggerInfoTable::onDoubleClicked(const QModelIndex& index) { - QModelIndexList sel = selectionModel()->selectedIndexes(); - if (sel.empty()) + if (!index.isValid()) return; - auto info = m_model->getRow(sel[0].row()); - uint64_t value = (uint64_t)info.value; + auto info = m_model->getRow(index.row()); + uint64_t targetAddress = 0; + + // Check which column was clicked and determine the target address + switch (index.column()) + { + case ValueColumn: + targetAddress = (uint64_t)info.value; + break; + case StorageColumn: + if (info.isStackEntry) + targetAddress = info.storageAddress; + else + return; // No navigation for empty storage column + break; + default: + // For other columns, navigate to the value address (original behavior) + targetAddress = (uint64_t)info.value; + break; + } + + if (targetAddress == 0) + return; UIContext* context = UIContext::contextForWidget(this); if (!context) @@ -920,7 +1071,54 @@ void DebuggerInfoTable::onDoubleClicked() return; if (m_debugger->GetData()) - frame->navigate(m_debugger->GetData(), value, true, true); + frame->navigate(m_debugger->GetData(), targetAddress, true, true); +} + + +void DebuggerInfoTable::contextMenuEvent(QContextMenuEvent* event) +{ + QMenu menu(this); + + QAction* increaseAction = menu.addAction("Show More Stack Entries"); + QAction* decreaseAction = menu.addAction("Show Fewer Stack Entries"); + + // Add current count info + menu.addSeparator(); + QAction* infoAction = menu.addAction(QString("Currently showing %1 entries").arg(m_stackEntryCount)); + infoAction->setEnabled(false); + + // Disable actions if at limits + if (m_stackEntryCount >= 64) // Set reasonable upper limit + increaseAction->setEnabled(false); + if (m_stackEntryCount <= 4) // Set reasonable lower limit + decreaseAction->setEnabled(false); + + connect(increaseAction, &QAction::triggered, this, &DebuggerInfoTable::increaseStackEntries); + connect(decreaseAction, &QAction::triggered, this, &DebuggerInfoTable::decreaseStackEntries); + + menu.exec(event->globalPos()); +} + + +void DebuggerInfoTable::increaseStackEntries() +{ + if (m_stackEntryCount < 64) + { + m_stackEntryCount += 4; // Increase by 4 entries at a time + if (m_currentLocation.isValid()) + updateContents(m_currentLocation); + } +} + + +void DebuggerInfoTable::decreaseStackEntries() +{ + if (m_stackEntryCount > 4) + { + m_stackEntryCount -= 4; // Decrease by 4 entries at a time + if (m_currentLocation.isValid()) + updateContents(m_currentLocation); + } } diff --git a/ui/debuggerinfowidget.h b/ui/debuggerinfowidget.h index 3bcd2538..99d32515 100644 --- a/ui/debuggerinfowidget.h +++ b/ui/debuggerinfowidget.h @@ -39,6 +39,7 @@ using namespace std; enum ColumnHeaders { ExprColumn, + StorageColumn, // Moved to second position ValueColumn, HintColumn, }; @@ -52,9 +53,12 @@ struct DebuggerInfoEntry size_t instrIndex; size_t operandIndex; uint64_t address; + uint64_t storageAddress; // For stack entries, this will be the actual stack address + bool isStackEntry; // Flag to identify stack entries DebuggerInfoEntry(const std::vector& t, intx::uint512 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) + uint64_t a, uint64_t sa = 0, bool stack = false): + tokens(t), value(v), hints(h), instrIndex(i), operandIndex(o), address(a), storageAddress(sa), isStackEntry(stack) {} }; @@ -112,6 +116,8 @@ Q_OBJECT; BinaryViewRef m_data; DebuggerControllerRef m_debugger; + int m_stackEntryCount; // Number of stack entries to display + ViewLocation m_currentLocation; // Store current location for context menu updates std::vector getILInfoEntries(const ViewLocation& location); std::vector getInfoForLLIL(LowLevelILFunctionRef llil, const LowLevelILInstruction& instr); @@ -126,10 +132,17 @@ Q_OBJECT; std::vector getInfoForHLILCalls(HighLevelILFunctionRef hlil, const HighLevelILInstruction& instr); std::vector getInfoForHLILConditions(HighLevelILFunctionRef hlil, const HighLevelILInstruction& instr); + std::vector getStackInfo(const ViewLocation& location); + void updateColumnWidths(); +protected: + virtual void contextMenuEvent(QContextMenuEvent* event) override; + private slots: - void onDoubleClicked(); + void onDoubleClicked(const QModelIndex& index); + void increaseStackEntries(); + void decreaseStackEntries(); public: DebuggerInfoTable(BinaryViewRef data);