1
0
Fork 0
x64dbg/src/gui/Src/BasicView/HexDump.cpp

1451 lines
40 KiB
C++

#include "HexDump.h"
#include "Configuration.h"
#include "Bridge.h"
#include "StringUtil.h"
#include <QMessageBox>
static int getStringMaxLength(HexDump::DataDescriptor desc);
static int byteStringMaxLength(HexDump::ByteViewMode mode);
static int wordStringMaxLength(HexDump::WordViewMode mode);
static int dwordStringMaxLength(HexDump::DwordViewMode mode);
static int qwordStringMaxLength(HexDump::QwordViewMode mode);
static int twordStringMaxLength(HexDump::TwordViewMode mode);
HexDump::HexDump(QWidget* parent)
: AbstractTableView(parent)
{
memset(&mSelection, 0, sizeof(SelectionData));
setDrawDebugOnly(true);
mGuiState = HexDump::NoState;
setRowCount(0);
mMemPage = new MemoryPage(0, 0);
mForceColumn = -1;
clearDescriptors();
mBackgroundColor = ConfigColor("HexDumpBackgroundColor");
mTextColor = ConfigColor("HexDumpTextColor");
mSelectionColor = ConfigColor("HexDumpSelectionColor");
mRvaDisplayEnabled = false;
mSyncAddrExpression = "";
mNonprintReplace = QChar('.'); //QChar(0x25CA);
mNullReplace = QChar('.'); //QChar(0x2022);
// Slots
connect(Bridge::getBridge(), SIGNAL(updateDump()), this, SLOT(updateDumpSlot()));
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(debugStateChanged(DBGSTATE)));
setupCopyMenu();
Initialize();
}
HexDump::~HexDump()
{
delete mMemPage;
}
void HexDump::updateColors()
{
AbstractTableView::updateColors();
mBackgroundColor = ConfigColor("HexDumpBackgroundColor");
mTextColor = ConfigColor("HexDumpTextColor");
mSelectionColor = ConfigColor("HexDumpSelectionColor");
mModifiedBytesColor = ConfigColor("HexDumpModifiedBytesColor");
mModifiedBytesBackgroundColor = ConfigColor("HexDumpModifiedBytesBackgroundColor");
mRestoredBytesColor = ConfigColor("HexDumpRestoredBytesColor");
mRestoredBytesBackgroundColor = ConfigColor("HexDumpRestoredBytesBackgroundColor");
mByte00Color = ConfigColor("HexDumpByte00Color");
mByte00BackgroundColor = ConfigColor("HexDumpByte00BackgroundColor");
mByte7FColor = ConfigColor("HexDumpByte7FColor");
mByte7FBackgroundColor = ConfigColor("HexDumpByte7FBackgroundColor");
mByteFFColor = ConfigColor("HexDumpByteFFColor");
mByteFFBackgroundColor = ConfigColor("HexDumpByteFFBackgroundColor");
mByteIsPrintColor = ConfigColor("HexDumpByteIsPrintColor");
mByteIsPrintBackgroundColor = ConfigColor("HexDumpByteIsPrintBackgroundColor");
mUserModuleCodePointerHighlightColor = ConfigColor("HexDumpUserModuleCodePointerHighlightColor");
mUserModuleDataPointerHighlightColor = ConfigColor("HexDumpUserModuleDataPointerHighlightColor");
mSystemModuleCodePointerHighlightColor = ConfigColor("HexDumpSystemModuleCodePointerHighlightColor");
mSystemModuleDataPointerHighlightColor = ConfigColor("HexDumpSystemModuleDataPointerHighlightColor");
mUnknownCodePointerHighlightColor = ConfigColor("HexDumpUnknownCodePointerHighlightColor");
mUnknownDataPointerHighlightColor = ConfigColor("HexDumpUnknownDataPointerHighlightColor");
reloadData();
}
void HexDump::updateFonts()
{
duint setting;
if(BridgeSettingGetUint("Gui", "NonprintReplaceCharacter", &setting))
mNonprintReplace = QChar(uint(setting));
if(BridgeSettingGetUint("Gui", "NullReplaceCharacter", &setting))
mNullReplace = QChar(uint(setting));
setFont(ConfigFont("HexDump"));
invalidateCachedFont();
}
void HexDump::updateShortcuts()
{
AbstractTableView::updateShortcuts();
mCopyAddress->setShortcut(ConfigShortcut("ActionCopyAddress"));
mCopyRva->setShortcut(ConfigShortcut("ActionCopyRva"));
mCopySelection->setShortcut(ConfigShortcut("ActionCopy"));
}
void HexDump::updateDumpSlot()
{
if(mSyncAddrExpression.length() && DbgFunctions()->ValFromString)
{
duint syncAddr;
if(DbgFunctions()->ValFromString(mSyncAddrExpression.toUtf8().constData(), &syncAddr)
&& DbgMemIsValidReadPtr(syncAddr))
{
printDumpAt(syncAddr, false, false, true);
}
}
reloadData();
}
void HexDump::copySelectionSlot()
{
Bridge::CopyToClipboard(makeCopyText());
}
void HexDump::printDumpAt(dsint parVA, bool select, bool repaint, bool updateTableOffset)
{
duint wSize;
auto wBase = DbgMemFindBaseAddr(parVA, &wSize); //get memory base
if(!wBase || !wSize)
return;
dsint wRVA = parVA - wBase; //calculate rva
int wBytePerRowCount = getBytePerRowCount(); //get the number of bytes per row
dsint wRowCount;
// Byte offset used to be aligned on the given RVA
mByteOffset = (int)((dsint)wRVA % (dsint)wBytePerRowCount);
mByteOffset = mByteOffset > 0 ? wBytePerRowCount - mByteOffset : 0;
// Compute row count
wRowCount = wSize / wBytePerRowCount;
wRowCount += mByteOffset > 0 ? 1 : 0;
if(mRvaDisplayEnabled && mMemPage->getBase() != mRvaDisplayPageBase)
mRvaDisplayEnabled = false;
setRowCount(wRowCount); //set the number of rows
mMemPage->setAttributes(wBase, wSize); // Set base and size (Useful when memory page changed)
if(updateTableOffset)
{
setTableOffset(-1); //make sure the requested address is always first
setTableOffset((wRVA + mByteOffset) / wBytePerRowCount); //change the displayed offset
}
if(select)
{
setSingleSelection(wRVA);
dsint wEndingAddress = wRVA + getSizeOf(mDescriptor.at(0).data.itemSize) - 1;
expandSelectionUpTo(wEndingAddress);
}
if(repaint)
reloadData();
}
void HexDump::printDumpAt(dsint parVA)
{
printDumpAt(parVA, true);
}
void HexDump::gotoPreviousSlot()
{
printDumpAt(mHistory.historyPrev());
}
void HexDump::gotoNextSlot()
{
printDumpAt(mHistory.historyNext());
}
duint HexDump::rvaToVa(dsint rva) const
{
return mMemPage->va(rva);
}
duint HexDump::getTableOffsetRva() const
{
return getTableOffset() * getBytePerRowCount() - mByteOffset;
}
QString HexDump::makeAddrText(duint va) const
{
char label[MAX_LABEL_SIZE] = "";
QString addrText = "";
dsint cur_addr = va;
if(mRvaDisplayEnabled) //RVA display
{
dsint rva = cur_addr - mRvaDisplayBase;
if(rva == 0)
{
#ifdef _WIN64
addrText = "$ ==> ";
#else
addrText = "$ ==> ";
#endif //_WIN64
}
else if(rva > 0)
{
#ifdef _WIN64
addrText = "$+" + QString("%1").arg(rva, -15, 16, QChar(' ')).toUpper();
#else
addrText = "$+" + QString("%1").arg(rva, -7, 16, QChar(' ')).toUpper();
#endif //_WIN64
}
else if(rva < 0)
{
#ifdef _WIN64
addrText = "$-" + QString("%1").arg(-rva, -15, 16, QChar(' ')).toUpper();
#else
addrText = "$-" + QString("%1").arg(-rva, -7, 16, QChar(' ')).toUpper();
#endif //_WIN64
}
}
addrText += ToPtrString(cur_addr);
if(DbgGetLabelAt(cur_addr, SEG_DEFAULT, label)) //has label
{
char module[MAX_MODULE_SIZE] = "";
if(DbgGetModuleAt(cur_addr, module) && !QString(label).startsWith("JMP.&"))
addrText += " <" + QString(module) + "." + QString(label) + ">";
else
addrText += " <" + QString(label) + ">";
}
else
*label = 0;
return std::move(addrText);
}
QString HexDump::makeCopyText()
{
dsint deltaRowBase = getSelectionStart() % getBytePerRowCount() + mByteOffset;
if(deltaRowBase >= getBytePerRowCount())
deltaRowBase -= getBytePerRowCount();
auto curRow = getSelectionStart() - deltaRowBase;
QString result;
while(curRow <= getSelectionEnd())
{
for(int col = 0; col < getColumnCount(); col++)
{
if(col)
result += " ";
RichTextPainter::List richText;
getColumnRichText(col, curRow, richText);
QString colText;
for(auto & r : richText)
colText += r.text;
if(col + 1 == getColumnCount())
result += colText;
else
result += colText.leftJustified(getColumnWidth(col) / getCharWidth(), QChar(' '), true);
}
curRow += getBytePerRowCount();
result += "\n";
}
return std::move(result);
}
void HexDump::setupCopyMenu()
{
// Copy -> Data
mCopySelection = new QAction(DIcon("copy_selection.png"), tr("&Selected lines"), this);
connect(mCopySelection, SIGNAL(triggered(bool)), this, SLOT(copySelectionSlot()));
mCopySelection->setShortcutContext(Qt::WidgetShortcut);
addAction(mCopySelection);
// Copy -> Address
mCopyAddress = new QAction(DIcon("copy_address.png"), tr("&Address"), this);
connect(mCopyAddress, SIGNAL(triggered()), this, SLOT(copyAddressSlot()));
mCopyAddress->setShortcutContext(Qt::WidgetShortcut);
addAction(mCopyAddress);
// Copy -> RVA
mCopyRva = new QAction(DIcon("copy_address.png"), "&RVA", this);
connect(mCopyRva, SIGNAL(triggered()), this, SLOT(copyRvaSlot()));
mCopyRva->setShortcutContext(Qt::WidgetShortcut);
addAction(mCopyRva);
}
void HexDump::copyAddressSlot()
{
QString addrText = ToPtrString(rvaToVa(getInitialSelection()));
Bridge::CopyToClipboard(addrText);
}
void HexDump::copyRvaSlot()
{
duint addr = rvaToVa(getInitialSelection());
duint base = DbgFunctions()->ModBaseFromAddr(addr);
if(base)
{
QString addrText = ToHexString(addr - base);
Bridge::CopyToClipboard(addrText);
}
else
QMessageBox::warning(this, tr("Error!"), tr("Selection not in a module..."));
}
void HexDump::mouseMoveEvent(QMouseEvent* event)
{
bool wAccept = true;
int x = event->x();
int y = event->y();
if(mGuiState == HexDump::MultiRowsSelectionState)
{
//qDebug() << "State = MultiRowsSelectionState";
if((transY(y) >= 0) && y <= this->height())
{
int wColIndex = getColumnIndexFromX(x);
if(mForceColumn != -1)
{
wColIndex = mForceColumn;
x = getColumnPosition(mForceColumn) + 1;
}
if(wColIndex > 0) // No selection for first column (addresses)
{
dsint wStartingAddress = getItemStartingAddress(x, y);
dsint dataSize = getSizeOf(mDescriptor.at(wColIndex - 1).data.itemSize) - 1;
dsint wEndingAddress = wStartingAddress + dataSize;
if(wEndingAddress < (dsint)mMemPage->getSize())
{
if(wStartingAddress < getInitialSelection())
{
expandSelectionUpTo(wStartingAddress);
mSelection.toIndex += dataSize;
emit selectionUpdated();
}
else
expandSelectionUpTo(wEndingAddress);
mGuiState = HexDump::MultiRowsSelectionState;
updateViewport();
}
}
else
{
dsint wStartingAddress = getItemStartingAddress(getColumnPosition(1) + 1, y);
dsint dataSize = getSizeOf(mDescriptor.at(0).data.itemSize) * mDescriptor.at(0).itemCount - 1;
dsint wEndingAddress = wStartingAddress + dataSize;
if(wEndingAddress < (dsint)mMemPage->getSize())
{
if(wStartingAddress < getInitialSelection())
{
expandSelectionUpTo(wStartingAddress);
mSelection.toIndex += dataSize;
emit selectionUpdated();
}
else
expandSelectionUpTo(wEndingAddress);
mGuiState = HexDump::MultiRowsSelectionState;
updateViewport();
}
}
wAccept = true;
}
else if(y > this->height() && mGuiState == HexDump::MultiRowsSelectionState)
{
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
}
else if(transY(y) < 0 && mGuiState == HexDump::MultiRowsSelectionState)
{
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
}
}
if(wAccept == true)
AbstractTableView::mouseMoveEvent(event);
}
void HexDump::mousePressEvent(QMouseEvent* event)
{
if(event->buttons() == Qt::MiddleButton) //copy address to clipboard
{
if(!DbgIsDebugging())
return;
MessageBeep(MB_OK);
QString addrText = ToPtrString(rvaToVa(getInitialSelection()));
Bridge::CopyToClipboard(addrText);
return;
}
//qDebug() << "HexDump::mousePressEvent";
int x = event->x();
int y = event->y();
bool wAccept = false;
if(((event->buttons() & Qt::LeftButton) != 0) && ((event->buttons() & Qt::RightButton) == 0))
{
if(getGuiState() == AbstractTableView::NoState)
{
if(y > getHeaderHeight() && y <= this->height())
{
int wColIndex = getColumnIndexFromX(x);
if(mForceColumn != -1)
{
wColIndex = mForceColumn;
x = getColumnPosition(mForceColumn) + 1;
}
if(wColIndex > 0 && mDescriptor.at(wColIndex - 1).isData == true) // No selection for first column (addresses) and no data columns
{
dsint wStartingAddress = getItemStartingAddress(x, y);
dsint dataSize = getSizeOf(mDescriptor.at(wColIndex - 1).data.itemSize) - 1;
dsint wEndingAddress = wStartingAddress + dataSize;
if(wEndingAddress < (dsint)mMemPage->getSize())
{
bool bUpdateTo = false;
if(!(event->modifiers() & Qt::ShiftModifier))
setSingleSelection(wStartingAddress);
else if(getInitialSelection() > wEndingAddress)
{
wEndingAddress -= dataSize;
bUpdateTo = true;
}
expandSelectionUpTo(wEndingAddress);
if(bUpdateTo)
{
mSelection.toIndex += dataSize;
emit selectionUpdated();
}
mGuiState = HexDump::MultiRowsSelectionState;
updateViewport();
}
}
else if(wColIndex == 0)
{
dsint wStartingAddress = getItemStartingAddress(getColumnPosition(1) + 1, y);
dsint dataSize = getSizeOf(mDescriptor.at(0).data.itemSize) * mDescriptor.at(0).itemCount - 1;
dsint wEndingAddress = wStartingAddress + dataSize;
if(wEndingAddress < (dsint)mMemPage->getSize())
{
bool bUpdateTo = false;
if(!(event->modifiers() & Qt::ShiftModifier))
setSingleSelection(wStartingAddress);
else if(getInitialSelection() > wEndingAddress)
{
wEndingAddress -= dataSize;
bUpdateTo = true;
}
expandSelectionUpTo(wEndingAddress);
if(bUpdateTo)
{
mSelection.toIndex += dataSize;
emit selectionUpdated();
}
mGuiState = HexDump::MultiRowsSelectionState;
updateViewport();
}
}
wAccept = true;
}
}
}
if(wAccept == false)
AbstractTableView::mousePressEvent(event);
}
void HexDump::mouseReleaseEvent(QMouseEvent* event)
{
bool wAccept = true;
if((event->buttons() & Qt::LeftButton) == 0)
{
if(mGuiState == HexDump::MultiRowsSelectionState)
{
mGuiState = HexDump::NoState;
updateViewport();
wAccept = false;
}
}
if((event->button() & Qt::BackButton) != 0) //Go to previous/next history
{
wAccept = true;
printDumpAt(mHistory.historyPrev());
}
else if((event->button() & Qt::ForwardButton) != 0)
{
wAccept = true;
printDumpAt(mHistory.historyNext());
}
if(wAccept == true)
AbstractTableView::mouseReleaseEvent(event);
}
void HexDump::keyPressEvent(QKeyEvent* event)
{
int key = event->key();
dsint selStart = getInitialSelection();
char granularity = 1; //Size of a data word.
char action = 0; //Where to scroll the scrollbar
Qt::KeyboardModifiers modifiers = event->modifiers();
for(int i = 0; i < mDescriptor.size(); i++) //Find the first data column
{
if(mDescriptor.at(i).isData)
{
granularity = getSizeOf(mDescriptor.at(i).data.itemSize);
break;
}
}
if(modifiers == 0) //No modifier
{
//selStart -= selStart % granularity; //Align the selection to word boundary. TODO: Unaligned data?
switch(key)
{
case Qt::Key_Left:
{
selStart -= granularity;
if(0 <= selStart)
action = -1;
}
break;
case Qt::Key_Right:
{
selStart += granularity;
if(mMemPage->getSize() > selStart)
action = 1;
}
break;
case Qt::Key_Up:
{
selStart -= getBytePerRowCount();
if(0 <= selStart)
action = -1;
}
break;
case Qt::Key_Down:
{
selStart += getBytePerRowCount();
if(mMemPage->getSize() > selStart)
action = 1;
}
break;
default:
AbstractTableView::keyPressEvent(event);
}
if(action != 0)
{
//Check if selection is out of viewport. Step the scrollbar if necessary. (TODO)
if(action == 1 && selStart >= getViewableRowsCount() * getBytePerRowCount() + getTableOffsetRva())
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
else if(action == -1 && selStart < getTableOffsetRva())
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
setSingleSelection(selStart);
if(granularity > 1)
expandSelectionUpTo(selStart + granularity - 1);
reloadData();
}
}
else if(modifiers == Qt::ControlModifier || modifiers == (Qt::ControlModifier | Qt::AltModifier))
{
duint offsetVa = rvaToVa(getTableOffsetRva());
switch(key)
{
case Qt::Key_Left:
action = (modifiers & Qt::AltModifier) ? -1 : -granularity;
break;
case Qt::Key_Right:
action = (modifiers & Qt::AltModifier) ? 1 : granularity;
break;
case Qt::Key_Up:
action = 0;
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
break;
case Qt::Key_Down:
action = 0;
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
break;
}
if(action != 0)
{
offsetVa += action;
if(mMemPage->inRange(offsetVa))
printDumpAt(offsetVa, false);
}
}
else if(modifiers == Qt::ShiftModifier)
{
//TODO
}
/*
Let's keep the old code for a while until nobody remembers previous behaviour.
if(key == Qt::Key_Left || (key == Qt::Key_Up && control)) //TODO: see if Ctrl+Up is redundant
{
duint offsetVa = rvaToVa(getTableOffsetRva()) - 1;
if(mMemPage->inRange(offsetVa))
printDumpAt(offsetVa, false);
}
else if(key == Qt::Key_Right || (key == Qt::Key_Down && control)) //TODO: see if Ctrl+Down is redundant
{
duint offsetVa = rvaToVa(getTableOffsetRva()) + 1;
if(mMemPage->inRange(offsetVa))
printDumpAt(offsetVa, false);
}
else
AbstractTableView::keyPressEvent(event);
*/
}
QString HexDump::paintContent(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h)
{
// Reset byte offset when base address is reached
if(rowBase == 0 && mByteOffset != 0)
printDumpAt(mMemPage->getBase(), false, false);
// Compute RVA
int wBytePerRowCount = getBytePerRowCount();
dsint wRva = (rowBase + rowOffset) * wBytePerRowCount - mByteOffset;
if(col && mDescriptor.at(col - 1).isData)
printSelected(painter, rowBase, rowOffset, col, x, y, w, h);
RichTextPainter::List richText;
getColumnRichText(col, wRva, richText);
RichTextPainter::paintRichText(painter, x, y, w, h, 4, richText, mFontMetrics);
return QString();
}
void HexDump::printSelected(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h)
{
if((col > 0) && ((col - 1) < mDescriptor.size()))
{
ColumnDescriptor curDescriptor = mDescriptor.at(col - 1);
int wBytePerRowCount = getBytePerRowCount();
dsint wRva = (rowBase + rowOffset) * wBytePerRowCount - mByteOffset;
int wItemPixWidth = getItemPixelWidth(curDescriptor);
int wCharWidth = getCharWidth();
if(wItemPixWidth == wCharWidth)
x += 4;
for(int i = 0; i < curDescriptor.itemCount; i++)
{
int wSelectionX = x + i * wItemPixWidth;
if(isSelected(wRva + i * getSizeOf(curDescriptor.data.itemSize)) == true)
{
int wSelectionWidth = wItemPixWidth > w - (wSelectionX - x) ? w - (wSelectionX - x) : wItemPixWidth;
wSelectionWidth = wSelectionWidth < 0 ? 0 : wSelectionWidth;
painter->setPen(mTextColor);
painter->fillRect(QRect(wSelectionX, y, wSelectionWidth, h), QBrush(mSelectionColor));
}
int separator = curDescriptor.separator;
if(i && separator && !(i % separator))
{
painter->setPen(mSeparatorColor);
painter->drawLine(wSelectionX, y, wSelectionX, y + h);
}
}
}
}
/************************************************************************************
Selection Management
************************************************************************************/
void HexDump::expandSelectionUpTo(dsint rva)
{
if(rva < mSelection.firstSelectedIndex)
{
mSelection.fromIndex = rva;
mSelection.toIndex = mSelection.firstSelectedIndex;
emit selectionUpdated();
}
else if(rva > mSelection.firstSelectedIndex)
{
mSelection.fromIndex = mSelection.firstSelectedIndex;
mSelection.toIndex = rva;
emit selectionUpdated();
}
else if(rva == mSelection.firstSelectedIndex)
{
setSingleSelection(rva);
}
}
void HexDump::setSingleSelection(dsint rva)
{
mSelection.firstSelectedIndex = rva;
mSelection.fromIndex = rva;
mSelection.toIndex = rva;
emit selectionUpdated();
}
dsint HexDump::getInitialSelection() const
{
return mSelection.firstSelectedIndex;
}
dsint HexDump::getSelectionStart() const
{
return mSelection.fromIndex;
}
dsint HexDump::getSelectionEnd() const
{
return mSelection.toIndex;
}
bool HexDump::isSelected(dsint rva) const
{
return rva >= mSelection.fromIndex && rva <= mSelection.toIndex;
}
void HexDump::getColumnRichText(int col, dsint rva, RichTextPainter::List & richText)
{
RichTextPainter::CustomRichText_t curData;
curData.highlight = false;
curData.flags = RichTextPainter::FlagAll;
curData.textColor = mTextColor;
curData.textBackground = Qt::transparent;
curData.highlightColor = Qt::transparent;
RichTextPainter::CustomRichText_t spaceData;
spaceData.highlight = false;
spaceData.flags = RichTextPainter::FlagNone;
spaceData.highlightColor = Qt::transparent;
if(!col) //address
{
curData.text = makeAddrText(rvaToVa(rva));
richText.push_back(curData);
}
else if(mDescriptor.at(col - 1).isData == true)
{
const ColumnDescriptor & desc = mDescriptor.at(col - 1);
int wI;
int wByteCount = getSizeOf(desc.data.itemSize);
int wBufferByteCount = desc.itemCount * wByteCount;
wBufferByteCount = wBufferByteCount > (dsint)(mMemPage->getSize() - rva) ? mMemPage->getSize() - rva : wBufferByteCount;
byte_t* wData = new byte_t[wBufferByteCount];
mMemPage->read(wData, rva, wBufferByteCount);
if(desc.textCodec) //convert the row bytes to unicode
{
//This might produce invalid characters in variables-width encodings. This is currently ignored.
curData.text = desc.textCodec->toUnicode(QByteArray((const char*)wData, wBufferByteCount));
curData.text.replace('\t', "\\t");
curData.text.replace('\f', "\\f");
curData.text.replace('\v', "\\v");
curData.text.replace('\n', "\\n");
curData.text.replace('\r', "\\r");
richText.push_back(curData);
}
else
{
for(wI = 0; wI < desc.itemCount && (rva + wI) < (dsint)mMemPage->getSize(); wI++)
{
curData.text.clear();
curData.textColor = mTextColor;
curData.textBackground = Qt::transparent;
curData.flags = RichTextPainter::FlagAll;
int maxLen = getStringMaxLength(desc.data);
if((rva + wI + wByteCount - 1) < (dsint)mMemPage->getSize())
{
toString(desc.data, rva + wI * wByteCount, wData + wI * wByteCount, curData);
if(curData.text.length() < maxLen)
{
spaceData.text = QString(' ').repeated(maxLen - curData.text.length());
richText.push_back(spaceData);
}
if(wI % sizeof(duint) == 0 && wByteCount == 1 && desc.data.byteMode == HexByte) //pointer underlining
{
auto ptr = *(duint*)(wData + wI * wByteCount);
if(spaceData.highlight = curData.highlight = DbgMemIsValidReadPtr(ptr))
{
auto codePage = DbgFunctions()->MemIsCodePage(ptr, false);
auto modbase = DbgFunctions()->ModBaseFromAddr(ptr);
if(modbase)
{
if(DbgFunctions()->ModGetParty(modbase) == 1) //system
spaceData.highlightColor = curData.highlightColor = codePage ? mSystemModuleCodePointerHighlightColor : mSystemModuleDataPointerHighlightColor;
else //user
spaceData.highlightColor = curData.highlightColor = codePage ? mUserModuleCodePointerHighlightColor : mUserModuleDataPointerHighlightColor;
}
else
spaceData.highlightColor = curData.highlightColor = codePage ? mUnknownCodePointerHighlightColor : mUnknownDataPointerHighlightColor;
}
}
richText.push_back(curData);
if(maxLen)
{
spaceData.text = QString(' ');
if(wI % sizeof(duint) == sizeof(duint) - 1)
spaceData.highlight = false;
richText.push_back(spaceData);
}
}
else
{
curData.text = QString("?").rightJustified(maxLen, ' ');
if(maxLen)
curData.text.append(' ');
richText.push_back(curData);
}
}
}
delete[] wData;
}
}
void HexDump::toString(DataDescriptor desc, duint rva, byte_t* data, RichTextPainter::CustomRichText_t & richText) //convert data to string
{
switch(desc.itemSize)
{
case Byte:
{
byteToString(rva, *((byte_t*)data), desc.byteMode, richText);
}
break;
case Word:
{
wordToString(rva, *((uint16*)data), desc.wordMode, richText);
}
break;
case Dword:
{
dwordToString(rva, *((uint32*)data), desc.dwordMode, richText);
}
break;
case Qword:
{
qwordToString(rva, *((uint64*)data), desc.qwordMode, richText);
}
break;
case Tword:
{
twordToString(rva, data, desc.twordMode, richText);
}
break;
default:
{
}
break;
}
if(desc.itemSize == Byte) //byte patches are handled in byteToString
return;
dsint start = rvaToVa(rva);
dsint end = start + getSizeOf(desc.itemSize) - 1;
if(DbgFunctions()->PatchInRange(start, end))
richText.textColor = ConfigColor("HexDumpModifiedBytesColor");
}
void HexDump::byteToString(duint rva, byte_t byte, ByteViewMode mode, RichTextPainter::CustomRichText_t & richText)
{
QString wStr = "";
switch(mode)
{
case HexByte:
{
wStr = ToByteString((unsigned char)byte);
}
break;
case AsciiByte:
{
QChar wChar = QChar::fromLatin1((char)byte);
if(wChar.isPrint())
wStr = QString(wChar);
else if(!wChar.unicode())
wStr = mNullReplace;
else
wStr = mNonprintReplace;
}
break;
case SignedDecByte:
{
wStr = QString::number((int)((char)byte));
}
break;
case UnsignedDecByte:
{
wStr = QString::number((unsigned int)byte);
}
break;
default:
{
}
break;
}
richText.text = wStr;
DBGPATCHINFO patchInfo;
if(DbgFunctions()->PatchGetEx(rvaToVa(rva), &patchInfo))
{
if(byte == patchInfo.newbyte)
{
richText.textColor = mModifiedBytesColor;
richText.textBackground = mModifiedBytesBackgroundColor;
}
else
{
richText.textColor = mRestoredBytesColor;
richText.textBackground = mRestoredBytesBackgroundColor;
}
}
else
{
switch(byte)
{
case 0x00:
richText.textColor = mByte00Color;
richText.textBackground = mByte00BackgroundColor;
break;
case 0x7F:
richText.textColor = mByte7FColor;
richText.textBackground = mByte7FBackgroundColor;
break;
case 0xFF:
richText.textColor = mByteFFColor;
richText.textBackground = mByteFFBackgroundColor;
break;
default:
if(isprint(byte) || isspace(byte))
{
richText.textColor = mByteIsPrintColor;
richText.textBackground = mByteIsPrintBackgroundColor;
}
break;
}
}
}
void HexDump::wordToString(duint rva, uint16 word, WordViewMode mode, RichTextPainter::CustomRichText_t & richText)
{
Q_UNUSED(rva);
QString wStr;
switch(mode)
{
case HexWord:
{
wStr = ToWordString((unsigned short)word);
}
break;
case UnicodeWord:
{
QChar wChar = QChar::fromLatin1((char)word & 0xFF);
if(wChar.isPrint() && (word >> 8) == 0)
wStr = QString(wChar);
else if(!wChar.unicode())
wStr = mNullReplace;
else
wStr = mNonprintReplace;
}
break;
case SignedDecWord:
{
wStr = QString::number((int)((short)word));
}
break;
case UnsignedDecWord:
{
wStr = QString::number((unsigned int)word);
}
break;
default:
{
}
break;
}
richText.text = wStr;
}
void HexDump::dwordToString(duint rva, uint32 dword, DwordViewMode mode, RichTextPainter::CustomRichText_t & richText)
{
Q_UNUSED(rva);
QString wStr;
switch(mode)
{
case HexDword:
{
wStr = QString("%1").arg((unsigned int)dword, 8, 16, QChar('0')).toUpper();
}
break;
case SignedDecDword:
{
wStr = QString::number((int)dword);
}
break;
case UnsignedDecDword:
{
wStr = QString::number((unsigned int)dword);
}
break;
case FloatDword:
{
wStr = ToFloatString(&dword);
}
break;
default:
{
}
break;
}
richText.text = wStr;
}
void HexDump::qwordToString(duint rva, uint64 qword, QwordViewMode mode, RichTextPainter::CustomRichText_t & richText)
{
Q_UNUSED(rva);
QString wStr;
switch(mode)
{
case HexQword:
{
wStr = QString("%1").arg((unsigned long long)qword, 16, 16, QChar('0')).toUpper();
}
break;
case SignedDecQword:
{
wStr = QString::number((long long)qword);
}
break;
case UnsignedDecQword:
{
wStr = QString::number((unsigned long long)qword);
}
break;
case DoubleQword:
{
wStr = ToDoubleString(&qword);
}
break;
default:
{
}
break;
}
richText.text = wStr;
}
void HexDump::twordToString(duint rva, void* tword, TwordViewMode mode, RichTextPainter::CustomRichText_t & richText)
{
Q_UNUSED(rva);
QString wStr;
switch(mode)
{
case FloatTword:
{
wStr = ToLongDoubleString(tword);
}
break;
default:
{
}
break;
}
richText.text = wStr;
}
int HexDump::getSizeOf(DataSize size)
{
return int(size);
}
static int getStringMaxLength(HexDump::DataDescriptor desc)
{
int wLength = 0;
switch(desc.itemSize)
{
case HexDump::Byte:
{
wLength = byteStringMaxLength(desc.byteMode);
}
break;
case HexDump::Word:
{
wLength = wordStringMaxLength(desc.wordMode);
}
break;
case HexDump::Dword:
{
wLength = dwordStringMaxLength(desc.dwordMode);
}
break;
case HexDump::Qword:
{
wLength = qwordStringMaxLength(desc.qwordMode);
}
break;
case HexDump::Tword:
{
wLength = twordStringMaxLength(desc.twordMode);
}
break;
default:
{
}
break;
}
return wLength;
}
static int byteStringMaxLength(HexDump::ByteViewMode mode)
{
int wLength = 0;
switch(mode)
{
case HexDump::HexByte:
{
wLength = 2;
}
break;
case HexDump::AsciiByte:
{
wLength = 0;
}
break;
case HexDump::SignedDecByte:
{
wLength = 4;
}
break;
case HexDump::UnsignedDecByte:
{
wLength = 3;
}
break;
default:
{
}
break;
}
return wLength;
}
static int wordStringMaxLength(HexDump::WordViewMode mode)
{
int wLength = 0;
switch(mode)
{
case HexDump::HexWord:
{
wLength = 4;
}
break;
case HexDump::UnicodeWord:
{
wLength = 0;
}
break;
case HexDump::SignedDecWord:
{
wLength = 6;
}
break;
case HexDump::UnsignedDecWord:
{
wLength = 5;
}
break;
default:
{
}
break;
}
return wLength;
}
static int dwordStringMaxLength(HexDump::DwordViewMode mode)
{
int wLength = 0;
switch(mode)
{
case HexDump::HexDword:
{
wLength = 8;
}
break;
case HexDump::SignedDecDword:
{
wLength = 11;
}
break;
case HexDump::UnsignedDecDword:
{
wLength = 10;
}
break;
case HexDump::FloatDword:
{
wLength = 13;
}
break;
default:
{
}
break;
}
return wLength;
}
static int qwordStringMaxLength(HexDump::QwordViewMode mode)
{
int wLength = 0;
switch(mode)
{
case HexDump::HexQword:
{
wLength = 16;
}
break;
case HexDump::SignedDecQword:
{
wLength = 20;
}
break;
case HexDump::UnsignedDecQword:
{
wLength = 20;
}
break;
case HexDump::DoubleQword:
{
wLength = 23;
}
break;
default:
{
}
break;
}
return wLength;
}
static int twordStringMaxLength(HexDump::TwordViewMode mode)
{
int wLength = 0;
switch(mode)
{
case HexDump::FloatTword:
{
wLength = 29;
}
break;
default:
{
}
break;
}
return wLength;
}
int HexDump::getItemIndexFromX(int x) const
{
int wColIndex = getColumnIndexFromX(x);
if(wColIndex > 0)
{
int wColStartingPos = getColumnPosition(wColIndex);
int wRelativeX = x - wColStartingPos;
int wItemPixWidth = getItemPixelWidth(mDescriptor.at(wColIndex - 1));
int wCharWidth = getCharWidth();
if(wItemPixWidth == wCharWidth)
wRelativeX -= 4;
int wItemIndex = wRelativeX / wItemPixWidth;
wItemIndex = wItemIndex < 0 ? 0 : wItemIndex;
wItemIndex = wItemIndex > (mDescriptor.at(wColIndex - 1).itemCount - 1) ? (mDescriptor.at(wColIndex - 1).itemCount - 1) : wItemIndex;
return wItemIndex;
}
else
{
return 0;
}
}
dsint HexDump::getItemStartingAddress(int x, int y)
{
int wRowOffset = getIndexOffsetFromY(transY(y));
int wItemIndex = getItemIndexFromX(x);
int wColIndex = getColumnIndexFromX(x);
dsint wStartingAddress = 0;
if(wColIndex > 0)
{
wColIndex -= 1;
wStartingAddress = (getTableOffset() + wRowOffset) * (mDescriptor.at(wColIndex).itemCount * getSizeOf(mDescriptor.at(wColIndex).data.itemSize)) + wItemIndex * getSizeOf(mDescriptor.at(wColIndex).data.itemSize) - mByteOffset;
}
return wStartingAddress;
}
int HexDump::getBytePerRowCount() const
{
return mDescriptor.at(0).itemCount * getSizeOf(mDescriptor.at(0).data.itemSize);
}
int HexDump::getItemPixelWidth(ColumnDescriptor desc) const
{
int wCharWidth = getCharWidth();
int wItemPixWidth = getStringMaxLength(desc.data) * wCharWidth + wCharWidth;
return wItemPixWidth;
}
void HexDump::appendDescriptor(int width, QString title, bool clickable, ColumnDescriptor descriptor)
{
addColumnAt(width, title, clickable);
mDescriptor.append(descriptor);
}
//Clears the descriptors, append a new descriptor and fix the tableOffset (use this instead of clearDescriptors()
void HexDump::appendResetDescriptor(int width, QString title, bool clickable, ColumnDescriptor descriptor)
{
setAllowPainting(false);
if(mDescriptor.size())
{
dsint wRVA = getTableOffset() * getBytePerRowCount() - mByteOffset;
clearDescriptors();
appendDescriptor(width, title, clickable, descriptor);
printDumpAt(rvaToVa(wRVA), true, false);
}
else
appendDescriptor(width, title, clickable, descriptor);
setAllowPainting(true);
}
void HexDump::clearDescriptors()
{
deleteAllColumns();
mDescriptor.clear();
int charwidth = getCharWidth();
addColumnAt(8 + charwidth * 2 * sizeof(duint), tr("Address"), false); //address
}
void HexDump::debugStateChanged(DBGSTATE state)
{
if(state == stopped)
{
mMemPage->setAttributes(0, 0);
setRowCount(0);
reloadData();
}
}