盲目性的习惯编写程序,毕竟是很不好的心态;显而已知在这些过程中便形成了编程就变得异常了.之 还是安下心来认真,然而认真还是不够的,今天我便认真阅读和思考了这样一些关于Linux在Windows下实现结构化的异常处理问题;往往这就是我们所突劣的重要所在,比如我们在Windows平台下编写程序,经常会遇到代码 种种原因而发生异常的情况;而Linux下是没有类似的机制的,异常处理例程是是静态包含在内核代码中的; 精确定位出可能产生异常的代码的地址,这样便给异常处理带来很大的麻烦.通常用Linux是开放式的,既然内核自己提供类似的机制,那就让我们自己来实现它. 我们要先认识SEH总名称(Structure Exception Handling) 技术的出现为程序员供了一个标准解决方案,程序员只需要注册好异常处理函数(由编译器负责,通常不是一个完整的函数,而是和可能发生异常的代码在同一个函数中),如果在当前线程的代码引发异常时系统便会自动调用异常处理函数,完成善后工作.要完成SEH工作, 能够拦截异常,拦截之后便转移到相关的代码, 在可能产生异常的代码之前注册异常代码处理例程. 要做到异常拦截,程序员 直接修改IDT表,用自己的GP(general-Protection)异常处理服务代码替换掉内核的异常处理代码就行啦,这个工能很容易实现.以下代码如: 注意:在程序推出时 恢复原来的GP异常入口. 要做到异常处理时, 在自己的异常处理服务程序区检测到相应的异常处理程序是否注册,如果是,那么则恢复现场,然后跳转到注册异常处理程序中运行;如果没有注册就把控制权转交给内核的异常处理程序. 我们在异常处理时把新的异常服务程序分为两个部分,第一部分用汇编语言实现,通常用于保护现场及完成一些对于C代码无法实现的操作,代码如下:
拥有帝国一切,皆有可能。欢迎访问phome.net ,这一部分代码要使用的是机器码而不是汇编代码,是 其中部分代码需要在初始化时动态修改,以机器码的形式写出来便便于计算偏移量.另外也方便在注释中使用了英特尔汇编语言格式,毕竟是AT&T汇编的格式太麻烦.现在我们来解释一下这些代码的作用: 第1、2行,起保护现场,保存所有有可能会破坏到的寄存器. 第3行,EAX指向堆找栈中的异常错误代码. 第4、5行,以指向异常错误代码的指针为参数调第二部分异常处理程序, 汇汇译之前无法确定第二部分异常处理程序的地址, 初始化时需要将第二部分异常处理程序的地址填入0xe8之后的双字中. 第6 行,第二部分异常处理程序执行完毕之后恢复调用前的堆栈. 第7、8 行,确认该异常是否已经处理,如果没有处理则跳转到_call_system_handler(见第15行) 第9、10行,将异常发生时的标志寄存的值复制到第1行保存的标志寄存器的位置. 第11、12行,恢复异常发生前的现场. 第13行,清除堆栈中的错误代码,注意这里绝对不能用add esp,4 此时标志寄存器已经恢复到发生异常的状态,add指令会改变标志寄存器的值,而用pop指令清除堆栈的话没,有地方存放取出的数据, 只能用lea指令. 第14行,以远的调用方式返回被中断代码,这一句非常重要, 是异常处理程序返回被中断代码, 本来应该使用IRET来返回,但异常可能发生在某个子程序中,因此在返回时需要调整堆栈指针, 这里带调整堆栈的RETF指令来完成这一任务,这里RETF附带的参数也需要按每次异常发生是的状态来修正. 第15、16行,如果当前异常不能处理,恢复现场,准备转到系统的异常处理程序. 第17行,跳转到系统的异常处理程序.这里是跳转的地址也是在初始化是设置. 异常处理程序的第二部分,用于搜索注册的异常处理程序链以检查当前异常是否需要由我们处理.如果是,设置返回信息,返回非0值;如果不是,返回0,这样 |