调试线程时无限循环

时间:2016-12-30 12:25:49

标签: c++ windows debugging reverse-engineering

我正在尝试将硬件断点附加到游戏过程中并且我已经成功了。然后我试图遍历异常并等待我放在那里的那个,这也工作正常。问题是它发生后会进入无限循环,我无法制动。你能建议吗? 我这样做的原因是我想在此时停止线程,使用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, &currentHP, 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;

}

当我运行它时,游戏运行正常,直到断点发生,当它发生时,我得到无限的“断点”cout的垃圾邮件,当我调试它时,这一行:                     if(DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress ==(void *)hpChangeBreakpoint)

始终为true,但dwFirstChance标志为1,所以它总是一个新的异常?在这个无限循环中唯一改变的是hThread,总是不同的。我觉得因为缺乏知识而缺少某些东西,因此会感谢任何帮助或暗示正确的方向。谢谢!

2 个答案:

答案 0 :(得分:1)

你是否倾听/了解Resume Flag (RF)?您需要将其设置为步入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

不同

还提供一些优化建议 - 每次OpenThread时都不需要致电EXCEPTION_DEBUG_EVENT

首先你已经有了这个线程句柄

HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
在致电SetThreadContext

之后,

根本不关闭它

并且异常只能在此线程的上下文中发生,所有其他线程不受此影响。

第二个你永远不会关闭在EXCEPTION_DEBUG_EVENT中打开的线程句柄 - 所以你已经有资源泄漏了。

调试器获得CREATE_THREAD_DEBUG_EVENT上的线程句柄和CREATE_PROCESS_DEBUG_EVENT以及必须关闭它(或者只是或通常维护它并关闭EXIT_THREAD_DEBUG_EVENT和{{1} })

你没有处理EXIT_PROCESS_DEBUG_EVENT因此没有关闭文件句柄。

您的代码有巨大的句柄泄漏

SuspendThread / ResumeThread - 为了什么?!绝对无意义 - 线程(以及进程中的所有线程)已经暂停了

LOAD_DLL_DEBUG_EVENT

答案 1 :(得分:0)

非常感谢你花时间回答我。如果有些问题很奇怪,我很抱歉,但我是一个JS开发者,我在这里做的是我的爱好。我所知道的是,感觉就像一个与我的JS不同而且更深刻的世界......;)

我编辑了代码,也删除了你提到的冗余。线程的暂停/恢复是因为它们之间我有一些内存修饰,但根据你的说法,线程在这一点暂停,即使我要修改内存,也没有必要吗? / p>

回到主题,无限循环仍在这里。我添加了RF标志,但我刚刚开始阅读文章,除了添加它之外,我也理解为什么。与此同时,你能不能给我另一个暗示,为什么它可能仍然不起作用? 另外,我已经添加了LOAD_DLL_DEBUG_EVENT处理,我正在关闭处理程序,因为此时我不需要做任何其他事情(我呢?)。我不完全得到的是,当我应该关闭从CREATE_PROCESS和CREATE_THREAD调试事件收到的处理程序时?我试图将我的思想包含在调试器的工作方式中,这是我到目前为止的第四天,但正如我所看到的,这就是发生的事情:

WaitForDebugEvent接收一个调试事件,只要它不是我的,它由ContinueDebugEvent用DBG_EXCEPTION_NOT_HANDLED处理,所以它被传回并且游戏处理它。 最后WaitForDebugEvent接收我的调试事件,即EXCEPTION_SINGLE_STEP,我在那里做我的东西,然后继续使用DBG_CONTINUE - 标记我处理的异常,系统继续跟踪它。这是对的吗?

我的实际代码仍然循环并在无限循环中打印“断点”:

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

}