Alkalmazások és operációs rendszerek optimizálása „Babeş-Bolyai” Tudományegyetem, Matematika-Informatika Kar Windows NT driverek fejlesztése: Egyszerű driverek készítése előadás dr. Robu Judit szeminárium drd. Lukács Sándor
BBTE, Alkalmazások és operációs rendszerek optimizálása 2 Driver objektumok (DRIVER_OBJECT) typedef struct _DRIVER_OBJECT { // The following links all of the devices created by a single driver together on a // list, and the Flags word provides an extensible flag location for driver objects. PDEVICE_OBJECT DeviceObject; // de DEVICE objektum lista első eleme ULONG Flags; // The following section describes where the driver is loaded. PVOID DriverStart; // a driver bináris képének kezdőcíme ULONG DriverSize; PVOID DriverSection;... // The driver name field is used by the error log thread // determine the name of the driver that an I/O request is/was bound. UNICODE_STRING DriverName;... // The following section describes the entry points to this particular driver. PDRIVER_INITIALIZE DriverInit; // DriverEntry kezdőcíme PDRIVER_STARTIO DriverStartIo; // StartIo kezdőcíme (hardver driverek használják) PDRIVER_UNLOAD DriverUnload; // DriverUnload kezdőcíme PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // IRP_MJ_xxx dispatch } DRIVER_OBJECT; // függvények kezdőcímei
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 3 Device objektumok (DEVICE_OBJECT) typedef struct _DEVICE_OBJECT { LONG ReferenceCount; struct _DRIVER_OBJECT *DriverObject; struct _DEVICE_OBJECT *NextDevice;... struct _IRP *CurrentIrp; // csak StartIo rutinnal párhuzamosan használt, // NULL ha nincs aktuális IRP feldolgozás alatt... ULONG Flags; // DO_BUFFERED_IO, DO_DIRECT_IO, - I/O típus DO_BUS_ENUMERATED_DEVICE, - PDO DO_DEVICE_INITIALIZING,... - inicializálás aktív ULONG Characteristics; // FILE_DEVICE_IS_MOUNTED, FILE_READ_ONLY_DEVICE, FILE_REMOVABLE_MEDIA, FILE_VIRTUAL_VOLUME, PVOID DeviceExtension; // programozó által definált DEVICE_TYPE DeviceType; // FILE_DEVICE_CD_ROM, FILE_DEVICE_DISK, FILE_DEVICE_FILE_SYSTEM, FILE_DEVICE_KEYBOARD,... CCHAR StackSize; // minimális I/O stack mélysége, tipikusan minden új... // device objektum esetén +1 mint az alatta lévőnek } DEVICE_OBJECT;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 4 Standard I/O művelet feldolgozás (az ábra Walter Oney: Programming the Microsoft Windows Driver Model, Microsoft Press, 1999 alapján készült) DISPATCH COMPLETION
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 5 I/O művelet típusok több I/O típus van állomány alapú / jellegű I/O támogatja a Read / Write / Seek stb. jellegű műveleteket nagyon állományrendszer specifikus device / FS kontroll I/O kérések explicit, eszköz specifikus I/O kérések küldése vannak FS specifikus kérések is további kommunikációs csatornák Filter Manager specifikus kommunikációs portok szinkron / aszinkron támogatás, kétírányú kezdeményezés speciális esetben osztott memória minden driver az elvárásoknak és a támogatott eszköz tulajdonságainak megfelelően támogat egy vagy több típust
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 6 IRP dispatch kérések 1 IRP_MJ_CREATE – ZwCreateFile device objektumok vagy állományok megnyítása egy FILE_OBJECT objektumot inicializál IRP_MJ_CLEANUP – ZwClose, 1 lépés a FILE_OBJECT-re mutató utolsó handle bezárásakor hívódik meg IRP_MJ_CLOSE – ZwClose, 2 lépés az utolsó I/O művelet elvégzése után hívódik meg IRP_MJ_READ – ZwReadFile IRP_MJ_WRITE – ZwWriteFile állományok vagy lemezegységek írása / olvasása IRP_MJ_FLUSH_BUFFERS – ZwFlushBuffersFile egy ilyen kérés esetén egy driver kiűrití az össze átementi cachet IRP_MJ_DIRECTORY_CONTROL – ZwQueryDirectoryFile könyvtár lekérdezések esetén hívódik meg (pl. FindFirst, FindNext)
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 7 IRP dispatch kérések 2 IRP_MJ_QUERY_INFORMATION – ZwQueryInformationFile IRP_MJ_SET_INFORMATION – ZwSetInformationFile állományokkal kapcsolatos információk lekérdezése / beállítása FileBasicInformation – create time, file attributes etc. FileNameInformation – állomány neve FileStandardInformation – állomány mérete, hard linkek száma, könyvtár-e?, az állomány törlödik-e bezárások (delete pending) FilePositionInformation – állomány pozíció IRP_MJ_QUERY_VOLUME_INFORMATION – ZwQueryVolumeInformationFile IRP_MJ_SET_VOLUME_INFORMATION – ZwSetVolumeInformationFile lemezegységekkel kapcsolatos információk lekérdezése / beállítása FileFsDeviceInformation – CD-ROM, HDD, floppy, read-only stb. FileFsFullSizeInformation – a lemezegység mérete, szabad blokkok stb. FileFsVolumeInformation – lemezegyés neve, serial number stb. IRP_MJ_FILE_SYSTEM_CONTROL – ZwFsControlFile IRP_MJ_DEVICE_CONTROL – ZwDeviceIoControlFile FSCTL és IOCTL kérések végrehajtása speciális kérések/parancsok a hardver egységek és FS driverek számára (pl. IOCTL_CDROM_DISC_INFO, IOCTL_CDROM_GET_DRIVE_GEOMETRY, IOCTL_CDROM_RAW_READ, FSCTL_DISMOUNT_VOLUME, FSCTL_SET_SPARSE, FSCTL_GET_VOLUME_BITMAP, FSCTL_MOVE_FILE etc.)
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 8 Driver, device és I/O stack függőség (az ábra Walter Oney: Programming the Microsoft Windows Driver Model, Microsoft Press, 1999 alapján készült) DISPATCH COMPLETION
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 9 IRP paraméterek 1 typedef struct _IO_STACK_LOCATION { UCHAR MajorFunction; UCHAR MinorFunction; UCHAR Flags; UCHAR Control; union { // System service parameters for: NtCreateFile struct { PIO_SECURITY_CONTEXT SecurityContext; ULONG Options; USHORT POINTER_ALIGNMENT FileAttributes; USHORT ShareAccess; ULONG POINTER_ALIGNMENT EaLength; } Create; // System service parameters for: NtReadFile struct { ULONG Length; ULONG POINTER_ALIGNMENT Key; LARGE_INTEGER ByteOffset; } Read ;... } Parameters;...
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 10 IRP paraméterek 2 // Save a pointer to this device driver's device object for this request // so it can be passed to the completion routine if needed. PDEVICE_OBJECT DeviceObject; // The following location contains a pointer to the file object for this request. PFILE_OBJECT FileObject; // The following routine is invoked depending on the flags in the above flags field. PIO_COMPLETION_ROUTINE CompletionRoutine; // The following is used to store the address of the context parameter // that should be passed to the CompletionRoutine. PVOID Context; } IO_STACK_LOCATION, *PIO_STACK_LOCATION; egy IRP feldolgozása során minden driver számára le van foglalva egy IRPSP struktúra irpSp = IoGetCurrentIrpStackLocation(Irp) – a jelenlegi driver szintjén lévő IRPSP struktúra címe
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 11 I/O bufferek kezelése egy I/O kérés végrehajtársa különböző processz kontextusokban történhet pl. egy READ/WRITE kérés végrehajtása a hívó fél (gyakran user módbeli alkalmazás) kontextusában kezdődig, majd miután a hardver végrehajtotta a kérést és egy megaszkítást generál, egy tetszőleges rendszer kontextusban folyatódik és fejeződik be a legtöbb esetben a memória mutatók, bufferek stb. NEM használhatók tetszőleges kontextusban három alapvető I/O buffer kezelési módszer Buffered I/O – az I/O manager elkészít egy, a hívó bufferrel azonos méretű NonPaged rendszer buffert és ebbe másolja az adatokat átmenetileg READ esetén a completion rutinban: kernel nonpaged alkalmazás WRITE esetén a dispatch turinban: alkalmazás kernel nonpaged Direct I/O – az I/O manager rögzíti a memóriába az alkalmazás bufferének megfelelő fizikai lapokat, elkészít egy, ezeket a lapokat leíró MDL-t, majd ezt az MDL-t továbbítja a drivernek amely tetszőlegesen betűkrözi a lapokat a rendszer virtuális címterületére (új laptábla bemenetekkel) Neither I/O – az I/O manager nem végez semmilyen buffer és cím átalakítást, közvetlenül a hívó buffer eredeti címét adja át a drivernek
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 12 Buffered I/O ha DEVICE_OBJECT Flags mezője tartalmazza a DO_BUFFERED_IO-t ha az IOCTL kód buffered I/O-t kér
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 13 Direct I/O, PIO feldolgozással ha DEVICE_OBJECT Flags mezője tartalmazza a DO_DIRECT_IO-t ha az IOCTL kód buffered I/O-t kér PIO – programmed I/O
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 14 Direct I/O, DMA feldolgozással ha DEVICE_OBJECT Flags mezője tartalmazza a DO_DIRECT_IO-t ha az IOCTL kód buffered I/O-t kér DMA – direct memory access
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 15 IRP_MJ_CREATE amikor egy új állományt vagy könyvtárat hozunk létre, vagy egy már létező állományt, könyvtárat, partíciót vagy eszközt akarunk megnyítni paraméterek: DeviceObject – a device amelyen a műveletet végre kell hajtani Options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; Disposition = (IrpSp->Parameters.Create.Options >> 24) & 0xFF; CREATE_NEW, OPEN_EXISTING, CREATE_ALWAYS,... IrpSp->Parameters.Create.FileAttributes IrpSp->Parameters.Create.ShareAccess IrpSp->FileObject->FileName IrpSp->FileObject->RelatedFileObject – ha nem abszolút path-el dolgozunk IrpSp->FileObject->Flags FO_SYNCHRONOUS_IO, FO_NO_INTERMEDIATE_BUFFERING kimenet: IrpSp->FileObject->FsContext, IrpSp->FileObject->FsContext2 FS driver specifikus, bármilyen saját információ vagy pointer Irp->IoStatus.Status STATUS_SUCCESS, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED Irp->IoStatus.Information FILE_OPENED, FILE_DOES_NOT_EXIST
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 16 IRP_MJ_CLEANUP amikor egy file objektum handle referencia számlálója nullára vált (az utolsó handelt is bezárátk) paraméterek: DeviceObject – a device amelyen a műveletet végre kell hajtani IrpSp->FileObject az IrpSp->FileObject->FileName NEM érvényes IrpSp->FileObject->FsContext, IrpSp->FileObject->FsContext2 kimenet: IrpSp->FileObject->Flags |= FO_CLEANUP_COMPLETE ezt mindig el kell végezni a cleanup végén Irp->IoStatus.Status STATUS_SUCCESS Irp->IoStatus.Information 0
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 17 IRP_MJ_CLOSE amikor egy file objektum referencia számlálója nullára vált (már egyetlen pointer vagy handel sem mutat a file objektumra) paraméterek: DeviceObject – a device amelyen a műveletet végre kell hajtani IrpSp->FileObject az IrpSp->FileObject->FileName NEM érvényes IrpSp->FileObject->FsContext, IrpSp->FileObject->FsContext2 kimenet: fel kell szabadítani minden file specifikus erőforrást ezt mindig el kell végezni a close végén Irp->IoStatus.Status STATUS_SUCCESS Irp->IoStatus.Information 0
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 18 IRP_MJ_READ több aleset van, ezek közül az IRP_MN_xxx kódokkal teszünk különbséget IRP_MN_NORMAL – klasszikus olvasás paraméterek: DeviceObject – a device amelyen a műveletet végre kell hajtani IrpSp->FileObject->FsContext, IrpSp->FileObject->FsContext2 IrpSp->Parameters.Read.ByteOffset.QuadPart; IrpSp->Parameters.Read.Length; Irp->AssociatedIrp.SystemBuffer – BUFFERED I/O esetén Irp->MdlAddress – DIRECT I/O esetén Irp->UserBuffer – NEITHER I/O esetén kimenet: Irp->IoStatus.Status Irp->IoStatus.Information = BytesReaded; sikeres végrehajtás és FO_SYNCHRONOUS_IO kérés esetén mindig updatelni kell az aktuális file pozíciót IrpSp->FileObject->CurrentByteOffset.QuadPart
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 19 IRP_MJ_DEVICE_CONTROL a legtöbb esetben egy felhasználói alkalmazás vagy egy 3rd party kernel komponens IOCTL kérése estén küldődik paramétrek: DeviceObject – a device amelyen a műveletet végre kell hajtani Irp->AssociatedIrp.SystemBuffer – BUFFERED I/O esetén Irp->MdlAddress – DIRECT I/O esetén Irp->UserBuffer – NEITHER I/O esetén IrpSp->Parameters.DeviceIoControl.InputBufferLength IrpSp->Parameters.DeviceIoControl.IoControlCode IrpSp->Parameters.DeviceIoControl.OutputBufferLength kimenet: Irp->IoStatus.Status Irp->IoStatus.Information – kimeneti adatok mérete
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 20 Egy „összeadó driver” implementálása minimális driver, amely... létrehoz egy device objektumot ezt elérhetővé teszi a user mód számára is szimbolikus link által megengedi a device objektum megnyítását (CREATE), azért, hogy lehessen kéreseket küldeni a driver fele egy handel-nek a segítségével természetesen szükséges CLEANUP és CLOSE is a device specifikus I/O kommunikációt felhasználva (IOCTL) elfogad és végrehajt egy speciális kérést két bemenő egész értéket összead, és az eredményt egy kimeneti értékbe helyezi az adatok átadását BUFFERED I/O módszerrel oldjuk meg
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 21 Általános definíciók // driver and device name #define DRIVER_NAME "sumdriver.sys" #define DEVICE_NAME L"\\sumdriver" #define DOS_DEVICE_NAME L"\\dosdevices\\sumdevice" // driver specific I/O structures typedef struct _IOCTL_SUM { __int32 a; __int32 b; __int32 c; } IOCTL_SUM, *PIOCTL_SUM; // driver global data extern DRV_GLOBAL_DATA DrvGlobalData; // IOCTL codes #define IOCTL_DRV_SUM_TWO_INTS \ (ULONG)CTL_CODE( FILE_DEVICE_UNKNOWN, 0x0981, METHOD_BUFFERED, FILE_ANY_ACCESS) typedef struct _DRV_GLOBAL_DATA { PDRIVER_OBJECT DriverObject; PDEVICE_OBJECT DeviceObject; UNICODE_STRING DeviceName; UNICODE_STRING DosDeviceName; } DRV_GLOBAL_DATA, *PDRV_GLOBAL_DATA;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 22 DriverEntry 1 NTSTATUS DriverEntry( __in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath )... // clear global data area RtlZeroMemory(&DrvGlobalData, sizeof(DRV_GLOBAL_DATA)); // get DriverObject into global data DrvGlobalData.DriverObject = DriverObject; // setup unload routine DrvGlobalData.DriverObject->DriverUnload = DriverUnload; // initialize the dispatch entry points DriverObject->MajorFunction[IRP_MJ_CREATE] = Drv_MJ_CREATE; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = Drv_MJ_CLEANUP; DriverObject->MajorFunction[IRP_MJ_CLOSE] = Drv_MJ_CLOSE; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Drv_MJ_DEVICE_CONTROL;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 23 DriverEntry 2 // create the main device object RtlInitUnicodeString(&(DrvGlobalData.DeviceName), DEVICE_NAME); status = IoCreateDevice(DriverObject, 0, &DrvGlobalData.DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DrvGlobalData.DeviceObject); if (!NT_SUCCESS(status)) { ExKdPrint((" DriverEntry - error loading driver\n")); return status; } // clear DO_DEVICE_INITIALIZING DrvGlobalData.DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; // create symbolic link RtlInitUnicodeString(&(DrvGlobalData.DosDeviceName), DOS_DEVICE_NAME); IoCreateSymbolicLink(&(DrvGlobalData.DosDeviceName), &(DrvGlobalData.DeviceName)); return status;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 24 DriverUnload VOID DriverUnload(__in PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); ExKdPrint(("try to unload driver\n")); // break into kernel debugger if enabled // DbgBreakPointWithStatus(0x ); // delete symbolic link IoDeleteSymbolicLink(&(DrvGlobalData.DosDeviceName)); // delete main device object IoDeleteDevice(DrvGlobalData.DeviceObject); ExKdPrint(("...GAME OVER!\n")); }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 25 Drv_MJ_xxx váz CREATE, CLEANUP, CLOSE, DEVICE_CONTROL számára NTSTATUS Drv_MJ_xxx(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { NTSTATUS status; PIO_STACK_LOCATION irpSp; status = STATUS_UNSUCCESSFUL; irpSp = IoGetCurrentIrpStackLocation(Irp); __try { //... ide kerul az IRP MJ specifikus kód status = STATUS_SUCCESS; // csak ha sikeres volt a végrehajtás } __except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); ExKdPrint(("EXCEPTION - %d - %s - %d\n", status, __FILE__, __LINE__)); } __finally { Irp->IoStatus.Status = status; // végső stáruszkód beállítása IoCompleteRequest(Irp, IO_NO_INCREMENT); // ezzel bezárul a dispatch fázis } // és elkezdődik a completion fázis return status; }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 26 Drv_MJ_xxx kódok 1 Drv_MJ_CREATE // request to open the device? if (DeviceObject == DrvGlobalData.DeviceObject) { Irp->IoStatus.Information = FILE_OPENED; status = STATUS_SUCCESS; } else { Irp->IoStatus.Information = FILE_DOES_NOT_EXIST; status = STATUS_OBJECT_NAME_NOT_FOUND; } Drv_MJ_CLEANUP // signal on FOB that we have done our work fileObject = irpSp->FileObject; fileObject->Flags |= FO_CLEANUP_COMPLETE; status = STATUS_SUCCESS;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 27 Drv_MJ_xxx kódok 2 Drv_MJ_CLOSE status = STATUS_SUCCESS; Drv_MJ_DEVICE_CONTROL ioctlCode = irpSp->Parameters.DeviceIoControl.IoControlCode; switch (ioctlCode) { case IOCTL_DRV_SUM_TWO_INTS: { status = Drv_IOCTL_DRV_SUM_TWO_INTS(Irp, irpSp); break; } default: { ExKdPrint(("unhandled IOCTL = 0x%08x\n", ioctlCode)); status = STATUS_INVALID_PARAMETER; }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 28 Drv_IOCTL_DRV_SUM_TWO_INTS NTSTATUS Drv_IOCTL_DRV_SUM_TWO_INTS( __in PIRP Irp, __in PIO_STACK_LOCATION IrpSp) { NTSTATUS status; PIOCTL_SUM sum; UNREFERENCED_PARAMETER(IrpSp); ExKdPrint(("Drv_IOCTL_DRV_SUM_TWO_INTS\n")); status = STATUS_UNSUCCESSFUL; // get user buffer pointer sum = (PIOCTL_SUM)(Irp->AssociatedIrp.SystemBuffer); // calculate sum and set return data length and status sum->c = sum->a + sum->b; Irp->IoStatus.Information = sizeof(IOCTL_SUM); status = STATUS_SUCCESS; return status; }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 29 User módbeli vezérlő alkalmazás 1 #define FILE_DEVICE_UNKNOWN 0x #define METHOD_BUFFERED 0 #define FILE_ANY_ACCESS 0 #define FILE_READ_ACCESS (0x0001) // file & pipe #define FILE_WRITE_ACCESS (0x0002) // file & pipe #define CTL_CODE(DeviceType, Function, Method, Access) \ (((DeviceType)<<16)|((Access)<<14)|((Function)<<2)|(Method)) #define IOCTL_DRV_SUM_TWO_INTS \ (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0981, \ METHOD_BUFFERED, FILE_ANY_ACCESS) typedef struct _IOCTL_SUM // driver specifikus adat struktúra amely { // segítségével történik a kommunikáció a __int32 a; // driver és az alkalmazás között __int32 b; // a kérés és válasz számára lehet __int32 c; // két különböző struktúrát is használni } IOCTL_SUM, *PIOCTL_SUM;
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 30 User módbeli vezérlő alkalmazás 2 // global kernel device handle HANDLE DevHandle;... int main(void) { __int32 a,b,c; DevHandle = CreateFile(L"\\\\?\\sumdevice", 0, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (DevHandle == INVALID_HANDLE_VALUE) { printf("invalid handle, can't open device\n"); return -1; } a = 10; b = 20; SumTwoInts(a, b, &c); printf("%d + %d = %d\n", a, b, c); CloseHandle(DevHandle); return 0; }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 31 User módbeli vezérlő alkalmazás 3 int SumTwoInts(__int32 a, __int32 b, __int32 *c) { IOCTL_SUM inout; // adatstruktúra, amelyet a kommunikációra felhasználunk // ez mindig azonosan kell legyen deklarálva a driver és az inout.a = a; // alkalmazás kódjában inout.b = b; if (DeviceIoControl(DevHandle, IOCTL_DRV_SUM_TWO_INTS, &inout, sizeof(IOCTL_SUM), &inout, sizeof(IOCTL_SUM), &b, NULL)) { // itt lehet a BE- és KI-menetnek különbőző buffert megadni *c = inout.c; return 1; } else { printf("last error: %d\n", GetLastError()); return 0; }
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 32 Egy I/O kérés végrehajtása
2006BBTE, Alkalmazások és operációs rendszerek optimizálása 33 Köszönöm a figyelmet!