李sir_Blog

博客园 首页 联系 订阅 管理

(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,
       0,
       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->Flags & DO_BUFFERED_IO)
  (*fltobj)->Flags |= DO_BUFFERED_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;
  return status;
 }
 *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);

 // 删除这些设备
 for(i=0;i<CCP_MAX_COM_ID;i++)
 {
  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

通过一个名字获得一个对象的指针。

// 这个函数是事实存在的,只是文档中没有公开。声明一下
// 就可以直接使用了。
NTSTATUS
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,
        NULL,
        &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;
}

 

 

 

posted on 2010-09-09 09:58  李sir  阅读(1606)  评论(0编辑  收藏  举报