How to intercept all calls to AfxThrowMemoryException

395 views Asked by At

I have a large MFC program. We have rare situations where a customer gets an CMemoryException.

The problem is, that we get the exception but not the location were the exception was thrown.

I can intercept the IAT (Import Address Table) but in this situation I can only detect calls made from my application into the MFC DLL or from other DLLs into the MFC DLL.

How can I intercept all calls the AfxThrowMemoryException? So all calls from within the MFC DLL also can be trapped by me.

In fact I don't know the internal address of the function I want to trap. OK I can use the IAT and calculate the internal address.

I know Detours, but I wouldn't like to deliver it with my software too..

Or is there any simpler method to rap the throw operation inside the C++ code?

Best would be, that I can trapp any exception before they are thrown. So I can see the caller code.

1

There are 1 answers

0
xMRi On

I replaced the function header itself. For the debug version there was an additional redirection. The following code works.

Just implement you own MyAfxThrow...Exception function. Should have the same signature like the AfxThrow... functions.

typedef void (WINAPI *PFN_VOID)();
auto RedirectExceptionHandler = [](PFN_VOID pOld, PFN_VOID pNew) -> bool
{
    // Get the real address of the there might be 1 or 2 indirections
    BYTE* p = reinterpret_cast<BYTE*>(pOld);

    // Debug version starts here. We have a Jump Relative first
    //  00CDF86F E9 39 AD 06 01       jmp         AfxThrowMemoryException(01D4A5ADh)
    if (*p == 0xE9)
    {
        // Get the relative jump address
        int offset = *reinterpret_cast<int*>(p + 1);
        // Calculate the new physical address
        p = p + offset + 5;
    }

    // Release starts here. We have a JP 
    //  01D4A5AD FF 25 2C 15 17 02    jmp         dword ptr[__imp_AfxThrowMemoryException(0217152Ch)]
    if (*p != 0xFF && *(p + 1) != 25)
        // Unexpected OP-Code
        return false;

    // Get the offset where the pointer is stored
    p = *reinterpret_cast<BYTE**>(p + 2);

    // Get the pointer to the execution address.
    BYTE* pCode = *reinterpret_cast<BYTE**>(p);

    // Code before the patch
    //  790319D0 55                   push        ebp
    //  790319D1 8B EC                mov         ebp, esp
    //  790319D3 51                   push        ecx
    //  790319D4 C7 45 FC CC CC CC CC mov         dword ptr[ebp - 4], 0CCCCCCCCh

    MEMORY_BASIC_INFORMATION mbi;
    if (VirtualQuery(pCode, &mbi, sizeof(MEMORY_BASIC_INFORMATION)))
    {
        // Try to change the page to be writable if it's not already
        if (VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect))
        {
            // Code after the patch
            //  790319D0 68 70 1A 7B 01       push        17B1A70h  
            //  790319D5 C3                   ret  

            // Set the new target address
            pCode[0] = 0x68; // PUSH <address>
            *reinterpret_cast<void**>(pCode + 1) = reinterpret_cast<void*>(pNew);
            pCode[5] = 0xC3; // RET

            // Restore the old flag on the page
            VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect);
            return true;
        }
        else
        {
            // Can't change protection.
            ASSERT(FALSE);
            return false;
        }
    }
    else
        return false;
};

// Replace AfxThrow...Exception with MyAfxThrow...Exception
bool bSuccess = true;
bSuccess &= RedirectExceptionHandler(AfxThrowMemoryException      , MyAfxThrowMemoryException);
bSuccess &= RedirectExceptionHandler(AfxThrowResourceException    , MyAfxThrowResourceException);
bSuccess &= RedirectExceptionHandler(AfxThrowInvalidArgException  , MyAfxThrowInvalidArgException);
bSuccess &= RedirectExceptionHandler(AfxThrowNotSupportedException, MyAfxThrowNotSupportedException);