关于seh的那些事一
seh是英文structured exception handing的简写,叫做结构化异常,我觉得更精确的描述应该是线程异常处理回调,因为seh是在每个线程之间独立的异常处理方法,基本上,每个高级语言都会有seh的应用,比如delphi的异常处理就是try..except,很遗憾的事,易语言并没有,但是毛太祖说过,自己动手,丰衣足食.那么,我们今天就通过易语言内联汇编建立一个易语言的try..except模型
建立seh的模型我们就先要知道seh的一些特点,1.seh是基于线程的,所以我们可以为每个线程设置不同的的异常处理程序 2.seh可以在一个线程多次安装,那么,这个线程碰到异常后,按照最后安装的seh最先执行的顺序.
怎么安装seh呢?W32系统为每个线程定义了一个线程信息结构,这个结构叫NT_TIB结构
NT_TIB struct
004 线程堆栈顶部
008 线程堆栈底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在内存中的镜像地址,线程TEB结构地址
020 进程PID
024 线程ID
02C 指向线程局部存储指针
030 PEB结构地址(进程结构)
034 上个错误号
NT_TIB end
这里最重要是第一个字段,这个字段就是SEH链指针,这个指针指向一个exception_registration结构,这个结构可以自由定义,但是前2字段是固定的
exception_registration struct
prev 上一个SEH链指针
handler 异常处理函数地址
...........后面字段可以根据自己需要自己定义
exception_registration end
这里我们看到了 只要在一个线程里填充exception_registration这个结构,就可以注册一个seh异常处理了,那么exception_registration这个结构的地址在哪里,也就是TIB的第一个字段[向SEH链指针]是多少呢?
答案是fs:[0].
FS段选择器指定数据段的0偏移处为TIB的第一个字段,同理,fs:[20]为进程PID,fs:[24]为线程序PID,为fs:[18]等于线程环境块,teb结构里有一些很有趣的东西,我们先不说,我们写个验证程序试验下
代码如下:
.版本 2
.子程序 测试
.局部变量 局_变量一
.局部变量 局_进程PID
.局部变量 局_线程PID
局_变量一 = 0
局_进程PID = 0
局_线程PID = 0
' _asm
' {
' mov eax, [fs:0]
' mov [ebp-04h],eax
' mov eax, [fs:20h]
' mov [ebp-08h],eax
' mov eax, [fs:24h]
' mov [ebp-0ch],eax
' }
信息框 (局_变量一, 0, )
信息框 (局_进程PID, 0, “我是进程PID”)
信息框 (局_线程PID, 0, “我是线程PID”)
说明这个结构是对.
现在安装一个seh就很简单了,就是申请一块内存,填充一个exception_registration结构,然后把地址写入FS[0]处.现在基本都用堆栈构造exception_registration结构,好处是遍于封装,那么我们也用堆栈来构造
push 异常处理地址
push fs[0] 上一个异常处理指针
mov fs:[0],esp
把这2个推入堆栈后,然后把esp放入FS段选择器指定数据段的0偏移处
这样就注册了一个seh
用汇编注册seh是非常简单的事情,易语言的难点在于重定位,我们下面来分析下易语言的写法.
我先把易语言代码发出来,再逐一解释
.版本 2
.子程序 子程序_模拟try
' _asm{
' pushad
' call my
' my:
' pop eax ;自定位
' lea ecx, dword [eax+safe-6]
' push ecx
' lea ecx,dword [eax+handle-6]
' push ecx
' push dword [fs:0]
' mov dword [fs:0], esp
' xor eax,eax
' mov [eax],eax
' jmp safe
' handle:
' pushad
' mov edi,[ebp+10h]
' mov eax,[ebp+0ch]
' push dword [eax+8h]
' pop dword [edi+0B8h]
' popad
' mov eax,0
' ret 10h
' safe:
' mov esp, dword [fs:0]
' pop dword [fs:0]
' add esp, 8h
' popad
' }
这一段就是易语言内联汇编写的模拟try..except的模型,我们下面来分析下
首先.第一个难点,要解决重定位的问题,我们首先
pushad
call my
my:
pop eax ;自定位
利用CALL的特性,把pop eax 处的地址弹入eax,那么
asm{处的地址就是eax-6,因为pushad call my包含6字节
下面
lea ecx, dword [eax-6+safe]
push ecx '推入安全地址safe
lea ecx,dword [eax-6+handle]
push ecx '推入seh处理地址handle
push dword [fs:0] '推入原seh结构地址
mov dword [fs:0], esp 把构造seh结构的堆栈放入fs加0偏移处,注册seh
易语言注册seh的办法,这里我们扩充了exception_registration结构,除开必须要的2个字段外,我们增加了一个1个安全地址,safe,也就是,现在exception_registration结构是这样的
exception_registration struct
prev 上一个SEH链指针
handler 异常处理函数地址
safe 安全的地址
exception_registration end
这里说明下,易语言内联汇编关于标号,是相对便移,也就是相对asm{开始处的便移,所以我们先进行了一个自定位,然后eax-6+safe计算寻找到了真实地址
xor eax,eax
mov [eax],eax
jmp safe
这里构造了一个读写异常,也就是如果我们要写一些要被保护的代码的话,在这里写,写完后跳到safe安全地址去执行.
如果发生了异常.进入hanld处理函数.
handle:
pushad '保护积存器
mov edi,[ebp+10h] '第三个参数,寄存器context地址
mov eax,[ebp+0ch] '第二个参数,exception_registration地址
push dword [eax+08h] 'exception_registration地址+8,这里就是我们自定义的safe地址了
pop dword [edi+0B8h] 'safe地址等于eip,[edi+0B8h]等于eip
popad '保护积存器
mov eax,0
ret 10h '恢复堆栈平衡
seh的异常回调,有4个参数.
类似这样:
context这里是个结构,寄存器结构,好东西.我们说下lpseh,这个参数指向注册回调函数时候使用的exception_registration结构的地址
那么,我们知道,我们注册的exception_registration结构里,有个安全地址,也就是,我们把安全地址等于context结构的eip,然后返回0,就可以走到安全位置去了.
safe:
mov esp, dword [fs:0]
pop dword [fs:0]
add esp, 8h
popad
撤消注册的seh,恢复堆栈平衡,我们在注册的时候用了3个push,由于撤消seh要pop一个堆栈,那么还有2个push,我们要平衡,所以add,esp,8h,然后恢复被保存的寄存器.注(pop eax ;自定位,这里跟call myl平衡了堆栈,call=push返回地址 jmp目标地址)
整个易语言的try..except的模型就说完了,一个是重定位问题,堆栈还要注意平衡,再就是exception_registration这个结构我们自己在固定的结构后面增加了一个safe字段,整个模型没有用到一个全局变量,都在堆栈里进行,方便封装成模块或者作为模型.
posted on 2013-12-28 19:00 shellcode 阅读(2018) 评论(0) 编辑 收藏 举报