|
/*
------------------ 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 */
|