LoadImageNotify


/*

------------------ BEGIN MAKEFILE -----------------


!INCLUDE $(NTMAKEENV)\makefile.def


------------------ END MAKEFILE -------------------

*/


/*

------------------ BEGIN SOURCES ------------------


TARGETNAME=LoadImageNotify

TARGETPATH=obj

TARGETTYPE=DRIVER

INCLUDES=

SOURCES=LoadImageNotify.c


----------------- END SOURCES ---------------------

*/


//

//  This sample demonstrates an alternative to detecting when the loader

//  maps an executable image into memory (exe / dll / sys etc).  Instead

//  of using PsSetLoadImageNotifyRoutine() or

//  PsSetCreateProcessNotifyRoutine() to "officially" give us image load

//  events, we can hook NtCreateSection().  This is pretty nasty.. but

//  its another method for our bag of tricks.

//

//  Extract the SOURCES and MAKEFILE above as needed then use the DDK

//  build environment to create the driver.  Use a driver loader to load

//  the driver into the kernel.  You can find a driver loader in the

//  downloads section of http://www.osronline.com.

//

//  Michael Wookey / July 2006

//

#include <ntddk.h>


#ifndef SEC_IMAGE

#define SEC_IMAGE 0x01000000

#endif


struct KSERVICE_TABLE_DESCRIPTOR {

    PULONG_PTR Base;

    PULONG Count;

    ULONG Limit;

    PUCHAR Number;

};


#define INVALID_INDEX -1

ULONG g_Index_NtCreateSection = INVALID_INDEX;


extern struct KSERVICE_TABLE_DESCRIPTOR *KeServiceDescriptorTable; 


//

//  Pointer to the original function.

//

NTSTATUS

(*Orig_NtCreateSection) (

    OUT PHANDLE SectionHandle,

    IN ACCESS_MASK DesiredAccess,

    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

    IN PLARGE_INTEGER MaximumSize OPTIONAL,

    IN ULONG SectionPageProtection,

    IN ULONG AllocationAttributes,

    IN HANDLE FileHandle OPTIONAL

    );



//

//  Our trampoline function.

//

NTSTATUS

Hook_NtCreateSection(

    OUT PHANDLE SectionHandle,

    IN ACCESS_MASK DesiredAccess,

    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

    IN PLARGE_INTEGER MaximumSize OPTIONAL,

    IN ULONG SectionPageProtection,

    IN ULONG AllocationAttributes,

    IN HANDLE FileHandle OPTIONAL

    )

{

    NTSTATUS rc = STATUS_ACCESS_DENIED;

    PFILE_OBJECT fileObject = NULL;


    if (   (SectionPageProtection & PAGE_EXECUTE)

        && (AllocationAttributes & SEC_IMAGE))

    {

        //

        //  The loader is mapping a new image.  Obtain a filename...

        //

        ObReferenceObjectByHandle(FileHandle, 0, 0, KernelMode, &fileObject, NULL);


        if (fileObject)

        { 

            //

            //  Print the image that is being loaded.  We are using

            //  DbgPrint() so make sure you build this driver in a

            //  checked environment to see the output.

            //

            DbgPrint("Image: %wZ\n", &fileObject->FileName);

        }

    }


    //

    //  Pass through to call the original function.

    //

    rc = (*Orig_NtCreateSection)(

            SectionHandle,

            DesiredAccess,

            ObjectAttributes,

            MaximumSize,

            SectionPageProtection,

            AllocationAttributes,

            FileHandle);

    

    return rc;

}



VOID

CreateNtCreateSectionHook()

{

    ULONG tableEntryOffsetAddress = 0;

    ULONG mappedTableEntryAddress = 0;

    UNICODE_STRING sNtCreateSection;

    ULONG NtCreateSectionRoutineAddress = 0;

    ULONG tableRoutineAddress = 0;

    BOOLEAN foundRoutineAddress = FALSE;


    //

    //  Locate the address of NtCreateSection in ntoskrnl

    //

    RtlInitUnicodeString(&sNtCreateSection, L"NtCreateSection");


    NtCreateSectionRoutineAddress = (ULONG) MmGetSystemRoutineAddress(&sNtCreateSection); 


    if (NtCreateSectionRoutineAddress)

    {

        //

        //  Now, look through the SSDT for the index that contains the

        //  address of NtCreateSection.  By doing things this way we

        //  don't hardcode the index into the SSDT to any particular

        //  version of the kernel.

        //

        for (g_Index_NtCreateSection = 0 ; 

             g_Index_NtCreateSection < KeServiceDescriptorTable->Limit ; 

             g_Index_NtCreateSection++)

        {

            tableRoutineAddress = KeServiceDescriptorTable->Base[g_Index_NtCreateSection];


            if (tableRoutineAddress == NtCreateSectionRoutineAddress)

            {

                foundRoutineAddress = TRUE;

                break;

            }

        }

    }

    

    if (FALSE == foundRoutineAddress)

    {

        //

        //  Reset the index to being invalid so that on driver unload,

        //  we don't attempt to mess with the SSDT.

        //

        g_Index_NtCreateSection = INVALID_INDEX;

    }

    else

    {

        //    

        //  Hook service dispatch table

        //

        tableEntryOffsetAddress = (sizeof(ULONG) * g_Index_NtCreateSection) 

                                + (ULONG) KeServiceDescriptorTable->Base;

        

        mappedTableEntryAddress = (ULONG) MmMapIoSpace(

                                            MmGetPhysicalAddress((PVOID) tableEntryOffsetAddress), 

                                            sizeof(ULONG), 

                                            MmNonCached);


        //

        //  I should probably rewrite the following in C, but I'm too

        //  lazy to do all the function pointer casting right now...

        //

        __asm mov eax, mappedTableEntryAddress  


        //  

        //  Save the original function pointer...

        //

        __asm mov ebx, dword ptr[eax]

        __asm mov Orig_NtCreateSection, ebx


        //

        // Replace the table entry with our own routine

        //

        __asm mov ebx, Hook_NtCreateSection     

        __asm mov dword ptr[eax], ebx


        MmUnmapIoSpace((PVOID) mappedTableEntryAddress, sizeof(ULONG));

    }

}



VOID 

DriverUnload(

    IN PDRIVER_OBJECT DriverObject

    )

{

    ULONG tableEntryOffsetAddress = 0;

    ULONG mappedTableEntryAddress = 0;


    if (INVALID_INDEX != g_Index_NtCreateSection)

    {

        tableEntryOffsetAddress = (sizeof(ULONG) * g_Index_NtCreateSection) 

                                + (ULONG) KeServiceDescriptorTable->Base;


        mappedTableEntryAddress = (ULONG) MmMapIoSpace(

                                            MmGetPhysicalAddress((PVOID) tableEntryOffsetAddress), 

                                            sizeof(ULONG), 

                                            MmNonCached);


        //

        //  Restore the original function pointer to the SSDT table.

        //

        __asm mov eax, mappedTableEntryAddress

        __asm mov ebx, Orig_NtCreateSection

        __asm mov dword ptr[eax], ebx


        MmUnmapIoSpace((PVOID) mappedTableEntryAddress, sizeof(ULONG));

    }

}



NTSTATUS

DriverEntry(

    IN PDRIVER_OBJECT DriverObject,

    IN PUNICODE_STRING RegistryPath

    )

{

    DriverObject->DriverUnload = DriverUnload;


    CreateNtCreateSectionHook();


    return STATUS_SUCCESS;

}


/* EOF */