【Day 06 】- Module 隐藏大法,不可能再被发现了吧 / _ \(基於 VAD 断链的隐藏方法)

Agenda

  • 资安宣言
  • 测试环境与工具
  • 学习目标
  • 技术原理与程序码
  • References
  • 下期预告

资安宣言


撰写本系列文章目的在於提升资讯安全之实务能力,
并透过实作体悟到资讯安全领域的重要性,
本系列所有文章之内容皆有一定技术水平,
不得从事非法行为、恶意攻击等非法活动,
「一切不合法规之行为皆受法律所约束」,
为了避免造成公司、厂商或玩家之间困扰,
所有实作不会拿已上市产品、Online Game 等等来作范例学习,
且部分具有深度、价值之内容,将会提升一定阅读门槛(不对该技术做分析、解说),
请勿透过本系列文章所学,从事任何非法活动,请不要以身试法!!!


测试环境与工具

学习目标

  • 1.了解 VadRoot 二元树
  • 2.列举 VadRoot 二元树
  • 3.修改 VadRoot 二元树,达成隐藏 DLL 的效果
  • 4.探讨 User mode 对 VadRoot 的影响

技术原理与程序码

首先开始前要先说一下,
小弟我目前还属於菜鸟阶段,正不断努力学习中,
若有发现错误或不妥之处还请不吝赐教。
欢迎大家多多留言,互相交流交流。

那就开始今天的主题罗~

一、什麽是 VadRoot 二元树?

二、这棵树长什麽样子?

  1. 使用 Administrator 权限打开 WinDbg
  2. 选 Kernel Debug...
    • (需要打开测试模式,依照提示的指示进行操作)
  3. 选 Local 按确定
    • (需要打开测试模式,依照提示的指示进行操作)
  4. 输入 !process 0 0 notepad.exe
    • 查看 notepad.exe
    lkd> !process 0 0 notepad.exe
    PROCESS ffffe30717823080
        SessionId: 1  Cid: 1534    Peb: a1635df000  ParentCid: 1054
        DirBase: 1285ff000  ObjectTable: ffffcf0189d605c0  HandleCount: 260.
        Image: notepad.exe
    
  5. 输入 dt _eprocess ffffe30717823080
    • 查看 notepad.exe 的 EProcess
    lkd> dt _eprocess ffffe30717823080
    nt!_EPROCESS
       +0x000 Pcb              : _KPROCESS
       +0x2d8 ProcessLock      : _EX_PUSH_LOCK
       +0x2e0 UniqueProcessId  : 0x00000000`00001534 Void
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
    -->+0x628 VadRoot          : _RTL_AVL_TREE
       +0x630 VadHint          : 0xffffe307`16a0a650 Void
       +0x638 VadCount         : 0x7a
       +0x640 VadPhysicalPages : 0
       +0x648 VadPhysicalPagesLimit : 0
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
  6. 输入 !vad
    • 第二个查看 VAD 的方式:
    • MSDN-vad


      (太多了列不完,大家自己看)

好,到这大概已经简单带过什麽是 VAD 了 XD

现在要来写个 Driver 列举这棵树的 DLL 资讯,
当然,我们得先来看看这棵树的整体样貌,以及了解到底要「列举什麽」:

  1. 在 EProcess(ffffd88966e915c0)中看到有个 _RTL_AVL_TREE 类型的 VadRoot:
    • 输入 dt _eprocess ffffd88966e915c0
    +0x628 VadRoot          : _RTL_AVL_TREE 
    
    • 太多找不到 VadRoot?
    • 输入:dt _eprocess ffffd88966e915c0 VadRoot
    • 会直接帮你过滤:
    lkd> dt _eprocess ffffd88966e915c0 vadroot
    nt!_EPROCESS
       +0x628 VadRoot : _RTL_AVL_TREE
    
  2. 输入 dt _RTL_AVL_TREE
    • 查看 _RTL_AVL_TREE 长什麽样子
    lkd> dt _RTL_AVL_TREE
    nt!_RTL_AVL_TREE
       +0x000 Root             : Ptr64 _RTL_BALANCED_NODE
    
    • 发现这是一个「根」
  3. 输入 dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE
    • 查看 Root 底下有什麽东东
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE
    nt!_RTL_BALANCED_NODE
       +0x000 Children         : [2] 0xffffd889`6858fd10 _RTL_BALANCED_NODE
       +0x000 Left             : 0xffffd889`6858fd10 _RTL_BALANCED_NODE
       +0x008 Right            : 0xffffd889`6864ec80 _RTL_BALANCED_NODE
       +0x010 Red              : 0y0
       +0x010 Balance          : 0y00
       +0x010 ParentValue      : 0x78
    
    • 这边可以看出这个节点有两个 Children,
    • 分别是 Left、Right,每个 Children 都有个指针
    • 进去 Children 看的话可以发现和 Left、Right 是一样的:
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE Children[0]
    nt!_RTL_BALANCED_NODE
       +0x000 Children    : [0] 0xffffd889`6858fd10 _RTL_BALANCED_NODE
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE Children[1]
    nt!_RTL_BALANCED_NODE
       +0x000 Children    : [1] 0xffffd889`6864ec80 _RTL_BALANCED_NODE
    
  4. 往 Left 走
    lkd> dt 0xffffd889`6858fd10 _RTL_BALANCED_NODE
    nt!_RTL_BALANCED_NODE
       +0x000 Children         : [2] 0xffffd889`671c59d0 _RTL_BALANCED_NODE
       +0x000 Left             : 0xffffd889`671c59d0 _RTL_BALANCED_NODE
       +0x008 Right            : 0xffffd889`687a8a90 _RTL_BALANCED_NODE
       +0x010 Red              : 0y1
       +0x010 Balance          : 0y01
       +0x010 ParentValue      : 1
    
  5. 标签 TAG:
    • 每个节点都会有个 TAG
    • 这个 TAG 可以让我们知道:这个节点的「范围」储存了什麽样的资讯
    • 而每个 TAG 都储存在这个节点位置的 -C 位置
    • 意思就是:
    • 0xffffd889`687a8a90 这个节点要往前 C 个位置
    • 0xffffd889`687a8a90 -c = 0xFFFFD889687A8A84
    • 所以输入:db FFFFD889687A8A84
    lkd> db FFFFD889687A8A84
    ffffd889`687a8a84  56 61 64 -- -- 67  Vad zi.. `.\.8.g
    ffffd889`687a8a94  89 d8 ff -- -- 68  ......vh......Xh
    ffffd889`687a8aa4  89 d8 ff -- -- 00  ................
    
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
    
  6. Vad Tag
    • 节点的 Tag 是 Vad 就代表是 _MMVAD Structure
    • 为什麽我知道?某本书说的,我忘了哪一本。
    • 再来就是旧版本的 Windows 就是 _MMVAD Structure
    • 所以不难猜到 _MMVAD Structure
    • 输入 dt _MMVAD
    lkd> dt _MMVAD
    nt!_MMVAD
       +0x000 Core             : _MMVAD_SHORT
       +0x040 u2               : <unnamed-tag>
       +0x048 Subsection       : Ptr64 _SUBSECTION
       +0x050 FirstPrototypePte : Ptr64 _MMPTE
       +0x058 LastContiguousPte : Ptr64 _MMPTE
       +0x060 ViewLinks        : _LIST_ENTRY
       +0x070 VadsProcess      : Ptr64 _EPROCESS
       +0x078 u4               : <unnamed-tag>
       +0x080 FileObject       : Ptr64 _FILE_OBJECT
    
  7. 输入:dt _mmvad 0xffffd889687a8a90
    • 查看这个节点的 _MMVAD 内容
    lkd> dt _mmvad 0xffffd889687a8a90
    nt!_MMVAD
       +0x000 Core             : _MMVAD_SHORT
       +0x040 u2               : <unnamed-tag>
       +0x048 Subsection       : 0xffffd889`66c422d0 _SUBSECTION
       +0x050 FirstPrototypePte : 0xffff9d02`3a34e2c0 _MMPTE
       +0x058 LastContiguousPte : 0xffff9d02`3a34e390 _MMPTE
       +0x060 ViewLinks        : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x070 VadsProcess      : 0xffffd889`66e915c1 _EPROCESS
       +0x078 u4               : <unnamed-tag>
       +0x080 FileObject       : (null) 
    
  8. 输入:dt 0xffffd889`66c422d0 _SUBSECTION
    • 重点要看 Subsection
    • 因为这里面包含我们要找的东东
    • 我每个都看过了,所以直接告诉大家捷径
    lkd> dt 0xffffd889`66c422d0 _SUBSECTION
    nt!_SUBSECTION
       +0x000 ControlArea      : 0xffffd889`66c42250 _CONTROL_AREA
       +0x008 SubsectionBase   : 0x?? _MMPTE
       +0x010 NextSubsection   : 0x?? _SUBSECTION
       +0x018 GlobalPerSessionHead : _RTL_AVL_TREE
       +0x018 CreationWaitList : (null) 
       +0x018 SessionDriverProtos : (null) 
       +0x020 u                : <unnamed-tag>
       +0x024 StartingSector   : 0
       +0x028 NumberOfFullSectors : 2
       +0x02c PtesInSubsection : 1
       +0x030 u1               : <unnamed-tag>
       +0x034 UnusedPtes       : 0y00.. ..00 (0)
       +0x034 ExtentQueryNeeded : 0y0
       +0x034 DirtyPages       : 0y0
       +0x034 u2               : <unnamed-tag>
    
  9. 输入 dt 0xffffd889`66c42250 _CONTROL_AREA
    • 重点要看 ControlArea
    • 因为这里面包含我们要找的东东
    • 我每个都看过了,所以直接告诉大家捷径
    lkd> dt 0xffffd889`66c42250 _CONTROL_AREA
    nt!_CONTROL_AREA
       +0x000 Segment          : 0xffff9d02`3a2f7690 _SEGMENT
       +0x008 ListHead         : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x018 NumberOfSectionReferences : 1
       +0x020 NumberOfPfnReferences : 0x1b
       +0x028 NumberOfMappedViews : 0x3c
       +0x030 NumberOfUserReferences : 0x3d
       +0x038 u                : <unnamed-tag>
       +0x03c u1               : <unnamed-tag>
       +0x040 FilePointer      : _EX_FAST_REF
       +0x048 ControlAreaLock  : 0n0
       +0x04c ModifiedWriteCount : 0
       +0x050 WaitList         : (null) 
       +0x058 u2               : <unnamed-tag>
       +0x068 FileObjectLock   : _EX_PUSH_LOCK
       +0x070 LockedPages      : 1
       +0x078 u3               : <unnamed-tag>
    
  10. 输入:dt 0xffffd889`66c42250 _CONTROL_AREA FilePointer.
    • 查看 FilePointer 地址
    lkd> dt 0xffffd889`66c42250 _CONTROL_AREA FilePointer.
    nt!_CONTROL_AREA
       +0x040 FilePointer  : 
          +0x000 Object       : 0xffffd889`66c4423e Void
          +0x000 RefCnt       : 0y1110
          +0x000 Value        : 0xffffd889`66c4423e
    
  11. 输入:dt _file_object 0xffffd88966c44230
    • 把第十步拿到的地址,最後面那个数字改成「0」
    • 因为最後面那个数字会随机变化:1 - F
    • 然後这就是我们要找的东西
    • 下面可以看到 FileName 了
    • 手动列举到了 profapi.dll 这个 DLL
    lkd> dt _file_object 0xffffd88966c44230
    nt!_FILE_OBJECT
       +0x000 Type             : 0n5
       +0x002 Size             : 0n216
       +0x008 DeviceObject     : 0x?? _DEVICE_OBJECT
       +0x010 Vpb              : 0x?? _VPB
       +0x018 FsContext        : 0x?? Void
       +0x020 FsContext2       : 0x?? Void
       +0x028 SectionObjectPointer : 0x?? _SECTION_OBJECT_POINTERS
       +0x030 PrivateCacheMap  : (null) 
       +0x038 FinalStatus      : 0n0
       +0x040 RelatedFileObject : 0x?? _FILE_OBJECT
       +0x048 LockOperation    : 0 ''
       +0x049 DeletePending    : 0 ''
       +0x04a ReadAccess       : 0x1 ''
       +0x04b WriteAccess      : 0 ''
       +0x04c DeleteAccess     : 0 ''
       +0x04d SharedRead       : 0x1 ''
       +0x04e SharedWrite      : 0 ''
       +0x04f SharedDelete     : 0x1 ''
       +0x050 Flags            : 0x44042
    ->>+0x058 FileName         : _UNICODE_STRING "\Windows\System32\profapi.dll"
       +0x068 CurrentByteOffset : _LARGE_INTEGER 0x0
       +0x070 Waiters          : 0
       +0x074 Busy             : 0
       +0x078 LastLock         : (null) 
       +0x080 Lock             : _KEVENT
       +0x098 Event            : _KEVENT
       +0x0b0 CompletionContext : (null) 
       +0x0b8 IrpListLock      : 0
       +0x0c0 IrpList          : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x0d0 FileObjectExtension : (null)
    

所以我们的 Driver 要做什麽事情ㄋ??
第一种方法:

  1. 先找到要隐藏的目标,我们称这个节点为 X
  2. 将 X 的下一个节点和 X 上面的节点合并(连起来)就可以了

第二种方法:

  1. 直接砍掉 FILE OBJECT(这个不好,因为不怎麽稳定)

第三种方法:

  1. 改 _FILE_OBJECT 的 Pointer

效果呈现如下:
隐藏前:

隐藏後:

/images/emoticon/emoticon01.gif

成功击败 Process Explorer :D

注意:已经有按照名称排序。

最後说一下,当我自己写 Driver 快写好的时候..
我发现 Blackbone 已经把 VAD Unlink 写好了!!!
差点吐血 XDD
/images/emoticon/emoticon05.gif

所以大家想看原始码就到 Blackbone 看吧~~
附上连结在这...
就不讲解程序码了,因为我没时间了,因为我把时间拿去写 Driver 了.. ...

以上所有实作、测试都是在 16299.15 测试的,其它版本逻辑一样,作法不同。

然後最好是不要想把 ntdll.dll 隐藏,
因为当系统需要它的时候又找不到时,就会...XDDDDDDDDDDDDDDDDD

分享一下写这个驱动时常常看到的画面:

/images/emoticon/emoticon08.gif

最後简单整理了一下思维:

  1. [R0、R3] PEB 断链是为了防止:
    • CreateToolhelp32Snapshot/EnumProcessModulesEx... ...
    • 等等的这些 API 列举到注入的 DLL。
  2. [R0、R3] 删除 PE 头部(MZ/0x5A4D/Header)
    • 是为了防止透过暴力搜寻 PE 特徵来找出 DLL。
  3. [R0] 修改 VadRoot
    • 是为了防止透过 ZwQueryVirtualMemory 之类的低效率方式找到 DLL。

所以还有没有找出 DLL 的方法??
答:有阿 XD
只要 DLL 存在就一定会占用记忆体空间,
某个 DLL 与某个 DLL 之间的记忆体地址差距
相较其它 DLL 来的大是不是就感觉有点问题?
东西就在 Memory 里面,不管躲得再好都有机会找出来,

以下补充:
在 User mode 有没有什麽其他有趣的 DLL 隐藏法?
答:有,请发挥你/你的想像力,想像力就是你/你的超能力!
这里来讲几个 XD

  1. 研究 FreeLibrary 函数
    • 能不能执行这个函数,然後只执行「一半」就好?
  2. 备份还原法
    • 先正常 Load DLL
    • 然後「备份起来」
    • 再正常 FreeLibrary 将 DLL 完整卸载
    • 再还原一开始备份好的数据 XD
  3. 自建 LoadLibrary 函数
    • 既然正常载入都会有痕迹
    • 那我乾脆自己实现载入 DLL 的功能
    • 这样是不是就能避开所有系统已知的预设痕迹了呢?

好啦,这篇/这个 Module 系列就到这结束了!

大家若有发现哪里写得不好或错误的地方,都留个言讨论一下吧 XD
那我们下期见 o( ̄▽ ̄)ブ

References

下期预告


<<:  JavaScript入门 Day16_阵列2

>>:  Day14-"字串"

Day30 测试写起乃 - 完赛感言!

终於来到第三十天了,来讲讲完赛感言吧! 其实这次是第二次参加铁人赛了,这次是不小心按到开赛所以根本没...

Day 18 - 产业研究分析浅谈

转换一下, 来谈谈PM的日常, 还有其他工作类型, 例如像是产业研究分析的任务, 尤其是针对想要选...

06. DB x tinker x seeder

连线设定其实也没什麽好介绍的,改 env 这种事 sail 已经弄好了。 即便现在,用 compos...

Multicasting for RxJava

在进入正题之前先让大家看看在 Reactive Programming 中的一种使用案例: val ...

Day 7-单元测试 NUnit 更多常用的特性-2 (基础-6)

如何撰写测试验证例外 — ExpectedExcetption 与 Assert.Throws(de...