1
0
Fork 0
x64dbg/x64_dbg_gui/Project/Src/BasicView/Disassembly.cpp

1210 lines
37 KiB
C++

#include "Disassembly.h"
Disassembly::Disassembly(QWidget *parent) : AbstractTableView(parent)
{
mBase = 0;
mSize = 0;
mInstBuffer.clear();
historyClear();
SelectionData_t data;
memset(&data, 0, sizeof(SelectionData_t));
mSelection = data;
mCipRva = 0;
mDisasm = new QBeaEngine();
mIsLastInstDisplayed = false;
mGuiState = Disassembly::NoState;
setRowCount(mSize);
int charwidth=QFontMetrics(this->font()).width(QChar(' '));
addColumnAt(charwidth*2*sizeof(int_t)+8, "", false); //address
addColumnAt(charwidth*2*12+8, "", false); //bytes
addColumnAt(charwidth*40, "", false); //disassembly
addColumnAt(100, "", false); //comments
setShowHeader(false); //hide header
backgroundColor=QColor("#FFFBF0"); //DisassemblyBackgroundColor
connect(Bridge::getBridge(), SIGNAL(disassembleAt(int_t, int_t)), this, SLOT(disassembleAt(int_t, int_t)));
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(debugStateChangedSlot(DBGSTATE)));
connect(Bridge::getBridge(), SIGNAL(repaintGui()), this, SLOT(reloadData()));
}
/************************************************************************************
Reimplemented Functions
************************************************************************************/
/**
* @brief This method has been reimplemented. It returns the string to paint or paints it
* by its own.
*
* @param[in] painter Pointer to the painter that allows painting by its own
* @param[in] rowBase Index of the top item (Table offset)
* @param[in] rowOffset Index offset starting from rowBase
* @param[in] col Column index
* @param[in] x Rectangle x
* @param[in] y Rectangle y
* @param[in] w Rectangle width
* @param[in] h Rectangle heigth
*
* @return String to paint.
*/
QString Disassembly::paintContent(QPainter* painter, int_t rowBase, int rowOffset, int col, int x, int y, int w, int h)
{
int_t wRVA = mInstBuffer.at(rowOffset).rva;
bool wIsSelected = isSelected(&mInstBuffer, rowOffset); // isSelected(rowBase, rowOffset);
// Highlight if selected
if(wIsSelected)
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#C0C0C0"))); //DisassemblySelectionColor
switch(col)
{
case 0: // Draw address (+ label)
{
char label[MAX_LABEL_SIZE]="";
int_t cur_addr=mInstBuffer.at(rowOffset).rva+mBase;
QString addrText="";
if(mRvaDisplayEnabled) //RVA display
{
int_t 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 += QString("%1").arg(cur_addr, sizeof(int_t)*2, 16, QChar('0')).toUpper();
if(DbgGetLabelAt(cur_addr, SEG_DEFAULT, label)) //has label
{
char module[MAX_MODULE_SIZE]="";
if(DbgGetModuleAt(cur_addr, module))
addrText+=" <"+QString(module)+"."+QString(label)+">";
else
addrText+=" <"+QString(label)+">";
}
else
*label=0;
BPXTYPE bpxtype=DbgGetBpxTypeAt(cur_addr);
bool isbookmark=DbgGetBookmarkAt(cur_addr);
painter->save();
if(mInstBuffer.at(rowOffset).rva == mCipRva) //cip
{
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#000000"))); //DisassemblyCipColor
if(!isbookmark)
{
if(bpxtype&bp_normal) //breakpoint
{
painter->setPen(QPen(QColor("#FF0000"))); //DisassemblyMainBpColor
}
else
{
painter->setPen(QPen(QColor("#FFFBF0"))); //DisassemblyOtherBpColor
}
}
else
{
painter->setPen(QPen(QColor("#FEE970"))); //DisassemblyBookmarkColor
}
}
else //other address
{
if(!isbookmark)
{
if(*label) //label
{
if(bpxtype==bp_none) //label only
painter->setPen(QPen(QColor("#FF0000"))); //red -> address + label text (DisassemblyMainLabelColor)
else //label+breakpoint
{
if(bpxtype&bp_normal)
{
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#FF0000"))); //fill red (DisassemblyMainBpColor)
}
else
{
painter->setPen(QPen(QColor("#000000"))); //black address (???)
}
}
}
else //no label
{
if(bpxtype==bp_none) //no label, no breakpoint
{
if(wIsSelected)
painter->setPen(QPen(QColor("#000000"))); //black address (DisassemblySelectedAddressColor)
else
painter->setPen(QPen(QColor("#808080"))); //DisassemblyAddressColor
}
else //breakpoint only
{
if(bpxtype&bp_normal)
{
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#FF0000"))); //fill red (DisassemblyMainBpColor)
}
else
{
if(wIsSelected)
painter->setPen(QPen(QColor("#000000"))); //black address (DisassemblySelectedAddressColor)
else
painter->setPen(QPen(QColor("#808080"))); //DisassemblyAddressColor
}
}
}
}
else //bookmark
{
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#FEE970"))); //DisassemblyBookmarkColor
if(wIsSelected)
painter->setPen(QPen(QColor("#000000"))); //black address (DisassemblySelectedAddressColor)
else
painter->setPen(QPen(QColor("#808080"))); //DisassemblyAddressColor
}
}
painter->drawText(QRect(x + 4, y , w - 4 , h), Qt::AlignVCenter | Qt::AlignLeft, addrText);
painter->restore();
break;
}
case 1: //draw bytes (TODO: some spaces between bytes)
{
//draw functions
int_t cur_addr=mInstBuffer.at(rowOffset).rva+mBase;
Function_t funcType;
switch(DbgGetFunctionTypeAt(cur_addr))
{
case FUNC_SINGLE:
funcType=Function_single;
break;
case FUNC_NONE:
funcType=Function_none;
break;
case FUNC_BEGIN:
funcType=Function_start;
break;
case FUNC_MIDDLE:
funcType=Function_middle;
break;
case FUNC_END:
funcType=Function_end;
break;
}
int funcsize = paintFunctionGraphic(painter, x, y, funcType, false);
//draw jump arrows
int jumpsize = paintJumpsGraphic(painter, x + funcsize, y, wRVA); //jump line
//draw bytes
painter->save();
painter->setPen(QColor("#000000")); //DisassemblyBytesColor
QString wBytes = "";
for(int i = 0; i < mInstBuffer.at(rowOffset).dump.size(); i++)
wBytes += QString("%1").arg((unsigned char)(mInstBuffer.at(rowOffset).dump.at(i)), 2, 16, QChar('0')).toUpper();
painter->drawText(QRect(x + jumpsize + funcsize, y, getColumnWidth(col) - jumpsize - funcsize, getRowHeight()), 0, wBytes);
painter->restore();
break;
}
case 2: //draw disassembly (with colours needed)
{
int_t cur_addr=mInstBuffer.at(rowOffset).rva+mBase;
int loopsize=0;
int depth=0;
while(1) //paint all loop depths
{
LOOPTYPE loopType=DbgGetLoopTypeAt(cur_addr, depth);
if(loopType==LOOP_NONE)
break;
Function_t funcType;
switch(loopType)
{
case LOOP_NONE:
funcType=Function_none;
break;
case LOOP_BEGIN:
funcType=Function_start;
break;
case LOOP_ENTRY:
funcType=Function_loop_entry;
break;
case LOOP_MIDDLE:
funcType=Function_middle;
break;
case LOOP_END:
funcType=Function_end;
break;
}
loopsize+=paintFunctionGraphic(painter, x+loopsize, y, funcType, true);
depth++;
}
QList<CustomRichText_t> richText;
BeaHighlight::PrintRtfInstruction(&richText, &mInstBuffer.at(rowOffset).disasm);
RichTextPainter::paintRichText(painter, x + loopsize, y, getColumnWidth(col) - loopsize, getRowHeight(), 4, &richText, QFontMetrics(this->font()).width(QChar(' ')));
break;
}
case 3: //draw comments
{
char comment[MAX_COMMENT_SIZE]="";
if(DbgGetCommentAt(mInstBuffer.at(rowOffset).rva+mBase, comment))
{
painter->save();
painter->setPen(QColor("#000000")); //DisassemblyCommentColor
painter->drawText(QRect(x + 4, y , w - 4 , h), Qt::AlignVCenter | Qt::AlignLeft, QString(comment));
painter->restore();
}
}
break;
default:
break;
}
return "";
}
/************************************************************************************
Mouse Management
************************************************************************************/
/**
* @brief This method has been reimplemented. It manages the following actions:
* - Multi-rows selection
*
* @param[in] event Mouse event
*
* @return Nothing.
*/
void Disassembly::mouseMoveEvent(QMouseEvent* event)
{
//qDebug() << "Disassembly::mouseMoveEvent";
bool wAccept = true;
if(mGuiState == Disassembly::MultiRowsSelectionState)
{
//qDebug() << "State = MultiRowsSelectionState";
if((transY(event->y()) >= 0) && (transY(event->y()) <= this->getTableHeigth()))
{
int wI = getIndexOffsetFromY(transY(event->y()));
if(mSize > 0)
{
// Bound
wI = wI >= mInstBuffer.size() ? mInstBuffer.size() - 1 : wI;
wI = wI < 0 ? 0 : wI;
int_t wRowIndex = mInstBuffer.at(wI).rva;
if(wRowIndex < getRowCount())
{
expandSelectionUpTo(wRowIndex);
repaint();
wAccept = false;
}
}
}
}
if(wAccept == true)
AbstractTableView::mouseMoveEvent(event);
}
/**
* @brief This method has been reimplemented. It manages the following actions:
* - Multi-rows selection
*
* @param[in] event Mouse event
*
* @return Nothing.
*/
void Disassembly::mousePressEvent(QMouseEvent* event)
{
//qDebug() << "Disassembly::mousePressEvent";
bool wAccept = false;
if(DbgIsDebugging() && ((event->buttons() & Qt::LeftButton) != 0) && ((event->buttons() & Qt::RightButton) == 0))
{
if(getGuiState() == AbstractTableView::NoState)
{
if(event->y() > getHeaderHeight())
{
int_t wRowIndex = getInstructionRVA(getTableOffset(), getIndexOffsetFromY(transY(event->y())));
if(wRowIndex < getRowCount())
{
setSingleSelection(wRowIndex);
mGuiState = Disassembly::MultiRowsSelectionState;
repaint();
wAccept = true;
}
}
}
}
if(wAccept == false)
AbstractTableView::mousePressEvent(event);
}
/**
* @brief This method has been reimplemented. It manages the following actions:
* - Multi-rows selection
*
* @param[in] event Mouse event
*
* @return Nothing.
*/
void Disassembly::mouseReleaseEvent(QMouseEvent* event)
{
bool wAccept = true;
if((event->buttons() & Qt::LeftButton) == 0)
{
if(mGuiState == Disassembly::MultiRowsSelectionState)
{
mGuiState = Disassembly::NoState;
repaint();
wAccept = false;
}
}
if(wAccept == true)
AbstractTableView::mouseReleaseEvent(event);
}
/************************************************************************************
Keyboard Management
************************************************************************************/
/**
* @brief This method has been reimplemented. It processes the Up/Down key events.
*
* @param[in] event Key event
*
* @return Nothing.
*/
void Disassembly::keyPressEvent(QKeyEvent* event)
{
int key = event->key();
if(key == Qt::Key_Up || key == Qt::Key_Down)
{
int_t botRVA = getTableOffset();
int_t topRVA = getInstructionRVA(getTableOffset(), getNbrOfLineToPrint() - 1);
if(key == Qt::Key_Up)
selectPrevious();
else
selectNext();
if(getInitialSelection() < botRVA)
{
setTableOffset(getInitialSelection());
}
else if(getInitialSelection() >= topRVA)
{
setTableOffset(getInstructionRVA(getInitialSelection(),-getNbrOfLineToPrint() + 2));
}
repaint();
}
else if(key == Qt::Key_Return || key == Qt::Key_Enter)
{
uint_t dest = DbgGetBranchDestination(getInitialSelection() + mBase);
if(!dest)
return;
QString cmd="disasm "+QString("%1").arg(dest, sizeof(int_t)*2, 16, QChar('0')).toUpper();
DbgCmdExec(cmd.toUtf8().constData());
}
else
{
AbstractTableView::keyPressEvent(event);
}
}
/************************************************************************************
ScrollBar Management
***********************************************************************************/
/**
* @brief This method has been reimplemented. It realigns the slider on real instructions except
* when the type is QAbstractSlider::SliderNoAction. This type (QAbstractSlider::SliderNoAction)
* is used to force the disassembling at a specific RVA.
*
* @param[in] type Type of action
* @param[in] value Old table offset
* @param[in] delta Scrollbar value delta compared to the previous state
*
* @return Return the value of the new table offset.
*/
int_t Disassembly::sliderMovedHook(int type, int_t value, int_t delta)
{
int_t wNewValue;
if(type == QAbstractSlider::SliderNoAction) // QAbstractSlider::SliderNoAction is used to disassembe at a specific address
{
wNewValue = value + delta;
}
else if(type == QAbstractSlider::SliderMove) // If it's a slider action, disassemble one instruction back and one instruction next in order to be aligned on a real instruction
{
if(value + delta > 0)
{
wNewValue = getInstructionRVA(value + delta, -1);
wNewValue = getInstructionRVA(wNewValue, 1);
}
else
wNewValue = 0;
}
else // For other actions, disassemble according to the delta
{
wNewValue = getInstructionRVA(value, delta);
}
return wNewValue;
}
/************************************************************************************
Jumps Graphic
************************************************************************************/
/**
* @brief This method paints the graphic for jumps.
*
* @param[in] painter Pointer to the painter that allows painting by its own
* @param[in] x Rectangle x
* @param[in] y Rectangle y
* @param[in] addr RVA of address to process
*
* @return Nothing.
*/
int Disassembly::paintJumpsGraphic(QPainter* painter, int x, int y, int_t addr)
{
int_t selHeadRVA = mSelection.fromIndex;
int_t rva = addr;
Instruction_t instruction = DisassembleAt(selHeadRVA);
Int32 branchType = instruction.disasm.Instruction.BranchType;
GraphicDump_t wPict = GD_Nothing;
if( branchType == (Int32)JO ||
branchType == (Int32)JC ||
branchType == (Int32)JE ||
branchType == (Int32)JA ||
branchType == (Int32)JS ||
branchType == (Int32)JP ||
branchType == (Int32)JL ||
branchType == (Int32)JG ||
branchType == (Int32)JB ||
branchType == (Int32)JECXZ ||
branchType == (Int32)JmpType ||
branchType == (Int32)RetType ||
branchType == (Int32)JNO ||
branchType == (Int32)JNC ||
branchType == (Int32)JNE ||
branchType == (Int32)JNA ||
branchType == (Int32)JNS ||
branchType == (Int32)JNP ||
branchType == (Int32)JNL ||
branchType == (Int32)JNG ||
branchType == (Int32)JNB)
{
int_t destRVA = (int_t)instruction.disasm.Instruction.AddrValue;
if(destRVA > (int_t)mBase)
{
destRVA -= (int_t)mBase;
if(destRVA < selHeadRVA)
{
if(rva == destRVA)
wPict = GD_HeadFromBottom;
else if(rva > destRVA && rva < selHeadRVA)
wPict = GD_Vert;
else if(rva == selHeadRVA)
wPict = GD_FootToTop;
}
else if(destRVA > selHeadRVA)
{
if(rva == selHeadRVA)
wPict = GD_FootToBottom;
else if(rva > selHeadRVA && rva < destRVA)
wPict = GD_Vert;
else if(rva == destRVA)
wPict = GD_HeadFromTop;
}
}
}
painter->save() ;
if(DbgIsJumpGoingToExecute(instruction.rva+mBase)) //change pen color when jump is executed
painter->setPen(QColor("#FF0000")); //DisassemblyJumpLineTrueColor
else
painter->setPen(QColor("#808080")); //DisassemblyJumpLineFalseColor
if(wPict == GD_Vert)
{
painter->drawLine(x, y, x, y + getRowHeight());
}
else if(wPict == GD_FootToBottom)
{
painter->drawLine(x, y + getRowHeight() / 2, x + 5, y + getRowHeight() / 2);
painter->drawLine(x, y + getRowHeight() / 2, x, y + getRowHeight());
}
if(wPict == GD_FootToTop)
{
painter->drawLine(x, y + getRowHeight() / 2, x + 5, y + getRowHeight() / 2);
painter->drawLine(x, y, x, y + getRowHeight() / 2);
}
else if(wPict == GD_HeadFromBottom)
{
QPoint wPoints[] =
{
QPoint(x + 3, y + getRowHeight() / 2 - 2),
QPoint(x + 5, y + getRowHeight() / 2),
QPoint(x + 3, y + getRowHeight() / 2 + 2),
};
painter->drawLine(x, y + getRowHeight() / 2, x + 5, y + getRowHeight() / 2);
painter->drawLine(x, y + getRowHeight() / 2, x, y + getRowHeight());
painter->drawPolyline(wPoints, 3);
}
if(wPict == GD_HeadFromTop)
{
QPoint wPoints[] =
{
QPoint(x + 3, y + getRowHeight() / 2 - 2),
QPoint(x + 5, y + getRowHeight() / 2),
QPoint(x + 3, y + getRowHeight() / 2 + 2),
};
painter->drawLine(x, y + getRowHeight() / 2, x + 5, y + getRowHeight() / 2);
painter->drawLine(x, y, x, y + getRowHeight() / 2);
painter->drawPolyline(wPoints, 3);
}
painter->restore();
return 7;
}
/************************************************************************************
Function Graphic
************************************************************************************/
/**
* @brief This method paints the graphic for functions/loops.
*
* @param[in] painter Pointer to the painter that allows painting by its own
* @param[in] x Rectangle x
* @param[in] y Rectangle y
* @param[in] funcType Type of drawing to make
*
* @return Width of the painted data.
*/
int Disassembly::paintFunctionGraphic(QPainter* painter, int x, int y, Function_t funcType, bool loop)
{
if(loop && funcType==Function_none)
return 0;
painter->save();
painter->setPen(QPen(Qt::black, 2)); //thick black line
int height=getRowHeight();
int x_add=5;
int y_add=4;
int end_add=2;
int line_width=3;
if(loop)
{
end_add=-1;
x_add=4;
}
switch(funcType)
{
case Function_single:
{
if(loop)
y_add=height/2+1;
painter->drawLine(x+x_add+line_width, y+y_add, x+x_add, y+y_add);
painter->drawLine(x+x_add, y+y_add, x+x_add, y+height-y_add-1);
if(loop)
y_add=height/2-1;
painter->drawLine(x+x_add, y+height-y_add, x+x_add+line_width, y+height-y_add);
}
break;
case Function_start:
{
if(loop)
y_add=height/2+1;
painter->drawLine(x+x_add+line_width, y+y_add, x+x_add, y+y_add);
painter->drawLine(x+x_add, y+y_add, x+x_add, y+height);
}
break;
case Function_middle:
{
painter->drawLine(x+x_add, y, x+x_add, y+height);
}
break;
case Function_loop_entry:
{
int trisize=2;
int y_start=(height-trisize*2)/2+y;
painter->drawLine(x+x_add, y_start, x+trisize+x_add, y_start+trisize);
painter->drawLine(x+trisize+x_add, y_start+trisize, x+x_add, y_start+trisize*2);
painter->drawLine(x+x_add, y, x+x_add, y_start-1);
painter->drawLine(x+x_add, y_start+trisize*2+2, x+x_add, y+height);
}
break;
case Function_end:
{
if(loop)
y_add=height/2-1;
painter->drawLine(x+x_add, y, x+x_add, y+height-y_add);
painter->drawLine(x+x_add, y+height-y_add, x+x_add+line_width, y+height-y_add);
}
break;
case Function_none:
{
}
break;
}
painter->restore();
return x_add+line_width+end_add;
}
/************************************************************************************
Instructions Management
***********************************************************************************/
/**
* @brief Returns the RVA of count-th instructions before the given instruction RVA.
*
* @param[in] rva Instruction RVA
* @param[in] count Instruction count
*
* @return RVA of count-th instructions before the given instruction RVA.
*/
int_t Disassembly::getPreviousInstructionRVA(int_t rva, uint_t count)
{
QByteArray wBuffer;
int_t wBottomByteRealRVA;
int_t wVirtualRVA;
int_t wMaxByteCountToRead ;
wBottomByteRealRVA = (int_t)rva - 16 * (count + 3);
wBottomByteRealRVA = wBottomByteRealRVA < 0 ? 0 : wBottomByteRealRVA;
wVirtualRVA = (int_t)rva - wBottomByteRealRVA;
wMaxByteCountToRead = wVirtualRVA + 1 + 16;
wBuffer.resize(wMaxByteCountToRead);
DbgMemRead(mBase + wBottomByteRealRVA, reinterpret_cast<byte_t*>(wBuffer.data()), wMaxByteCountToRead);
int_t addr = mDisasm->DisassembleBack(reinterpret_cast<byte_t*>(wBuffer.data()), 0, wMaxByteCountToRead, wVirtualRVA, count);
addr += rva - wVirtualRVA;
return addr;
}
/**
* @brief Returns the RVA of count-th instructions after the given instruction RVA.
*
* @param[in] rva Instruction RVA
* @param[in] count Instruction count
*
* @return RVA of count-th instructions after the given instruction RVA.
*/
int_t Disassembly::getNextInstructionRVA(int_t rva, uint_t count)
{
QByteArray wBuffer;
int_t wVirtualRVA = 0;
int_t wRemainingBytes;
int_t wMaxByteCountToRead;
int_t wNewRVA;
wRemainingBytes = mSize - rva;
wMaxByteCountToRead = 16 * (count + 1);
wMaxByteCountToRead = wRemainingBytes > wMaxByteCountToRead ? wMaxByteCountToRead : wRemainingBytes;
wBuffer.resize(wMaxByteCountToRead);
DbgMemRead(mBase + rva, reinterpret_cast<byte_t*>(wBuffer.data()), wMaxByteCountToRead);
wNewRVA = mDisasm->DisassembleNext(reinterpret_cast<byte_t*>(wBuffer.data()), 0, wMaxByteCountToRead, wVirtualRVA, count);
wNewRVA += rva;
return wNewRVA;
}
/**
* @brief Returns the RVA of count-th instructions before/after (depending on the sign) the given instruction RVA.
*
* @param[in] rva Instruction RVA
* @param[in] count Instruction count
*
* @return RVA of count-th instructions before/after the given instruction RVA.
*/
int_t Disassembly::getInstructionRVA(int_t index, int_t count)
{
int_t wAddr = 0;
if(count == 0)
wAddr = index;
if(count < 0)
wAddr = getPreviousInstructionRVA(index, qAbs(count));
else if(count > 0)
wAddr = getNextInstructionRVA(index, qAbs(count));
if(wAddr < 0)
wAddr = 0;
else if(wAddr > getRowCount() - 1)
wAddr = getRowCount() - 1;
return wAddr;
}
/**
* @brief Disassembles the instruction at the given RVA.
*
* @param[in] rva RVA of instruction to disassemble
*
* @return Return the disassembled instruction.
*/
Instruction_t Disassembly::DisassembleAt(int_t rva)
{
QByteArray wBuffer;
int_t base = mBase;
int_t wMaxByteCountToRead = 16 * 2;
// Bounding
//TODO: fix problems with negative sizes
int_t size = getSize();
if(!size)
size=rva;
wMaxByteCountToRead = wMaxByteCountToRead > (size - rva) ? (size - rva) : wMaxByteCountToRead;
wBuffer.resize(wMaxByteCountToRead);
DbgMemRead(mBase+rva, reinterpret_cast<byte_t*>(wBuffer.data()), wMaxByteCountToRead);
return mDisasm->DisassembleAt(reinterpret_cast<byte_t*>(wBuffer.data()), wMaxByteCountToRead, 0, base, rva);
}
/**
* @brief Disassembles the instruction count instruction afterc the instruction at the given RVA.
* Count can be positive or negative.
*
* @param[in] rva RVA of reference instruction
* @param[in] count Number of instruction
*
* @return Return the disassembled instruction.
*/
Instruction_t Disassembly::DisassembleAt(int_t rva, int_t count)
{
rva = getNextInstructionRVA(rva, count);
return DisassembleAt(rva);
}
/************************************************************************************
Selection Management
************************************************************************************/
void Disassembly::expandSelectionUpTo(int_t to)
{
if(to < mSelection.firstSelectedIndex)
{
mSelection.fromIndex = to;
mSelection.toIndex = mSelection.firstSelectedIndex;
}
else if(to > mSelection.firstSelectedIndex)
{
mSelection.fromIndex = mSelection.firstSelectedIndex;
mSelection.toIndex = to;
}
}
void Disassembly::setSingleSelection(int_t index)
{
mSelection.firstSelectedIndex = index;
mSelection.fromIndex = index;
mSelection.toIndex = index;
emit selectionChanged(rvaToVa(index));
}
int_t Disassembly::getInitialSelection()
{
return mSelection.firstSelectedIndex;
}
int_t Disassembly::getSelectionSize()
{
return mSelection.toIndex - mSelection.fromIndex;
}
int_t Disassembly::getSelectionStart()
{
return mSelection.fromIndex;
}
int_t Disassembly::getSelectionEnd()
{
return mSelection.toIndex;
}
void Disassembly::selectNext()
{
int_t wAddr = getInstructionRVA(getInitialSelection(), 1);
setSingleSelection(wAddr);
}
void Disassembly::selectPrevious()
{
int_t wAddr = getInstructionRVA(getInitialSelection(), -1);
setSingleSelection(wAddr);
}
bool Disassembly::isSelected(int_t base, int_t offset)
{
int_t wAddr = base;
if(offset < 0)
wAddr = getPreviousInstructionRVA(getTableOffset(), offset);
else if(offset > 0)
wAddr = getNextInstructionRVA(getTableOffset(), offset);
if(wAddr >= mSelection.fromIndex && wAddr <= mSelection.toIndex)
return true;
else
return false;
}
bool Disassembly::isSelected(QList<Instruction_t>* buffer, int index)
{
if(buffer->size() > 0 && index >= 0 && index < buffer->size())
{
if(buffer->at(index).rva >= mSelection.fromIndex && buffer->at(index).rva <= mSelection.toIndex)
return true;
else
return false;
}
else
{
return false;
}
}
/************************************************************************************
Update/Reload/Refresh/Repaint
************************************************************************************/
void Disassembly::prepareData()
{
int_t wViewableRowsCount = getViewableRowsCount();
int_t wAddrPrev = getTableOffset();
int_t wAddr = wAddrPrev;
int wCount = 0;
for(int wI = 0; wI < wViewableRowsCount && getRowCount() > 0; wI++)
{
wAddrPrev = wAddr;
wAddr = getNextInstructionRVA(wAddr, 1);
if(wAddr == wAddrPrev)
{
break;
}
wCount++;
}
setNbrOfLineToPrint(wCount);
int wI = 0;
int_t wRVA = 0;
Instruction_t wInst;
wRVA = getTableOffset();
mInstBuffer.clear();
for(wI = 0; wI < wCount; wI++)
{
wInst = DisassembleAt(wRVA);
mInstBuffer.append(wInst);
wRVA += wInst.lentgh;
}
}
void Disassembly::reloadData()
{
emit selectionChanged(rvaToVa(mSelection.firstSelectedIndex));
AbstractTableView::reloadData();
}
/************************************************************************************
Public Methods
************************************************************************************/
uint_t Disassembly::rvaToVa(int_t rva)
{
return mBase + rva;
}
void Disassembly::disassembleAt(int_t parVA, int_t parCIP, bool history, int_t newTableOffset)
{
int_t wBase = DbgMemFindBaseAddr(parVA, 0);
int_t wSize = DbgMemGetPageSize(wBase);
if(!wBase || !wSize)
return;
int_t wRVA = parVA - wBase;
int_t wCipRva = parCIP - wBase;
HistoryData_t newHistory;
//VA history
if(history)
{
//truncate everything right from the current VA
if(mVaHistory.size() && mCurrentVa<mVaHistory.size()-1) //mCurrentVa is not the last
mVaHistory.erase(mVaHistory.begin()+mCurrentVa+1, mVaHistory.end());
//NOTE: mCurrentVa always points to the last entry of the list
//add the currently selected address to the history
int_t selectionVA=rvaToVa(getInitialSelection()); //currently selected VA
int_t selectionTableOffset=getTableOffset();
if(selectionVA && mVaHistory.size() && mVaHistory.last().va!=selectionVA) //do not have 2x the same va in a row
{
if(mVaHistory.size() >= 1024) //max 1024 in the history
{
mCurrentVa--;
mVaHistory.erase(mVaHistory.begin()); //remove the oldest element
}
mCurrentVa++;
newHistory.va=selectionVA;
newHistory.tableOffset=selectionTableOffset;
mVaHistory.push_back(newHistory);
}
}
// Set base and size (Useful when memory page changed)
mBase = wBase;
mSize = wSize;
if(mRvaDisplayEnabled && mBase != mRvaDisplayPageBase)
mRvaDisplayEnabled = false;
setRowCount(wSize);
setSingleSelection(wRVA); // Selects disassembled instruction
//set CIP rva
mCipRva = wCipRva;
if(newTableOffset==-1) //nothing specified
{
// Update table offset depending on the location of the instruction to disassemble
if(mInstBuffer.size() > 0 && wRVA >= (int_t)mInstBuffer.first().rva && wRVA < (int_t)mInstBuffer.last().rva)
{
int wI;
bool wIsAligned = false;
// Check if the new RVA is aligned on an instruction from the cache (buffer)
for(wI = 0; wI < mInstBuffer.size(); wI++)
{
if(mInstBuffer.at(wI).rva == wRVA)
{
wIsAligned = true;
break;
}
}
if(wIsAligned == true)
{
repaint();
}
else
{
setTableOffset(wRVA);
}
}
else if(mInstBuffer.size() > 0 && wRVA == (int_t)mInstBuffer.last().rva)
{
setTableOffset(mInstBuffer.first().rva + mInstBuffer.first().lentgh);
}
else
{
setTableOffset(wRVA);
}
if(history)
{
//new disassembled address
newHistory.va=parVA;
newHistory.tableOffset=getTableOffset();
if(mVaHistory.size())
{
if(mVaHistory.last().va!=parVA) //not 2x the same va in history
{
if(mVaHistory.size() >= 1024) //max 1024 in the history
{
mCurrentVa--;
mVaHistory.erase(mVaHistory.begin()); //remove the oldest element
}
mCurrentVa++;
mVaHistory.push_back(newHistory); //add a va to the history
}
}
else //the list is empty
mVaHistory.push_back(newHistory);
}
}
else //specified new table offset
setTableOffset(newTableOffset);
/*
//print history
if(history)
{
QString strList = "";
for(int i=0; i<mVaHistory.size(); i++)
strList += QString().sprintf("[%d]:%p,%p\n", i, mVaHistory.at(i).va, mVaHistory.at(i).tableOffset);
MessageBoxA(GuiGetWindowHandle(), strList.toUtf8().constData(), QString().sprintf("mCurrentVa=%d", mCurrentVa).toUtf8().constData(), MB_ICONINFORMATION);
}
*/
reloadData();
}
void Disassembly::disassembleAt(int_t parVA, int_t parCIP)
{
disassembleAt(parVA, parCIP, true, -1);
}
void Disassembly::disassembleClear()
{
historyClear();
mBase = 0;
mSize = 0;
setRowCount(0);
reloadData();
}
void Disassembly::debugStateChangedSlot(DBGSTATE state)
{
if(state==stopped)
{
disassembleClear();
}
}
int_t Disassembly::getBase()
{
return mBase;
}
int_t Disassembly::getSize()
{
return mSize;
}
void Disassembly::historyClear()
{
mVaHistory.clear(); //clear history for new targets
mCurrentVa = 0;
}
void Disassembly::historyPrevious()
{
if(!mCurrentVa || !mVaHistory.size()) //we are at the earliest history entry
return;
mCurrentVa--;
disassembleAt(mVaHistory.at(mCurrentVa).va, rvaToVa(mCipRva), false, mVaHistory.at(mCurrentVa).tableOffset);
}
void Disassembly::historyNext()
{
int size = mVaHistory.size();
if(!size || mCurrentVa >= mVaHistory.size()-1) //we are at the newest history entry
return;
mCurrentVa++;
disassembleAt(mVaHistory.at(mCurrentVa).va, rvaToVa(mCipRva), false, mVaHistory.at(mCurrentVa).tableOffset);
}
bool Disassembly::historyHasPrevious()
{
if(!mCurrentVa || !mVaHistory.size()) //we are at the earliest history entry
return false;
return true;
}
bool Disassembly::historyHasNext()
{
int size = mVaHistory.size();
if(!size || mCurrentVa >= mVaHistory.size()-1) //we are at the newest history entry
return false;
return true;
}