Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows 7 - CreateProcess w/ DEBUG_PROCESS Access Violation

ok...burned out on this one...scratching my head all day. I have a very simple, single purpose c++ DLL (StartApplication.dll) used to start an application.

  • Works fine in WinXP, but not in win7
  • Uses CreateProcess() with DEBUG_PROCESS (so i can wait for the program to finish before terminating).
  • If I monitor processes in Task Manager, I can see the process start, but no window is created and nothing further happens
  • If I change to NORMAL_PRIORITY_CLASS, the program will execute as it should (but i lose the ability to wait around before terminating as I can only do this while debugging)
  • Error code gives me STATUS_ACCESS_VIOLATION
  • I have UAC turned off and setting the executable to run as Administrator and with WinXP compatibility does nothing

Heres the code. Any thoughts would be greatly appreciated

    //...blah blah...handful of stuff preceding this to set up command line and
    //directories etc....not of use here probably...

SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;

STARTUPINFO si = {0}; si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;

si.hStdOutput = (NULL == stdOutFileName)? INVALID_HANDLE_VALUE :
    ::CreateFile(stdOutFileName, GENERIC_WRITE, FILE_SHARE_READ, &sa
        , CREATE_ALWAYS, 0, NULL);

si.hStdError = (NULL == stdErrFileName)? INVALID_HANDLE_VALUE :
    ::CreateFile(stdErrFileName, GENERIC_WRITE, FILE_SHARE_READ, &sa
        , CREATE_ALWAYS, 0, NULL)        
PROCESS_INFORMATION pi = {0};
if (::CreateProcess(useApplicationName? applicationName : NULL, processCommandLine
    , NULL, NULL, TRUE, /*NORMAL_PRIORITY_CLASS*/DEBUG_PROCESS, NULL, currentDirectory, &si, &pi)) 
{
    BOOL cont = TRUE;
    while (cont) 
    {
        DWORD continueStatus = DBG_CONTINUE;

        DEBUG_EVENT debugEvent = {0};
        if (!::WaitForDebugEvent(&debugEvent, INFINITE)) 
        { 
            errorCode = ErrorCode_Other;
            ::TerminateProcess(pi.hProcess, 0);
            break;
        }
        else
        {
            switch (debugEvent.dwDebugEventCode) 
            {
                case EXCEPTION_DEBUG_EVENT: 
                    switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode) 
                    {
                        case EXCEPTION_ACCESS_VIOLATION:
                        case EXCEPTION_DATATYPE_MISALIGNMENT:
                        case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
                        case EXCEPTION_FLT_DENORMAL_OPERAND: 
                        case EXCEPTION_FLT_DIVIDE_BY_ZERO:
                        case EXCEPTION_FLT_INEXACT_RESULT:
                        case EXCEPTION_FLT_INVALID_OPERATION:
                        case EXCEPTION_FLT_OVERFLOW:
                        case EXCEPTION_FLT_STACK_CHECK:
                        case EXCEPTION_FLT_UNDERFLOW:
                        case EXCEPTION_INT_DIVIDE_BY_ZERO:
                        case EXCEPTION_INT_OVERFLOW:
                        case EXCEPTION_PRIV_INSTRUCTION:
                        case EXCEPTION_IN_PAGE_ERROR:
                        case EXCEPTION_ILLEGAL_INSTRUCTION:
                        case EXCEPTION_NONCONTINUABLE_EXCEPTION:
                        case EXCEPTION_STACK_OVERFLOW:
                        case EXCEPTION_INVALID_DISPOSITION:
                        case EXCEPTION_INVALID_HANDLE:
                            errorCode = ErrorCode_ApplicationException; 
                            *exceptionCode = debugEvent.u.Exception.
                                ExceptionRecord.ExceptionCode;
                            ::TerminateProcess(pi.hProcess, 0);
                            break;
                        default:
                            ;
                    }
                    break;

                case EXIT_PROCESS_DEBUG_EVENT:
                    cont = FALSE;   
                    break;
                default:
                    ;
            } 
            ::ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId
                , continueStatus);
        }    
    }

    ::GetExitCodeProcess(pi.hProcess, reinterpret_cast<LPDWORD>(exitCode));

    ::CloseHandle(pi.hProcess);
    ::CloseHandle(pi.hThread);
}
if (INVALID_HANDLE_VALUE != si.hStdOutput)
    ::CloseHandle(si.hStdOutput);
if (INVALID_HANDLE_VALUE != si.hStdError)
    ::CloseHandle(si.hStdError);

return errorCode;

}

like image 778
0xDEADFACE Avatar asked May 17 '26 15:05

0xDEADFACE


1 Answers

Your code terminates the whole process after the first exception occures. Normally you should let the program take care of exceptions, only if the exception wasn't handled the process will terminate.

To see if an exception was fatal (ie. not handled the first time around) check u.Exception.dwFirstChance:

  • If it's 0, set your error codes accordingly and terminate.
  • otherwise, the exception occured for the first time, you should call ContinueDebugEvent with DBG_EXCEPTION_NOT_HANDLED to pass the exception to the process.

EDIT

If you only want to watch exceptions and not handle them from inside the debugger, you should always continue with DBG_EXCEPTION_NOT_HANDLED.

There's one catch:

Right before the main thread starts, Windows raises an INT3 exception which needs to be passed to the process (DBG_CONTINUE).

Pseudo code:

bool FirstInt3 = true;

while (cont) 
{
    DWORD continueStatus = DBG_EXCEPTION_NOT_HANDLED;

    // ....

    switch(...)
    {
        case EXCEPTION_DEBUG_EVENT: 
            if(!FirstChance)
            {
                // Fatal exception

                // Log Exception that terminated the program here
                // Don't do anything else, Windows automatically terminates the process
                // You will get an EXIT_PROCESS_DEBUG_EVENT on the next event
            }
            switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode) 
            {
                case EXCEPTION_BREAKPOINT:
                    if(FirstInt3)
                    {
                        FirstInt3 = false;
                        continueStatus = DBG_CONTINUE;
                        break;
                    }
                default:
                    // Log ExceptionCode here
                    break;
            }
            break;
    }
like image 121
pezcode Avatar answered May 20 '26 05:05

pezcode