Reputation: 2576
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
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:
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