Windows-PWN环境搭建&基础知识

0x00 前言

随队参加了不少比赛,发现pwn题越来越丰富了,人也越来越自闭了,Windows Pwn好像也越来越多了,这里进行总结供今后参考。

先贴一个winpwn和传统linux下pwn的区别:

0x01 环境搭建

成功的在windbg中调试程序,不过貌似有些问题。然而对于gdb,到最后我也没能在exp中起gdb,尝试过用gdb进行attach,但是执行一阵就退出,且执行过程中无法看到汇编指令,这东西也已经困扰我好几天了,最后只能退一步利用IDA来进行调试了,总不能一直卡在这里,其实IDA也挺不错,至于gdb以后再说吧。环境搭建就不详细说了,因为本人的也不很成功,利用windbg+IDA进行调试。
在这里推荐一位师傅的文章:
Windows Pwn环境搭建

0x02 Windows Pwn基础知识

2.1 Windows程序保护机制

2.1.1 基本保护机制

ALSR:
与Linux相同,地址随机化技术(Address Space Layout Randomization),启动时讲DLL随机的加到内存中。ASLR技术在windows10开始默认启动。

High Entropy VA:
这个保护被称为高熵64位地址空间布局随机化,一旦开启,表示此程序的地址随机化的取值空间为64 bit,这会导致攻击者更难去推测随机化后的地址。

Force Integrity:
这个保护被称为强制签名保护,一旦开启,表示此程序加载时需要验证其中的签名,如果签名不正确,程序将会被阻止运行。

Isolation:
这个保护被称为隔离保护,一旦开启,表示此程序加载时将会在一个相对独立的隔离环境中被加载,从而阻止攻击者过度提升权限。

PAE/NX/DEP:
DEP类似Linux下的NX保护机制,这项技术使操作系统能够将一页或多页内存标记为不可执行,从而防止从该内存区域运行代码,以帮助防止利用缓冲区溢出。它帮助防止代码在数据页面(例如堆,栈和内存池)中运行,在Windows中常称为DEP(数据执行保护,即Data Execution Prevention),同时引入了一个新的机制被称为PAE(物理地址扩展,即Physical Address Extension),PAE是一项处理器功能,使x86处理器可以在部分Windows版本上访问4 GB以上的物理内存。在基于x86的系统上运行的某些32位版本的Windows Server可以使用PAE访问最多64 GB或128 GB的物理内存,具体取决于处理器的物理地址大小。使用PAE,操作系统将从两级线性地址转换转换为三级地址转换。两级线性地址转换将线性地址拆分为三个独立的字段索引到内存表中,三级地址转换将其拆分为四个独立的字段:一个2位的字段,两个9位的字段和一个12位的字段。PAE模式下的页表条目(PTE)和页目录条目(PDE)的大小从32位增加到64位。附加位允许操作系统PTE或PDE引用4 GB以上的物理内存。同时,PAE将允许在基于x64的系统上运行的32位Windows中启用DEP等功能。

SEHOP:
即结构化异常处理保护(Structured Exception Handling Overwrite Protection),这个保护能够防止攻击者利用结构化异常处理来进行进一步的利用。

CFG:
即控制流防护(Control Flow Guard),这项技术通过在间接跳转前插入校验代码,检查目标地址的有效性,进而可以阻止执行流跳转到预期之外的地点, 最终及时并有效的进行异常处理,避免引发相关的安全问题。简单的说,就是在程序间接跳转之前,会判断这个将要跳转的地址是否是合法的。

RFG:
即返回地址防护(Return Flow Guard),这项技术会在每个函数头部将返回地址保存到fs:[rsp] (Thread Control Stack),并在函数返回前将其与栈上返回地址进行比较,从而有效阻止了这些攻击方式。

SafeSEH:
即安全结构化异常处理(Safe Structured Exception Handlers),这项技术可以理解为一个白名单版的安全沙箱,它会事先为你定义一些异常处理程序,并基于此构造安全结构化异常处理表,程序正式运行后,安全结构化异常处理表之外的异常处理程序将会被阻止运行。

GS:
这个保护类似于Linux中的Canary保护,一旦开启,会在返回地址和BP之前压入一个额外的Security Cookie。系统会比较栈中的这个值和原先存放在.data中的值做一个比较。如果两者不吻合,说法栈中发生了溢出。

Authenticode:签名保护。

.NET:DLL混淆级保护。

2.2.2 结构化异常处理(SEH机制)

2.2.2.1 简介

结构化异常处理是Windows操作系统上Microsoft对C/C++程序语言做的语法扩展,用于处理异常事件的程序控制结构。异常事件是指打断程序正常执行流程的不在期望之中的硬件、软件事件

硬件异常是CPU抛出的如“除0”、数值溢出等;软件异常是操作系统与程序通过RaiseException语句抛出的异常。

Microsoft扩展了C语言的语法,用try-except与try-finally语句来处理异常。异常处理程序可以释放已经获取的资源、显示出错信息与程序内部状态供调试、从错误中恢复、尝试重新执行出错的代码或者关闭程序等等。一个try语句不能既有except,又有__finally。但try-except与try-finally语句可以嵌套使用。

2.2.2.2 SEH相关的重要结构体:

TIB结构体TIB(Thread Information Block,线程信息块)是保存线程基本信息的数据结构,它存在于x86的机器上,它也被称为是Win32的TEB(Thread Environment Block,线程环境块)。TIB/TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TIB/TEB。

TEB结构位于Windows.h,内容如下:

typedef struct _TEB
{ 
    PVOID Reserved1[12]; 
    PPEB ProcessEnvironmentBlock; 
    PVOID Reserved2[399]; 
    BYTE Reserved3[1952]; 
    PVOID TlsSlots[64]; 
    BYTE Reserved4[8]; 
    PVOID Reserved5[26]; 
    PVOID ReservedForOle; 
    PVOID Reserved6[4]; 
    PVOID TlsExpansionSlots; 
} TEB, *PTEB;

线程信息块TIB(Thread Information Block或TEB):

typedef struct _NT_TIB
{ 
    struct _EXCEPTION_REGISTRATION_RECORD *Exceptionlist; // 指向当前线程的 SEH 
    PVOID StackBase; // 当前线程所使用的栈的栈底 
    PVOID StackLimit; // 当前线程所使用的栈的栈顶 
    PVOID SubSystemTib; // 子系统 
    union { 
        PVOID FiberData; 
        ULONG Version; 
    }; 
    PVOID ArbitraryUserPointer; 
    struct _NT_TIB *Self; //指向TIB结构自身 
} NT_TIB;

Fs:[0]总是指向当前线程的TIB,其中0偏移的指向线程的异常链表,即ExceptionList是指向异常处理链表(EXCEPTION_REGISTRATION结构)的一个指针。

_EXCEPTION_REGISTRATION_RECORD结构体:

该结构体主要用于描述线程异常处理句柄的地址,多个该结构的链表描述了多个线程异常处理过程的嵌套层次关系。结构体内容:

// Code in https://source.winehq.org/source/include/winnt.h#2623 
typedef struct _EXCEPTION_REGISTRATION_RECORD
{ 
    struct _EXCEPTION_REGISTRATION_RECORD *Next; // 指向下一个结构的指针 
    PEXCEPTION_ROUTINE Handler; // 当前异常处理回调函数的地址 
}EXCEPTION_REGISTRATION_RECORD;

2.2.2.3 线程异常处理

2.2.2.3.1 线程异常处理特点

①Windows系统为每个线程单独提供了一种异常处理的方法,当一个线程出现错误时,操作系统调用用户定义的一系列回调函数,在这些回调函数中,可以进行修复错误或其它的一些操作,最后的返回值告系统系统下一步的动作(如继续搜索异常处理程序或终止程序等)。  
②SEH是基于线程的,使用SEH可以为每个线程设置不同的异常处理程序(回调函数)而且可以为每个线程设置多个异常处理程序。  
③ 由于SEH使用了与硬件平台相关的数据指针,所以不同硬件平台使用SHE的方法有所不同。

2.2.2.3.2 回调函数原型
EXCEPTION_DISPOSITION __cdecl _except_handler(
     struct _EXCEPTION_RECORD *ExceptionRecord,//指向包含异常信息的EXCEPTION_RECORD结构
     void* EstablisherFrame,//指向该异常相关的EXCEPTION_REGISTRATION结构
     struct _CONTEXT *ContextRecord,//指向线程环境CONTEXT结构的指针
     void* DispatcherContext){ //该域暂无意义

     ……

    //4种返回值及含义
    //1.ExceptionContinueExecution(0):回调函数处理了异常,可以从异常发生的指令处重新执行。
    //2.ExceptionContinueSearch(1):回调函数不能处理该异常,需要要SEH链中的其他回调函数处理。
    //3.ExceptionNestedException(2):回调函数在执行中又发生了新的异常,即发生了嵌套异常
    //4.ExceptionCollidedUnwind(3):发生了嵌套的展开操作
    return}
2.2.2.3.3 注册异常回调

push _exception_handler //异常回调函数_exception_handler的地址,即handler
push fs:[0]             //保存前一个异常回调函数的地址,即prev
mov fs:[0],esp          //安装新的EXCEPTION_REGISTRATION结构(两个成员:prev,handler)。
                        //此时栈顶分别是prev和handler,为新的EXCEPTION_REGISTRATION结
                        //构,mov fs:[0],esp,就可以让fs:[0]指向该指构。
2.2.2.3.4 异常回调函数的调用过程

①线程信息块(TIB),永远放在fs段选择器指定的数据段的0偏移处,即fs:[0]的地方就是TIB结构。对不同的线程fs寄存器的内容有所有不同,但fs:[0]都是指向当前线程的TIB结构体,所以fs:[0]是一个EXCEPTION_REGISTRATION结构体的指针。
②当异常发生时,系统从fs:[0]指向的内存地址处取出ExceptionList字段,然后从ExceptionList字段指向的EXCEPTION_REGISTRATION结构中取出handler字段,并根据其中的地址去调用异常处理程序(回调函数)。

2.2.2.3.5 SEH链及异常的传递

通知调试器→SEH链→顶层异常处理→系统默认处理

(1)系统查看产生异常的进程是否被正在被调试,如果正在被调试,那么向调试器发送EXCEPTION_DEBUG_EVENT事件。
(2)如果进程没有没有被调试或者调试器不去处理这个异常,那么系统检查异常所处的线程并在这个线程环境中查看fs:[0]来确定是否安装SEH异常处理回调函数,如果有则调用它。
(3)回调函数尝试处理这个异常,如果可以正确处理的话,则修正错误并将返回值设置为ExceptionContinueExecution,这时系统将结束整个查找过程(4)如果回调函数返回ExceptionContinueSearch,相当于告诉系统它无法处理这个异常,系统将根据SEH链中的prev字段得到前一个回调函数地址并重复步骤3,直至链中的某个回调函数返回ExceptionContinueExection为止,查找结束。
(5)如果到了SEH链的尾部却没有一个回调函数愿意处理这个异常,那么系统会再被检查进程是否正在被调试,如果被调试的话,则再一次通知调试器。(6)如果调试器还是不去处理这个异常或进程没有被调试,那么系统检查有没有Final型的异常处理回调函数,如果有,就去调用它,当这个回调函数返回时,系统会根据这个函数的返回值做相应的动作。
(7)如果没有安装Final型回调函数,系统直接调用默认的异常处理程序终止进程,但在终止之前,系统再次调用发生异常的线程中的所有异常处理过程,目的是让线程异常处理过程获得最后清理未释放资源的机会,其后程序终止。

2.2.2.3.6 异常处理流程及全局展开

2.2.3 新机制–导入表和导出表

Windows程序没有延迟绑定机制自然也就没有PLT/GOT表,但是Windows程序显然也是要调用所谓的库函数的,Windows下的函数库是DLL文件,类似于Unix下的libc文件,程序调用库函数需要借助的就是导入表和导出表了。

导入表是PE数据组织中的一个很重要的组成部分,它是为实现代码重用而设置的。通过分析导入表数据,可以获得诸如PE文件的指令中调用了多少外来函数,以及这些外来函数都存在于哪些动态链接库里等信息。Windows加载器在运行PE时会将导入表中声明的动态链接库一并加载到进程的地址空间,并修正指令代码中调用的函数地址。在数据目录中一共有四种类型的数据与导入表数据有关: 导入表、导入函数地址表、绑定导入表、延迟加载导入表。

程序中,导入表的地址通常位于.idata段

参考链接:

结构化异常SEH处理机制详细介绍
SCTF 2020 EasyWinHeap 入门 Windows Pwn
Windows Pwn 环境搭建


 Previous
WinPwn入门之-HITB-Babystack WinPwn入门之-HITB-Babystack
0x00 前言学习Windows Pwn的第二步,感觉不baby啊。 0x01 查看文件利用checksec脚本查看保护机制 后来发现更好的查看文件保护机制的方法:winchecksec 我们看到DEP开启也就是说数据不可执行,地址随
Next 
2019-TSCTF-babykernel 2019-TSCTF-babykernel
0x00 前言这道题是2019-TSCTF的一道kernel题,出题人是17学长,拿过来假装是参加了2019 TSCTF的比赛。题目链接提取码:12wp 0x01 查看文件在windows中解压img文件,便可以得到里面的cpio文件。利用
  TOC