查看原文
其他

从异常分发流程的实现,看反用户调试器

2018-04-20 又出bug了 看雪学院

本文是对从3环调试器工作流程的实现看反调试 的一个补充和扩展,主要从异常分发的流程的实现来看怎么反调试。
     

后面还有一篇链接:[原创]从异常分发流程的实现看反双机调试【有码】
     

后面顺变会附带上写满注释的wrk代码,方便大家对照着看。
     

由于本人水平有限,错误之处难免,请大神们不吝指正哈!


一个INT 3被执行的时候关键数据结构变化



栈的变化:

    

INT 3刚被执行的时候堆栈的情况:

   

         

说明:

1. 从tss段中取出对应权限的esp

2. 把用户空间的ss寄存器压入HardwareSegss

3. 把用户空间的esp寄存器压入HardwareEsp

4. 把标志寄存器压入到EFLAGS

5. 把用户空间的cs寄存器压入到SegCs中

6. 把用户空间的eip压入到Eip中

    

填充TrapFrame结构:

    

         

说明:

            

1. V8 6Gs、V86Fs、V86Ds、V86Es,这些寄存器现在仅仅作为保留,不必研究

           

2. HardwareSegSs、HardwareEsp、Eflags、SegCs、Eip,这些在中断的时候cpu会帮我保存

            

3. 其他的寄存器操作系统在中断例程保存


异常结构相关:

      

异常结构体

   

         

说明:


1. 在学习阶段只需要关注ExceptionCode和ExceptionAddress即可,其他的参数如果不写调试器暂时用不到

      

异常结构体在什么阶段被填充(以INT 3为例):

   

      

说明:

         

1. 从上图可以看到在CommonDispatchException中被填充

         

2. 但是我们也可以看到我们分析的重点会在KiDispatchException中,是的我们的分析的重点就是他,但是分析它之前我们得做一些准备工作

    


异常结构体具体是如何被填充的?


      

这个问题只能贴代码了,就贴CommonDispatchException代码,这块的代码已经有很详细的注释了,等会全部的代码放到附件中提供下载   

       



为什么讲异常分发流程会讲下核心数据结构?


    

1. 后面我会把详细注释过的wrk的源代码作为附录,大家可以对照注释去看异常分发的源代码

    

2. 根据我的经验,阻碍看源代码就是对这些数据结构不了解,特别是TrapFrame、异常时候的堆栈、在异常中怎么返回这类问题


异常分发全流程:



1. 如果你看到这张图不想看了,你的感觉是对的,我第一次看到这张图也是一脸懵逼,感觉好复杂啊,幸好我们今天仅仅只学习其中的一块,就是图的左半部分


2. 如果你看到这里有什么第一次、第二次XX,先不用纠结这个问题,我们只讨论第一次的话题,因为男人的第一次是难以忘怀的,不对是第一次分发才和3环调试器有密切关系,第二次分发以后专门开个帖子对着源代码讲下


3. 忽略图中的内核调试器



第一次分发且有用户调试器



先来个流程图:

       


说明:


1. 可以看到核心的函数是 DbgkForwardException


2. 把调试事件发送到调试对象的核心函数是DbgkSendApiMessage



核心代码的分析



KiDispatchException 的分析:

   


主要调用了DbgkForwardException,这个是用户调试的主要函数

   

    

DbgkForwardException的分析:

   

    

先检查 CrossThreadFlags 的 PS_CROSS_THREAD_FLAGS_HIDEFROMDBG 标志位是否被设置,如果被设置代表不会被调试。

调用 DbgkpSendapiMessage 发送异常信息给调试器

   


DbgkpSendApiMessage 的分析:


说明:


1. 这个函数主要调用了 DbgkpQueueMessage 函数

    

DbgkpQueueMessage 的分析:

   


这里会获取一个全局锁,这个是一个反调试的好地方

   


把调试事件插入DebugObject的EventList里面去:

   


等待调试器处理这个调试事件

   



从异常分发流程中总结出来的反3环调试

  


1. 直接对idt做hook,比如3号中断,这样就没法使用软件断点了

    

2. 对需要保护的进程的每个线程的的 Ethread 结构体的CrossThreadFlag 设置 PS_CROSS_THREAD_FLAGS_HIDEFROMDBG,有一个专门的api是:ZwSetInformationThread(hThread, ThreadHideFromDebugger, NULL, NULL)

    

3. DbgkpSendApiMessage 进行挂钩,直接返回STATUS_UNSUCCESSFUL

    

4. DbgkpQueueMessage 进行挂钩,直接返回STATUS_UNSUCCESSFUL

    

5. 这个就比较比较猥琐了:ExAcquireFastMutex (&DbgkpProcessDebugPortMutex),直接锁住这个全局变量,所有的调试器都会卡死,并且没有钩子

    

6. DbgkpSuspendProcess和DbgkpResumeProcess 挂钩,向0x64端口写入0xFE,让机器重启


谢谢大家观看!






本文由看雪论坛 又出bug了 原创

转载请注明来自看雪社区



往期热门阅读:



扫描二维码关注我们,更多干货等你来拿!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存