/*
Query.c - 1998 James M. Finnegan - Microsoft Systems Journal
This console app displays all of the device names present in a Windows
NT system, and tests each for accessibility.
*/
#include <windows.h>
#include <stdio.h>
void main()
{
static char szDevices[65535];
char *szCurrentDevice;
char szName[80];
int i=0;
printf("Win32® Device Name Enumerator - James M. Finnegan, 1998\n");
printf("Copyright (c)1998 Microsoft Systems Journal, All Rights Reserved\n\n");
// Get all the current device names...
if(QueryDosDevice(NULL, szDevices, 65535) != 0)
{
printf("%-20.20s Accessible?\n", "Device Name");
printf("-------------------- -----------\n");
for(;;)
{
static HANDLE hDriver;
// Try to open each device name...
szCurrentDevice = &szDevices[i];
sprintf(szName,"\\\\.\\%s", szCurrentDevice);
hDriver = CreateFile(szName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, // Default security
OPEN_EXISTING,
0,
0); // No template
// If the open failed, print out the error code
if(hDriver == INVALID_HANDLE_VALUE)
printf("%-20.20s No: Error %ld\n", szCurrentDevice,
GetLastError());
// Otherwise, print success and close the driver
else
{
printf("%-20.20s Yes\n", szCurrentDevice);
// Close the driver
CloseHandle(hDriver);
}
// Go to next NULL character
while(szDevices[i] != 0)
i++;
// Bump pointer to the next string
i++;
// The list is double-NULL terminated, so if the character is
// now NULL, we're at the end
if(szDevices[i] == 0)
break;
}
}
else
printf("No Devices in list!\n");
}
Figure 5 Sample Source in the DDK
ATDISK | Driver for ATA hard disk support |
FLOPPY | Floppy disk driver |
DISKPERF | Disk filter driver to measure performance |
SCSI* | Various SCSI support components (disk, tape, CD-ROM) |
ATAPI | SCSI Port to ATAPI (IDE) miniport interface |
Figure 9 Restricted Device Types
FILE_DEVICE_CD_ROM_FILE_SYSTEM |
FILE_DEVICE_DISK |
FILE_DEVICE_DISK_FILE_SYSTEM |
FILE_DEVICE_FILE_SYSTEM |
FILE_DEVICE_NETWORK |
FILE_DEVICE_NETWORK_FILE_SYSTEM |
FILE_DEVICE_TAPE_FILE_SYSTEM |
FILE_DEVICE_VIRTUAL_DISK |
Figure 10 Accessible Device Types
FILE_DEVICE_BEEP |
FILE_DEVICE_CD_ROM |
FILE_DEVICE_CONTROLLER |
FILE_DEVICE_DATALINK |
FILE_DEVICE_DFS |
FILE_DEVICE_INPORT_PORT |
FILE_DEVICE_KEYBOARD |
FILE_DEVICE_MAILSLOT |
FILE_DEVICE_MIDI_IN |
FILE_DEVICE_MIDI_OUT |
FILE_DEVICE_MOUSE |
FILE_DEVICE_MULTI_UNC_PROVIDER |
FILE_DEVICE_NAMED_PIPE |
FILE_DEVICE_NETWORK_BROWSER |
FILE_DEVICE_NULL |
FILE_DEVICE_PARALLEL_PORT |
FILE_DEVICE_PHYSICAL_NETCARD |
FILE_DEVICE_PRINTER |
FILE_DEVICE_SCANNER |
FILE_DEVICE_SERIAL_MOUSE_PORT |
FILE_DEVICE_SERIAL_PORT |
FILE_DEVICE_SCREEN |
FILE_DEVICE_SOUND |
FILE_DEVICE_STREAMS |
FILE_DEVICE_TAPE |
FILE_DEVICE_TRANSPORT |
FILE_DEVICE_UNKNOWN |
FILE_DEVICE_VIDEO |
FILE_DEVICE_WAVE_IN |
FILE_DEVICE_WAVE_OUT |
FILE_DEVICE_8042_PORT |
FILE_DEVICE_NETWORK_REDIRECTOR |
FILE_DEVICE_BATTERY |
FILE_DEVICE_BUS_EXTENDER |
FILE_DEVICE_MODEM |
FILE_DEVICE_VDM |
Figure 11 Major IOCTL Messages
IRP_MJ_CREATE |
IRP_MJ_CREATE_NAMED_PIPE |
IRP_MJ_CLOSE |
IRP_MJ_READ |
IRP_MJ_WRITE |
IRP_MJ_QUERY_INFORMATION |
IRP_MJ_SET_INFORMATION |
IRP_MJ_QUERY_EA |
IRP_MJ_SET_EA |
IRP_MJ_FLUSH_BUFFERS |
IRP_MJ_QUERY_VOLUME_INFORMATION |
IRP_MJ_SET_VOLUME_INFORMATION |
IRP_MJ_DIRECTORY_CONTROL |
IRP_MJ_FILE_SYSTEM_CONTROL |
IRP_MJ_DEVICE_CONTROL |
IRP_MJ_INTERNAL_DEVICE_CONTROL |
IRP_MJ_SHUTDOWN |
IRP_MJ_LOCK_CONTROL |
IRP_MJ_CLEANUP |
IRP_MJ_CREATE_MAILSLOT |
IRP_MJ_QUERY_SECURITY |
IRP_MJ_SET_SECURITY |
IRP_MJ_QUERY_POWER |
IRP_MJ_SET_POWER |
IRP_MJ_DEVICE_CHANGE |
IRP_MJ_QUERY_QUOTA |
IRP_MJ_SET_QUOTA |
IRP_MJ_PNP_POWER |
Figure 12 MSJDrvr.c
/* MSJDrvr.C - 1998 James M. Finnegan - Microsoft Systems Journal
This module implements all of the IOCTLs described in the accompanying article
*/
#include "ntddk.h"
#define FILE_DEVICE_UNKNOWN 0x00000022
#define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN
#define IOCTL_MSJDRVR_GET_STRING CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MSJDRVR_READ_MBR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0801,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MSJDRVR_READ_CMOS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
void MSJUnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS MSJDispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS MSJDispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS MSJDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
/*++
Routine Description:
This routine is called when the driver is loaded by NT.
Arguments:
DriverObject - Pointer to driver object created by system.
RegistryPath - Pointer to the name of the services node for this driver.
Return Value:
The function value is the final status from the initialization operation.
--*/
{
NTSTATUS ntStatus;
UNICODE_STRING uszDriverString;
UNICODE_STRING uszDeviceString;
PDEVICE_OBJECT pDeviceObject;
// Point uszDriverString at the driver name
RtlInitUnicodeString(&uszDriverString, L"\\Device\\MSJDrvr");
// Create and initialize device object
ntStatus = IoCreateDevice(DriverObject,
0,
&uszDriverString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&pDeviceObject);
if(ntStatus != STATUS_SUCCESS)
return ntStatus;
// Point uszDeviceString at the device name
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\MSJDrvr");
// Create symbolic link to the user-visible name
ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);
if(ntStatus != STATUS_SUCCESS)
{
// Delete device object if not successful
IoDeleteDevice(pDeviceObject);
return ntStatus;
}
// Load structure to point to IRP handlers...
DriverObject->DriverUnload = MSJUnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = MSJDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MSJDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MSJDispatchIoctl;
// Return success
return ntStatus;
}
NTSTATUS MSJDispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information=0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
NTSTATUS MSJDispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information=0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
typedef struct tagCMOSMemory
{
ULONG ulBaseMem;
ULONG ulExtMem;
}CMOS_MEMORY, *PCMOS_MEMORY;
NTSTATUS MSJReadDriveZeroMasterBootRecord(PUCHAR pBuffer)
{
NTSTATUS ntStatus;
UNICODE_STRING uszDeviceName;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT pDriveDeviceObject;
// Initialize unicode string
RtlInitUnicodeString(&uszDeviceName, L"\\DosDevices\\PhysicalDrive0");
// Get a pointer to PhysicalDrive0
ntStatus = IoGetDeviceObjectPointer(&uszDeviceName,
// If the device object pointer is valid...
if(NT_SUCCESS(ntStatus))
{
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PIRP pIrp;
LARGE_INTEGER sectorNum;
KeInitializeEvent(&event, NotificationEvent, FALSE);
sectorNum.LowPart = sectorNum.HighPart = 0;
pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
pDriveDeviceObject,
pBuffer,
512,
§orNum,
&event,
&ioStatus);
if(!pIrp)
return FALSE;
ntStatus = IoCallDriver(pDriveDeviceObject, pIrp);
if(ntStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
ntStatus = ioStatus.Status;
}
if(!NT_SUCCESS(ntStatus))
return ntStatus;
// Ditch the fileObject pointer created earlier
ObDereferenceObject(fileObject);
}
return ntStatus;
}
NTSTATUS MSJReadMemoryStatsFromCMOS(PCMOS_MEMORY pMemory)
{
UCHAR byHiByte, byLowByte;
UCHAR byBuffer[256];
#if 0
/*
The following code demonstrates a straight port from old MS-DOS or 16-bit
Windows code to obtain CMOS data. It shows that I/O ports can be freely
poked and prodded via kernel-mode. Even though this code works, the
proper way to obtain CMOS info under Windows NT is demonstrated below,
via the call to HalGetBusData().
*/
// Get base memory bytes from CMOS
WRITE_PORT_UCHAR((PUCHAR)0x70, 0x15);
byLowByte = READ_PORT_UCHAR((PUCHAR)0x71);
WRITE_PORT_UCHAR((PUCHAR)0x70, 0x16);
byHiByte = READ_PORT_UCHAR((PUCHAR)0x71);
pMemory->ulBaseMem = (byHiByte << 8) | byLowByte;
// Get extended memory bytes from CMOS
WRITE_PORT_UCHAR((PUCHAR)0x70, 0x17);
byLowByte = READ_PORT_UCHAR((PUCHAR)0x71);
WRITE_PORT_UCHAR((PUCHAR)0x70, 0x18);
byHiByte = READ_PORT_UCHAR((PUCHAR)0x71);
pMemory->ulExtMem = (byHiByte << 8) | byLowByte;
#endif
// Read the CMOS info from HAL, since it "owns" the CMOS resource...
HalGetBusData(Cmos, 0, 0, &byBuffer, sizeof(byBuffer));
// Get the values from the appropriate offsets and load up the
// caller's structure...
// Base memory
byLowByte = byBuffer[0x15];
byHiByte = byBuffer[0x16];
pMemory->ulBaseMem = (byHiByte << 8) | byLowByte;
// Extended memory
byLowByte = byBuffer[0x17];
byHiByte = byBuffer[0x18];
pMemory->ulExtMem = (byHiByte << 8) | byLowByte;
return STATUS_SUCCESS;
}
NTSTATUS MSJDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_MSJDRVR_GET_STRING:
strcpy(Irp->AssociatedIrp.SystemBuffer, "Hello from NT Kernel-land!\n");
ntStatus = STATUS_SUCCESS;
break;
case IOCTL_MSJDRVR_READ_MBR:
// Read the MBR if the output buffer is at least the size of one sector
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >= 512)
ntStatus = MSJReadDriveZeroMasterBootRecord(
Irp->AssociatedIrp.SystemBuffer);
else
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
case IOCTL_MSJDRVR_READ_CMOS:
// Query the CMOS if the output buffer can hold our results...
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof(CMOS_MEMORY))
ntStatus = MSJReadMemoryStatsFromCMOS(Irp->
AssociatedIrp.SystemBuffer);
else
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
default:
break;
}
Irp->IoStatus.Status = ntStatus;
// Set # of bytes to copy back to user-mode...
if(ntStatus == STATUS_SUCCESS)
Irp->IoStatus.Information=irpStack->
Parameters.DeviceIoControl.OutputBufferLength;
else
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
void MSJUnloadDriver(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uszDeviceString;
IoDeleteDevice(DriverObject->DeviceObject);
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\MSJDrvr");
IoDeleteSymbolicLink(&uszDeviceString);
}
Figure 14 Start Values
0 SERVICE_BOOT_START | Drivers with this startup type are started by the Windows NT loader very early in the boot process. You should probably avoid this type unless you really need to be loaded early in the boot process. |
1 SERVICE_SYSTEM_START | The driver will be started during initialization of Windows NT. |
2 SERVICE_AUTO_START | The driver will be automatically started during system startup (after the GUI is loaded, but before anyone logs in). |
3 SERVICE_DEMAND_START | The driver can be started on demand (for example, by the Drivers applet in the Control Panel, or by the NET START command at the command prompt, or by an application via the SCM API). |
4 SERVICE_DISABLED | The driver is disabled and cannot be manipulated (started or stopped). |
Figure 15 Error Control Values
0 SERVICE_ERROR_IGNORE | The error is logged. |
1 SERVICE_ERROR_NORMAL | The SCM pop up a dialog box to indicate driver failure to the user. |
2 SERVICE_ERROR_SEVERE | If the last-known-good configuration is being started, startup continues. Otherwise, the system is restarted with the last-known-good configuration. |
3 SERVICE_ERROR_CRITICAL | If the last-known-good configuration is being started, startup fails. Otherwise, the system is restarted with the last-known good configuration. |
Figure 16 Dynamic.c
/*
Dynamic.c - 1998 James M. Finnegan - Microsoft Systems Journal
This module implements the functionality to dynamically load and
unload via the Service Control Manager.
*/
#include <windows.h>
BOOL InstallAndStartDriver(SC_HANDLE hSCManager, LPCTSTR DriverName,
LPCTSTR ServiceExe)
{
SC_HANDLE hService;
BOOL bReturn;
// Create the driver entry in the SC Manager.
hService = CreateService(hSCManager, // SCManager database
DriverName, // name of service
DriverName, // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_KERNEL_DRIVER, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
ServiceExe, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL // no password
);
// This may fail because the service entry already exists. However,
// we've tried to load it earlier, so something clearly is wrong if
// we can't create it!
if(!hService)
return FALSE;
// Start the driver!
bReturn = StartService(hService, 0, NULL);
// Dispose of the handle...
CloseServiceHandle(hService);
// Return whatever the driver start returned...
return bReturn;
}
BOOL LoadDynamicNTDriver()
{
SC_HANDLE hSCManager;
char currentDirectory[128];
BOOL bReturn;
// Open Service Control Manager on the local machine...
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
// Assume driver is in the same directory as the DLL. Hence, create
// a fully qualified pathname for the SCM to add to the registry.
GetCurrentDirectory(128, currentDirectory);
lstrcat(currentDirectory,"\\MSJDrvr.sys");
// Install drive in the registry and start it...
bReturn = InstallAndStartDriver(hSCManager, "MSJDrvr", currentDirectory);
// Close the Service Control Manager...
CloseServiceHandle(hSCManager);
return bReturn;
}
////////////////////////////////////////////////////////////////////////////
// Unload routines
////////////////////////////////////////////////////////////////////////////
BOOL UnloadDynamicNTDriver()
{
SC_HANDLE hSCManager;
SC_HANDLE hService;
SERVICE_STATUS serviceStatus;
BOOL bReturn;
// Open Service Control Manager on the local machine...
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
// Open the Service Control Manager for our driver service...
hService = OpenService(hSCManager, "MSJDrvr", SERVICE_ALL_ACCESS);
// Stop the driver. Will return TRUE on success...
bReturn = ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus);
// Delete the driver from the registry...
if(bReturn == TRUE)
bReturn = DeleteService(hService);
// Close the SC Manager
CloseServiceHandle(hService);
// Close Service Control Manager
CloseServiceHandle(hSCManager);
return bReturn;
}
HANDLE LoadDriver(OSVERSIONINFO *os, BOOL *fNTDynaLoaded)
{
HANDLE hDevice;
// Default to FALSE on informing the caller we've dynamically
// loaded the Windows NT driver via the SCM.
*fNTDynaLoaded = FALSE;
// If we're under Windows NT...
if(os->dwPlatformId == VER_PLATFORM_WIN32_NT)
{
// Try opening the device driver (in case it's static). We'll
// Deal with a failure in a moment...
hDevice = CreateFile("\\\\.\\MSJDrvr",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, // Default security
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // Perform asynchronous I/O
0); // No template
// If the device driver wasn't started, let's dynamically load it
// here...
if(hDevice == INVALID_HANDLE_VALUE)
{
// Load and start the driver...
LoadDynamicNTDriver();
// Open the driver...
hDevice = CreateFile("\\\\.\\MSJDrvr",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, // Default security
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // Perform asynchronous I/O
0); // No template
// Set the global to indicate that we've Dynaloaded. This will
// Cause DllMain to stop and unload on a process detach.
*fNTDynaLoaded = TRUE;
}
return hDevice;
}
// Otherwise, assume Win95!
else
{
// Dynaload the VxD...
return CreateFile("\\\\.\\MSJDrvr.vxd", 0,0,0, CREATE_NEW,
FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_OVERLAPPED, 0);
}
}