易语言关于SEH异常处理

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:异常没被识别,交由上一级处理函数处理;

前言 本程序基于东灿的异常调试模块5.2部分版权信息如下: 1. 该模块功能是补足易错误管理器无法捕捉的异常。 2. 本模块可以截获许多异常,比如内存读写错误,非法汇编指令,算术异常,其他异常等等。 3. 本模块拥有try/catch的异常处理模型,支持多层嵌套,支持向上处理。允许用户自己设定跳过致命异常。 4. 并且支持易原错误管理器接管 5. 建议配合配套工具使用更加好用 正文: 程序原使用模块: 模块名 作者或工作室 用处 高级ApiHookEx 作者未知 HookApi,在模块中用于Hook:CreateRemoteThreadEx和CreateRemoteThread AppThreadVar 作者未知 进程内变量,线程内变量。可以在任意ec模块,任意DLL内调用,全部共享 callstack 作者未知 函数调用堆栈跟踪。 disasm 作者未知 支持MMX/SSE/SSE2/SSE3/3DNow取CPU指令长度 GetThisModuleHandle 作者未知 取出当前ModuleHandle e-try/catch(即new_SEH) 星锋工作室-东灿 SEH结构体等 精简内容 简化模块(即多模块合一,但互相不影响,并且删去了重复的API,数据类型等) 增加注释:对于代码,追加了更多的注释。 部分原理理解(很多都是本人自己的理解,可能不对,若有错误,欢迎指出):AppThreadVar:工作核心:tls线程局部存储(缩写为TLS。进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。) 部分功能:它主要是为了避免多个线程同时访存同一全局变量或者静态变量时所导致的冲突,尤其是多个线程同时需要修改这一变量时。为了解决这个问题,我们可以通过TLS机制,为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。而从全局变量的角度上来看,就好像一个全局变量被克隆成了多份副本,而每一份副本都可以被一个线程独立地改变。 用途:动态TLS和静态TLS这两项技术在创建DLL的时候更加有用,这是因为DLL通常并不知道它们被链接到的应用程序的结构是什么样的。1. 如果应用程序高度依赖全局变量或静态变量,那么TLS可以成为我们的救生符。因而最好在开发中最大限度地减少对此类变量的使用,更多的依赖于自动变量(栈上的变量)和通过函数参数传入的数据,因为栈上的变量始终都是与某个特定的线程相关联的。如果不使用此类变量,那么就可以避免使用TLS。2. 但是在编写应用程序时,我们一般都知道自己要创建多少线程,自己会如何使用这些线程,然后我们就可以设计一些替代方案来为每个线程关联数据,或者设计得好一点的话,可以使用基于栈的方法(局部变量)来为每个线程关联数据 TLS分动态和静态,AppThreadVar我认为是动态TLS(调用了TlsAlloc函数) 相当于静态变量(我也只能理解到这里了) ApiHookEx和大部分的APIHook工作原理类似 e-try/catch说实话,这个模块我几乎不能理解原理(能理解的基本上是一些Api,比如MiniDumpWriteDump) 这个可能也是用于捕获异常,而且比较神奇的是,这个捕获异常以后不会导致程序的退出,而是程序能够正常运行! 可以抛出自定义数值异常,由catch_int/catch_eq_int接收 可以抛出最后一次异常,让上层错误处理器处理 部分实例: try/catch测试:try/catch结果: 部分崩溃代码测试: 结果部分: 程序可以自己写出DMP文件: 更多功能请自己测试!!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值