Windows 内核重要对象(数据结构)汇总

        最近闲下来了,总结一下Windows(当前最新的桌面操作系统时Windows 11)系统内核中几个重要的对象

文件 FILE_OBJECT

typedef struct _FILE_OBJECT {
  CSHORT                                Type;                    //只读,如果是文件对象,值为5
  CSHORT                                Size;                    //只读,文件对象大小(in byte),不包括对象扩展
  PDEVICE_OBJECT                        DeviceObject;            //指向文件打开设备的指针
  PVPB                                  Vpb;                     //关联当前文件对象的卷参数块指针,!=NULL时,文件驻留在挂载的卷上
  PVOID                                 FsContext;               //关于文件状态的指针,由关联驱动程序维护,对于文件系统驱动,它指向FSRTL_ADVANCED_FCB_HEADER,否则,系统可能蓝屏;通常,这个Header结构内嵌在FCB中;但,对于NTFS,这个Header内嵌在SCB中。
  PVOID                                 FsContext2;              //关于文件状态的指针(不透明),由关联驱动程序维护;在WDM驱动栈中,只有功能设备能使用上述两个上下文指针。
  PSECTION_OBJECT_POINTERS              SectionObjectPointer;    //用于缓存管理器交互,由文件系统设置,只读
  PVOID                                 PrivateCacheMap;         //用于缓存管理器交互,由文件系统设置,只读,不透明
  NTSTATUS                              FinalStatus;             //只读,指示文件对象I/O请求的最终状态;在特定同步时使用。
  struct _FILE_OBJECT                   *RelatedFileObject;      //和当前文件管理的已打开文件对象,该指针通常(不总是)指向当前文件所在目录,该指针旨在处理IRP_MJ_CREATE时有用。
  BOOLEAN                               LockOperation;           //只读,一旦是TRUE,这个值会一直保持到对象销毁。
  BOOLEAN                               DeletePending;           //只读,如果=TRUE,表示有进程将文件设置为“等待删除”,只是动作还未执行。
  BOOLEAN                               ReadAccess;              //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  BOOLEAN                               WriteAccess;             //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  BOOLEAN                               DeleteAccess;            //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  BOOLEAN                               SharedRead;              //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  BOOLEAN                               SharedWrite;             //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  BOOLEAN                               SharedDelete;            //只读,表示当前对象所有者的权限,检查和/或设置文件的共享访问权限时使用。
  ULONG                                 Flags;                   //
  UNICODE_STRING                        FileName;                //卷上打开的文件的名称,如果打开的是卷,则无效;该值仅在IRP_MJ_CREATE Pre阶段有效,Post阶段或其他IRP处理过程中无效。
  LARGE_INTEGER                         CurrentByteOffset;       //只读,文件偏移量(byte),由文件系统维护,仅用于被以同步方式打开的文件对象。指向文件流的当前指针位置,该指针在成功完成读写操作时进行更新
  __volatile ULONG                      Waiters;                 //同步访问目标文件的等待进程数。
  __volatile ULONG                      Busy;                    //只读,当前访问文件的进程是否繁忙。
  PVOID                                 LastLock;                //事件锁,不透明,用于文件对象的最后一个锁。
  KEVENT                                Lock;                    //事件锁,不透明,用于控制对文件的同步访问,仅适用于以“同步访问”方式访问文件的进程。
  KEVENT                                Event;                   //不透明,用户未提供时,系统将它指向I/O请求的完成信号。
  __volatile PIO_COMPLETION_CONTEXT     CompletionContext;       //由IO管理器进行使用,在完成一个IRP的时候,发送一个消息到本地过程调用(LPC)端口
  KSPIN_LOCK                            IrpListLock;             //不透明,指向KSPIN_LOCK,用于同步文件对象的IRP列表访问的自旋锁。
  LIST_ENTRY                            IrpList;                 //不透明,指向与文件对象关联的 IRP 列表头。
  __volatile _IOP_FILE_OBJECT_EXTENSION *FileObjectExtension;    //不透明,指向FOBX结构,可以通过FsRtlxxx访问。
  struct                                _IOP_FILE_OBJECT_EXTENSION;//无
} FILE_OBJECT, *PFILE_OBJECT;

根据微软的文档介绍:

  • 应将文件对象中的不透明成员视为不可访问。
  • 驱动程序可以使用只读成员获取相关信息,但不能修改只读成员。
  • 不能通过检查FILE_OBJECT内容确定文件类型(目录、文件、卷),应通过ZwQueryInformationFile。
  • DeviceObject 和VPNB,由IO管理器在发送create请求给文件系统驱动之前初始化
  • FsContextFsContext2SectionObjectPointerPrivateCacheMap文件系统驱动缓存管理器进行初始化和维护,IO管理器不维护这些域中的内容,但是IO管理器会检查和使用FsContext域的内容
  • FileName由IO管理器进行初始化表示要打开的文件、卷或物理设备的字符串,可以是相对路径也可以是绝对路径。为相对路径名时,可通过查询RelatedFileObject域的非空值拼装出绝对路径。

驱动对象

typedef struct _DRIVER_OBJECT {
  CSHORT             Type;                //无
  CSHORT             Size;                //无
  PDEVICE_OBJECT     DeviceObject;        //指向由驱动创建的设备对象,应使用IoCreateDevide更新该值,可以通过该结构的NextDevice遍历驱动程序创建的所有设备。
  ULONG              Flags;               //无
  PVOID              DriverStart;         //无
  ULONG              DriverSize;          //无
  PVOID              DriverSection;       //无
  PDRIVER_EXTENSION  DriverExtension;     //指向驱动的扩展结构体,该结构中只有AddDevice可以修改,用来存储驱动程序的AddDevice函数的指针。
  UNICODE_STRING     DriverName;          //驱动程序的名称字符串,一般为“\Driver\驱动名称”
  PUNICODE_STRING    HardwareDatabase;    //指向注册表路径“\Registry\Mechine\Hardware”,存储硬件配置信息,驱动程序只有读取权限。
  PFAST_IO_DISPATCH  FastIoDispatch;      //指向驱动的Fast I/O入口点,该成员制备FSD和网络传输驱动用
  PDRIVER_INITIALIZE DriverInit;          //由I/O管理器配置,DriverEntry的入口点函数
  PDRIVER_STARTIO    DriverStartIo;       //驱动的StartIo函数指针,在DriverEntry中设置(可选)
  PDRIVER_UNLOAD     DriverUnload;        //驱动的卸载函数指针,在DriverEntry中设置(可选)
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
                                          //派遣函数指针列表,用于处理函数关心的IRP
} DRIVER_OBJECT, *PDRIVER_OBJECT;

开发过程中,通常需要重点关注的几个参数如下:

  • DeviceObject:该成员指向由驱动程序创建的设备,可能由一个,也可能由多个(如电脑上的扬声器,就有两个,如果要播放立体环绕音,就需要控制两个设备进行不同的IRP传输)
  • DriverUnload:当驱动程序卸载时调用函数的指针,如果不支持驱动卸载可以留NULL,如果支持,可以在DriverUnload做一些清理工作。
  • MajorFunction:一组指针,指向用户注册的用于处理IRP的函数,这些函数都是"Pre"阶段收到IRP处理通知,如果需要在IRP返回用户时继续处理,需要在函数内注册完成函数(参考IoSetCompletionRoutine

设备对象

typedef struct _DEVICE_OBJECT {
  CSHORT                   Type;                //只读,表示对象类型,设备对象=3
  USHORT                   Size;                //只读,本结构体大小(byte),包括设备扩展,但不包含DeviceObjectExtension 指向的成员大小
  LONG                     ReferenceCount;      //只读,I/O管理器用,记录打开设备的句柄数
  struct _DRIVER_OBJECT    *DriverObject;       //只读,设备的驱动程序(通过调用IoCreateDevice或IoCreateDeviceSecure产生该对象)。
  struct _DEVICE_OBJECT    *NextDevice;         //指向本设备的驱动对象创建的兄弟设备,由I/O管理器维护。
  struct _DEVICE_OBJECT    *AttachedDevice;     //指向被绑定设备对象(通常是过滤驱动),如果没有=NULL(通常是功能驱动)。
  struct _IRP              *CurrentIrp;         //如果驱动有StartIo函数,指针指向当前正在处理的IRP,否则为NULL。
  PIO_TIMER                Timer;               //可读写,I/O管理器会每秒钟调用一次驱动提供的时钟处理函数。
  ULONG                    Flags;               //指示性标记,,可以通过“或”操作使用1~n个值。
  ULONG                    Characteristics;     //驱动设备的附加信息,可以通过“或”操作使用1~n个值
  __volatile PVPB          Vpb;                 //不透明,指向和设备对象关联的volume parameter block (VPB) 
  PVOID                    DeviceExtension;     //由驱动程序定义,给设备对象使用
  DEVICE_TYPE              DeviceType;          //指示当前设备类型,详细信息参考本文链接“Specify device types”
  CCHAR                    StackSize;           //处理IRP的设备栈数量,I/O管理器自动设置。
  union {
    LIST_ENTRY         ListEntry;               //双向链表
    WAIT_CONTEXT_BLOCK Wcb;                     //I/O管理器使用的上下文
  } Queue;                                      //无
  ULONG                    AlignmentRequirement;//数传输时对设备地址对齐的要求,值只能是Wdm.h中FILE_XXX_ALIGNMENT中的一个
  KDEVICE_QUEUE            DeviceQueue;         //不透明,设备对象队列。
  KDPC                     Dpc;                 //延迟过程调用
  ULONG                    ActiveThreadCount;   //保留
  PSECURITY_DESCRIPTOR     SecurityDescriptor;  //如需要,请通过调用ZwSetSecurityObject更新该值。
  KEVENT                   DeviceLock;          //不透明,I/O管理器用
  USHORT                   SectorSize;          //当设备对象为卷时,表示卷的扇区大小(byte),I/O管理器通过该参数确保在禁用了中间缓冲时文件操作都能正确对齐。创建设备对象时,使用默认的系统字节/扇区值。一下请可少见,但有:文件系统驱动程序、遗留驱动器和minifilter驱动程序可以在发生挂载时根据底层卷硬件的几何形状更新此值。其他驱动程序不应该修改这个成员。
  USHORT                   Spare1;              //系统用,不透明
  struct _DEVOBJ_EXTENSION *DeviceObjectExtension;//给I/O管理器和PnP管理器使用的设备扩展指针,不透明。
  PVOID                    Reserved;            //保留
} DEVICE_OBJECT, *PDEVICE_OBJECT;
  • 系统用一个设备对象(Device Object)代表一个设备,驱动程序通过IoCreateDevice 和IoCreateDeviceSecure创建设备,系统根据配置自动创建设备对象。

  • 底层驱动调用IoCreateDevice后,需要设置AlinmentRequirement值,方法如下:

    • 设备实际对齐要求 - 1

    • 对比设备对象的AlinmentRequirement

    • 如果步骤一结果大,则设备对象的AlinmentRequirement = 设备实际对齐要求 - 1,否则保留当前设备对象的AlinmentRequirement值。

  • StackSize:通常情况下该变量只需要查询,不需要修改维护,但如果开发者在绑定到某设备时使用了IoGetDeviceObjectPointer,则需要调用者在自己的设备对象中对该变量手动+1(当前设备对象的StackSize = 被绑定设备的DEVICE_OJBECT->StackSize+1)。

  • 尽量用 IoAttachDevice 或 IoAttachDeviceToDeviceStack代替IoGetDeviceObjectPointer 

IRP(I/O Request Package)

typedef struct _IRP {
  CSHORT                    Type;
  USHORT                    Size;
  PMDL                      MdlAddress;            //指向描述用户缓冲区的 MDL(如果驱动程序使用的是直接I/O,且IRP是Read、Write、Device_Control或Internal_Device_Control(IOCTL代码指定METHOD_IN_DIRECT)则 MDL 描述包含设备或驱动程序数据的缓冲区)
  ULONG                     Flags;                 //只读,供文件系统驱动使用,一个或多个IRO_XXX按位OR。
  union {
    struct _IRP     *MasterIrp;
    __volatile LONG IrpCount;
    PVOID           SystemBuffer;                  //如果驱动程序使用的是Buffered I/O,请参考文章最后链接“I/O控制代码的缓冲区说明”。
  } AssociatedIrp;
  LIST_ENTRY                ThreadListEntry;
  IO_STATUS_BLOCK           IoStatus;              //调用 IoCompleteRequest前,存储状态和信息的 IO_STATUS_BLOCK 结构
  KPROCESSOR_MODE           RequestorMode;         //指示发起者的模式(UserMode或KernelMode)
  BOOLEAN                   PendingReturned;       //如果=TRUE,表示驱动程序已将IRP挂起,所有IoCompletion都要检查这个值。
  CHAR                      StackCount;
  CHAR                      CurrentLocation;
  BOOLEAN                   Cancel;                //当前IRP已经被设置为Cancel,当前处理者适当处理。
  KIRQL                     CancelIrql;            //调用 ioAcquireCancelSpinLock 时驱动程序运行所在的IRQL
  CCHAR                     ApcEnvironment;
  UCHAR                     AllocationFlags;
  union {
    PIO_STATUS_BLOCK UserIosb;
    PVOID            IoRingContext;
  };
  PKEVENT                   UserEvent;
  union {
    struct {
      union {
        PIO_APC_ROUTINE UserApcRoutine;
        PVOID           IssuingProcess;
      };
      union {
        PVOID                 UserApcContext;
#if ...
        _IORING_OBJECT        *IoRing;
#else
        struct _IORING_OBJECT *IoRing;
#endif
      };
    } AsynchronousParameters;
    LARGE_INTEGER AllocationSize;
  } Overlay;
  __volatile PDRIVER_CANCEL CancelRoutine;          //驱动程序提供的“取消”函数指针,如果取消 IRP,则调用该例程。 NULL 指示 IRP 当前不可取消。
  PVOID                     UserBuffer;             //存放数据的缓冲区,请参考文章最后链接“I/O控制代码的缓冲区说明”。
  union {
    struct {
      union {
        KDEVICE_QUEUE_ENTRY DeviceQueueEntry;       //如果 IRP 在与驱动程序的设备对象关联的设备队列中排队,则此字段链接设备队列中的 IRP。 仅当驱动程序正在处理 IRP 时,才能使用这些链接。
        struct {
          PVOID DriverContext[4];                   //如果 IRP 不在与驱动程序的设备对象关联的设备队列中排队,驱动程序可以使用此字段来存储最多四个指针。 此字段只能在驱动程序拥有 IRP 时使用。
        };
      };
      PETHREAD     Thread;                          //向调用方线程控制块(TCB)的指针。 对于源自用户模式的请求,I/O 管理器始终将此字段设置为指向发出请求的线程的 TCB。
      PCHAR        AuxiliaryBuffer;
      struct {
        LIST_ENTRY ListEntry;                       //如果驱动程序管理自己的内部 IRP 队列,则它使用此字段将一个 IRP 链接到下一个 IRP。 仅当驱动程序在其队列中保存 IRP 或正在处理 IRP 时,才能使用这些链接。
        union {
          struct _IO_STACK_LOCATION *CurrentStackLocation;
          ULONG                     PacketType;
        };
      };
      PFILE_OBJECT OriginalFileObject;
    } Overlay;
    KAPC  Apc;
    PVOID CompletionKey;
  } Tail;
} IRP;

参考文档:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值