首页
论坛
专栏
课程

[原创]看雪2019CTF第二场第一题神秘来信:SEH异常处理--MysteriousLetter2

2019-6-22 22:47 3231

[原创]看雪2019CTF第二场第一题神秘来信:SEH异常处理--MysteriousLetter2

2019-6-22 22:47
3231

先看程序主逻辑

这里可以看到要求输入六位的序列号,最后三位是353,然后前三位的和是5.
但是我们并没有发现success的提示。
我们搜索并定位

可以看到success的提示在except里面

这里我们要提到SEH(结构化异常处理),这是Windows下的一种异常处理机制。

简单介绍一下SEH。

参考加密与解密

  • 功能
    SEH实际包含两个主要功能:结束处理(termination handling)和异常处理(exception handling)
    每当你建立一个try块,它必须跟随一个finally块或一个except块。
    一个try块之后不能既有finally块又有except块。但可以在try-except块中嵌套try-finally块,反过来 也可以。
    __try,__finally关键字用来标出结束处理程序两段代码的轮廓
    不管保护体(try块) 是如何退出的。不论你在保护体中使用return,还是goto,或者是longjump,结束处理程序 (finally块)都将被调用。
    在try使用__leave关键字会引起跳转到try块的结尾
  • TIB结构:在用户模式下,TIB(ThreadInformationBlock)位于TEB的头部。而TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TEB。
    nt!_TEB
     +0x000 NtTib            : _NT_TIB
     +0x01c EnvironmentPointer : Ptr32 Void
    
    我们看一下TIB的结构
    typedef struct _NT_TIB          //sizeof  1ch
    {
    00h   struct _EXCEPTION_REGISTRATION  *ExceptionList;          //SEH链入口
    04h   PVOID                            StackBase;              //堆栈基址
    08h   PVOID                            StackLimit;             //堆栈大小
    0ch   PVOID                            SubSystemTib;
         union {
             PVOID                FiberData;
    10h       DWORD                Version;
         };
    14h   PVOID                            ArbitraryUserPointer;
    18h   struct _NT_TIB                   *Self;                  //本NT_TIB结构自身的线性地址
    }NT_TIB;
    
    我们看到,ExceptionList在TIB的头部。而在X86下,TEB总是由fs:[0]指向的。
    ExceptionList是一个链表的结构.
    画了一个流程图便于理解
+---------+    +----------------+      +---------------+
| 发生异常 +--->+   TIB          +----->+   Next        +--+
|         |    |   fs:[0]       |      +---------------+  |            +------------------+
+---------+    +----------------+      |   Handler     +-------------->+  异常处理函数     |
                                       +---------------+  |            |  ...             |
                                                          |            |  retn            |
                                               +----------+            +------------------+
                                               |
                                       +-------v-------+
                                       |   Next        +--+
                                       +---------------+  |            +------------------+
                                       |   Handler     +-------------->+  异常处理函数     |
                                       +---------------+  |            |  ...             |
                                                          |            |  retn            |
                                               +----------+            +------------------+
                                               |
                                       +-------v-------+
                                       |  FFFFFFh      |
                                       +---------------+               +------------------+
                                       |   Handler     +-------------->+  异常处理函数     |
                                       +---------------+               |  ...             |
                                                                       |  retn            |
                                                                       +------------------+

next是下一个链的地址。如果next的值是FFFFFFh,表示是链表的最后一个节点,该节点的回调函数是系统设置的一个终结处理函数,所有无人值守的异常都会到达这里。
异常处理函数可以是自定义的函数,系统有一个默认的函数,但我们可以自定义一个异常处理函数,让它来处理。
但是得先安装自定义函数才能使用。
我们可以写一个异常处理的例子

//Powered by HAPPY
#include <Windows.h>
#include <iostream>
int exception_memory_access_violation(LPEXCEPTION_POINTERS p_exinfo)
{
    if (p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        return  EXCEPTION_EXECUTE_HANDLER; //handle this exception
    }
    else return EXCEPTION_CONTINUE_SEARCH; //Do not handle this exception
}

int main()
{
    char* mem = 0;
    std::cout << "Hello World!\n";
    __try {
        *mem = 0; //throw exception
    }
    __except (exception_memory_access_violation(GetExceptionInformation()))  //handler
    {
        puts("Memory error in except");
    }
}

我们可以将其编译后反汇编研究下except的代码以及是如何安装SEH的。限于篇幅,我们不做深入探究。了解更多可以看一下https://blog.csdn.net/w1012747007/article/details/77131781

异常分析

这里,我们看到filter(这是IDA自动生成的提示)

0x401373filter返回1,即EXCEPTION_EXECUTE_HANDLER,意思是捕获该异常,也就是说无论发生什么异常,都将被0x401379的异常处理捕获。
这里,我们知道了,如果想要程序输出success,程序中就需要出现一次异常。让0x401379的exception执行.
我们从try块开头开始分析。

这里的给ms_exc.registration.TryLevel赋值是用于处理嵌套的try。trylevel为0表示最外层,具体可以参见此处
大致的流程如下

esi的值是输入的序列号转为十六进制的结果。(例如输入的序列号是110799,那么esi就是0x110799)

重点来看0x401354的处理,我们分析一下执行到0x401354时帧栈

+--------+
| eax    |
+--------+
| EIP    +->0x401353 (call会把下一条汇编的地址压入。)
+--------+
|        |
+--------+


执行完0x401354,eax就是0x401353
然后div的时候,如果esi的值是0,就会触发异常,也就是我们想要的结果。
我们只需要esi的值原来是0x401353,也就是和eax一样。
而esi的值的由来前面分析过,所以序列码就是401353

 

顺带blog原文备份
blog



[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最后于 2019-6-22 22:48 被微笑明天编辑 ,原因:
最新回复 (3)
kanxue 8 2019-6-25 17:19
2
0
图片没帖上来,你的blog图片也不完整。
微笑明天 2019-6-26 22:15
3
0
图应该修好了
天涯Q 2019-8-7 14:09
4
0
微笑明天 图应该修好了
 老哥,方便给下联系方式吗,购买技术,私信我联系方式
游客
登录 | 注册 方可回帖
返回