Home www.visualprog.cz

 

Základy vytvoření sys ovládače pracujícího v kernel vrstvě 

Windows 2000

 

19.04.2002

(c) 2002 Pavel Pindora  Email Kontakt .

Pro více informací je zde formulář

 

Click on of the PayPal button to donate money to this Web

 

 

Důležité, přečtěte si červeně vybarvené texty než začnete s programováním

Veškeré pokusy, s programováním v Kernel oblasti, mohou mít vlivem zanesených chyb katastrofální následky, projevující se nestabilitou systému, porušením integrity dat,  nemožností najet Windows a následnou nutností jeho přeinstalování se všemi důsledky, které mohou vzniknout.

Je výhodné mít zvláštní k těmto účelům vyčleněný počítač .

Uvedené postupy jsou určeny ryze a více méně pro teoretické nastudování problematiky programovacích postupů a autor tohoto článku nenese žádnou odpovědnost za případné škody.

 

Kompilace ,tvorba INF souboru, zavedení do systému

Aplikační program, popis činnosti

Výpis zdrojáku pro Kernel

Výpis zdrojáku pro aplikační exe program

 

Způsob tvorby "kernel" ovladače pro Win NT4 ,původní z 23.1.2001

New 6.3.2004 Driver pro simulaci tiskárny

New 13.3.2004 Vteřinový časovač pro timeout

Co je kernel vrstva je vysvětleno  zde a hlavním důvodem proč psát programy pro tuto vrstvu, je možnost používat privilegované instrukce jako např. out dx,al což představuje zápis registru al na port určený hodnotou v registru dx. Pokud tedy tuto instrukci použijeme v MS Visual C++, tak překlad proběhne bez problému ale spuštěním nám systém ukončí proces hláškou o nemožnosti používat tyto instrukce.Jak dokumentuje následující obrázek.

Pro psaní do kernelu musíme mít Microsoft DDK , který je možno stáhnout z internetu na http://www.microsoft.com/ddk/installW2k.htm?  Pro vyzkoušení funkce ovládače budeme potřebovat i program, který bude pracovat v normální aplikační vrstvě a bude se na tento ovládač odkazovat, využíváním jeho dat . 

Co vše budeme při tvorbě kernel ovládače potřebovat ? Nejlépe si připravíme složku se vším potřebným a bude se jmenovat např. Pokus_DDK 

V ní je 

1. d_kernel.chm help soubor s vysvětlením všeho potřebného 

       zástupce WINDDK\2195\help\d_kernel.chm

2.Generate Inf Wizard pro generování instalačního souboru, který ovládač zaregistruje do systému

                    WINDDK\2195\tools\geninf.exe

3. kompilace.bat textový soubor bat , kompilující zdroják a vytvářející sys soubor

        jeho výpis:

            cls
            path=G:\WINDDK\2195\bin;g:\Microsoft Visual Studio\VC98\bin
            build -ceZ
            pause
            nmake
            pause
4. Pokus_read.exe Zástupce aplikačního programu, není potřeba, budeme ho spouštět přímo z Visual C++

5. Zástupce - kompilace.bat   bat soubor provádějící kompilaci

 

Funkce ovládače bude jednoduchá , inicializuje se timer a v jeho obslužné rutině se inkrementuje proměnná Ldata , její hodnota se pak přenese do aplikačního programu, !!! navíc se tato hodnota přenese na LPT port s adresou 378 hexa a je nutné se ujistit , že to má počítač ( kde se budou dělat pokusy s driverem ) stejné, jinak je potřeba změnit nebo úplně odstavit _asm blok ve funkci TimerRoutine souboru Pokus_sys.c  !!!, 

Název kernel binárního souboru je Pokus_sys.sys a aplikačního Pokus_read.exe.

Základem ovládače jsou vnořené funkce :

 

//------------------------ Volána při startu Windows 2000 ------------------------

NTSTATUS DriverEntry 

(
        IN PDRIVER_OBJECT DriverObject,
        IN PUNICODE_STRING RegistryPath
)

 

 //------------------------ Příkazem hTest = CreateFile  v exe aplikaci, je zavolána tato funkce v sys

NTSTATUS LdUnldOpen 

¨(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
)

 

// -----------------------Příkaz dwStatus = ReadFileEx v exe volá ------------------------

NTSTATUS LdUnldRead

(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
)

Zde je mimo jiné vlastní předávání dat 

// Tady vkladam data pro predani do aplikacni casti 
*(pIoBuff+0) = 0x77;
*(pIoBuff+1) = Ldata;


 a v aplikačním exe jsou vypsána do konzoly příkazy printf

	data = buffer[0];
	if( data==0x77 && (data1!=(long)buffer[1]) )
	{
		data1 = (long)buffer[1];
		printf("data naplnena v Kernel pokus_sys.sys Ln79  = 0x%02x\n",buffer[0] );
		printf("data naplnena v Kernel pokus_sys.sys Ln80  = 0x%08x\n",(long)buffer[1] ); // %d\n"   ,(long)buffer[1] )
		printf("data naplnena v Kernel pokus_sys.sys Ln81  = 0x%02x\n",buffer[2] );
	};

//---------------------- Vlastní asi vteřinový časovač volá ------------------------

VOID *TimerRoutine 

(
        IN PDEVICE_OBJECT DeviceObject,
        IN PVOID Context
)

{
        Ldata++;
        __asm // __asm block
                    {
                            mov eax, Ldata
                            mov dx, 0378h
                            out dx, al
                    }
}

Tady se inkrementuje proměnná Ldata a tato je vyslána na LPT port s adresou 378hexa. Pozor přečtěte si výše popsané červené upozornění. .

 

Další funkce jsou LdUnldClose, LdUnldUnload...

 

Kompilace,tvorba INF, zavedení do systému

Pokud Zástupcem - kompilace.bat program úspěšně přeložíme, vytvoří se Pokus_sys.sys viz následující obrázek, 

 

tento pak zkopírujeme do adresáře Win2k\system32\drivers , tím to ale nekončí , ještě je potřeba sys zaregistrovat a zavést ho do složky Porty (COM a LPT) . 

 

 

To obstará INF soubor , který můžeme vygenerovat pomocí, již výše zmiňovaného, Inf Wizardu , následující obrázky stručně přibližují popsanou činnost. Vygenerovaný INF soubor, pak pomocí správce hardware, zavede sys do systému a počítač musíme restartovat, driver je zaveden až po restartu. Pokud uděláme chybu a místo Windows 2000 se zobrazí modrá obrazovka, je nutné W2k spustit v nouzovém režimu, ovládač odinstalovat a znovu provést restart. Modrou smrt bezpečně při startu provedou zakomentované příkazy, počínaje funkcí KeInitializeDpc a konče KeSetTimerEx v DriverEntry  

 

                            Vložíme informace o tvůrci ovládače

 

                                    Ovládač nebude digitálně podepsán

 

                            Ovládač není v seznamu nabízených

 

                        Driver bude zaveden do složky Portů proto je nutné zvolit Ports

 

                            Zvolíme platformu pro Intel Pentium

 

                        Přidělíme název a způsob zavedení do systému

 

Driver nebude obsluhovat žádnou kartu jedná se jen o jakýsi virtuální hardware

 

                                    Navolíme cestu k přeloženému sys souboru

 

                                                Nakonec vše dokončíme generováním INF souboru

 

 

Aplikační program, popis činnosti

 

Z obrázku je zřejmé, že se jedná o jednoduchou konzolovou aplikaci , která zobrazí data poskytována drivrem je to mnou libovolně zvolené hexadecimální číslo 0x77, pak stav proměnné Ldata ( 0xffffffef ) .

 

 

Jednoduše si popíšeme jak se driver přilinkuje k aplikaci, je to stejné jako při otevírání souboru pomocí příkazu CreateFile přičemž jako název souboru se vloží ( pro DOS konzolu ) název LOADTEST ten je určen ve zdrojáku sys souboru Pokus_sys.sys následujícími řádky.

#define NT_DEVICE_NAME L"\\Device\\Ldunld" 
#define DOS_DEVICE_NAME L"\\DosDevices\\LOADTEST" 

hTest = CreateFile	(
			"\\\\.\\LOADTEST",// Jedna se DOS konzolu viz Pokus_sys.c Ln6
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL 
		);

Pokud vše proběhne v pořádku tak můžeme načíst data ( 0x77,Ldata ) pomocí příkazu ReadFileEx , kde do ovládače předáme buffer, kde se uloží data a jeho velikost.

	dwStatus = ReadFileEx(
				hTest,
				buffer,
				255,
				DriverlpOverlapped,
				ReadCompleteApc
			);

A nakonec je třeba zdůraznit ošetření veškerých hazardů, aby se aplikace nezhroutila,
před jakýmikoliv pokusy si znovu přečtěte červeně vybarvené texty.
//********************************************************************************************

Výpis zdrojáku pro Kernel

#include <ntddk.h>
//#include "gpioctl.h"        // Get IOCTL interface definitions


#define NT_DEVICE_NAME L"\\Device\\Ldunld" 
#define DOS_DEVICE_NAME L"\\DosDevices\\LOADTEST" 
#define FILE_DEVICE_SIMPLDRV 0x00008300 
//------------------------------------------------------
typedef struct _IO_BUFFER 
	{ 
		ULONG QueueTime;
		ULONG DequeueTime;
		ULONG Data;
	}
	IO_BUFFER, *PIO_BUFFER;

typedef struct _RING_BUFF 
	{
		ULONG Size; 
		PULONG pBase;
		PULONG pHead;
		PULONG pTail;
	} 
	RING_BUFF, *PRING_BUFF;

typedef struct _DEVICE_EXTENSION 
	{
		PDEVICE_OBJECT	DeviceObject;
		RING_BUFF		RingBuff;
		ULONG			OpenInstances; // sanity check
	} 
	DEVICE_EXTENSION, *PDEVICE_EXTENSION;

VOID			*Context;
long			Ldata;
PRKDPC			Dpc;
PKTIMER			Timer;
LARGE_INTEGER	DueTime;//LARGE_INTEGER
//------------------------------------------------------

NTSTATUS	LdUnldOpen
	(
		IN PDEVICE_OBJECT DeviceObject,
		IN PIRP Irp
	);

NTSTATUS	LdUnldClose
	(
		IN PDEVICE_OBJECT DeviceObject,
		IN PIRP Irp
	);

VOID	LdUnldUnload
	(
		IN PDRIVER_OBJECT DriverObject
	);

NTSTATUS	LdUnldRead
	(
		IN PDEVICE_OBJECT DeviceObject,
		IN PIRP Irp
	)
{
	PDEVICE_EXTENSION deviceExtension;
	PIO_STACK_LOCATION irpStack;
	char *pIoBuff;
	ULONG inputBufferLength;
	ULONG outputBufferLength;


	irpStack					= IoGetCurrentIrpStackLocation (Irp);
	deviceExtension				= DeviceObject->DeviceExtension;

	KdPrint( ("LDUNLD: Opened!!\n") );
	Irp->IoStatus.Status		= STATUS_SUCCESS;
	Irp->IoStatus.Information	= 555;
	IoCompleteRequest( Irp, IO_NO_INCREMENT );
	pIoBuff						= (char*)Irp->AssociatedIrp.SystemBuffer;
	inputBufferLength			= irpStack->Parameters.DeviceIoControl.InputBufferLength;
	outputBufferLength			= irpStack->Parameters.DeviceIoControl.OutputBufferLength;

	// Tady vkladam data pro predani do aplikacni casti 
	*(pIoBuff+0)				= 0x77;
	*(pIoBuff+1)				= Ldata;
	//*(pIoBuff+2)				= 0x99;

	return STATUS_SUCCESS;
};

NTSTATUS	LdUnldReadDevice
( 
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP Irp
)
{
	return STATUS_SUCCESS;
};

//*************************************************************************

VOID *TimerRoutine	(
			IN PDEVICE_OBJECT DeviceObject,
			IN PVOID Context
		)
{
	Ldata++;
	__asm                  // __asm block
			{
				mov eax,	Ldata
				mov	dx,		0378h
				out	dx,		al
			}

}
//*************************************************************************
VOID *DpcTimerRoutine		(
					IN PKDPC Dpc,
					IN PVOID DeferredContext,
					IN PVOID SystemArgument1,
					IN PVOID SystemArgument2
				)
{
	Ldata++;
}
//*************************************************************************/
NTSTATUS	DriverEntry
(
	IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPath
)
{
	PDEVICE_OBJECT deviceObject = NULL;
	PDEVICE_EXTENSION pDevExt;
	NTSTATUS status;
	UNICODE_STRING uniNtNameString;
	UNICODE_STRING uniWin32NameString;
	
	KdPrint( ("LDUNLD: Entered the Load/Unload driver!\n") );
	RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME );

	status = IoCreateDevice(
				DriverObject,
				sizeof (DEVICE_EXTENSION),// We don't use a device extension
				&uniNtNameString,
				FILE_DEVICE_SIMPLDRV,
				0, // No standard device characteristics
				TRUE, // This isn't an exclusive device
				&deviceObject
			);

	if ( NT_SUCCESS(status) )
	{ 
		//---------------22.6.99----------------------------------
		deviceObject->Flags |= DO_BUFFERED_IO;
		pDevExt = deviceObject->DeviceExtension;
		pDevExt->DeviceObject = deviceObject;
		pDevExt->OpenInstances = 0;
		pDevExt->RingBuff.pHead = 
		pDevExt->RingBuff.pTail = 
		pDevExt->RingBuff.pBase = ExAllocatePoolWithTag
						( 
							NonPagedPool,
							255, // SizeInBytes
							'QPRI' 
						);

		DriverObject->MajorFunction[IRP_MJ_CREATE]	= LdUnldOpen;
		DriverObject->MajorFunction[IRP_MJ_CLOSE]	= LdUnldClose;
		DriverObject->MajorFunction[IRP_MJ_READ]	= LdUnldRead;
		DriverObject->DriverUnload					= LdUnldUnload;

		KdPrint( ("LDUNLD: just about ready!\n") );
		RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );
		status = IoCreateSymbolicLink( &uniWin32NameString, &uniNtNameString );

		if (!NT_SUCCESS(status))
		{
			KdPrint( ("LDUNLD: Couldn't create the symbolic link\n") );
			IoDeleteDevice( DriverObject->DeviceObject );
		}
		else
		{
			KdPrint( ("LDUNLD: All initialized!\n") );
			
			status = IoInitializeTimer	(	deviceObject,
							TimerRoutine,
							Context
						);
			IoStartTimer( deviceObject );	//*/
			/*
			KeInitializeDpc	(
						Dpc,
						TimerRoutine,
						Context
							);
			KeInitializeTimerEx	(
							Timer,
							SynchronizationTimer
						);
			KeSetTimerEx(
						Timer,
						DueTime,
						10,
						Dpc
					);//*/
		}
	}
	else
	{
		KdPrint( ("LDUNLD: Couldn't create the device\n") );
	}

	return status;
}

NTSTATUS	LdUnldOpen
(
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP Irp
)
{
	KdPrint( ("LDUNLD: Opened!!\n") );
	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest( Irp, IO_NO_INCREMENT );
	
	return STATUS_SUCCESS;
}

NTSTATUS	LdUnldClose
(
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP Irp
)
{
	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	KdPrint( ("LDUNLD: Closed!!\n") );
	IoCompleteRequest( Irp, IO_NO_INCREMENT );
	return STATUS_SUCCESS;
}

VOID	LdUnldUnload
(
	IN PDRIVER_OBJECT DriverObject
)
{
	UNICODE_STRING uniWin32NameString;
	KdPrint( ("LDUNLD: Unloading!!\n") );
	RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );
	IoDeleteSymbolicLink( &uniWin32NameString );
	IoDeleteDevice( DriverObject->DeviceObject );
}

/********************************************************************************************
 

Výpis zdrojáku pro aplikační exe program

 
#include "windows.h"
#include "stdio.h"
/********************Testovaci soft**************************/
#define MAXBUFF 1000
DWORD IoBuff[MAXBUFF];
DWORD dwStatus;
DWORD dwRetries = 0;

LPOVERLAPPED *DriverlpOverlapped;

typedef enum _OPERATION_TYPE 
	{
		eRead,
		eWrite,
		eIoControl,
		eMaxOpTypes
	} 
	OP_TYPE, *POP_TYPE;

DWORD data,data1;
typedef struct _IO_BUFFER 
	{
		ULONG QueueTime;
		ULONG DequeueTime;
		ULONG Data;
	} 
	IO_BUFFER, *PIO_BUFFER;

//----------------------------------------------
VOID WINAPI ReadCompleteApc
	(
		ULONG ulError,
		ULONG ulTransfered,
		LPOVERLAPPED lpOvl
	)
{
	//--------Jen Testovani--------
	//-----------------------------
	return;
};
//----------------------------------------------
DWORD	SLEEPEX
	(
		DWORD dT,
		int n 
	)
{
	DWORD dwStatus;

	switch ( dwStatus = SleepEx( dT, TRUE) ) 
	{
	case STATUS_WAIT_0: 
					break;
	case WAIT_IO_COMPLETION:
						#ifdef DBG_SLEEP
							printf("SleepEx returned: WAIT_IO_COMPLETION\n" );
						#endif
					break;
	default:
						printf("Unhandled SleepEx: %d\n", dwStatus);
					break;
	}
	return dwStatus;
};

/**************************************************************/

int __cdecl main(int argc, char **argv)
{
	char		*buffer;
	HANDLE		hTest = 0;
	COORD		curPos;
	HANDLE		hStdOut		= GetStdHandle(STD_OUTPUT_HANDLE),
				hStdIn		= GetStdHandle(STD_INPUT_HANDLE); 
	ULONG		drv1,drv2;

	curPos.X = 0;
	curPos.Y = 1;

	/*__asm                  // __asm block
			{
				mov eax,	0ffh
				mov	dx,		0378h
				out	dx,		al
			}*/

	hTest = CreateFile	(
				"\\\\.\\LOADTEST",// Jedna se DOS konzolu viz Pokus_sys.c Ln6
				GENERIC_READ | GENERIC_WRITE,
				0,
				NULL,
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				NULL 
			);


	if ( hTest != ((HANDLE)-1))
	{ 
		printf("Fajn - Nasel jsem tvuj prvni Kernel driver!!!\n");
		buffer = (char*) malloc( 1255 );
		if( buffer==NULL )
		{
			printf("Konec, chyba alokace buff \n");
			getch();
			return 0;
		};
		DriverlpOverlapped = (LPOVERLAPPED) malloc( sizeof(LPOVERLAPPED));

		while( !_kbhit() )
		{
			SetConsoleCursorPosition( hStdOut,curPos );
			printf("Ukonci stiskem klavesy\n");
			if( DriverlpOverlapped==0 )
			{
				printf("Konec, chyba alokace lpO \n");
				break;
			};
			dwStatus = ReadFileEx	(
							hTest,
							buffer,
							255,
							DriverlpOverlapped,
							ReadCompleteApc
						);
			if ( !dwStatus )// Nastala chyba ve cteni
			{
				//( hStdOut );
				CloseHandle(hTest);
				FillConsoleOutputCharacter(
								hStdOut,
								' ',
								1000,
								curPos,
								0
							);

				dwStatus = GetLastError();
				printf("ReadFileEx error: %d\n", dwStatus );
				if( ERROR_INVALID_FUNCTION == dwStatus ) 
				{
					printf("ReadFileEx error=Incorrect function\n");
				};
				if( ERROR_IO_DEVICE == dwStatus ) 
				{
					if ( ++dwRetries < 12 ) 
					{
						printf("ReadFileEx => _Retry : %d\n", dwRetries );
						SLEEPEX( 1000, eRead );
					} 
					else 
					{
						printf("ReadFileEx => MAX_READ_RETRIES %d\n", dwRetries );
						return dwStatus;
					};
				};
				printf("Stopnu cteni z drivru Pokus_sys\n");
				if( dwStatus==12 )
					printf("ReadFileEx => ERROR_INVALID_ACCESS %d\n", dwStatus );
				break;
			};
			data = buffer[0];
			if( data==0x77 && (data1!=(long)buffer[1]) )
			{
				data1 = (long)buffer[1];
				printf("data naplnena v Kernel pokus_sys.sys Ln79  = 0x%02x\n",buffer[0] );
				printf("data naplnena v Kernel pokus_sys.sys Ln80  = 0x%08x\n",(long)buffer[1] ); // %d\n"   ,(long)buffer[1] )
				printf("data naplnena v Kernel pokus_sys.sys Ln81  = 0x%02x\n",buffer[2] );
			};
			//drv1 = getch();
			//if( drv1==0x20 )
			///	break;
		};

		CloseHandle(hTest);
	} 
	else
	{ 
		printf("Can't get a handle to LOADTEST\n"); 
	};
	getch();
	return 1;
} 


 

Způsob tvorby "kernel" ovladače pro Win NT4

 

Výpis programu VisualSys.sys, zaváděného do kernelové oblasti Windows NT
Výpis aplikačního programu testující VisualSys.sys pro Windows NT

Nejdříve zkompilujeme VisualSys.c,uvedený Výpisem programu pro kernel ,jako sys knihovnu a umístíme ji do adresáře /WINNT/SYSTEM32, pro kompilaci musíme vlastnit DDK pro Windows NT od Microsoft Corporation, potom ji pomocí regini s parametrem VisualSys.ini zaregistrujeme do registrů a počítač restartujeme.
Výpis VisualSys.ini:

\registry\machine\system\currentcontrolset\services\VisualSys
Type = REG_DWORD 0x00000001
Start = REG_DWORD 0x00000003
Group = Extended base
ErrorControl = REG_DWORD 0x00000001

Potom zavedeme VisualSys.sys do paměti počítače a to za pomocí například příkazu NET v DOS prompt
NET START VISUALSYS.SYS

a nyní zkompilujeme aplikační program,znázorněný Výpisem programu pro aplikační oblast , a vytvořímme VisualSys.exe.
Na řádku
if ( !dwStatus )
umístíme breakpoint a ve vývojovém prostředí Visual C++ aplikaci spustíme, ten se zastaví na tomto řádku, nato prohlédneme buffer a v první buňce musí být číslo 77 hexa, toto číslo zajišťuje řádek
*deviceExtension ->RingBuff.pHead = 0x77;
v části VisualSys.sys

Výpis programu pro kernel

#define NT_DEVICE_NAME L"\\Device\\Ldunld"
#define DOS_DEVICE_NAME L"\\DosDevices\\LOADTEST"
#define FILE_DEVICE_SIMPLDRV 0x00008300
//------------------------------------------------------
typedef struct _IO_BUFFER {
ULONG QueueTime;
ULONG DequeueTime;
ULONG Data;
}IO_BUFFER, *PIO_BUFFER;

typedef struct _RING_BUFF {
ULONG Size;
PULONG pBase;
PULONG pHead;
PULONG pTail;
}
RING_BUFF, *PRING_BUFF;

typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject;
RING_BUFF RingBuff;
ULONG OpenInstances; // sanity check
}
DEVICE_EXTENSION, *PDEVICE_EXTENSION;
//------------------------------------------------------

NTSTATUS
LdUnldOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);

NTSTATUS
LdUnldClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);

VOID
LdUnldUnload(
IN PDRIVER_OBJECT DriverObject
);

NTSTATUS
LdUnldRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
char *pIoBuff;
ULONG inputBufferLength;
ULONG outputBufferLength;

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation (Irp);
deviceExtension = DeviceObject->DeviceExtension;

KdPrint( ("LDUNLD: Opened!!\n") );

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 555;

IoCompleteRequest( Irp, IO_NO_INCREMENT );
pIoBuff = (char*)Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
*deviceExtension ->RingBuff.pHead = 0x77;
*(pIoBuff+0) = *deviceExtension->RingBuff.pHead;

return STATUS_SUCCESS;
};

NTSTATUS
LdUnldReadDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
return STATUS_SUCCESS;
};

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{

PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_EXTENSION pDevExt;
NTSTATUS status;
UNICODE_STRING uniNtNameString;
UNICODE_STRING uniWin32NameString;

KdPrint( ("LDUNLD: Entered the Load/Unload driver!\n") );


RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME );


status = IoCreateDevice(
DriverObject,
sizeof (DEVICE_EXTENSION),// We don't use a device extension
&uniNtNameString,
FILE_DEVICE_SIMPLDRV,
0, // No standard device characteristics
TRUE, // This isn't an exclusive device
&deviceObject
);


if ( NT_SUCCESS(status) )
{
//---------------22.6.99----------------------------------
deviceObject->Flags |= DO_BUFFERED_IO;
pDevExt = deviceObject->DeviceExtension;
pDevExt->DeviceObject = deviceObject;

pDevExt->OpenInstances = 0;
pDevExt->RingBuff.pHead =
pDevExt->RingBuff.pTail =
pDevExt->RingBuff.pBase = ExAllocatePoolWithTag(
NonPagedPool,
255, // SizeInBytes
'QPRI'
);

DriverObject->MajorFunction[IRP_MJ_CREATE] = LdUnldOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = LdUnldClose;
DriverObject->MajorFunction[IRP_MJ_READ] = LdUnldRead;
DriverObject->DriverUnload = LdUnldUnload;

KdPrint( ("LDUNLD: just about ready!\n") );
RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );
status = IoCreateSymbolicLink( &uniWin32NameString, &uniNtNameString );

if (!NT_SUCCESS(status))
{
KdPrint( ("LDUNLD: Couldn't create the symbolic link\n") );
IoDeleteDevice( DriverObject->DeviceObject );
}
else
{
KdPrint( ("LDUNLD: All initialized!\n") );
}
}
else
KdPrint( ("LDUNLD: Couldn't create the device\n") );
}
return status;
}

NTSTATUS
LdUnldOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
KdPrint( ("LDUNLD: Opened!!\n") );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );

return STATUS_SUCCESS;
}

NTSTATUS
LdUnldClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
KdPrint( ("LDUNLD: Closed!!\n") );
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}

VOID
LdUnldUnload(
IN PDRIVER_OBJECT DriverObject
)
{
UNICODE_STRING uniWin32NameString;

KdPrint( ("LDUNLD: Unloading!!\n") );
RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );
IoDeleteSymbolicLink( &uniWin32NameString );
IoDeleteDevice( DriverObject->DeviceObject );
}



Výpis programu pro aplikační oblast

#include "windows.h"
#include "stdio.h"
/********************Testovaci soft**************************/
#define MAXBUFF 1000
DWORD IoBuff[MAXBUFF];
DWORD dwStatus;
DWORD dwRetries = 0;

LPOVERLAPPED *lpOverlapped;

typedef enum _OPERATION_TYPE {
eRead,
eWrite,
eIoControl,
eMaxOpTypes
} OP_TYPE, *POP_TYPE;

DWORD data;

typedef struct _IO_BUFFER {
ULONG QueueTime;
ULONG DequeueTime;
ULONG Data;
} IO_BUFFER, *PIO_BUFFER;

//----------------------------------------------
VOID WINAPI
ReadCompleteApc(
ULONG ulError,
ULONG ulTransfered,
LPOVERLAPPED lpOvl
)
{
//--------Jen Testovani--------
printf( "Test CompleteApc" );
//-----------------------------

return;
};
//----------------------------------------------
DWORD
SLEEPEX(
DWORD dT,
int n )
{
DWORD dwStatus;

switch ( dwStatus = SleepEx( dT, TRUE) ) {
case STATUS_WAIT_0:
break;

case WAIT_IO_COMPLETION:
#ifdef DBG_SLEEP
printf("SleepEx returned: WAIT_IO_COMPLETION\n" );
#endif
break;

default:
printf("Unhandled SleepEx: %d\n", dwStatus);
break;
}
return dwStatus;
};

/**************************************************************/

int __cdecl main(int argc, char **argv)
{
char *buffer;


HANDLE hTest = 0;
hTest = CreateFile(
"\\\\.\\LOADTEST",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);

if ( hTest != ((HANDLE)-1))
{
printf("Wow - it really worked!!!\n");

lpOverlapped = (LPOVERLAPPED) malloc( sizeof(LPOVERLAPPED));
buffer = (char*) malloc( 255 );
dwStatus = ReadFileEx(
hTest,
buffer,
255,
lpOverlapped,
ReadCompleteApc
);
if ( !dwStatus )
{
dwStatus = GetLastError();
printf("ReadFileEx error: %d\n", dwStatus );
if( ERROR_INVALID_FUNCTION == dwStatus )
printf("ReadFileEx error=Incorrect function");
if( ERROR_IO_DEVICE == dwStatus )
{
if ( ++dwRetries < 12 ) {
printf("ReadFileEx => _Retry : %d\n", dwRetries );
SLEEPEX( 1000, eRead );
}
else
{
printf("ReadFileEx => MAX_READ_RETRIES %d\n", dwRetries );
return dwStatus;
};
};
};
if( data==555 )
{
printf("??? data naplnena Irp->IoStatus.Information = 555 v ldunld.sys");
}
CloseHandle(hTest);

}
else
{
printf("Can't get a handle to LOADTEST\n");
};
return 1;
}


New 6.3.2004    Driver pro simulaci tiskárny

Potřeboval jsem simulovat tiskárnu pomocí PCčka, použil jsem k tomu notebook s Windows XP Prof. Vlastní sys driver je zkompilován pomocí DDK pro Windows NT4 .

 

Aplikační část má klasickou konstrukci pro přístup k portům pomocí CreateFile. Je potřeba řící ,že do bodu DriverEntry ,se vstupuje porestartu systému a zavádění ovládačů , nikoli při volání CreateFile .

hTest = CreateFile

(
"\\\\.\\Interrupt",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);

 
 
Pomocí WriteFile je zapsáno řídící slovo do Control Byte LPT1, v našem případě je jeho adresa 0x378+2 a zapisovaná 
hodnota je 0x32 , což znamená :
		povolení interruptu od ACK, (musí být povoleno ve Windows, properties portu LPT1 v Device Manager)
		povolení obousměnrného portu (musí být navoleno v Biosu Bidirectional)
		inicializace BUSY a ACK
Volá se InterruptWrite v driveru.
		char ctrl = *buffer		= LPTCTRL2;
		buffer[1]				= delayBUSY;
		lpOverlapped->hEvent		= 0;
		lpOverlapped->Internal		= 0;
		lpOverlapped->InternalHigh	         = 0;
		lpOverlapped->Offset		= 0;
		lpOverlapped->OffsetHigh	         = 0;
		dwStatus = WriteFile(hTest, buffer, nBytesToRead, &nBytesRead, lpOverlapped) ; 
 
 
Pro načtení dat je použit ReadFileEx a musí být v samostatném vlákně , protože (pro jednoduchost) 
dokud není nic načteno driver čeká ve funkci InterruptRead , kde je nekonečná smyčka
 	    	  		for(;;)
  	    	  		{
  	    	    	  		if(indxBuffer>0x10)
  	    	    	  		break;
	  	    	   	}
přerušená dokud není bufer naplněn zvoleným počtem znaků z LPT1 v našem případě 0x10.
		memset(buffer,0,APPMAXBYTE );
		printf( "Celkem %d Read\n",kolikRead );
		kolikRead++;
		lpOverlapped->hEvent	= 0;
		lpOverlapped->Internal	= 0;
		lpOverlapped->InternalHigh	= 0;
		lpOverlapped->Offset	= 0;
		lpOverlapped->OffsetHigh	= 0;

		printf("Waiting to ACK low\n");
		dwStatus = ReadFileEx
				( 
					hTest,
					buffer,
					nBytesToRead,
					lpOverlapped,
					ReadCompleteApc
				);
	
Velikost bufru, který má driver k dipozici je v ExAllocatePoolWithTag(
								         NonPagedPool,
								         MAXBYTE, // SizeInBytes	
								         'QPRI'
							             );
umístěného v DriverEntry a dán MAXBYTE.
 
 
Pro úplnost ještě kompletní výpis driveru ve funkční ale zjednodušené verzi.
#include <ntddk.h>

#define PortAddress 0x378
#define CONTROL1    PortAddress+1
#define CONTROL    PortAddress+2
#define CTRLBYTE2   0x32
#define MAXBYTE    255
#define outportb(ADDR,BYTE) outp(ADDR,BYTE)
#define inportb(ADDR)       inp(ADDR)
#define FILE_DEVICE_SIMPLDRV 0x00008300

char LPYBuffe[1000];
int interrOK   =1;
int indxBuffer =0;
char ctrlByte   =0;
int prvniIRQ   =0;
char delayBUSY  =1;

typedef struct _LOCAL_DEVICE_INFO
{
    PKINTERRUPT InterruptObject;
    ULONG      Level;     
    ULONG      Vector;    
    KAFFINITY   Affinity;  
}
LOCAL_DEVICE_INFO, *PLOCAL_DEVICE_INFO;

//------------------------------------------------------
typedef struct _IO_BUFFER {
                      ULONG QueueTime;
                      ULONG DequeueTime;
                      ULONG Data;
                       }
                       IO_BUFFER, *PIO_BUFFER;

typedef struct _RING_BUFF{
                      ULONG  Size;
                      PULONG pBase;
                      PULONG pHead;
                      PULONG pTail;
                       }
                       RING_BUFF, *PRING_BUFF;

typedef struct _DEVICE_EXTENSION{
                            PDEVICE_OBJECT DeviceObject;
                             RING_BUFF      RingBuff;
                            ULONG          OpenInstances; // sanity check
                             }
                             DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//------------------------------------------------------
NTSTATUS InterruptReadDevice
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
return STATUS_SUCCESS;
};
//-------------------------------------------------
NTSTATUS InterruptRead
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PDEVICE_EXTENSION deviceExtension;
    PIO_STACK_LOCATION irpStack;
    char            *pIoBuff;
    int            i;
    unsigned long     inputBufferLength;
    unsigned long      outputBufferLength;

    KdPrint( ("Interrupt Read!!\n") );

    irpStack        = IoGetCurrentIrpStackLocation (Irp);
    inputBufferLength= irpStack->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength= irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    deviceExtension  = DeviceObject->DeviceExtension;
    Irp->IoStatus.Status= STATUS_SUCCESS;
    pIoBuff         = (char*) Irp->AssociatedIrp.SystemBuffer;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    if( pIoBuff!=0 )
    {
        for(;;)
        {
            if(indxBuffer>0x10)
            break;
         }

        *(pIoBuff+0) = inportb(0x378);
        *(pIoBuff+1) = inportb(CONTROL1 );
        *(pIoBuff+2) = inportb(CONTROL );
        *(pIoBuff+3) = delayBUSY;
        *(pIoBuff+4) = (char)indxBuffer;

        if( indxBuffer>0 )
        {
            for( i=5;i<MAXBYTE-1;i++)
            {
                *(pIoBuff+i) = LPYBuffe[i-5];
            }
            Irp->IoStatus.Information = indxBuffer + 5;
        }
        else
            Irp->IoStatus.Information = 4;
    }
    else
        Irp->IoStatus.Information = 10001;

    return STATUS_SUCCESS;
};
//-------------------------------------------------
NTSTATUS InterruptWrite
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PDEVICE_EXTENSION  deviceExtension;
    PIO_STACK_LOCATION irpStack;
    char            *pIoBuff;
    int            i;
    unsigned long      inputBufferLength;
    unsigned long      outputBufferLength;

    KdPrint( ("Interrupt Write!!\n") );

    irpStack        = IoGetCurrentIrpStackLocation (Irp);
    inputBufferLength= irpStack->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength= irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    deviceExtension  = DeviceObject->DeviceExtension;
    Irp->IoStatus.Status= STATUS_SUCCESS;
    pIoBuff         = (char*) Irp->AssociatedIrp.SystemBuffer;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );


    if( pIoBuff!=0 )
    {
    ctrlByte = *(pIoBuff+0);
    delayBUSY= *(pIoBuff+1);
    outportb(CONTROL, 0x32 );
    prvniIRQ = 0;
    if( indxBuffer>0 )
    {
        for( i=0;i<MAXBYTE-1;i++)
        {
            if( ctrlByte==0x12 )
            {
                LPYBuffe[i] = 0;
                indxBuffer= 0;
            }
        }
     }
        Irp->IoStatus.Information = outputBufferLength;
    }
    else
    Irp->IoStatus.Information = 10002;

    return STATUS_SUCCESS;
};
//-------------------------------------------------
NTSTATUS InterruptCreateDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}
//-------------------------------------------------
BOOLEAN InterruptIsr(IN PKINTERRUPT Interrupt, IN OUT PVOID Context)
{
    PDEVICE_OBJECT DeviceObject = Context;
    int iD;

    interrOK++; //funguje
    //--------- Toto je prvni IRQ ---------------
    outportb(CONTROL, inportb(CONTROL) & 0xFD );// Set BUSY
    outportb(CONTROL, inportb(CONTROL) & 0xFE );// Set ACK
    if( indxBuffer<MAXBYTE )
    {
        //for( iD=0;iD<10000; ) iD++;
        *(LPYBuffe+indxBuffer) = inportb(0x378);
        indxBuffer++;
    }
    else
        indxBuffer = 0;
    //--------------------------------------------*/
    IoRequestDpc( DeviceObject,
              DeviceObject->CurrentIrp,
               NULL);//*/

    return TRUE;
}
//-------------------------------------------------
VOID InterruptDpcRoutine(IN PKDPC Dpc, PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
    char *pIoBuff;
    PLOCAL_DEVICE_INFO DeviceExtension;
    PIRP pIrp;
    int iD;

    pIrp = DeviceObject->CurrentIrp;
    DeviceExtension = DeviceObject->DeviceExtension;

    //interrOK++; //funguje
    //--------- Toto je druhy IRQ ---------------
    outportb(CONTROL, inportb(CONTROL) | 0x01 );// Res ACK
    for(iD=0;iD<(delayBUSY * 100);) iD++;
    outportb(CONTROL, inportb(CONTROL) | 0x02 );// Res BUSY
    for(iD=0;iD<(delayBUSY * 100);) iD++;
    outportb(CONTROL, inportb(CONTROL) & 0xFE );// Set ACK
    prvniIRQ = 1;
    //------------------------------------------
    return;
}
//-------------------------------------------------
VOID InterruptUnload(IN PDRIVER_OBJECT DriverObject)
{
    WCHAR DOSNameBuffer[] = L"\\DosDevices\\Interrupt";
    UNICODE_STRING uniDOSString;
    PLOCAL_DEVICE_INFO extension = DriverObject->DeviceObject->DeviceExtension;


    /* Disable Parallel Port IRQ's */
    outportb(CONTROL, inportb(CONTROL) & 0xEF);

    /* Disconnect Interrupt */
    IoDisconnectInterrupt(extension->InterruptObject);

    /* Delete Symbolic Link */
    RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
    IoDeleteSymbolicLink (&uniDOSString);

    /* Delete Device */
    IoDeleteDevice(DriverObject->DeviceObject);
}
//-------------------------------------------------
NTSTATUS DriverEntry
(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
    )
{
    PDEVICE_OBJECT     DeviceObject;
    NTSTATUS         status;
    PLOCAL_DEVICE_INFO DeviceExtension;
    PDEVICE_EXTENSION pDevExt;

    ULONG             MappedVector;
    KIRQL             Irql;

    WCHAR NameBuffer[]    = L"\\Device\\Interrupt";
    WCHAR DOSNameBuffer[] = L"\\DosDevices\\Interrupt";
    UNICODE_STRING uniNameString, uniDOSString;

    KdPrint( ("DriverEntry") );

    RtlInitUnicodeString(&uniNameString, NameBuffer);
    RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);

    DriverObject->MajorFunction[IRP_MJ_CREATE] = InterruptCreateDispatch;
    DriverObject->MajorFunction[IRP_MJ_READ] = InterruptRead;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = InterruptWrite;
    DriverObject->DriverUnload = InterruptUnload;

    status = IoCreateDevice
                        (
                        DriverObject,             // DriverObject
                        sizeof(LOCAL_DEVICE_INFO), // DeviceExtensionSize
                        &uniNameString,           // DeviceName
                        FILE_DEVICE_UNKNOWN,       // DeviceType
                        0,                       // DeviceCharacteristics
                        TRUE,                    // Exclusive
                        &DeviceObject            // *DeviceObject
                        );

if(!NT_SUCCESS(status))
      return 996;

    //------------------ Pro ...Read --------------------------
    DeviceObject->Flags |=DO_BUFFERED_IO;
    pDevExt              = DeviceObject->DeviceExtension;
    pDevExt->DeviceObject  = DeviceObject;
    pDevExt->OpenInstances = 0;
    pDevExt->RingBuff.pHead = pDevExt->RingBuff.pTail
                         = pDevExt->RingBuff.pBase
                         = ExAllocatePoolWithTag(
                                                  NonPagedPool,
                                                  MAXBYTE, // SizeInBytes
                                                  'QPRI'
                                       );
    //------------------ pro Interrupt ------------------------
    DeviceExtension = DeviceObject->DeviceExtension;
    status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);

    if (!NT_SUCCESS(status))
    return 997;

    DeviceExtension->Level = 7;
    DeviceExtension->Vector = DeviceExtension->Level;

    MappedVector = HalGetInterruptVector(
Isa,
0,
DeviceExtension->Level,
DeviceExtension->Vector,
&Irql,
&DeviceExtension->Affinity
);

    if (MappedVector == 0)
    {
        DbgPrint("HalGetInterruptVector failed\n");
        return 998;
    }

    IoInitializeDpcRequest(DeviceObject,InterruptDpcRoutine);

    status = IoConnectInterrupt(
                               &DeviceExtension->InterruptObject, // InterruptObject
                            InterruptIsr,                 // ServiceRoutine
                            DeviceObject,                 // ServiceContext
                            NULL,                       // SpinLock
                            MappedVector,                 // Vector
                            Irql,                       // Irql
                            Irql,                       // SynchronizeIrql
                            Latched,                    // InterruptMode
                            FALSE,                      // ShareVector
                            DeviceExtension->Affinity,       // ProcessorEnableMask
                            FALSE                      // FloatingSave
                            );

    if (!NT_SUCCESS (status))
    {
        DbgPrint("Interrupt.sys: IoConnectInterrupt Failed\n");
        return 999;
    }
    outportb(CONTROL, CTRLBYTE2 );

    return STATUS_SUCCESS;
}
//-------------------------------------------------

 


New 13.3.2004 Vteřinový časovač pro timeout  

Protože je potřeba opustit driver, když uplyne nějaký čas, je vhodné zavést do ovládače časovač. Použil jsem k tomu účelu vteřinový timer . Jeho inicializace je v následujícím listingu a je umístěna ve funkci DriverEntry, TimerRoutine je bod který obsluhuje vteřinové přerušení od časovače.

	status = IoInitializeTimer	( DeviceObject,TimerRoutine,Context );
	IoStartTimer( DeviceObject );

  TimerRoutine je bod který obsluhuje vteřinové přerušení od časovače.

//---------------------- Vlastn?si vte?nov�egrave;asovaè volᠭ-----------------------
VOID TimerRoutine  
(
        IN PDEVICE_OBJECT DeviceObject,
        IN PVOID Context
) 
{
	if( startTime>0 )
	{
		if( delayTiTe>delayTime )
		{
			delayTiTe		= 0;
			SkutecnyPocet	= 0;
			startTime		= 0;
		}
		delayTiTe++;
	}
}
//-------------------------------------------------

 Pokud potřebujeme jiné časové tiky použijeme KeSetTimer ale to příště.