看雪CTF签到题SEH异常处理--MysteriousLetter2

先看程序主逻辑


这里可以看到要求输入六位的序列号,最后三位是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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,817评论 1 32
  • 【JAVA 线程】 线程 进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者...
    Rtia阅读 2,919评论 2 20
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,289评论 0 8
  • 整理来自互联网 1,JDK:Java Development Kit,java的开发和运行环境,java的开发工具...
    Ncompass阅读 1,656评论 0 6
  • 前些年读过一篇散文,名字就是《每次醒来,你都不在》。每每重复读它,眼珠子都会通红。这么诛心的话语,总让我和苏轼的“...
    德润长江阅读 313评论 0 1

友情链接更多精彩内容