c++ - 如何拦截对 AfxThrowMemoryException 的所有调用
问题描述
我有一个大型 MFC 程序。我们很少有客户获得CMemoryException
.
问题是,我们得到了异常,但没有抛出异常的位置。
我可以拦截 IAT(导入地址表),但在这种情况下,我只能检测从我的应用程序到 MFC DLL 或从其他 DLL 到 MFC DLL 的调用。
如何拦截所有呼叫AfxThrowMemoryException
?所以来自 MFC DLL 的所有调用也可以被我捕获。
实际上我不知道我要捕获的函数的内部地址。好的,我可以使用 IAT 并计算内部地址。
我知道 Detours,但我也不想用我的软件来交付它。
或者有什么更简单的方法可以在 C++ 代码中调用 throw 操作?
最好的是,我可以在抛出任何异常之前捕获它们。所以我可以看到调用者代码。
解决方案
我替换了函数头本身。对于调试版本,有一个额外的重定向。以下代码有效。
只需实现您自己的 MyAfxThrow...Exception 函数。应该具有与 AfxThrow... 函数相同的签名。
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);
推荐阅读
- sql - 单个外键的多个表
- java - 如何在java中编写单元测试以加载文件
- sql - 如何在 ms 访问中使用 UNION 连接两个不同的表
- regex - 如何通过ANT中的正则表达式获取索引
- c++ - 如何编译静态 OpenCV4 库?
- laravel - laravel phpunit 未知数据库':内存:
- java - 将依赖项从 Glide 3.7 更新到 Glide 4.9.0 后,错误提示“无法解析符号 Glide”
- python - 使用 pyodbc 与 sql server 2008 r2 连接,SQL Server 不存在或访问被拒绝
- laravel-5.4 - 同时更新2个表列,laravel
- python - 如何在Qtablewidget PYQT中禁用除一列之外的所有列-python