avatar

Catalog
调试对象

前言

本篇开始学习软件调试的基础知识,也是Windows内核基础的最后一个阶段了。软件调试实际上东西不多,若是想开发一个调试器,掌握十几个API就差不多了,但是如果想要在调试与反调试的对抗中占据主动,对细节的了解才是最重要的。

软件调试系列主要用到kernel32.dll、ntdll.dll、ntoskrnl.exe这几个文件,对于一些Windbg未导入的符号表ReactOS也有助于代码的分析。下面进入正文。

调试器与被调试程序

媒介

调试器是一个进程,被调试程序是一个进程,如何才能将两个进程联系到一起呢?就需要一个媒介,进程间是相互隔离的,但是高2G往往又是相同的,因此这个媒介可以利用内核层来实现。

在接下来的学习中,会一点点接开这个媒介是什么,以及如何创建,使用的。

建立联系的方式

打开调试器,有两种与被调试程序建立联系的方式:

  • 在调试器中打开一个程序:这种方式是通过CreateProcess()建立联系的
  • 将一个正在运行的程序附加到调试器中:这种方式是通过DebugActiveProcess()建立联系的

两种建立联系的方式本质上区别并不大,只是第一种有一个创建进程的过程,多出一步,所以仅分析第二种通过DebugActiveProcess()建立联系的方式即可。

DebugActiveProcess执行流程

下面开始分析DebugActiveProcess的执行流程,这里要先说明一下,调试是调试器向被调试进程发起的,所以调用DebugActiveProcess的进程就是调试器进程。

关联调试对象与调试器

  1. 首先进入kernel32.dll中的DebugActiveProcess,最前面有一个值得关注的函数DbgUiConnectToDbg,进入后发现它调用了另一个dll中的同名函数。

  2. 进入ntdll.dll中的DbgUiConnectToDbg,这里分为两个部分来看:

    • 红色方框主要来梳理调用关系,DbgUiConnectToDbg内部调用了ZwCreateDebugObject,顾名思义,这个函数用来创建调试对象_DEBUG_OBJECT(后面简称调试对象),也就是之前提到的媒介,当然,它也只是进入0环的入口函数,具体创建过程在0环。
    • 再看橙色方框,先解释一下fs:[18h]为什么是TEB,我们知道3环的fs:[0]指向TEB,TEB中的第一个成员是NtTib,NtTib中0x18处的成员为Self,指向NtTib的地址,也就是TEB本身,所以fs:[18h]的值,就是TEB。接着令将地址TEB+0xF24作为ZwCreateDebugObject的参数传进去了。有点逆向基础的都知道,eax是保存函数返回值的地方,所以ZwCreateDebugObject的返回值会保存在eax中,也就是TEB+0xF24。由于调试对象是0环的结构体,所以返回的不可能是地址,只能是句柄,因此TEB+0xF24保存的值为调试对象的句柄。

    下面为_DEBUG_OBJECT的结构:

    c
    1
    2
    3
    4
    5
    6
    typedef struct _DEBUG_OBJECT {
    KEVENT EventsPresent; //+0x00,用于指示有调试事件发生的事件对象
    FAST_MUTEX Mutex; //+0x10,用于同步的互斥对象
    LIST_ENTRY EventList; //+0x30,保存调试事件的链表
    ULONG Flags; //+0x38,标志位
    } DEBUG_OBJECT, *PDEBUG_OBJECT;
  3. 回到kernel32.dll中,当执行到红色方框处时,DbgUiConnectToDbg已经完成了两件事:

    • 创建调试对象_DEBUG_OBJECT(位于0环)
    • 将调试对象与调试器进程关联起来(调试对象句柄位于调试器进程TEB+0xF24处)

关联调试对象与被调试进程

前面介绍了调试用的媒介调试对象,是如何与调试器关联起来的,接下来继续分析DebugActiveProcess函数看看被调试进程是如何与调试对象关联的。

  1. 这里还是分为2个部分看

    • 红色方框还是调用关系,这里调用的第一个函数ProcessIdToHandle,顾名思义,这个函数的作用是将进程Id转换为进程的句柄,我们分析的DebugActiveProcess只有一个参数就是被调试进程的Id,这里转换的就是被调试进程的Id;转换完后,会调用DbgUiDebugActiveProcess,进入函数内部,显然,具体实现在ntdll.dll中。
    • 橙色方框来看细节,ProcessIdToHandle执行完后将eax中的值又赋给了esi,此时esi存的就是被调试进程的句柄,并且它作为参数传入DbgUiDebugActiveProcess函数中。
  2. 进入ntdll.dll中,这部分做了一件事,就是调用NtDebugActiveProcess,跟进后发现,要进内核的,所以放到后面分析。这里关注一下它的两个参数,第一个参数是调试对象的句柄,在分析DebugActiveProcess执行流程的开头提到过,目前执行的是调试器的线程,这里从TEB+0xF24获取到先前创建的调试对象的句柄第二个参数就是前面经过ProcessIdToHandle转换的被调试进程的句柄

  3. 进入到ntoskrnl.exe中,这部分的代码比较长,逻辑也是比较重要的,所以分为3个部分来看,这里先看第一部分的代码。这里调用了ObReferenceObjectByHandle,这个函数的作用是将句柄转换为地址,由于现在已经进入0环了,句柄就没什么用了,因此需要转换为地址来用。这里最需要注意的就是它的参数,该函数有6个参数,第一个参数是被调试进程的句柄,用于转换成该进程EPROCESS的地址;第五个参数是最重要的,没有逆向基础的人可能很难理解,它是一个OUT类型的参数,存的是被调试进程的句柄的所在地址。在执行完函数后,该地址原先存放的句柄会被替换成进程EPROCESS的地址,并依然存放在这。这种手法在3环API也比较常见,逆多了程序就很容易理解了。

  4. 接着来看第二部分:

    • 紫色方框:将被调试进程EPROCESS的地址暂存到esi中
    • 橙色方框:作两个判断,有两个进程是不能调试的,一个是自己本身的进程,另一个是PsInitialSystemProcess系统初始化进程,若调试的是这两个进程,则跳转离开。
    • 红色方框:调用ObReferenceObjectByHandle将调试对象的句柄转换成调试对象的地址。存在之前那个OUT参数那里,原先存的被调试进程的EPROCESS已经暂存到了esi中。
  5. 来看最后一部分,这里将调试对象的地址被调试进程的EPROCESS作为参数传入,并调用函数DbgkpSetProcessDebugObject

  6. 进入DbgkpSetProcessDebugObject,此处仍位于ntoskrnl.exe中,红色方框用来标记出两个重要参数被调试进程EPROCESS的地址与调试对象的地址。橙色方框,先判断被调试进程的DebugPort处的值是否为0,若不为0,说明已经被调试了,就只能跳走。若不为0,说明未被调试,则将调试对象_DEBUG_OBJECT存到被调试进程EPROCESS.DebugPort处。至此,调试对象与被调试进程也关联起来了,调试对象存到了被调试进程EPROCESS.DebugPort处。

_DEBUG_OBJECT的本质:桥

通过上面的学习,了解到调试对象_DEBUG_OBJECT的本质就是调试器进程与被调试进程之间的桥梁,它作为媒介,先后与调试器,被调试进程创建联系,从而将两者联系起来。参考下图:

攻防对抗

反调试

在掌握了调试原理后,自然也就可以总结出一些反调试的手段:

  1. 清零DebugPort,只要起一个线程不断的检查当前进程的DebugPort,一旦有值就退出程序或者将其清零,这样可以中断调试对象与被调试进程的联系,以达到反调试的目的。
  2. 遍历所有进程TEB+0xF24处,看有没有值,若有值,一定就是调试器,则退出程序。
  3. Hook NtCreateDebugObject,不让它创建调试对象。

反反调试

有反调试,自然就有反反调试,正所谓道高一尺魔高一丈,针对各类反调试手段,也会衍生出各类的反反调试,攻防领域永远都在交替上升:

  1. 针对Hook NtCreateDebugObject的反调试方式,可以自己分配一个内存给_DEBUG_OBJECT,并为它的成员赋值。
  2. 针对清零DebugPort的反调试方式,可以不使用DebugPort的位置,在进程中另找一个区域存放_DEBUG_OBJECT的地址。把原先+0xbc的值都选为新的偏移处。
  3. 重写整个DebugActiveProcess函数

参考资料

参考教程:

  • 海哥逆向中级预习班

参考链接

Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/09/11/%E8%B0%83%E8%AF%95%E5%AF%B9%E8%B1%A1/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶