使用函数钩子实现打印throw异常时的调用栈

13 篇文章 0 订阅
​
#include <Windows.h>
#include <winnt.h>
#include <Dbghelp.h>
#include <excpt.h>
#include <ehdata.h>
#include <exception>
#include <iostream>
#include <thread>

#pragma comment(lib, "Dbghelp.lib")

typedef void(__stdcall* PFNPREPARE_FOR_THROW)(void* ExceptionInfo);

typedef struct WinRTExceptionInfo
{
	void* description;
	void* restrictedErrorString;
	void* restrictedErrorReference;
	void* capabilitySid;
	long                 hr;
	void* restrictedInfo;
	ThrowInfo* throwInfo;
	unsigned int         size;
	PFNPREPARE_FOR_THROW PrepareThrow;
} WINRTEXCEPTIONINFO;

namespace foundation
{
	static void getExceptContext(bool);
	void ReportExcept(unsigned long code, PEXCEPTION_POINTERS excpInfo)
	{
		getExceptContext(true);
	}

#if 0
	__declspec(noreturn) extern "C" void __stdcall
		jCxxThrowException(
			void* pExceptionObject,   // The object thrown
			ThrowInfo * pThrowInfo          // Everything we need to know about it
		)
	{
		static const EHExceptionRecord ExceptionTemplate = { // A generic exception record
			EH_EXCEPTION_NUMBER,            // Exception number
			EXCEPTION_NONCONTINUABLE,       // Exception flags (we don't do resume)
			NULL,                           // Additional record (none)
			NULL,                           // Address of exception (OS fills in)
			EH_EXCEPTION_PARAMETERS,        // Number of parameters
			{
				EH_MAGIC_NUMBER1,           // Our version control magic number
				NULL,                       // pExceptionObject
				NULL,
	#if _EH_RELATIVE_OFFSETS
					NULL                        // Image base of thrown object
	#endif
				}                      // pThrowInfo
		};
		EHExceptionRecord ThisException = ExceptionTemplate;    // This exception

		ThisException.params.magicNumber = EH_MAGIC_NUMBER1;
		ThrowInfo* pTI = (ThrowInfo*)pThrowInfo;

		ThisException.params.pExceptionObject = pExceptionObject;
		ThisException.params.pThrowInfo = pTI;

#if _EH_RELATIVE_OFFSETS
		PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
		ThisException.params.pThrowImageBase = ThrowImageBase;
#endif

		if (pTI != NULL)
		{
			if (THROW_ISPURE(*pTI))
			{
				ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
			}
#if _EH_RELATIVE_OFFSETS
			else if (ThrowImageBase == NULL)
			{
				ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
			}
#endif

		}

#if  (defined(_M_X64) && defined(_NTSUBSET_))
		RtlRaiseException((PEXCEPTION_RECORD)&ThisException);
#else
		RaiseException(ThisException.ExceptionCode,
			ThisException.ExceptionFlags,
			ThisException.NumberParameters,
			(PULONG_PTR)&ThisException.params);
#endif
}


#else
	//平台工具集V142 【vs2019】!!!
	extern "C" __declspec(noreturn) void __stdcall jCxxThrowException(
		void* pExceptionObject, // The object thrown
		void* pThrowInfo  // Everything we need to know about it
	) {
		//EHTRACE_FMT1("Throwing object @ 0x%p", pExceptionObject);
		auto pTI = reinterpret_cast<ThrowInfo*>(pThrowInfo);
		ULONG_PTR magicNumber = EH_MAGIC_NUMBER1;
		if (pTI && (pTI->attributes & TI_IsWinRT)) {
			// The pointer to the ExceptionInfo structure is stored sizeof(void*) in front of each WinRT Exception Info.
			WINRTEXCEPTIONINFO** ppWei = *static_cast<WINRTEXCEPTIONINFO***>(pExceptionObject);
			--ppWei;
			const auto pWei = *ppWei;
			pTI = pWei->throwInfo;
			pWei->PrepareThrow(ppWei);
		}

#if _EH_RELATIVE_TYPEINFO
		void* throwImageBase = RtlPcToFileHeader(const_cast<void*>(static_cast<const void*>(pTI)), &throwImageBase);
#endif // _EH_RELATIVE_TYPEINFO

		// If the throw info indicates this throw is from a pure region,
		// set the magic number to the Pure one, so only a pure-region
		// catch will see it.
		//
		// Also use the Pure magic number on Win64 if we were unable to
		// determine an image base, since that was the old way to determine
		// a pure throw, before the TI_IsPure bit was added to the FuncInfo
		// attributes field.
		if (pTI && ((pTI->attributes & TI_IsPure)
#if _EH_RELATIVE_TYPEINFO
			|| !throwImageBase
#endif // _EH_RELATIVE_TYPEINFO
			)) {
			magicNumber = EH_PURE_MAGIC_NUMBER1;
		}

		// Build the parameters for the EHExceptionRecord:
		const ULONG_PTR parameters[] = {
			magicNumber,
			reinterpret_cast<ULONG_PTR>(pExceptionObject),
			reinterpret_cast<ULONG_PTR>(pTI),
	#if _EH_RELATIVE_TYPEINFO
			reinterpret_cast<ULONG_PTR>(throwImageBase),
	#endif // _EH_RELATIVE_TYPEINFO
		};

		// Hand it off to the OS:
		RaiseException(EH_EXCEPTION_NUMBER, EXCEPTION_NONCONTINUABLE, _countof(parameters), parameters);
	}


#endif
void getSelfContext()
{
	DWORD id = ::GetCurrentThreadId();
	HANDLE h = OpenThread(
		THREAD_ALL_ACCESS,
		TRUE,
		id
	); //获得真实句柄

	std::thread _th([=] {
		::SuspendThread(h);
		CONTEXT ctx = { 0 };
		::GetThreadContext(h, &ctx);
		MINIDUMP_EXCEPTION_INFORMATION eInfo;

		EXCEPTION_POINTERS excpInfo;
		excpInfo.ExceptionRecord = NULL;
		excpInfo.ContextRecord = &ctx;

		eInfo.ThreadId = GetCurrentThreadId(); 
		eInfo.ExceptionPointers = &excpInfo;
		eInfo.ClientPointers = FALSE;

		HANDLE hFile = CreateFile(L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE,
			0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

		::MiniDumpWriteDump(
			GetCurrentProcess(),
			GetCurrentProcessId(),
			hFile,
			MiniDumpNormal,
			&eInfo,
			NULL,
			NULL);

		::CloseHandle(hFile);
		::ResumeThread(h);
		});

	_th.join();
}

void XX()
{
	std::cout << "inside it" << std::endl;
	std::exception exp("what is the matter?");
	throw exp;
}

bool JmpHook(unsigned char* func, unsigned char* dst)
{
	char original_bytes[16] = { 0 };
	DWORD old_protection;
	if (0 == VirtualProtect(func, 1024, PAGE_EXECUTE_READWRITE, &old_protection))
		return false;

	memcpy(original_bytes, dst,  sizeof(void*) == 4 ? 5 : 14);


#if defined(_M_X64) || defined(__amd64__) // x86_64
	func[0] = 0xFF; // absolute jmp
	func[1] = 0x25; // absolute jmp
	*(uint32_t*)(func + 2) = 0;
	*(uint64_t*)(func + 6) = (uint64_t)dst;
#else
	*(unsigned char*)func = (char)0xE9; //relative jmp near instruction
	*(uint32_t*)((unsigned char*)func + 1) = (unsigned char*)dst - (unsigned char*)func - 5;
#endif

#if defined(_WIN32)
	if (!VirtualProtect(func, 1024, old_protection, &old_protection))
	{
		memcpy(func, original_bytes, sizeof(void*) == 4 ? 5 : 14);
		return false;
	}
#endif

	return true;
}

VOID WINAPI CXXThrowExcept(void* a, void* b)
{
	getSelfContext();

	std::exception* e = dynamic_cast<std::exception*>((std::exception*)a); //正确
	if (e)
		std::cout << e->what() << std::endl;

	_JCxxThrowException(a, (ThrowInfo*)b);

}

int main()
{
#ifdef _DEBUG
	auto hRun = GetModuleHandleA("vcruntime140d.dll");
#else
	auto hRun= GetModuleHandleA("vcruntime140.dll");
#endif
	if (nullptr == hRun)
	{
		std::cout << "couldn't find vcruntime140.dll" << std::endl;
		return EXIT_FAILURE;
	}

	auto addr = GetProcAddress(hRun, "_CxxThrowException");
	JmpHook((unsigned char*)addr, (unsigned char*)CXXThrowExcept);

	try
	{
		XX();
	}
	catch(std::exception& e)
	{
		std::cout << e.what() << std::endl;
	}

	return 0;
}

​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue中直接调用数据库信息并显示在前端,一般需要通过后端接口来实现。以下是一个基本的示例流程: 1. 在后端,你可以使用Node.js和Express框架来创建一个API接口。在这个接口中,你可以连接数据库,并编写查询语句来获取所需的数据。 2. 在后端接口中,通过使用数据库的适配器(如MySQL,MongoDB等)来连接数据库。编写查询语句,获取需要的数据。 3. 在后端接口中,将获取的数据作为响应返回给前端。可以使用JSON格式返回数据。 4. 在Vue的前端代码中,可以使用Axios等HTTP库来发送请求到后端接口,获取数据。 5. 在Vue组件中,通过调用后端接口并获取数据,将数据赋值给Vue的数据属性。 6. 在Vue模板中,使用数据绑定语法将数据显示在前端页面上。 以下是一个简单的示例代码: // 后端代码 // 安装依赖 npm install express mysql // app.js const express = require('express'); const mysql = require('mysql'); const app = express(); // 创建数据库连接 const connection = mysql.createConnection({ host: 'localhost', user: 'root', password: 'password', database: 'test' }); // 连接数据库 connection.connect(); // 创建API接口 app.get('/api/data', (req, res) => { // 查询数据库 connection.query('SELECT * FROM data', (error, results) => { if (error) throw error; res.json(results); }); }); // 监听端口 app.listen(3000, () => { console.log('Server is running on port 3000'); }); // 前端代码 <template> <div> <ul> <li v-for="item in items" :key="item.id">{{ item.name }}</li> </ul> </div> </template> <script> import axios from 'axios'; export default { data() { return { items: [] }; }, mounted() { this.fetchData(); }, methods: { fetchData() { axios.get('/api/data') .then(response => { this.items = response.data; }) .catch(error => { console.log(error); }); } } }; </script> 在上述示例中,后端接口使用了Express框架和MySQL数据库。前端代码使用了Vue和Axios库。在mounted生命周期钩子函数中,调用fetchData方法来获取数据,然后将数据赋值给Vue的items属性。在模板中使用v-for指令循环渲染数据。请根据你的具体情况进行修改和适配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值