2008年4月20日

SEH原理追踪

附件
我也来分析看雪的seh实例程序。当然第一步是ollydbg载入啦

入口

00401000 > $ 8D4424 F8 lea eax, dword ptr [esp-8] ;;把esp-8后的地址传给 eax,lea的 ;;用法,别把[]当成取值啦,这里这样用是提前把esp指向
;;_EXCEPTION_REGISTRATION结构地址,当然下面就是在堆栈中去构造那个结构,参考图1,2

00401004 . 64:8705 00000>xchg dword ptr fs:[0], eax ;;交换指令,eax存放旧的fs[0], 现在fs:[0]被替换,为我们提前准备的地址
0040100B . BB 2E104000 mov ebx, 0040102E ;;开始构造,放我们的异常处理函数
00401010 . 53 push ebx ;;push一下, [esp-4] : handler
00401011 . 50 push eax ;;push两下, [esp-8] : pre
00401012 . BE 00000000 mov esi, 0 ;;构造异常,esi放入0地址
00401017 . 8B06 mov eax, dword ptr [esi] ;;取0地址的值,当然出错啦,相当于在 ;;c语言里这样用: int a = *0;
;;正常情况不会到这里了,直接到我们定义的异常处理函数地址
00401019 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
0040101B . 68 00304000 push 00403000 ; |Title = "OK"
00401020 . 68 10304000 push 00403010 ; |Text = "SEH Fail"
00401025 . 6A 00 push 0 ; |hOwner = NULL
00401027 . E8 1C000000 call ; \MessageBoxA
0040102C . EB 13 jmp short 00401041
0040102E . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401030 . 68 00304000 push 00403000 ; |Title = "OK"
00401035 . 68 03304000 push 00403003 ; |Text = "SEH Succeed "
0040103A . 6A 00 push 0 ; |hOwner = NULL
0040103C . E8 07000000 call ; \MessageBoxA
00401041 > 6A 00 push 0 ; /ExitCode = 0
00401043 . E8 06000000 call ; \ExitProcess
00401048 $- FF25 08204000 jmp dword ptr [<&USER32.MessageBoxA>>; USER32.MessageBoxA
0040104E .- FF25 00204000 jmp dword ptr [<&KERNEL32.ExitProces>; kernel32.ExitProcess

很明显是masm的,注释在代码中



图1, 先指向esp-8















图 2 push两下放好数据









OK,异常产生了,单步过这一句,od提示访问[00000000]异常,在跟壳的时候貌似经常出现这些访问异常


00401017 . 8B06 mov eax, dword ptr [esi]

shift+f7

到了ntdll区域,看od提示

7C92EAEC > 8B4C24 04 mov ecx, dword ptr [esp+4]
7C92EAF0 8B1C24 mov ebx, dword ptr [esp] ;;停在这里,可见F7的作用,当然就是step了一下
7C92EAF3 51 push ecx
7C92EAF4 53 push ebx
7C92EAF5 E8 C78C0200 call 7C9577C1
7C92EAFA 0AC0 or al, al
7C92EAFC 74 0C je short 7C92EB0A
7C92EAFE 5B pop ebx


这个时候看堆栈,换堆栈了,这个堆栈地址应该是内核堆栈,
OD ALT+M可以看到主线程的堆栈是

0012D000 - 00130000

看看有些什么东西,下面就很枯燥了,一堆结构指针以及数据,不过用来做坏事相当有用哦(@_@)

0012FCCC 0012FCD4 ;; 指向EXCEPTION_RECORD,不就在下面吗
0012FCD0 0012FCF0 ;; 指向EXCEPTION_CONTEXT,EXCEPTION_RECORD结构之后
0012FCD4 C0000005 ;; 异常代码,windows.inc 定义的STATUS_开头的类型, 这里就是读取内存 ;; 异常,还有很多,暂时不管,你脱壳时迟早会遇到,到时有兴趣再查一样
0012FCD8 00000000 ;; 异常标识,0--可修复, 1-不可修复, 2-正在展开,不要试图修复什 ;; 么,需要的话,释放必要的资源
0012FCDC 00000000 ;; 指向另一个EXCEPTION_RECORD结构
0012FCE0 00401017 Seh.00401017 ;; 异常发生的地址,被抓住了吧
0012FCE4 00000002 ;; ExceptionInformation所含有的dword数目
0012FCE8 00000000 ;; ExceptionInformation 1
0012FCEC 00000000 ;; ExceptionInformation 2
0012FCF0 0001003F ;; EXCEPTION_CONTEXT结构, ContextFlags
0012FCF4 00000000 ;; iDr0
0012FCF8 00000000 ;; iDr1
0012FCFC 00000000 ;; iDr2
0012FD00 00000000 ;; iDr3
0012FD04 00000000 ;; iDr6
0012FD08 00000000 ;; iDr7
0012FD0C FFFF027F ;; FloatSave 1C - 88h, 这里就是 0012FD0C - 0012FD78
;;不重要,略去
0012FD7C 00000000 ;; Gs
0012FD80 0000003B ;; Fs
0012FD84 00000023 ;; Es
0012FD88 00000023 ;; Ds
0012FD8C 7C930738 ntdll.7C930738 ;; Edi
0012FD90 00000000 ;; Esi
0012FD94 0040102E Seh.0040102E ;; Ebx
0012FD98 7C92EB94 ntdll.KiFastSystemCallRet ;; Edx
0012FD9C 0012FFB0 ;; Ecx
0012FDA0 0012FFE0 ;; Eax
0012FDA4 0012FFF0 ;; Ebp
0012FDA8 00401017 Seh.00401017 ;; Eip,指向的是发生异常的地址
0012FDAC 0000001B ;; Cs
0012FDB0 00010383 ;; Flag
0012FDB4 0012FFBC ;; Esp
0012FDB8 00000023 ;; Ss
0012FDBC 0000027F
0012FDC0 00000000

结构分析完了, 现在继续走, 就是处理这些数据了

7C92EAF3 51 push ecx ;;0012FCF0, 看上面的堆栈,EXCEPTION_CONTEXT结构地址
7C92EAF4 53 push ebx ;;0012FCD4, EXCEPTION_RECORD结构指针
7C92EAF5 E8 C78C0200 call 7C9577C1 ;;如果f8过,会出现SEH succeed 提示窗口,即执行了程序 ;;自己的异常代码,F7跟进追踪
7C92EAFA 0AC0 or al, al
7C92EAFC 74 0C je short 7C92EB0A
7C92EAFE 5B pop ebx
7C92EAFF 59 pop ecx

;; 到这里
7C9577C1 8BFF mov edi, edi ; ntdll.7C930738
7C9577C3 55 push ebp
7C9577C4 8BEC mov ebp, esp
7C9577C6 83EC 64 sub esp, 64
7C9577C9 56 push esi
7C9577CA FF75 0C push dword ptr [ebp+C] ;;ebp+4返回地址,+8参数1地址。+C参数2地址
7C9577CD 8B75 08 mov esi, dword ptr [ebp+8]
7C9577D0 56 push esi
7C9577D1 C645 FF 00 mov byte ptr [ebp-1], 0
7C9577D5 E8 C2FFFFFF call 7C95779C ;; 跟进, 没干什么, 检测 7C99C320 地址是否指向 7C99C320??
7C9577DA 84C0 test al, al
7C9577DC 0F85 84720100 jnz 7C96EA66
7C9577E2 53 push ebx ;; 0012FCD4, EXCEPTION_RECORD结构指针
7C9577E3 8D45 F4 lea eax, dword ptr [ebp-C] ;; 局部变量3 ,ebp = 0012FCBC ,存放线程堆栈地址上限
7C9577E6 50 push eax
7C9577E7 8D45 F8 lea eax, dword ptr [ebp-8] ;; local2, 存放下限
7C9577EA 50 push eax
7C9577EB E8 1CC1FCFF call 7C92390C ;; 取线程堆栈地址范围, 小技巧, dd跟随ebp地址, dd跟随fs指向的地址, 寄存器窗口上有
7C9577F0 E8 38C1FCFF call 7C92392D ;; mov eax, dword ptr fs:[0]
7C9577F5 8365 08 00 and dword ptr [ebp+8], 0 ;; 参数1清零
7C9577F9 8BD8 mov ebx, eax ;; 后面是一系列检测地址是否合法,可以略看,大概是检测堆栈地址,SEH异常处理函数地址
7C9577FB 83FB FF cmp ebx, -1
7C9577FE 0F84 8F000000 je 7C957893
7C957804 57 push edi
7C957805 3B5D F8 cmp ebx, dword ptr [ebp-8]
7C957808 ^ 0F82 1D32FFFF jb 7C94AA2B
7C95780E 8D43 08 lea eax, dword ptr [ebx+8]
7C957811 3B45 F4 cmp eax, dword ptr [ebp-C]
7C957814 ^ 0F87 1132FFFF ja 7C94AA2B
7C95781A F6C3 03 test bl, 3
7C95781D ^ 0F85 0832FFFF jnz 7C94AA2B
7C957823 8B43 04 mov eax, dword ptr [ebx+4]
7C957826 3B45 F8 cmp eax, dword ptr [ebp-8]
7C957829 72 09 jb short 7C957834
7C95782B 3B45 F4 cmp eax, dword ptr [ebp-C]
7C95782E ^ 0F82 F731FFFF jb 7C94AA2B
7C957834 50 push eax ;; 异常处理函数地址
7C957835 E8 67000000 call 7C9578A1 ;; 可以看见利用fs一系列指针,fs:[18]即为fs指向的地址
;; fs:[30],貌似指向PEB,PEB:[C]又是什么,继续偏移+18,找到ImageBase,再取入口地址,来检测异常处理函数地址^_^!!,真不嫌麻烦
下一个call继续跟进,得到NTHEADER地址

7C9579D1 E8 738EFDFF call RtlImageNtHeader

;;后面不太清楚在干啥,反正是系统API就直接F8,是call xxxxxxxx这种就F7进, 否则跑飞了又要重来了,还是很痛苦滴

7C95783A 84C0 test al, al
7C95783C ^ 0F84 E931FFFF je 7C94AA2B


当到了这里时

7C923799 55 push ebp
7C92379A 8BEC mov ebp, esp
7C92379C FF75 0C push dword ptr [ebp+C]
7C92379F 52 push edx
7C9237A0 64:FF35 0000000>push dword ptr fs:[0]
7C9237A7 64:8925 0000000>mov dword ptr fs:[0], esp ; 替换fs:[0]了,指向我们定义的地址
7C9237AE FF75 14 push dword ptr [ebp+14] ; 参数4 _lpDispatchrContext ?,大概放了线程堆栈范围
7C9237B1 FF75 10 push dword ptr [ebp+10] ; 参数3 _lpDContext,指向Context结构
7C9237B4 FF75 0C push dword ptr [ebp+C] ; 参数2 _lpSEH ,指向ERR结构
7C9237B7 FF75 08 push dword ptr [ebp+8] ; 参数1 _lpExceptionRecord ,指向ExceptionRecord结构
7C9237BA 8B4D 18 mov ecx, dword ptr [ebp+18] ;; 取我们定义的地址,到这里可以看下堆栈
7C9237BD FFD1 call ecx ;; 直接去执行了
7C9237BF 64:8B25 0000000>mov esp, dword ptr fs:[0]
7C9237C6 64:8F05 0000000>pop dword ptr fs:[0]
7C9237CD 8BE5 mov esp, ebp
7C9237CF 5D pop ebp
7C9237D0 C2 1400 retn 14


相关数据结构

;//================================以下是两个成员的详细结构

EXCEPTION_RECORD STRUCT
ExceptionCode DWORD ? ;//异常码
ExceptionFlags DWORD ? ;//异常标志
pExceptionRecord DWORD ? ;//指向另外一个EXCEPTION_RECORD的指针
ExceptionAddress DWORD ? ;//异常发生的地址
NumberParameters DWORD ? ;//下面ExceptionInformation所含有的dword数目
ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORD ENDS ;//EXCEPTION_MAXIMUM_PARAMETERS ==15

;//================================具体解释========================================

ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多
的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:

C0000005h----读写内存冲突
C0000094h----非法除0
C00000FDh----堆栈溢出或者说越界
80000001h----由Virtual Alloc建立起来的属性页冲突
C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常
C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,
如果RtlUnwind时没有Exception Record参数也同样会填入这个代码
80000003h----调试时因代码中int3中断
80000004h----处于被单步调试状态

注:也可以自己定义异常代码,遵循如下规则:
_____________________________________________________________________+

位: 31~30 29~28 27~16 15~0
_____________________________________________________________________+
含义: 严重程度 29位 功能代码 异常代码
0==成功 0==Mcrosoft MICROSOFT定义 用户定义
1==通知 1==客户
2==警告 28位
3==错误 被保留必须为0
ExceptionFlags 异常标志
0----可修复异常
1----不可修复异常
2----正在展开,不要试图修复什么,需要的话,释放必要的资源
pExceptionRecord 如果程序本身导致异常,指向那个异常结构
ExceptionAddress 发生异常的eip地址
ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下
第一个dword 0==读冲突 1==写冲突
第二个dword 读写冲突地址
;//================================解释结束========================================
off.
CONTEXT STRUCT ; _
ContextFlags DWORD ? ; | +0
iDr0 DWORD ? ; | +4
iDr1 DWORD ? ; | +8
iDr2 DWORD ? ; >调试寄存器 +C
iDr3 DWORD ? ; | +10
iDr6 DWORD ? ; | +14
iDr7 DWORD ? ; _| +18
FloatSave FLOATING_SAVE_AREA <> ;浮点寄存器区 +1C~~~88h
regGs DWORD ? ;--| +8C
regFs DWORD ? ; |\段寄存器 +90
regEs DWORD ? ; |/ +94
regDs DWORD ? ;--| +98
regEdi DWORD ? ;____________ +9C
regEsi DWORD ? ; | 通用 +A0
regEbx DWORD ? ; | 寄 +A4
regEdx DWORD ? ; | 存 +A8
regEcx DWORD ? ; | 器 +AC
regEax DWORD ? ;_______|___组_ +B0
regEbp DWORD ? ;++++++++++++++++ +B4
regEip DWORD ? ; |控制 +B8
regCs DWORD ? ; |寄存 +BC
regFlag DWORD ? ; |器组 +C0
regEsp DWORD ? ; | +C4
regSs DWORD ? ;++++++++++++++++ +C8
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
;//================================以上是两个成员的详细结构



没有评论: