169 lines
5.5 KiB
C
169 lines
5.5 KiB
C
|
/*********************************************************************
|
||
|
|
||
|
Author: Dale Roberts
|
||
|
Date: 8/30/95
|
||
|
Program: GIVEIO.SYS
|
||
|
Compile: Use DDK BUILD facility
|
||
|
|
||
|
Purpose: Give direct port I/O access to a user mode process.
|
||
|
|
||
|
*********************************************************************/
|
||
|
#include <ntddk.h>
|
||
|
|
||
|
/*
|
||
|
* The name of our device driver.
|
||
|
*/
|
||
|
#define DEVICE_NAME_STRING L"giveio"
|
||
|
|
||
|
/*
|
||
|
* This is the "structure" of the IOPM. It is just a simple
|
||
|
* character array of length 0x2000.
|
||
|
*
|
||
|
* This holds 8K * 8 bits -> 64K bits of the IOPM, which maps the
|
||
|
* entire 64K I/O space of the x86 processor. Any 0 bits will give
|
||
|
* access to the corresponding port for user mode processes. Any 1
|
||
|
* bits will disallow I/O access to the corresponding port.
|
||
|
*/
|
||
|
#define IOPM_SIZE 0x2000
|
||
|
typedef UCHAR IOPM[IOPM_SIZE];
|
||
|
|
||
|
/*
|
||
|
* This will hold simply an array of 0's which will be copied
|
||
|
* into our actual IOPM in the TSS by Ke386SetIoAccessMap().
|
||
|
* The memory is allocated at driver load time.
|
||
|
*/
|
||
|
IOPM *IOPM_local = 0;
|
||
|
|
||
|
/*
|
||
|
* These are the two undocumented calls that we will use to give
|
||
|
* the calling process I/O access.
|
||
|
*
|
||
|
* Ke386IoSetAccessMap() copies the passed map to the TSS.
|
||
|
*
|
||
|
* Ke386IoSetAccessProcess() adjusts the IOPM offset pointer so that
|
||
|
* the newly copied map is actually used. Otherwise, the IOPM offset
|
||
|
* points beyond the end of the TSS segment limit, causing any I/O
|
||
|
* access by the user mode process to generate an exception.
|
||
|
*/
|
||
|
void Ke386SetIoAccessMap(int, IOPM *);
|
||
|
void Ke386QueryIoAccessMap(int, IOPM *);
|
||
|
void Ke386IoSetAccessProcess(PEPROCESS, int);
|
||
|
|
||
|
/*********************************************************************
|
||
|
Release any allocated objects.
|
||
|
*********************************************************************/
|
||
|
VOID GiveioUnload(IN PDRIVER_OBJECT DriverObject)
|
||
|
{
|
||
|
WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
|
||
|
UNICODE_STRING uniDOSString;
|
||
|
|
||
|
if(IOPM_local)
|
||
|
MmFreeNonCachedMemory(IOPM_local, sizeof(IOPM));
|
||
|
|
||
|
RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
|
||
|
IoDeleteSymbolicLink (&uniDOSString);
|
||
|
IoDeleteDevice(DriverObject->DeviceObject);
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
Set the IOPM (I/O permission map) of the calling process so that it
|
||
|
is given full I/O access. Our IOPM_local[] array is all zeros, so
|
||
|
the IOPM will be all zeros. If OnFlag is 1, the process is given I/O
|
||
|
access. If it is 0, access is removed.
|
||
|
*********************************************************************/
|
||
|
VOID SetIOPermissionMap(int OnFlag)
|
||
|
{
|
||
|
Ke386IoSetAccessProcess(PsGetCurrentProcess(), OnFlag);
|
||
|
Ke386SetIoAccessMap(1, IOPM_local);
|
||
|
}
|
||
|
|
||
|
void GiveIO(void)
|
||
|
{
|
||
|
SetIOPermissionMap(1);
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
Service handler for a CreateFile() user mode call.
|
||
|
|
||
|
This routine is entered in the driver object function call table by
|
||
|
the DriverEntry() routine. When the user mode application calls
|
||
|
CreateFile(), this routine gets called while still in the context of
|
||
|
the user mode application, but with the CPL (the processor's Current
|
||
|
Privelege Level) set to 0. This allows us to do kernel mode
|
||
|
operations. GiveIO() is called to give the calling process I/O
|
||
|
access. All the user mode application needs do to obtain I/O access
|
||
|
is open this device with CreateFile(). No other operations are
|
||
|
required.
|
||
|
*********************************************************************/
|
||
|
NTSTATUS GiveioCreateDispatch(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
{
|
||
|
GiveIO(); // give the calling process I/O access
|
||
|
|
||
|
Irp->IoStatus.Information = 0;
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
Driver Entry routine.
|
||
|
|
||
|
This routine is called only once after the driver is initially
|
||
|
loaded into memory. It allocates everything necessary for the
|
||
|
driver's operation. In our case, it allocates memory for our IOPM
|
||
|
array, and creates a device which user mode applications can open.
|
||
|
It also creates a symbolic link to the device driver. This allows
|
||
|
a user mode application to access our driver using the \\.\giveio
|
||
|
notation.
|
||
|
*********************************************************************/
|
||
|
NTSTATUS DriverEntry(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath
|
||
|
)
|
||
|
{
|
||
|
PDEVICE_OBJECT deviceObject;
|
||
|
NTSTATUS status;
|
||
|
WCHAR NameBuffer[] = L"\\Device\\" DEVICE_NAME_STRING;
|
||
|
WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
|
||
|
UNICODE_STRING uniNameString, uniDOSString;
|
||
|
|
||
|
//
|
||
|
// Allocate a buffer for the local IOPM and zero it.
|
||
|
//
|
||
|
IOPM_local = MmAllocateNonCachedMemory(sizeof(IOPM));
|
||
|
if(IOPM_local == 0)
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
RtlZeroMemory(IOPM_local, sizeof(IOPM));
|
||
|
|
||
|
//
|
||
|
// Set up device driver name and device object.
|
||
|
//
|
||
|
RtlInitUnicodeString(&uniNameString, NameBuffer);
|
||
|
RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
|
||
|
|
||
|
status = IoCreateDevice(DriverObject, 0,
|
||
|
&uniNameString,
|
||
|
FILE_DEVICE_UNKNOWN,
|
||
|
0, FALSE, &deviceObject);
|
||
|
|
||
|
if(!NT_SUCCESS(status))
|
||
|
return status;
|
||
|
|
||
|
status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);
|
||
|
|
||
|
if (!NT_SUCCESS(status))
|
||
|
return status;
|
||
|
|
||
|
//
|
||
|
// Initialize the Driver Object with driver's entry points.
|
||
|
// All we require are the Create and Unload operations.
|
||
|
//
|
||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = GiveioCreateDispatch;
|
||
|
DriverObject->DriverUnload = GiveioUnload;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|