windows内核研究(软件调试-异常的处理流程)
软件调试
异常的处理流程
在程序是以调试状态下发生异常时,一但程序发生异常,首先接管异常的不是异常处理程序而是我们的调试器
测试代码:
#include<iostream>
#include<windows.h>int main() {int x = 100;int y = 0;_try{int z = x / y; // 除0的异常std::cout << "无法执行的代码 " << z << std::endl;}_except(1){std::cout << "SEH异常处理代码" << std::endl;}system("pause");return 0;
}
在我们正常执行这个程序时,会打印SEH异常处理代码,这符合我们的预期,但是一但有调试器在调试这个程序时,它就会把这个异常先抛给调试器
在x64dbg中可以选择让调试器来处理异常,这样即使是我们程序当中有对应的异常处理程序也不会去执行
我们来梳理一下异常的处理流程
- 当发生异常时,首先会判断是否有调试器,如果有就发送给调试器(第一次)
- 如果调试器不处理,寻找异常处理器,处理了程序就继续运行
- 如果我们没有写对应的异常处理函数,调试器又没有处理异常,此时异常处理器会有最后一道防线
UnhandledExceptionFilter
- UnhandledExceptionFilter会通过NtQueryinformationProcess查询当前进程是否正在被调试,如果是,返回EXCEPTION_CONTINUE_SEARCH,进入第二轮分发(如果没有调试则通过SetunhandledExceptionFilter注册异常处理函数,有就调用,如果没有则终止程序)
利用UnhandledExceptionFilter来反调试
#include<iostream>
#include<windows.h>int g_num = 0;LONG NTAPI TopLevelExcepFilter(PEXCEPTION_POINTERS pExcepInfo ) {std::cout << "异常修复!\n";g_num = 1; // 把异常处理让程序正常运行return EXCEPTION_CONTINUE_EXECUTION;
}int main() {int x = 100;int i = 0;// 这里只有在不被调试的情况下才会执行SetUnhandledExceptionFilter(&TopLevelExcepFilter);int z = x / g_num;std::cout << "程序正常运行!\n";for (;;){Sleep(1000);printf("%d\n", ++i);}system("pause");return 0;
}
效果
使用调试器加载则无法运行