/*++ 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; }