1
0
Fork 0

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:
Georgeto 2017-08-14 00:17:47 +02:00 committed by Duncan Ogilvie
parent 84bbc5cf4a
commit 6587cbc564
11 changed files with 271 additions and 6 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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))
{

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -28,6 +28,8 @@ public:
CustomRichTextFlags flags;
bool highlight;
QColor highlightColor;
int highlightWidth = 2;
bool highlightConnectPrev = false;
};
typedef std::vector<CustomRichText_t> List;