Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1864 lines
43 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
stat.c
Abstract:
Pentium stat driver.
Author:
Ken Reneris
Environment:
Notes:
Revision History:
--*/
#include "stdarg.h"
#include "stdio.h"
#define _NTDDK_ 1
#include "\nt\private\ntos\inc\ntos.h" // *** USES INTERNAL DEFINES ***
#include "..\..\pstat.h"
#include "stat.h"
#include "zwapi.h"
typedef
VOID
(*pHalProfileInterrupt) (
KPROFILE_SOURCE ProfileSource
);
//
// Global data (not in device extension)
//
//
// stats
//
PACCUMULATORS StatProcessorAccumulators[MAXIMUM_PROCESSORS];
ACCUMULATORS StatGlobalAccumulators [MAXIMUM_PROCESSORS];
PKPCR KiProcessorControlRegister [MAXIMUM_PROCESSORS];
//
// hooked thunks
//
ULONG KeUpdateSystemTimeThunk;
ULONG KeUpdateRunTimeThunk;
pHalProfileInterrupt HaldStartProfileInterrupt;
pHalProfileInterrupt HaldStopProfileInterrupt;
pHalQuerySystemInformation HaldQuerySystemInformation;
pHalSetSystemInformation HaldSetSystemInformation;
//
// hardware control
//
ULONG NoCESR;
ULONG MsrCESR;
ULONG MsrCount;
#define MsrTSC 0x10
#define NoCount 2
ULONG CESR[MAX_EVENTS];
FAST_MUTEX HookLock;
ULONG StatMaxThunkCounter;
LIST_ENTRY HookedThunkList;
LIST_ENTRY LazyFreeList;
ULONG LazyFreeCountdown;
KTIMER LazyFreeTimer;
KDPC LazyFreeDpc;
WORK_QUEUE_ITEM LazyFreePoolWorkItem;
extern COUNTED_EVENTS P5Events[];
extern COUNTED_EVENTS P6Events[];
ULONG MaxEvent;
PCOUNTED_EVENTS Events;
ULONG ProcType;
#define GENERIC_X86 0
#define INTEL_P5 1
#define INTEL_P6 2
//
// Profile support
//
#define PROFILE_SOURCE_BASE 0x1000
typedef struct {
ULONG CESR;
KPROFILE_SOURCE Source;
ULONGLONG InitialCount;
} PROFILE_EVENT, *PPROFILE_EVENT;
BOOLEAN ProfileSupported;
PPROFILE_EVENT ProfileEvents, CurrentProfileEvent;
//
// bugbug: these should be enumerated off
//
PUCHAR HalName[] = {
"hal.dll", "halast.dll", "halsp.dll", "halmps.dll",
"halncr.dll", NULL };
//
//
//
ULONGLONG FASTCALL RDMSR(ULONG);
VOID WRMSR(ULONG, ULONGLONG);
VOID StatSystemTimeHook(VOID);
VOID StatRunTimeHook(VOID);
VOID SystemTimeHook(VOID);
VOID RunTimeHook(VOID);
PKPCR CurrentPcr();
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
StatDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
StatQueryEvents (
ULONG Index,
PEVENTID Buffer,
ULONG Length
);
NTSTATUS
StatHookGenericThunk (
IN PHOOKTHUNK Buffer
);
VOID
StatRemoveGenericHook (
IN PULONG pTracerId
);
NTSTATUS
StatOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
StatClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
BOOLEAN
StatHookTimer (VOID);
VOID StatReadStats (PULONG Buffer);
VOID StatSetCESR (PSETEVENT);
ULONG StatGetP5CESR (PSETEVENT);
ULONG StatGetP6CESR (PSETEVENT, BOOLEAN);
VOID RemoveAllHookedThunks (VOID);
VOID FASTCALL TimerHook (ULONG p);
VOID FASTCALL TimerHook (ULONG p);
VOID SetMaxThunkCounter (VOID);
VOID RemoveAllHookedThunks (VOID);
VOID LazyFreePoolDPC (PKDPC, PVOID, PVOID, PVOID);
VOID LazyFreePool (PVOID);
PPROFILE_EVENT
StatProfileEvent (
KPROFILE_SOURCE Source
);
VOID
StatStartProfileInterrupt (
KPROFILE_SOURCE Source
);
VOID
StatStopProfileInterrupt (
KPROFILE_SOURCE Source
);
NTSTATUS
FASTCALL
StatProfileInterrupt (
IN PKTRAP_FRAME TrapFrame
);
NTSTATUS
StatQuerySystemInformation(
IN HAL_QUERY_INFORMATION_CLASS InformationClass,
IN ULONG BufferSize,
OUT PVOID Buffer,
OUT PULONG ReturnedLength
);
NTSTATUS
StatSetSystemInformation(
IN HAL_SET_INFORMATION_CLASS InformationClass,
IN ULONG BufferSize,
IN PVOID Buffer
);
VOID
CreateHook (
IN PVOID HookCode,
IN PVOID HookAddress,
IN ULONG HitCounters,
IN ULONG HookType
);
NTSTATUS
openfile (
IN PHANDLE filehandle,
IN PUCHAR BasePath,
IN PUCHAR Name
);
VOID
readfile (
HANDLE handle,
ULONG offset,
ULONG len,
PVOID buffer
);
ULONG
ImportThunkAddress (
IN PUCHAR SourceModule,
IN ULONG ImageBase,
IN PUCHAR ImportModule,
IN PUCHAR ThunkName
);
ULONG
LookupImageBase (
PUCHAR SourceModule
);
ULONG
ConvertImportAddress (
IN ULONG ImageRelativeAddress,
IN ULONG PoolAddress,
IN PIMAGE_SECTION_HEADER SectionHeader
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,StatHookTimer)
#pragma alloc_text(PAGE,StatDeviceControl)
#pragma alloc_text(PAGE,StatOpen)
#pragma alloc_text(PAGE,StatClose)
#pragma alloc_text(PAGE,StatReadStats)
#pragma alloc_text(PAGE,StatSetCESR)
#pragma alloc_text(PAGE,StatGetP5CESR)
#pragma alloc_text(PAGE,StatGetP6CESR)
#pragma alloc_text(PAGE,StatDeviceControl)
#pragma alloc_text(PAGE,StatQueryEvents)
#pragma alloc_text(PAGE,ImportThunkAddress)
#pragma alloc_text(PAGE,StatHookGenericThunk)
#pragma alloc_text(PAGE,StatRemoveGenericHook)
#pragma alloc_text(PAGE,SetMaxThunkCounter)
#pragma alloc_text(PAGE,LazyFreePool)
#pragma alloc_text(PAGE,StatQuerySystemInformation)
#pragma alloc_text(PAGE,StatSetSystemInformation)
#pragma alloc_text(PAGE,openfile)
#pragma alloc_text(PAGE,readfile)
#pragma alloc_text(PAGE,LookupImageBase)
#pragma alloc_text(PAGE,ConvertImportAddress)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine initializes the stat driver.
Arguments:
DriverObject - Pointer to driver object created by system.
RegistryPath - Pointer to the Unicode name of the registry path
for this driver.
Return Value:
The function value is the final status from the initialization operation.
--*/
{
UNICODE_STRING unicodeString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
ULONG i;
KdPrint(( "STAT: DriverEntry()\n" ));
//
// Create non-exclusive device object for beep device.
//
RtlInitUnicodeString(&unicodeString, L"\\Device\\PStat");
status = IoCreateDevice(
DriverObject,
0,
&unicodeString,
FILE_DEVICE_UNKNOWN, // DeviceType
0,
FALSE,
&deviceObject
);
if (status != STATUS_SUCCESS) {
KdPrint(( "Stat - DriverEntry: unable to create device object: %X\n", status ));
return(status);
}
deviceObject->Flags |= DO_BUFFERED_IO;
//
// Set up the device driver entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = StatOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = StatClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = StatDeviceControl;
//
// Initialize globals
//
for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
StatProcessorAccumulators[i] =
&StatGlobalAccumulators[i];
}
ExInitializeFastMutex (&HookLock);
KeInitializeDpc (&LazyFreeDpc, LazyFreePoolDPC, 0);
ExInitializeWorkItem (&LazyFreePoolWorkItem, LazyFreePool, NULL)
KeInitializeTimer (&LazyFreeTimer);
if (strcmp (CurrentPcr()->Prcb->VendorString, "GenuineIntel") == 0) {
switch (CurrentPcr()->Prcb->CpuType) {
case 5:
NoCESR = 1;
MsrCESR = 0x11;
MsrCount = 0x12;
Events = P5Events;
ProcType = INTEL_P5;
ProfileSupported = FALSE;
break;
case 6:
NoCESR = 2;
MsrCESR = 0x186;
MsrCount = 0xc1;
Events = P6Events;
ProcType = INTEL_P6;
ProfileSupported = TRUE;
break;
}
}
if (Events) {
while (Events[MaxEvent].Description) {
MaxEvent += 1;
}
}
if (ProfileSupported) {
i = (ULONG) StatProfileInterrupt;
status = HalSetSystemInformation (
HalProfileSourceInterruptHandler,
sizeof (i),
&i
);
if (!NT_SUCCESS(status)) {
// hal did not support hooking the performance interrupt
ProfileSupported = FALSE;
}
}
if (ProfileSupported) {
//
// Allocate ProfileEvents
//
ProfileEvents = ExAllocatePool (NonPagedPool, sizeof (PROFILE_EVENT) * MaxEvent);
if (!ProfileEvents) {
ProfileSupported = FALSE;
} else {
RtlZeroMemory (ProfileEvents, sizeof (PROFILE_EVENT) * MaxEvent);
}
}
if (!StatHookTimer()) {
IoDeleteDevice(DriverObject->DeviceObject);
return STATUS_UNSUCCESSFUL;
}
InitializeListHead (&HookedThunkList);
InitializeListHead (&LazyFreeList);
return(STATUS_SUCCESS);
}
NTSTATUS
StatDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for device control requests.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
ULONG BufferLength;
PULONG Buffer;
//
// Get a pointer to the current parameters for this request. The
// information is contained in the current stack location.
//
irpSp = IoGetCurrentIrpStackLocation(Irp);
//
// Case on the device control subfunction that is being performed by the
// requestor.
//
status = STATUS_SUCCESS;
try {
Buffer = (PULONG) irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
BufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case PSTAT_READ_STATS:
//
// read stats
//
StatReadStats (Buffer);
break;
case PSTAT_SET_CESR:
//
// Set MSRs to collect stats
//
StatSetCESR ((PSETEVENT) Buffer);
break;
case PSTAT_HOOK_THUNK:
//
// Hook an import entry point
//
status = StatHookGenericThunk ((PHOOKTHUNK) Buffer);
break;
case PSTAT_REMOVE_HOOK:
//
// Remove a hook from an entry point
//
StatRemoveGenericHook (Buffer);
break;
case PSTAT_QUERY_EVENTS:
//
// Query possible stats which can be collected
//
status = StatQueryEvents (*Buffer, (PEVENTID) Buffer, BufferLength);
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
//
// Request is done...
//
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(status);
}
NTSTATUS
StatOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PAGED_CODE();
//
// Complete the request and return status.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
NTSTATUS
StatClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PAGED_CODE();
//
// Complete the request and return status.
//
RemoveAllHookedThunks ();
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
#if 0
VOID
StatUnload (
IN PDRIVER_OBJECT DriverObject
)
{
PDEVICE_OBJECT deviceObject;
PAGED_CODE();
RemoveAllHookedThunks ();
KeCancelTimer (&LazyFreeTimer);
LazyFreePool (NULL);
//
// Restore hooked addresses
//
*((PULONG) HalThunkForKeUpdateSystemTime) = KeUpdateSystemTimeThunk;
if (HalThunkForKeUpdateRunTime) {
*((PULONG) HalThunkForKeUpdateRunTime) = KeUpdateRunTimeThunk;
}
//
// Delete the device object.
//
IoDeleteDevice(DriverObject->DeviceObject);
return;
}
#endif
VOID
StatReadStats (PULONG Buffer)
{
PACCUMULATORS Accum;
ULONG i, r1;
pPSTATS Inf;
PKPCR Pcr;
PAGED_CODE();
Buffer[0] = sizeof (PSTATS);
Inf = (pPSTATS)(Buffer + 1);
for (i = 0; i < MAXIMUM_PROCESSORS; i++, Inf++) {
Pcr = KiProcessorControlRegister[i];
if (Pcr == NULL) {
continue;
}
Accum = StatProcessorAccumulators[i];
do {
r1 = Accum->CountStart;
Inf->Counters[0] = Accum->Counters[0];
Inf->Counters[1] = Accum->Counters[1];
Inf->TSC = Accum->TSC;
Inf->SpinLockAcquires = Pcr->KernelReserved[0];
Inf->SpinLockCollisions = Pcr->KernelReserved[1];
Inf->SpinLockSpins = Pcr->KernelReserved[2];
Inf->Irqls = Pcr->KernelReserved[3];
} while (r1 != Accum->CountEnd);
RtlMoveMemory (Inf->ThunkCounters, (CONST VOID *)(Accum->ThunkCounters),
StatMaxThunkCounter * sizeof (ULONG));
}
}
NTSTATUS
StatQueryEvents (
ULONG Index,
PEVENTID Buffer,
ULONG Length
)
{
ULONG i;
if (Index >= MaxEvent) {
return STATUS_NO_MORE_ENTRIES;
}
i = sizeof (EVENTID) +
strlen(Events[Index].Token) + 1 +
strlen(Events[Index].Description) + 1;
if (Length < i) {
return STATUS_BUFFER_TOO_SMALL;
}
memset (Buffer, 0, i);
Buffer->EventId = Events[Index].Encoding;
Buffer->DescriptionOffset = strlen (Events[Index].Token) + 1;
Buffer->SuggestedIntervalBase = Events[Index].SuggestedIntervalBase;
strcpy (Buffer->Buffer, Events[Index].Token);
strcpy (Buffer->Buffer + Buffer->DescriptionOffset, Events[Index].Description);
if (ProfileSupported) {
Buffer->ProfileSource = PROFILE_SOURCE_BASE + Index;
}
return STATUS_SUCCESS;
}
ULONG
StatGetP5CESR (
PSETEVENT NewEvent
)
{
ULONG NewCESR;
if (!NewEvent->Active) {
return 0;
}
NewCESR = NewEvent->EventId & 0x3f;
NewCESR |= NewEvent->UserMode ? 0x80 : 0;
NewCESR |= NewEvent->KernelMode ? 0x40 : 0;
return NewCESR;
}
ULONG
StatGetP6CESR (
PSETEVENT NewEvent,
BOOLEAN Profile
)
{
ULONG NewCESR;
NewCESR = NewEvent->EventId & 0xffff;
NewCESR |= NewEvent->Active ? (1 << 22) : 0;
NewCESR |= NewEvent->UserMode ? (1 << 16) : 0;
NewCESR |= NewEvent->KernelMode ? (1 << 17) : 0;
NewCESR |= NewEvent->EdgeDetect ? (1 << 18) : 0;
NewCESR |= Profile ? (1 << 20) : 0;
return NewCESR;
}
VOID
StatSetCESR (
PSETEVENT NewEvent
)
{
ULONG i, j, NoProc;
ULONG NewCESR[MAX_EVENTS];
PAGED_CODE();
switch (ProcType) {
case INTEL_P5:
NewCESR[0] = StatGetP5CESR(NewEvent+0);
NewCESR[0] |= StatGetP5CESR(NewEvent+1) << 16;
break;
case INTEL_P6:
NewCESR[0] = StatGetP6CESR(NewEvent+0, FALSE);
NewCESR[1] = StatGetP6CESR(NewEvent+1, FALSE);
break;
}
//
// Check if CESR changed
//
for (i=0; i < NoCESR; i++) {
if (NewCESR[i] != CESR[i]) {
break;
}
}
if (i == NoCESR) {
// no change, all done
return;
}
//
// Set new CESR values
//
for (i=0; i < NoCESR; i++) {
CESR[i] = NewCESR[i];
}
//
// Clear each processors Pcr pointer so they will reset.
// Also count how many processors there are.
//
for (i = 0, NoProc = 0; i < MAXIMUM_PROCESSORS; i++) {
if (KiProcessorControlRegister[i]) {
KiProcessorControlRegister[i] = NULL;
NoProc++;
}
}
//
// wait for each processor to get the new Pcr value
//
do {
//Sleep (0); // yield
j = 0;
for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
if (KiProcessorControlRegister[i]) {
j++;
}
}
} while (j < NoProc);
}
VOID
FASTCALL
StatTimerHook (
IN ULONG processor
)
{
PACCUMULATORS Total;
ULONG i;
if (KiProcessorControlRegister[processor] == NULL) {
for (i=0; i < NoCESR; i++) {
WRMSR (MsrCESR+i, 0); // clear old CESR
}
for (i=0; i < NoCESR; i++) {
WRMSR (MsrCESR+i, CESR[i]); // write new CESR
}
KiProcessorControlRegister[processor] = CurrentPcr();
}
Total = StatProcessorAccumulators[ processor ];
Total->CountStart += 1;
for (i=0; i < NoCount; i++) {
Total->Counters[i] = RDMSR(MsrCount+i);
}
Total->TSC = RDMSR(MsrTSC);
Total->CountEnd += 1;
}
VOID
FASTCALL
TimerHook (
IN ULONG processor
)
{
// for compatibility
//
if (KiProcessorControlRegister[processor] == NULL) {
KiProcessorControlRegister[processor] = CurrentPcr();
}
}
BOOLEAN
StatHookTimer (VOID)
{
PULONG Address;
ULONG hal;
ULONG HalThunkForKeUpdateSystemTime;
ULONG HalThunkForKeUpdateRunTime;
ULONG HalThunkForStartProfileInterrupt;
ULONG HalThunkForStopProfileInterrupt;
for (hal=0; HalName[hal]; hal++) {
HalThunkForKeUpdateSystemTime =
ImportThunkAddress (
HalName[hal],
0,
"ntoskrnl.exe",
"KeUpdateSystemTime"
);
if (HalThunkForKeUpdateSystemTime) {
break;
}
}
if (!HalThunkForKeUpdateSystemTime) {
//
// Imports were not found
//
return FALSE;
}
HalThunkForKeUpdateRunTime =
ImportThunkAddress (
HalName[hal],
0,
"ntoskrnl.exe",
"KeUpdateRunTime"
);
HalThunkForStartProfileInterrupt =
ImportThunkAddress (
"ntoskrnl.exe",
0,
"hal.dll",
"HalStartProfileInterrupt"
);
HalThunkForStopProfileInterrupt =
ImportThunkAddress (
"ntoskrnl.exe",
0,
"hal.dll",
"HalStopProfileInterrupt"
);
//
// Patch in timer hooks, Read current values
//
KeUpdateSystemTimeThunk = *((PULONG) HalThunkForKeUpdateSystemTime);
if (HalThunkForKeUpdateRunTime) {
KeUpdateRunTimeThunk = *((PULONG) HalThunkForKeUpdateRunTime);
}
HaldStartProfileInterrupt = (pHalProfileInterrupt) *((PULONG) HalThunkForStartProfileInterrupt);
HaldStopProfileInterrupt = (pHalProfileInterrupt) *((PULONG) HalThunkForStopProfileInterrupt);
HaldQuerySystemInformation = HalQuerySystemInformation;
HaldSetSystemInformation = HalSetSystemInformation;
//
// Set Stat hook functions
//
switch (ProcType) {
case INTEL_P6:
case INTEL_P5:
Address = (PULONG) HalThunkForKeUpdateSystemTime;
*Address = (ULONG) StatSystemTimeHook;
if (HalThunkForKeUpdateRunTime) {
Address = (PULONG) HalThunkForKeUpdateRunTime;
*Address = (ULONG)StatRunTimeHook;
}
if (ProfileSupported) {
Address = (PULONG) HalThunkForStartProfileInterrupt;
*Address = (ULONG) StatStartProfileInterrupt;
Address = (PULONG) HalThunkForStopProfileInterrupt;
*Address = (ULONG) StatStopProfileInterrupt;
HalQuerySystemInformation = StatQuerySystemInformation;
HalSetSystemInformation = StatSetSystemInformation;
}
break;
default:
Address = (PULONG) HalThunkForKeUpdateSystemTime;
*Address = (ULONG)SystemTimeHook;
if (HalThunkForKeUpdateRunTime) {
Address = (PULONG) HalThunkForKeUpdateRunTime;
*Address = (ULONG)RunTimeHook;
}
break;
}
return TRUE;
}
PPROFILE_EVENT
StatProfileEvent(
KPROFILE_SOURCE Source
)
{
ULONG Index;
Index = (ULONG) Source;
if (Index < PROFILE_SOURCE_BASE) {
return NULL;
}
Index -= PROFILE_SOURCE_BASE;
if (Index > MaxEvent) {
return NULL;
}
return ProfileEvents + Index;
}
VOID
StatStartProfileInterrupt (
KPROFILE_SOURCE Source
)
{
ULONG i;
PPROFILE_EVENT ProfileEvent;
//
// If this isn't a profile source we're supporting, pass it on
//
ProfileEvent = StatProfileEvent(Source);
if (!ProfileEvent) {
HaldStartProfileInterrupt (Source);
return;
}
if (CurrentPcr()->Number == 0) {
if (!ProfileEvent->Source) {
return ;
}
CurrentProfileEvent = ProfileEvent;
}
//
// Set the CESR
//
WRMSR (MsrCESR, ProfileEvent->CESR);
//
// Prime the interval counter
//
WRMSR (MsrCount, ProfileEvent->InitialCount);
}
VOID
StatStopProfileInterrupt (
KPROFILE_SOURCE Source
)
{
ULONG i;
PPROFILE_EVENT ProfileEvent;
//
// If this isn't a profile source we're supporting, pass it on
//
ProfileEvent = StatProfileEvent(Source);
if (!ProfileEvent) {
HaldStopProfileInterrupt (Source);
return ;
}
if (CurrentPcr()->Number == 0) {
if (ProfileEvent == CurrentProfileEvent) {
//
// Stop calling the kernel
//
CurrentProfileEvent = NULL;
}
}
}
NTSTATUS
FASTCALL
StatProfileInterrupt (
IN PKTRAP_FRAME TrapFrame
)
{
ULONG i;
ULONG current;
PPROFILE_EVENT ProfileEvent;
ProfileEvent = CurrentProfileEvent;
if (ProfileEvent) {
current = (ULONG) RDMSR(MsrCount);
//
// Did this event fire?
//
if (current < ProfileEvent->InitialCount) {
//
// Notify kernel
//
_asm {
push ebp ; Save these as KeProfileInterrupt nukes them
push ebx
push esi
push edi
}
KeProfileInterruptWithSource (TrapFrame, ProfileEvent->Source);
_asm {
pop edi
pop esi
pop ebx
pop ebp
}
//
// Reset trigger counter
//
WRMSR (MsrCount, ProfileEvent->InitialCount);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
StatQuerySystemInformation (
IN HAL_QUERY_INFORMATION_CLASS InformationClass,
IN ULONG BufferSize,
OUT PVOID Buffer,
OUT PULONG ReturnedLength
)
{
PHAL_PROFILE_SOURCE_INFORMATION ProfileSource;
ULONG i;
PPROFILE_EVENT ProfileEvent;
if (InformationClass == HalProfileSourceInformation) {
ProfileSource = (PHAL_PROFILE_SOURCE_INFORMATION) Buffer;
*ReturnedLength = sizeof (HAL_PROFILE_SOURCE_INFORMATION);
if (BufferSize < sizeof (HAL_PROFILE_SOURCE_INFORMATION)) {
return STATUS_BUFFER_TOO_SMALL;
}
ProfileEvent = StatProfileEvent(ProfileSource->Source);
if (ProfileEvent) {
ProfileSource->Interval = 0 - (ULONG) ProfileEvent->InitialCount;
ProfileSource->Supported = TRUE;
return STATUS_SUCCESS;
}
}
//
// Not our QuerySystemInformation request, pass it on
//
return HaldQuerySystemInformation (InformationClass, BufferSize, Buffer, ReturnedLength);
}
NTSTATUS
StatSetSystemInformation(
IN HAL_SET_INFORMATION_CLASS InformationClass,
IN ULONG BufferSize,
IN PVOID Buffer
)
{
PHAL_PROFILE_SOURCE_INTERVAL ProfileInterval;
SETEVENT SetEvent;
PPROFILE_EVENT ProfileEvent;
if (InformationClass == HalProfileSourceInterval) {
ProfileInterval = (PHAL_PROFILE_SOURCE_INTERVAL) Buffer;
if (BufferSize < sizeof (HAL_PROFILE_SOURCE_INTERVAL)) {
return STATUS_BUFFER_TOO_SMALL;
}
ProfileEvent = StatProfileEvent(ProfileInterval->Source);
if (ProfileEvent) {
ProfileEvent->Source = ProfileInterval->Source;
ProfileEvent->InitialCount = 0;
ProfileEvent->InitialCount -= (ULONGLONG) ProfileInterval->Interval;
SetEvent.EventId = Events[ProfileEvent->Source - PROFILE_SOURCE_BASE].Encoding;
SetEvent.Active = TRUE;
SetEvent.UserMode = TRUE;
SetEvent.KernelMode = TRUE;
switch (ProcType) {
case INTEL_P6:
ProfileEvent->CESR = StatGetP6CESR (&SetEvent, TRUE);
break;
}
return STATUS_SUCCESS;
}
}
//
// Not our SetSystemInforamtion request, pass it on
//
return HaldSetSystemInformation (InformationClass, BufferSize, Buffer);
}
NTSTATUS
StatHookGenericThunk (
IN PHOOKTHUNK ThunkToHook
)
{
ULONG HookAddress;
ULONG i, TracerId;
UCHAR sourcename[50];
ULONG HitCounterOffset;
PLIST_ENTRY Link;
PHOOKEDTHUNK HookRecord;
UCHAR IdInUse[MAX_THUNK_COUNTERS];
PAGED_CODE();
i = strlen (ThunkToHook->SourceModule);
if (i >= 50) {
return STATUS_UNSUCCESSFUL;
}
strcpy (sourcename, ThunkToHook->SourceModule);
HookAddress = ImportThunkAddress (
sourcename,
ThunkToHook->ImageBase,
ThunkToHook->TargetModule,
ThunkToHook->Function
);
if (!HookAddress) {
return STATUS_UNSUCCESSFUL;
}
//
// Hook this thunk
//
//
// If counting bucket is not known (also tracerid), then allocate one
//
TracerId = ThunkToHook->TracerId;
ExAcquireFastMutex (&HookLock);
if (TracerId == 0) {
RtlZeroMemory (IdInUse, MAX_THUNK_COUNTERS);
for (Link = HookedThunkList.Flink;
Link != &HookedThunkList;
Link = Link->Flink) {
HookRecord = CONTAINING_RECORD (Link, HOOKEDTHUNK, HookList);
IdInUse[HookRecord->TracerId-1] = 1;
}
while (IdInUse[TracerId]) {
if (++TracerId >= MAX_THUNK_COUNTERS) {
goto Abort;
}
}
TracerId += 1;
}
if (TracerId >= MAX_THUNK_COUNTERS) {
goto Abort;
}
if (TracerId > StatMaxThunkCounter) {
StatMaxThunkCounter = TracerId;
}
HookRecord = ExAllocatePool (NonPagedPool, sizeof (HOOKEDTHUNK));
if (!HookRecord) {
goto Abort;
}
HitCounterOffset =
((ULONG) &StatGlobalAccumulators[0].ThunkCounters[TracerId-1]
- (ULONG) StatGlobalAccumulators);
HookRecord->HookAddress = HookAddress;
HookRecord->OriginalDispatch = *((PULONG) HookAddress);
HookRecord->TracerId = TracerId;
InsertHeadList (&HookedThunkList, &HookRecord->HookList);
CreateHook (HookRecord->HookCode, (PVOID)HookAddress, HitCounterOffset, 0);
SetMaxThunkCounter ();
ExReleaseFastMutex (&HookLock);
ThunkToHook->TracerId = TracerId;
return STATUS_SUCCESS;
Abort:
ExReleaseFastMutex (&HookLock);
return STATUS_UNSUCCESSFUL;
}
VOID
StatRemoveGenericHook (
IN PULONG pTracerId
)
{
PLIST_ENTRY Link, NextLink, Temp, NextTemp;
PHOOKEDTHUNK HookRecord, AltRecord;
ULONG HitCounterOffset;
LIST_ENTRY DisabledHooks;
ULONG TracerId;
PULONG HookAddress;
PAGED_CODE();
//
// Run list of hooks undo-ing any hook which matches this tracerid.
// Note: that hooks are undone in the reverse order for which they
// were applied.
//
TracerId = *pTracerId;
InitializeListHead (&DisabledHooks);
ExAcquireFastMutex (&HookLock);
Link = HookedThunkList.Flink;
while (Link != &HookedThunkList) {
NextLink = Link->Flink;
HookRecord = CONTAINING_RECORD (Link, HOOKEDTHUNK, HookList);
if (HookRecord->TracerId == TracerId) {
//
// Found a hook with a matching ID
// Scan for any hooks which need to be temporaly removed
// in order to get this hook removed
//
HookAddress = (PULONG) HookRecord->HookAddress;
Temp = HookedThunkList.Flink;
while (Temp != Link) {
NextTemp = Temp->Flink;
AltRecord = CONTAINING_RECORD (Temp, HOOKEDTHUNK, HookList);
if (AltRecord->HookAddress == HookRecord->HookAddress) {
RemoveEntryList(&AltRecord->HookList);
*HookAddress = AltRecord->OriginalDispatch;
InsertTailList (&DisabledHooks, &AltRecord->HookList);
}
Temp = NextTemp;
}
//
// Remove this hook
//
RemoveEntryList(&HookRecord->HookList);
HookAddress = (PULONG) HookRecord->HookAddress;
*HookAddress = HookRecord->OriginalDispatch;
InsertTailList (&LazyFreeList, &HookRecord->HookList);
}
Link = NextLink;
}
//
// Re-hook any hooks which were disabled for the remove operation
//
while (DisabledHooks.Flink != &DisabledHooks) {
HookRecord = CONTAINING_RECORD (DisabledHooks.Flink, HOOKEDTHUNK, HookList);
AltRecord = ExAllocatePool (NonPagedPool, sizeof (HOOKEDTHUNK));
if (!AltRecord) {
goto OutOfMemory;
}
RemoveEntryList(&HookRecord->HookList);
HookAddress = (PULONG) HookRecord->HookAddress;
AltRecord->HookAddress = HookRecord->HookAddress;
AltRecord->OriginalDispatch = *HookAddress;
AltRecord->TracerId = HookRecord->TracerId;
InsertHeadList (&HookedThunkList, &AltRecord->HookList);
HitCounterOffset =
(ULONG) &StatGlobalAccumulators[0].ThunkCounters[AltRecord->TracerId-1]
- (ULONG) StatGlobalAccumulators;
CreateHook (AltRecord->HookCode, (PVOID)HookAddress, HitCounterOffset, 0);
InsertTailList (&LazyFreeList, &HookRecord->HookList);
}
SetMaxThunkCounter();
ExReleaseFastMutex (&HookLock);
return ;
OutOfMemory:
while (DisabledHooks.Flink != &DisabledHooks) {
HookRecord = CONTAINING_RECORD (DisabledHooks.Flink, HOOKEDTHUNK, HookList);
RemoveEntryList(&HookRecord->HookList);
InsertTailList (&LazyFreeList, &HookRecord->HookList);
}
ExReleaseFastMutex (&HookLock);
RemoveAllHookedThunks ();
return ;
}
VOID RemoveAllHookedThunks ()
{
PHOOKEDTHUNK HookRecord;
PULONG HookAddress;
PAGED_CODE();
ExAcquireFastMutex (&HookLock);
while (!IsListEmpty(&HookedThunkList)) {
HookRecord = CONTAINING_RECORD (HookedThunkList.Flink, HOOKEDTHUNK, HookList);
RemoveEntryList(&HookRecord->HookList);
HookAddress = (PULONG) HookRecord->HookAddress;
*HookAddress = HookRecord->OriginalDispatch;
InsertTailList (&LazyFreeList, &HookRecord->HookList);
}
SetMaxThunkCounter();
ExReleaseFastMutex (&HookLock);
}
VOID SetMaxThunkCounter ()
{
LARGE_INTEGER duetime;
PLIST_ENTRY Link;
PHOOKEDTHUNK HookRecord;
ULONG Max;
PAGED_CODE();
Max = 0;
for (Link = HookedThunkList.Flink;
Link != &HookedThunkList;
Link = Link->Flink) {
HookRecord = CONTAINING_RECORD (Link, HOOKEDTHUNK, HookList);
if (HookRecord->TracerId > Max) {
Max = HookRecord->TracerId;
}
}
StatMaxThunkCounter = Max;
LazyFreeCountdown = 2;
duetime.QuadPart = -10000000;
KeSetTimer (&LazyFreeTimer, duetime, &LazyFreeDpc);
}
VOID LazyFreePoolDPC (PKDPC dpc, PVOID a, PVOID b, PVOID c)
{
ExQueueWorkItem (&LazyFreePoolWorkItem, DelayedWorkQueue);
}
VOID LazyFreePool (PVOID conext)
{
PHOOKEDTHUNK HookRecord;
LARGE_INTEGER duetime;
PAGED_CODE();
ExAcquireFastMutex (&HookLock);
if (--LazyFreeCountdown == 0) {
while (!IsListEmpty(&LazyFreeList)) {
HookRecord = CONTAINING_RECORD (LazyFreeList.Flink, HOOKEDTHUNK, HookList);
RemoveEntryList(&HookRecord->HookList);
RtlFillMemory(HookRecord, sizeof(HOOKEDTHUNK), 0xCC);
ExFreePool (HookRecord) ;
}
} else {
duetime.QuadPart = -10000000;
KeSetTimer (&LazyFreeTimer, duetime, &LazyFreeDpc);
}
ExReleaseFastMutex (&HookLock);
}
#define IMPKERNELADDRESS(a) ((ULONG)a + ImageBase)
#define IMPIMAGEADDRESS(a) ConvertImportAddress((ULONG)a, (ULONG)Pool, &SectionHeader)
#define INITIAL_POOLSIZE 0x7000
ULONG
ImportThunkAddress (
IN PUCHAR SourceModule,
IN ULONG ImageBase,
IN PUCHAR ImportModule,
IN PUCHAR ThunkName
)
{
NTSTATUS status;
ULONG i, j;
ULONG Dir;
PVOID Pool;
ULONG PoolSize;
HANDLE filehandle;
IMAGE_DOS_HEADER DosImageHeader;
IMAGE_NT_HEADERS NtImageHeader;
PIMAGE_NT_HEADERS LoadedNtHeader;
PIMAGE_SECTION_HEADER pSectionHeader;
IMAGE_SECTION_HEADER SectionHeader;
PIMAGE_IMPORT_DESCRIPTOR ImpDescriptor;
PULONG pThunkAddr, pThunkData;
ULONG ImportAddress;
PIMAGE_IMPORT_BY_NAME pImportNameData;
PAGED_CODE();
status = openfile (&filehandle, "\\SystemRoot\\", SourceModule);
if (!NT_SUCCESS(status)) {
status = openfile (&filehandle, "\\SystemRoot\\System32\\", SourceModule);
}
if (!NT_SUCCESS(status)) {
status = openfile (&filehandle, "\\SystemRoot\\System32\\Drivers\\", SourceModule);
}
if (!NT_SUCCESS(status)) {
return 0;
}
Pool = NULL;
ImportAddress = 0;
try {
//
// Find module in loaded module list
//
PoolSize = INITIAL_POOLSIZE;
Pool = ExAllocatePool (PagedPool, PoolSize);
try {
//
// Read in source image's headers
//
readfile (
filehandle,
0,
sizeof (DosImageHeader),
(PVOID) &DosImageHeader
);
if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) {
return 0;
}
readfile (
filehandle,
DosImageHeader.e_lfanew,
sizeof (NtImageHeader),
(PVOID) &NtImageHeader
);
if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) {
return 0;
}
if (!ImageBase) {
ImageBase = LookupImageBase (SourceModule);
if (!ImageBase) {
ImageBase = NtImageHeader.OptionalHeader.ImageBase;
}
}
//
// Check in read in copy header against loaded image
//
LoadedNtHeader = (PIMAGE_NT_HEADERS) ((ULONG) ImageBase +
DosImageHeader.e_lfanew);
if (LoadedNtHeader->Signature != IMAGE_NT_SIGNATURE ||
LoadedNtHeader->FileHeader.TimeDateStamp !=
NtImageHeader.FileHeader.TimeDateStamp) {
return 0;
}
//
// read in complete sections headers from image
//
i = NtImageHeader.FileHeader.NumberOfSections
* sizeof (IMAGE_SECTION_HEADER);
j = ((ULONG) IMAGE_FIRST_SECTION (&NtImageHeader)) -
((ULONG) &NtImageHeader) +
DosImageHeader.e_lfanew;
if (i > PoolSize) {
ExFreePool (Pool);
PoolSize = i;
Pool = ExAllocatePool (PagedPool, PoolSize);
}
readfile (
filehandle,
j, // file offset
i, // length
Pool
);
//
// Find section with import directory
//
Dir = NtImageHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
i = 0;
pSectionHeader = Pool;
for (; ;) {
if (i >= NtImageHeader.FileHeader.NumberOfSections) {
return 0;
}
if (pSectionHeader->VirtualAddress <= Dir &&
pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData > Dir) {
break;
}
i += 1;
pSectionHeader += 1;
}
//
// read in complete import section from image
//
Dir -= pSectionHeader->VirtualAddress;
pSectionHeader->VirtualAddress += Dir;
pSectionHeader->PointerToRawData += Dir;
pSectionHeader->SizeOfRawData -= Dir;
SectionHeader = *pSectionHeader;
if (SectionHeader.SizeOfRawData > PoolSize) {
ExFreePool (Pool);
PoolSize = SectionHeader.SizeOfRawData;
Pool = ExAllocatePool (PagedPool, PoolSize);
}
readfile (
filehandle,
SectionHeader.PointerToRawData,
SectionHeader.SizeOfRawData,
Pool
);
//
// Find imports from specified module
//
ImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) Pool;
while (ImpDescriptor->Characteristics) {
if (_stricmp((PUCHAR)IMPIMAGEADDRESS((ULONG)(ImpDescriptor->Name)), ImportModule) == 0) {
break;
}
ImpDescriptor += 1;
}
//
// Find thunk for imported ThunkName
//
pThunkData = (PULONG) IMPIMAGEADDRESS (ImpDescriptor->OriginalFirstThunk);
pThunkAddr = (PULONG) IMPKERNELADDRESS (ImpDescriptor->FirstThunk);
for (; ;) {
if (*pThunkData == 0L) {
// end of table
break;
}
pImportNameData = (PIMAGE_IMPORT_BY_NAME) IMPIMAGEADDRESS (*pThunkData);
if (_stricmp(pImportNameData->Name, ThunkName) == 0) {
ImportAddress = (ULONG)pThunkAddr;
break;
}
// check next thunk
pThunkData += 1;
pThunkAddr += 1;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
return 0;
}
} finally {
//
// Clean up
//
NtClose (filehandle);
if (Pool) {
ExFreePool (Pool);
}
}
if (!ImportAddress) {
// not found
return 0;
}
//
// Convert ImportAddress from pool address to image address
//
// ImportAddress += ImageBase + SectionHeader.VirtualAddress - (ULONG) Pool;
return ImportAddress;
}
ULONG
LookupImageBase (
PUCHAR SourceModule
)
{
NTSTATUS status;
ULONG BufferSize;
ULONG junk, ModuleNumber, ImageBase;
PRTL_PROCESS_MODULES Modules;
PRTL_PROCESS_MODULE_INFORMATION Module;
ImageBase = 0;
BufferSize = 64000;
Modules = ExAllocatePool (PagedPool, BufferSize);
if (!Modules) {
return 0;
}
//
// Locate system drivers.
//
status = ZwQuerySystemInformation (
SystemModuleInformation,
Modules,
BufferSize,
&junk
);
if (NT_SUCCESS(status)) {
Module = &Modules->Modules[ 0 ];
for (ModuleNumber=0; ModuleNumber < Modules->NumberOfModules; ModuleNumber++,Module++) {
if (_stricmp (Module->FullPathName + Module->OffsetToFileName, SourceModule) == 0) {
ImageBase = (ULONG) Module->ImageBase;
break;
}
}
}
ExFreePool (Modules);
return ImageBase;
}
NTSTATUS
openfile (
IN PHANDLE filehandle,
IN PUCHAR BasePath,
IN PUCHAR Name
)
{
ANSI_STRING AscBasePath, AscName;
UNICODE_STRING UniPathName, UniName;
NTSTATUS status;
OBJECT_ATTRIBUTES ObjA;
IO_STATUS_BLOCK IOSB;
UCHAR StringBuf[500];
//
// Build name
//
UniPathName.Buffer = (PWCHAR)StringBuf;
UniPathName.Length = 0;
UniPathName.MaximumLength = sizeof( StringBuf );
RtlInitString(&AscBasePath, BasePath);
RtlAnsiStringToUnicodeString( &UniPathName, &AscBasePath, FALSE );
RtlInitString(&AscName, Name);
RtlAnsiStringToUnicodeString( &UniName, &AscName, TRUE );
RtlAppendUnicodeStringToString (&UniPathName, &UniName);
InitializeObjectAttributes(
&ObjA,
&UniPathName,
OBJ_CASE_INSENSITIVE,
0,
0 );
//
// open file
//
status = ZwOpenFile (
filehandle, // return handle
SYNCHRONIZE | FILE_READ_DATA, // desired access
&ObjA, // Object
&IOSB, // io status block
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
FILE_SYNCHRONOUS_IO_ALERT // open options
);
RtlFreeUnicodeString (&UniName);
return status;
}
VOID
readfile (
HANDLE handle,
ULONG offset,
ULONG len,
PVOID buffer
)
{
NTSTATUS status;
IO_STATUS_BLOCK iosb;
LARGE_INTEGER foffset;
foffset = RtlConvertUlongToLargeInteger(offset);
status = ZwReadFile (
handle,
NULL, // event
NULL, // apc routine
NULL, // apc context
&iosb,
buffer,
len,
&foffset,
NULL
);
if (!NT_SUCCESS(status)) {
ExRaiseStatus (1);
}
}
ULONG
ConvertImportAddress (
IN ULONG ImageRelativeAddress,
IN ULONG PoolAddress,
IN PIMAGE_SECTION_HEADER SectionHeader
)
{
ULONG EffectiveAddress;
EffectiveAddress = PoolAddress + ImageRelativeAddress -
SectionHeader->VirtualAddress;
if (EffectiveAddress < PoolAddress ||
EffectiveAddress > PoolAddress + SectionHeader->SizeOfRawData) {
ExRaiseStatus (1);
}
return EffectiveAddress;
}