FUTo: Bypassing Blacklight and IceSword

1.前言

摘要

自从介绍了FU,ROOTKIT世界就从执行系统HOOK,转移到了隐藏他们的存在。因为在攻击方面

新的改变,那么新的防御就不得被建立。新的算法被ROOTKIT检测系统采用。例如BLACKLIGHT,

就已经从原来检测ROOTKIT HOOK的存在,转变到试图找到是什么ROOTKIT正在隐藏。这篇文章将

讨论被Blacklight和IceSword用来检隐藏进程的算法。他也将展示,在ROOTKIT检测领域的缺点,

并且介绍一个基于FU的更完全的隐藏技术。

2.说明

在过去的一两年中,在ROOTKIT世界中已经有了很大的发展。最近的里程碑包括FU ROOTKIT,他

使用了Direct Kernel Object Manipulation (DKOM)

他是第一个ROOTKIT检测程序VICE的产生,,第一个主流ROOTKIT检测程序的诞生,Shadow Walker

的诞生,他是一个HOOK了memory manager进行隐藏的ROOTKIT。

3.Blacklight

这篇文章将先研究Blacklight,因为他的算法是被我所关注的。并且,IceSword采用了和

Blacklight类似的算法,因此如果在Blacklight中发现一个弱点,那么他也可能在IceSword

中存在。Blacklight占用一个USERLAND来检测进程,虽然简单,但是他的算法却是惊人的有效。

借助于,在开始时,创建TLS(ThreadLocal Storage)回收表,Blacklight产生了一个强大的反

调试特征。借助在进程对象完全创建前分流主进程,Blacklight的TLS表试图迷惑调试器。

这能被实现,因为TLS回收程序在进程完全初始化前被调用。Blacklight也有反调试措施,他能检

测到调试器对他的扫描。与其战胜反调试措施,作者更想使TSL程序失效。为了做到这个,作者使

用了一个叫LordPE的工具,他允许使用者编辑PE文件。作者使用这个工具来ZERO OUT TSL回收表,

这就使分流程序丧失了能力,并且给作者使用API监控器的可能。

你应该注意到,在使回收程序丧失能力的同时,将允许你attach一个调试器,但是当使用者在

Blacklight单击扫描时Blacklight的GUI将探测调试器并且退出。围绕着第二种反调试程序,作

者决定分析Blacklight调用过程,在最后作者将使用Rohitab API Monitor。

在测试中,你能看到调用API OpenProcess失败(tls zero is Blacklight without a TLS table)。

Blacklight试图用PID号为0x1CC, 0x1D0, 0x1D4, 0x1D8打开一个进程。在PID为0x0到0x4E1C

范围内,他调用OpenProcess来循环所有可能的PID。Blacklight保存一个表,表中包括他使用

PIDB方法能打开的所有进程。然后,他调用CreateToolhelp32Snapshot,这给了Blacklight第二

个进程表。然后Blacklight将两个表对比,看PIDB表中的进程,是否有不在被CreateToolhelp-

-32Snapshot函数返回中所产生的表中。如果有差异,这些进程就被认为是隐藏的进程,并报告给

使用者。

3.1) Windows OpenProcess

In Windows, OpenProcess是一个包装NtOpenProcess程序的函数。NtOpenProcess在内核中使用

NTOSKRNL.EXE被实现 他的函数原形是:

NTSTATUS NtOpenProcess (
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL);

NtOpenProcess执行三个主要的功能,

1.他检测被PsLookupProcessByProcessId调用的进程是否存在。

2.他试图给被ObOpenObjectByPointer调用的进程,开放一个句柄。

3.如果他成功的为进程打开一个句柄,他将给调用者返回那个句柄。

PsLookupProcessByProcessId是下一个研究的地方。一个突出的问题是,PsLookupProcessBy-

-ProcessId如何知道一个被给出的PID是有效的进程的一部分,答案就在下面的几行汇编码中,

PsLookupProcessByProcessId:
mov edi, edi
push ebp
mov ebp, esp
push ebx
push esi
mov eax, large fs:124h
push [ebp+arg_4]
mov esi, eax
dec dword ptr [esi+0D4h]
push PspCidTable

call ExMapHandleToPointer

从上面的汇编码中,我们清楚的知道ExMapHandleToPointer向PspCidTable询问以得到PID。

现在我们已经清晰的了解Blacklight如何检测隐藏的进程:

1.Blacklight通过一个有效的PID范围来开始一个循环,0 through 0x41DC.

2.在每个可能的PIDS上他调用OpenProcess。

3.OpenProcess调用NtOpenProcess

4.NtOpenProcessPsLookupProcessByProcessId来校验进程存在。

5.PsLookupProcessByProcessId使用PspCidTable来校验进程的存在。

6.NtOpenProcess调用ObOpenObjectByPointer来得到进程句柄。

7.如果OpenProcess成功,Blacklight将存储进程信息并继续循环。

8.如果用尽了所有可能的PID来建立进程列表。Blacklight将PIDB和创建的列表(由调用

CreateToolhelp32Snapshot所创建)进行对比,CreateToolhelp32Snapshot是一个API函数,

用来得到所有在系统运行的进程的快照。如果两个表有差别就意味着,含有隐藏进程。

3.2) The PspCidTable

The PspCidTable is a "handle table for process and thread client IDs".Every process'

PID corresponds to its location in the PspCidTable. The PspCidTable is a pointer

to a HANDLE_TABLE structure.

typedef struct _HANDLE_TABLE {

PVOID p_hTable;
PEPROCESS QuotaProcess;
PVOID UniqueProcessId;
EX_PUSH_LOCK HandleTableLock [4];
LIST_ENTRY HandleTableList;
EX_PUSH_LOCK HandleContentionEvent;
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
DWORD ExtraInfoPages;
DWORD FirstFree;
DWORD LastFree;
DWORD NextHandleNeedingPool;
DWORD HandleCount;
DWORD Flags;
};

WINDOWS提供了许多无输出的函数来利用和得到PspCidTable信息。他们包括:

- [ExCreateHandleTable]建立一个无进程句柄表,

- [ExDupHandleTable]当spawning一个进程时被调用

- [ExSweepHandleTable] 被用于进程 rundown.

- [ExDestroyHandleTable] 当进程存在是被调用.

- [ExCreateHandle] 创建新的进程表入口.

- [ExChangeHandle] 在句柄上被用来改变access mask .

- [ExDestroyHandle] 执行CloseHandle的功能.

- [ExMapHandleToPointer] 返回对象相应的句柄的地址.

- [ExReferenceHandleDebugIn] 跟踪句柄.

- [ExSnapShotHandleTables]被用来句柄搜索 (for example in oh.exe).]

下面是使用无输出函数移动PspCidTable表中的一个进程对象的代码。为了无输出函数的需要,

他使用编码的地址。然而,一个ROOTKIT能找到这些函数地址。

typedef PHANDLE_TABLE_ENTRY (*ExMapHandleToPointerFUNC)
( IN PHANDLE_TABLE HandleTable,
IN HANDLE ProcessId);

void HideFromBlacklight(DWORD eproc)
{
PHANDLE_TABLE_ENTRY CidEntry;
ExMapHandleToPointerFUNC map;
ExUnlockHandleTableEntryFUNC umap;
PEPROCESS p;
CLIENT_ID ClientId;

map = (ExMapHandleToPointerFUNC)0x80493285;

CidEntry = map((PHANDLE_TABLE)0x8188d7c8,
LongToHandle( *((DWORD*)(eproc+PIDOFFSET)) ) );
if(CidEntry != NULL)
{
CidEntry->Object = 0;
}
return;
}

既然PspCidTable的工作是保持追踪所有的进程和线程,那么ROOTKIT检测器使用PspCidTable来

找到隐藏进程也是合乎逻辑的。但是,使用一个单独的数据结构不是一个很好的算法,如果

ROOTKIT改变这个数据结构,系统和另外的程序将不能找到存在的隐藏进程,新的ROOTKIT检测

程序算法应该被设计成有重叠的依靠部分,以便一个单独的改变将被识别。

4) FUTo

为了验证现有的ROOTKIT检测程序算法有缺陷,作者建立了FUTO,FUTO是一个FU ROOTKIT的新版

本。FUTO添加了不是用任何函数调用就可操作PspCidTable的功能. 他使用DKOM技术来隐藏

PspCidTable表中的特殊的对象当实现FUTO的新功能时,有一些设计上的考虑。首先,像

ExMapHandleXXX功能,PspCidTable不能被内核输出,为了克服这个,FUTO自动的检测

PspCidTable借助于寻找PsLookupProcessByProcessId函数并且分解他寻找第一个函数调用,

当写这篇文章时,第一个函数调用总是对ExMapHandleToPointer。ExMapHandleToPointer装载

PspCidTable来作为他的第一个参数,使用这个知识,就相当直接的找到PspCidTable

PsLookupProcessByProcessId:
mov edi, edi
push ebp
mov ebp, esp
push ebx
push esi
mov eax, large fs:124h
push [ebp+arg_4]
mov esi, eax
dec dword ptr [esi+0D4h]
push PspCidTable
call ExMapHandleToPointer

一个寻找PspCidTable更明智的方法应该被写出,Opc0de写了一个更好的方法来探测像

PspCidTable这样的无输出变量, PspActiveProcessHead, PspLoadedModuleList等等。

Opc0des的方法不需要像FUTO那样的内存扫描。Instead Opc0de found that the

KdVersionBlockfield in the Process Control Region structure pointed to a

structureKDDEBUGGER_DATA32. The structure looks like this:

typedef struct _KDDEBUGGER_DATA32 {

DBGKD_DEBUG_DATA_HEADER32 Header;
ULONG KernBase;
ULONG BreakpointWithStatus; // address of breakpoint
ULONG SavedContext;
USHORT ThCallbackStack; // offset in thread data
USHORT NextCallback; // saved pointer to next callback frame
USHORT FramePointer; // saved frame pointer
USHORT PaeEnabled:1;
ULONG KiCallUserMode; // kernel routine
ULONG KeUserCallbackDispatcher; // address in ntdll

ULONG PsLoadedModuleList;
ULONG PsActiveProcessHead;
ULONG PspCidTable;

ULONG ExpSystemResourcesList;
ULONG ExpPagedPoolDescriptor;
ULONG ExpNumberOfPagedPools;

[...]

ULONG KdPrintCircularBuffer;
ULONG KdPrintCircularBufferEnd;
ULONG KdPrintWritePointer;
ULONG KdPrintRolloverCount;

ULONG MmLoadedUserImageList;

} KDDEBUGGER_DATA32, *PKDDEBUGGER_DATA32;


As the reader can see the structure contains pointers to many of the commonly

needed/used non-exported variables.这是一个更好的方法来寻到PspCidTable和另外变量

第二个设计考虑有一点麻烦,当FUTO从PspCidTable移动一个对象时,HANDLE_ENTRY被NULL代

替来表示的进程不存在,当被隐藏的进程被关闭时问题就产生了。当进程被关闭时,他将索

引进入PspCidTable并且丢弃NULL对象,这将引起蓝屏。解决方法是:首先,FUTO使用

PsSetCreateProcessNotifyRoutine建立一个进程通报程序。当进程创建时回收函数将被调用

但是更重要的是他将在进程被删除是调用。回收在隐藏的进程被终止前执行。因此,他在系

统崩溃前调用。当FUTO删除包含指向流氓对象的索引时,他将保存the HANDLE_ENTRYs的值同

样也保留,索引为以后使用,当进程被关闭时,FUTO将在关闭进程前恢复对象。

(下面的是废话~~~~~~~~~~~,不翻译了
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值