underline relocated bytes in disassembly view (#1683)
* DBG: add relocation info to module * GUI: underline relocated bytes * DBG: remove unnecessary wrapper function * DBG: store relocations in sorted vector instead of set * GUI: warn about patches in relocation regions (closes #263)
This commit is contained in:
parent
84bbc5cf4a
commit
6587cbc564
|
@ -349,6 +349,26 @@ static duint _membpsize(duint addr)
|
|||
return info ? info->memsize : 0;
|
||||
}
|
||||
|
||||
static bool _modrelocationsfromaddr(duint addr, ListOf(DBGRELOCATIONINFO) relocations)
|
||||
{
|
||||
std::vector<MODRELOCATIONINFO> infos;
|
||||
if(!ModRelocationsFromAddr(addr, &infos))
|
||||
return false;
|
||||
|
||||
BridgeList<MODRELOCATIONINFO>::CopyData(relocations, infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _modrelocationsinrange(duint addr, duint size, ListOf(DBGRELOCATIONINFO) relocations)
|
||||
{
|
||||
std::vector<MODRELOCATIONINFO> infos;
|
||||
if(!ModRelocationsInRange(addr, size, &infos))
|
||||
return false;
|
||||
|
||||
BridgeList<MODRELOCATIONINFO>::CopyData(relocations, infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dbgfunctionsinit()
|
||||
{
|
||||
_dbgfunctions.AssembleAtEx = _assembleatex;
|
||||
|
@ -416,4 +436,7 @@ void dbgfunctionsinit()
|
|||
_dbgfunctions.EnumErrorCodes = _enumerrorcodes;
|
||||
_dbgfunctions.EnumExceptions = _enumexceptions;
|
||||
_dbgfunctions.MemBpSize = _membpsize;
|
||||
_dbgfunctions.ModRelocationsFromAddr = _modrelocationsfromaddr;
|
||||
_dbgfunctions.ModRelocationAtAddr = (MODRELOCATIONATADDR)ModRelocationAtAddr;
|
||||
_dbgfunctions.ModRelocationsInRange = _modrelocationsinrange;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,13 @@ typedef struct
|
|||
char szExeArgs[MAX_COMMAND_LINE_SIZE];
|
||||
} DBGPROCESSINFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD rva;
|
||||
BYTE type;
|
||||
WORD size;
|
||||
} DBGRELOCATIONINFO;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
InstructionBody = 0,
|
||||
|
@ -185,6 +192,9 @@ typedef void(*GETCALLSTACKEX)(DBGCALLSTACK* callstack, bool cache);
|
|||
typedef bool(*GETUSERCOMMENT)(duint addr, char* comment);
|
||||
typedef void(*ENUMCONSTANTS)(ListOf(CONSTANTINFO) constants);
|
||||
typedef duint(*MEMBPSIZE)(duint addr);
|
||||
typedef bool(*MODRELOCATIONSFROMADDR)(duint addr, ListOf(DBGRELOCATIONINFO) relocations);
|
||||
typedef bool(*MODRELOCATIONATADDR)(duint addr, DBGRELOCATIONINFO* relocation);
|
||||
typedef bool(*MODRELOCATIONSINRANGE)(duint addr, duint size, ListOf(DBGRELOCATIONINFO) relocations);
|
||||
|
||||
//The list of all the DbgFunctions() return value.
|
||||
//WARNING: This list is append only. Do not insert things in the middle or plugins would break.
|
||||
|
@ -255,6 +265,9 @@ typedef struct DBGFUNCTIONS_
|
|||
ENUMCONSTANTS EnumErrorCodes;
|
||||
ENUMCONSTANTS EnumExceptions;
|
||||
MEMBPSIZE MemBpSize;
|
||||
MODRELOCATIONSFROMADDR ModRelocationsFromAddr;
|
||||
MODRELOCATIONATADDR ModRelocationAtAddr;
|
||||
MODRELOCATIONSINRANGE ModRelocationsInRange;
|
||||
} DBGFUNCTIONS;
|
||||
|
||||
#ifdef BUILD_DBG
|
||||
|
|
|
@ -10,6 +10,74 @@
|
|||
std::map<Range, MODINFO, RangeCompare> modinfo;
|
||||
std::unordered_map<duint, std::string> hashNameMap;
|
||||
|
||||
bool MODRELOCATIONINFO::Contains(duint Address) const
|
||||
{
|
||||
return Address >= rva && Address < rva + size;
|
||||
}
|
||||
|
||||
void ReadBaseRelocationTable(MODINFO & Info, ULONG_PTR FileMapVA)
|
||||
{
|
||||
// Clear relocations
|
||||
Info.relocations.clear();
|
||||
|
||||
// Parse base relocation table
|
||||
duint characteristics = GetPE32DataFromMappedFile(FileMapVA, 0, UE_CHARACTERISTICS);
|
||||
if((characteristics & IMAGE_FILE_RELOCS_STRIPPED) == IMAGE_FILE_RELOCS_STRIPPED)
|
||||
return;
|
||||
|
||||
// Get address and size of base relocation table
|
||||
duint relocDirRva = GetPE32DataFromMappedFile(FileMapVA, 0, UE_RELOCATIONTABLEADDRESS);
|
||||
duint relocDirSize = GetPE32DataFromMappedFile(FileMapVA, 0, UE_RELOCATIONTABLESIZE);
|
||||
if(relocDirRva == 0 || relocDirSize == 0)
|
||||
return;
|
||||
|
||||
duint curPos = Info.base + relocDirRva;
|
||||
// Until we reach the end of base relocation table
|
||||
while(curPos < Info.base + relocDirRva + relocDirSize)
|
||||
{
|
||||
// Read base relocation block header
|
||||
IMAGE_BASE_RELOCATION baseRelocBlock;
|
||||
MemRead(curPos, &baseRelocBlock, sizeof(baseRelocBlock));
|
||||
|
||||
// For every entry in base relocation block
|
||||
duint count = (baseRelocBlock.SizeOfBlock - 8) / 2;
|
||||
for(duint i = 0; i < count; i++)
|
||||
{
|
||||
uint16 data = 0;
|
||||
MemRead(curPos + 8 + 2 * i, &data, sizeof(uint16));
|
||||
|
||||
auto type = (data & 0xF000) >> 12;
|
||||
auto offset = data & 0x0FFF;
|
||||
|
||||
auto relocAddr = Info.base + baseRelocBlock.VirtualAddress + offset;
|
||||
switch(type)
|
||||
{
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
Info.relocations.push_back(MODRELOCATIONINFO{ baseRelocBlock.VirtualAddress + offset, type, 4 });
|
||||
break;
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
Info.relocations.push_back(MODRELOCATIONINFO{ baseRelocBlock.VirtualAddress + offset, type, 8 });
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
case IMAGE_REL_BASED_HIGHADJ:
|
||||
Info.relocations.push_back(MODRELOCATIONINFO{ baseRelocBlock.VirtualAddress + offset, type, 2 });
|
||||
break;
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
curPos += baseRelocBlock.SizeOfBlock;
|
||||
}
|
||||
|
||||
std::sort(Info.relocations.begin(), Info.relocations.end(), [](MODRELOCATIONINFO const & a, MODRELOCATIONINFO const & b)
|
||||
{
|
||||
return a.rva < b.rva;
|
||||
});
|
||||
}
|
||||
|
||||
void GetModuleInfo(MODINFO & Info, ULONG_PTR FileMapVA)
|
||||
{
|
||||
// Get the entry point
|
||||
|
@ -50,6 +118,8 @@ void GetModuleInfo(MODINFO & Info, ULONG_PTR FileMapVA)
|
|||
|
||||
// Clear imports by default
|
||||
Info.imports.clear();
|
||||
|
||||
ReadBaseRelocationTable(Info, FileMapVA);
|
||||
}
|
||||
|
||||
bool ModLoad(duint Base, duint Size, const char* FullPath)
|
||||
|
@ -486,4 +556,78 @@ void ModSetParty(duint Address, int Party)
|
|||
return;
|
||||
|
||||
module->party = Party;
|
||||
}
|
||||
}
|
||||
|
||||
bool ModRelocationsFromAddr(duint Address, std::vector<MODRELOCATIONINFO>* Relocations)
|
||||
{
|
||||
SHARED_ACQUIRE(LockModules);
|
||||
|
||||
auto module = ModInfoFromAddr(Address);
|
||||
|
||||
if(!module || module->relocations.empty())
|
||||
return false;
|
||||
|
||||
// Copy vector <-> set
|
||||
Relocations->resize(module->relocations.size());
|
||||
*Relocations = module->relocations;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModRelocationAtAddr(duint Address, MODRELOCATIONINFO* Relocation)
|
||||
{
|
||||
SHARED_ACQUIRE(LockModules);
|
||||
|
||||
auto module = ModInfoFromAddr(Address);
|
||||
|
||||
if(!module || module->relocations.empty())
|
||||
return false;
|
||||
|
||||
DWORD rva = (DWORD)(Address - module->base);
|
||||
|
||||
// We assume there are no overlapping relocations
|
||||
auto ub = std::upper_bound(module->relocations.cbegin(), module->relocations.cend(), rva,
|
||||
[](DWORD a, MODRELOCATIONINFO const & b)
|
||||
{
|
||||
return a < b.rva;
|
||||
});
|
||||
if(ub != module->relocations.begin() && (--ub)->Contains(rva))
|
||||
{
|
||||
if(Relocation)
|
||||
*Relocation = *ub;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModRelocationsInRange(duint Address, duint Size, std::vector<MODRELOCATIONINFO>* Relocations)
|
||||
{
|
||||
SHARED_ACQUIRE(LockModules);
|
||||
|
||||
auto module = ModInfoFromAddr(Address);
|
||||
|
||||
if(!module || module->relocations.empty())
|
||||
return false;
|
||||
|
||||
DWORD rva = (DWORD)(Address - module->base);
|
||||
|
||||
// We assume there are no overlapping relocations
|
||||
auto ub = std::upper_bound(module->relocations.cbegin(), module->relocations.cend(), rva,
|
||||
[](DWORD a, MODRELOCATIONINFO const & b)
|
||||
{
|
||||
return a < b.rva;
|
||||
});
|
||||
if(ub != module->relocations.begin())
|
||||
ub--;
|
||||
|
||||
Relocations->clear();
|
||||
while(ub != module->relocations.end() && ub->rva < rva + Size)
|
||||
{
|
||||
if(ub->rva >= rva)
|
||||
Relocations->push_back(*ub);
|
||||
ub++;
|
||||
}
|
||||
|
||||
return !Relocations->empty();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,15 @@ struct MODIMPORTINFO
|
|||
char moduleName[MAX_MODULE_SIZE];
|
||||
};
|
||||
|
||||
struct MODRELOCATIONINFO
|
||||
{
|
||||
DWORD rva; // Virtual address
|
||||
BYTE type; // Relocation type (IMAGE_REL_BASED_*)
|
||||
WORD size;
|
||||
|
||||
bool Contains(duint Address) const;
|
||||
};
|
||||
|
||||
struct MODINFO
|
||||
{
|
||||
duint base = 0; // Module base
|
||||
|
@ -30,6 +39,7 @@ struct MODINFO
|
|||
|
||||
std::vector<MODSECTIONINFO> sections;
|
||||
std::vector<MODIMPORTINFO> imports;
|
||||
std::vector<MODRELOCATIONINFO> relocations;
|
||||
|
||||
HANDLE fileHandle = nullptr;
|
||||
DWORD loadedSize = 0;
|
||||
|
@ -67,5 +77,8 @@ void ModGetList(std::vector<MODINFO> & list);
|
|||
int ModGetParty(duint Address);
|
||||
void ModSetParty(duint Address, int Party);
|
||||
bool ModAddImportToModule(duint Base, const MODIMPORTINFO & importInfo);
|
||||
bool ModRelocationsFromAddr(duint Address, std::vector<MODRELOCATIONINFO>* Relocations);
|
||||
bool ModRelocationAtAddr(duint Address, MODRELOCATIONINFO* Relocation);
|
||||
bool ModRelocationsInRange(duint Address, duint Size, std::vector<MODRELOCATIONINFO>* Relocations);
|
||||
|
||||
#endif // _MODULE_H
|
||||
|
|
|
@ -452,19 +452,38 @@ QString Disassembly::paintContent(QPainter* painter, dsint rowBase, int rowOffse
|
|||
//draw bytes
|
||||
RichTextPainter::List richBytes;
|
||||
RichTextPainter::CustomRichText_t space;
|
||||
space.highlight = false;
|
||||
space.highlightColor = ConfigColor("DisassemblyRelocationUnderlineColor");
|
||||
space.highlightWidth = 1;
|
||||
space.highlightConnectPrev = true;
|
||||
space.flags = RichTextPainter::FlagNone;
|
||||
space.text = " ";
|
||||
RichTextPainter::CustomRichText_t curByte;
|
||||
curByte.highlight = false;
|
||||
curByte.highlightColor = ConfigColor("DisassemblyRelocationUnderlineColor");
|
||||
curByte.highlightWidth = 1;
|
||||
curByte.flags = RichTextPainter::FlagAll;
|
||||
auto dump = mInstBuffer.at(rowOffset).dump;
|
||||
for(int i = 0; i < dump.size(); i++)
|
||||
{
|
||||
DBGRELOCATIONINFO relocInfo;
|
||||
if(DbgFunctions()->ModRelocationAtAddr(cur_addr + i, &relocInfo))
|
||||
{
|
||||
bool prevInSameReloc = relocInfo.rva < cur_addr + i - DbgFunctions()->ModBaseFromAddr(cur_addr + i);
|
||||
space.highlight = prevInSameReloc;
|
||||
curByte.highlight = true;
|
||||
curByte.highlightConnectPrev = prevInSameReloc;
|
||||
}
|
||||
else
|
||||
{
|
||||
space.highlight = false;
|
||||
curByte.highlight = false;
|
||||
}
|
||||
|
||||
if(i)
|
||||
richBytes.push_back(space);
|
||||
|
||||
auto byte = (unsigned char)dump.at(i);
|
||||
curByte.text = ToByteString(byte);
|
||||
|
||||
DBGPATCHINFO patchInfo;
|
||||
if(DbgFunctions()->PatchGetEx(cur_addr + i, &patchInfo))
|
||||
{
|
||||
|
|
|
@ -424,6 +424,7 @@ void AppearanceDialog::colorInfoListInit()
|
|||
colorInfoListAppend(tr("0x7F Bytes"), "DisassemblyByte7FColor", "DisassemblyByte7FBackgroundColor");
|
||||
colorInfoListAppend(tr("0xFF Bytes"), "DisassemblyByteFFColor", "DisassemblyByteFFBackgroundColor");
|
||||
colorInfoListAppend(tr("IsPrint Bytes"), "DisassemblyByteIsPrintColor", "DisassemblyByteIsPrintBackgroundColor");
|
||||
colorInfoListAppend(tr("Relocation underline"), "DisassemblyRelocationUnderlineColor", "");
|
||||
#ifdef _WIN64
|
||||
colorInfoListAppend(tr("RIP"), "DisassemblyCipColor", "DisassemblyCipBackgroundColor");
|
||||
#else //x86
|
||||
|
@ -546,7 +547,7 @@ void AppearanceDialog::colorInfoListInit()
|
|||
colorInfoListAppend(tr("Background"), "HexEditBackgroundColor", "");
|
||||
colorInfoListAppend(tr("Selection"), "HexEditSelectionColor", "");
|
||||
|
||||
colorInfoListAppend(tr("Graph"), "", "");
|
||||
colorInfoListAppend(tr("Graph:"), "", "");
|
||||
colorInfoListAppend(tr("Background"), "GraphBackgroundColor", "");
|
||||
colorInfoListAppend(tr("Node"), "GraphNodeColor", "GraphNodeBackgroundColor");
|
||||
colorInfoListAppend(tr("Terminal node shadow"), "GraphRetShadowColor", "");
|
||||
|
@ -568,6 +569,8 @@ void AppearanceDialog::colorInfoListInit()
|
|||
colorInfoListAppend(tr("Breakpoint Summary Parentheses"), "BreakpointSummaryParenColor", "");
|
||||
colorInfoListAppend(tr("Breakpoint Summary Keywords"), "BreakpointSummaryKeywordColor", "");
|
||||
colorInfoListAppend(tr("Breakpoint Summary Strings"), "BreakpointSummaryStringColor", "");
|
||||
colorInfoListAppend(tr("Patch located in relocation region"), "PatchRelocatedByteHighlightColor", "");
|
||||
|
||||
|
||||
//dev helper
|
||||
const QMap<QString, QColor>* Colors = &Config()->defaultColors;
|
||||
|
|
|
@ -264,6 +264,11 @@ void PatchDialog::on_listModules_itemSelectionChanged()
|
|||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
Qt::CheckState state = patchList.at(i).status.checked ? Qt::Checked : Qt::Unchecked;
|
||||
item->setCheckState(state);
|
||||
if(DbgFunctions()->ModRelocationAtAddr(patchList.at(i).patch.addr, nullptr))
|
||||
{
|
||||
item->setTextColor(ConfigColor("PatchRelocatedByteHighlightColor"));
|
||||
item->setToolTip(tr("Byte is located in relocation region"));
|
||||
}
|
||||
}
|
||||
mIsWorking = false;
|
||||
}
|
||||
|
@ -446,6 +451,10 @@ void PatchDialog::on_btnPatchFile_clicked()
|
|||
SimpleInfoBox(this, tr("Information"), tr("Nothing to patch!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(containsRelocatedBytes(curPatchList) && !showRelocatedBytesWarning())
|
||||
return;
|
||||
|
||||
char szModName[MAX_PATH] = "";
|
||||
if(!DbgFunctions()->ModPathFromAddr(DbgFunctions()->ModBaseFromName(mod.toUtf8().constData()), szModName, MAX_PATH))
|
||||
{
|
||||
|
@ -615,6 +624,9 @@ void PatchDialog::on_btnExport_clicked()
|
|||
if(!mPatches.size())
|
||||
return;
|
||||
|
||||
if(containsRelocatedBytes() && !showRelocatedBytesWarning())
|
||||
return;
|
||||
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save patch"), "", tr("Patch files (*.1337)"));
|
||||
if(!filename.length())
|
||||
return;
|
||||
|
@ -665,3 +677,31 @@ void PatchDialog::saveAs1337(const QString & filename)
|
|||
|
||||
SimpleInfoBox(this, tr("Information"), tr("%1 patch(es) exported!").arg(patches));
|
||||
}
|
||||
|
||||
bool PatchDialog::containsRelocatedBytes()
|
||||
{
|
||||
for(PatchMap::iterator i = mPatches.begin(); i != mPatches.end(); ++i)
|
||||
{
|
||||
const PatchInfoList & curPatchList = i.value();
|
||||
if(containsRelocatedBytes(curPatchList))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PatchDialog::containsRelocatedBytes(const PatchInfoList & patchList)
|
||||
{
|
||||
for(int i = 0; i < patchList.size(); i++)
|
||||
{
|
||||
if(patchList.at(i).status.checked && DbgFunctions()->ModRelocationAtAddr(patchList.at(i).patch.addr, nullptr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PatchDialog::showRelocatedBytesWarning()
|
||||
{
|
||||
auto result = QMessageBox::question(this, tr("Patches overlap with relocation regions"), tr("Your patches overlap with relocation regions. This can cause your code to become corrupted when you load the patched executable. Do you want to continue?"));
|
||||
return result == QMessageBox::Yes;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,10 @@ private:
|
|||
void saveAs1337(const QString & filename);
|
||||
//void saveAsC(const QString & filename);
|
||||
|
||||
bool containsRelocatedBytes();
|
||||
bool containsRelocatedBytes(const PatchInfoList & patchList);
|
||||
bool showRelocatedBytesWarning();
|
||||
|
||||
private slots:
|
||||
void dbgStateChanged(DBGSTATE state);
|
||||
void updatePatches();
|
||||
|
|
|
@ -67,6 +67,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultColors.insert("DisassemblyByteFFBackgroundColor", Qt::transparent);
|
||||
defaultColors.insert("DisassemblyByteIsPrintColor", QColor("#800080"));
|
||||
defaultColors.insert("DisassemblyByteIsPrintBackgroundColor", Qt::transparent);
|
||||
defaultColors.insert("DisassemblyRelocationUnderlineColor", QColor("#000000"));
|
||||
defaultColors.insert("DisassemblyCommentColor", QColor("#000000"));
|
||||
defaultColors.insert("DisassemblyCommentBackgroundColor", Qt::transparent);
|
||||
defaultColors.insert("DisassemblyAutoCommentColor", QColor("#AA5500"));
|
||||
|
@ -236,6 +237,8 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultColors.insert("BreakpointSummaryKeywordColor", QColor("#8B671F"));
|
||||
defaultColors.insert("BreakpointSummaryStringColor", QColor("#008000"));
|
||||
|
||||
defaultColors.insert("PatchRelocatedByteHighlightColor", QColor("#0000DD"));
|
||||
|
||||
//bool settings
|
||||
QMap<QString, bool> disassemblyBool;
|
||||
disassemblyBool.insert("ArgumentSpaces", false);
|
||||
|
|
|
@ -7,7 +7,6 @@ void RichTextPainter::paintRichText(QPainter* painter, int x, int y, int w, int
|
|||
{
|
||||
QPen pen;
|
||||
QPen highlightPen;
|
||||
highlightPen.setWidth(2);
|
||||
QBrush brush(Qt::cyan);
|
||||
for(const CustomRichText_t & curRichText : richText)
|
||||
{
|
||||
|
@ -46,8 +45,10 @@ void RichTextPainter::paintRichText(QPainter* painter, int x, int y, int w, int
|
|||
if(curRichText.highlight && curRichText.highlightColor.alpha())
|
||||
{
|
||||
highlightPen.setColor(curRichText.highlightColor);
|
||||
highlightPen.setWidth(curRichText.highlightWidth);
|
||||
painter->setPen(highlightPen);
|
||||
painter->drawLine(x + xinc + 1, y + h - 1, x + xinc + backgroundWidth - 1, y + h - 1);
|
||||
int highlightOffsetX = curRichText.highlightConnectPrev ? -1 : 1;
|
||||
painter->drawLine(x + xinc + highlightOffsetX, y + h - 1, x + xinc + backgroundWidth - 1, y + h - 1);
|
||||
}
|
||||
xinc += textWidth;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
CustomRichTextFlags flags;
|
||||
bool highlight;
|
||||
QColor highlightColor;
|
||||
int highlightWidth = 2;
|
||||
bool highlightConnectPrev = false;
|
||||
};
|
||||
|
||||
typedef std::vector<CustomRichText_t> List;
|
||||
|
|
Loading…
Reference in New Issue