13 12
发新话题
打印

一个关于授权问题

并且我只借鉴他的某一部分功能(声明:是驱动)

TOP

就这个文件
//======================================================================
//
// Regsys.c
//
// Copyright (C) 1996-1998 Mark Russinovich and Bryce Cogswell
//
// Hooks the registry by replacing registry related calls in the system
// service table with pointers to our own hook routines. Very simple
// yet very effective.
//
//======================================================================

#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "..\gui\ioctlcmd.h"
#include "regsys.h"


//----------------------------------------------------------------------
//                           DEFINES
//----------------------------------------------------------------------
// print macro that only turns on when debugging is on
#if DBG
#define DbgPrint(arg) DbgPrint arg
#else
#define DbgPrint(arg)
#endif

//
// Macro for easy hook/unhook. On X86 implementations of Zw* functions, the DWORD
// following the first byte is the system call number, so we reach into the Zw function
// passed as a parameter, and pull the number out. This makes system call hooking
// dependent ONLY on the Zw* function implementation not changing.
//

#if defined(_ALPHA_)
#define SYSCALL(_function)  ServiceTable->ServiceTable[ (*(PULONG)_function)  & 0x0000FFFF ]
#else
#define SYSCALL(_function)  ServiceTable->ServiceTable[ *(PULONG)((PUCHAR)_function+1)]
#endif

//
// Number of predefined rootkeys
//
#define NUMROOTKEYS     4


//
// The name of the System process, in which context we're called in our DriverEntry
//
#define SYSNAME         "System"

//
// A unicode string constant for the "default" value
//
#define DEFAULTNAMELEN  (9*sizeof(WCHAR))
WCHAR                   DefaultValueString[] = L"(Default)";
UNICODE_STRING          DefaultValue = {
    DEFAULTNAMELEN,
    DEFAULTNAMELEN,
    DefaultValueString
};

//
// A filter to use if we're monitoring boot activity
//
FILTER  BootFilter = {
    "*", "", "*", "",
    TRUE, TRUE, TRUE, TRUE
};

//----------------------------------------------------------------------
//                         FORWARD DEFINES
//----------------------------------------------------------------------
NTSTATUS RegmonDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
VOID     RegmonUnload( IN PDRIVER_OBJECT DriverObject );

//----------------------------------------------------------------------
//                         GLOBALS
//----------------------------------------------------------------------
// our user-inteface device object
PDEVICE_OBJECT          GUIDevice;

//
// Is a GUI talking to us?
//
BOOLEAN                 GUIActive = FALSE;

//
// Are we logging a boot sequence?
//
BOOLEAN                 BootLogging = FALSE;
KEVENT                  LoggingEvent;
HANDLE                  LogFile = INVALID_HANDLE_VALUE;
PSTORE_BUF              BootSavedStoreList = NULL;
PSTORE_BUF              BootSavedStoreTail;

//
// Is registry hooked?
//
BOOLEAN                 RegHooked = FALSE;

//
// Global error string
//
CHAR                    errstring[256];

//
// Global filter (sent to us by the GUI)
//
FILTER                  FilterDef;

//
// Lock to protect filter arrays
//
KMUTEX                  FilterMutex;

//
// Array of process and path filters
//
ULONG                   NumProcessFilters = 0;
PCHAR                   ProcessFilters[MAXFILTERS];
ULONG                   NumProcessExcludeFilters = 0;
PCHAR                   ProcessExcludeFilters[MAXFILTERS];
ULONG                   NumPathIncludeFilters = 0;
PCHAR                   PathIncludeFilters[MAXFILTERS];
ULONG                   NumPathExcludeFilters = 0;
PCHAR                   PathExcludeFilters[MAXFILTERS];

//
// Pointer to system global service table
//
PSRVTABLE               ServiceTable;

//
// This is the offset into a KPEB of the current process name. This is determined
// dynamically by scanning the process block belonging to the GUI for its name.
//
ULONG                   ProcessNameOffset;

//
// We save off pointers to the actual Registry functions in these variables
//
NTSTATUS (*RealRegOpenKey)( IN PHANDLE, IN OUT ACCESS_MASK, IN POBJECT_ATTRIBUTES );
NTSTATUS (*RealRegQueryKey)( IN HANDLE, IN KEY_INFORMATION_CLASS,
                             OUT PVOID, IN ULONG, OUT PULONG );
NTSTATUS (*RealRegQueryValueKey)( IN HANDLE, IN PUNICODE_STRING,
                                  IN KEY_VALUE_INFORMATION_CLASS,
                                  OUT PVOID, IN ULONG, OUT PULONG );
NTSTATUS (*RealRegEnumerateValueKey)( IN HANDLE, IN ULONG,  
                                      IN KEY_VALUE_INFORMATION_CLASS,
                                      OUT PVOID, IN ULONG, OUT PULONG );
NTSTATUS (*RealRegEnumerateKey)( IN HANDLE, IN ULONG,
                                 IN KEY_INFORMATION_CLASS,
                                 OUT PVOID, IN ULONG, OUT PULONG );
NTSTATUS (*RealRegSetValueKey)( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName,
                                IN ULONG TitleIndex, IN ULONG Type,
                                IN PVOID Data, IN ULONG DataSize );
NTSTATUS (*RealRegCreateKey)( OUT PHANDLE, IN ACCESS_MASK,
                              IN POBJECT_ATTRIBUTES , IN ULONG,
                              IN PUNICODE_STRING, IN ULONG, OUT PULONG );
NTSTATUS (*RealRegDeleteValueKey)( IN HANDLE, IN PUNICODE_STRING );
NTSTATUS (*RealRegCloseKey)( IN HANDLE );
NTSTATUS (*RealRegDeleteKey)( IN HANDLE );
NTSTATUS (*RealRegFlushKey)( IN HANDLE );

//
// Lenghs of rootkeys (filled in at init). This table allows us to translate
// path names into better-known forms. Current user is treated specially since
// its not a full match.
//
ROOTKEY CurrentUser[2] = {
    { "\\\\REGISTRY\\USER\\S", "HKCU", 0 },
    { "HKU\\S", "HKCU", 0 }
};

ROOTKEY RootKey[NUMROOTKEYS] = {
    { "\\\\REGISTRY\\USER", "HKU", 0 },
    { "\\\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT",
      "HKCC", 0 },
    { "\\\\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES", "HKCR", 0 },
    { "\\\\REGISTRY\\MACHINE", "HKLM", 0 }
};

//
// This is a hash table for keeping names around for quick lookup.
//
PHASH_ENTRY             HashTable[NUMHASH];

//
// Mutex for hash table accesses
//
KMUTEX                  HashMutex;

//
// Data structure for storing messages we generate
//
PSTORE_BUF              Store           = NULL;
ULONG                   Sequence        = 0;
KMUTEX                  StoreMutex;

//
// Maximum amount of data we will grab for buffered unread data
//
ULONG                   NumStore        = 0;
ULONG                   MaxStore        = MAXMEM/MAX_STORE;

//
// Free hash list. Note: we don't use lookaside lists since
// we want to be able to run on NT 3.51 - lookasides were
// introduced in NT 4.0
//
PHASH_ENTRY             FreeHashList = NULL;


//======================================================================
//      P A T T E R N   M A T C H I N G   R O U T I N E S
//======================================================================


//----------------------------------------------------------------------
//
// MatchOkay
//
// Only thing left after compare is more mask. This routine makes
// sure that its a valid wild card ending so that its really a match.
//
//----------------------------------------------------------------------
BOOLEAN MatchOkay( PCHAR Pattern )
{
    //
    // If pattern isn't empty, it must be a wildcard
    //
    if( *Pattern && *Pattern != '*' ) {

        return FALSE;
    }

    //
    // Matched
    //
    return TRUE;
}



//----------------------------------------------------------------------
//
// MatchWithPattern
//
// Performs nifty wildcard comparison.
//
//----------------------------------------------------------------------
BOOLEAN MatchWithPattern( PCHAR Pattern, PCHAR Name )
{
    CHAR   upcase;

    //
    // End of pattern?
    //
    if( !*Pattern ) {

        return FALSE;
    }

    //
    // If we hit a wild card, do recursion
    //
    if( *Pattern == '*' ) {

        Pattern++;

        while( *Name && *Pattern ) {

            if( *Name >= 'a' && *Name <= 'z' )
                upcase = *Name - 'a' + 'A';
            else
                upcase = *Name;

            //
            // See if this substring matches
            //
            if( *Pattern == upcase || *Name == '*' ) {

                if( MatchWithPattern( Pattern+1, Name+1 )) {

                    return TRUE;
                }
            }

            //
            // Try the next substring
            //
            Name++;
        }

        //
        // See if match condition was met
        //
        return MatchOkay( Pattern );
    }

    //
    // Do straight compare until we hit a wild card
    //
    while( *Name && *Pattern != '*' ) {

        if( *Name >= 'a' && *Name <= 'z' )
            upcase = *Name - 'a' + 'A';
        else
            upcase = *Name;

        if( *Pattern == upcase ) {

            Pattern++;
            Name++;

        } else {

            return FALSE;
        }
    }

    //
    // If not done, recurse
    //
    if( *Name ) {

        return MatchWithPattern( Pattern, Name );
    }

    //
    // Make sure its a match
    //
    return MatchOkay( Pattern );
}

//======================================================================
//       B O O T   L O G G I N G   W O R K   R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// RegmonOpenBootLog
//
// Open a log file.
//
//----------------------------------------------------------------------
NTSTATUS RegmonOpenBootLog()
{
    WCHAR                   logFileNameBuffer[] =  L"\\SystemRoot\\REGMON.LOG";
    UNICODE_STRING          logFileUnicodeString;
    OBJECT_ATTRIBUTES       objectAttributes;
    IO_STATUS_BLOCK         ioStatus;
    NTSTATUS                ntStatus;

    RtlInitUnicodeString( &logFileUnicodeString, logFileNameBuffer );
    InitializeObjectAttributes( &objectAttributes, &logFileUnicodeString,
                                OBJ_CASE_INSENSITIVE, NULL, NULL );

    ntStatus = ZwCreateFile( &LogFile, FILE_WRITE_DATA|SYNCHRONIZE,
                             &objectAttributes, &ioStatus, NULL,
                             FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
                             FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
    return ntStatus;
}

//----------------------------------------------------------------------
//
// RegmonCloseBootLog - worker thread routine
//
// Close the boot log file.
//
//----------------------------------------------------------------------
VOID RegmonCloseBootLog( PVOID Context )
{
    ZwClose( LogFile );
    KeSetEvent( &LoggingEvent, 0, FALSE );
    LogFile = INVALID_HANDLE_VALUE;
}

//----------------------------------------------------------------------
//
// RegmonWriteBuffer
//
// Dumps a buffer to the log file.
//
//----------------------------------------------------------------------
VOID RegmonWriteBuffer( PSTORE_BUF LogStore )
{
    ULONG       len;
    ULONG       itemcnt;
    UCHAR       seqtext[64];
    static CHAR diskFullError[] = "Not enough disk space for log file\n";
    PCHAR       textptr, items[10];
    PENTRY      entry;
    FILE_END_OF_FILE_INFORMATION zeroLengthFile;
    IO_STATUS_BLOCK ioStatus;

    //
    // Process the buffer
    //
    for( entry = (PENTRY) LogStore->Data; entry < (PENTRY) ((PCHAR) LogStore + LogStore->Len); ) {
        
        len = strlen( entry->text );
        len += 4; len &= 0xFFFFFFFC;
        
        //
        // Write out the entry.
        //
        sprintf( seqtext, "%d: ", entry->seq );
        ZwWriteFile( LogFile, NULL, NULL, NULL, &ioStatus,
                     seqtext, strlen(seqtext), NULL, NULL );        
        ZwWriteFile( LogFile, NULL, NULL, NULL, &ioStatus,
                     entry->text, strlen(entry->text), NULL, NULL );
        ZwWriteFile( LogFile, NULL, NULL, NULL, &ioStatus,
                     "\n\r", strlen("\n\r"), NULL, NULL );
            
        //
        // If the disk is full, delete the log file
        // and tell the user there wasn't enough room for it.
        //
        if( ioStatus.Status == STATUS_DISK_FULL ) {

            zeroLengthFile.EndOfFile.QuadPart = 0;
            ZwSetInformationFile( LogFile, &ioStatus,
                                  &zeroLengthFile, sizeof(zeroLengthFile), FileEndOfFileInformation );
            ZwWriteFile( LogFile, NULL, NULL, NULL, &ioStatus,
                         diskFullError, strlen(diskFullError), NULL, NULL );
            ZwClose( LogFile );
            LogFile = INVALID_HANDLE_VALUE;
            BootLogging = FALSE;
            break;
        }
        entry = (PVOID) (entry->text + len);
    }
}


//----------------------------------------------------------------------
//
// RegmonWriteBootLog - worker thread routine
//
// Writes a buffer out to the log file. We do this in a worker routine
// because the log file handle, which we opened in DriverEntry, is
// only valid in the System process, and worker threads execute in
// the system process. We are protected by the Store mutex while
// in this procedure, since we are called from NewStore, which
// is called after the Store mutex is acquired.
//
// NOTE: When Regmon is configured to log activity during a boot it
// is marked to start as the very first driver in the boot sequence.
// Because the SystemRoot symbolic link is not initialized until
// all boot drivers have finished initializing, Regmon is not able
// to open a boot log until some point later. In order that we can
// capture all registry activity we store away output buffers on a list
// until we try and succeed at opening the boot log. When we can we
// send out the accumulated data and then begin dumping buffers
// as they are generated.
//
//----------------------------------------------------------------------
VOID RegmonWriteBootLog( PVOID Context )
{
    PSTORE_BUF  currentStore = Context;
    PSTORE_BUF  saveStore, curSaveStore;
    NTSTATUS    ntStatus;

    //
    // If boot logging is still on, but the log file hasn't been opened,
    // try to open it
    //
    if( BootLogging && LogFile == INVALID_HANDLE_VALUE ) {

        ntStatus = RegmonOpenBootLog();
        
        if( NT_SUCCESS( ntStatus )) {

            //
            // Finally! Process all the buffers we've saved away
            //
            curSaveStore = BootSavedStoreList;
            while( curSaveStore ) {

                RegmonWriteBuffer( curSaveStore );
                BootSavedStoreList = curSaveStore->Next;
                ExFreePool( curSaveStore );
                curSaveStore = BootSavedStoreList;
            }
        }
    }
   
    //
    // Either write out the current buffer or save it away to
    // write out later
    //
    if( LogFile != INVALID_HANDLE_VALUE ) {

        RegmonWriteBuffer( currentStore );

    } else {

        //
        // Save this buffer away until we can successfully open
        // the log file and write it out to disk
        //
        saveStore = ExAllocatePool( PagedPool, sizeof(*saveStore));
        memcpy( saveStore, currentStore, sizeof(*saveStore) );
        saveStore->Next = NULL;
        if( BootSavedStoreList ) {

            BootSavedStoreTail->Next = saveStore;
            BootSavedStoreTail = saveStore;

        } else {
            
            BootSavedStoreList = saveStore;
            BootSavedStoreTail = saveStore;
        }
    }

    //
    // Signal the event
    //
    KeSetEvent( &LoggingEvent, 0, FALSE );
}


//======================================================================
//                   B U F F E R  R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// RegmonFreeStore
//
// Frees all the data output buffers that we have currently allocated.
//
//----------------------------------------------------------------------
VOID RegmonFreeStore()
{
    PSTORE_BUF      next;
   
    while( Store ) {
        next = Store->Next;
        ExFreePool( Store );
        Store = next;
    }
}      


//----------------------------------------------------------------------
//
// RegmonNewStore
//
// Called when the current buffer has filled up. This moves us to the
// pre-allocated buffer and then allocates another buffer.
//
//----------------------------------------------------------------------
void RegmonNewStore( void )
{
    PSTORE_BUF prev = Store, newstore;
    WORK_QUEUE_ITEM  workItem;

    //
    // If we're boot logging, write the current store out to disk
    //
    if( BootLogging ) {

        ExInitializeWorkItem( &workItem, RegmonWriteBootLog, Store );
        ExQueueWorkItem( &workItem, CriticalWorkQueue );
        KeWaitForSingleObject( &LoggingEvent, Executive, KernelMode, FALSE, NULL );
    }

    //
    // If we have maxed out or haven't accessed the current store
    // just return
    //
    if( MaxStore == NumStore ) {
        Store->Len = 0;
        return;
    }

    //
    // See if we can re-use a store
    //
    if( !Store->Len ) {

        return;
    }

    //
    // Move to the next buffer and allocate another one
    //
    newstore = ExAllocatePool( PagedPool, sizeof(*Store) );
    if( newstore ) {

        Store   = newstore;
        Store->Len  = 0;
        Store->Next = prev;
        NumStore++;

    } else {

        Store->Len = 0;

    }
}


//----------------------------------------------------------------------
//
// RegmonOldestStore
//
// Goes through the linked list of storage buffers and returns the
// oldest one.
//
//----------------------------------------------------------------------
PSTORE_BUF RegmonOldestStore( void )
{
    PSTORE_BUF  ptr = Store, prev = NULL;

    while ( ptr->Next ) {

        ptr = (prev = ptr)->Next;
    }
    if ( prev ) {

        prev->Next = NULL;   
    }
    NumStore--;
    return ptr;
}


//----------------------------------------------------------------------
//
// RegmonResetStore
//
// When a GUI is no longer communicating with us, but we can't unload,
// we reset the storage buffers.
//
//----------------------------------------------------------------------
VOID RegmonResetStore()
{
    PSTORE_BUF  current, next;

    MUTEX_WAIT( StoreMutex );

    //
    // Traverse the list of output buffers
    //
    current = Store->Next;
    while( current ) {

        //
        // Free the buffer
        //
        next = current->Next;
        ExFreePool( current );
        current = next;
    }

    //
    // Move the output pointer in the buffer that's being kept
    // the start of the buffer.
    //
    Store->Len = 0;
    Store->Next = NULL;

    MUTEX_RELEASE( StoreMutex );
}



//----------------------------------------------------------------------
//
// UpdateStore
//
// Add a new string to Store, if it fits.
//
//----------------------------------------------------------------------
void UpdateStore( const char * format, ... )
{      
    PENTRY          Entry;
    ULONG           len;
    va_list         arg_ptr;
    static CHAR     text[MAXPATHLEN*2];

#define A (&format)
    DbgPrint(( (char *)format, A[1], A[2], A[3], A[4], A[5], A[6] ));
    DbgPrint(( "\n" ));
#undef A

    //
    // only do this if a GUI is active
    //
    if( !GUIActive ) return;

    //
    // Lock the buffer pool
    //
    MUTEX_WAIT( StoreMutex );

    //
    // Get a sequence numnber
    //
    InterlockedIncrement( &Sequence );

    //
    // Sprint the string to get the length
    //
    va_start( arg_ptr, format );
    len = vsprintf( text, format, arg_ptr );
    va_end( arg_ptr );   
        len += 4; len &=  0xFFFFFFFC; // +1 to include null terminator and +3 to allign on longword

    //
    // See if its time to switch to extra buffer
    //
    if ( Store->Len + len + sizeof(ENTRY) + 1 >= MAX_STORE ) {

        RegmonNewStore();
    }

    //
    // Store the sequence number so that
    // a call's result can be paired with its
    // initial data collected when it was made.
    //
    Entry = (void *)(Store->Data+Store->Len);
    Entry->seq = Sequence;
    memcpy( Entry->text, text, len );
    Store->Len += sizeof(Entry->seq)+len;

    //
    // Release the buffer pool
    //
    MUTEX_RELEASE( StoreMutex );
}


//----------------------------------------------------------------------
//
// strncatZ
//
// Appends a string to another and attaches a null. NT 3.51 ntoskrnl
// does not export this function so we have to make our own, and give
// it a name that won't conflict with the strncat that NT 4.0 exports.
//
//----------------------------------------------------------------------
PCHAR strncatZ( PCHAR dest, PCHAR source, int length )
{      
    int     origlen = strlen(dest);

    strncpy( dest+origlen, source, length );
    dest[ origlen+length ] = 0;
    return(dest);
}

//======================================================================
//                   H A S H  R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// RegmonHashCleanup
//
// Called when we are unloading to free any memory that we have
// in our possession.
//
//----------------------------------------------------------------------
VOID RegmonHashCleanup()
{
    PHASH_ENTRY             hashEntry, nextEntry;
    ULONG                   i;

    MUTEX_WAIT( HashMutex );   

    //
    // First free the hash table entries
    //      
    for( i = 0; i < NUMHASH; i++ ) {
        hashEntry = HashTable;
        while( hashEntry ) {
            nextEntry = hashEntry->Next;
            ExFreePool( hashEntry->FullPathName );
            ExFreePool( hashEntry );
            hashEntry = nextEntry;
        }
    }
   
    hashEntry = FreeHashList;
    while( hashEntry ) {
        nextEntry = hashEntry->Next;
        ExFreePool( hashEntry );
        hashEntry = nextEntry;
    }
    MUTEX_RELEASE( HashMutex );
}

//----------------------------------------------------------------------
//
// RegmonStoreHash
//
// Stores the key and associated fullpath in the hash table.
//
//----------------------------------------------------------------------
VOID RegmonStoreHash( POBJECT object, PCHAR fullname )
{
    PHASH_ENTRY     newEntry;

    MUTEX_WAIT( HashMutex );

    if( FreeHashList ) {

        newEntry = FreeHashList;
        FreeHashList = newEntry->Next;

    } else {

        newEntry = ExAllocatePool( PagedPool, sizeof(HASH_ENTRY));
    }

    newEntry->Object                = object;
    newEntry->FullPathName          = ExAllocatePool( PagedPool, strlen(fullname)+1 );
    newEntry->Next                  = HashTable[ HASHOBJECT( object) ];
    HashTable[ HASHOBJECT(object) ] = newEntry;     
    strcpy( newEntry->FullPathName, fullname );

    MUTEX_RELEASE( HashMutex );
}


//----------------------------------------------------------------------
//
// RegmonFreeHashEntry
//
// When we see a key close, we can free the string we had associated
// with the fileobject being closed since we know it won't be used
// again.
//
//----------------------------------------------------------------------
VOID RegmonFreeHashEntry( POBJECT object )
{
    PHASH_ENTRY             hashEntry, prevEntry;

    MUTEX_WAIT( HashMutex );

    //
    // look-up the entry
    //
    hashEntry = HashTable[ HASHOBJECT( object ) ];
    prevEntry = NULL;
    while( hashEntry && hashEntry->Object != object ) {
        prevEntry = hashEntry;
        hashEntry = hashEntry->Next;
    }
  
    //
    // If we fall off (didn''t find it), just return
    //
    if( !hashEntry ) {
        MUTEX_RELEASE( HashMutex );
        return;
    }

    //
    // Remove it from the hash list
    //
    if( prevEntry )
        prevEntry->Next = hashEntry->Next;
    else
        HashTable[ HASHOBJECT( object )] = hashEntry->Next;

    //
    // Free the memory associated with it
    //
    ExFreePool( hashEntry->FullPathName );
    hashEntry->Next = FreeHashList;
    FreeHashList = hashEntry;

    MUTEX_RELEASE( HashMutex );
}

//======================================================================
//  R E G I S T R Y  P A R A M E T E R  S U P P O R T  R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// ConverToUpper
//
// Obvious.
//
//----------------------------------------------------------------------
VOID ConvertToUpper( PCHAR Dest, PCHAR Source, ULONG Len )
{
    ULONG   i;
   
    for( i = 0; i < Len; i++ ) {
        if( Source >= 'a' && Source <= 'z' ) {

            Dest = Source - 'a' + 'A';

        } else {

            Dest = Source;
        }
    }
}


//----------------------------------------------------------------------
//
// GetPointer
//
// Translates a handle to an object pointer.
//
//----------------------------------------------------------------------
POBJECT GetPointer( HANDLE handle )
{
    POBJECT         pKey;

    //
    // Ignore null handles
    //
    if( !handle ) return NULL;

    //
    // Get the pointer the handle refers to
    //
    if( ObReferenceObjectByHandle( handle, 0, NULL, KernelMode, &pKey, NULL ) !=
        STATUS_SUCCESS ) {

        DbgPrint(("Error %x getting key pointer\n"));
        pKey = NULL;
    }
    return pKey;
}


//----------------------------------------------------------------------
//
// ReleasePointer
//
// Dereferences the object.
//
//----------------------------------------------------------------------
VOID ReleasePointer( POBJECT object )
{
    if( object ) ObDereferenceObject( object );
}


//----------------------------------------------------------------------
//
// AppendKeyInformation
//
// Appends key enumerate and query information to the output buffer.
//
//----------------------------------------------------------------------
VOID AppendKeyInformation( IN KEY_INFORMATION_CLASS KeyInformationClass,
                           IN PVOID KeyInformation, PCHAR Buffer )
{
    PKEY_BASIC_INFORMATION  pbasicinfo;
    PKEY_FULL_INFORMATION   pfullinfo;
    PKEY_NODE_INFORMATION   pnodeinfo;
    UNICODE_STRING          ukeyname;      
    ANSI_STRING             akeyname;

    switch( KeyInformationClass ) {

    case KeyBasicInformation:
        pbasicinfo = (PKEY_BASIC_INFORMATION) KeyInformation;
        ukeyname.Length = (USHORT) pbasicinfo->NameLength;
        ukeyname.MaximumLength = (USHORT) pbasicinfo->NameLength;
        ukeyname.Buffer = pbasicinfo->Name;
        RtlUnicodeStringToAnsiString( &akeyname, &ukeyname, TRUE );
        sprintf( Buffer, "Name: %s", akeyname.Buffer );
        RtlFreeAnsiString( &akeyname );
        break;

    case KeyFullInformation:
        pfullinfo = (PKEY_FULL_INFORMATION) KeyInformation;
        sprintf( Buffer, "Subkeys = %d", pfullinfo->SubKeys );
        break;  
        
    case KeyNodeInformation:
        pnodeinfo = (PKEY_NODE_INFORMATION) KeyInformation;
        ukeyname.Length = (USHORT) pnodeinfo->NameLength;
        ukeyname.MaximumLength = (USHORT) pnodeinfo->NameLength;
        ukeyname.Buffer = pnodeinfo->Name;
        RtlUnicodeStringToAnsiString( &akeyname, &ukeyname, TRUE );
        sprintf( Buffer, "Name: %s", akeyname.Buffer );
        RtlFreeAnsiString( &akeyname );
        break;

    default:
        sprintf( Buffer, "Unknown Info Class" );
        break;
    }
}


//----------------------------------------------------------------------
//
// AppendRegValueType
//
// Returns the string form of an registry value type.
//
//----------------------------------------------------------------------
VOID AppendRegValueType( ULONG Type, PCHAR Buffer )
{
    CHAR            tmp[MAXDATALEN];

    switch( Type ) {
    case REG_BINARY:
        strcat( Buffer, "BINARY" );
        break;
    case REG_DWORD_LITTLE_ENDIAN:
        strcat( Buffer, "DWORD_LITTLE_END" );
        break;
    case REG_DWORD_BIG_ENDIAN:
        strcat( Buffer, "DWORD_BIG_END" );
        break;
    case REG_EXPAND_SZ:
        strcat( Buffer, "EXPAND_SZ" );
        break;
    case REG_LINK:
        strcat( Buffer, "LINK" );
        break;
    case REG_MULTI_SZ:
        strcat( Buffer, "MULTI_SZ" );
        break;
    case REG_NONE:
        strcat( Buffer, "NONE" );
        break;
    case REG_SZ:
        strcat( Buffer, "SZ" );
        break;
    case REG_RESOURCE_LIST:
        strcat( Buffer, "RESOURCE_LIST" );
        break;
    case REG_RESOURCE_REQUIREMENTS_LIST:
        strcat( Buffer, "REQ_LIST" );
        break;
    case REG_FULL_RESOURCE_DESCRIPTOR:
        strcat( Buffer, "DESCRIPTOR" );
        break;
    default:
        sprintf( tmp, "UNKNOWN TYPE: %d", Type );
        strcat( Buffer, tmp );
        break;
    }
}


//----------------------------------------------------------------------
//
// AppendRegValueData
//
// We expand certain registry types to provide more information. In
// all cases, calculate the length of the data being copied so
// we don't overflow the buffer that's passed in. The length of Buffer
// must be MAXVALLEN.
//
//----------------------------------------------------------------------
VOID AppendRegValueData( IN ULONG Type, IN PVOID Data, IN ULONG Length,
                         IN OUT PCHAR Buffer )
{
    PWCHAR                  pstring;
    PULONG                  pulong;
    PUCHAR                  pbinary;
    CHAR                    tmp[MAXDATALEN];
    UNICODE_STRING          ukeyname;      
    ANSI_STRING             akeyname;
    int                     len, i;

    switch( Type ) {
    case REG_SZ:   
    case REG_EXPAND_SZ:
    case REG_MULTI_SZ:
        pstring = (PWCHAR) Data;
        ukeyname.Length = (USHORT) Length;
        ukeyname.MaximumLength = (USHORT) Length;
        ukeyname.Buffer = pstring;
        RtlUnicodeStringToAnsiString( &akeyname,
                                      &ukeyname, TRUE );   
        strcat( Buffer, "\"");
        strncatZ( Buffer+1, akeyname.Buffer, MAXVALLEN - 6);
        if( akeyname.Length > MAXVALLEN - 6 ) strcat( Buffer,"...");
        strcat( Buffer, "\"");
        RtlFreeAnsiString( &akeyname );
        break;

    case REG_DWORD:
        pulong = (PULONG) Data;
        sprintf( tmp, "0x%X", *pulong );
        strcat( Buffer, tmp );
        break;

    case REG_BINARY:
    case REG_RESOURCE_LIST:
    case REG_FULL_RESOURCE_DESCRIPTOR:
    case REG_RESOURCE_REQUIREMENTS_LIST:
        pbinary = (PCHAR) Data;
        if( Length > 8 ) len = 8;
        else len = Length;
        for( i = 0; i < len; i++ ) {
            sprintf( tmp, "%02X ", (UCHAR) pbinary);
            strcat( Buffer, tmp );
        }
        if( Length > 8) strcat( Buffer, "...");
        break;

    default:
        AppendRegValueType( Type, Buffer );
        break;
    }
}


//----------------------------------------------------------------------
//
// AppendValueInformation
//
// Appends value enumerate and query information to the output buffer.
//
//----------------------------------------------------------------------
VOID AppendValueInformation( IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
                             IN PVOID KeyValueInformation, PCHAR Buffer, PCHAR ValueName )
{
    PKEY_VALUE_BASIC_INFORMATION    pbasicinfo;
    PKEY_VALUE_FULL_INFORMATION     pfullinfo;
    PKEY_VALUE_PARTIAL_INFORMATION  ppartinfo;
    UNICODE_STRING                  ukeyname;      
    ANSI_STRING                     akeyname;

    switch( KeyValueInformationClass ) {

    case KeyValueBasicInformation:
        pbasicinfo = (PKEY_VALUE_BASIC_INFORMATION)
            KeyValueInformation;
        sprintf( Buffer, "Type: ");
        AppendRegValueType( pbasicinfo->Type, Buffer );
        strncatZ( Buffer, " Name: ", MAXVALLEN - 1 - strlen(Buffer) );
        ukeyname.Length = (USHORT) pbasicinfo->NameLength;
        ukeyname.MaximumLength = (USHORT) pbasicinfo->NameLength;
        ukeyname.Buffer = pbasicinfo->Name;
        RtlUnicodeStringToAnsiString( &akeyname, &ukeyname, TRUE );
        strncatZ( Buffer, akeyname.Buffer, MAXVALLEN - 1 - strlen(Buffer) );
        if( ValueName ) strncpy( ValueName, akeyname.Buffer, MAXVALLEN - 1 );
        RtlFreeAnsiString( &akeyname );                 
        break;

    case KeyValueFullInformation:   
        pfullinfo = (PKEY_VALUE_FULL_INFORMATION)
            KeyValueInformation;
        AppendRegValueData( pfullinfo->Type,
                            (PVOID) ((PCHAR) pfullinfo + pfullinfo->DataOffset),
                            pfullinfo->DataLength, Buffer );
        if( ValueName ) {
            ukeyname.Length = (USHORT) pfullinfo->NameLength;
            ukeyname.MaximumLength = (USHORT) pfullinfo->NameLength;
            ukeyname.Buffer = pfullinfo->Name;
            RtlUnicodeStringToAnsiString( &akeyname, &ukeyname, TRUE );
            strncpy( ValueName, akeyname.Buffer, MAXVALLEN - 1 );
            RtlFreeAnsiString( &akeyname );
        }
        break;

    case KeyValuePartialInformation:
        ppartinfo = (PKEY_VALUE_PARTIAL_INFORMATION)
            KeyValueInformation;
        AppendRegValueData( ppartinfo->Type,
                            (PVOID) ppartinfo->Data,
                            ppartinfo->DataLength, Buffer );
        break;

    default:
        sprintf( Buffer, "Unknown Info Class" );
        break;
    }
}

//----------------------------------------------------------------------
//
// ErrorString
//
// Returns the string form of an error code.
//
//----------------------------------------------------------------------
PCHAR ErrorString( NTSTATUS retval )
{
    //
    // Before transating, apply error filter
    //
    if( retval == STATUS_SUCCESS && !FilterDef.logsuccess ) return NULL;
    if( retval != STATUS_SUCCESS && !FilterDef.logerror   ) return NULL;

    //
    // Passed filter, so log it
    //
    switch( retval ) {
    case STATUS_BUFFER_TOO_SMALL:
        return "BUFTOOSMALL";
    case STATUS_SUCCESS:
        return "SUCCESS";
    case STATUS_KEY_DELETED:
        return "KEYDELETED";
    case STATUS_REGISTRY_IO_FAILED:
        return "IOFAILED";
    case STATUS_REGISTRY_CORRUPT:
        return "CORRUPT";
    case STATUS_NO_MEMORY:
        return "OUTOFMEM";
    case STATUS_ACCESS_DENIED:
        return "ACCDENIED";
    case STATUS_NO_MORE_ENTRIES:
        return "NOMORE";
    case STATUS_OBJECT_NAME_NOT_FOUND:
        return "NOTFOUND";
    case STATUS_BUFFER_OVERFLOW:
        return "BUFOVRFLOW";
    case STATUS_OBJECT_PATH_SYNTAX_BAD:
        return "SYNTAXERR";
    default:
        sprintf(errstring, "%x", retval );
        return errstring;
    }
}


//----------------------------------------------------------------------
//
// RegmonFreeFilters
//
// Fress storage we allocated for filter strings.
//
//----------------------------------------------------------------------
VOID RegmonFreeFilters()
{
    ULONG   i;

    for( i = 0; i < NumProcessFilters; i++ ) {

        ExFreePool( ProcessFilters );
    }
    for( i = 0; i < NumProcessExcludeFilters; i++ ) {

        ExFreePool( ProcessExcludeFilters );
    }
    for( i = 0; i < NumPathIncludeFilters; i++ ) {

        ExFreePool( PathIncludeFilters );
    }
    for( i = 0; i < NumPathExcludeFilters; i++ ) {

        ExFreePool( PathExcludeFilters );
    }
    NumProcessFilters = 0;
    NumProcessExcludeFilters = 0;
    NumPathIncludeFilters = 0;
    NumPathExcludeFilters = 0;
}

//----------------------------------------------------------------------
//
// MakeFilterArray
//
// Takes a filter string and splits into components (a component
// is seperated with a ';')
//
//----------------------------------------------------------------------
VOID MakeFilterArray( PCHAR FilterString,
                      PCHAR FilterArray[],
                      PULONG NumFilters )
{
    PCHAR filterStart;
    ULONG filterLength;

    //
    // Scan through the process filters
    //
    filterStart = FilterString;
    while( *filterStart ) {

        filterLength = 0;
        while( filterStart[filterLength] &&
               filterStart[filterLength] != ';' ) {

            filterLength++;
        }

        //
        // Ignore zero-length components
        //
        if( filterLength ) {

            FilterArray[ *NumFilters ] =
                ExAllocatePool( PagedPool, filterLength + 1 );
            strncpy( FilterArray[ *NumFilters ],
                     filterStart, filterLength );
            FilterArray[ *NumFilters ][filterLength] = 0;
            (*NumFilters)++;
        }
   
        //
        // Are we done?
        //
        if( !filterStart[filterLength] ) break;

        //
        // Move to the next component (skip over ';')
        //
        filterStart += filterLength + 1;
    }
}

//----------------------------------------------------------------------
//
// RegmonUpdateFilters
//
// Takes a new filter specification and updates the filter
// arrays with them.
//
//----------------------------------------------------------------------
VOID RegmonUpdateFilters()
{
    //
    // Free old filters (if any)
    //
    MUTEX_WAIT( FilterMutex );

    RegmonFreeFilters();

    //
    // Create new filter arrays
    //
    MakeFilterArray( FilterDef.processfilter,
                     ProcessFilters, &NumProcessFilters );
    MakeFilterArray( FilterDef.processexclude,
                     ProcessExcludeFilters, &NumProcessExcludeFilters );
    MakeFilterArray( FilterDef.pathfilter,
                     PathIncludeFilters, &NumPathIncludeFilters );
    MakeFilterArray( FilterDef.excludefilter,
                     PathExcludeFilters, &NumPathExcludeFilters );   

    MUTEX_RELEASE( FilterMutex );
}


//----------------------------------------------------------------------
//
// ApplyNameFilter
//
// If the name matches the exclusion mask, we do not log it. Else if
// it doesn't match the inclusion mask we do not log it.
//
//----------------------------------------------------------------------
BOOLEAN ApplyNameFilter( PCHAR fullname )
{
    ULONG    i;

    //   
    // If it matches the exclusion string, do not log it
    //
    MUTEX_WAIT( FilterMutex );

    for( i = 0; i < NumPathExcludeFilters; i++ ) {

        if( MatchWithPattern( PathExcludeFilters, fullname ) ) {

            MUTEX_RELEASE( FilterMutex );
            return FALSE;
        }
    }

    //
    // If it matches an include filter then log it
    //
    for( i = 0; i < NumPathIncludeFilters; i++ ) {

        if( MatchWithPattern( PathIncludeFilters, fullname )) {

            MUTEX_RELEASE( FilterMutex );
            return TRUE;
        }
    }

    //
    // It didn't match any include filters so don't log
    //
    MUTEX_RELEASE( FilterMutex );
    return FALSE;
}

//----------------------------------------------------------------------
//
// GetFullName
//
// Returns the full pathname of a key, if we can obtain one, else
// returns a handle.
//
//----------------------------------------------------------------------
void GetFullName( HANDLE hKey, PUNICODE_STRING lpszSubKeyVal,
                  PCHAR fullname )
{
    PHASH_ENTRY             hashEntry;
    POBJECT                 pKey = NULL;
    CHAR                    tmpkey[16];
    ANSI_STRING             keyname;
    PCHAR                   tmpname;
    CHAR                    cmpname[MAXROOTLEN];
    PCHAR                   nameptr;
    PUNICODE_STRING         fullUniName;
    ULONG                   actualLen;
    int                     i;

    //
    // Allocate a temporary buffer
    //
    tmpname = ExAllocatePool( PagedPool, MAXPATHLEN );

    //
    // Translate the hkey into a pointer
    //
    fullname[0] = 0;
    tmpname[0] = 0;

    //
    // Is it a valid handle?
    //
    if( pKey = GetPointer( hKey )) {

        //
        // See if we find the key in the hash table
        //
        ReleasePointer( pKey );
        MUTEX_WAIT( HashMutex );
        hashEntry = HashTable[ HASHOBJECT( pKey ) ];
        while( hashEntry && hashEntry->Object != pKey )
            hashEntry = hashEntry->Next;
        MUTEX_RELEASE( HashMutex );

        if( hashEntry ) {

            strcpy( tmpname, hashEntry->FullPathName );

        } else {

            //
            // We will only get here if key was created before we loaded - ask the Configuration
            // Manager what the name of the key is.
            //
            if( pKey ) {

                fullUniName = ExAllocatePool( PagedPool, MAXPATHLEN*2+2*sizeof(ULONG));
                fullUniName->MaximumLength = MAXPATHLEN*2;
        
                if( NT_SUCCESS(ObQueryNameString( pKey, fullUniName, MAXPATHLEN, &actualLen ) )) {

                    RtlUnicodeStringToAnsiString( &keyname, fullUniName, TRUE );
                    if( keyname.Buffer[0] ) {         
                        strcpy( tmpname, "\\" );
                        strncatZ( tmpname, keyname.Buffer, MAXPATHLEN -2 );
                    }
                    RtlFreeAnsiString( &keyname );
                }
                ExFreePool( fullUniName );
            }
        }
    }

    //
    // Append subkey and value, if they are there
    //
    if( lpszSubKeyVal ) {
        RtlUnicodeStringToAnsiString( &keyname, lpszSubKeyVal, TRUE );
        if( keyname.Buffer[0] ) {
            strcat( tmpname, "\\" );
            strncatZ( tmpname, keyname.Buffer, MAXPATHLEN - 1 - strlen(tmpname) );
        }
        RtlFreeAnsiString( &keyname );
    }

    //
    // See if it matches current user
    //
    for( i = 0; i < 2; i++ ) {
        ConvertToUpper( cmpname, tmpname, CurrentUser.RootNameLen );
        if( !strncmp( cmpname, CurrentUser.RootName,
                      CurrentUser.RootNameLen )) {

            DbgPrint(( " CurrentUser(%d) %s ==> %s\n", i,
                       tmpname, CurrentUser.RootName ));

            //
            // Its current user. Process to next slash
            //
            nameptr = tmpname + CurrentUser.RootNameLen;
            while( *nameptr && *nameptr != '\\' ) nameptr++;
            strcpy( fullname, CurrentUser.RootShort );
            strcat( fullname, nameptr );
            ExFreePool( tmpname );
            return;
        }
    }     

    //
    // Now, see if we can translate a root key name
    //
    for( i = 0; i < NUMROOTKEYS; i++ ) {
        ConvertToUpper( cmpname, tmpname, RootKey.RootNameLen );
        if( !strncmp( cmpname, RootKey.RootName,
                      RootKey.RootNameLen )) {
            nameptr = tmpname + RootKey.RootNameLen;
            strcpy( fullname, RootKey.RootShort );
            strcat( fullname, nameptr );
            ExFreePool( tmpname );
            return;
        }
    }

    //
    // No translation
    //
    strcpy( fullname, tmpname );
    ExFreePool( tmpname );
}


//----------------------------------------------------------------------
//
// GetProcessNameOffset
//
// In an effort to remain version-independent, rather than using a
// hard-coded into the KPEB (Kernel Process Environment Block), we
// scan the KPEB looking for the name, which should match that
// of the GUI process
//
//----------------------------------------------------------------------
ULONG GetProcessNameOffset()
{
    PEPROCESS       curproc;
    int             i;

    curproc = PsGetCurrentProcess();

    //
    // Scan for 12KB, hopping the KPEB never grows that big!
    //
    for( i = 0; i < 3*PAGE_SIZE; i++ ) {
     
        if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) )) {

            return i;
        }
    }

    //
    // Name not found - oh, well
    //
    return 0;
}



//----------------------------------------------------------------------
//
// GetProcess
//
// Uses undocumented data structure offsets to obtain the name of the
// currently executing process.
//
//----------------------------------------------------------------------
PCHAR GetProcess( PCHAR Name )
{
    PEPROCESS       curproc;
    char            *nameptr;
    ULONG           i;

    //
    // We only try and get the name if we located the name offset
    //
    if( ProcessNameOffset ) {
   
        curproc = PsGetCurrentProcess();
        nameptr   = (PCHAR) curproc + ProcessNameOffset;
        strncpy( Name, nameptr, 16 );

    } else {
     
        strcpy( Name, "???");
    }

    //
    // Apply process name filters
    //
    MUTEX_WAIT( FilterMutex );
    for( i = 0; i < NumProcessExcludeFilters; i++ ) {

        if( MatchWithPattern( ProcessExcludeFilters, Name )) {

            MUTEX_RELEASE( FilterMutex );
            return NULL;
        }
    }
    for( i = 0; i < NumProcessFilters; i++ ) {

        if( MatchWithPattern( ProcessFilters, Name ) ) {

            MUTEX_RELEASE( FilterMutex );
            return Name;
        }
    }
    MUTEX_RELEASE( FilterMutex );
    return NULL;
}

//======================================================================
//                    H O O K  R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// HookRegOpenKey
//
//----------------------------------------------------------------------
NTSTATUS HookRegOpenKey( IN OUT PHANDLE pHandle, IN ACCESS_MASK ReqAccess,
                         IN POBJECT_ATTRIBUTES pOpenInfo )
{
    NTSTATUS                ntstatus;
    POBJECT                 regobj;
    CHAR                    fullname[MAXPATHLEN], data[MAXDATALEN], name[MAXPROCNAMELEN];

    GetFullName( pOpenInfo->RootDirectory, pOpenInfo->ObjectName, fullname );
    ntstatus = RealRegOpenKey( pHandle, ReqAccess, pOpenInfo );
    DbgPrint(("RegOpenKey: %s => %x, %x\n", fullname, *pHandle, ntstatus ));
    data[0] = 0;
    if( NT_SUCCESS( ntstatus )) {   
        regobj = GetPointer( *pHandle );
        RegmonFreeHashEntry( regobj );
        RegmonStoreHash( regobj, fullname );
        sprintf(data,"Key: 0x%X", regobj );
        ReleasePointer( regobj );
    }
    if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tOpenKey\t%s\t%s\t%s", name,
                     fullname, ErrorString( ntstatus ), data );
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegCreateKey
//
//----------------------------------------------------------------------
NTSTATUS HookRegCreateKey( OUT PHANDLE pHandle, IN ACCESS_MASK ReqAccess,
                           IN POBJECT_ATTRIBUTES pOpenInfo, IN ULONG TitleIndex,
                           IN PUNICODE_STRING Class, IN ULONG CreateOptions, OUT PULONG Disposition )
{
    NTSTATUS                ntstatus;
    POBJECT                 regobj;
    CHAR                    fullname[MAXPATHLEN], data[MAXDATALEN], name[MAXPROCNAMELEN];

    GetFullName( pOpenInfo->RootDirectory, pOpenInfo->ObjectName, fullname );
        ntstatus = RealRegCreateKey( pHandle, ReqAccess, pOpenInfo, TitleIndex,
                                 Class, CreateOptions, Disposition );
    DbgPrint(("RegCreateKey: %s => %x, %x\n", fullname, *pHandle, ntstatus ));
    data[0] = 0;
    if( NT_SUCCESS( ntstatus )) {   
        regobj = GetPointer( *pHandle );
        RegmonFreeHashEntry( regobj );
        RegmonStoreHash( regobj, fullname );
        sprintf(data,"Key: 0x%X", regobj );
        ReleasePointer( regobj );
    }
    if( ((NT_SUCCESS( ntstatus ) &&
          (Disposition && *Disposition == REG_CREATED_NEW_KEY && FilterDef.logwrites)) ||
         FilterDef.logreads ) &&
        GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tCreateKey\t%s\t%s\t%s", name,
                     fullname, ErrorString( ntstatus ), data);
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegCloseKey
//
// This is actually a hook for NtClose which is used for closing any
// object. Therefore, we must ensure that we are seeing a close for
// a registry object that we know about.
//
//----------------------------------------------------------------------
NTSTATUS HookRegCloseKey( IN HANDLE Handle )
{
    NTSTATUS                ntstatus;
    POBJECT                 regobj;
    CHAR                    name[MAXPROCNAMELEN];
    PCHAR                   fullname, data;
    ULONG                   retlen;
    BOOLEAN                 iskey = FALSE;
    KEY_BASIC_INFORMATION   basicInfo;
   
    //
    // Allocate data from pool since this close routine can be called from other
    // drivers, where the stack space may already be strained
    //
    fullname = ExAllocatePool( PagedPool, MAXPATHLEN );
    data     = ExAllocatePool( PagedPool, MAXVALLEN );

    //
    // Determine if the object is a key by querying it
    //
    ntstatus = RealRegQueryKey( Handle, KeyBasicInformation,
                                &basicInfo, 0, &retlen );
    if( ntstatus != STATUS_OBJECT_TYPE_MISMATCH ) {
        iskey = TRUE;        
        GetFullName( Handle, NULL, fullname );

        // get the pointer for later
        regobj = GetPointer( Handle );
        ReleasePointer( regobj );
    }

    ntstatus = RealRegCloseKey( Handle );
    if( iskey ) {
        DbgPrint(("RegCloseKey: %s => %x, %x\n", fullname, Handle, ntstatus ));
        data[0] = 0;
        if( NT_SUCCESS( ntstatus )) {
            if( regobj ) RegmonFreeHashEntry( regobj );
            sprintf(data,"Key: 0x%X", regobj );
            if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
                UpdateStore( "%s\tCloseKey\t%s\t%s\t%s",
                             name, fullname, ErrorString( ntstatus ), data );
            }
        }
    }

    ExFreePool( fullname );
    ExFreePool( data );
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegFlushKey
//
//----------------------------------------------------------------------
NTSTATUS HookRegFlushKey( IN HANDLE Handle )
{
    NTSTATUS                ntstatus;
    CHAR                    fullname[MAXPATHLEN], name[MAXPROCNAMELEN];
    POBJECT                 regobj;

    GetFullName( Handle, NULL, fullname );
    ntstatus = RealRegFlushKey( Handle );
    DbgPrint(("RegFlushKey: %s => 0x%X\n", fullname, ntstatus ));
    regobj = GetPointer( Handle );
    ReleasePointer( regobj );
    if( FilterDef.logwrites && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tFlushKey\t%s\t%s\tKey: 0x%X",
                     name, fullname, ErrorString( ntstatus ), regobj);
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegDeleteKey
//
// Once we've deleted a key, we can remove its reference in the hash
// table.
//
//----------------------------------------------------------------------
NTSTATUS HookRegDeleteKey( IN HANDLE Handle )
{
    NTSTATUS                ntstatus;
    POBJECT                 regobj;
    CHAR                    fullname[MAXPATHLEN], name[MAXPROCNAMELEN];

    GetFullName( Handle, NULL, fullname );
    regobj = GetPointer( Handle );
    ReleasePointer( regobj );
    ntstatus = RealRegDeleteKey( Handle );
    DbgPrint(("RegDeleteKey: %s => 0x%X\n", fullname, ntstatus ));
    if( FilterDef.logwrites && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tDeleteKey\t%s\t%s\tKey: 0x%X",
                     name, fullname,
                     ErrorString( ntstatus ), regobj);
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegDeleteValueKey
//
//----------------------------------------------------------------------
NTSTATUS HookRegDeleteValueKey( IN HANDLE Handle, PUNICODE_STRING Name )
{
    NTSTATUS                ntstatus;
    CHAR                    fullname[MAXPATHLEN], name[MAXPROCNAMELEN];

    GetFullName( Handle, Name, fullname );
    ntstatus = RealRegDeleteValueKey( Handle, Name );
    DbgPrint(("RegDeleteValueKey: %s => %x\n", fullname, ntstatus ));
    if( FilterDef.logwrites && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus ) ) {
        UpdateStore( "%s\tDeleteValueKey\t%s\t%s\t",
                     name, fullname, ErrorString( ntstatus ));
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegSetValueKey
//
//----------------------------------------------------------------------
NTSTATUS HookRegSetValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName,
                             IN ULONG TitleIndex, IN ULONG Type, IN PVOID Data, IN ULONG DataSize )
{
    NTSTATUS                ntstatus;
    PUNICODE_STRING         valueName;
    CHAR                    fullname[MAXPATHLEN], data[MAXVALLEN], name[MAXPROCNAMELEN];

    if( !ValueName || !ValueName->Length ) valueName = &DefaultValue;
    else                                   valueName = ValueName;
    GetFullName( KeyHandle, valueName, fullname );
    ntstatus = RealRegSetValueKey( KeyHandle, ValueName, TitleIndex,
                                   Type, Data, DataSize );
    data[0] = 0;
    if( NT_SUCCESS( ntstatus ))
        AppendRegValueData( Type, Data, DataSize, data );
    DbgPrint(("SetValue: %s (%s)\n", fullname, data ));
    if( FilterDef.logwrites && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tSetValue\t%s\t%s\t%s",
                     name, fullname, ErrorString( ntstatus ), data );
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegEnumerateKey
//
// This is a documented Zw-class function.
//
//----------------------------------------------------------------------
NTSTATUS HookRegEnumerateKey( IN HANDLE KeyHandle, IN ULONG Index,
                              IN KEY_INFORMATION_CLASS KeyInformationClass,
                              OUT PVOID KeyInformation, IN ULONG Length, OUT PULONG pResultLength )
{
    NTSTATUS                ntstatus;
    CHAR                    fullname[MAXPATHLEN], data[MAXVALLEN], name[MAXPROCNAMELEN];

    GetFullName( KeyHandle, NULL, fullname );
    ntstatus = RealRegEnumerateKey( KeyHandle, Index, KeyInformationClass,
                                    KeyInformation, Length, pResultLength );
    data[0] = 0;
    if( NT_SUCCESS( ntstatus ))
        AppendKeyInformation( KeyInformationClass, KeyInformation, data );

    DbgPrint(("EnumerateKey: %s (%s) => %x\n", fullname, data, ntstatus ));
    if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tEnumerateKey\t%s\t%s\t%s",
                     name, fullname, ErrorString( ntstatus ), data );
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegQueryKey
//
// This is a documented Zw-class function. This will get called
// from our CloseKey hook routine, because this is the only easy
// way we can determine if a registry key is being closed. Thus, we
// have to watch for those calls and not log any data about them.
//
//----------------------------------------------------------------------
NTSTATUS HookRegQueryKey( IN HANDLE  KeyHandle,
                          IN KEY_INFORMATION_CLASS  KeyInformationClass,
                          OUT PVOID  KeyInformation, IN ULONG  Length,
                          OUT PULONG  pResultLength )
{
    NTSTATUS                ntstatus;
    CHAR                    name[MAXPROCNAMELEN];
    PCHAR                   fullname, data;

    //
    // Allocate data from pool since this routine is called from the HookRegClose routine,
    // which is called on non-key object and so is likely to be originating in a driver
    // that may already have strained stack space.
    //
    fullname = ExAllocatePool( PagedPool, MAXPATHLEN );
    data     = ExAllocatePool( PagedPool, MAXVALLEN );

    GetFullName( KeyHandle, NULL, fullname );

    ntstatus = RealRegQueryKey( KeyHandle, KeyInformationClass,
                                KeyInformation, Length, pResultLength );

    // print out different stuff depending on type of info asked for
    data[0] = 0;
    if( NT_SUCCESS( ntstatus ))
        AppendKeyInformation( KeyInformationClass, KeyInformation, data );

    DbgPrint(("QueryKey: %s (%s) => %x\n", fullname, data, ntstatus ));
    if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tQueryKey\t%s\t%s\t%s",
                     name, fullname, ErrorString( ntstatus ), data );
    }

    ExFreePool( fullname );
    ExFreePool( data );
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegEnumerateValueKey
//
// This is a documented Zw-class function.
//
//----------------------------------------------------------------------
NTSTATUS HookRegEnumerateValueKey( IN HANDLE KeyHandle, IN ULONG Index,
                                   IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
                                   OUT PVOID KeyValueInformation, IN ULONG Length,
                                   OUT PULONG  pResultLength )
{
    NTSTATUS                ntstatus;
    CHAR                    fullname[MAXPATHLEN], data[MAXVALLEN];
    CHAR                    valuename[MAXVALLEN], name[MAXPROCNAMELEN];

    GetFullName( KeyHandle, NULL, fullname );
    ntstatus = RealRegEnumerateValueKey( KeyHandle, Index,
                                         KeyValueInformationClass,
                                         KeyValueInformation, Length,
                                         pResultLength );
    data[0] = 0;

    if( NT_SUCCESS( ntstatus )) {
        AppendValueInformation( KeyValueInformationClass,
                                KeyValueInformation, data, valuename );
        strcat( fullname, "\\" );
        strncatZ( fullname, valuename, MAXPATHLEN - 1 - strlen(fullname) );
    }

        DbgPrint(("EnumerateValue: %s (%s) =>%x\n", fullname, data, ntstatus ));
    if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tEnumerateValue\t%s\t%s\t%s",
                     name, fullname, ErrorString( ntstatus ), data );
    }

    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegQueryValueKey
//
// This is a documented Zw-class function.
//
//----------------------------------------------------------------------
NTSTATUS HookRegQueryValueKey( IN HANDLE KeyHandle,
                               IN PUNICODE_STRING ValueName,
                               IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
                               OUT PVOID KeyValueInformation, IN ULONG Length,
                               OUT PULONG  pResultLength )
{
    NTSTATUS                ntstatus;
    PUNICODE_STRING         valueName;
    CHAR                    fullname[MAXPATHLEN], data[MAXVALLEN], name[MAXPROCNAMELEN];

    if( !ValueName || !ValueName->Length ) valueName = &DefaultValue;
    else                                   valueName = ValueName;
    GetFullName( KeyHandle, valueName, fullname );
    ntstatus = RealRegQueryValueKey( KeyHandle, ValueName,
                                     KeyValueInformationClass,
                                     KeyValueInformation, Length,
                                     pResultLength );
    data[0] = 0;
    if( NT_SUCCESS( ntstatus ))
        AppendValueInformation( KeyValueInformationClass,
                                KeyValueInformation, data, FALSE );
    DbgPrint(("QueryValue: %s (%s) =>%x\n", fullname, data, ntstatus ));
    if( FilterDef.logreads && GetProcess( name ) && ApplyNameFilter(fullname) && ErrorString( ntstatus )) {
        UpdateStore( "%s\tQueryValue\t%s\t%s\t%s",
                     name, fullname, ErrorString( ntstatus ), data );
    }
    return ntstatus;
}


//----------------------------------------------------------------------
//
// HookRegistry
//
// Replaces entries in the system service table with pointers to
// our own hook routines. We save off the real routine addresses.
//
//----------------------------------------------------------------------
VOID HookRegistry( void )
{
    if( !RegHooked ) {

        //
        // Hook everything
        //

        RealRegOpenKey = SYSCALL( ZwOpenKey );
        SYSCALL( ZwOpenKey ) = (PVOID) HookRegOpenKey;

        RealRegQueryKey = SYSCALL( ZwQueryKey );
        SYSCALL( ZwQueryKey ) = (PVOID) HookRegQueryKey;

        RealRegQueryValueKey = SYSCALL( ZwQueryValueKey );
        SYSCALL( ZwQueryValueKey ) = (PVOID) HookRegQueryValueKey;

        RealRegEnumerateValueKey = SYSCALL( ZwEnumerateValueKey );
        SYSCALL( ZwEnumerateValueKey ) = (PVOID) HookRegEnumerateValueKey;

        RealRegEnumerateKey = SYSCALL( ZwEnumerateKey );
        SYSCALL( ZwEnumerateKey ) = (PVOID) HookRegEnumerateKey;

        RealRegDeleteKey = SYSCALL( ZwDeleteKey );
        SYSCALL( ZwDeleteKey ) = (PVOID) HookRegDeleteKey;

        RealRegFlushKey = SYSCALL( ZwFlushKey );
        SYSCALL( ZwFlushKey ) = (PVOID) HookRegFlushKey;

        RealRegSetValueKey = SYSCALL( ZwSetValueKey );
        SYSCALL( ZwSetValueKey ) = (PVOID) HookRegSetValueKey;

        RealRegCreateKey = SYSCALL( ZwCreateKey );
#if defined(_ALPHA_)
        SYSCALL( ZwCreateKey ) = (PVOID) ((ULONG) HookRegCreateKey + ((ULONG) RealRegCreateKey & 0x00000003));
#else
        SYSCALL( ZwCreateKey ) = (PVOID) HookRegCreateKey;
#endif

        RealRegDeleteValueKey = SYSCALL( ZwDeleteValueKey );
        SYSCALL( ZwDeleteValueKey ) = (PVOID) HookRegDeleteValueKey;

        RealRegCloseKey = SYSCALL( ZwClose );
        SYSCALL( ZwClose ) = (PVOID) HookRegCloseKey;

        RegHooked = TRUE;

    }
}


//----------------------------------------------------------------------
//
// UnhookRegistry
//
// Unhooks all registry routines by replacing the hook addresses in
// the system service table with the real routine addresses that we
// saved off.
//
//----------------------------------------------------------------------
VOID UnhookRegistry( )
{
    if( RegHooked ) {

        //
        // Unhook everything
        //
        SYSCALL( ZwOpenKey ) = (PVOID) RealRegOpenKey;
        SYSCALL( ZwQueryKey ) = (PVOID) RealRegQueryKey;
        SYSCALL( ZwQueryValueKey ) = (PVOID) RealRegQueryValueKey;
        SYSCALL( ZwEnumerateValueKey ) = (PVOID) RealRegEnumerateValueKey;
        SYSCALL( ZwEnumerateKey ) = (PVOID) RealRegEnumerateKey;
        SYSCALL( ZwClose ) = (PVOID) RealRegCloseKey;
        SYSCALL( ZwFlushKey ) = (PVOID) RealRegFlushKey;
        SYSCALL( ZwDeleteKey ) = (PVOID) RealRegDeleteKey;
        SYSCALL( ZwSetValueKey ) = (PVOID) RealRegSetValueKey;
        SYSCALL( ZwCreateKey ) = (PVOID) RealRegCreateKey;
        SYSCALL( ZwDeleteValueKey ) = (PVOID) RealRegDeleteValueKey;

        RegHooked = FALSE;
    }
}

//======================================================================
//         D E V I C E - D R I V E R  R O U T I N E S
//======================================================================

//----------------------------------------------------------------------
//
// RegmonDeviceControl
//
//----------------------------------------------------------------------
BOOLEAN  RegmonDeviceControl( IN PFILE_OBJECT FileObject, IN BOOLEAN Wait,
                              IN PVOID InputBuffer, IN ULONG InputBufferLength,
                              OUT PVOID OutputBuffer, IN ULONG OutputBufferLength,
                              IN ULONG IoControlCode, OUT PIO_STATUS_BLOCK IoStatus
                              ) {
    BOOLEAN                 retval = FALSE;
    PSTORE_BUF              old;

    //
    // Its a message from our GUI!
    //
    IoStatus->Status      = STATUS_SUCCESS; // Assume success
    IoStatus->Information = 0;              // Assume nothing returned
    switch ( IoControlCode ) {

    case REGMON_version:

        //
        // Version #
        //
RegmonOpenBootLog;
DbgPrint (("Regmon: version\n"));

  *(ULONG *)OutputBuffer = REGMONVERSION;
  IoStatus->Information = sizeof(ULONG);
  break;
        if ( OutputBufferLength < sizeof(ULONG) ) {

            IoStatus->Status = STATUS_INVALID_PARAMETER;
            break;
        }
            
        *(ULONG *)OutputBuffer = REGMONV