2337 lines
54 KiB
C
2337 lines
54 KiB
C
/*++
|
||
|
||
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_
|
||
#include "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];
|
||
ULONG EventID[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 DisableRDPMC;
|
||
BOOLEAN ProfileSupported;
|
||
PPROFILE_EVENT ProfileEvents, CurrentProfileEvent;
|
||
|
||
ULONGLONG FASTCALL RDMSR(ULONG);
|
||
VOID WRMSR(ULONG, ULONGLONG);
|
||
VOID StatSystemTimeHook(VOID);
|
||
VOID StatRunTimeHook(VOID);
|
||
VOID SystemTimeHook(VOID);
|
||
VOID RunTimeHook(VOID);
|
||
PKPCR CurrentPcr(VOID);
|
||
ULONG GetCR4(VOID);
|
||
VOID SetCR4(ULONG);
|
||
|
||
|
||
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
|
||
StatQueryEventsInfo (
|
||
PEVENTS_INFO 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);
|
||
|
||
|
||
VOID
|
||
StatEnableRDPMC(
|
||
);
|
||
|
||
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_PTR ImageBase,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName,
|
||
IN PVOID ModuleList
|
||
);
|
||
|
||
ULONG
|
||
ImportThunkAddressModule (
|
||
IN PRTL_PROCESS_MODULE_INFORMATION SourceModule,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName
|
||
);
|
||
|
||
ULONG
|
||
ImportThunkAddressProcessFile(
|
||
IN ULONG_PTR ImageBase,
|
||
IN HANDLE FileHandle,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName
|
||
);
|
||
|
||
ULONG_PTR
|
||
LookupImageBase (
|
||
IN PUCHAR SourceModule,
|
||
IN PVOID ModuleList
|
||
);
|
||
|
||
ULONG
|
||
ConvertImportAddress (
|
||
IN ULONG ImageRelativeAddress,
|
||
IN ULONG PoolAddress,
|
||
IN PIMAGE_SECTION_HEADER SectionHeader
|
||
);
|
||
|
||
#if 0
|
||
PRTL_PROCESS_MODULE_INFORMATION
|
||
GetModuleInformationFuzzy(
|
||
IN PUCHAR StartsWith,
|
||
IN PUCHAR EndsWith,
|
||
IN PRTL_PROCESS_MODULES Modules
|
||
);
|
||
#endif
|
||
|
||
PVOID
|
||
GetLoadedModuleList(
|
||
VOID
|
||
);
|
||
|
||
#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,ImportThunkAddressModule)
|
||
#pragma alloc_text(PAGE,ImportThunkAddressProcessFile)
|
||
#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)
|
||
#pragma alloc_text(PAGE,StatEnableRDPMC)
|
||
#pragma alloc_text(PAGE,GetLoadedModuleList)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
StatEnableRDPMC()
|
||
{
|
||
ULONG Cr4;
|
||
PKPRCB Prcb;
|
||
|
||
Prcb = CurrentPcr()->Prcb;
|
||
if (strcmp(Prcb->VendorString, "GenuineIntel") == 0) {
|
||
|
||
//
|
||
// Profiling only supported on family 6 or above.
|
||
//
|
||
|
||
if (Prcb->CpuType < 6) {
|
||
DisableRDPMC = TRUE;
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Check for busted parts. Anything below stepping 6,1,9
|
||
// is subject to errata 26 which says RDPMC cannot be used
|
||
// with SMM. As we have know way of knowing if SMM is in
|
||
// use (but it probably is), we must disable on those chips.
|
||
//
|
||
|
||
if ((Prcb->CpuType == 6) &&
|
||
(Prcb->CpuStep < 0x0109)) {
|
||
DisableRDPMC = TRUE;
|
||
return;
|
||
}
|
||
|
||
//
|
||
// This processor is believed to be able to handle RDPMC
|
||
// from user mode. Enable it by setting CR4.PCE (bit 8).
|
||
//
|
||
|
||
Cr4 = GetCR4();
|
||
|
||
Cr4 |= 0x100;
|
||
|
||
SetCR4(Cr4);
|
||
}
|
||
}
|
||
|
||
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;
|
||
DisableRDPMC = TRUE;
|
||
break;
|
||
|
||
case 6:
|
||
NoCESR = 2;
|
||
MsrCESR = 0x186;
|
||
MsrCount = 0xc1;
|
||
Events = P6Events;
|
||
ProcType = INTEL_P6;
|
||
ProfileSupported = TRUE;
|
||
DisableRDPMC = FALSE;
|
||
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;
|
||
|
||
case PSTAT_QUERY_EVENTS_INFO:
|
||
//
|
||
// Query events info
|
||
//
|
||
status = StatQueryEventsInfo( (PEVENTS_INFO) 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->TSC = Accum->TSC;
|
||
|
||
for (i=0; i < MAX_EVENTS; i++) {
|
||
Inf->Counters[i] = Accum->Counters[i];
|
||
Inf->EventId[i] = EventID[i];
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
StatQueryEventsInfo (
|
||
PEVENTS_INFO Buffer,
|
||
ULONG Length
|
||
)
|
||
{
|
||
size_t maxLenToken, maxLenOfficialToken;
|
||
size_t maxLenDescription, maxLenOfficialDescription;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( Length < sizeof(*Buffer) ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
maxLenToken = maxLenOfficialToken = 0;
|
||
maxLenDescription = maxLenOfficialDescription = 0;
|
||
if ( MaxEvent ) {
|
||
ULONG i;
|
||
size_t len;
|
||
for ( i = 0; i < MaxEvent; i++ ) {
|
||
len = ( Events[i].Token ) ? strlen( Events[i].Token ) : 0;
|
||
if ( len > maxLenToken ) {
|
||
maxLenToken = len;
|
||
}
|
||
len = ( Events[i].OfficialToken ) ? strlen( Events[i].OfficialToken ) : 0;
|
||
if ( len > maxLenToken ) {
|
||
maxLenOfficialToken = len;
|
||
}
|
||
len = ( Events[i].Description ) ? strlen( Events[i].Description ) : 0;
|
||
if ( len > maxLenDescription ) {
|
||
maxLenDescription = len;
|
||
}
|
||
len = ( Events[i].OfficialDescription ) ? strlen( Events[i].OfficialDescription ) : 0;
|
||
if ( len > maxLenOfficialDescription ) {
|
||
maxLenOfficialDescription = len;
|
||
}
|
||
}
|
||
}
|
||
|
||
Buffer->Events = MaxEvent;
|
||
Buffer->TokenMaxLength = maxLenToken;
|
||
Buffer->DescriptionMaxLength = maxLenDescription;
|
||
Buffer->OfficialTokenMaxLength = maxLenOfficialToken;
|
||
Buffer->OfficialDescriptionMaxLength = maxLenOfficialDescription;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // StatQueryEventsInfo()
|
||
|
||
|
||
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 events
|
||
//
|
||
|
||
for (i=0; i < MAX_EVENTS; i++) {
|
||
EventID[i] = NewEvent[i].EventId;
|
||
}
|
||
|
||
//
|
||
// 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();
|
||
|
||
//
|
||
// Enable RDPMC from Rings 1, 2 and 3.
|
||
//
|
||
|
||
StatEnableRDPMC();
|
||
}
|
||
|
||
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();
|
||
}
|
||
}
|
||
|
||
PVOID
|
||
GetLoadedModuleList(
|
||
VOID
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG BufferSize;
|
||
ULONG NeededSize;
|
||
ULONG ModuleNumber;
|
||
PRTL_PROCESS_MODULES Modules;
|
||
PRTL_PROCESS_MODULE_INFORMATION Module;
|
||
|
||
//
|
||
// 64K should be plenty,... if it isn't we'll come around again.
|
||
//
|
||
|
||
BufferSize = 64000;
|
||
|
||
while (TRUE) {
|
||
Modules = ExAllocatePool (PagedPool, BufferSize);
|
||
if (!Modules) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Get system loaded module list.
|
||
//
|
||
|
||
Status = ZwQuerySystemInformation (
|
||
SystemModuleInformation,
|
||
Modules,
|
||
BufferSize,
|
||
&NeededSize
|
||
);
|
||
|
||
if (NeededSize > BufferSize) {
|
||
|
||
//
|
||
// Buffer not big enough, try again.
|
||
//
|
||
|
||
ExFreePool(Modules);
|
||
BufferSize = NeededSize;
|
||
continue;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Not good, give up.
|
||
//
|
||
|
||
ExFreePool(Modules);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// All is good.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
return Modules;
|
||
}
|
||
|
||
|
||
#if 0
|
||
PRTL_PROCESS_MODULE_INFORMATION
|
||
GetModuleInformationFuzzy(
|
||
IN PUCHAR StartsWith,
|
||
IN PUCHAR EndsWith,
|
||
IN PRTL_PROCESS_MODULES Modules
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Run Down the loaded module list looking for a module
|
||
whos name begins with StartWith and ends with EndsWith.
|
||
(What's in the middle doesn't matter). This is useful
|
||
for finding the kernel and the hal which are of the
|
||
form
|
||
nt*.exe for the kernel
|
||
hal*.dll for the hal.
|
||
|
||
Arguments:
|
||
|
||
StartsWith Beginning string match.
|
||
EndsWith Ending string match.
|
||
ModuleList List of loaded modules.
|
||
|
||
Returns:
|
||
|
||
Pointer to the loaded module information for the matching
|
||
module or null if no match is found.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG StartLength = 0;
|
||
ULONG EndLength = 0;
|
||
ULONG ModulesRemaining;
|
||
PRTL_PROCESS_MODULE_INFORMATION Module;
|
||
PUCHAR FileName;
|
||
ULONG FileNameLength;
|
||
|
||
if (StartsWith) {
|
||
StartLength = strlen(StartsWith);
|
||
}
|
||
|
||
if (EndsWith) {
|
||
EndLength = strlen(EndsWith);
|
||
}
|
||
|
||
if ((!StartsWith) && (!EndsWith)) {
|
||
|
||
//
|
||
// In theory we could claim this matches anything,.. in reality
|
||
// the caller doesn't know what they're doing.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
for (ModulesRemaining = Modules->NumberOfModules, Module = Modules->Modules;
|
||
ModulesRemaining;
|
||
ModulesRemaining--, Module++) {
|
||
|
||
FileName = Module->FullPathName + Module->OffsetToFileName;
|
||
|
||
//
|
||
// Check start.
|
||
//
|
||
|
||
if (StartLength) {
|
||
if (_strnicmp(FileName, StartsWith, StartLength) != 0) {
|
||
|
||
//
|
||
// No match.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
FileNameLength = strlen(FileName);
|
||
|
||
if (FileNameLength < (StartLength + EndLength)) {
|
||
|
||
//
|
||
// FileName is too short to contain both strings.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
if (!EndLength) {
|
||
|
||
//
|
||
// Not checking the end but the start matches, success.
|
||
//
|
||
|
||
return Module;
|
||
}
|
||
|
||
//
|
||
// Check end.
|
||
//
|
||
|
||
if (_stricmp(FileName + FileNameLength - EndLength, EndsWith) == 0) {
|
||
|
||
//
|
||
// Tail matches!
|
||
//
|
||
|
||
return Module;
|
||
}
|
||
}
|
||
|
||
//
|
||
// No match found.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
|
||
BOOLEAN
|
||
StatHookTimer (VOID)
|
||
{
|
||
PULONG Address;
|
||
ULONG HalThunkForKeUpdateSystemTime;
|
||
ULONG HalThunkForKeUpdateRunTime;
|
||
ULONG HalThunkForStartProfileInterrupt;
|
||
ULONG HalThunkForStopProfileInterrupt;
|
||
PRTL_PROCESS_MODULES ModuleList;
|
||
PRTL_PROCESS_MODULE_INFORMATION Kernel;
|
||
PRTL_PROCESS_MODULE_INFORMATION Hal;
|
||
|
||
ModuleList = GetLoadedModuleList();
|
||
if (!ModuleList) {
|
||
|
||
//
|
||
// No loaded module list, we aren't going to make much
|
||
// progress, give up.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
#if 0
|
||
Kernel = GetModuleInformationFuzzy("nt", ".exe", ModuleList);
|
||
Hal = GetModuleInformationFuzzy("hal", ".dll", ModuleList);
|
||
|
||
if ((!Kernel) || (!Hal)) {
|
||
ExFreePool(ModuleList);
|
||
return FALSE;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// The kernel is always the first entry on the loaded module
|
||
// list, the hal is always the second. If this ever changes
|
||
// we'll need to come up with another approach.
|
||
//
|
||
|
||
if (ModuleList->NumberOfModules < 2) {
|
||
|
||
//
|
||
// Something's very wrong with this module list.
|
||
//
|
||
|
||
return 0;
|
||
}
|
||
|
||
Kernel = ModuleList->Modules;
|
||
Hal = Kernel + 1;
|
||
|
||
|
||
HalThunkForKeUpdateSystemTime =
|
||
ImportThunkAddressModule(
|
||
Hal,
|
||
"ntoskrnl.exe",
|
||
"KeUpdateSystemTime"
|
||
);
|
||
|
||
//
|
||
// KeUpdateRunTime is not always available.
|
||
//
|
||
|
||
HalThunkForKeUpdateRunTime =
|
||
ImportThunkAddressModule(
|
||
Hal,
|
||
"ntoskrnl.exe",
|
||
"KeUpdateRunTime"
|
||
);
|
||
|
||
HalThunkForStartProfileInterrupt =
|
||
ImportThunkAddressModule(
|
||
Kernel,
|
||
"hal.dll",
|
||
"HalStartProfileInterrupt"
|
||
);
|
||
|
||
HalThunkForStopProfileInterrupt =
|
||
ImportThunkAddressModule(
|
||
Kernel,
|
||
"hal.dll",
|
||
"HalStopProfileInterrupt"
|
||
);
|
||
|
||
ExFreePool(ModuleList);
|
||
|
||
if (!HalThunkForKeUpdateSystemTime ||
|
||
!HalThunkForStartProfileInterrupt ||
|
||
!HalThunkForStopProfileInterrupt) {
|
||
|
||
//
|
||
// Imports not found.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
_declspec(naked)
|
||
VOID
|
||
FASTCALL
|
||
NakedCallToKeProfileInterruptWithSource(
|
||
IN PKTRAP_FRAME TrapFrame,
|
||
IN KPROFILE_SOURCE Source
|
||
)
|
||
{
|
||
_asm {
|
||
push ebp ; Save these as KeProfileInterrupt nukes them
|
||
push ebx
|
||
push esi
|
||
push edi
|
||
}
|
||
|
||
KeProfileInterruptWithSource (TrapFrame, Source);
|
||
|
||
_asm {
|
||
pop edi
|
||
pop esi
|
||
pop ebx
|
||
pop ebp
|
||
ret
|
||
}
|
||
}
|
||
|
||
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
|
||
//
|
||
|
||
NakedCallToKeProfileInterruptWithSource( TrapFrame, ProfileEvent->Source );
|
||
|
||
//
|
||
// 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,
|
||
NULL
|
||
);
|
||
|
||
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
|
||
ImportThunkAddressProcessFile(
|
||
IN ULONG_PTR ImageBase,
|
||
IN HANDLE FileHandle,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName
|
||
)
|
||
{
|
||
ULONG i, j;
|
||
ULONG Dir;
|
||
PVOID Pool;
|
||
ULONG PoolSize;
|
||
IMAGE_DOS_HEADER DosImageHeader;
|
||
IMAGE_NT_HEADERS NtImageHeader;
|
||
PIMAGE_NT_HEADERS LoadedNtHeader;
|
||
PIMAGE_IMPORT_BY_NAME pImportNameData;
|
||
PIMAGE_SECTION_HEADER pSectionHeader;
|
||
IMAGE_SECTION_HEADER SectionHeader;
|
||
PIMAGE_IMPORT_DESCRIPTOR ImpDescriptor;
|
||
PULONG pThunkAddr, pThunkData;
|
||
|
||
PAGED_CODE();
|
||
|
||
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 = 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) {
|
||
|
||
//
|
||
// Success, return this address.
|
||
//
|
||
|
||
return (ULONG)pThunkAddr;
|
||
}
|
||
|
||
// check next thunk
|
||
pThunkData += 1;
|
||
pThunkAddr += 1;
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return 0;
|
||
}
|
||
} finally {
|
||
|
||
//
|
||
// Clean up
|
||
//
|
||
|
||
if (Pool) {
|
||
ExFreePool (Pool);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
ULONG
|
||
ImportThunkAddress (
|
||
IN PUCHAR SourceModule,
|
||
IN ULONG_PTR ImageBase,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName,
|
||
IN PVOID ModuleList
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE FileHandle;
|
||
ULONG ImportAddress;
|
||
|
||
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;
|
||
}
|
||
|
||
if (!ImageBase) {
|
||
ImageBase = LookupImageBase (SourceModule, ModuleList);
|
||
}
|
||
|
||
ImportAddress = ImportThunkAddressProcessFile(ImageBase,
|
||
FileHandle,
|
||
ImportModule,
|
||
ThunkName);
|
||
NtClose (FileHandle);
|
||
return ImportAddress;
|
||
}
|
||
|
||
ULONG
|
||
ImportThunkAddressModule (
|
||
IN PRTL_PROCESS_MODULE_INFORMATION SourceModule,
|
||
IN PUCHAR ImportModule,
|
||
IN PUCHAR ThunkName
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE FileHandle;
|
||
ULONG ImportAddress;
|
||
PUCHAR SubPath;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Strip the system root from the file path so we can use
|
||
// the \SystemRoot object as the head of the path.
|
||
//
|
||
|
||
SubPath = strchr(SourceModule->FullPathName + 1, '\\');
|
||
if (!SubPath) {
|
||
|
||
//
|
||
// If we got here we don't know what we're doing,
|
||
// bail out.
|
||
//
|
||
|
||
return 0;
|
||
}
|
||
|
||
Status = openfile (&FileHandle, "\\SystemRoot", SubPath);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return 0;
|
||
}
|
||
|
||
ImportAddress = ImportThunkAddressProcessFile(
|
||
(ULONG_PTR)SourceModule->ImageBase,
|
||
FileHandle,
|
||
ImportModule,
|
||
ThunkName);
|
||
|
||
NtClose(FileHandle);
|
||
return ImportAddress;
|
||
}
|
||
|
||
ULONG_PTR
|
||
LookupImageBase (
|
||
IN PUCHAR SourceModule,
|
||
IN PVOID ModuleList
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
ULONG BufferSize;
|
||
ULONG junk, ModuleNumber;
|
||
ULONG_PTR ImageBase;
|
||
PRTL_PROCESS_MODULES Modules;
|
||
PRTL_PROCESS_MODULE_INFORMATION Module;
|
||
|
||
ImageBase = 0;
|
||
|
||
if (ModuleList) {
|
||
Modules = ModuleList;
|
||
} else {
|
||
BufferSize = 64000;
|
||
Modules = ExAllocatePool (PagedPool, BufferSize);
|
||
if (!Modules) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Locate system drivers.
|
||
//
|
||
|
||
status = ZwQuerySystemInformation (
|
||
SystemModuleInformation,
|
||
Modules,
|
||
BufferSize,
|
||
&junk
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(Modules);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
Module = &Modules->Modules[ 0 ];
|
||
for (ModuleNumber = 0;
|
||
ModuleNumber < Modules->NumberOfModules;
|
||
ModuleNumber++,Module++) {
|
||
if (_stricmp(Module->FullPathName + Module->OffsetToFileName,
|
||
SourceModule) == 0) {
|
||
ImageBase = (ULONG_PTR)Module->ImageBase;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!ModuleList) {
|
||
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);
|
||
|
||
status = RtlAnsiStringToUnicodeString( &UniPathName, &AscBasePath, FALSE );
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
RtlInitString(&AscName, Name);
|
||
|
||
status = RtlAnsiStringToUnicodeString( &UniName, &AscName, TRUE );
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = RtlAppendUnicodeStringToString (&UniPathName, &UniName);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
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;
|
||
}
|