简单认识Anti-RootKit(ZT) - 酒水不犯茶水 - BlogJava
简单认识Anti-RootKit(ZT)
from:http://www.debugman.com/read.php?tid=646
现在RK(rootkit)和ARK(anti- rootkit)的斗争已经进行了很久,在印象中最早出来的ARK工具是冰刃(IceSword),从冰刃开始出来到现在RK和ARK的斗争一直在继续,目前冰刃还是在流行当中,自己感觉也正是冰刃的出来才带动了当前流行的RK和ARK的斗争呵呵,现在很多病毒木马已经广泛的带有驱动,使用一些RK的技术和方法使自己更底层些更强大些,当前流行的ARK工具主要包括:隐藏进程检测,内核驱动检测,SSDT检测,代码HOOK检测,注册表隐藏的检测,隐藏文件的检测等一些功能的,下面谈谈自己对一些功能的简单愚见 嘻嘻。
关于进程检测:其实最早在R3下隐藏进程的方法已经很早开始流行起来被用到各种软件上来,在R0下如断ActiveProcessLinks链,擦掉句柄表等等,一步步的发展渐渐的更强大起来一些技巧和方法开始出来和流行起来,如FUTO,phide_ex等。。。。。。
检测隐藏进程的方法可以把一些现在流行的方法组合起来用,首先可以通过进程的EPROCESS结构中的进程活动链表ActiveProcessLinks来进行扫描一边,可以通过进程句柄表的枚举通过EPROCESS的HANDLE_TABLE,HANDLE_TABLE结构中的 HandleTableLis链表来扫描一边来获得一些EPROCESS,可以通过定位PsLookupProcessByProcessId代码中的 PspCidTable链表扫描一边获得一些EPROCESS,PspCidTable在各系统中枚举是不太一样的,也可以通过先找出 KiWaitInListHead,KiWaitOutListHead和KiDispatcherReadyListHead这些链表然后对这些链表扫描获得一些EPROCESS,以上具体的实现代码可以GOOGLE之网上实现的代码已经很多了,再者也可以找到内核中的线程切换SwapContext函数进行HOOK下的,在自己实现的SwapContext函数根据线程的偏移量找出进程的EPROCESS结构,把上面获得的所有EPROCESS汇集起来还需要判断下当前进程是否是真正的活着的:)可以通过EPROCESS里的标志位Flags(如XP 下0x248)一些标志判断下的,还要注意下对上面这些链表汇集起来是会有重复的进程的,在你自己的汇集函数中根据EPROCESS判断下的废话了,感觉实现了上面的一些方法对付一般的隐藏进程已经足够了的,但厉害的RK还是有的,现在存在可以逃过这些方法的RK的,在进程EPROCESS的结构里偏移0x1f8(XP SP2下)有个struct MMSUPPORT Vm结构:
struct _MMSUPPORT
{
/* off 0x00000000 */ union LARGE_INTEGER LastTrimTime;
/* off 0x00000008 */ struct MMSUPPORT_FLAGS Flags;
/* off 0x0000000C */ unsigned long PageFaultCount;
/* off 0x00000010 */ unsigned long PeakWorkingSetSize;
/* off 0x00000014 */ unsigned long WorkingSetSize;
/* off 0x00000018 */ unsigned long MinimumWorkingSetSize;
/* off 0x0000001C */ unsigned long MaximumWorkingSetSize;
/* off 0x00000020 */ struct _MMWSL* VmWorkingSetList;
/* off 0x00000024 */ struct LIST_ENTRY WorkingSetExpansionLinks;
/* off 0x0000002C */ unsigned long Claim;
/* off 0x00000030 */ unsigned long NextEstimationSlot;
/* off 0x00000034 */ unsigned long NextAgingSlot;
/* off 0x00000038 */ unsigned long EstimatedAvailable;
/* off 0x0000003C */ unsigned long GrowthSinceLastEstimate;
};
在这个结构里+0x24有个 WorkingSetExpansionLinks他也是个LIST_ENTRY链表的,遍例下他可以获得进程的EPROCESS的,如
PEPROCESS eprocess, eprocess2
eprocess =PsGetCurrentProcess();
lp=(PLIST_ENTRY)(*(PVOID )((PUCHAR)eprocess+0x1f8+0x24+4));
cur =lp->Flink;
for(;cur!=lp;cur=cur->Flink)
{
eprocess2=(PEPROCESS)((ULONG)cur-0x1f8-0x24);
PVOID session= (PVOID)(*(PULONG)((PCHAR) eprocess2+ 0x170));
if(MmIsAddressValid(session)){
AddProcess(eprocess2);
}
}
再者在进程EPROCESS的结构里偏移0x0b4(XP SP2下)存在个struct _LIST_ENTRY SessionProcessLinks结构,他也是个链表的 :)通过遍例他也可以的获得一些EPROCESS。还有个地方可以的 呵呵在每个线程对象里(ETHREAD)偏移0x34里有个struct _KAPC_STATE ApcState 结构的在_KAPC_STATE结构里偏移0x10,再者也可以通过遍例内存来查找隐藏进程,从内存MmSystemRangeStart开始到 System进程的EPROCESS地址就可以了主要是判断这个地址是否是个有效的进程,方法挺多的如判断下是否是进程对象这个地址如果是 EPROCESS看看PID,ThreadListHead,ReadyListHead是否正确有效的等等,很多方法的应该组合起来判断下保证肯定是进程就可以了,还可以通过HOOK一些函数的如KeUpdateRunTime,KeDispatchInterrupt等来检测隐藏进程,还可以设置下 PsSetCreateProcessNotifyRoutine在每次进程创建的时候对线程插入个APC的来进行统计检测的,其实我觉得对于检测隐藏进程的方法技巧还有很多,伟大的WINDOWS还需要我们挖掘呀。进程的结束可以通过调用ZwTerminateProcess或者调用未公开的 PspTerminateProcess函数的,关于这个函数在网上已经很广泛了,可以通过遍例进程的每个线程调用 PspTerminateThreadByPointer的结束每个线程的,这些未公开的函数都需要事先的查找和定位的,还可以使用RKU (RkUnhooker)的内存清零大法的切换到该进程然后对该进程内存清零RtlZeroMemory,再者也可以对该进程的每个线程插入APC来结束进程的,最后如果你有时间你也可以通过观看2K的代码自己来实现进程的结束。
内核驱动检测首先你可以通过 ZwQuerySystemInformation的SystemModuleInformation功能号来枚举内核驱动的,然后可以通过打开目录对象,进行枚举代码就略了GOOGLE之吧,也可以通过枚举IoDriverObjectType和IoDeviceObjectType对象类型进行查找枚举顺便把他们的DeviceObject和AttachedDevice等也枚举下吧,接着可以通过查找PsLoadedModuleList对该链进行下枚举的,可以对这个目录对象再搜索一边的”\\Driver”。通过对上面这些方法的枚举可以查找到很多驱动对象了,相信现在你的驱动对象链表已经够多了嘿嘿,够累吧,接下来,你可以对上面你已经查找到的驱动对象的0x38偏移MajorFunction查找一边看看他的地址是否在已知的驱动地址范围内,如不在你知道该怎么办的,再对MajorFunction里的每个例程地址找一边的从0到28也看看他们的地址是否在已知的驱动地址范围内,最后再说一种的方法的,也可以像进程那样内存枚举的,像进程那样从MmSystemRangeStart开始枚举吧,判断下是否是PE文件有没有那几个关键PE特征的,如MZ,PE等,看看是否存在PE文件头是否有效,看看这个地址是否已经是你检测出来的驱动地址的,避免重复的,看看你所检测出来的所有驱动对象的 MajorFunction[X]和DriverStartIo是否有在这个地址,如果有并且这个地址你先前没有检测出来没有重复的他很有可能是个未知的驱动的,其实和进程内存查找一样的,关键是判断的,需要判断对的,肯定他是某个对象的然后你就可以把他加如到你自己的某个链表里。最后也可以通过对一些关键函数的HOOK 如ExAllocatePool,ExAllocatePoolWithTag等在自己实现这些函数里记录下esp+0x24地址的,对这些地址进行判断的来看看这些地址是否包含在某些内核模块当中当然还需要判断下他是否就是个PE驱动文件,这种方法就是RKU用到的方法的。驱动就说这些吧。
前面说得太多了,后面说少点吧 嘿嘿。
关于SSDT HOOK的检测,通过定位ntoskrnl.exe磁盘文件里KeServiceDescriptorTable与内存中的KeServiceDescriptorTable对各个服务函数进行比较就可以的。代码网上很多的。
关于代码HOOK检测,我也不想说什么的,可以对内存中ntoskrnl.exe 的导入函数和导出函数与磁盘文件中的地址进行比较,也可以通过对ntoskrnl.exe PE文件里的某些节(section)进行扫描的,再加上对一些关键文件的导入函数和导出函数进行扫描,加上对某些关键驱动(如文件系统驱动)的 MajorFunction里的每个例程进行扫描,再者对IDT,GDT扫描下的。
关于注册表隐藏的检测,首先可以用到把一些注册表相关的函数INLINE HOOK的SSDT HOOK的都恢复下再使用的其实所谓的不相关的也需要UNHOOK下的,如badrkdemo 他就HOOK了ObOpenObjectByName函数组织对注册表的访问的 具体的看情况来吧哈哈,也可以通过对一些未公开的函数进行使用的CM系列函数的,再者可以通过分析HIVE文件的来显示注册表各个项的,通过分析HIVE文件其实也不是很难的,了解了HIVE文件结构和HIVE文件的组织的,就可以读他了,这些资料网上可以找到的,通过读HIVE文件来给用户显示当前注册表各个项的可以的但我并不推荐自己改写系统的HIVE文件的,如提供DELETE MODIFE等功能的我觉得如改的不好,或结构没有完全清楚的,写到HIVE文件里是错误的,那么当再次启动时系统读HIVE文件时就不好过了,自己一点愚见,如果你够强大当然是没有问题的。
关于隐藏文件的检测,现在流行的隐藏文件的RK很多的,如Unreal.A,AK922等,自己可以通过在驱动中自己构建IRP包自己发送给文件驱动的方法的,还有就是先恢复些关键函数的,像注册表那样的,恢复INLINE HOOK SSDT HOOK,文件驱动关键例程HOOK的,现在流行HOOK内核的完成例程的,HOOK是防不胜防的,还要注意下附加在文件系统上的一些过滤驱动的,还有就是通过使用DeviceIoControl发送一些特殊的IoControlCode控制代码给文件系统的,这需要对文件系统的熟悉的,还有就是自己分析磁盘文件的对FAT32,NTFS等格式文件系统自己分析来查找文件的,关于自己分析磁盘文件的,网上的信息和资料也是很多的,首先判断下属于哪个文件系统,然后根据特定的文件系统格式自己分析的就可以的,其实这些方法的关键是怎么读和写的,读和写做到最底层,把读和写做好我想他检测文件功能是强大的。
够了,一些ARK的功能说到这就可以了,我希望各位搞RK的和ARK的人看了之后又会作出很多厉害,强大的东西来,希望看了之后会对各位有一点帮助的,希望可以在当前流行的RK和ARK中会有更新更强大的东西出现的,来激励我们学习和前进的,引用一位好友的话“现在感觉大部分木马病毒什么的都是用的老一套东西的什么SSDT HOOK的。。。。。。,希望可以有些新的技术出现的”,其实现在有些RK是很牛的,其实都是一个目标的希望技术和知识可以不断进步的 嘿嘿。一个没有未来的人:)谈谈关于RK和ARK未来的发展 RK 更底层,ARK也更底层,攻和防,RK和ARK的斗争会继续的,RK会出现固化在某个文件里,会在重装系统后还会存在,会写到硬件中。。。。。。,ARK 势必也需要对这些问题关注的。
谢谢各位看完文章的,本人一介小菜知识有限,以上是自己的一点愚见,如有什么错误和不足之处,请各位指教。
本文之中的有些知识是朋友和一些牛人给予帮助,谢谢他们的帮助。
作者:single
2007-10-19