深入浅出 Windows系统的Rootkit对抗机制

admin 2024年11月3日00:18:23评论5 views字数 5638阅读18分47秒阅读模式

在  国庆专题: 深度了解”核晶“的工作原理并且手动实现一个自己的"核晶"

中我提到过 

"在XP时代的安全软件的工作是基于SSDT HOOK的,任何R3的API,包括ALPC/注入/各种乱七八糟的API都会通过ssdt/sssdt(shadow ssdt,是win32k的专用API)表到内核里面做操作.可以简单理解为安全软件对系统的颗粒度控制非常高.包括所谓的计划任务RPC都可以通过SSDT HOOK直接解码.(重点)

在win7x64之后,因为微软的PG系统存在(对PG感兴趣吗?感兴趣给公众号留言单独开一篇),ssdt 无法hook了,至少无法正常的手段hook(etw hook这种歪门邪道除外,这种歪门邪道非常容易被微软拉黑驱动签名从而再也无法加载).所以win7x64的安全都交给了垃圾的微软回调"

你可能很好奇为什么X64的杀毒软件如此的守规矩,现在,让我们来分析一下微软的Rootkit对抗机制PatchGuard

在研究/看别人研究PG的时候,我注意到了在KiFilterFiberContext函数里面的一个

深入浅出 Windows系统的Rootkit对抗机制
这个操作

windows10 patchguard 分析研究(版本:1803)
中,他们说这个是注册了一个 “TV” 回调
实际上是错误的,这个 “TV”字符串只是用于填充
ObjectAttributes.ObjectName这个字段的unicode_string,真实名字应该叫做”542875F90F9B47F497B64BA219CACF69”,仅此而已
如果你想跟进sub_140386010 你就错了,这个函数是一个巨大的、不可名状的、超过1W+行代码的PG检查函数(10990行代码),至于是什么我们会在下文说,先关心一下这个注册了什么回调:

根据
https://github.com/huoji120/ExecutiveCallbackObjects/tree/master/542875F90F9B47F497B64BA219CACF69
的说法,这个注册的对象是mssecflt.sys的SecKernelIntegrityCallback
深入浅出 Windows系统的Rootkit对抗机制
按照之前在ntoskrnl的信息,标注上符号
深入浅出 Windows系统的Rootkit对抗机制

查看引用,发现确实存在之前的”TV”也就是542875F90F9B47F497B64BA219CACF69的注册信息
深入浅出 Windows系统的Rootkit对抗机制

本文本部分会粗略介绍这个回调的几个启动方式.

线程启动

查看参数1的引用
深入浅出 Windows系统的Rootkit对抗机制
让我们从SecDeferredIntegrityCheck开始:
深入浅出 Windows系统的Rootkit对抗机制
而这个SecDeferredIntegrityCheck是被SecKernelIntegrityImmediateCheck所引用的
深入浅出 Windows系统的Rootkit对抗机制
而SecKernelIntegrityImmediateCheck只是PG众多函数的一员,他是在SecCreateThreadNotifyRoutine中引用的,而这个SecCreateThreadNotifyRoutine是系统注册的一个createthread回调(也就是说当有线程创建的时候,这个PG Context就会attach到线程里面,究极套娃)
此外你也看到,他有三种不同的方式
深入浅出 Windows系统的Rootkit对抗机制
回到SecKernelIntegrityImmediateCheck,他首先会执行疑似解密PG的context操作
深入浅出 Windows系统的Rootkit对抗机制
解密完成后,给当前线程插入一个APC通过 KeInitializeApc参数
深入浅出 Windows系统的Rootkit对抗机制

回头看SecDeferredIntegrityCheck:
深入浅出 Windows系统的Rootkit对抗机制

FltCreateCommunicationPort

在mssecflt.sys创建的时候,会调用这个FltCreateCommunicationPort创建一个minifilter端口
深入浅出 Windows系统的Rootkit对抗机制
当通讯id为一个指定的数值的时候(具体是啥我懒得算了)
深入浅出 Windows系统的Rootkit对抗机制
就会调用这个SecDeferredIntegrityCheck

这个minifilter port可以被用于R3/R0手动触发PG测试
也许是windows defender或者其他的系统组件会手动触发测试这个pg的呢

定时器

另外一个定时器就比较套娃了,通过ExAllocateTimer来做定时器:
简单介绍一下
DriverEntry -> SecDeviceOpen(SecCreateDeviceObject)/SecRegistrationWatchdogInitializeSec(PsCreateSystemThread) -> SecIntilization > SecDetInitialize -> SecDetInitializeTimers-> SecDetTimerCallback(ExAllocateTimer) -> SecDetTimerPerformDeferredAssertions(IoQueueWorkItem) -> SecDetTimerPerformDeferredAssertionsImpl -> SecKernelIntegrityPeriodicCheck->SecKernelIntegrityCheck->PG回调
此外这个SecRegistrationWatchdogInitializeSec,是一个系统线程来着
深入浅出 Windows系统的Rootkit对抗机制
然后初始化的时候又注册一个(因此他不仅仅只有一个PG context,是有多个)

句柄回调(obregcallback)

在SecObAddCallback(这个被driver entry调用)函数中,他会注册线程、进程两个句柄回调:
深入浅出 Windows系统的Rootkit对抗机制
无论如何,最终走向SecDetPerformProcessAssertionsWithContextImpl,到最后也是pg的回调.
深入浅出 Windows系统的Rootkit对抗机制

入口:

首先是一堆关于APC的检查:
当前线程 =APC_LEVEL 而且APC是关闭状态会蓝屏
当前线程 >APC_LEVEL 也会蓝屏
深入浅出 Windows系统的Rootkit对抗机制

申请PG Context内存、解密加密:

这里要确保这个上下文是不是在之前已经申请的内存里面
深入浅出 Windows系统的Rootkit对抗机制
加密解密:
深入浅出 Windows系统的Rootkit对抗机制
在末尾会释放掉它:
深入浅出 Windows系统的Rootkit对抗机制

加密解密的逻辑如下:

__int64 __fastcall EncryptDecryptEntries(PPgCtx PgCtx)
{
int SomeFlags; // er11
__int64 i; // r10
int ChecksStatusFlags; // er9
__int64 OffsetPgEntries; // rcx
__int64 LoopInitValue_Xor; // rbp
unsigned __int64 SizeIterations; // rdi
char *pData; // rbx
unsigned __int64 pDataEnd; // r14
__int64 XoredRAXRDX_1; // r11
__int64 i2; // rsi
__int64 v13; // rdx
_DWORD *pFaultRegion; // rax
int v15; // ecx
unsigned __int64 TimestampCtr; // rax
__int64 XoredRAXRDX; // r11
__int64 a1[2];

SomeFlags = PgCtx->SomeFlags;
if ( !_bittest(&SomeFlags, 30u) )
{
i = 0i64;
while ( 1 )
{
ChecksStatusFlags = PgCtx->ChecksStatusFlags;
a1[1] = (a1[0] & 0xffffffff) != 0;
if ( a1 == ((PgCtx->ChecksStatusFlags >> 21) & 1) || !(SomeFlags & 2) )
break;
OffsetPgEntries = PgCtx->OffsetStartPgEntries;
LoopInitValue_Xor = PgCtx->LoopInitValue;
SizeIterations = (PgCtx->SizeofAllPgEntries - OffsetPgEntries) >> 3;
pData = &PgCtx->CmpAppendDllSection[OffsetPgEntries];
pDataEnd = PgCtx + 8 * SizeIterations + OffsetPgEntries;
if ( a1[0] & 0xffffffff )
{
TimestampCtr = __rdtsc();
a1 = (__ROR8__(TimestampCtr, 3) ^ TimestampCtr) * 0x7010008004002001ui64;
XoredRAXRDX = a1[0] ^ a1[1];
PgCtx->XoredRAXRDX = XoredRAXRDX;
if ( pData > pDataEnd )
SizeIterations = 0i64;
if ( SizeIterations )
{
do
{
++i;
a1[0] = XoredRAXRDX ^ *pData;
a1[1] = LoopInitValue_Xor ^ *pData;
*pData = a1;
XoredRAXRDX = (a[1] + __ROR8__(XoredRAXRDX, XoredRAXRDX & 0x3F)) ^ 0xEFFi64;
pData += 8;
}
while ( i != SizeIterations );
ChecksStatusFlags = PgCtx->ChecksStatusFlags;
}
PgCtx->XoredRAXRDX2 = XoredRAXRDX;
PgCtx->ChecksStatusFlags = ChecksStatusFlags | 0x200000;
return a1;
}
XoredRAXRDX_1 = PgCtx->XoredRAXRDX;
i2 = 0i64;
if ( pData > pDataEnd )
SizeIterations = 0i64;
if ( SizeIterations )
{
do
{
*pData ^= XoredRAXRDX_1;
++i2;
v13 = *pData;
pData += 8;
XoredRAXRDX_1 = ((LoopInitValue_Xor ^ v13) + __ROR8__(XoredRAXRDX_1, XoredRAXRDX_1 & 0x3F)) ^ 0xEFF;
}
while ( i2 != SizeIterations );
ChecksStatusFlags = PgCtx->ChecksStatusFlags;
}
PgCtx->ChecksStatusFlags = ChecksStatusFlags & 0xFFDFFFFF;
if ( XoredRAXRDX_1 != PgCtx->XoredRAXRDX2 )
{
pPgFault = PgCtx->pPgFault;
v15 = PgCtx->SizeofAllPgEntries;
pPgFault->Ptr1 = PgCtx;
pPgFault->Id1 = v15;
if (!PgCtx->SomethingWentWrong;)
{
PgCtx->pPgFault->XoredChecksum = XoredRAXRDX_1 ^ PgCtx->XoredRAXRDX2;
if (!PgCtx->SomethingWentWrong;)
{
PgCtx->EncodedPointerPgCtxEntry = 0i64;
PgCtx->PgEntryType = 0x100i64;
PgCtx->EncodedPointerPgCtxLocal = &PgCtx + 0A3A03F5891C8B4E8h;
a1[0] = 0;
PgCtx->PgEntryData = XoredRAXRDX_1;
PgCtx->SomethingWentWrong = 1;
SomeFlags = PgCtx->SomeFlags;
if ( !_bittest(&SomeFlags, 0x1Eu) )
continue;
}
}
}
return a1;
}
}
return a1;
}

Kisystemcall64检测:

kisystemcall64是syscall的转向的点,pg会写入一个有效的特定的值去判断kisystemcall64然后syscall一次检查kisystemcall64的地址是否正确
深入浅出 Windows系统的Rootkit对抗机制
要避免被发现,hypervisor在被writemsr的时候应该老老实实的写入(不用担心漏调用的问题,因为过一会PG就恢复了)

ByePg检测:

Bypg是can1337的一个作品通过修改KebugEx2里面的HalTimerWatchdogStop函数指针恢复系统从而绕过PG
在pgcontext里面存在两处HalTimerWatchdogStop、一处是函数地址,另外一处是这个指针地址,这个地址被单独拿出来检查:
深入浅出 Windows系统的Rootkit对抗机制

隐藏进程检测:

一些通过ActiveProcessLinks断链(也叫做eprocess断链)的进程,pg是这样寻找蛛丝马迹进行蓝屏的:
首先pg遍历ActiveProcessLinks,给这些进程的eprocess->pcb.visted设置为一个随机数(默认是0)
然后遍历句柄表、线程表等看这些进程的pcb.Visited是否为0,为0就是异常进程了.

pcb.Visited(只是PG专用):
深入浅出 Windows系统的Rootkit对抗机制
在初始化的时候,生成随机数:
深入浅出 Windows系统的Rootkit对抗机制
检测时候
随机数确定:
深入浅出 Windows系统的Rootkit对抗机制
如果这个随机数跟eprocess->pcb.visited里面的不同,说明被隐藏了,隐藏进程即便是简单的断链,其他的句柄表、线程表什么的都得有,PG会遍历这些表去发些隐藏进程
ActiveProcessLinks:
深入浅出 Windows系统的Rootkit对抗机制
KiProcessListHead:
深入浅出 Windows系统的Rootkit对抗机制
PspCidTable:
深入浅出 Windows系统的Rootkit对抗机制
HandleTable:
深入浅出 Windows系统的Rootkit对抗机制

驱动断链检测:

深入浅出 Windows系统的Rootkit对抗机制
申请了一个Pool内存,标签是LoadedModuleTag(叫做’rwPD’)的内存,大小是 TableSize * 8, v320不用管恒定为0
深入浅出 Windows系统的Rootkit对抗机制
为了防止破解,生成随机数填充:
深入浅出 Windows系统的Rootkit对抗机制
得到dllbase在ldr中
深入浅出 Windows系统的Rootkit对抗机制
把dllbase插入到表里面:
深入浅出 Windows系统的Rootkit对抗机制
之后遍历整个表、进行比大小的操作
深入浅出 Windows系统的Rootkit对抗机制
如果这些dllbase跟一个InvertedTableEntries(个人猜测是驱动加载的时候就记录了的表)不匹配,说明有驱动断链.从而蓝屏

原文始发于微信公众号(冲鸭安全):深入浅出 Windows系统的Rootkit对抗机制

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月3日00:18:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   深入浅出 Windows系统的Rootkit对抗机制https://cn-sec.com/archives/3348190.html

发表评论

匿名网友 填写信息