天天看點

驅動 API

(1)IoGetDeviceObjectPointer:

The IoGetDeviceObjectPointer routine returns a pointer to the top object in the named device object's stack and a pointer to the corresponding file object, if the requested access to the objects can be granted.

例程:

PDEVICE_OBJECT  ccpOpenCom(ULONG id,NTSTATUS *status)

{

UNICODE_STRING name_str;

static WCHAR name[32]={0};

PFILE_OBJECT fileobj=NULL;

PDEVICE_OBJECT devobj = NULL;

memset(name,0,sizeof(WCHAR)*32);

RtlStringCchPrintfw(name,32,L\\Device\\Serial%d,id);

RtlInitUnicodeString(&name_str,name);

//打開裝置對象

*status = IOGetDeviceObjectPointer(&name_str,FILE_ALL_Access,&fileobj,&devobj);

如果打開成功了。記得一定要将檔案對象解除引用

if(*status==STATUS_SUCCESS)

obDereferenceObject(fileobj);

return devobj;

}

(2)IoCreateDevice:IoAttachDeviceToDeviceStack:

Call IoCreateDevice to create a filter device object to attach to a volume or file system stack. In the FileSpy sample, this is done as follows:

status = IoCreateDevice(

        gFileSpyDriverObject,                 //DriverObject

        sizeof(FILESPY_DEVICE_EXTENSION),     //DeviceExtensionSize

        NULL,                                 //DeviceName

        DeviceObject->DeviceType,             //DeviceType

        0,                                    //DeviceCharacteristics

        FALSE,                                //Exclusive

        &newDeviceObject);                    //DeviceObject

IoAttachDeviceToDeviceStack:

The IoAttachDeviceToDeviceStack routine attaches the caller's device object to the highest device object in the chain and returns a pointer to the previously highest device object.

// 生成裝置,在綁定一個裝置前。應該把這個裝置對象的多個子域設定成和要綁定的目标對象一緻。包括标志和特性。

NTSTATUS

ccpAttachDevice(

    PDRIVER_OBJECT driver,

    PDEVICE_OBJECT oldobj,

    PDEVICE_OBJECT *fltobj,

    PDEVICE_OBJECT *next)

NTSTATUS status;

 PDEVICE_OBJECT topdev = NULL;

 // 生成裝置,然後綁定之。

 status = IoCreateDevice(driver,

       0,

       NULL,

       oldobj->DeviceType,

       FALSE,

       fltobj);

 if (status != STATUS_SUCCESS)

  return status;

// 拷貝重要标志位。

 if(oldobj->Flags & DO_BUFFERED_IO)

  (*fltobj)->Flags |= DO_BUFFERED_IO;

 if(oldobj->Flags & DO_DIRECT_IO)

  (*fltobj)->Flags |= DO_DIRECT_IO;

 if(oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN)

  (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN;

 (*fltobj)->Flags |=  DO_POWER_PAGABLE;

// 綁定一個裝置到另一個裝置上

 topdev = IoAttachDeviceToDeviceStack(*fltobj,oldobj);

if (topdev == NULL)

 {

  // 如果綁定失敗了,銷毀裝置,重新來過。

  IoDeleteDevice(*fltobj);

  *fltobj = NULL;

  status = STATUS_UNSUCCESSFUL;

 }

 *next = topdev;

 // 設定這個裝置已經啟動。

 (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING;

 return STATUS_SUCCESS;

(3)IoDetachDevice

The IoDetachDevice routine releases an attachment between the caller's device object and a lower driver's device object

解除綁定

(4)ObDereferenceObject

The ObDereferenceObject routine decrements the given object's reference count and performs retention checks.

(5)KeDelayExecutionThread

The KeDelayExecutionThread routine puts the current thread into an alertable or nonalertable wait state for a given interval.

NTSTATUS 

  KeDelayExecutionThread(

    IN KPROCESSOR_MODE  WaitMode,

    IN BOOLEAN  Alertable,

    IN PLARGE_INTEGER  Interval

    );

ULONG i;

 LARGE_INTEGER interval;

 // 首先解除綁定

 for(i=0;i<CCP_MAX_COM_ID;i++)

  if(s_nextobj[i] != NULL)

   IoDetachDevice(s_nextobj[i]);

 // 睡眠5秒。等待所有irp處理結束

 interval.QuadPart = (5*1000 * DELAY_ONE_MILLISECOND);  

 KeDelayExecutionThread(KernelMode,FALSE,&interval);

 // 删除這些裝置

  if(s_fltobj[i] != NULL)

   IoDeleteDevice(s_fltobj[i]);

(6)PoStartNextPowerIrp

Beginning with Windows Vista, calling PoStartNextPowerIrp is not required and a call to this routine performs no power management operation. However, on Windows Server 2003, Windows XP, and Windows 2000, after a driver processes a query-power IRP or a set-power IRP, the driver must call PoStartNextPowerIrp to notify the power manager that it is ready to receive another power IRP. Drivers must call PoStartNextPowerIrp while the IRP stack location points to the current driver and before calling PoCallDriver.

(7)MmGetSystemAddressForMdlSafe

The MmGetSystemAddressForMdlSafe macro returns a nonpaged system-space virtual address for the buffer that the specified MDL describes.

PVOID 

  MmGetSystemAddressForMdlSafe(

    IN PMDL  Mdl,

    IN MM_PAGE_PRIORITY  Priority

NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp)

    PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

    NTSTATUS status;

    ULONG i,j;

    // 首先得知道發送給了哪個裝置。裝置一共最多CCP_MAX_COM_ID

    // 個,是前面的代碼儲存好的,都在s_fltobj中。

    for(i=0;i<CCP_MAX_COM_ID;i++)

    {

        if(s_fltobj[i] == device)

        {   

            // 所有電源操作,全部直接放過。

            if(irpsp->MajorFunction == IRP_MJ_POWER)

            {

                // 直接發送,然後傳回說已經被處理了。

                PoStartNextPowerIrp(irp);

                IoSkipCurrentIrpStackLocation(irp);

                return PoCallDriver(s_nextobj[i],irp);

            }

            // 此外我們隻過濾寫請求。寫請求的話,獲得緩沖區以及其長度。

            // 然後列印一下。

            if(irpsp->MajorFunction == IRP_MJ_WRITE)

                // 如果是寫,先獲得長度

                ULONG len = irpsp->Parameters.Write.Length;

                // 然後獲得緩沖區

              //Irp結構中一共有3個地方可以描述緩沖區。一個是IRP->MDLAddress,一個是irp->UserBuffer,一個是AssociateIrp.SystemBuffer,

             //不同的IO類型。IRP的緩沖區不同。SystemBuffer是一般用于比較簡單且不追求效率情況下的解決方案:把應用層(R3層)中記憶體空間中的緩沖資料拷貝到核心空間。

            //userbuffer是最追求效率的解決方案。應用層的緩沖區位址直接放到UserBuffer裡。在核心空間中通路。在目前程序和發送請求程序一緻的情況下。核心通路應用層的記憶體空間當然是沒錯的。但是一旦核心程序已經切換了。這個通路就結束了。

//核心空間是所有程序共用的。而應用層空間則是各個程序隔離的。

//更簡單的解決方案是把應用層的位址空間映射到核心空間。這需要在頁表中增加一個映射。通過構造MDL就能實作這個功能。MDL可以翻譯為記憶體描述符鍊這個

//MDL中可以讀出一個核心空間的虛拟位址。這就彌補了USerBuffer的不足。同時比SystemBuffer的完全拷貝方法要輕量,因為這個記憶體實際還是在老地方。沒有拷貝。

                PUCHAR buf = NULL;

                if(irp->MdlAddress != NULL)

                    buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);

                else

                    buf = (PUCHAR)irp->UserBuffer;

                if(buf == NULL)

                    buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer;

                // 列印内容

                for(j=0;j<len;++j)

                {

                    DbgPrint("comcap: Send Data: %2x\r\n",

                        buf[j]);

                }

            // 這些請求直接下發執行即可。我們并不禁止或者改變它。

            IoSkipCurrentIrpStackLocation(irp);

          //IoCallDriver把請求發送給真實的裝置。因為真實的裝置已經被過濾裝置綁定,是以收到irp的是過濾裝置的對象。

            return IoCallDriver(s_nextobj[i],irp);

        }

    }

    // 如果根本就不在被綁定的裝置中,那是有問題的,直接傳回參數錯誤。

    irp->IoStatus.Information = 0;

    irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

    IoCompleteRequest(irp,IO_NO_INCREMENT);

    return STATUS_SUCCESS; 

(8)ZwCreateFile

ZwCreateFile是很重要的函數。同名的函數有兩個。是以在使用者層調用CreateFile就可以引發對這個函數的調用。

它不但可以打開檔案而且可以打開裝置對象。應用程式為了互動核心而調用這個函數。這個函數最終調用NTCreateFile

(9)IOGetAttachedDevice

The IoGetAttachedDevice routine returns a pointer to the highest-level device object associated with the specified device.

獲得一個裝置所在的裝置棧最頂端的那個裝置。

(10)鍵盤和cpu的互動方式是中斷和讀取端口。這個操作是串行的。一次中斷發生就等于鍵盤給了cpu一次通知。這個通知隻能通知一個事件:某個鍵被按下了。某個鍵被談起來了。cpu隻接收通知并讀取端口的掃描碼。從不主動去檢視任何鍵。為此一個鍵實際需要兩個掃描碼。一個表示鍵按下。另一個表示鍵彈起。如果按下的掃描碼為X。則同一個鍵彈起的掃描碼為x+0x80.xp下的端口和中斷号都是定死的。即中斷号為0x93,端口為0x60、每次中斷發生。CPU都會去讀取端口0x60的掃描碼。0x60中隻儲存一個位元組。但是掃描碼是可以有兩個位元組的。此時會發生兩次中斷。cpu會先後讀到掃描碼的兩個位元組。同時按下兩個鍵之類的事情在這中機制下是不可能發生的。無論如何按鍵。資訊的傳遞都是一次一個位元組串行發生的。

(11)ObReferenceObjectByName

通過一個名字獲得一個對象的指針。

// 這個函數是事實存在的,隻是文檔中沒有公開。聲明一下

// 就可以直接使用了。

ObReferenceObjectByName(

                        PUNICODE_STRING ObjectName,

                        ULONG Attributes,

                        PACCESS_STATE AccessState,

                        ACCESS_MASK DesiredAccess,

                        POBJECT_TYPE ObjectType,

                        KPROCESSOR_MODE AccessMode,

                        PVOID ParseContext,

                        PVOID *Object

                        );

// 這個函數經過改造。能打開驅動對象Kbdclass,然後綁定

// 它下面的所有的裝置:

NTSTATUS

c2pAttachDevices(

                  IN PDRIVER_OBJECT DriverObject,

                  IN PUNICODE_STRING RegistryPath

                  )

{

    NTSTATUS status = 0;

    UNICODE_STRING uniNtNameString;

    PC2P_DEV_EXT devExt;

    PDEVICE_OBJECT pFilterDeviceObject = NULL;

    PDEVICE_OBJECT pTargetDeviceObject = NULL;

    PDEVICE_OBJECT pLowerDeviceObject = NULL;

    PDRIVER_OBJECT KbdDriverObject = NULL;

    KdPrint(("MyAttach\n"));

    // 初始化一個字元串,就是Kdbclass驅動的名字。

    RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME);

    // 請參照前面打開裝置對象的例子。隻是這裡打開的是驅動對象。

    status = ObReferenceObjectByName (

        &uniNtNameString,

        OBJ_CASE_INSENSITIVE,

        NULL,

        0,

        IoDriverObjectType,

        KernelMode,

        &KbdDriverObject

        );

    // 如果失敗了就直接傳回

    if(!NT_SUCCESS(status))

    {

        KdPrint(("MyAttach: Couldn't get the MyTest Device Object\n"));

        return( status );

    else

        // 這個打開需要解應用。早點解除了免得之後忘記。

        ObDereferenceObject(DriverObject);

    // 這是裝置鍊中的第一個裝置 

    pTargetDeviceObject = KbdDriverObject->DeviceObject;

    // 現在開始周遊這個裝置鍊

    while (pTargetDeviceObject)

        // 生成一個過濾裝置,這是前面讀者學習過的。這裡的IN宏和OUT宏都是

        // 空宏,隻有标志性意義,表明這個參數是一個輸入或者輸出參數。

        status = IoCreateDevice(

            IN DriverObject,

            IN sizeof(C2P_DEV_EXT),

            IN NULL,

            IN pTargetDeviceObject->DeviceType,

            IN pTargetDeviceObject->Characteristics,

            IN FALSE,

            OUT &pFilterDeviceObject //獲得裝置鍊

            );

        // 如果失敗了就直接退出。

        if (!NT_SUCCESS(status))

        {

            KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object\n"));

            return (status);

        }

        // 綁定。pLowerDeviceObject是綁定之後得到的下一個裝置。也就是

        // 前面常常說的所謂真實裝置。

        pLowerDeviceObject =

            IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject);

        // 如果綁定失敗了,放棄之前的操作,退出。

        if(!pLowerDeviceObject)

            KdPrint(("MyAttach: Couldn't attach to MyTest Device Object\n"));

            IoDeleteDevice(pFilterDeviceObject);

            pFilterDeviceObject = NULL;

            return( status );

        // 裝置擴充!下面要詳細講述裝置擴充的應用。

        devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension);

        c2pDevExtInit(

            devExt,

            pFilterDeviceObject,

            pTargetDeviceObject,

            pLowerDeviceObject );

        // 下面的操作和前面過濾序列槽的操作基本一緻。這裡不再解釋了。

        pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType;

        pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics;

        pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1;

        pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ;

        //next device

        pTargetDeviceObject = pTargetDeviceObject->NextDevice;

    return status;

下一篇: 事件驅動