

























现在我已经收集了足够的信息来解释 "View -> Source" 功能的实现。让我总结一下整个流程。根据代码分析,我来详细解释 View → Source 功能的实现原理和源码调试流程:
用户操作 → GUI 层 → Bridge 层 → 调试核心 (dbg) → PDB 符号系统
SourceViewerManager (src/gui/Src/Gui/SourceViewerManager.h/cpp)
loadSourceFile 信号并加载源文件SourceView (src/gui/Src/Gui/SourceView.h/cpp)
src/bridge/bridgemain.cpp)BRIDGE_IMPEXP void GuiLoadSourceFileEx(const char* path, duint addr)
{
_gui_sendmessage(GUI_LOAD_SOURCE_FILE, (void*)path, (void*)addr);
}
SymGetSourceLine (src/dbg/symbolinfo.cpp)
SymGetSourceAddr (src/dbg/symbolinfo.cpp)
// MainWindow.cpp
connect(ui->actionSource, SIGNAL(triggered()), this, SLOT(displaySourceViewWidget()));
void MainWindow::displaySourceViewWidget()
{
showQWidgetTab(mSourceViewManager);
}
// CPUDisassembly.cpp
void CPUDisassembly::openSourceSlot()
{
char szSourceFile[MAX_STRING_SIZE] = "";
int line = 0;
auto sel = rvaToVa(getInitialSelection());
// 1. 从地址获取源文件信息
if(!DbgFunctions()->GetSourceFromAddr(sel, szSourceFile, &line))
return;
// 2. 发送信号加载源文件
emit Bridge::getBridge()->loadSourceFile(szSourceFile, sel);
emit displaySourceManagerWidget();
}
// SourceViewerManager.cpp
void SourceViewerManager::loadSourceFile(QString path, duint addr)
{
// 1. 检查文件是否已加载
for(int i = 0; i < count(); i++)
{
SourceView* curView = (SourceView*)this->widget(i);
if(curView->getSourcePath().compare(path, Qt::CaseInsensitive) == 0)
{
curView->setSelection(addr); // 跳转到对应行
setCurrentIndex(i);
return;
}
}
// 2. 创建新的源文件视图
SourceView* newView = new SourceView(path, addr, this);
addTab(newView, title);
setCurrentIndex(count() - 1);
// 3. 设置焦点并跳转到对应行
QTimer::singleShot(50, [newView, addr]()
{
newView->setSelection(addr);
newView->setFocus();
});
}
// SourceView.cpp
duint SourceView::addrFromIndex(size_t index)
{
// 调用调试核心获取行号对应的地址
return DbgFunctions()->GetAddrFromLineEx(mModBase,
mSourcePath.toUtf8().constData(), int(index + 1));
}
// _dbgfunctions.cpp
_dbgfunctions.GetAddrFromLineEx = [](duint mod, const char* szSourceFile, int line)
{
duint addr = 0;
if(SymGetSourceAddr(mod, szSourceFile, line, &addr))
return addr;
return 0;
};
// symbolinfo.cpp
bool SymGetSourceAddr(duint Module, const char* FileName, int Line, duint* Address)
{
MODINFO* modInfo = ModInfoFromAddr(Module);
SymbolSourceBase* sym = modInfo->symbols;
LineInfo lineInfo;
if(!sym->findSourceLineInfo(FileName, Line, lineInfo))
return false;
*Address = lineInfo.rva + modInfo->base;
return true;
}
// symbolsourcedia.cpp
bool SymbolSourceDIA::loadSourceLinesAsync()
{
PDBDiaFile pdb;
pdb.open(_path.c_str());
// 1. 枚举所有行号信息
std::vector<DiaLineInfo_t> lines;
std::map<DWORD, String> files;
pdb.enumerateLineNumbers(0, uint32_t(_imageSize), lines, files, _requiresShutdown);
// 2. 构建地址→行号映射
for(const auto & line : lines)
{
_linesData.emplace_back();
lineInfo.rva = line.rva;
lineInfo.lineNumber = line.lineNumber;
lineInfo.sourceFileIndex = found->second.sourceFileIndex;
}
// 3. 构建行号→地址映射
for(size_t i = 0; i < _linesData.size(); i++)
{
_sourceLines[line.sourceFileIndex].push_back(lineIndex);
}
_linesLoaded = true;
}
// pdbdiafile.cpp
bool PDBDiaFile::enumerateLineNumbers(uint32_t rva, uint32_t size,
std::vector<DiaLineInfo_t> & lines,
std::map<DWORD, std::string> & files,
const std::atomic<bool> & cancelled)
{
// 1. 使用 DIA API 查找行号
CComPtr<IDiaEnumLineNumbers> lineNumbersEnum;
m_session->findLinesByRVA(rva, size, &lineNumbersEnum);
// 2. 遍历行号信息
while(lineNumbersEnum->Next(1, &lineNumberInfo, &fetched) == S_OK)
{
// 获取源文件
lineNumberInfo->get_sourceFile(&sourceFile);
sourceFile->get_fileName(&fileName);
// 获取行号和地址
lineNumberInfo->get_lineNumber(&lineInfo.lineNumber);
lineNumberInfo->get_relativeVirtualAddress(&lineInfo.rva);
lines.push_back(lineInfo);
files.insert({ sourceFileId, fileName });
}
}
反汇编地址 → SymGetSourceLine → 源文件路径 + 行号 → 显示源文件
源文件行号 → SymGetSourceAddr → RVA + 模块基址 → 反汇编地址
源码调试的核心依赖:
这就是 x64dbg 如何通过 PDB 符号系统实现源码级调试的完整流程!
采用EnigmaVirtualBox制作单文件便携版只是兴趣爱好,在WES7环境中制作,不保证全系统下正常,随缘随心随性。 温馨提醒建议在SandBoxie中测试https://www.lanzous.com/iatkifc
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。