调试线程时无限循环
Infinite loop while debugging a thread
我正在尝试将硬件断点附加到游戏进程,我成功了。然后我试图遍历异常并等待我放在那里的异常,它也工作正常。问题是,在它发生之后,它进入了我无法刹车的无限循环。你能建议吗?
我这样做的原因是我想在此时停止线程,使用 Context 读取 EAX 值,然后继续该过程。
Header.h 包括此处调用的函数,它们都可以正常工作,因此我现在不包括它。
#include "Header.h"
#include
int main() {
SetDebugPrivilege(TRUE);
DWORD dwProcessID = 0;
DWORD dwGame = 0;
printf("Looking for game process...\n");
while (dwProcessID == 0) {
dwProcessID = GetProcessID(L"PathOfExile.exe");
if (dwProcessID != 0)
dwGame = 1;
Sleep(100);
}
printf("dwProcessID = %p\n", dwProcessID);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);
MODULEENTRY32 module;
module.dwSize = sizeof(MODULEENTRY32);
Module32First(snapshot, &module);
printf("PoE base address = %p\n", module.modBaseAddr);
hpChangeBreakpoint = (DWORD*)((char *)module.modBaseAddr + 0x1AAD20);
std::cout << hpChangeBreakpoint << std::endl;
//hpChangeBreakpoint = 0x013FB279;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
BOOL bDebugging = DebugActiveProcess(dwProcessID);
printf("bDebugging = %d\n", bDebugging);
DWORD dwThreadID = GetProcessThreadID(dwProcessID);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
CONTEXT parentCtx;
parentCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(hThread, &parentCtx))
{
parentCtx.Dr0 = (DWORD)hpChangeBreakpoint;
parentCtx.Dr7 = 0x00000001;
std::cout << "GetThreadContext successful" << std::endl;
SetThreadContext(hThread, &parentCtx);
}
DEBUG_EVENT DebugEvent;
DWORD dbgFlag = DBG_CONTINUE;
DWORD *currentHpPointerAddress = nullptr;
DWORD *maxHpPointerAddress = nullptr;
BOOLEAN bQuit = FALSE;
while (!bQuit && WaitForDebugEvent(&DebugEvent, INFINITE))
{
dbgFlag = DBG_CONTINUE;
switch (DebugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
switch (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_SINGLE_STEP:
if (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
{
#define RESUME_FLAG 0x10000
CONTEXT childContext;
childContext.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(hThread, &childContext))
{
childContext.EFlags |= RESUME_FLAG;
SetThreadContext(hThread, &childContext);
std::cout << "current HP: " << childContext.Ecx << std::endl;
currentHpPointerAddress = (DWORD*)((char *)childContext.Edi + 0x8E0);
maxHpPointerAddress = (DWORD*)((char *)childContext.Edi + 0x8E4);
}
if (GetThreadContext(hThread, &parentCtx))
{
parentCtx.Dr0 = 0;
parentCtx.Dr7 = 0x400;
SetThreadContext(hThread, &parentCtx);
bQuit = TRUE;
}
}
else
dbgFlag = DBG_EXCEPTION_NOT_HANDLED;
break;
default:
dbgFlag = DBG_EXCEPTION_NOT_HANDLED;
}
break;
case LOAD_DLL_DEBUG_EVENT:
{
CloseHandle(DebugEvent.u.LoadDll.hFile);
break;
}
case CREATE_PROCESS_DEBUG_EVENT:
{
CloseHandle(DebugEvent.u.CreateProcessInfo.hFile);
break;
}
case EXIT_PROCESS_DEBUG_EVENT:
break;
default:
__nop();
}
if (!ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dbgFlag))
{
break;
}
if (bQuit)
DebugActiveProcessStop(dwProcessID);
}
while (1)
{
WORD currentHP = 0;
WORD maxHP = 0;
if (
ReadProcessMemory(hProcess, currentHpPointerAddress, ¤tHP, sizeof(currentHP), NULL) == 0
|| ReadProcessMemory(hProcess, maxHpPointerAddress, &maxHP, sizeof(maxHP), NULL) == 0
)
{
printf("Failed to read memory: %u\n", GetLastError());
}
else {
std::cout << "HP: " << currentHP << " / " << maxHP << std::endl;
}
Sleep(1000);
}
system("pause>nul");
return 0;
}
当我 运行 它时,游戏运行正常,直到断点发生,当断点发生时,我得到无限 "breakpoint" cout 的垃圾邮件,当我调试它时,这一行:
如果 (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
始终为真,但dwFirstChance标志为1,所以它总是一个新的异常?在这个无限循环中唯一改变的是 hThread,它总是不同的。我觉得我因为缺乏知识而错过了一些东西,因此会感谢任何帮助或正确方向的提示。谢谢!
你 listen/know 关于 Resume Flag (RF)
吗?您需要将其设置为 step over DrX
brealpoint
所以代码必须是下一个
#define RESUME_FLAG 0x10000
CONTEXT Context = {};
Context.ContextFlags = CONTEXT_CONTROL;// not need CONTEXT_FULL here;
if (GetThreadContext(hThread, &Context))
{
Context.EFlags |= RESUME_FLAG; // !!! this line is key point
SetThreadContext(hThread, &Context);
}
这将从 win2003 或 windows vista 开始工作。不幸的是,XP 不允许您在 CONTEXT
中设置此标志。所以在这里你需要删除 Dr0
断点以跨过它( 或修补 XP 内核 - 在 ntoskrnl 代码中搜索 0x003E0DD7
DWORD
并将其替换为 0x003F0DD7
- 这是 Eflags
面具 - 在 RESUME_FLAG
中有所不同)
还提供一些优化建议 - 您不需要每次 EXCEPTION_DEBUG_EVENT
时都调用 OpenThread
。
起初你已经有了这个线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
干脆不关,等你调用后SetThreadContext
并且异常只能在此线程的上下文中发生,所有其他线程不受此影响。
在第二个你永远不会关闭线程句柄,在 EXCEPTION_DEBUG_EVENT
中打开 - 所以你已经有资源泄漏。
调试器在 CREATE_THREAD_DEBUG_EVENT
和 CREATE_PROCESS_DEBUG_EVENT
上获得了线程句柄并且 必须 关闭它(或者只是或通常维护它并在 EXIT_THREAD_DEBUG_EVENT
上关闭和 EXIT_PROCESS_DEBUG_EVENT
)
您没有处理 LOAD_DLL_DEBUG_EVENT
因此没有关闭文件句柄。
您的代码有 巨大的句柄泄漏 !
SuspendThread / ResumeThread - 为了什么?!绝对毫无意义 - 线程(以及进程中的所有线程)已经在此时挂起
struct CThread : LIST_ENTRY
{
HANDLE _hThread;
ULONG _dwThreadId;
CThread(HANDLE hThread, ULONG dwThreadId)
{
_hThread = hThread;
_dwThreadId = dwThreadId;
}
~CThread()
{
//CloseHandle(_hThread);// will be closed in ContinueDebugEvent
}
static CThread* get(ULONG dwThreadId, PLIST_ENTRY ThreadListHead, CThread* pHintThread)
{
if (pHintThread && pHintThread->_dwThreadId == dwThreadId)
{
return pHintThread;
}
PLIST_ENTRY entry = ThreadListHead;
while ((entry = entry->Flink) != ThreadListHead)
{
pHintThread = static_cast<CThread*>(entry);
if (pHintThread->_dwThreadId == dwThreadId)
{
return pHintThread;
}
}
return 0;//??
}
static void DeleteAll(PLIST_ENTRY ThreadListHead)
{
PLIST_ENTRY entry = ThreadListHead->Flink;
while (entry != ThreadListHead)
{
CThread* pThread = static_cast<CThread*>(entry);
entry = entry->Flink;
delete pThread;
}
}
};
void RunDebuggerLoop()
{
BOOL bQuit = FALSE;
LIST_ENTRY ThreadListHead = { &ThreadListHead, &ThreadListHead };
CThread* pThread = 0;
DEBUG_EVENT de;
BOOLEAN bFirst = TRUE;
while (!bQuit && WaitForDebugEvent(&de, INFINITE))
{
NTSTATUS status = DBG_CONTINUE;
switch(de.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
if (
!de.u.Exception.dwFirstChance
||
!(pThread = CThread::get(de.dwThreadId, &ThreadListHead, pThread))
)
{
bQuit = TRUE;
continue;
}
status = DBG_EXCEPTION_NOT_HANDLED;
switch (de.u.Exception.ExceptionRecord.ExceptionCode)
{
case STATUS_BREAKPOINT:
case STATUS_WX86_BREAKPOINT:
if (bFirst)
{
bFirst = FALSE;
status = DBG_CONTINUE;
}
break;
case STATUS_SINGLE_STEP:
case STATUS_WX86_SINGLE_STEP:
{
::CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_CONTROL;
if (GetThreadContext(pThread->_hThread, &ctx))
{
ctx.EFlags |= RESUME_FLAG;
SetThreadContext(pThread->_hThread, &ctx);
}
}
break;
case STATUS_ACCESS_VIOLATION:
if (de.u.Exception.ExceptionRecord.NumberParameters > 1)
{
ULONG_PTR ptr = de.u.Exception.ExceptionRecord.ExceptionInformation[1];
}
break;
}
break;
case CREATE_PROCESS_DEBUG_EVENT:
CloseHandle(de.u.CreateProcessInfo.hFile);
//CloseHandle(de.u.CreateProcessInfo.hProcess);// will be auto closed in ContinueDebugEvent
de.u.CreateThread.hThread = de.u.CreateProcessInfo.hThread;
case CREATE_THREAD_DEBUG_EVENT:
if (pThread = new CThread(de.u.CreateThread.hThread, de.dwThreadId))
{
InsertHeadList(&ThreadListHead, pThread);
}
break;
case EXIT_THREAD_DEBUG_EVENT:
if (pThread = CThread::get(de.dwThreadId, &ThreadListHead, pThread))
{
RemoveEntryList(pThread);
delete pThread;
pThread = 0;
}
break;
case LOAD_DLL_DEBUG_EVENT:
CloseHandle(de.u.LoadDll.hFile);
break;
case EXIT_PROCESS_DEBUG_EVENT:
bQuit = TRUE;
break;
case OUTPUT_DEBUG_STRING_EVENT:
case UNLOAD_DLL_DEBUG_EVENT:
__nop();
break;
default:
__nop();
}
if (!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, status))
{
break;
}
}
CThread::DeleteAll(&ThreadListHead);
}
void Ep()
{
// tag by * in begin of CommandLine
PWSTR CommandLine = GetCommandLine();
if (!CommandLine || *CommandLine != '*')
{
// debugger case
WCHAR FileName[MAX_PATH];
if (ULONG n = GetModuleFileName(0, FileName, RTL_NUMBER_OF(FileName)))
{
if (n < MAX_PATH)
{
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };
PWSTR newCommandLine = (PWSTR)alloca((wcslen(CommandLine) + 2)*sizeof(WCHAR));
*newCommandLine = '*';
wcscpy(newCommandLine + 1, CommandLine);
if (CreateProcessW(FileName, newCommandLine, 0, 0, 0, DEBUG_PROCESS, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
RunDebuggerLoop();
}
}
}
ExitProcess(0);
}
else
{
// main case
wcscpy(CommandLine, CommandLine + 1);
OutputDebugStringA("AAAAAA\n");
if (MessageBoxW(0, L"xxx", CommandLine, MB_YESNO) == IDYES)
{
OutputDebugStringW(L"WWWWWWWW\n");
}
ExitProcess(0);
}
}
非常感谢您花时间回答我。如果有些问题很奇怪,我很抱歉,但我是一名 JS 开发人员,我在这里做的是我的爱好。我所知道的是,与我的 JS 相比,它感觉像是一个不同且更深刻的世界。;)
我确实编辑了代码,还删除了您提到的多余内容。线程的 suspend/resume 在那里,因为它们之间我有一些内存修改,但是根据你所说的,线程在这一点上是挂起的,即使我会修改内存,也不需要它们?
言归正传,死循环还在。我已经添加了 RF 标志,但我才刚刚开始通读相关文章,所以除了添加它之外,我也明白为什么。同时,您能再给我一个提示,说明为什么它可能仍然无法正常工作吗?
此外,我添加了 LOAD_DLL_DEBUG_EVENT 处理程序,并且我将立即关闭处理程序,因为此时我不需要对其执行任何其他操作(是吗?)。我不完全明白的是我应该何时关闭从 CREATE_PROCESS 和 CREATE_THREAD 调试事件接收到的处理程序?我正在努力了解调试器是如何工作的,这是我使用它的第 4 天,但正如我所看到的,这就是发生的事情:
WaitForDebugEvent 接收到一个调试事件,只要不是我的,它就会被 ContinueDebugEvent 和 DBG_EXCEPTION_NOT_HANDLED 处理,所以它被传回并由游戏处理。
最后,WaitForDebugEvent 收到我的调试事件,即 EXCEPTION_SINGLE_STEP,我在那里做我的事情,然后用 DBG_CONTINUE 继续它 - 这将异常标记为由我处理,系统会继续跟踪它。对吗?
我的实际代码仍在无限循环中循环并打印 "Breakpoint":
#include "Header.h"
#include <iostream>
int main() {
hpChangeBreakpoint = 0x013FB279;
SetDebugPrivilege(TRUE);
DWORD dwProcessID = 0;
DWORD dwGame = 0;
printf("Looking for game process...\n");
while (dwProcessID == 0) {
dwProcessID = GetProcessID(L"PathOfExile.exe");
if (dwProcessID != 0)
dwGame = 1;
Sleep(100);
}
printf("dwProcessID = %p\n", dwProcessID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
BOOL bDebugging = DebugActiveProcess(dwProcessID);
printf("bDebugging = %d\n", bDebugging);
DWORD dwThreadID = GetProcessThreadID(dwProcessID);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
CONTEXT context;
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(hThread, &context))
{
context.Dr0 = hpChangeBreakpoint;
context.Dr7 = 0x00000001;
std::cout << "GetThreadContext successful" << std::endl;
SetThreadContext(hThread, &context);
}
DEBUG_EVENT DebugEvent;
BOOL bContinueDebugging = false;
for(;;)
{
WaitForDebugEvent(&DebugEvent, INFINITE);
switch (DebugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
switch (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_SINGLE_STEP:
if (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
{
#define RESUME_FLAG 0x10000
CONTEXT Context;
Context.ContextFlags = CONTEXT_CONTROL;
Context.EFlags |= RESUME_FLAG;
std::cout << "Breakpoint" << std::endl;
bContinueDebugging = true;
}
if (bContinueDebugging)
{
// DBG_CONTINUE to tell the program we have handled the exception
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_CONTINUE);
bContinueDebugging = false;
}
else // if the exception was not handled by our exception-handler, we want the program to handle it, so..
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
break;
default:
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
break;
case LOAD_DLL_DEBUG_EVENT:
{
std::cout << "load dll debug event" << std::endl;
CloseHandle(DebugEvent.u.LoadDll.hFile);
break;
}
default:
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
}
system("pause>nul");
return 0;
}
我正在尝试将硬件断点附加到游戏进程,我成功了。然后我试图遍历异常并等待我放在那里的异常,它也工作正常。问题是,在它发生之后,它进入了我无法刹车的无限循环。你能建议吗? 我这样做的原因是我想在此时停止线程,使用 Context 读取 EAX 值,然后继续该过程。
Header.h 包括此处调用的函数,它们都可以正常工作,因此我现在不包括它。
#include "Header.h" #include
int main() {
SetDebugPrivilege(TRUE);
DWORD dwProcessID = 0;
DWORD dwGame = 0;
printf("Looking for game process...\n");
while (dwProcessID == 0) {
dwProcessID = GetProcessID(L"PathOfExile.exe");
if (dwProcessID != 0)
dwGame = 1;
Sleep(100);
}
printf("dwProcessID = %p\n", dwProcessID);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);
MODULEENTRY32 module;
module.dwSize = sizeof(MODULEENTRY32);
Module32First(snapshot, &module);
printf("PoE base address = %p\n", module.modBaseAddr);
hpChangeBreakpoint = (DWORD*)((char *)module.modBaseAddr + 0x1AAD20);
std::cout << hpChangeBreakpoint << std::endl;
//hpChangeBreakpoint = 0x013FB279;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
BOOL bDebugging = DebugActiveProcess(dwProcessID);
printf("bDebugging = %d\n", bDebugging);
DWORD dwThreadID = GetProcessThreadID(dwProcessID);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
CONTEXT parentCtx;
parentCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(hThread, &parentCtx))
{
parentCtx.Dr0 = (DWORD)hpChangeBreakpoint;
parentCtx.Dr7 = 0x00000001;
std::cout << "GetThreadContext successful" << std::endl;
SetThreadContext(hThread, &parentCtx);
}
DEBUG_EVENT DebugEvent;
DWORD dbgFlag = DBG_CONTINUE;
DWORD *currentHpPointerAddress = nullptr;
DWORD *maxHpPointerAddress = nullptr;
BOOLEAN bQuit = FALSE;
while (!bQuit && WaitForDebugEvent(&DebugEvent, INFINITE))
{
dbgFlag = DBG_CONTINUE;
switch (DebugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
switch (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_SINGLE_STEP:
if (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
{
#define RESUME_FLAG 0x10000
CONTEXT childContext;
childContext.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(hThread, &childContext))
{
childContext.EFlags |= RESUME_FLAG;
SetThreadContext(hThread, &childContext);
std::cout << "current HP: " << childContext.Ecx << std::endl;
currentHpPointerAddress = (DWORD*)((char *)childContext.Edi + 0x8E0);
maxHpPointerAddress = (DWORD*)((char *)childContext.Edi + 0x8E4);
}
if (GetThreadContext(hThread, &parentCtx))
{
parentCtx.Dr0 = 0;
parentCtx.Dr7 = 0x400;
SetThreadContext(hThread, &parentCtx);
bQuit = TRUE;
}
}
else
dbgFlag = DBG_EXCEPTION_NOT_HANDLED;
break;
default:
dbgFlag = DBG_EXCEPTION_NOT_HANDLED;
}
break;
case LOAD_DLL_DEBUG_EVENT:
{
CloseHandle(DebugEvent.u.LoadDll.hFile);
break;
}
case CREATE_PROCESS_DEBUG_EVENT:
{
CloseHandle(DebugEvent.u.CreateProcessInfo.hFile);
break;
}
case EXIT_PROCESS_DEBUG_EVENT:
break;
default:
__nop();
}
if (!ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dbgFlag))
{
break;
}
if (bQuit)
DebugActiveProcessStop(dwProcessID);
}
while (1)
{
WORD currentHP = 0;
WORD maxHP = 0;
if (
ReadProcessMemory(hProcess, currentHpPointerAddress, ¤tHP, sizeof(currentHP), NULL) == 0
|| ReadProcessMemory(hProcess, maxHpPointerAddress, &maxHP, sizeof(maxHP), NULL) == 0
)
{
printf("Failed to read memory: %u\n", GetLastError());
}
else {
std::cout << "HP: " << currentHP << " / " << maxHP << std::endl;
}
Sleep(1000);
}
system("pause>nul");
return 0;
}
当我 运行 它时,游戏运行正常,直到断点发生,当断点发生时,我得到无限 "breakpoint" cout 的垃圾邮件,当我调试它时,这一行: 如果 (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
始终为真,但dwFirstChance标志为1,所以它总是一个新的异常?在这个无限循环中唯一改变的是 hThread,它总是不同的。我觉得我因为缺乏知识而错过了一些东西,因此会感谢任何帮助或正确方向的提示。谢谢!
你 listen/know 关于 Resume Flag (RF)
吗?您需要将其设置为 step over DrX
brealpoint
所以代码必须是下一个
#define RESUME_FLAG 0x10000
CONTEXT Context = {};
Context.ContextFlags = CONTEXT_CONTROL;// not need CONTEXT_FULL here;
if (GetThreadContext(hThread, &Context))
{
Context.EFlags |= RESUME_FLAG; // !!! this line is key point
SetThreadContext(hThread, &Context);
}
这将从 win2003 或 windows vista 开始工作。不幸的是,XP 不允许您在 CONTEXT
中设置此标志。所以在这里你需要删除 Dr0
断点以跨过它( 或修补 XP 内核 - 在 ntoskrnl 代码中搜索 0x003E0DD7
DWORD
并将其替换为 0x003F0DD7
- 这是 Eflags
面具 - 在 RESUME_FLAG
中有所不同)
还提供一些优化建议 - 您不需要每次 EXCEPTION_DEBUG_EVENT
时都调用 OpenThread
。
起初你已经有了这个线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
干脆不关,等你调用后SetThreadContext
并且异常只能在此线程的上下文中发生,所有其他线程不受此影响。
在第二个你永远不会关闭线程句柄,在 EXCEPTION_DEBUG_EVENT
中打开 - 所以你已经有资源泄漏。
调试器在 CREATE_THREAD_DEBUG_EVENT
和 CREATE_PROCESS_DEBUG_EVENT
上获得了线程句柄并且 必须 关闭它(或者只是或通常维护它并在 EXIT_THREAD_DEBUG_EVENT
上关闭和 EXIT_PROCESS_DEBUG_EVENT
)
您没有处理 LOAD_DLL_DEBUG_EVENT
因此没有关闭文件句柄。
您的代码有 巨大的句柄泄漏 !
SuspendThread / ResumeThread - 为了什么?!绝对毫无意义 - 线程(以及进程中的所有线程)已经在此时挂起
struct CThread : LIST_ENTRY
{
HANDLE _hThread;
ULONG _dwThreadId;
CThread(HANDLE hThread, ULONG dwThreadId)
{
_hThread = hThread;
_dwThreadId = dwThreadId;
}
~CThread()
{
//CloseHandle(_hThread);// will be closed in ContinueDebugEvent
}
static CThread* get(ULONG dwThreadId, PLIST_ENTRY ThreadListHead, CThread* pHintThread)
{
if (pHintThread && pHintThread->_dwThreadId == dwThreadId)
{
return pHintThread;
}
PLIST_ENTRY entry = ThreadListHead;
while ((entry = entry->Flink) != ThreadListHead)
{
pHintThread = static_cast<CThread*>(entry);
if (pHintThread->_dwThreadId == dwThreadId)
{
return pHintThread;
}
}
return 0;//??
}
static void DeleteAll(PLIST_ENTRY ThreadListHead)
{
PLIST_ENTRY entry = ThreadListHead->Flink;
while (entry != ThreadListHead)
{
CThread* pThread = static_cast<CThread*>(entry);
entry = entry->Flink;
delete pThread;
}
}
};
void RunDebuggerLoop()
{
BOOL bQuit = FALSE;
LIST_ENTRY ThreadListHead = { &ThreadListHead, &ThreadListHead };
CThread* pThread = 0;
DEBUG_EVENT de;
BOOLEAN bFirst = TRUE;
while (!bQuit && WaitForDebugEvent(&de, INFINITE))
{
NTSTATUS status = DBG_CONTINUE;
switch(de.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
if (
!de.u.Exception.dwFirstChance
||
!(pThread = CThread::get(de.dwThreadId, &ThreadListHead, pThread))
)
{
bQuit = TRUE;
continue;
}
status = DBG_EXCEPTION_NOT_HANDLED;
switch (de.u.Exception.ExceptionRecord.ExceptionCode)
{
case STATUS_BREAKPOINT:
case STATUS_WX86_BREAKPOINT:
if (bFirst)
{
bFirst = FALSE;
status = DBG_CONTINUE;
}
break;
case STATUS_SINGLE_STEP:
case STATUS_WX86_SINGLE_STEP:
{
::CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_CONTROL;
if (GetThreadContext(pThread->_hThread, &ctx))
{
ctx.EFlags |= RESUME_FLAG;
SetThreadContext(pThread->_hThread, &ctx);
}
}
break;
case STATUS_ACCESS_VIOLATION:
if (de.u.Exception.ExceptionRecord.NumberParameters > 1)
{
ULONG_PTR ptr = de.u.Exception.ExceptionRecord.ExceptionInformation[1];
}
break;
}
break;
case CREATE_PROCESS_DEBUG_EVENT:
CloseHandle(de.u.CreateProcessInfo.hFile);
//CloseHandle(de.u.CreateProcessInfo.hProcess);// will be auto closed in ContinueDebugEvent
de.u.CreateThread.hThread = de.u.CreateProcessInfo.hThread;
case CREATE_THREAD_DEBUG_EVENT:
if (pThread = new CThread(de.u.CreateThread.hThread, de.dwThreadId))
{
InsertHeadList(&ThreadListHead, pThread);
}
break;
case EXIT_THREAD_DEBUG_EVENT:
if (pThread = CThread::get(de.dwThreadId, &ThreadListHead, pThread))
{
RemoveEntryList(pThread);
delete pThread;
pThread = 0;
}
break;
case LOAD_DLL_DEBUG_EVENT:
CloseHandle(de.u.LoadDll.hFile);
break;
case EXIT_PROCESS_DEBUG_EVENT:
bQuit = TRUE;
break;
case OUTPUT_DEBUG_STRING_EVENT:
case UNLOAD_DLL_DEBUG_EVENT:
__nop();
break;
default:
__nop();
}
if (!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, status))
{
break;
}
}
CThread::DeleteAll(&ThreadListHead);
}
void Ep()
{
// tag by * in begin of CommandLine
PWSTR CommandLine = GetCommandLine();
if (!CommandLine || *CommandLine != '*')
{
// debugger case
WCHAR FileName[MAX_PATH];
if (ULONG n = GetModuleFileName(0, FileName, RTL_NUMBER_OF(FileName)))
{
if (n < MAX_PATH)
{
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };
PWSTR newCommandLine = (PWSTR)alloca((wcslen(CommandLine) + 2)*sizeof(WCHAR));
*newCommandLine = '*';
wcscpy(newCommandLine + 1, CommandLine);
if (CreateProcessW(FileName, newCommandLine, 0, 0, 0, DEBUG_PROCESS, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
RunDebuggerLoop();
}
}
}
ExitProcess(0);
}
else
{
// main case
wcscpy(CommandLine, CommandLine + 1);
OutputDebugStringA("AAAAAA\n");
if (MessageBoxW(0, L"xxx", CommandLine, MB_YESNO) == IDYES)
{
OutputDebugStringW(L"WWWWWWWW\n");
}
ExitProcess(0);
}
}
非常感谢您花时间回答我。如果有些问题很奇怪,我很抱歉,但我是一名 JS 开发人员,我在这里做的是我的爱好。我所知道的是,与我的 JS 相比,它感觉像是一个不同且更深刻的世界。;)
我确实编辑了代码,还删除了您提到的多余内容。线程的 suspend/resume 在那里,因为它们之间我有一些内存修改,但是根据你所说的,线程在这一点上是挂起的,即使我会修改内存,也不需要它们?
言归正传,死循环还在。我已经添加了 RF 标志,但我才刚刚开始通读相关文章,所以除了添加它之外,我也明白为什么。同时,您能再给我一个提示,说明为什么它可能仍然无法正常工作吗? 此外,我添加了 LOAD_DLL_DEBUG_EVENT 处理程序,并且我将立即关闭处理程序,因为此时我不需要对其执行任何其他操作(是吗?)。我不完全明白的是我应该何时关闭从 CREATE_PROCESS 和 CREATE_THREAD 调试事件接收到的处理程序?我正在努力了解调试器是如何工作的,这是我使用它的第 4 天,但正如我所看到的,这就是发生的事情:
WaitForDebugEvent 接收到一个调试事件,只要不是我的,它就会被 ContinueDebugEvent 和 DBG_EXCEPTION_NOT_HANDLED 处理,所以它被传回并由游戏处理。 最后,WaitForDebugEvent 收到我的调试事件,即 EXCEPTION_SINGLE_STEP,我在那里做我的事情,然后用 DBG_CONTINUE 继续它 - 这将异常标记为由我处理,系统会继续跟踪它。对吗?
我的实际代码仍在无限循环中循环并打印 "Breakpoint":
#include "Header.h"
#include <iostream>
int main() {
hpChangeBreakpoint = 0x013FB279;
SetDebugPrivilege(TRUE);
DWORD dwProcessID = 0;
DWORD dwGame = 0;
printf("Looking for game process...\n");
while (dwProcessID == 0) {
dwProcessID = GetProcessID(L"PathOfExile.exe");
if (dwProcessID != 0)
dwGame = 1;
Sleep(100);
}
printf("dwProcessID = %p\n", dwProcessID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
BOOL bDebugging = DebugActiveProcess(dwProcessID);
printf("bDebugging = %d\n", bDebugging);
DWORD dwThreadID = GetProcessThreadID(dwProcessID);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
CONTEXT context;
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(hThread, &context))
{
context.Dr0 = hpChangeBreakpoint;
context.Dr7 = 0x00000001;
std::cout << "GetThreadContext successful" << std::endl;
SetThreadContext(hThread, &context);
}
DEBUG_EVENT DebugEvent;
BOOL bContinueDebugging = false;
for(;;)
{
WaitForDebugEvent(&DebugEvent, INFINITE);
switch (DebugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
switch (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_SINGLE_STEP:
if (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)
{
#define RESUME_FLAG 0x10000
CONTEXT Context;
Context.ContextFlags = CONTEXT_CONTROL;
Context.EFlags |= RESUME_FLAG;
std::cout << "Breakpoint" << std::endl;
bContinueDebugging = true;
}
if (bContinueDebugging)
{
// DBG_CONTINUE to tell the program we have handled the exception
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_CONTINUE);
bContinueDebugging = false;
}
else // if the exception was not handled by our exception-handler, we want the program to handle it, so..
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
break;
default:
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
break;
case LOAD_DLL_DEBUG_EVENT:
{
std::cout << "load dll debug event" << std::endl;
CloseHandle(DebugEvent.u.LoadDll.hFile);
break;
}
default:
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
}
system("pause>nul");
return 0;
}