欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]从3环调试器工作流程的实现看反调试

[原创]从3环调试器工作流程的实现看反调试

     今天是我在15pb最后一天的课程,明天火车就要离开北京南下到广东去求职了。在这里祝愿20、21期的伙伴有一个好的前程,也祝愿15pb各位老师身体健康,事业红火。
    本文不会讲异常分发流程,因为异常分发流程我想分为两篇文章来写,链接如下:
        1. 从异常分发流程的实现看反用户调试器
        2. 从异常分发流程的实现看反双机调试【有码】
    由于本人水平有限,错误之处难免,请大神们不吝指正哈!

目录

    从调试器代码框架上去看整个调试流程(之前我写的3环调试器扣出来的):... 1

        以调试方式启动调试器:... 1

        以附加的方式启动调试器:... 1

        调试器等待调试事件的到来:... 1

        调试器事件的分发:... 1

        总结:...1

    从关键数据结构角度来看调试流程:... 1

        调试内核对象的结构体:...1

        常见的调试事件:...1

        调试器获取调试事件的答题流程图:...1

        总结:... 1

    对调试框架中关键函数进行源代码分析:... 1

        CreateProcess以创建进程方式调试... 1

        DebugActiveProcess以附加方式调试:... 1

        WaitForDebugEvent调试器中等待调试事件的到来... 1

    从整个流程总结出的反调试方法:... 1



从调试器代码框架上去看整个调试流程(之前我写的3环调试器扣出来的):

    以调试方式启动:

   

    以附加的方式启动调试器:

    调试器等待调试事件的到来:

   异常分发流程:

总结:

    1.  以调试方式或者附加的方式启动程序,对应的API分别是:CreateProcess和DebugActiveProcess

    2.  WaitForDebugEvent以等待调试事件的到来

    3. DispatchEvent,分发调试事件,这里面有各种handle


从关键数据结构角度来看调试流程:

    调试内核对象的结构体:

    常见调试事件:

    调试器获取调试事件的大体流程图:


对调试框架中关键函数进行源代码分析:

    CreateProcess以创建进程方式调试:

        以调试方式启动进程在(3环):

       

        具体分析DbgUiConnectToDbg:
           

            总结下:

                1.       创建一个一个调试内核对象,通过ZwCreateDebugObject

                2.       放到主动调试进程的teb中,这个偏移是f24

        具体分析下创建调试对象(0环):

       

       

       把上面创建的调试对象,填充到被调试进程的EPROCESS的DebugPort字段(0环):

     


    DebugActiveProcess以附加方式调试 :

        直接贴出以调试方式打开进程:

       

        因为DbgUiConnectToDbg已经在上面分析过直接分析DbgUiDebugActiveProcess:

       

         总结:

               1. NtDebugActiveProcess,根据Teb中F24中中存放的句柄,找到调试对象,然后发送假的进程创建消息(因为这里是attach的进程,进程之前已经创建 了,所以这里发送的是假的)

               2.DbgUiIssueRemoteBreakin,在被调试进程中创建一个线程,然后在线程中触发int3断点,这个也是附加调试调试器能段下程序的原因

       

        NtDebugActiveProcess的函数简单分析:

       

       

        总结:

                1.       DbgkpPostFakeProcessCreateMessages,主要发送进程创建、线程创建、模块加载的伪造消息

                2.       DbgkpSetProcessDebugObject,主要任务设置事件,以便调试线程从阻塞中返回获取调试信息


       

       DbgUiIssueRemoteBreakin的函数的详细分析:

      

      

     总结:

             1.       在被调试线程中创建一个远程线程,这个远程线程函数DbgUiRiRemoteBreakin

             2.       这个函数主要设置一个int3断点,以便产生int3断点


    WaitForDebugEvent调试器中等待调试事件的到来:

        

         调用了DbgUIWaitStateChange等待调试对象被设置:

        

        直接跳到内核去看,可以看到是直接等待DebugObject->EventsPresent 这个内核事件被触发

       

       获取具体的调试信息DbgUiConvertStateChangeStructure这个结构体为PDEBUG_EVENT:

      

      一大堆的switchcase匹配,各种调试事件,主要的任务就是填充WaitForDebugEvent中的第二个参数(LPDEBUG_EVENT)

     



从整个流程总结出来反调试的方法,其实远远不止我下面写的:

    1.       被调试进程的Debugport一定不为0,所以可以对debugport清零破坏掉调试流程

    2.       遍历所有的线程的teb的偏移f24字段,这个是调试对象Debugport的句柄,如果不为0直接清零,达到反调试的手段

    3.       在内核中挂钩ObCreateObject,其中第第三个参数为DbgkDebugObjectType,这个参数是全局变量,挂钩后禁止创建

    4.       对NtCreateDebugObject挂钩,可以禁止调试对象的创建

    5.       可以对NtWaitForDebugEvent挂钩,破坏WaitForDebugEvent让它没法正常接收调试事件

    6.       在线程启动回调中拦截线程函数为DbgUiRemoteBreakin的创建,让附加的调试器没法断下来(其实也可以在3环中做这个东西,让调试器没法附加,写到壳里面)

    7.       可以对DbgkpQueueMessage函数挂钩,这样就算被调试程序出现异常,调试事件也没法添加到DEBUG_OBJECT的EVENT_LIST中,这些都是调试系列函数不会影响操作系统

    8.       在整个调试流程中调试器需要用到被调试进程的句柄,所以可以遍历句柄表并且关闭,会影响到调试器的ReadProcessMemory和WriteProcessMemory,中断调试流程

    9.       挂钩自己进程的DbgBreakPoint(),没错没什么正常的程序会使用这个api的,一旦在自己进程发现DbgBreakPoint了就可能自己被调试了

    10.   PEB的BeingDebugged检测


最后,谢谢大家观看!

     












注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功