928 lines
34 KiB
C++
928 lines
34 KiB
C++
#include "CPUStack.h"
|
|
#include "CPUDump.h"
|
|
#include <QClipboard>
|
|
#include "Configuration.h"
|
|
#include "Bridge.h"
|
|
#include "CommonActions.h"
|
|
#include "HexEditDialog.h"
|
|
#include "WordEditDialog.h"
|
|
#include "CPUMultiDump.h"
|
|
#include "GotoDialog.h"
|
|
|
|
CPUStack::CPUStack(CPUMultiDump* multiDump, QWidget* parent) : HexDump(parent)
|
|
{
|
|
setWindowTitle("Stack");
|
|
setShowHeader(false);
|
|
int charwidth = getCharWidth();
|
|
ColumnDescriptor wColDesc;
|
|
DataDescriptor dDesc;
|
|
mMultiDump = multiDump;
|
|
|
|
mForceColumn = 1;
|
|
|
|
wColDesc.isData = true; //void*
|
|
wColDesc.itemCount = 1;
|
|
wColDesc.separator = 0;
|
|
#ifdef _WIN64
|
|
wColDesc.data.itemSize = Qword;
|
|
wColDesc.data.qwordMode = HexQword;
|
|
#else
|
|
wColDesc.data.itemSize = Dword;
|
|
wColDesc.data.dwordMode = HexDword;
|
|
#endif
|
|
appendDescriptor(10 + charwidth * 2 * sizeof(duint), "void*", false, wColDesc);
|
|
|
|
wColDesc.isData = false; //comments
|
|
wColDesc.itemCount = 0;
|
|
wColDesc.separator = 0;
|
|
dDesc.itemSize = Byte;
|
|
dDesc.byteMode = AsciiByte;
|
|
wColDesc.data = dDesc;
|
|
appendDescriptor(2000, tr("Comments"), false, wColDesc);
|
|
|
|
setupContextMenu();
|
|
|
|
mGoto = 0;
|
|
|
|
// Slots
|
|
connect(Bridge::getBridge(), SIGNAL(stackDumpAt(duint, duint)), this, SLOT(stackDumpAt(duint, duint)));
|
|
connect(Bridge::getBridge(), SIGNAL(selectionStackGet(SELECTIONDATA*)), this, SLOT(selectionGet(SELECTIONDATA*)));
|
|
connect(Bridge::getBridge(), SIGNAL(selectionStackSet(const SELECTIONDATA*)), this, SLOT(selectionSet(const SELECTIONDATA*)));
|
|
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChangedSlot(DBGSTATE)));
|
|
connect(Bridge::getBridge(), SIGNAL(focusStack()), this, SLOT(setFocus()));
|
|
connect(Bridge::getBridge(), SIGNAL(updateDump()), this, SLOT(updateSlot()));
|
|
|
|
connect(this, SIGNAL(selectionUpdated()), this, SLOT(selectionUpdatedSlot()));
|
|
|
|
Initialize();
|
|
}
|
|
|
|
void CPUStack::updateColors()
|
|
{
|
|
HexDump::updateColors();
|
|
|
|
mBackgroundColor = ConfigColor("StackBackgroundColor");
|
|
mTextColor = ConfigColor("StackTextColor");
|
|
mSelectionColor = ConfigColor("StackSelectionColor");
|
|
mStackReturnToColor = ConfigColor("StackReturnToColor");
|
|
mStackSEHChainColor = ConfigColor("StackSEHChainColor");
|
|
mUserStackFrameColor = ConfigColor("StackFrameColor");
|
|
mSystemStackFrameColor = ConfigColor("StackFrameSystemColor");
|
|
}
|
|
|
|
void CPUStack::updateFonts()
|
|
{
|
|
setFont(ConfigFont("Stack"));
|
|
invalidateCachedFont();
|
|
}
|
|
|
|
void CPUStack::setupContextMenu()
|
|
{
|
|
mMenuBuilder = new MenuBuilder(this, [](QMenu*)
|
|
{
|
|
return DbgIsDebugging();
|
|
});
|
|
mCommonActions = new CommonActions(this, getActionHelperFuncs(), [this]()
|
|
{
|
|
return rvaToVa(getSelectionStart());
|
|
});
|
|
|
|
//Realign
|
|
mMenuBuilder->addAction(makeAction(DIcon("align-stack-pointer"), tr("Align Stack Pointer"), SLOT(realignSlot())), [this](QMenu*)
|
|
{
|
|
return (mCsp & (sizeof(duint) - 1)) != 0;
|
|
});
|
|
|
|
// Modify
|
|
mMenuBuilder->addAction(makeAction(DIcon("modify"), tr("Modify"), SLOT(modifySlot())));
|
|
|
|
auto binaryMenu = new MenuBuilder(this);
|
|
|
|
//Binary->Edit
|
|
binaryMenu->addAction(makeShortcutAction(DIcon("binary_edit"), tr("&Edit"), SLOT(binaryEditSlot()), "ActionBinaryEdit"));
|
|
|
|
//Binary->Fill
|
|
binaryMenu->addAction(makeShortcutAction(DIcon("binary_fill"), tr("&Fill..."), SLOT(binaryFillSlot()), "ActionBinaryFill"));
|
|
|
|
//Binary->Separator
|
|
binaryMenu->addSeparator();
|
|
|
|
//Binary->Copy
|
|
binaryMenu->addAction(makeShortcutAction(DIcon("binary_copy"), tr("&Copy"), SLOT(binaryCopySlot()), "ActionBinaryCopy"));
|
|
|
|
//Binary->Paste
|
|
binaryMenu->addAction(makeShortcutAction(DIcon("binary_paste"), tr("&Paste"), SLOT(binaryPasteSlot()), "ActionBinaryPaste"));
|
|
|
|
//Binary->Paste (Ignore Size)
|
|
binaryMenu->addAction(makeShortcutAction(DIcon("binary_paste_ignoresize"), tr("Paste (&Ignore Size)"), SLOT(binaryPasteIgnoreSizeSlot()), "ActionBinaryPasteIgnoreSize"));
|
|
|
|
mMenuBuilder->addMenu(makeMenu(DIcon("binary"), tr("B&inary")), binaryMenu);
|
|
|
|
auto copyMenu = new MenuBuilder(this);
|
|
copyMenu->addAction(mCopySelection);
|
|
copyMenu->addAction(mCopyAddress);
|
|
copyMenu->addAction(mCopyRva, [this](QMenu*)
|
|
{
|
|
return DbgFunctions()->ModBaseFromAddr(rvaToVa(getInitialSelection())) != 0;
|
|
});
|
|
mMenuBuilder->addMenu(makeMenu(DIcon("copy"), tr("&Copy")), copyMenu);
|
|
|
|
//Breakpoint (hardware access) menu
|
|
auto hardwareAccessMenu = makeMenu(DIcon("breakpoint_access"), tr("Hardware, Access"));
|
|
hardwareAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_byte"), tr("&Byte"), "bphws $, r, 1"));
|
|
hardwareAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_word"), tr("&Word"), "bphws $, r, 2"));
|
|
hardwareAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_dword"), tr("&Dword"), "bphws $, r, 4"));
|
|
#ifdef _WIN64
|
|
hardwareAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_qword"), tr("&Qword"), "bphws $, r, 8"));
|
|
#endif //_WIN64
|
|
|
|
//Breakpoint (hardware write) menu
|
|
auto hardwareWriteMenu = makeMenu(DIcon("breakpoint_write"), tr("Hardware, Write"));
|
|
hardwareWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_byte"), tr("&Byte"), "bphws $, w, 1"));
|
|
hardwareWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_word"), tr("&Word"), "bphws $, w, 2"));
|
|
hardwareWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_dword"), tr("&Dword"), "bphws $, w, 4"));
|
|
#ifdef _WIN64
|
|
hardwareWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_qword"), tr("&Qword"), "bphws $, r, 8"));
|
|
#endif //_WIN64
|
|
|
|
//Breakpoint (remove hardware)
|
|
auto hardwareRemove = mCommonActions->makeCommandAction(DIcon("breakpoint_remove"), tr("Remove &Hardware"), "bphwc $");
|
|
|
|
//Breakpoint (memory access) menu
|
|
auto memoryAccessMenu = makeMenu(DIcon("breakpoint_memory_access"), tr("Memory, Access"));
|
|
memoryAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_memory_singleshoot"), tr("&Singleshoot"), "bpm $, 0, a"));
|
|
memoryAccessMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_memory_restore_on_hit"), tr("&Restore on hit"), "bpm $, 1, a"));
|
|
|
|
//Breakpoint (memory write) menu
|
|
auto memoryWriteMenu = makeMenu(DIcon("breakpoint_memory_write"), tr("Memory, Write"));
|
|
memoryWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_memory_singleshoot"), tr("&Singleshoot"), "bpm $, 0, w"));
|
|
memoryWriteMenu->addAction(mCommonActions->makeCommandAction(DIcon("breakpoint_memory_restore_on_hit"), tr("&Restore on hit"), "bpm $, 1, w"));
|
|
|
|
//Breakpoint (remove memory) menu
|
|
auto memoryRemove = mCommonActions->makeCommandAction(DIcon("breakpoint_remove"), tr("Remove &Memory"), "bpmc $");
|
|
|
|
//Breakpoint menu
|
|
auto breakpointMenu = new MenuBuilder(this);
|
|
|
|
//Breakpoint menu
|
|
breakpointMenu->addBuilder(new MenuBuilder(this, [ = ](QMenu * menu)
|
|
{
|
|
duint selectedAddr = rvaToVa(getInitialSelection());
|
|
if(DbgGetBpxTypeAt(selectedAddr) & bp_hardware) //hardware breakpoint set
|
|
{
|
|
menu->addAction(hardwareRemove);
|
|
}
|
|
else //memory breakpoint not set
|
|
{
|
|
menu->addMenu(hardwareAccessMenu);
|
|
menu->addMenu(hardwareWriteMenu);
|
|
}
|
|
|
|
menu->addSeparator();
|
|
|
|
if(DbgGetBpxTypeAt(selectedAddr) & bp_memory) //memory breakpoint set
|
|
{
|
|
menu->addAction(memoryRemove);
|
|
}
|
|
else //memory breakpoint not set
|
|
{
|
|
menu->addMenu(memoryAccessMenu);
|
|
menu->addMenu(memoryWriteMenu);
|
|
}
|
|
return true;
|
|
}));
|
|
mMenuBuilder->addMenu(makeMenu(DIcon("breakpoint"), tr("Brea&kpoint")), breakpointMenu);
|
|
|
|
// Restore Selection
|
|
mMenuBuilder->addAction(makeShortcutAction(DIcon("eraser"), tr("&Restore selection"), SLOT(undoSelectionSlot()), "ActionUndoSelection"), [this](QMenu*)
|
|
{
|
|
dsint start = rvaToVa(getSelectionStart());
|
|
dsint end = rvaToVa(getSelectionEnd());
|
|
return DbgFunctions()->PatchInRange(start, end);
|
|
});
|
|
|
|
//Find Pattern
|
|
mMenuBuilder->addAction(makeShortcutAction(DIcon("search-for"), tr("&Find Pattern..."), SLOT(findPattern()), "ActionFindPattern"));
|
|
|
|
//Follow CSP
|
|
mMenuBuilder->addAction(makeShortcutAction(DIcon("neworigin"), ArchValue(tr("Follow E&SP"), tr("Follow R&SP")), SLOT(gotoCspSlot()), "ActionGotoOrigin"));
|
|
mMenuBuilder->addAction(makeShortcutAction(DIcon("cbp"), ArchValue(tr("Follow E&BP"), tr("Follow R&BP")), SLOT(gotoCbpSlot()), "ActionGotoCBP"), [](QMenu*)
|
|
{
|
|
return DbgMemIsValidReadPtr(DbgValFromString("cbp"));
|
|
});
|
|
|
|
auto gotoMenu = new MenuBuilder(this);
|
|
|
|
//Go to Expression
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("geolocation-goto"), tr("Go to &Expression"), SLOT(gotoExpressionSlot()), "ActionGotoExpression"));
|
|
|
|
//Go to Base of Stack Frame
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("neworigin"), tr("Go to Base of Stack Frame"), SLOT(gotoFrameBaseSlot()), "ActionGotoBaseOfStackFrame"));
|
|
|
|
//Go to Previous Frame
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("previous"), tr("Go to Previous Stack Frame"), SLOT(gotoPreviousFrameSlot()), "ActionGotoPrevStackFrame"));
|
|
|
|
//Go to Next Frame
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("next"), tr("Go to Next Stack Frame"), SLOT(gotoNextFrameSlot()), "ActionGotoNextStackFrame"));
|
|
|
|
//Go to Previous
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("previous"), tr("Go to Previous"), SLOT(gotoPreviousSlot()), "ActionGotoPrevious"), [this](QMenu*)
|
|
{
|
|
return mHistory.historyHasPrev();
|
|
});
|
|
|
|
//Go to Next
|
|
gotoMenu->addAction(makeShortcutAction(DIcon("next"), tr("Go to Next"), SLOT(gotoNextSlot()), "ActionGotoNext"), [this](QMenu*)
|
|
{
|
|
return mHistory.historyHasNext();
|
|
});
|
|
|
|
mMenuBuilder->addMenu(makeMenu(DIcon("goto"), tr("&Go to")), gotoMenu);
|
|
|
|
//Freeze the stack
|
|
mMenuBuilder->addAction(mFreezeStack = makeShortcutAction(DIcon("freeze"), tr("Freeze the stack"), SLOT(freezeStackSlot()), "ActionFreezeStack"));
|
|
mFreezeStack->setCheckable(true);
|
|
|
|
//Follow in Memory Map
|
|
mCommonActions->build(mMenuBuilder, CommonActions::ActionMemoryMap | CommonActions::ActionDump | CommonActions::ActionDumpData);
|
|
|
|
//Follow in Stack
|
|
auto followStackName = ArchValue(tr("Follow DWORD in &Stack"), tr("Follow QWORD in &Stack"));
|
|
mFollowStack = makeAction(DIcon("stack"), followStackName, SLOT(followStackSlot()));
|
|
mFollowStack->setShortcutContext(Qt::WidgetShortcut);
|
|
mFollowStack->setShortcut(QKeySequence("enter"));
|
|
mMenuBuilder->addAction(mFollowStack, [this](QMenu*)
|
|
{
|
|
duint ptr;
|
|
if(!DbgMemRead(rvaToVa(getInitialSelection()), (unsigned char*)&ptr, sizeof(ptr)))
|
|
return false;
|
|
duint stackBegin = mMemPage->getBase();
|
|
duint stackEnd = stackBegin + mMemPage->getSize();
|
|
return ptr >= stackBegin && ptr < stackEnd;
|
|
});
|
|
|
|
//Follow in Disassembler
|
|
auto disasmIcon = DIcon(ArchValue("processor32", "processor64"));
|
|
mFollowDisasm = makeAction(disasmIcon, ArchValue(tr("&Follow DWORD in Disassembler"), tr("&Follow QWORD in Disassembler")), SLOT(followDisasmSlot()));
|
|
mFollowDisasm->setShortcutContext(Qt::WidgetShortcut);
|
|
mFollowDisasm->setShortcut(QKeySequence("enter"));
|
|
mMenuBuilder->addAction(mFollowDisasm, [this](QMenu*)
|
|
{
|
|
duint ptr;
|
|
return DbgMemRead(rvaToVa(getInitialSelection()), (unsigned char*)&ptr, sizeof(ptr)) && DbgMemIsValidReadPtr(ptr);
|
|
});
|
|
|
|
//Follow PTR in Dump
|
|
auto followDumpName = ArchValue(tr("Follow DWORD in &Dump"), tr("Follow QWORD in &Dump"));
|
|
|
|
mCommonActions->build(mMenuBuilder, CommonActions::ActionDumpN | CommonActions::ActionWatch);
|
|
mMenuBuilder->addAction(makeAction("Edit columns...", SLOT(editColumnDialog())));
|
|
|
|
mPluginMenu = new QMenu(this);
|
|
Bridge::getBridge()->emitMenuAddToList(this, mPluginMenu, GUI_STACK_MENU);
|
|
|
|
mMenuBuilder->addSeparator();
|
|
mMenuBuilder->addBuilder(new MenuBuilder(this, [this](QMenu * menu)
|
|
{
|
|
DbgMenuPrepare(GUI_STACK_MENU);
|
|
menu->addActions(mPluginMenu->actions());
|
|
return true;
|
|
}));
|
|
|
|
mMenuBuilder->loadFromConfig();
|
|
}
|
|
|
|
void CPUStack::updateFreezeStackAction()
|
|
{
|
|
if(bStackFrozen)
|
|
mFreezeStack->setText(tr("Unfreeze the stack"));
|
|
else
|
|
mFreezeStack->setText(tr("Freeze the stack"));
|
|
mFreezeStack->setChecked(bStackFrozen);
|
|
}
|
|
|
|
void CPUStack::getColumnRichText(int col, dsint rva, RichTextPainter::List & richText)
|
|
{
|
|
// Compute VA
|
|
duint wVa = rvaToVa(rva);
|
|
|
|
bool wActiveStack = (wVa >= mCsp); //inactive stack
|
|
|
|
STACK_COMMENT comment;
|
|
RichTextPainter::CustomRichText_t curData;
|
|
curData.underline = false;
|
|
curData.flags = RichTextPainter::FlagColor;
|
|
curData.textColor = mTextColor;
|
|
|
|
if(col && mDescriptor.at(col - 1).isData == true) //paint stack data
|
|
{
|
|
HexDump::getColumnRichText(col, rva, richText);
|
|
if(!wActiveStack)
|
|
{
|
|
QColor inactiveColor = ConfigColor("StackInactiveTextColor");
|
|
for(int i = 0; i < int(richText.size()); i++)
|
|
{
|
|
richText[i].flags = RichTextPainter::FlagColor;
|
|
richText[i].textColor = inactiveColor;
|
|
}
|
|
}
|
|
}
|
|
else if(col && DbgStackCommentGet(wVa, &comment)) //paint stack comments
|
|
{
|
|
if(wActiveStack)
|
|
{
|
|
if(*comment.color)
|
|
{
|
|
if(comment.color[0] == '!')
|
|
{
|
|
if(strcmp(comment.color, "!sehclr") == 0)
|
|
curData.textColor = mStackSEHChainColor;
|
|
else if(strcmp(comment.color, "!rtnclr") == 0)
|
|
curData.textColor = mStackReturnToColor;
|
|
else
|
|
curData.textColor = mTextColor;
|
|
}
|
|
else
|
|
curData.textColor = QColor(QString(comment.color));
|
|
}
|
|
else
|
|
curData.textColor = mTextColor;
|
|
}
|
|
else
|
|
curData.textColor = ConfigColor("StackInactiveTextColor");
|
|
curData.text = comment.comment;
|
|
richText.push_back(curData);
|
|
}
|
|
else
|
|
HexDump::getColumnRichText(col, rva, richText);
|
|
}
|
|
|
|
QString CPUStack::paintContent(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h)
|
|
{
|
|
// Compute RVA
|
|
int wBytePerRowCount = getBytePerRowCount();
|
|
dsint wRva = (rowBase + rowOffset) * wBytePerRowCount - mByteOffset;
|
|
duint wVa = rvaToVa(wRva);
|
|
|
|
bool wIsSelected = isSelected(wRva);
|
|
if(wIsSelected) //highlight if selected
|
|
painter->fillRect(QRect(x, y, w, h), QBrush(mSelectionColor));
|
|
|
|
if(col == 0) // paint stack address
|
|
{
|
|
QColor background;
|
|
char labelText[MAX_LABEL_SIZE] = "";
|
|
if(DbgGetLabelAt(wVa, SEG_DEFAULT, labelText)) //label
|
|
{
|
|
if(wVa == mCsp) //CSP
|
|
{
|
|
background = ConfigColor("StackCspBackgroundColor");
|
|
painter->setPen(QPen(ConfigColor("StackCspColor")));
|
|
}
|
|
else //no CSP
|
|
{
|
|
background = ConfigColor("StackLabelBackgroundColor");
|
|
painter->setPen(ConfigColor("StackLabelColor"));
|
|
}
|
|
}
|
|
else //no label
|
|
{
|
|
if(wVa == mCsp) //CSP
|
|
{
|
|
background = ConfigColor("StackCspBackgroundColor");
|
|
painter->setPen(QPen(ConfigColor("StackCspColor")));
|
|
}
|
|
else if(wIsSelected) //selected normal address
|
|
{
|
|
background = ConfigColor("StackSelectedAddressBackgroundColor");
|
|
painter->setPen(QPen(ConfigColor("StackSelectedAddressColor"))); //black address (DisassemblySelectedAddressColor)
|
|
}
|
|
else //normal address
|
|
{
|
|
background = ConfigColor("StackAddressBackgroundColor");
|
|
painter->setPen(QPen(ConfigColor("StackAddressColor")));
|
|
}
|
|
}
|
|
if(background.alpha())
|
|
painter->fillRect(QRect(x, y, w, h), QBrush(background)); //fill background when defined
|
|
painter->drawText(QRect(x + 4, y, w - 4, h), Qt::AlignVCenter | Qt::AlignLeft, makeAddrText(wVa));
|
|
return QString();
|
|
}
|
|
else if(col == 1) // paint stack data
|
|
{
|
|
if(mCallstack.size())
|
|
{
|
|
int stackFrameBitfield = 0; // 0:none, 1:top of stack frame, 2:bottom of stack frame, 4:middle of stack frame
|
|
int party = 0;
|
|
if(wVa >= mCallstack[0].addr)
|
|
{
|
|
for(size_t i = 0; i < mCallstack.size() - 1; i++)
|
|
{
|
|
if(wVa >= mCallstack[i].addr && wVa < mCallstack[i + 1].addr)
|
|
{
|
|
stackFrameBitfield |= (mCallstack[i].addr == wVa) ? 1 : 0;
|
|
stackFrameBitfield |= (mCallstack[i + 1].addr == wVa + sizeof(duint)) ? 2 : 0;
|
|
if(stackFrameBitfield == 0)
|
|
stackFrameBitfield = 4;
|
|
party = mCallstack[i].party;
|
|
break;
|
|
}
|
|
}
|
|
// draw stack frame
|
|
if(stackFrameBitfield == 0)
|
|
return HexDump::paintContent(painter, rowBase, rowOffset, 1, x, y, w, h);
|
|
else
|
|
{
|
|
int height = getRowHeight();
|
|
int halfHeight = height / 2;
|
|
int width = 5;
|
|
int offset = 2;
|
|
auto result = HexDump::paintContent(painter, rowBase, rowOffset, 1, x + (width - 2), y, w - (width - 2), h);
|
|
if(party == mod_user)
|
|
painter->setPen(QPen(mUserStackFrameColor, 2));
|
|
else
|
|
painter->setPen(QPen(mSystemStackFrameColor, 2));
|
|
if((stackFrameBitfield & 1) != 0)
|
|
{
|
|
painter->drawLine(x + width, y + halfHeight / 2, x + offset, y + halfHeight / 2);
|
|
painter->drawLine(x + offset, y + halfHeight / 2, x + offset, y + halfHeight);
|
|
}
|
|
else
|
|
painter->drawLine(x + offset, y, x + offset, y + halfHeight);
|
|
if((stackFrameBitfield & 2) != 0)
|
|
{
|
|
painter->drawLine(x + width, y + height / 4 * 3, x + offset, y + height / 4 * 3);
|
|
painter->drawLine(x + offset, y + height / 4 * 3, x + offset, y + halfHeight);
|
|
}
|
|
else
|
|
painter->drawLine(x + offset, y + height, x + offset, y + halfHeight);
|
|
return result;
|
|
}
|
|
}
|
|
else
|
|
return HexDump::paintContent(painter, rowBase, rowOffset, 1, x, y, w, h);
|
|
}
|
|
else
|
|
return HexDump::paintContent(painter, rowBase, rowOffset, 1, x, y, w, h);
|
|
}
|
|
else
|
|
return HexDump::paintContent(painter, rowBase, rowOffset, col, x, y, w, h);
|
|
}
|
|
|
|
void CPUStack::contextMenuEvent(QContextMenuEvent* event)
|
|
{
|
|
QMenu wMenu(this); //create context menu
|
|
mMenuBuilder->build(&wMenu);
|
|
wMenu.exec(event->globalPos());
|
|
}
|
|
|
|
void CPUStack::mouseDoubleClickEvent(QMouseEvent* event)
|
|
{
|
|
if(event->button() != Qt::LeftButton || !DbgIsDebugging())
|
|
return;
|
|
switch(getColumnIndexFromX(event->x()))
|
|
{
|
|
case 0: //address
|
|
{
|
|
//very ugly way to calculate the base of the current row (no clue why it works)
|
|
dsint deltaRowBase = getInitialSelection() % getBytePerRowCount() + mByteOffset;
|
|
if(deltaRowBase >= getBytePerRowCount())
|
|
deltaRowBase -= getBytePerRowCount();
|
|
dsint mSelectedVa = rvaToVa(getInitialSelection() - deltaRowBase);
|
|
if(mRvaDisplayEnabled && mSelectedVa == mRvaDisplayBase)
|
|
mRvaDisplayEnabled = false;
|
|
else
|
|
{
|
|
mRvaDisplayEnabled = true;
|
|
mRvaDisplayBase = mSelectedVa;
|
|
mRvaDisplayPageBase = mMemPage->getBase();
|
|
}
|
|
reloadData();
|
|
}
|
|
break;
|
|
|
|
case 1: // value
|
|
{
|
|
modifySlot();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
duint wVa = rvaToVa(getInitialSelection());
|
|
STACK_COMMENT comment;
|
|
if(DbgStackCommentGet(wVa, &comment) && strcmp(comment.color, "!rtnclr") == 0)
|
|
followDisasmSlot();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CPUStack::wheelEvent(QWheelEvent* event)
|
|
{
|
|
if(event->modifiers() == Qt::NoModifier)
|
|
AbstractTableView::wheelEvent(event);
|
|
else if(event->modifiers() == Qt::ControlModifier) // Zoom
|
|
Config()->zoomFont("Stack", event);
|
|
}
|
|
|
|
void CPUStack::stackDumpAt(duint addr, duint csp)
|
|
{
|
|
if(DbgMemIsValidReadPtr(addr))
|
|
mHistory.addVaToHistory(addr);
|
|
mCsp = csp;
|
|
|
|
// Get the callstack
|
|
DBGCALLSTACK callstack;
|
|
memset(&callstack, 0, sizeof(DBGCALLSTACK));
|
|
DbgFunctions()->GetCallStack(&callstack);
|
|
mCallstack.resize(callstack.total);
|
|
if(mCallstack.size())
|
|
{
|
|
// callstack data highest >> lowest
|
|
std::qsort(callstack.entries, callstack.total, sizeof(DBGCALLSTACKENTRY), [](const void* a, const void* b)
|
|
{
|
|
auto p = (const DBGCALLSTACKENTRY*)a;
|
|
auto q = (const DBGCALLSTACKENTRY*)b;
|
|
if(p->addr < q->addr)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
});
|
|
for(size_t i = 0; i < mCallstack.size(); i++)
|
|
{
|
|
mCallstack[i].addr = callstack.entries[i].addr;
|
|
mCallstack[i].party = DbgFunctions()->ModGetParty(callstack.entries[i].to);
|
|
}
|
|
BridgeFree(callstack.entries);
|
|
}
|
|
bool isInvisible;
|
|
isInvisible = (addr < mMemPage->va(getTableOffsetRva())) || (addr >= mMemPage->va(getTableOffsetRva() + getViewableRowsCount() * getBytePerRowCount()));
|
|
|
|
printDumpAt(addr, true, true, isInvisible || addr == csp);
|
|
}
|
|
|
|
void CPUStack::updateSlot()
|
|
{
|
|
if(!DbgIsDebugging())
|
|
return;
|
|
// Get the callstack
|
|
DBGCALLSTACK callstack;
|
|
memset(&callstack, 0, sizeof(DBGCALLSTACK));
|
|
DbgFunctions()->GetCallStack(&callstack);
|
|
mCallstack.resize(callstack.total);
|
|
if(mCallstack.size())
|
|
{
|
|
// callstack data highest >> lowest
|
|
std::qsort(callstack.entries, callstack.total, sizeof(DBGCALLSTACKENTRY), [](const void* a, const void* b)
|
|
{
|
|
auto p = (const DBGCALLSTACKENTRY*)a;
|
|
auto q = (const DBGCALLSTACKENTRY*)b;
|
|
if(p->addr < q->addr)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
});
|
|
for(size_t i = 0; i < mCallstack.size(); i++)
|
|
{
|
|
mCallstack[i].addr = callstack.entries[i].addr;
|
|
mCallstack[i].party = DbgFunctions()->ModGetParty(callstack.entries[i].to);
|
|
}
|
|
BridgeFree(callstack.entries);
|
|
}
|
|
}
|
|
|
|
void CPUStack::disasmSelectionChanged(dsint parVA)
|
|
{
|
|
// When the selected instruction is changed, select the argument that is in the stack.
|
|
DISASM_INSTR instr;
|
|
if(!DbgIsDebugging() || !DbgMemIsValidReadPtr(parVA))
|
|
return;
|
|
DbgDisasmAt(parVA, &instr);
|
|
|
|
duint underlineStart = 0;
|
|
duint underlineEnd = 0;
|
|
|
|
for(int i = 0; i < instr.argcount; i++)
|
|
{
|
|
const DISASM_ARG & arg = instr.arg[i];
|
|
if(arg.type == arg_memory)
|
|
{
|
|
if(arg.value >= mMemPage->getBase() && arg.value < mMemPage->getBase() + mMemPage->getSize())
|
|
{
|
|
if(Config()->getBool("Gui", "AutoFollowInStack"))
|
|
{
|
|
//TODO: When the stack is unaligned?
|
|
stackDumpAt(arg.value & (~(sizeof(void*) - 1)), mCsp);
|
|
}
|
|
else
|
|
{
|
|
BASIC_INSTRUCTION_INFO info;
|
|
DbgDisasmFastAt(parVA, &info);
|
|
underlineStart = arg.value;
|
|
underlineEnd = mUnderlineRangeStartVa + info.memory.size - 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(mUnderlineRangeStartVa != underlineStart || mUnderlineRangeEndVa != underlineEnd)
|
|
{
|
|
mUnderlineRangeStartVa = underlineStart;
|
|
mUnderlineRangeEndVa = underlineEnd;
|
|
reloadData();
|
|
}
|
|
}
|
|
|
|
void CPUStack::gotoCspSlot()
|
|
{
|
|
DbgCmdExec("sdump csp");
|
|
}
|
|
|
|
void CPUStack::gotoCbpSlot()
|
|
{
|
|
DbgCmdExec("sdump cbp");
|
|
}
|
|
|
|
int CPUStack::getCurrentFrame(const std::vector<CPUStack::CPUCallStack> & mCallstack, duint wVA)
|
|
{
|
|
if(mCallstack.size())
|
|
for(size_t i = 0; i < mCallstack.size() - 1; i++)
|
|
if(wVA >= mCallstack[i].addr && wVA < mCallstack[i + 1].addr)
|
|
return int(i);
|
|
return -1;
|
|
}
|
|
|
|
void CPUStack::gotoFrameBaseSlot()
|
|
{
|
|
int frame = getCurrentFrame(mCallstack, rvaToVa(getInitialSelection()));
|
|
if(frame != -1)
|
|
DbgCmdExec(QString("sdump \"%1\"").arg(ToPtrString(mCallstack[frame].addr)));
|
|
}
|
|
|
|
void CPUStack::gotoNextFrameSlot()
|
|
{
|
|
int frame = getCurrentFrame(mCallstack, rvaToVa(getInitialSelection()));
|
|
if(frame != -1 && frame + 1 < int(mCallstack.size()))
|
|
DbgCmdExec(QString("sdump \"%1\"").arg(ToPtrString(mCallstack[frame + 1].addr)));
|
|
}
|
|
|
|
void CPUStack::gotoPreviousFrameSlot()
|
|
{
|
|
int frame = getCurrentFrame(mCallstack, rvaToVa(getInitialSelection()));
|
|
if(frame > 0)
|
|
DbgCmdExec(QString("sdump \"%1\"").arg(ToPtrString(mCallstack[frame - 1].addr)));
|
|
}
|
|
|
|
void CPUStack::gotoExpressionSlot()
|
|
{
|
|
if(!DbgIsDebugging())
|
|
return;
|
|
duint size = 0;
|
|
duint base = DbgMemFindBaseAddr(mCsp, &size);
|
|
if(!mGoto)
|
|
mGoto = new GotoDialog(this);
|
|
mGoto->validRangeStart = base;
|
|
mGoto->validRangeEnd = base + size;
|
|
mGoto->setWindowTitle(tr("Enter expression to follow in Stack..."));
|
|
mGoto->setInitialExpression(ToPtrString(rvaToVa(getInitialSelection())));
|
|
if(mGoto->exec() == QDialog::Accepted)
|
|
{
|
|
duint value = DbgValFromString(mGoto->expressionText.toUtf8().constData());
|
|
DbgCmdExec(QString().sprintf("sdump %p", value));
|
|
}
|
|
}
|
|
|
|
void CPUStack::selectionGet(SELECTIONDATA* selection)
|
|
{
|
|
selection->start = rvaToVa(getSelectionStart());
|
|
selection->end = rvaToVa(getSelectionEnd());
|
|
Bridge::getBridge()->setResult(BridgeResult::SelectionGet, 1);
|
|
}
|
|
|
|
void CPUStack::selectionSet(const SELECTIONDATA* selection)
|
|
{
|
|
dsint selMin = mMemPage->getBase();
|
|
dsint selMax = selMin + mMemPage->getSize();
|
|
dsint start = selection->start;
|
|
dsint end = selection->end;
|
|
if(start < selMin || start >= selMax || end < selMin || end >= selMax) //selection out of range
|
|
{
|
|
Bridge::getBridge()->setResult(BridgeResult::SelectionSet, 0);
|
|
return;
|
|
}
|
|
setSingleSelection(start - selMin);
|
|
expandSelectionUpTo(end - selMin);
|
|
reloadData();
|
|
Bridge::getBridge()->setResult(BridgeResult::SelectionSet, 1);
|
|
}
|
|
void CPUStack::selectionUpdatedSlot()
|
|
{
|
|
duint selectedData;
|
|
if(mMemPage->read((byte_t*)&selectedData, getInitialSelection(), sizeof(duint)))
|
|
if(DbgMemIsValidReadPtr(selectedData)) //data is a pointer
|
|
{
|
|
duint stackBegin = mMemPage->getBase();
|
|
duint stackEnd = stackBegin + mMemPage->getSize();
|
|
if(selectedData >= stackBegin && selectedData < stackEnd) //data is a pointer to stack address
|
|
{
|
|
disconnect(SIGNAL(enterPressedSignal()));
|
|
connect(this, SIGNAL(enterPressedSignal()), this, SLOT(followStackSlot()));
|
|
mFollowDisasm->setShortcut(QKeySequence(""));
|
|
mFollowStack->setShortcut(QKeySequence("enter"));
|
|
}
|
|
else
|
|
{
|
|
disconnect(SIGNAL(enterPressedSignal()));
|
|
connect(this, SIGNAL(enterPressedSignal()), this, SLOT(followDisasmSlot()));
|
|
mFollowStack->setShortcut(QKeySequence(""));
|
|
mFollowDisasm->setShortcut(QKeySequence("enter"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPUStack::followDisasmSlot()
|
|
{
|
|
duint selectedData;
|
|
if(mMemPage->read((byte_t*)&selectedData, getInitialSelection(), sizeof(duint)))
|
|
if(DbgMemIsValidReadPtr(selectedData)) //data is a pointer
|
|
{
|
|
QString addrText = ToPtrString(selectedData);
|
|
DbgCmdExec(QString("disasm " + addrText));
|
|
}
|
|
}
|
|
|
|
void CPUStack::followStackSlot()
|
|
{
|
|
duint selectedData;
|
|
if(mMemPage->read((byte_t*)&selectedData, getInitialSelection(), sizeof(duint)))
|
|
if(DbgMemIsValidReadPtr(selectedData)) //data is a pointer
|
|
{
|
|
QString addrText = ToPtrString(selectedData);
|
|
DbgCmdExec(QString("sdump " + addrText));
|
|
}
|
|
}
|
|
|
|
void CPUStack::binaryEditSlot()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
dsint selStart = getSelectionStart();
|
|
dsint selSize = getSelectionEnd() - selStart + 1;
|
|
byte_t* data = new byte_t[selSize];
|
|
mMemPage->read(data, selStart, selSize);
|
|
hexEdit.mHexEdit->setData(QByteArray((const char*)data, selSize));
|
|
delete [] data;
|
|
hexEdit.setWindowTitle(tr("Edit data at %1").arg(ToPtrString(rvaToVa(selStart))));
|
|
if(hexEdit.exec() != QDialog::Accepted)
|
|
return;
|
|
dsint dataSize = hexEdit.mHexEdit->data().size();
|
|
dsint newSize = selSize > dataSize ? selSize : dataSize;
|
|
data = new byte_t[newSize];
|
|
mMemPage->read(data, selStart, newSize);
|
|
QByteArray patched = hexEdit.mHexEdit->applyMaskedData(QByteArray((const char*)data, newSize));
|
|
mMemPage->write(patched.constData(), selStart, patched.size());
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::binaryFillSlot()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
hexEdit.showKeepSize(false);
|
|
hexEdit.mHexEdit->setOverwriteMode(false);
|
|
dsint selStart = getSelectionStart();
|
|
hexEdit.setWindowTitle(tr("Fill data at %1").arg(ToPtrString(rvaToVa(selStart))));
|
|
if(hexEdit.exec() != QDialog::Accepted)
|
|
return;
|
|
QString pattern = hexEdit.mHexEdit->pattern();
|
|
dsint selSize = getSelectionEnd() - selStart + 1;
|
|
byte_t* data = new byte_t[selSize];
|
|
mMemPage->read(data, selStart, selSize);
|
|
hexEdit.mHexEdit->setData(QByteArray((const char*)data, selSize));
|
|
delete [] data;
|
|
hexEdit.mHexEdit->fill(0, QString(pattern));
|
|
QByteArray patched(hexEdit.mHexEdit->data());
|
|
mMemPage->write(patched, selStart, patched.size());
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::binaryCopySlot()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
dsint selStart = getSelectionStart();
|
|
dsint selSize = getSelectionEnd() - selStart + 1;
|
|
byte_t* data = new byte_t[selSize];
|
|
mMemPage->read(data, selStart, selSize);
|
|
hexEdit.mHexEdit->setData(QByteArray((const char*)data, selSize));
|
|
delete [] data;
|
|
Bridge::CopyToClipboard(hexEdit.mHexEdit->pattern(true));
|
|
}
|
|
|
|
void CPUStack::binaryPasteSlot()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
dsint selStart = getSelectionStart();
|
|
dsint selSize = getSelectionEnd() - selStart + 1;
|
|
QClipboard* clipboard = QApplication::clipboard();
|
|
hexEdit.mHexEdit->setData(clipboard->text());
|
|
|
|
byte_t* data = new byte_t[selSize];
|
|
mMemPage->read(data, selStart, selSize);
|
|
QByteArray patched = hexEdit.mHexEdit->applyMaskedData(QByteArray((const char*)data, selSize));
|
|
if(patched.size() < selSize)
|
|
selSize = patched.size();
|
|
mMemPage->write(patched.constData(), selStart, selSize);
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::binaryPasteIgnoreSizeSlot()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
dsint selStart = getSelectionStart();
|
|
dsint selSize = getSelectionEnd() - selStart + 1;
|
|
QClipboard* clipboard = QApplication::clipboard();
|
|
hexEdit.mHexEdit->setData(clipboard->text());
|
|
|
|
byte_t* data = new byte_t[selSize];
|
|
mMemPage->read(data, selStart, selSize);
|
|
QByteArray patched = hexEdit.mHexEdit->applyMaskedData(QByteArray((const char*)data, selSize));
|
|
delete [] data;
|
|
mMemPage->write(patched.constData(), selStart, patched.size());
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::findPattern()
|
|
{
|
|
HexEditDialog hexEdit(this);
|
|
hexEdit.showEntireBlock(true);
|
|
hexEdit.isDataCopiable(false);
|
|
hexEdit.mHexEdit->setOverwriteMode(false);
|
|
hexEdit.setWindowTitle(tr("Find Pattern..."));
|
|
if(hexEdit.exec() != QDialog::Accepted)
|
|
return;
|
|
dsint addr = rvaToVa(getSelectionStart());
|
|
if(hexEdit.entireBlock())
|
|
addr = DbgMemFindBaseAddr(addr, 0);
|
|
QString addrText = ToPtrString(addr);
|
|
DbgCmdExec(QString("findall " + addrText + ", " + hexEdit.mHexEdit->pattern() + ", &data&"));
|
|
emit displayReferencesWidget();
|
|
}
|
|
|
|
void CPUStack::undoSelectionSlot()
|
|
{
|
|
dsint start = rvaToVa(getSelectionStart());
|
|
dsint end = rvaToVa(getSelectionEnd());
|
|
if(!DbgFunctions()->PatchInRange(start, end)) //nothing patched in selected range
|
|
return;
|
|
DbgFunctions()->PatchRestoreRange(start, end);
|
|
reloadData();
|
|
}
|
|
|
|
void CPUStack::modifySlot()
|
|
{
|
|
dsint addr = getInitialSelection();
|
|
WordEditDialog wEditDialog(this);
|
|
dsint value = 0;
|
|
mMemPage->read(&value, addr, sizeof(dsint));
|
|
wEditDialog.setup(tr("Modify"), value, sizeof(dsint));
|
|
if(wEditDialog.exec() != QDialog::Accepted)
|
|
return;
|
|
value = wEditDialog.getVal();
|
|
mMemPage->write(&value, addr, sizeof(dsint));
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::realignSlot()
|
|
{
|
|
#ifdef _WIN64
|
|
mCsp &= ~0x7;
|
|
#else //x86
|
|
mCsp &= ~0x3;
|
|
#endif //_WIN64
|
|
DbgValToString("csp", mCsp);
|
|
GuiUpdateAllViews();
|
|
}
|
|
|
|
void CPUStack::freezeStackSlot()
|
|
{
|
|
if(bStackFrozen)
|
|
DbgCmdExec(QString("setfreezestack 0"));
|
|
else
|
|
DbgCmdExec(QString("setfreezestack 1"));
|
|
|
|
bStackFrozen = !bStackFrozen;
|
|
|
|
updateFreezeStackAction();
|
|
if(!bStackFrozen)
|
|
gotoCspSlot();
|
|
}
|
|
|
|
void CPUStack::dbgStateChangedSlot(DBGSTATE state)
|
|
{
|
|
if(state == initialized)
|
|
{
|
|
bStackFrozen = false;
|
|
updateFreezeStackAction();
|
|
}
|
|
}
|