The following is a complete solution. To do this, I adapted the sample IOCTL driver on MSDN . Please note: the IOCTL sample is the only relative WDM sample skeleton driver I could find, as well as the closest part that I could find in the WDM template , because most kernel mode templates from box in WDK are WDF-based drivers (any template The WDM driver is actually empty without absolutely no source code), but the only example logic that I saw this input / output was through the WDM driver . In addition, some interesting facts that I learned along the way: kernel drivers do not like floating arithmetic, and you cannot use "windows.h", which really limits you to "ntddk.h", a special header in kernel mode. It also means that I cannot do all my calculations inside kernel mode because I cannot call functions like QueryPerformanceFrequency, so I had to get the average performance ratio between timestamps and put them back in user mode for some calculations (without QueryPerformanceFrequency, the values ββyou get from CPU registers that store ticks, like what QueryPerformanceCounter uses, are useless because you don't know the step size, maybe this is a workaround for this, but I decided simply and use the average value, since it works well), In addition, in accordance with the second dream, the reason I used it is that otherwise you almost twitch on multiple threads, which actually confuses your calculations, because your frequencies will increase by the core, constantly checking the results of the QueryPerformanceCounter (you move your kernels as you do more calculations) - NOT FOR ME - its ratio ... so delta time is not so important, because its cycles at a time. .. you can always increase the delta, oh anyway should give you the same ratio with respect to step size . Moreover, it is as minimalistic as I could understand. Good luck, making it much smaller or shorter than that. In addition, if you want to install the driver, you have two options , if you do not want to buy a code signing certificate from any third party, both suck, so select one and suck it up. Start with the driver:
driver.c
// // Include files. // #include <ntddk.h> // various NT definitions #include <string.h> #include <intrin.h> #include "driver.h" #define NT_DEVICE_NAME L"\\Device\\KernelModeDriver" #define DOS_DEVICE_NAME L"\\DosDevices\\KernelModeDriver" #if DBG #define DRIVER_PRINT(_x_) \ DbgPrint("KernelModeDriver.sys: ");\ DbgPrint _x_; #else #define DRIVER_PRINT(_x_) #endif // // Device driver routine declarations. // DRIVER_INITIALIZE DriverEntry; _Dispatch_type_(IRP_MJ_CREATE) _Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH DriverCreateClose; _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH DriverDeviceControl; DRIVER_UNLOAD DriverUnloadDriver; VOID PrintIrpInfo( PIRP Irp ); VOID PrintChars( _In_reads_(CountChars) PCHAR BufferAddress, _In_ size_t CountChars ); #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( PAGE, DriverCreateClose) #pragma alloc_text( PAGE, DriverDeviceControl) #pragma alloc_text( PAGE, DriverUnloadDriver) #pragma alloc_text( PAGE, PrintIrpInfo) #pragma alloc_text( PAGE, PrintChars) #endif // ALLOC_PRAGMA NTSTATUS DriverEntry( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is called by the Operating System to initialize the driver. It creates the device object, fills in the dispatch entry points and completes the initialization. Arguments: DriverObject - a pointer to the object that represents this device driver. RegistryPath - a pointer to our Services key in the registry. Return Value: STATUS_SUCCESS if initialized; an error otherwise. --*/ { NTSTATUS ntStatus; UNICODE_STRING ntUnicodeString; // NT Device Name "\Device\KernelModeDriver" UNICODE_STRING ntWin32NameString; // Win32 Name "\DosDevices\KernelModeDriver" PDEVICE_OBJECT deviceObject = NULL; // ptr to device object UNREFERENCED_PARAMETER(RegistryPath); RtlInitUnicodeString( &ntUnicodeString, NT_DEVICE_NAME ); ntStatus = IoCreateDevice( DriverObject, // Our Driver Object 0, // We don't use a device extension &ntUnicodeString, // Device name "\Device\KernelModeDriver" FILE_DEVICE_UNKNOWN, // Device type FILE_DEVICE_SECURE_OPEN, // Device characteristics FALSE, // Not an exclusive device &deviceObject ); // Returned ptr to Device Object if ( !NT_SUCCESS( ntStatus ) ) { DRIVER_PRINT(("Couldn't create the device object\n")); return ntStatus; } // // Initialize the driver object with this driver entry points. // DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl; DriverObject->DriverUnload = DriverUnloadDriver; // // Initialize a Unicode String containing the Win32 name // for our device. // RtlInitUnicodeString( &ntWin32NameString, DOS_DEVICE_NAME ); // // Create a symbolic link between our device name and the Win32 name // ntStatus = IoCreateSymbolicLink( &ntWin32NameString, &ntUnicodeString ); if ( !NT_SUCCESS( ntStatus ) ) { // // Delete everything that this routine has allocated. // DRIVER_PRINT(("Couldn't create symbolic link\n")); IoDeleteDevice( deviceObject ); } return ntStatus; } NTSTATUS DriverCreateClose( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system when the KernelModeDriver is opened or closed. No action is performed other than completing the request successfully. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: NT status code --*/ { UNREFERENCED_PARAMETER(DeviceObject); PAGED_CODE(); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } VOID DriverUnloadDriver( _In_ PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine is called by the I/O system to unload the driver. Any resources previously allocated must be freed. Arguments: DriverObject - a pointer to the object that represents our driver. Return Value: None --*/ { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; UNICODE_STRING uniWin32NameString; PAGED_CODE(); // // Create counted string version of our Win32 device name. // RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); // // Delete the link from our device name to a name in the Win32 namespace. // IoDeleteSymbolicLink( &uniWin32NameString ); if ( deviceObject != NULL ) { IoDeleteDevice( deviceObject ); } } NTSTATUS DriverDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to perform a device I/O control function. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: NT status code --*/ { PIO_STACK_LOCATION irpSp;// Pointer to current stack location NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length void *inBuf; // pointer to input buffer unsigned __int64 *outBuf; // pointer to the output buffer UNREFERENCED_PARAMETER(DeviceObject); PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation( Irp ); inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; if (!inBufLength || !outBufLength || outBufLength != sizeof(unsigned __int64)*2) { ntStatus = STATUS_INVALID_PARAMETER; goto End; } // // Determine which I/O control code was specified. // switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_SIOCTL_METHOD_BUFFERED: // // In this method the I/O manager allocates a buffer large enough to // to accommodate larger of the user input buffer and output buffer, // assigns the address to Irp->AssociatedIrp.SystemBuffer, and // copies the content of the user input buffer into this SystemBuffer // DRIVER_PRINT(("Called IOCTL_SIOCTL_METHOD_BUFFERED\n")); PrintIrpInfo(Irp); // // Input buffer and output buffer is same in this case, read the // content of the buffer before writing to it // inBuf = (void *)Irp->AssociatedIrp.SystemBuffer; outBuf = (unsigned __int64 *)Irp->AssociatedIrp.SystemBuffer; // // Read the data from the buffer // DRIVER_PRINT(("\tData from User :")); // // We are using the following function to print characters instead // DebugPrint with %s format because we string we get may or // may not be null terminated. // PrintChars(inBuf, inBufLength); // // Write to the buffer // unsigned __int64 data[sizeof(unsigned __int64) * 2]; data[0] = __readmsr(232); data[1] = __readmsr(231); DRIVER_PRINT(("data[0]: %d", data[0])); DRIVER_PRINT(("data[1]: %d", data[1])); RtlCopyBytes(outBuf, data, outBufLength); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // Irp->IoStatus.Information = sizeof(unsigned __int64)*2; // // When the Irp is completed the content of the SystemBuffer // is copied to the User output buffer and the SystemBuffer is // is freed. // break; default: // // The specified I/O control code is unrecognized by this driver. // ntStatus = STATUS_INVALID_DEVICE_REQUEST; DRIVER_PRINT(("ERROR: unrecognized IOCTL %x\n", irpSp->Parameters.DeviceIoControl.IoControlCode)); break; } End: // // Finish the I/O operation by simply completing the packet and returning // the same status as in the packet itself. // Irp->IoStatus.Status = ntStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return ntStatus; } VOID PrintIrpInfo( PIRP Irp) { PIO_STACK_LOCATION irpSp; irpSp = IoGetCurrentIrpStackLocation( Irp ); PAGED_CODE(); DRIVER_PRINT(("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n", Irp->AssociatedIrp.SystemBuffer)); DRIVER_PRINT(("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer)); DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n", irpSp->Parameters.DeviceIoControl.Type3InputBuffer)); DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.InputBufferLength = %d\n", irpSp->Parameters.DeviceIoControl.InputBufferLength)); DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.OutputBufferLength = %d\n", irpSp->Parameters.DeviceIoControl.OutputBufferLength )); return; } VOID PrintChars( _In_reads_(CountChars) PCHAR BufferAddress, _In_ size_t CountChars ) { PAGED_CODE(); if (CountChars) { while (CountChars--) { if (*BufferAddress > 31 && *BufferAddress != 127) { KdPrint (( "%c", *BufferAddress) ); } else { KdPrint(( ".") ); } BufferAddress++; } KdPrint (("\n")); } return; }
driver.h
// // Device type -- in the "User Defined" range." // #define SIOCTL_TYPE 40000 // // The IOCTL function codes from 0x800 to 0xFFF are for customer use. // #define IOCTL_SIOCTL_METHOD_IN_DIRECT \ CTL_CODE( SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) #define IOCTL_SIOCTL_METHOD_OUT_DIRECT \ CTL_CODE( SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT , FILE_ANY_ACCESS ) #define IOCTL_SIOCTL_METHOD_BUFFERED \ CTL_CODE( SIOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS ) #define IOCTL_SIOCTL_METHOD_NEITHER \ CTL_CODE( SIOCTL_TYPE, 0x903, METHOD_NEITHER , FILE_ANY_ACCESS ) #define DRIVER_FUNC_INSTALL 0x01 #define DRIVER_FUNC_REMOVE 0x02 #define DRIVER_NAME "ReadMSRDriver"
Now here is the application that downloads and uses the driver (Win32 console application):
FrequencyCalculator.cpp
#include "stdafx.h"
It uses install.cpp, which you can get from the IOCTL sample. Over the next few days, if not today, I will post a working, fully working and turnkey solution (with code, obviously) on my blog .
Edit: block it http://www.dima.to/blog/?p=101 (the full source code is available there) ...