2016-12-30 2 views
2

Я пытаюсь подключить контрольную точку оборудования к игровому процессу, и я добился успеха. Затем я пытаюсь пройти через Исключения и дождаться того, что я там вложил, что тоже отлично работает. Проблема в том, что после этого она переходит в бесконечный цикл, который я не могу тормозить. Вы можете посоветовать? Причина, по которой я это делаю, заключается в том, что я хочу остановить поток в этот момент, прочитать значение EAX с помощью контекста, а затем продолжить процесс.Бесконечный цикл при отладке нити

Header.h включает функции, которые вызывают здесь, и все они работают нормально, поэтому Im не включает его в этот момент.

#include "header.h" #include

INT основной() {

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; 

}

Когда я запускаю его, игра работает отлично, пока точка останова не происходит, когда он делает, я получаю бесконечный спам «breakpoint» cout, и когда я его отлаживаю, эта строка: if (DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress == (void *) hpChangeBreakpoint)

всегда верно, но флаг dwFirstChance равен 1, поэтому его всегда является новым исключением? Единственное, что меняется в этом бесконечном цикле, это hThread, всегда другое. Я чувствую, что я что-то теряю из-за своей нехватки знаний, поэтому буду благодарен за любую помощь или намеки в правильном направлении. Спасибо!

ответ

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 ядро ​​- поиск 0x003E0DD7DWORD в Ntoskrnl код и заменить его на 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 и MUST закрыть его (или просто или обычно поддерживать его и близко от 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); 
    } 
} 
0

Большое спасибо за время, которое ты провел ответив мне. Извините, если некоторые из вопросов странные, но Im JS dev и то, что я здесь делаю, - это мое хобби. Я знаю, что это похоже на другой и более глубокий мир, чем мой JS ..;)

Я действительно редактировал код, также удалил то, что вы упоминали, является избыточным. Временное/возобновление потока было там, потому что между ними у меня были некоторые модификации памяти, но, основываясь на том, что вы сказали, что нить приостановлена ​​на этом этапе, даже если бы я изменил память, им не нужно?

Возвращаясь к теме, бесконечный цикл все еще здесь. Я добавил флагов РФ, но я только начинаю читать статьи, поэтому, кроме добавления, я также понимаю, почему. Между тем, вы бы так любезны дать мне еще один намек на то, почему он все еще не может работать? Кроме того, я добавил обработку LOAD_DLL_DEBUG_EVENT, и я немедленно закрываю обработчик, так как мне не нужно делать что-либо еще с этим на данный момент (я?). Я не полностью понимаю, когда я должен закрыть обработчики, полученные из событий CREATE_PROCESS и CREATE_THREAD debug? Я пытаюсь обратить внимание на то, как работает отладчик, мой 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; 

}

+0

«Возвращаясь к теме, бесконечный цикл до сих пор здесь Ive добавил RF флаг» - но вы не называете «SetThreadContext» !! какой смысл просто инициализировать «КОНТЕКСТ Контекст» - без вызова «SetThreadContext» - исправить это первым, для устранения основной проблемы. если вы не работаете под XP. «что нить приостановлена ​​на этом этапе» - до того, как вы вызове ContinueDebugEvent - весь процесс приостановлен - вам не нужно вызывать 'SuspendThread' /' ResumeThread' «даже если бы я изменил память, нет необходимости в них? " - да, не нужно – RbMm

+0

О, моя собака, такая очевидная :) Позвольте мне поближе познакомиться! Спасибо большое RbMm! – MTM

+0

Похоже, он проходит через помощника! :) Спасибо! Есть еще одна проблема - после прохождения этого исключения есть EXCEPTION_ACCESS_VIOLATION, если я не справляюсь с этим, после второй попытки это приведет к сбою приложения. Может ли это быть связано с тем, что я делаю? – MTM

Смежные вопросы