1,驱动框架常见数据结构有 驱动对象结构, 设备对象结构等。
A)驱动对象结构 (DRIVER_OBJECT)
每个驱动对象代表一个已加载的内核驱动程序,指向驱动对象结构的指针常常作为DriverEntry,AddDevice,Unload等函数的参数。驱动对象结构式半透明的。其中公开的域包括DeviceObject,DriverExtension,HardwareDatabase ,FastIoDispath,DriverInit,DriverStartIo,DriverUnload以及MajorFunction。
驱动对象的数据结构如下:
typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; // // 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; ULONG Flags; // // The following section describes where the driver is loaded. The count // field is used to count the number of times the driver has had its // registered reinitialization routine invoked. // PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; // // 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 is for registry support. Thise is a pointer // to the path to the hardware information in the registry // PUNICODE_STRING HardwareDatabase; // // The following section contains the optional pointer to an array of // alternate entry points to a driver for "fast I/O" support. Fast I/O // is performed by invoking the driver routine directly with separate // parameters, rather than using the standard IRP call mechanism. Note // that these functions may only be used for synchronous I/O, and when // the file is cached. // PFAST_IO_DISPATCH FastIoDispatch; // // The following section describes the entry points to this particular // driver. Note that the major function dispatch table must be the last // field in the object so that it remains extensible. // PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT; typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; // ntndis
其中DeviceObject域指向由此驱动创建的设备对象:FastIoDispath域指向快速I/O 入口。DriverInit指向驱动入口点地址(DriverEntry):DriverUnload 指向驱动卸载程序:MajorFunction 是一张函数分发表,数组的所引致与IRP_MJ_Xxx相对应。
自己重新对上面的结构体认识了下,省略了部分,主要了解下面的:
typedef struct _DRIVER_OBJECT{ //结构的类型和大小 CSHORT Type; CSHORT Size; //设备对象,这里实际上是一个设备对象的链表的开始。因为 DeviceObject 中有相关链表信息。 PDEVICE_OBJECT DeviceObject; ••• //这个内核模块在内核空间中的开始地址和大小 PVOID DriverStart; ULONG DriverSize; ••• //驱动的名字 UNICODE_STRING DriverName; ••• //快速IO分发函数 PFAST_IO_DISPATCH FastIoDispatch; ••• //驱动的卸载函数 PDRIVER_UNLOAD.DriverUnload; //普通分发函数 PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; }DRIVER_OBJECT;这样看起来是不是 少了很多呢, 上面做了注释的是 主要需要了解的。
-------------------------------------------------------------------------------------------------------
B)设备驱动程序(DEVICE_OBJECT)
系统使用设备对象来描述一个设备对象,数据结构如下:
typedef struct _DEVICE_OBJECT { CSHORT Type; USHORT Size; LONG ReferenceCount; struct _DRIVER_OBJECT *DriverObject; struct _DEVICE_OBJECT *NextDevice; struct _DEVICE_OBJECT *AttachedDevice; struct _IRP *CurrentIrp; PIO_TIMER Timer; ULONG Flags; ULONG Characteristics; __volatile PVPB Vpb; PVOID DeviceExtension; DEVICE_TYPE DeviceType; CCHAR StackSize; union { LIST_ENTRY ListEntry; WAIT_CONTEXT_BLOCK Wcb; } Queue; ULONG AlignmentRequirement; KDEVICE_QUEUE DeviceQueue; KDPC Dpc; ULONG ActiveThreadCount; PSECURITY_DESCRIPTOR SecurityDescriptor; KEVENT DeviceLock; USHORT SectorSize; USHORT Spare1; struct _DEVOBJ_EXTENSION * DeviceObjectExtension; PVOID Reserved; } DEVICE_OBJECT, *PDEVICE_OBJECT;
其中,DriverObject 域指向创建次设备对象的驱动程序对象:NextDevice 域指向一个驱动程序创建的下一个设备对象:AttachedDevice 域指向绑定到此设备对象上的设备对象;Flags域指定了设备对象的标记,如该谁被是缓冲读写方式(DO_BUFFERED_IO)还是直接读写方式(DO_DIRECT_IO);Vpb 域指向此设备对象相关的卷参数块;DeviceExtension 域指向设备扩展,设备扩展中的内容由程序设计者自定义,往往用来记录域设备相关的一些信息。
这个结构体也很复杂,我自己总结了下,如下:
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT{ //结构的类型和大小 CSHORT Type; CSHORT Size; //引用计数 ULONG ReferenceCount; //这个设备所属的驱动对象 struct _DRIVER_OBJECT *DriverObject; //下一个设备对象,在一个驱动对象中有N个设备,这些设备用这个指针连接起来作为一个单向的链表 struct _DRIVER_OBJECT *NextDevice; //设备类型 DEVICE_TYPE DeviceType; ••• //IRP栈大小 HAR StackSize; •••••• }DEVICE_OBJECT;这样一来,就清晰很多啦
----------------------------------------------------------------------------------------------------------------------------
2,进程域线程数据结构
A)执行体进程块(EXPROCESS)
执行体进程块是一个不透明的数据结构,用来描述一个进程对象。驱动程序可以用PsGetCurrentProcess函数获取指向当前进程的执行体进程进程块指针。
由于其数据结构不是透明的,想了好多办法,还是找到了啦:
typedef struct _EPROCESS { KPROCESS Pcb; EX_PUSH_LOCK ProcessLock; LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime; EX_RUNDOWN_REF RundownProtect; HANDLE UniqueProcessId; LIST_ENTRY ActiveProcessLinks; SIZE_T QuotaUsage[PsQuotaTypes]; SIZE_T QuotaPeak[PsQuotaTypes]; SIZE_T CommitCharge; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; LIST_ENTRY SessionProcessLinks; PVOID DebugPort; PVOID ExceptionPort; PHANDLE_TABLE ObjectTable; EX_FAST_REF Token; PFN_NUMBER WorkingSetPage; KGUARDED_MUTEX AddressCreationLock; KSPIN_LOCK HyperSpaceLock; struct _ETHREAD *ForkInProgress; ULONG_PTR HardwareTrigger; PMM_AVL_TABLE PhysicalVadRoot; PVOID CloneRoot; PFN_NUMBER NumberOfPrivatePages; PFN_NUMBER NumberOfLockedPages; PVOID Win32Process; struct _EJOB *Job; PVOID SectionObject; PVOID SectionBaseAddress; PEPROCESS_QUOTA_BLOCK QuotaBlock; PPAGEFAULT_HISTORY WorkingSetWatch; HANDLE Win32WindowStation; HANDLE InheritedFromUniqueProcessId; PVOID LdtInformation; PVOID VadFreeHint; PVOID VdmObjects; PVOID DeviceMap; PVOID Spare0[3]; union { HARDWARE_PTE PageDirectoryPte; ULONGLONG Filler; }; PVOID Session; UCHAR ImageFileName[ 16 ]; LIST_ENTRY JobLinks; PVOID LockedPagesList; LIST_ENTRY ThreadListHead; PVOID SecurityPort; PVOID PaeTop; ULONG ActiveThreads; ACCESS_MASK GrantedAccess; ULONG DefaultHardErrorProcessing; NTSTATUS LastThreadExitStatus; PPEB Peb; EX_FAST_REF PrefetchTrace; LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SIZE_T CommitChargeLimit; SIZE_T CommitChargePeak; PVOID AweInfo; SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo; MMSUPPORT Vm; LIST_ENTRY MmProcessLinks; ULONG ModifiedPageCount; ULONG JobStatus; union { ULONG Flags; struct { ULONG CreateReported : 1; ULONG NoDebugInherit : 1; ULONG ProcessExiting : 1; ULONG ProcessDelete : 1; ULONG Wow64SplitPages : 1; ULONG VmDeleted : 1; ULONG OutswapEnabled : 1; ULONG Outswapped : 1; ULONG ForkFailed : 1; ULONG Wow64VaSpace4Gb : 1; ULONG AddressSpaceInitialized : 2; ULONG SetTimerResolution : 1; ULONG BreakOnTermination : 1; ULONG SessionCreationUnderway : 1; ULONG WriteWatch : 1; ULONG ProcessInSession : 1; ULONG OverrideAddressSpace : 1; ULONG HasAddressSpace : 1; ULONG LaunchPrefetched : 1; ULONG InjectInpageErrors : 1; ULONG VmTopDown : 1; ULONG ImageNotifyDone : 1; ULONG PdeUpdateNeeded : 1; // NT32 only ULONG VdmAllowed : 1; ULONG SmapAllowed : 1; ULONG CreateFailed : 1; ULONG DefaultIoPriority : 3; ULONG Spare1 : 1; ULONG Spare2 : 1; }; }; NTSTATUS ExitStatus; USHORT NextPageColor; union { struct { UCHAR SubSystemMinorVersion; UCHAR SubSystemMajorVersion; }; USHORT SubSystemVersion; }; UCHAR PriorityClass; MM_AVL_TABLE VadRoot; ULONG Cookie; } EPROCESS, *PEPROCESS;又是一大堆这么多复杂的代码。 这里 有详情解释。
B)内核进程块(KPROCESS)
执行进程块的第一个域Pcb描述了内核进程块,
typedef struct _KPROCESS { DISPATCHER_HEADER Header; LIST_ENTRY ProfileListHead; ULONG DirectoryTableBase; ULONG Unused0; KGDTENTRY LdtDescriptor; KIDTENTRY Int21Descriptor; WORD IopmOffset; UCHAR Iopl; UCHAR Unused; ULONG ActiveProcessors; ULONG KernelTime; ULONG UserTime; LIST_ENTRY ReadyListHead; SINGLE_LIST_ENTRY SwapListEntry; PVOID VdmTrapcHandler; LIST_ENTRY ThreadListHead; ULONG ProcessLock; ULONG Affinity; union { ULONG AutoAlignment: 1; ULONG DisableBoost: 1; ULONG DisableQuantum: 1; ULONG ReservedFlags: 29; LONG ProcessFlags; }; CHAR BasePriority; CHAR QuantumReset; UCHAR State; UCHAR ThreadSeed; UCHAR PowerState; UCHAR IdealNode; UCHAR Visited; union { KEXECUTE_OPTIONS Flags; UCHAR ExecuteOptions; }; ULONG StackCount; LIST_ENTRY ProcessListEntry; UINT64 CycleTime; } KPROCESS, *PKPROCESS;
内核进程快主要是记录和处理器调度相关的一些信息。
C)执行体进程块(ETHREAD)
Executive Thread block( ETHREAD)与 EPROCESS 结构的情形类似,用来描述 thread 的相关信息的结构:另一个 thread 相关的结构是 Thread Environment Block(TEB)结构,它存在于用户进程空间。
D)内核线程块(KTHREAD)
执行进程结构的第一个域是Tcb描述了内核线程块,该模块也是不透明的。结构如下:
kd> dt -r _kthread nt!_KTHREAD +0x000 Header : _DISPATCHER_HEADER +0x000 Type : UChar +0x001 Absolute : UChar +0x002 Size : UChar +0x003 Inserted : UChar +0x004 SignalState : Int4B +0x008 WaitListHead : _LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY +0x010 MutantListHead : _LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY +0x018 InitialStack : Ptr32 Void +0x01c StackLimit : Ptr32 Void +0x020 Teb : Ptr32 Void +0x024 TlsArray : Ptr32 Void +0x028 KernelStack : Ptr32 Void +0x02c DebugActive : UChar +0x02d State : UChar +0x02e Alerted : [2] UChar +0x030 Iopl : UChar +0x031 NpxState : UChar +0x032 Saturation : Char +0x033 Priority : Char +0x034 ApcState : _KAPC_STATE +0x000 ApcListHead : [2] _LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY +0x010 Process : Ptr32 _KPROCESS
其实Header 域保存了一个分发器头,表明线程对象(ETHREAD结构)也是基于分发器头的同步对象:InitialStack和StackLimit分别记录线程栈基址和栈边界地址;Teb保存了一个指向线程环境块的指针。
3 ,存储系统数据结构
A)参数模块(VPB)
详细如下:
1: kd> ?? fastfat!FatData struct _FAT_DATA +0x000 NodeTypeCode : 0x500 +0x002 NodeByteSize : 304 +0x008 LazyWriteThread : 0xfffffa80`037151a0 +0x010 VcbQueue : _LIST_ENTRY [ 0xfffffa80`0384fa18 – 0xfffffa80`0384fa18 ] …… 1: kd> !list "-t nt!_LIST_ENTRY.Flink -e -x \"dd @$extret l4; dt fastfat!_VCB @$extret-0x58\" 0xfffffa80`0384fa18" dd @$extret l4; dt fastfat!_VCB @$extret-0x58 fffffa80`0384fa18 070e34d0 fffff880 070e34d0 fffff880 +0x000 VolumeFileHeader : _FSRTL_ADVANCED_FCB_HEADER +0x058 VcbLinks : _LIST_ENTRY [ 0xfffff880`070e34d0 – 0xfffff880`070e34d0 ] +0x068 TargetDeviceObject : 0xfffffa80`053c7040 _DEVICE_OBJECT +0x070 Vpb : 0xfffffa80`05b54a00 _VPB +0x078 VcbState : 0x101002 +0x07c VcbCondition : 1 ( VcbGood ) …… +0x410 SwapVpb : 0xfffffa80`056f19e0 _VPB +0x418 AsyncCloseList : _LIST_ENTRY [ 0xfffffa80`0384fdd8 – 0xfffffa80`0384fdd8 ] +0x428 DelayedCloseList : _LIST_ENTRY [ 0xfffff8a0`170d3880 – 0xfffff8a0`170d3880 ] +0x438 AdvancedFcbHeaderMutex : _FAST_MUTEX +0x470 CloseContext : 0xfffff8a0`0af0eef0 CLOSE_CONTEXT +0x478 CloseContextCount : 2 1: kd> dt _VPB 0xfffffa80`05b54a00 nt!_VPB +0x000 Type : 10 +0x002 Size : 96 +0x004 Flags : 1 +0x006 VolumeLabelLength : 0x10 +0x008 DeviceObject : 0xfffffa80`0384f820 _DEVICE_OBJECT +0x010 RealDevice : 0xfffffa80`05bab060 _DEVICE_OBJECT +0x018 SerialNumber : 0x51e712c6 +0x01c ReferenceCount : 9 +0x020 VolumeLabel : [32] "CANON_DC" 1: kd> dt _VPB 0xfffffa80`056f19e0 nt!_VPB +0x000 Type : 0 +0x002 Size : 0 +0x004 Flags : 0 +0x006 VolumeLabelLength : 0 +0x008 DeviceObject : (null) +0x010 RealDevice : (null) +0x018 SerialNumber : 0 +0x01c ReferenceCount : 0 +0x020 VolumeLabel : [32] "" 1: kd> !devobj 0xfffffa80`05bab060 Device object (fffffa8005bab060) is for: HarddiskVolume18 \Driver\volmgr DriverObject fffffa8004086e70 Current Irp 00000000 RefCount 9 Type 00000007 Flags 00003050 Vpb fffffa8005b54a00 Dacl fffff9a1171d06c0 DevExt fffffa8005bab1b0 DevObjExt fffffa8005bab318 Dope fffffa8004fa7fa0 DevNode fffffa80055d9d90 ExtensionFlags (0000000000) AttachedDevice (Upper) fffffa8005b4a040 \Driver\fvevol Device queue is not busy. 1: kd> dt _DEVICE_OBJECT 0xfffffa80`05bab060 nt!_DEVICE_OBJECT +0x000 Type : 3 +0x002 Size : 0x2b8 +0x004 ReferenceCount : 9 +0x008 DriverObject : 0xfffffa80`04086e70 _DRIVER_OBJECT +0x010 NextDevice : 0xfffffa80`045ed440 _DEVICE_OBJECT +0x018 AttachedDevice : 0xfffffa80`05b4a040 _DEVICE_OBJECT +0x020 CurrentIrp : (null) +0x028 Timer : (null) +0x030 Flags : 0x3050 +0x034 Characteristics : 1 +0x038 Vpb : 0xfffffa80`05b54a00 _VPB +0x040 DeviceExtension : 0xfffffa80`05bab1b0 +0x048 DeviceType : 7 +0x04c StackSize : 9 ” +0x050 Queue : <unnamed-tag> +0x098 AlignmentRequirement : 0 +0x0a0 DeviceQueue : _KDEVICE_QUEUE +0x0c8 Dpc : _KDPC +0x108 ActiveThreadCount : 0 +0x110 SecurityDescriptor : 0xfffff8a0`170b0620 +0x118 DeviceLock : _KEVENT +0x130 SectorSize : 0x200 +0x132 Spare1 : 1 +0x138 DeviceObjectExtension : 0xfffffa80`05bab318 _DEVOBJ_EXTENSION +0x140 Reserved : (null)又是一板的代码、、 唉。。 这个就不细说啦。 这哥们总结的非常好!
B) 文件对象(File_Object)
文件对象就是一个文件,设备,目录等对象的打开实例。在任何时候,针对一个共享文件可以有多个文件对象,即共享文件可以被多次打开,但是每一个文件对象只对应一个句柄。文件对象的数据结构如下所示:
typedef struct _FILE_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; PVPB Vpb; PVOID FsContext; PVOID FsContext2; PSECTION_OBJECT_POINTERS SectionObjectPointer; PVOID PrivateCacheMap; NTSTATUS FinalStatus; struct _FILE_OBJECT *RelatedFileObject; BOOLEAN LockOperation; BOOLEAN DeletePending; BOOLEAN ReadAccess; BOOLEAN WriteAccess; BOOLEAN DeleteAccess; BOOLEAN SharedRead; BOOLEAN SharedWrite; BOOLEAN SharedDelete; ULONG Flags; UNICODE_STRING FileName; LARGE_INTEGER CurrentByteOffset; __volatile ULONG Waiters; __volatile ULONG Busy; PVOID LastLock; KEVENT Lock; KEVENT Event; __volatile PIO_COMPLETION_CONTEXT CompletionContext; KSPIN_LOCK IrpListLock; LIST_ENTRY IrpList; __volatile PVOID FileObjectExtension; } FILE_OBJECT, *PFILE_OBJECT;其中, DeviceObject域记录域所打开文件相关联的设备。Vpb记录参数块; FsContext及 FsContext2域是自定义指针; SectionObjectPointer 是一个内存区对象指针,指向数据控制区域或者映像控制区域; FileName记录文件名。
C) SCSI请求块(SRB)
SCSI请求块结构用来在存储类驱动和存储端口驱动间传输I/O请求。然后端口驱动再将SRB转发给小端口驱动。SRB的结构如下:
typedef struct _SCSI_REQUEST_BLOCK { USHORT Length; UCHAR Function; UCHAR SrbStatus; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR QueueTag; UCHAR QueueAction; UCHAR CdbLength; UCHAR SenseInfoBufferLength; ULONG SrbFlags; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; PVOID SenseInfoBuffer; struct _SCSI_REQUEST_BLOCK *NextSrb; PVOID OriginalRequest; PVOID SrbExtension; union { ULONG InternalStatus; ULONG QueueSortKey; ULONG LinkTimeoutValue; }; #ifdef _WIN64 ULONG Reserved; #endif UCHAR Cdb[16]; } SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;Length 记录此数据结构的长度; Function记录执行的操作, SrbStatus记录请求完成时的状态, ScsiStatus记录 HBA 或者目标设备返回的 SCSI 状态, PathId 标识请求的 SISC端口或者总线; TargetId 标识目标i控制器或总线上的设备; Lun标识设备的逻辑单元号; CdbLength记录SCSI-2或者命令秒速块的大小; DataBuffer指向数据缓冲区; OriginalRequest指向原始的IRP 请求; SrbExtension指向Srb的扩展部分; Cdb指定要发送给目标设备的 SCSI-2或者命令描述符块。