При запуске приложения с моим пользовательским отладчиком WINAPI постоянно возникают исключения - PullRequest
0 голосов
/ 18 января 2019

Я нахожусь в процессе эксперимента по созданию собственного отладчика на C ++ в Visual Studio 2017. Тестирование нескольких консольных приложений - это нормально. Однако, когда я запускаю блокнот с ним, все в порядке, пока я не нажму File -> Open, и он не перейдет в постоянный цикл с выводом этих двух кодов исключений, и диалоговое окно open не открывается:

Exception: 3221356611
Exception: 998

Когда тот же процесс был запущен под WinDbg, эти исключения не возникали. Код скомпилирован как x86 и запускает 32-битный процесс в Windows 10 1803 build 17134.523 x64.

Любые предложения о том, что может вызвать это?

#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <iostream>
#include <map>

std::map < LPVOID, std::wstring > DllNameMap;

int main()
{
    std::wstring filename(L"c:\\windows\\syswow64\\notepad.exe");
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    DEBUG_EVENT debugEvent;

    // Start the child process. 
    if (!CreateProcess(NULL,   // No module name (use command line)
        (LPWSTR)filename.c_str(),           // Command line
        NULL,               // Process handle not inheritable
        NULL,               // Thread handle not inheritable
        FALSE,              // Set handle inheritance to FALSE
        CREATE_SUSPENDED,   // No creation flags
        NULL,               // Use parent's environment block
        NULL,               // Use parent's starting directory 
        &si,                // Pointer to STARTUPINFO structure
        &pi)                // Pointer to PROCESS_INFORMATION structure
        )
    {
        printf("CreateProcess failed (%d).\n", GetLastError());
        return -1;
    }

    if (DebugActiveProcess(pi.dwProcessId))
    {
        ResumeThread(pi.hThread);

        std::cout << "Debugger attached!" << std::endl;
        EnterDebugLoop(&debugEvent,pi.hProcess);
    }
    return 0;
}

void EnterDebugLoop(const LPDEBUG_EVENT DebugEv,HANDLE hProcess)
{

    DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation 

    for (;;)
    {
        // Wait for a debugging event to occur. The second parameter indicates
        // that the function does not return until a debugging event occurs. 

        WaitForDebugEvent(DebugEv, INFINITE);

        // Process the debugging event code. 

        switch (DebugEv->dwDebugEventCode)
        {
        case EXCEPTION_DEBUG_EVENT:
            // Process the exception code. When handling 
            // exceptions, remember to set the continuation 
            // status parameter (dwContinueStatus). This value 
            // is used by the ContinueDebugEvent function. 

            std::cout << "Exception: " << DebugEv->u.Exception.ExceptionRecord.ExceptionCode << std::endl;

            switch (DebugEv->u.Exception.ExceptionRecord.ExceptionCode)
            {
            case EXCEPTION_ACCESS_VIOLATION:
                std::cout << "ACCESS VIOLATION" << std::endl;
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                break;

            case EXCEPTION_BREAKPOINT:

                std::cout << "BREAKPOINT" << std::endl;
                // First chance: Display the current 
                // instruction and register values. 
                break;

            case EXCEPTION_DATATYPE_MISALIGNMENT:
                std::cout << "DATATYPE MISALIGNMENT" << std::endl;
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                break;

            case EXCEPTION_SINGLE_STEP:
                std::cout << "SINGLE STEP" << std::endl;
                // First chance: Update the display of the 
                // current instruction and register values. 
                break;

            case DBG_CONTROL_C:
                std::cout << "CTRL+C" << std::endl;
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                break;

            default:
                    // Handle other exceptions. 
                break;
            }

            break;

        case CREATE_THREAD_DEBUG_EVENT:
            std::cout << "Create Thread" << std::endl;
            // As needed, examine or change the thread's registers 
            // with the GetThreadContext and SetThreadContext functions; 
            // and suspend and resume thread execution with the 
            // SuspendThread and ResumeThread functions. 
            break;

        case CREATE_PROCESS_DEBUG_EVENT:
            std::cout << "Create Process" << std::endl;
            // As needed, examine or change the registers of the
            // process's initial thread with the GetThreadContext and
            // SetThreadContext functions; read from and write to the
            // process's virtual memory with the ReadProcessMemory and
            // WriteProcessMemory functions; and suspend and resume
            // thread execution with the SuspendThread and ResumeThread
            // functions. Be sure to close the handle to the process image
            // file with CloseHandle.

            //dwContinueStatus = OnCreateProcessDebugEvent(DebugEv);
            break;

        case EXIT_THREAD_DEBUG_EVENT:
            // Display the thread's exit code. 
            std::cout << "Exit Thread Exit Code " << DebugEv->u.ExitThread.dwExitCode << std::endl;


            //dwContinueStatus = OnExitThreadDebugEvent(DebugEv);
            break;

        case EXIT_PROCESS_DEBUG_EVENT:
            // Display the process's exit code. 
            std::cout << "Exit process Exit Code " << DebugEv->u.ExitProcess.dwExitCode << std::endl;
            ///dwContinueStatus = OnExitProcessDebugEvent(DebugEv);
            break;

        case LOAD_DLL_DEBUG_EVENT:
        {
            PVOID pDllPath = NULL;
            PUCHAR DllPath[(MAX_PATH + 1) * sizeof(WCHAR)];
            DWORD dwLen = 0;
            ZeroMemory(DllPath, sizeof(DllPath));

            if (DebugEv->u.LoadDll.lpImageName == NULL)
            {
                break;
            }

            // read DLL name pointer value
            if (ReadProcessMemory(
                hProcess,
                DebugEv->u.LoadDll.lpImageName,
                &pDllPath, sizeof(PVOID),
                &dwLen) && pDllPath)
            {
                dwLen = (DebugEv->u.LoadDll.fUnicode ? MAX_PATH * sizeof(WCHAR) : MAX_PATH);

                // read DLL name
                if (ReadProcessMemory(
                    hProcess,
                    pDllPath,
                    DllPath, dwLen,
                    &dwLen))
                {
                    char szDllPath[MAX_PATH], *lpszDllName = NULL;

                    if (DebugEv->u.LoadDll.fUnicode)
                    {
                        std::wstring path((wchar_t*)DllPath);
                        DllNameMap.insert(std::make_pair(DebugEv->u.LoadDll.lpBaseOfDll, path));
                        std::wcout << "Image loaded (Unicode): " << path.c_str() << std::endl;
                    }
                    else
                    {
                        // todo: Add to DllNameMAp
                        std::wcout << "Image loaded: " << DllPath << std::endl;

                    }
                }
                else
                {
                    std::cout << "Error processing memory : " << GetLastError() << std::endl;
                }
            }
            else
            {
                std::wcout << "ERROR reading process memory : " << GetLastError() << std::endl;
            }
        }
            // Read the debugging information included in the newly 
            // loaded DLL. Be sure to close the handle to the loaded DLL 
            // with CloseHandle.

            ///dwContinueStatus = OnLoadDllDebugEvent(DebugEv);
            break;

        case UNLOAD_DLL_DEBUG_EVENT:
            std::wcout << "Unload DLL: " << DllNameMap[DebugEv->u.UnloadDll.lpBaseOfDll] << std::endl;
            break;

        case OUTPUT_DEBUG_STRING_EVENT:
            // Display the output debugging string. 
            std::wcout << "Debug Event" << std::endl;
            if (DebugEv->u.DebugString.fUnicode)
            {
                std::wcout << (wchar_t)DebugEv->u.DebugString.lpDebugStringData << std::endl;
            }

            //dwContinueStatus = OnOutputDebugStringEvent(DebugEv);
            break;

        case RIP_EVENT:
            //dwContinueStatus = OnRipEvent(DebugEv);
            break;
        }

        // Resume executing the thread that reported the debugging event. 

        ContinueDebugEvent(DebugEv->dwProcessId,
            DebugEv->dwThreadId,
            dwContinueStatus);
    }
}

1 Ответ

0 голосов
/ 18 января 2019
  • 3221356611 это 0xC0020043 - RPC_NT_INTERNAL_ERROR
  • 998 это 0x3e6 - ERROR_NOACCESS ( Неверный доступ к памяти место )

ваша основная ошибка, которую вы безоговорочно возвращаете DBG_CONTINUE всегда. но EXCEPTION_DEBUG_EVENT вы должны возвращать этот код, только если вы обрабатываете исключение. в противном случае (и если dwFirstChance == TRUE, так что это исключение первого шанса), вы должны вернуть DBG_EXCEPTION_NOT_HANDLED. если вы вернете DBG_CONTINUE - программа начнет выполнение с текущего контекста. если вы вернете DBG_EXCEPTION_NOT_HANDLED - KiUserExceptionDispatcher будет вызываться в целевом процессе, который вызывает RtlDispatchException и где будут вызываться обработчики исключений. подробнее - Структурированная обработка исключений

но поскольку вы никогда не вернете DBG_EXCEPTION_NOT_HANDLED - обработчики исключений программы никогда не вызывались. по вашим 2 кодам исключений даже легко найти место, где это происходит: enter image description here

вызывается RpcpRaiseException, внутренний вызов RaiseException(ERROR_NOACCESS..), поэтому вы просматриваете исключение 998. если вы вернете DBG_EXCEPTION_NOT_HANDLED здесь - приложение само по себе обработает это исключение и никогда не вернется из RaiseException вызова. контекст будет переключен на блок __except{}. а потому что вы возвращаете DBG_CONTINUE - управление возвратом RaiseException и вызов RpcReportFatalError, какой внутренний вызов RaiseException(RPC_NT_INTERNAL_ERROR..) так вы и просматриваете 3221356611 (0xC0020043)

ваша следующая ошибка, что вы не закрыли hFile на LOAD_DLL_DEBUG_EVENT - Когда отладчик завершит работу с этим файлом, он должен закрыть дескриптор с помощью функции CloseHandle. та же ошибка на CREATE_PROCESS_DEBUG_EVENT

также ваша ошибка в том, как вы начинаете процесс отладки - вы не должны использовать флаг CREATE_SUSPENDED, не должны использовать DebugActiveProcess и ResumeThread. вам нужно только установить DEBUG_PROCESS или DEBUG_ONLY_THIS_PROCESS и все. не нужно создавать процесс в приостановленном состоянии, а главное не вызывать DebugActiveProcess здесь. этот вызов API плохо спроектирован, он создает дополнительный поток в целевом процессе. скажем, хр на этом этапе это вообще было фатально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...