Hello,大家好,我又来摸鱼了,来看这个的大部分人都是用易语言的吧,所以我就用易语言的定义来表示吧,照顾大部分人。当然本人不是专业人士,就一摸鱼外行,难免有亿些大错误,希望各位大佬们指正错误(别骂人),好了话不多说,直接切入话题。
一.什么是异常?
异常通俗点来讲,就是让你程序莫名其妙崩溃的东西,比如访问了一个不存在的内存地址,写入数据到不可写区域,或者是主动抛出异常。当然了并不是所有的崩溃都是异常(栈溢出等等)。
二.什么是异常捕获?
为什么抛出异常后,程序会崩溃呢?因为你的异常没有人去处理,只能交给系统去管,系统没办法,就只能干掉你的程序,从根本上解决问题咯(好主意)。那么有没有办法不崩溃?有,将异常给拦截,交给程序去处理,也就是所谓的异常处理。常见的有:调试器处理,结构异常处理(she),还有全局异常捕获函数,这里我们就从最简单的函数异常处理开始学习。
三.SetUnhandledExceptionFilter
设置异常处理的api就是头上这个,我们看看api定义,另外后面内存地址我统一用指针来概述,这样省点字数
.版本 2
.DLL命令 SetUnhandledExceptionFilter, 整数型, “Kernel32.dll”, “SetUnhandledExceptionFilter”
.参数 异常处理函数, 整数型
所以使用方法如下
我们可以测试下效果,自己制造一个空指针异常,置入汇编代码:mov [0],eax
即:置入代码({163,0,0,0,0})
编译(不要易语言调试,因为调试器优先级高,所以调试器处理不了就崩溃了),然后打开程序测试,我们会发现信息框是弹出来了,但是程序依旧崩溃,那是因为我仅仅对异常进行了捕或,而没有进行处理,所以最终任务还是丢给操作系统了。
四.异常处理
捕获的异常进了我们的函数,当然就该我们函数去处理啦,我相信你一定很好奇,为什么我要给处理函数定义一个变量,其实这个变量存放的就是异常信息指针。这个指针的数据结构大概如图
.版本 2
.数据类型 EXCEPTION_POINTERS
.成员 pExceptionRecord, 整数型
.成员 pContextRecord, 整数型
那为什么我参数不直接定义这个数据类型?因为易语言憋屈啊,参数传递只能用基本数据类型(呜呜呜),所以我们为了我们后续的方便,我们最好定义一个EXCEPTION_POINTERS局部变量,然后把参数指针指向的数据复制给我们定义的局部变量。
后面要用的api
.版本 2
.DLL命令 RtlMoveMemory_EXCEPTION_RECORD, , , “RtlMoveMemory”
.参数 Dest, EXCEPTION_RECORD, 传址
.参数 src, 整数型
.参数 Len, 整数型
.版本 2
.DLL命令 RtlMoveMemory_寄存器, , , “RtlMoveMemory”
.参数 Destination, CONTEXT, 传址
.参数 Source, 整数型
.参数 Length, 整数型
.DLL命令 RtlMoveMemory_异常信息, , , “RtlMoveMemory”
.参数 Destination, EXCEPTION_RECORD, 传址
.参数 Source, 整数型
.参数 Length, 整数型
部分代码
如果不懂易语言传址的意义请参考:https://blog.csdn.net/zcp528/article/details/106936630
因为EXCEPTION_POINTERS有存放的是两个整型指针,所以数据总长度是8
我们再来说说另外另个结构EXCEPTION_RECORD和CONTEXT
.版本 2
.数据类型 EXCEPTION_RECORD
.成员 ExceptionCode, 整数型
.成员 ExceptionFlags, 整数型
.成员 pExceptionRecord, 整数型
.成员 ExceptionAddress, 整数型, 传址, , 该地址发生异常。
.成员 NumberParameters, 整数型
.成员 ExceptionInformation, 整数型, , “15”
.版本 2
.数据类型 CONTEXT
.成员 ContextFlags, 整数型, , , 0
.成员 Dr0, 整数型, , , 4
.成员 Dr1, 整数型, , , 8
.成员 Dr2, 整数型, , , 12
.成员 Dr3, 整数型, , , 16
.成员 Dr6, 整数型, , , 20
.成员 Dr7, 整数型, , , 24
.成员 FloatSave, FLOATING_SAVE_AREA, , , 136
.成员 SegGs, 整数型, , , 140
.成员 SegFs, 整数型, , , 144
.成员 SegEs, 整数型, , , 148
.成员 SegDs, 整数型, , , 152
.成员 Edi, 整数型, , , 156
.成员 Esi, 整数型, , , 160
.成员 Ebx, 整数型, , , 164
.成员 Edx, 整数型, , , 168
.成员 Ecx, 整数型, , , 172
.成员 Eax, 整数型, , , 176
.成员 Ebp, 整数型, , , 180
.成员 Eip, 整数型, , , 184
.成员 SegCs, 整数型, , , 188
.成员 EFlags, 整数型, , , 192
.成员 Esp, 整数型, , , 196
.成员 SegSs, 整数型, , , 200
.成员 ExtendedRegisters, 字节型, , “512”
EXCEPTION_RECORD这个结构里面存放的是异常信息,比如异常代码之类的,你经常可以看到0xC000000X就是从中获取的
而context保存的有一个标识,具体用处我也不大了解,其他就存放的是寄存器:硬件断点寄存器,标志寄存器,段寄存器,扩展寄存器,浮点数寄存器。而具体偏移备注已经标明。
我这里的处理异常思路很简单,就是跳过mov [eax],0指令,因为这条指令一个占5个字节,所以我们只需要在原有的EIP基础上加上5就可以跳过
为什么我要绕来绕去,用写到内存进行赋值。。。。。。这就只能怪易语言了,指针操作的灵活性真的不能和C比,他局部变量的数据好像是clone过来的,而且加减法操作会自动把数据给你转成浮点数,所以我就加了个到整数(寄存器.eip+5)。
为什么要返回-1呢?这里我直接复制资料吧,里面的数字是我自己备注的,如有错误,请纠正
异常捕获函数有三种返回值
(代表整数1)EXCEPTION_EXECUTE_HANDLER:表明异常处理完毕,程序可以退出
(代表整数0)EXCEPTION_CONTINUE_EXECUTION:忽略此异常,从异常点继续运行。如果此时再发生异常,还会调用异常处理函数
(代表-1)EXCEPTION_CONTINUE_SEARCH:异常没被识别,交由上一级处理函数处理;