DLL注入实现子进程Runtime Error检测

     好久之前写的Offline Judge,当时RE评测结果功能的实现使用的是debug api,结果有一个bug,此功能就暂时被删掉了。昨天和学长讨论,说debug api可能会影响运行时间的评测结果,当时我还觉不使用debug api怎么能捕捉到子进程的异常呢。今天突然想到不用debug api的方法:注入dll,挂钩UnhandledExceptionFilter。然而写好之后令我没想到的是,一个7KB的DLL文件注入之后居然多占用了1.5M的内存,这样内存使用的信息也就只能“仅作参考”了。目前我还没有想到解决办法。对于一些对内存使用量不敏感的应用来说,这种方法也可以考虑,当然前提是程序内部没有对异常做出处理。

 

     最开始想到的方法是注入之后调用SetUnhandledExceptionFilter,不过发现居然没用(也许是在我的dll注入进去之后UnhandledExceptionFilter又被改掉了?还是什么?)。然后想到挂钩SetUnhandledExceptionFilter让所有人都靠边站,不过既然这样为何不直接挂钩UnhandledExceptionFilter呢,因为每次异常发生(测试用的是一个人为除零的小程序)都是windows的“xxxx已停止工作……”,这说明最后是UnhandledExceptionFilter得到控制权的(这也是一定的),而且并不是所有未处理异常都会被SetUnhandledExceptionFilter所设置的函数拦截到的。于是,动工。

 

void HookUnhandledExceptionFilter(){
	BYTE fakeEntry[5];
	fakeEntry[0] = 0xE9;
	LPVOID pFunc = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "UnhandledExceptionFilter");
	*((PDWORD)(&fakeEntry[1])) = (DWORD)NewExceptionFilter - (DWORD)pFunc - 5;
	DWORD dwProtect, dwWrite;
	VirtualProtect(pFunc, 5, PAGE_READWRITE, &dwProtect);
	WriteProcessMemory(GetCurrentProcess(), pFunc, fakeEntry, 5, &dwWrite);
	VirtualProtect(pFunc, 5, dwProtect, NULL);
	return 0;
}
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
	switch(reason) {
	case DLL_PROCESS_ATTACH:
		HookUnhandledExceptionFilter();
	break;
	}
	return 1;
}

 

     HookUnhandledExceptionFilter函数把原来的UnhandledExceptionFilter函数的前5个字节改成了跳转。其中的NewExceptionFilter是我们新的UnhandledExceptionFilter函数。

 

     NewExceptionFilter的作用就很简单了,得到ExceptionCode,根据异常类型向主程序传递信息。传递信息这里我是直接通过StdErr实现的,主程序通过匿名管道实现StdErr的重定向,只要检测是否有错误输出就可以知道是否有Runtime Error了。如果是其他应用,使用命名管道之类的手段即可。

    

    这是一种不用debug api来获得子进程异常的方法,也算作是一种思路吧。

posted @ 2012-01-26 19:27  dontpanic  阅读(747)  评论(0编辑  收藏  举报