c++ - 调试线程时无限循环

标签 c++ windows debugging reverse-engineering

我正在尝试将硬件断点附加到游戏进程,我成功了。然后我试图遍历异常并等待我放在那里的异常,它也工作正常。问题是,在它发生之后,它进入了我无法刹车的无限循环。你能建议吗? 我这样做的原因是我想在此时停止线程,使用 Context 读取 EAX 值,然后继续该过程。

Header.h 包括在这里调用的函数,它们都工作正常,因此我现在不包括它。

#include "Header.h" #包括

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 垃圾邮件,当我调试它时,这一行: 如果 (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void*)hpChangeBreakpoint)

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

最佳答案

你听/知道 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_EVENTCREATE_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);
    }
}

关于c++ - 调试线程时无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41396240/

相关文章:

c++ - 有没有办法传递函数模板作为另一个函数中的参数?

debugging - 通过KGDB进行模块调试

java - eclipse中F11和Ctrl+F11的区别?

linux - 难以理解一个简单的 shell 脚本

c# - 关闭 dll 中的 Win32 对话框时出现异常(来自 WPF 应用程序)

c++ - 运算符重载以构造异常消息

c++ - 使用 Boost::Spirit::X3 解析复数

c++ - 在eclipse中隐藏控制台

PHP | Powershell脚本输出数据

windows - 为所有用户存储注册表数据的位置