Malcolm McCaffery
Malcolm McCaffery

Reputation: 2576

Exceptions constantly thrown when launching app with my custom WINAPI Debugger

I'm in the process of experimenting with creating my own custom debugger with C++ in Visual Studio 2017. Testing a few console applications it is fine. However when I launch notepad with it, it is OK until I hit File -> Open dialog and it goes into constant loop with output these two exceptions code and the open dialog box doesn't open:

Exception: 3221356611
Exception: 998

When same process was launched under WinDbg these exceptions didn't occur. The code is compiled as x86 and launching 32-bit process on Windows 10 1803 build 17134.523 x64.

Any suggestions on what might cause these?

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

Upvotes: 1

Views: 579

Answers (1)

RbMm
RbMm

Reputation: 33744

  • 3221356611 this is 0xC0020043 - RPC_NT_INTERNAL_ERROR
  • 998 this is 0x3e6 - ERROR_NOACCESS (Invalid access to memory location)

your main error that you unconditionally return DBG_CONTINUE always. but on EXCEPTION_DEBUG_EVENT you mast return this code only if you handle exception. otherwise (and if dwFirstChance == TRUE so this is first chance exception) you must return DBG_EXCEPTION_NOT_HANDLED. if you return DBG_CONTINUE - the program begin continue execute from current context. if you return DBG_EXCEPTION_NOT_HANDLED - the KiUserExceptionDispatcher will be called in target process, which call RtlDispatchException and where will be called exception handlers. read more - Structured Exception Handling

but because you never return DBG_EXCEPTION_NOT_HANDLED - program exception handlers never called. by your 2 exceptions codes even easy locate place, where this happens: enter image description here

the RpcpRaiseException is called, which internal call RaiseException(ERROR_NOACCESS..) so you view 998 exception. if you return DBG_EXCEPTION_NOT_HANDLED here - application byself handle this exception and never return from RaiseException call. context will be swithched to __except{} block. but because you return DBG_CONTINUE - the RaiseException return control and RpcReportFatalError called, which internal call RaiseException(RPC_NT_INTERNAL_ERROR..) so you and view 3221356611 (0xC0020043)

your next error that you not close hFile on LOAD_DLL_DEBUG_EVENT - When the debugger is finished with this file, it should close the handle using the CloseHandle function. the same error on CREATE_PROCESS_DEBUG_EVENT

also your error in how you begin debug process - you must not use CREATE_SUSPENDED flag, must not use DebugActiveProcess and ResumeThread. you need only set DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS and all. not need create process in suspended state and main not call DebugActiveProcess here. this api call is bad designed, it create additional thread in target process. for say xp at this stage this at all was fatal.

Upvotes: 4

Related Questions