【发布时间】:2018-07-12 15:50:17
【问题描述】:
我想调试一个由进程 A 启动的进程 B,当我连接进程 B 时出现错误,我无法在主函数上中断。有没有办法在进程 B 启动时自动中断?
【问题讨论】:
标签: windows visual-studio debugging visual-c++
我想调试一个由进程 A 启动的进程 B,当我连接进程 B 时出现错误,我无法在主函数上中断。有没有办法在进程 B 启动时自动中断?
【问题讨论】:
标签: windows visual-studio debugging visual-c++
不幸的是,视觉工作室没有办法。起初和主要 - 这个调试器不能一次调试多个进程。它只能调试一个进程。如果您调试进程 A 并且它启动了进程 B - 此调试器根本无法调试新进程 B。
通常当我们调试进程时A - 存在附加到进程的DebugPort 对象。 ( EPROCESS.DebugPort ) 在 EPROCESS 对象中也存在 NoDebugInherit 标志。当进程 A 启动新进程 B - 系统下一步(伪代码):
if (Process->DebugPort && !Process->NoDebugInherit)
AttachDebugPortToNewProcess(Process->DebugPort);
如果 A 在调试器下且未设置 NoDebugInherit 标志 - 新进程 B 也附加到相同的 DebugPort(相同的调试器)。
NoDebugInherit 是如何设置的?
如果我们通过CreateProcess api 启动进程 - 如果我们将DEBUG_ONLY_THIS_PROCESS 设置为NoDebugInherit 设置为true。如果我们使用DEBUG_PROCESS 没有 DEBUG_ONLY_THIS_PROCESS - NoDebugInherit 设置为 false - 结果新进程创建的所有子进程也将被调试。
如果我们通过DebugActiveProcess 附加到已经运行的进程NoDebugInherit 设置为true。因此,如果此进程启动新进程 - 它将不会被调试。
我们可以通过NtQueryInformationProcess 和NtSetInformationProcess 和ProcessDebugFlags 查询和设置这个NoDebugInherit PROCESSINFOCLASS(它在ntddk.h 中声明) .有了这个我们可以随时通过下一个代码查询和更改ProcessDebugFlags:
// from WRK\WRK-v1.2\public\sdk\inc\ntpsapi.h
//
// Define process debug flags
//
#define PROCESS_DEBUG_INHERIT 0x00000001
NTSTATUS SetDebugInherit(HANDLE hProcess, BOOLEAN bInherit)
{
ULONG DebugFlags;
// hProcess must have PROCESS_QUERY_INFORMATION access
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessDebugFlags, &DebugFlags, sizeof(DebugFlags), 0);
if (0 <= status)
{
bInherit ? DebugFlags |= PROCESS_DEBUG_INHERIT : DebugFlags &= ~PROCESS_DEBUG_INHERIT;
// hProcess must have PROCESS_SET_INFORMATION access
status = NtSetInformationProcess(hProcess, ProcessDebugFlags, &DebugFlags, sizeof(DebugFlags));
}
return status;
}
如果返回 MSVS 或 WinDbg - 它总是以 DEBUG_ONLY_THIS_PROCESS 启动新进程。即使我们自己将NoDebugInherit 设置为false 并启动新进程——无论如何,如果我们使用MSVS,新进程将不会被调试甚至不会运行。如果 WinDbg 尝试一次正式的调试 2(和更多)进程,但也不能这样做。你需要更好的调试器,它可以同时调试多个进程。
另一种可能性在进程 A 中设置断点 CreateProcessInternalW 并在它命中后 - 将断点设置为 NtResumeThread(这必须是临时断点 - 仅在 CreateProcessInternalW 命中之后) .当NtResumeThread 命中时 - 新进程B 已经启动(处于挂起状态)。此时您可以运行 MSVS 或 WinDbg 的新实例并附加到它。但是这里也可能会出现问题,因为大多数调试器(MSVS 和 WinDbg)使用DebugActiveProcess。这个 api 在调用 NtDebugActiveProcess 之后需要并且足以开始调试过程,由于某种原因在目标进程 (DbgUiRemoteBreakin) 中创建 (DbgUiIssueRemoteBreakin) 新线程。结果新进程开始在这个线程上执行。在 xp 中这会导致进程初始化失败。在现代 Windows 中 - 这通常是可行的,但无论如何都会影响进程执行 - 主进程线程不是第一个进程。
如果 B 是你的进程 - 所以你可以修改它的代码,你总是可以在你想要附加/中断调试器的地方做下一步:
while (!IsDebuggerPresent()) Sleep(1000);
__debugbreak();
或者如果你可以显示 gui:
WCHAR sz[64];
swprintf(sz, L"attact to %u(0x%x)\n", GetCurrentProcessId(), GetCurrentProcessId());
while (!IsDebuggerPresent()) MessageBoxW(0,0,sz, MB_ICONINFORMATION);
__debugbreak();
【讨论】:
当您手动附加到进程 B 时,它必须已经运行了一段时间。
通过插件“Microsoft Child Process Debugging Power Tool”,VS 可以在子进程启动时自动附加到子进程,您可以在main() 的第一条语句处中断。
【讨论】: