首页
社区
课程
招聘
[原创]删除杀软回调 bypass EDR 研究
2023-10-20 12:20 12444

[原创]删除杀软回调 bypass EDR 研究

2023-10-20 12:20
12444

原文链接:删除杀软回调 bypass EDR 研究 (qq.com)


 通过删除杀软或EDR的“创建进程通知回调”、“创建线程通知回调”、“加载镜像通知回调”、“注册表通知回调”,极大的消弱杀软或EDR的动态查杀能力。


本文属于以下文章和项目的复现和升华。

文章:

对抗临近 | 红队大佬的私人秘籍:EDR绕过技术曝光!(https://mp.weixin.qq.com/s/jva2d8nLz6ti8fLTR0O-wQ)

https://br-sn.github.io/Removing-Kernel-Callbacks-Using-Signed-Drivers/

项目:

https://github.com/br-sn/CheekyBlinder

https://github.com/lawiet47/STFUEDR


01


杀软或EDR内核回调简介


Windows x64 系统中,由于 PatchGuard 的限制,杀软或EDR正常情况下,几乎不能通过 hook 的方式,完成其对恶意软件的监控和查杀。那怎么办呢?别急,微软为我们提供了其他的方法,完成此类功能,那就是系统回调机制。比如本文提到的“创建进程通知回调”、“创建线程通知回调”、“加载镜像通知回调”、“注册表通知回调”等等。

在恶意软件和杀软 攻与防的对抗中,二者经过激烈的较量,完成了螺旋式的上升变革,给我们的感觉是,杀软越来越强大了,我们的网络环境越来越安全了。


02

删除杀软回调项目简介


github 上有两个比较经典的项目,可以完成删除杀软回调的功能,项目如下所示:

https://github.com/br-sn/CheekyBlinder

https://github.com/lawiet47/STFUEDR

这些项目主要完成了三大功能:

  1. 利用合法驱动读取或修改内核数据;

  2. 寻找“创建进程通知回调”、“创建线程通知回调”、“加载镜像通知回调”、“注册表通知回调”内核数组地址;

  3. 将杀软或EDR驱动对应的回调数组中的某个元素,置 0 或删除;

这里需要注意的是,“创建进程通知”、“创建线程通知回调”、“加载镜像通知回调”是正常的数组,而“注册表通知回调”是一个双向循环链表。

下面简单介绍一下,上述四大回调数组内核地址的寻找方法和删除杀软回调的方法。

使用工具:windbg preview

系统环境:Windows 1809 x64


03

创建进程回调数组定位


    // 1. 由 PsSetCreateProcessNotifyRoutine 定位 nt!PspSetCreateProcessNotifyRoutine 地址
    0: kd> uf PsSetCreateProcessNotifyRoutine
    nt!PsSetCreateProcessNotifyRoutine:
    fffff802`6ca90570 4883ec28        sub     rsp,28h
    fffff802`6ca90574 8ac2            mov     al,dl
    fffff802`6ca90576 33d2            xor     edx,edx
    fffff802`6ca90578 84c0            test    al,al
    fffff802`6ca9057a 0f95c2          setne   dl
    fffff802`6ca9057d e80e010000      call    nt!PspSetCreateProcessNotifyRoutine (fffff802`6ca90690)
    fffff802`6ca90582 4883c428        add     rsp,28h
    fffff802`6ca90586 c3              ret
    
      
    // 2. 定位 nt!PspCreateProcessNotifyRoutine 数组地址
    0: kd> uf nt!PspSetCreateProcessNotifyRoutine
    nt!PspSetCreateProcessNotifyRoutine:
    fffff802`6ca90690 48895c2408      mov     qword ptr [rsp+8],rbx
    fffff802`6ca90695 48896c2410      mov     qword ptr [rsp+10h],rbp
    fffff802`6ca9069a 4889742418      mov     qword ptr [rsp+18h],rsi
    fffff802`6ca9069f 57              push    rdi
    fffff802`6ca906a0 4154            push    r12
    fffff802`6ca906a2 4155            push    r13
    fffff802`6ca906a4 4156            push    r14
    fffff802`6ca906a6 4157            push    r15
    
    ... ...
    
    nt!PspSetCreateProcessNotifyRoutine+0x49:
    fffff802`6ca906d9 488bd7          mov     rdx,rdi
    fffff802`6ca906dc 498bcf          mov     rcx,r15
    fffff802`6ca906df e8a4000000      call    nt!ExAllocateCallBack (fffff802`6ca90788)
    fffff802`6ca906e4 488bf8          mov     rdi,rax
    fffff802`6ca906e7 4885c0          test    rax,rax
    fffff802`6ca906ea 0f845b890c00    je      nt!PspSetCreateProcessNotifyRoutine+0xc89bb (fffff802`6cb5904b)  Branch
    
    nt!PspSetCreateProcessNotifyRoutine+0x60:
    fffff802`6ca906f0 33db            xor     ebx,ebx
    fffff802`6ca906f2 4c8d2d375dddff  lea     r13,[nt!PspCreateProcessNotifyRoutine (fffff802`6c866430)]
    
    nt!PspSetCreateProcessNotifyRoutine+0x69:
    fffff802`6ca906f9 488d0cdd00000000 lea     rcx,[rbx*8]
    fffff802`6ca90701 4533c0          xor     r8d,r8d
    fffff802`6ca90704 4903cd          add     rcx,r13
    fffff802`6ca90707 488bd7          mov     rdx,rdi
    fffff802`6ca9070a e86dd5aeff      call    nt!ExCompareExchangeCallBack (fffff802`6c57dc7c)
    fffff802`6ca9070f 84c0            test    al,al
    fffff802`6ca90711 750c            jne     nt!PspSetCreateProcessNotifyRoutine+0x8f (fffff802`6ca9071f)  Branch
    
    
    // 3. 显示回调数组
    0: kd> dq fffff802`6c866430
    fffff802`6c866430  ffffbb83`fc851a8f ffffbb83`fc9febaf
    fffff802`6c866440  ffffbb83`fe0e8b7f ffffbb83`fe0e8def
    fffff802`6c866450  ffffbb83`fe413f0f ffffbb83`fe43612f
    fffff802`6c866460  ffffbb83`fe436bdf ffffbb83`fc9feccf
    fffff802`6c866470  ffffbb83`fe4366cf ffffbb83`fe436b7f
    fffff802`6c866480  ffffbb83`fe436f3f ffffbb83`fe52133f
    fffff802`6c866490  ffffbb83`fe521f6f ffffbb83`fe4be96f
    fffff802`6c8664a0  ffffbb84`01737c3f 00000000`00000000
    0: kd> dq
    fffff802`6c8664b0  00000000`00000000 00000000`00000000
    fffff802`6c8664c0  00000000`00000000 00000000`00000000
    fffff802`6c8664d0  00000000`00000000 00000000`00000000
    fffff802`6c8664e0  00000000`00000000 00000000`00000000
    fffff802`6c8664f0  00000000`00000000 00000000`00000000
    fffff802`6c866500  00000000`00000000 00000000`00000000
    fffff802`6c866510  00000000`00000000 00000000`00000000
    fffff802`6c866520  00000000`00000000 00000000`00000000
    
    
    // 4. 取出回调数组第一个数据
    0: kd> dq (ffffbb83`fc851a8f>>4)<<4
    ffffbb83`fc851a80  00000000`00000020 fffff800`704d8230
    ffffbb83`fc851a90  00000000`00000000 00000000`00000000
    ffffbb83`fc851aa0  6e497350`02030000 00000000`00000000
    ffffbb83`fc851ab0  00000000`00100010 ffffbb83`fc851ac0
    ffffbb83`fc851ac0  00690067`00650052 00790072`00740073
    ffffbb83`fc851ad0  6e496c41`02030000 00000000`00000000
    ffffbb83`fc851ae0  00000001`00060000 ffffbb83`fc851ae8
    ffffbb83`fc851af0  ffffbb83`fc851ae8 00790072`00740073
    
    
    // 5. 上述数据中第 2 个 8 字节 指针(回调函数) 所在模块
    0: kd> lm a fffff800`704d8230
    Browse full module list
    start             end                 module name
    fffff800`704b0000 fffff800`70505000   360qpesv64   (no symbols)


    04

    创建线程回调数组定位

    方法一:

      // 1. 由 PsSetCreateThreadNotifyRoutine 定位 nt!PspSetCreateThreadNotifyRoutine 地址
      0: kd> uf PsSetCreateThreadNotifyRoutine
      nt!PsSetCreateThreadNotifyRoutine:
      fffff805`2e2a4350 4883ec28        sub     rsp,28h
      fffff805`2e2a4354 33d2            xor     edx,edx
      fffff805`2e2a4356 e865000000      call    nt!PspSetCreateThreadNotifyRoutine (fffff805`2e2a43c0)
      fffff805`2e2a435b 4883c428        add     rsp,28h
      fffff805`2e2a435f c3              ret
      
      
      // 2. 定位 nt!PspCreateThreadNotifyRoutine 数组地址
      0: kd> uf nt!PspSetCreateThreadNotifyRoutine
      nt!PspSetCreateThreadNotifyRoutine:
      fffff805`2e2a43c0 48895c2408      mov     qword ptr [rsp+8],rbx
      fffff805`2e2a43c5 4889742410      mov     qword ptr [rsp+10h],rsi
      fffff805`2e2a43ca 57              push    rdi
      fffff805`2e2a43cb 4883ec20        sub     rsp,20h
      fffff805`2e2a43cf 8bf2            mov     esi,edx
      fffff805`2e2a43d1 8bd2            mov     edx,edx
      fffff805`2e2a43d3 e8b0030000      call    nt!ExAllocateCallBack (fffff805`2e2a4788)
      fffff805`2e2a43d8 488bf8          mov     rdi,rax
      fffff805`2e2a43db 4885c0          test    rax,rax
      fffff805`2e2a43de 0f842e8b0c00    je      nt!PspSetCreateThreadNotifyRoutine+0xc8b52 (fffff805`2e36cf12)  Branch
      
      nt!PspSetCreateThreadNotifyRoutine+0x24:
      fffff805`2e2a43e4 33db            xor     ebx,ebx
      
      nt!PspSetCreateThreadNotifyRoutine+0x26:
      fffff805`2e2a43e6 488d0d435cddff  lea     rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`2e07a030)]
      fffff805`2e2a43ed 4533c0          xor     r8d,r8d
      fffff805`2e2a43f0 488d0cd9        lea     rcx,[rcx+rbx*8]
      fffff805`2e2a43f4 488bd7          mov     rdx,rdi
      fffff805`2e2a43f7 e880d8aeff      call    nt!ExCompareExchangeCallBack (fffff805`2dd91c7c)
      fffff805`2e2a43fc 84c0            test    al,al
      fffff805`2e2a43fe 7436            je      nt!PspSetCreateThreadNotifyRoutine+0x76 (fffff805`2e2a4436)  Branch
      
      nt!PspSetCreateThreadNotifyRoutine+0x40:
      fffff805`2e2a4400 40f6c601        test    sil,1
      fffff805`2e2a4404 0f85128b0c00    jne     nt!PspSetCreateThreadNotifyRoutine+0xc8b5c (fffff805`2e36cf1c)  Branch
      
      
      // 3. 显示回调数组
      0: kd> dq nt!PspCreateThreadNotifyRoutine
      fffff805`2e07a030  ffffbb8f`d044ab7f ffffbb8f`d368fdbf
      fffff805`2e07a040  00000000`00000000 00000000`00000000
      fffff805`2e07a050  00000000`00000000 00000000`00000000
      fffff805`2e07a060  00000000`00000000 00000000`00000000
      fffff805`2e07a070  00000000`00000000 00000000`00000000
      fffff805`2e07a080  00000000`00000000 00000000`00000000
      fffff805`2e07a090  00000000`00000000 00000000`00000000
      fffff805`2e07a0a0  00000000`00000000 00000000`00000000
      
      
      // 4. 取出回调数组第一个数据
      0: kd> dq (ffffbb8f`d044ab7f>>4)<<4
      ffffbb8f`d044ab70  00000000`00000020 fffff805`2ecdd72c
      ffffbb8f`d044ab80  00000000`00000000 e8f10366`0081e800
      ffffbb8f`d044ab90  72724d46`02030000 66d18b66`00218c0f
      ffffbb8f`d044aba0  ffffbb8f`cee83200 ffffbb8f`cee831f0
      ffffbb8f`d044abb0  00000002`00000040 c82b66c4`eb586643
      ffffbb8f`d044abc0  20206f49`02032b00 8a67c92b`66c3c02b
      ffffbb8f`d044abd0  00690072`0044005c 005c0072`00650076
      ffffbb8f`d044abe0  00610072`006d0076 006b0073`00640077
      
      
      // 5. 上述数据中 第 2 个 8字节 指针(回调函数) 所在模块
      0: kd> lm a fffff805`2ecdd72c
      Browse full module list
      start             end                 module name
      fffff805`2ecb0000 fffff805`2ed93000   360FsFlt   (deferred)


      方法二:

        // 1. 由 PsRemoveCreateThreadNotifyRoutine 定位 nt!PspCreateThreadNotifyRoutine 数组地址
        0: kd> uf PsRemoveCreateThreadNotifyRoutine
        nt!PsRemoveCreateThreadNotifyRoutine:
        fffff805`2e42d460 48895c2408      mov     qword ptr [rsp+8],rbx
        fffff805`2e42d465 48896c2410      mov     qword ptr [rsp+10h],rbp
        fffff805`2e42d46a 4889742418      mov     qword ptr [rsp+18h],rsi
        fffff805`2e42d46f 57              push    rdi
        fffff805`2e42d470 4156            push    r14
        fffff805`2e42d472 4157            push    r15
        fffff805`2e42d474 4883ec20        sub     rsp,20h
        fffff805`2e42d478 65488b342588010000 mov   rsi,qword ptr gs:[188h]
        fffff805`2e42d481 4183cfff        or      r15d,0FFFFFFFFh
        fffff805`2e42d485 4c8bf1          mov     r14,rcx
        fffff805`2e42d488 664401bee4010000 add     word ptr [rsi+1E4h],r15w
        fffff805`2e42d490 33ff            xor     edi,edi

        nt!PsRemoveCreateThreadNotifyRoutine+0x32:
        fffff805`2e42d492 488d0d97cbc4ff  lea     rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`2e07a030)]
        fffff805`2e42d499 488d2cf9        lea     rbp,[rcx+rdi*8]
        fffff805`2e42d49d 488bcd          mov     rcx,rbp
        fffff805`2e42d4a0 e81b4286ff      call    nt!ExReferenceCallBackBlock (fffff805`2dc916c0)
        fffff805`2e42d4a5 488bd8          mov     rbx,rax
        fffff805`2e42d4a8 4885c0          test    rax,rax
        fffff805`2e42d4ab 7429            je      nt!PsRemoveCreateThreadNotifyRoutine+0x76 (fffff805`2e42d4d6)  Branch

        nt!PsRemoveCreateThreadNotifyRoutine+0x4d:
        fffff805`2e42d4ad 488bc8          mov     rcx,rax
        fffff805`2e42d4b0 e8cb4286ff      call    nt!ExGetCallBackBlockRoutine (fffff805`2dc91780)
        fffff805`2e42d4b5 493bc6          cmp     rax,r14
        fffff805`2e42d4b8 7511            jne     nt!PsRemoveCreateThreadNotifyRoutine+0x6b (fffff805`2e42d4cb)  Branch


        05

        加载镜像回调数组定位

        方法一:

          // 1. 由 PsSetLoadImageNotifyRoutine 定位 nt!PsSetLoadImageNotifyRoutineEx 地址
          0: kd> uf PsSetLoadImageNotifyRoutine
          nt!PsSetLoadImageNotifyRoutine:
          fffff805`2e2a4370 4883ec28        sub     rsp,28h
          fffff805`2e2a4374 33d2            xor     edx,edx
          fffff805`2e2a4376 e8d5000000      call    nt!PsSetLoadImageNotifyRoutineEx (fffff805`2e2a4450)
          fffff805`2e2a437b 4883c428        add     rsp,28h
          fffff805`2e2a437f c3              ret
          
          
          // 2. 定位 nt!PspLoadImageNotifyRoutine 数组地址
          0: kd> uf nt!PsSetLoadImageNotifyRoutineEx
          nt!PsSetLoadImageNotifyRoutineEx:
          fffff805`2e2a4450 48895c2418      mov     qword ptr [rsp+18h],rbx
          fffff805`2e2a4455 4889742420      mov     qword ptr [rsp+20h],rsi
          fffff805`2e2a445a 57              push    rdi
          fffff805`2e2a445b 4883ec70        sub     rsp,70h
          fffff805`2e2a445f 488b058a37d8ff  mov     rax,qword ptr [nt!_security_cookie (fffff805`2e027bf0)]
          fffff805`2e2a4466 4833c4          xor     rax,rsp
          fffff805`2e2a4469 4889442460      mov     qword ptr [rsp+60h],rax
          fffff805`2e2a446e 488bf1          mov     rsi,rcx
          fffff805`2e2a4471 48f7c2feffffff  test    rdx,0FFFFFFFFFFFFFFFEh
          fffff805`2e2a4478 0f85c28a0c00    jne     nt!PsSetLoadImageNotifyRoutineEx+0xc8af0 (fffff805`2e36cf40)  Branch
          
          nt!PsSetLoadImageNotifyRoutineEx+0x2e:
          fffff805`2e2a447e e805030000      call    nt!ExAllocateCallBack (fffff805`2e2a4788)
          fffff805`2e2a4483 488bf8          mov     rdi,rax
          fffff805`2e2a4486 4885c0          test    rax,rax
          fffff805`2e2a4489 0f84c58a0c00    je      nt!PsSetLoadImageNotifyRoutineEx+0xc8b04 (fffff805`2e36cf54)  Branch
          
          nt!PsSetLoadImageNotifyRoutineEx+0x3f:
          fffff805`2e2a448f 33db            xor     ebx,ebx
          
          nt!PsSetLoadImageNotifyRoutineEx+0x41:
          fffff805`2e2a4491 488d0d985dddff  lea     rcx,[nt!PspLoadImageNotifyRoutine (fffff805`2e07a230)]
          fffff805`2e2a4498 4533c0          xor     r8d,r8d
          fffff805`2e2a449b 488d0cd9        lea     rcx,[rcx+rbx*8]
          fffff805`2e2a449f 488bd7          mov     rdx,rdi
          fffff805`2e2a44a2 e8d5d7aeff      call    nt!ExCompareExchangeCallBack (fffff805`2dd91c7c)
          fffff805`2e2a44a7 84c0            test    al,al
          fffff805`2e2a44a9 0f849f000000    je      nt!PsSetLoadImageNotifyRoutineEx+0xfe (fffff805`2e2a454e)  Branch
          
          
          // 3. 显示回调数组
          0: kd> dq nt!PspLoadImageNotifyRoutine
          fffff805`2e07a230  ffffbb8f`ceef9bdf ffffbb8f`d044a6ff
          fffff805`2e07a240  ffffbb8f`d055fa8f ffffbb8f`d055fc6f
          fffff805`2e07a250  00000000`00000000 00000000`00000000
          fffff805`2e07a260  00000000`00000000 00000000`00000000
          fffff805`2e07a270  00000000`00000000 00000000`00000000
          fffff805`2e07a280  00000000`00000000 00000000`00000000
          fffff805`2e07a290  00000000`00000000 00000000`00000000
          fffff805`2e07a2a0  00000000`00000000 00000000`00000000
          
          
          // 4. 取出回调数组第一个数据
          0: kd> dq (ffffbb8f`ceef9bdf>>4)<<4
          ffffbb8f`ceef9bd0  00000000`00000020 fffff805`317e37a4
          ffffbb8f`ceef9be0  00000000`00000000 ffffbb8f`ceef9be0
          ffffbb8f`ceef9bf0  434f444e`02030000 00000000`00000001
          ffffbb8f`ceef9c00  ffffbb8f`d387b8c0 ffffbb8f`d387b8c0
          ffffbb8f`ceef9c10  00000002`00000040 00000000`00000001
          ffffbb8f`ceef9c20  20206f49`02030000 a2aba245`696ddc74
          ffffbb8f`ceef9c30  00690072`0044005c 005c0072`00650076
          ffffbb8f`ceef9c40  00550041`00450050 00000000`00480054
          
          
          // 5. 上述数据中 第 2 个 8 字节指针(回调函数) 所在模块
          0: kd> lm a fffff805`317e37a4
          Browse full module list
          start             end                 module name
          fffff805`317d0000 fffff805`317fb000   DsArk64    (deferred)


          方法二:

            // 1. 由 PsRemoveLoadImageNotifyRoutine 定位 nt!PspLoadImageNotifyRoutine 数组地址
            0: kd> uf PsRemoveLoadImageNotifyRoutine
            nt!PsRemoveLoadImageNotifyRoutine:
            fffff805`2e42d560 48895c2408      mov     qword ptr [rsp+8],rbx
            fffff805`2e42d565 48896c2410      mov     qword ptr [rsp+10h],rbp
            fffff805`2e42d56a 4889742418      mov     qword ptr [rsp+18h],rsi
            fffff805`2e42d56f 57              push    rdi
            fffff805`2e42d570 4156            push    r14
            fffff805`2e42d572 4157            push    r15
            fffff805`2e42d574 4883ec20        sub     rsp,20h
            fffff805`2e42d578 65488b342588010000 mov   rsi,qword ptr gs:[188h]
            fffff805`2e42d581 4183cfff        or      r15d,0FFFFFFFFh
            fffff805`2e42d585 4c8bf1          mov     r14,rcx
            fffff805`2e42d588 664401bee4010000 add     word ptr [rsi+1E4h],r15w
            fffff805`2e42d590 33ff            xor     edi,edi
            
            nt!PsRemoveLoadImageNotifyRoutine+0x32:
            fffff805`2e42d592 488d0d97ccc4ff  lea     rcx,[nt!PspLoadImageNotifyRoutine (fffff805`2e07a230)]
            fffff805`2e42d599 488d2cf9        lea     rbp,[rcx+rdi*8]
            fffff805`2e42d59d 488bcd          mov     rcx,rbp
            fffff805`2e42d5a0 e81b4186ff      call    nt!ExReferenceCallBackBlock (fffff805`2dc916c0)
            fffff805`2e42d5a5 488bd8          mov     rbx,rax
            fffff805`2e42d5a8 4885c0          test    rax,rax
            fffff805`2e42d5ab 7429            je      nt!PsRemoveLoadImageNotifyRoutine+0x76 (fffff805`2e42d5d6)  Branch
            
            nt!PsRemoveLoadImageNotifyRoutine+0x4d:
            fffff805`2e42d5ad 488bc8          mov     rcx,rax
            fffff805`2e42d5b0 e8cb4186ff      call    nt!ExGetCallBackBlockRoutine (fffff805`2dc91780)
            fffff805`2e42d5b5 493bc6          cmp     rax,r14
            fffff805`2e42d5b8 7511            jne     nt!PsRemoveLoadImageNotifyRoutine+0x6b (fffff805`2e42d5cb)  Branch


            06

            注册表通知回调数组定位

              // 仅 CmUnRegisterCallback 可以定位
              
              // 1. 由 CmUnRegisterCallback 定位 nt!CallbackListHead 链表地址
              0: kd> uf CmUnRegisterCallback
              nt!CmUnRegisterCallback:
              fffff805`2e38bd50 4c8bdc          mov     r11,rsp
              fffff805`2e38bd53 53              push    rbx
              fffff805`2e38bd54 56              push    rsi
              fffff805`2e38bd55 57              push    rdi
              fffff805`2e38bd56 4154            push    r12
              fffff805`2e38bd58 4155            push    r13
              fffff805`2e38bd5a 4156            push    r14
              fffff805`2e38bd5c 4157            push    r15
              fffff805`2e38bd5e 4881ec80000000  sub     rsp,80h
              fffff805`2e38bd65 488bd9          mov     rbx,rcx
              fffff805`2e38bd68 be0d0000c0      mov     esi,0C000000Dh
              fffff805`2e38bd6d 89b424d8000000  mov     dword ptr [rsp+0D8h],esi
              fffff805`2e38bd74 33c0            xor     eax,eax
              fffff805`2e38bd76 498943b0        mov     qword ptr [r11-50h],rax
              fffff805`2e38bd7a 498943b8        mov     qword ptr [r11-48h],rax
              fffff805`2e38bd7e 498943c0        mov     qword ptr [r11-40h],rax
              fffff805`2e38bd82 49214380        and     qword ptr [r11-80h],rax
              fffff805`2e38bd86 65488b042588010000 mov   rax,qword ptr gs:[188h]
              fffff805`2e38bd8f 4183ccff        or      r12d,0FFFFFFFFh
              fffff805`2e38bd93 664401a0e4010000 add     word ptr [rax+1E4h],r12w
              fffff805`2e38bd9b 33d2            xor     edx,edx
              fffff805`2e38bd9d 4c8d35ecf3ccff  lea     r14,[nt!CmpCallbackListLock (fffff805`2e05b190)]
              fffff805`2e38bda4 498bce          mov     rcx,r14
              fffff805`2e38bda7 e894f094ff      call    nt!ExAcquirePushLockExclusiveEx (fffff805`2dcdae40)
              fffff805`2e38bdac 41bf00000080    mov     r15d,80000000h
              
              nt!CmUnRegisterCallback+0x62:
              fffff805`2e38bdb2 4533c0          xor     r8d,r8d
              fffff805`2e38bdb5 488d542438      lea     rdx,[rsp+38h]
              fffff805`2e38bdba 488d0ddff3ccff  lea     rcx,[nt!CallbackListHead (fffff805`2e05b1a0)]
              fffff805`2e38bdc1 e82a94e1ff      call    nt!CmListGetNextElement (fffff805`2e1a51f0)
              fffff805`2e38bdc6 488bf8          mov     rdi,rax
              fffff805`2e38bdc9 4889442440      mov     qword ptr [rsp+40h],rax
              fffff805`2e38bdce 4885c0          test    rax,rax
              fffff805`2e38bdd1 0f84cf000000    je      nt!CmUnRegisterCallback+0x156 (fffff805`2e38bea6)  Branch
              
              
              // 2. 显示 CMREG_CALLBACK 结构(nt!CallbackListHead)
              0: kd> dq nt!CallbackListHead
              fffff805`2e05b1a0  ffff9705`3572b420 ffff9705`3572ba20
              fffff805`2e05b1b0  00000000`00000000 01d9e206`c817750c
              fffff805`2e05b1c0  fffff805`2e05b1c0 fffff805`2e05b1c0
              fffff805`2e05b1d0  00000000`00000000 00000000`00000000
              fffff805`2e05b1e0  00000000`00060001 fffff805`2e05b1e8
              fffff805`2e05b1f0  fffff805`2e05b1e8 00000000`00000000
              fffff805`2e05b200  00000000`00060001 fffff805`2e05b208
              fffff805`2e05b210  fffff805`2e05b208 00000000`00000000
              
              
              // 3. 显示 nt!CallbackListHead 下一个结点
              0: kd> dq ffff9705`3572ba20
              ffff9705`3572ba20  fffff805`2e05b1a0 ffff9705`35449660
              ffff9705`3572ba30  00000000`00000000 01d9e206`c817750b
              ffff9705`3572ba40  00000000`00000000 fffff805`3015c728
              ffff9705`3572ba50  00000000`000c000c ffff9705`3572a530
              ffff9705`3572ba60  ffff9705`3572ba60 ffff9705`3572ba60
              ffff9705`3572ba70  74705041`03060000 00000000`00000000
              ffff9705`3572ba80  ffff9705`35729ae0 00000000`00000000
              ffff9705`3572ba90  00000000`00000000 00000000`00000000
              
              
              // 4. 偏移 0x28 位置 的 8 字节指针(回调函数)所在模块
              0: kd> lm a fffff805`3015c728
              Browse full module list
              start             end                 module name
              fffff805`30150000 fffff805`301a5000   360qpesv64   (deferred)


              07

              删除杀软回调的方法


              删除上述四大系统回调的方法被分为两类,“创建进程通知”、“创建线程通知回调”、“加载镜像通知回调”被分为一类,为数组类;“注册表通知回调”被分为一类,为双向循环链表类,以下简称链表类。

              数组类删除方法是:找到驱动对应数组中的元素,将该元素内核地址赋值为 0;

              链表类删除方法是:找到驱动对应的链表中的结点,利用数据结构中双向循环链表删除结点的方法,删除杀软驱动对应的结点。

              这里需要注意,数组类中:

              “创建进程通知”、“创建线程通知回调”,在 win7 及以上的 个人系统和 Server 系统中,通常情况下,数组大小为 64 个元素;而“加载镜像通知回调”中,通常情况下,win10 全系列及以上,数组大小为 64 个元素,win7-7601、win8-9200、win8.1-9600系统中,可能为 8 个或 64 个元素,为不定值,相关资料可以参考微软官方文档,解决方法是,通过 nt!PspLoadImageNotifyRoutineCount 确定当前注册回调个数。


              08

              删除杀软回调效果

              • 运行 Mimikatz 测试:

                这里测试国内外6款主流杀软,国内两款:某0 数字卫士和某绒,国外四款:卡巴斯基、Windows Defender、eset-nod32、avast;由于该项目主要消弱杀软或EDR的动态查杀能力,这里,使用内存加载 Mimikatz 的方式进行测试,注意该项目需要免杀或者重新构思编码,比如:换个合法漏洞驱动,编写自定义驱动使用 kdmapper 改良版等项目内存加载等,总之,首先要保证你的测试程序在杀软环境下要免杀而且能正常执行功能。在这里,只考虑删除杀软回调对杀软的影响,其他相关因素需要通过某些方法进行消除处理。

                测试结果:

                • eset-nod32、avast、某绒、某0 数字卫士:无需删除杀软回调,内存加载 Mimikatz,就可以正常执行功能;

                • 卡巴斯基、Windows Defender:删除全部回调前,内存加载 Mimikatz 被查杀;删除全部回调后,内存加载 Mimikatz 正常执行功能。

              • 注册表修改测试:

                • 删除全部回调前,某些注册表键值不可修改;

                • 删除全部回调后,某些注册表键值现在已经可以修改。


              09

              备注


              如果,需要该项目 Windows 全版本兼容,则需要将所有版本的特征码一一找出进行总结适配,下面有一个github项目可以节省你很多时间。

              https://github.com/bigbang95/ntoskrnl


              10

              参考链接

              文章:

              对抗临近 | 红队大佬的私人秘籍:EDR绕过技术曝光!(https://mp.weixin.qq.com/s/jva2d8nLz6ti8fLTR0O-wQ)

              https://br-sn.github.io/Removing-Kernel-Callbacks-Using-Signed-Drivers/

              项目:

              https://github.com/br-sn/CheekyBlinder

              https://github.com/lawiet47/STFUEDR


              11

              声明


              本文所述方法,仅供安全研究使用。凡擅自用于违法用途,将被追究法律责任。其违法行为均与本人无关。特此声明!



              [培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

              收藏
              点赞4
              打赏
              分享
              最新回复 (12)
              雪    币: 19827
              活跃值: (29423)
              能力值: ( LV2,RANK:10 )
              在线值:
              发帖
              回帖
              粉丝
              秋狝 2023-10-20 16:17
              2
              1
              感谢分享
              雪    币: 3129
              活跃值: (2497)
              能力值: ( LV8,RANK:147 )
              在线值:
              发帖
              回帖
              粉丝
              Roger 1 2023-10-20 16:53
              3
              0
              mark
              雪    币: 9172
              活跃值: (2056)
              能力值: ( LV2,RANK:10 )
              在线值:
              发帖
              回帖
              粉丝
              PPTV 2023-10-20 20:46
              4
              0
              mark
              雪    币: 1170
              活跃值: (1173)
              能力值: ( LV2,RANK:10 )
              在线值:
              发帖
              回帖
              粉丝
              HUAJIEN 2023-10-20 22:20
              5
              0
              感谢分享
              雪    币: 5419
              活跃值: (11840)
              能力值: ( LV12,RANK:312 )
              在线值:
              发帖
              回帖
              粉丝
              一半人生 5 2023-10-23 08:29
              6
              0
              发文不易,先点赞。
              上升到驱动级别对抗风险极大,部分Edr创建回调成功后,会保存了一份记录。利用上述同样的方式,启动线程检测是否被删除,不友好的可能直接上报蓝屏了。
              基于r3程序策略变种来对抗edr回调也是不错的方式,比如基于Process Herpaderping,Process Reimaging新的变种研究。
              雪    币: 420
              活跃值: (579)
              能力值: ( LV3,RANK:20 )
              在线值:
              发帖
              回帖
              粉丝
              bigbang95 2023-10-23 17:02
              7
              0
              一半人生 发文不易,先点赞。 上升到驱动级别对抗风险极大,部分Edr创建回调成功后,会保存了一份记录。利用上述同样的方式,启动线程检测是否被删除,不友好的可能直接上报蓝屏了。 基于r3程序策略变种来对抗ed ...
              感谢大佬指点,如果在EDR回调函数地址开始处直接 ret,或 直接在回调数组某回调元素结构中替换掉 EDR 原始回调函数地址,会不会能绕过 EDR 的这种 "记录" 保护呢?
              雪    币: 1473
              活跃值: (9290)
              能力值: ( LV13,RANK:385 )
              在线值:
              发帖
              回帖
              粉丝
              TkBinary 5 2023-10-23 17:21
              8
              0
              bigbang95 感谢大佬指点,如果在EDR回调函数地址开始处直接 ret,或 直接在回调数组某回调元素结构中替换掉 EDR 原始回调函数地址,会不会能绕过 EDR 的这种 "记录" 保护呢?
              你能想到的EDR也能想到. 对抗无止境. 比如保存得 "记录" 可否就是回调函数地址头部得x个字节. 看看自己有没有被改过. 这块反正挺麻烦得.就看有没有保存这块所谓得"记录" 其实摘钩啥的很简单. 难得是绕过. 就是让你在我系统上运行.我不动你得情况下.如何让你检测不到. 这个很麻烦. 
              雪    币: 420
              活跃值: (579)
              能力值: ( LV3,RANK:20 )
              在线值:
              发帖
              回帖
              粉丝
              bigbang95 2023-10-24 10:10
              9
              0
              TkBinary 你能想到的EDR也能想到. 对抗无止境. 比如保存得 "记录" 可否就是回调函数地址头部得x个字节. 看看自己有没有被改过. 这块反正挺麻烦得.就看有没有保存这块所谓得" ...
              是啊,哈哈,像”一半人生“大佬说的,得研究新变种
              雪    币: 405
              活跃值: (1950)
              能力值: ( LV4,RANK:50 )
              在线值:
              发帖
              回帖
              粉丝
              wowocock 1 2023-10-24 10:52
              10
              0
              早都有保护了。
              _int64 __fastcall RestoreSelf(char a1)
              {
                unsigned __int64 v1; // rax
                unsigned __int64 v2; // rax
                __int64 result; // rax

                *(_QWORD *)(qword_30CE8 + 240) = ShutDownDispatch;
                if ( a1 )
                  IoRegisterShutdownNotification(gDeviceObject);
                _disable();
                v1 = __readcr0();
                __writecr0(v1 & 0xFFFFFFFFFFFEFFFFui64);
                *(__m128i *)CreateProcessNotifyRoutine = _mm_loadu_si128(&CodeCreateProcessNotifyRoutine);
                *(__m128i *)LoadImageNotifyRoutine = _mm_loadu_si128(&CodeLoadImageNotifyRoutine);
                *(__m128i *)RegisterCallback = _mm_loadu_si128((const __m128i *)&CodeRegisterCallback);
                *(__m128i *)ShutDownDispatch = _mm_loadu_si128(CodeShutDownDispatch);
                v2 = __readcr0();
                result = v2 ^ 0x10000;
                __writecr0(result);
                _enable();
                return result;
              }

              char __fastcall DoSelfProtect(__int64 a1, char a2)
              {
                void **v2; // rax
                __int64 v4; // rbp
                int v5; // eax
                void (__fastcall *v6)(PVOID *, __int64, __int64); // rcx
                char v7; // di
                _QWORD *v8; // rbx
                __int64 v9; // rsi
                unsigned int v10; // eax
                unsigned __int64 SystemRoutineAddress; // rbx
                unsigned __int64 v12; // rax
                _DWORD *v13; // r8
                __int64 v14; // r9
                unsigned int v15; // ecx
                __int64 v16; // rdx
                __int64 v17; // rax
                char v18; // di
                _QWORD *v19; // rbx
                __int64 v20; // rsi
                __int64 v21; // rdx
                __int64 v22; // rcx
                unsigned int v23; // esi
                char v24; // r12
                __int64 v25; // rdi
                _QWORD *v26; // rbx
                unsigned int v27; // edx
                __int64 v28; // r8
                unsigned __int64 v29; // rax
                unsigned __int64 v30; // rax
                unsigned int v31; // edx
                __int64 v32; // r8
                unsigned __int64 v33; // rax
                unsigned __int64 v34; // rax
                unsigned int v35; // edx
                __int64 v36; // r8
                unsigned __int64 v37; // rax
                unsigned __int64 v38; // rax
                __int64 v39; // rdx
                __int64 v40; // r8
                unsigned __int64 v41; // rax
                unsigned __int64 v42; // rax
                _QWORD *v43; // rbx
                unsigned int v44; // esi
                __int64 v45; // rdi
                void ***v46; // r12
                char v47; // bl
                __int64 v48; // rdi
                __int64 v49; // r10
                void **v50; // rsi
                char Dst[8]; // [rsp+30h] [rbp-58h] BYREF
                char v53[16]; // [rsp+38h] [rbp-50h] BYREF
                char v54[16]; // [rsp+48h] [rbp-40h] BYREF
                __int64 v55; // [rsp+58h] [rbp-30h]
                __int64 v56; // [rsp+60h] [rbp-28h]
                int v57; // [rsp+68h] [rbp-20h]
                void *retaddr; // [rsp+88h] [rbp+0h] BYREF
                __int64 v59; // [rsp+90h] [rbp+8h] BYREF

                v2 = &retaddr;
                v4 = a1;
                if ( byte_3541F != 1 )
                {
                  LODWORD(v59) = 0;
                  v5 = sub_1EB94(a1, &v59);
                  v6 = LoadImageNotifyRoutine;
                  if ( v5 >= 0 )
                  {
                    if ( !(_DWORD)v59 )
                      goto LABEL_13;
                    v7 = 0;
                    if ( !(_DWORD)v59 )
                      goto LABEL_13;
                    v8 = (_QWORD *)(v4 + 8);
                    v9 = (unsigned int)v59;
                    do
                    {
                      if ( (void (__fastcall *)(PVOID *, __int64, __int64))*v8 == LoadImageNotifyRoutine )
                      {
                        v7 = 1;
                      }
                      else
                      {
                        if ( sub_1B6A0(*v8) )
                        {
                          PsRemoveLoadImageNotifyRoutine(*v8);
                          sub_1B598(*v8);
                        }
                        v6 = LoadImageNotifyRoutine;
                      }
                      v8 += 3;
                      --v9;
                    }
                    while ( v9 );
                    if ( !v7 )
              LABEL_13:
                      PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine);
                  }
                  v10 = IsAppServer(v6);
                  if ( v10 != 13 )
                  {
                    memmove(Dst, (const void *)(((unsigned __int64)v10 << 6) + 156960), 0x40ui64);
                    SystemRoutineAddress = MmGetSystemRoutineAddress(v53);
                    v12 = MmGetSystemRoutineAddress(v54);
                    if ( SystemRoutineAddress >= v12 )
                      LODWORD(v12) = SystemRoutineAddress + 512;
                    v59 = 0i64;
                    if ( (int)sub_1E998(SystemRoutineAddress, v12, v55, v56, (__int64)&v59) >= 0 )
                    {
                      v13 = (_DWORD *)v4;
                      v14 = v57 + v59 + *(int *)(v57 + v59) + 4;
                      v15 = 0;
                      v16 = 0i64;
                      while ( 1 )
                      {
                        v17 = *(_QWORD *)(v14 + 8 * v16);
                        if ( !v17 )
                          break;
                        *v13 = 2;
                        ++v16;
                        v13 += 6;
                        ++v15;
                        *((_QWORD *)v13 - 2) = *(_QWORD *)(v17 & 0xFFFFFFFFFFFFFFF8ui64);
                        if ( v16 >= 64 )
                          goto LABEL_30;
                      }
                      if ( !v15 )
                        goto LABEL_30;
                      v18 = 0;
                      v19 = (_QWORD *)(v4 + 8);
                      v20 = v15;
                      do
                      {
                        if ( (NTSTATUS (__fastcall *)(__int64, __int64, char))*v19 == CreateProcessNotifyRoutine )
                        {
                          v18 = 1;
                        }
                        else if ( sub_1B6A0(*v19) )
                        {
                          LOBYTE(v21) = 1;
                          PsSetCreateProcessNotifyRoutine(*v19, v21);
                          sub_1B598(*v19);
                        }
                        v19 += 3;
                        --v20;
                      }
                      while ( v20 );
                      if ( !v18 )
              LABEL_30:
                        PsSetCreateProcessNotifyRoutine(CreateProcessNotifyRoutine, 0i64);
                    }
                  }
                  LODWORD(v59) = 0;
                  if ( (int)sub_1EC9C(v4, &v59) >= 0 )
                  {
                    v23 = v59;
                    if ( (_DWORD)v59 )
                    {
                      v24 = 0;
                      v25 = 0i64;
                      if ( !(_DWORD)v59 )
                        goto LABEL_41;
                      v26 = (_QWORD *)(v4 + 8);
                      do
                      {
                        if ( (__int64 (__fastcall *)(__int64, int, _QWORD *))*v26 == RegisterCallback )
                        {
                          v24 = 1;
                        }
                        else if ( sub_1B6A0(*v26) )
                        {
                          CmUnRegisterCallback(*(_QWORD *)(v4 + 24 * v25 + 16));
                          sub_1B598(*v26);
                        }
                        v25 = (unsigned int)(v25 + 1);
                        v26 += 3;
                      }
                      while ( (unsigned int)v25 < v23 );
                      if ( !v24 )
                      {
              LABEL_41:
                        byte_35C49 = 1;
                        RegRegisterCallback();
                      }
                    }
                    else
                    {
                      RegRegisterCallback();
                    }
                  }
                  v27 = 0;
                  v28 = 0i64;
                  do
                  {
                    if ( *((_BYTE *)RegisterCallback + v28) != byte_34868[v28 + 0x10000] )
                      break;
                    ++v27;
                    ++v28;
                  }
                  while ( v27 < 0x10 );
                  if ( v27 != 16 )
                  {
                    byte_35C49 = 1;
                    _disable();
                    v29 = __readcr0();
                    __writecr0(v29 & 0xFFFFFFFFFFFEFFFFui64);
                    *(__m128i *)RegisterCallback = _mm_loadu_si128((const __m128i *)&CodeRegisterCallback);
                    v30 = __readcr0();
                    __writecr0(v30 ^ 0x10000);
                    _enable();
                  }
                  v31 = 0;
                  v32 = 0i64;
                  do
                  {
                    if ( *((_BYTE *)CreateProcessNotifyRoutine + v32) != byte_25C38[v32 + 0x10000] )
                      break;
                    ++v31;
                    ++v32;
                  }
                  while ( v31 < 0x10 );
                  if ( v31 != 16 )
                  {
                    byte_35C49 = 1;
                    _disable();
                    v33 = __readcr0();
                    __writecr0(v33 & 0xFFFFFFFFFFFEFFFFui64);
                    *(__m128i *)CreateProcessNotifyRoutine = _mm_loadu_si128(&CodeCreateProcessNotifyRoutine);
                    v34 = __readcr0();
                    __writecr0(v34 ^ 0x10000);
                    _enable();
                  }
                  v35 = 0;
                  v36 = 0i64;
                  do
                  {
                    if ( *((_BYTE *)LoadImageNotifyRoutine + v36) != byte_34850[v36 + 0x10000] )
                      break;
                    ++v35;
                    ++v36;
                  }
                  while ( v35 < 0x10 );
                  if ( v35 != 16 )
                  {
                    byte_35C49 = 1;
                    _disable();
                    v37 = __readcr0();
                    __writecr0(v37 & 0xFFFFFFFFFFFEFFFFui64);
                    *(__m128i *)LoadImageNotifyRoutine = _mm_loadu_si128(&CodeLoadImageNotifyRoutine);
                    v38 = __readcr0();
                    __writecr0(v38 ^ 0x10000);
                    _enable();
                  }
                  if ( *(__int64 (__fastcall **)(__int64, __int64))(qword_30CE8 + 240) != ShutDownDispatch )
                  {
                    *(_QWORD *)(qword_30CE8 + 240) = ShutDownDispatch;
                    byte_35C49 = 1;
                  }
                  v39 = 0i64;
                  v40 = 0i64;
                  do
                  {
                    LOBYTE(v2) = byte_34880[v40 + 0x10000];
                    if ( *((_BYTE *)ShutDownDispatch + v40) != (_BYTE)v2 )
                      break;
                    v39 = (unsigned int)(v39 + 1);
                    ++v40;
                  }
                  while ( (unsigned int)v39 < 0x10 );
                  if ( (_DWORD)v39 != 16 )
                  {
                    byte_35C49 = 1;
                    _disable();
                    v41 = __readcr0();
                    __writecr0(v41 & 0xFFFFFFFFFFFEFFFFui64);
                    *(__m128i *)ShutDownDispatch = _mm_loadu_si128(CodeShutDownDispatch);
                    v42 = __readcr0();
                    v2 = (void **)(v42 ^ 0x10000);
                    __writecr0((unsigned __int64)v2);
                    _enable();
                  }
                  if ( gDeviceObject )
                  {
                    v43 = (_QWORD *)qword_31CE0;
                    v44 = 0;
                    if ( !qword_31CE0 )
                    {
                      v2 = (void **)sub_17544(v22, v39, v40, 16i64);
                      qword_31CE0 = (__int64)v2;
                      if ( !v2 )
                        return (char)v2;
                      LOBYTE(v2) = MmIsAddressValid(v2);
                      if ( !(_BYTE)v2 )
                        return (char)v2;
                      v43 = (_QWORD *)qword_31CE0;
                    }
                    if ( (_QWORD *)*v43 != v43 )
                    {
                      v45 = v43[1];
                      if ( (_QWORD *)v45 != v43 )
                      {
                        v46 = (void ***)v4;
                        do
                        {
                          LOBYTE(v2) = MmIsAddressValid(v45);
                          if ( !(_BYTE)v2 )
                            break;
                          LOBYTE(v2) = MmIsAddressValid(*(_QWORD *)(v45 + 16));
                          if ( !(_BYTE)v2 )
                            break;
                          LOBYTE(v2) = MmIsAddressValid(*(_QWORD *)(*(_QWORD *)(v45 + 16) + 8i64));
                          if ( !(_BYTE)v2 )
                            break;
                          v2 = *(void ***)(v45 + 16);
                          ++v44;
                          *v46 = v2;
                          v45 = *(_QWORD *)(v45 + 8);
                          ++v46;
                        }
                        while ( (_QWORD *)v45 != v43 );
                        if ( v44 )
                        {
                          v47 = 0;
                          v48 = v44;
                          do
                          {
                            if ( *(_QWORD *)v4 == gDeviceObject )
                            {
                              v47 = 1;
                            }
                            else if ( a2 )
                            {
                              v2 = (void **)sub_1B6A0(*(_QWORD *)(*(_QWORD *)(*(_QWORD *)v4 + 8i64) + 24i64));
                              v50 = v2;
                              if ( v2 )
                              {
                                LOBYTE(v2) = IoUnregisterShutdownNotification(v49);
                                v50[24] = *(void **)(*(_QWORD *)(*(_QWORD *)v4 + 8i64) + 240i64);
                              }
                            }
                            v4 += 8i64;
                            --v48;
                          }
                          while ( v48 );
                          if ( !v47 )
                            LOBYTE(v2) = IoRegisterShutdownNotification(gDeviceObject);
                        }
                      }
                    }
                  }
                }
                return (char)v2;
              }
              雪    币: 405
              活跃值: (1950)
              能力值: ( LV4,RANK:50 )
              在线值:
              发帖
              回帖
              粉丝
              wowocock 1 2023-10-24 10:57
              11
              0
              保护调用时机也是五花八门,线程,DPC,每次磁盘访问,每次网络访问,各种调度切换==。反正你很难同时干掉。
              雪    币: 420
              活跃值: (579)
              能力值: ( LV3,RANK:20 )
              在线值:
              发帖
              回帖
              粉丝
              bigbang95 2023-10-24 11:31
              12
              0
              wowocock 保护调用时机也是五花八门,线程,DPC,每次磁盘访问,每次网络访问,各种调度切换==。反正你很难同时干掉。
              嗯嗯,看来只有新技术、新变种,才行啊
              雪    币: 0
              活跃值: (91)
              能力值: ( LV2,RANK:10 )
              在线值:
              发帖
              回帖
              粉丝
              luisfan 2023-10-25 11:07
              13
              0
              游客
              登录 | 注册 方可回帖
              返回