Windows 内核数据结构学习总结
生活随笔
收集整理的這篇文章主要介紹了
Windows 内核数据结构学习总结
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
《Windows內(nèi)核編程》---基本數(shù)據(jù)結(jié)構(gòu)
驅(qū)動對象:
每個驅(qū)動程序都會有唯一的驅(qū)動對象與之對應(yīng),并且這個驅(qū)動對象是在驅(qū)動加載時被內(nèi)核中的對象管理程序所創(chuàng)建的。驅(qū)動對象用
DRIVER_OBJECT數(shù)據(jù)結(jié)構(gòu)表示,它作為驅(qū)動的一個實例被內(nèi)核加載,并且內(nèi)核對一個驅(qū)動只加載一個實例。確切地說,是由內(nèi)核中的I/O
管理器負(fù)責(zé)加載的,驅(qū)動程序需要在DriverEntry中初始化。驅(qū)動對象的結(jié)構(gòu)定義如下(wdm.h):
typedef struct _DRIVER_OBJECT {
? ? //結(jié)構(gòu)的類型和大小
? ? CSHORT Type;
CSHORT Size;
?
//每個驅(qū)動程序會有一個或多個設(shè)備對象,其中,每個設(shè)備對象都有一個指針指向下一個驅(qū)動對象
//最后一個設(shè)備對象指向空。DeviceObject指向驅(qū)動對象的第一個設(shè)備對象。通過DeviceObject,就
//可以遍歷驅(qū)動對象中的所有設(shè)備對象了。
? ? 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;
?
? ? // 記錄驅(qū)動設(shè)備的名字,用UNICODE字符串記錄,該字符串一般/Driver/[驅(qū)動程序名稱]
? ? UNICODE_STRING DriverName;
?
//設(shè)備的硬件數(shù)據(jù)庫鍵名,也是UNICODE字符串記錄。一般為
// ?/REGISTRY/MACHINE/HADRWARE/DESCRIPTION/SYSTEM
? ? PUNICODE_STRING HardwareDatabase;
?
? ? //文件驅(qū)動中用到的派遣函數(shù)
? ? 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;
//記錄StartIO例程的函數(shù)地址,用于串行化操作
PDRIVER_STARTIO DriverStartIo;
//指定驅(qū)動卸載時所用的回調(diào)函數(shù)地址
PDRIVER_UNLOAD DriverUnload;
//MajorFunction域記錄的是一個函數(shù)指針數(shù)組,也就是MajorFunction是一個數(shù)組,數(shù)組中的每個
//成員記錄著一個指針,每一個指針指向的是一個函數(shù)。這個函數(shù)是處理IRP的派遣函數(shù)
? ? PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
實際上如果寫一個驅(qū)動程序,或者說編寫一個內(nèi)核模塊,要在Windows中加載,就必須填寫上面的結(jié)構(gòu),來告訴Windows程序提供的功能
。注意內(nèi)核模塊并不生成一個進(jìn)程,它只是寫一組回調(diào)函數(shù)讓W(xué)indows調(diào)用,且這組回調(diào)函數(shù)必須符合Windows內(nèi)核規(guī)定的格式。上面代
碼中的“快速IO分發(fā)函數(shù)”FastIoDispatch和“普通分發(fā)函數(shù)”MajorFunction就是這樣一種回調(diào)函數(shù)。這些函數(shù)用來處理發(fā)送給這個內(nèi)
核模塊的請求。
Windows中很多組件都擁有自己的DRIVER_OBJECT,例如:所有的硬件驅(qū)動程序、所有的類驅(qū)動(Disk、Cdrom…)、文件系統(tǒng)(NTFS和
FastFat,有各自的DRIVER_OBJECT),以及許多其他的內(nèi)核組件。我們可以使用一個軟件WinObj來查看所有的內(nèi)核對象。
?
設(shè)備對象:
每個驅(qū)動程序會創(chuàng)建一個或多個設(shè)備對象,用DEVICE_OBJECT數(shù)據(jù)結(jié)構(gòu)表示。每個設(shè)備對象都會有一個指針指向下一個設(shè)備對象,因此就
形成一個設(shè)備鏈。設(shè)備鏈的第一個設(shè)備是由DRIVER_OBJECT結(jié)構(gòu)體中指明的。
設(shè)備對象是內(nèi)核中的重要對象,其重要性不亞于Windows GUI編程中的窗口。窗口是唯一可以接收消息的對象,任何消息都是發(fā)送到一個
窗口中的;而在內(nèi)核編程中,大部分“消息”是以請求IRP的方式傳遞的。而設(shè)備對象(DEVICE_OBJECT)是唯一可以接收請求的實體,
任何一個請求IRP都是發(fā)送給某個設(shè)備對象的。
設(shè)備對象的結(jié)構(gòu)是DEVICE_OBJECT,常常被簡稱為DO。一個DO可以代表很多不同的東西,例如一個實際的硬盤、或?qū)崿F(xiàn)一個類似管道的功
能等等。我們總是在內(nèi)核程序中生成一個DO,而一個內(nèi)核程序是用一個驅(qū)動對象表示的,因此,一個設(shè)備對象總是屬于一個驅(qū)動對象。
在WDK的wdm.h文件中DO的定義如下:
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)
_DEVICE_OBJECT {
? ? //結(jié)構(gòu)的類型和大小
? ? CSHORT Type;
USHORT Size;
?
//引用計數(shù)
LONG ReferenceCount;
?
//指向驅(qū)動程序中的驅(qū)動對象,同屬于一個驅(qū)動程序的驅(qū)動對象指向的是同一驅(qū)動對象
struct _DRIVER_OBJECT *DriverObject;
?
//下一個設(shè)備對象。
//這里指的下一個設(shè)備對象是同屬于一個驅(qū)動對象的設(shè)備,也就是同一個驅(qū)動程序創(chuàng)建的若干設(shè)備
//對象,每個設(shè)備對象根據(jù)NextDevice域形成鏈表,從而可以枚舉每個設(shè)備對象
struct _DEVICE_OBJECT *NextDevice;
//指向下一個設(shè)備對象,這里指的是,如果有更高一層的驅(qū)動附加到這個驅(qū)動的時候
//AttachedDevice指向的就是那個更高一層的驅(qū)動
struct _DEVICE_OBJECT *AttachedDevice;
//在使用StartIO例程的時候,此域指向的是當(dāng)前IRP結(jié)構(gòu)
? ? struct _IRP *CurrentIrp;
PIO_TIMER Timer;
?
//此域是一個32位的無符號整型,每一位有具體的含義
//DO_BUFFERED_IO---讀寫操作使用緩沖方式(系統(tǒng)復(fù)制緩沖區(qū))訪問用戶模式數(shù)據(jù)
//DO_EXCLUSIVE---一次只允許一個線程打開設(shè)備句柄
//DO_DIRECT_IO---讀寫操作使用直接方式(內(nèi)存描述符表)訪問用戶模式數(shù)據(jù)
//DO_DEVICE_INITIALIZING---設(shè)備對象正在初始化
//DO_POWER_PAGABLE---必須在PASSIVE_LEVEL級上處理IRP_MJ_PNP請求
//DO_POWER_INRUSH---設(shè)備上電期間需要大電流
? ? ULONG Flags;
? ? ULONG Characteristics;
__volatile PVPB Vpb;
//指向設(shè)備擴(kuò)展對象,每個設(shè)備都會指定一個設(shè)備擴(kuò)展對象,設(shè)備擴(kuò)展對象記錄的是設(shè)備自己
//特殊定義的結(jié)構(gòu)體,即程序員自己定義的結(jié)構(gòu)體。另外,在驅(qū)動開發(fā)中,應(yīng)該盡量避免全局變量的
//使用,因為全局變量涉及不容易同步問題。解決的方法是:將全局變量存在設(shè)備擴(kuò)展中
PVOID DeviceExtension;
?
//設(shè)備類型,當(dāng)制作虛擬設(shè)備時,應(yīng)選擇FILE_DEVICE_UNKNOWN類型的設(shè)備
DEVICE_TYPE DeviceType;
?
//IRP棧大小。在多層驅(qū)動情況下,驅(qū)動與驅(qū)動之間會形成類似堆棧的結(jié)構(gòu),IRP會依次從
//最高層傳遞到最底層
? ? CCHAR StackSize;
? ? union {
? ? ? ? LIST_ENTRY ListEntry;
? ? ? ? WAIT_CONTEXT_BLOCK Wcb;
} Queue;
//設(shè)備在大容量傳輸時,需要內(nèi)存對齊,以保證傳輸速度
? ? ULONG AlignmentRequirement;
? ? KDEVICE_QUEUE DeviceQueue;
KDPC Dpc;
?
? ? // ?The following field is for exclusive use by the filesystem to keep
? ? // ?track of the number of Fsp threads currently using the device
? ? ULONG ActiveThreadCount;
? ? PSECURITY_DESCRIPTOR SecurityDescriptor;
? ? KEVENT DeviceLock;
?
? ? USHORT SectorSize;
? ? USHORT Spare1;
?
? ? struct _DEVOBJ_EXTENSION ?*DeviceObjectExtension;
? ? PVOID ?Reserved;
} DEVICE_OBJECT;
typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT;
一個驅(qū)動對象可以生成多個設(shè)備對象,而Windows向設(shè)備對象發(fā)送請求時,這些請求是被驅(qū)動對象的分發(fā)函數(shù)所捕獲的,即當(dāng)Windows內(nèi)
核向一個設(shè)備發(fā)送一個請求時,驅(qū)動對象的分發(fā)函數(shù)中的某一個會被調(diào)用,分發(fā)函數(shù)原型如下:
//參數(shù)device是請求的目標(biāo)設(shè)備;參數(shù)irp是請求的指針
NTSTATUE ASCEDispatch(PDEVICE_OBJECT device, PIRP irp);
?
附:設(shè)備擴(kuò)展:
設(shè)備對象記錄“通用”設(shè)備的信息,而另外一些“特殊”信息記錄在設(shè)備擴(kuò)展里。各個設(shè)備擴(kuò)展由程序員自己定義,每個設(shè)備的設(shè)備擴(kuò)
展不盡相同。設(shè)備擴(kuò)展是由程序員指定內(nèi)容和大小,由I/O管理器創(chuàng)建的,并保存在非分頁內(nèi)存中。
在驅(qū)動程序中,盡量避免使用全局函數(shù),因為全局函數(shù)往往導(dǎo)致函數(shù)的不可重入性。重入性指的是在多線程程序中,多個函數(shù)并行運(yùn)行
,函數(shù)的運(yùn)行結(jié)果不會根據(jù)函數(shù)的調(diào)用先后順序而導(dǎo)致不同。解決的辦法是,將全局變量以設(shè)備擴(kuò)展的形式存儲,并加以適當(dāng)?shù)耐奖?
護(hù)措施。除此之外,在設(shè)備擴(kuò)展中還會記錄下列一些內(nèi)容:
設(shè)備對象的反向指針;
設(shè)備狀態(tài)或驅(qū)動環(huán)境變量;
中斷對象指針;
控制器對象指針。
由于設(shè)備擴(kuò)展是驅(qū)動程序?qū)S玫?#xff0c;它的結(jié)構(gòu)必須在驅(qū)動程序的頭文件中定義。
?
請求IRP:
內(nèi)核中大部分請求以IRP的形式發(fā)送。IRP是一個內(nèi)核數(shù)據(jù)結(jié)構(gòu),比較復(fù)雜,因為它要表示無數(shù)種實際請求。在WDK的wdm.h中可看到IRP的
結(jié)構(gòu):
// I/O Request Packet (IRP) definition
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _IRP {
? ? //結(jié)構(gòu)的類型和大小
? ? CSHORT Type;
? ? USHORT Size;
?
// Define the common fields used to control the IRP.
?
? ? // Define a pointer to the Memory Descriptor List (MDL) for this I/O
? ? // request. ?This field is only used if the I/O is "direct I/O".
? ? PMDL MdlAddress;
?
? ? // Flags word - used to remember various flags.
? ? ULONG Flags;
?
? ? // The following union is used for one of three purposes:
? ? //
? ? // ? ?1. This IRP is an associated IRP. ?The field is a pointer to a master
? ? // ? ? ? IRP.
? ? //
? ? // ? ?2. This is the master IRP. ?The field is the count of the number of
? ? // ? ? ? IRPs which must complete (associated IRPs) before the master can
? ? // ? ? ? complete.
? ? //
? ? // ? ?3. This operation is being buffered and the field is the address of
? ? // ? ? ? the system space buffer.
? ? union {
? ? ? ? struct _IRP *MasterIrp;
? ? ? ? __volatile LONG IrpCount;
? ? ? ? PVOID SystemBuffer;
? ? } AssociatedIrp;
?
? ? // Thread list entry - allows queueing the IRP to the thread pending I/O
? ? // request packet list.
? ? LIST_ENTRY ThreadListEntry;
?
? ? // I/O status - final status of operation.
? ? IO_STATUS_BLOCK IoStatus;
?
? ? // Requestor mode - mode of the original requestor of this operation.
? ? KPROCESSOR_MODE RequestorMode;
?
? ? // Pending returned - TRUE if pending was initially returned as the
? ? // status for this packet.
? ? BOOLEAN PendingReturned;
?
? ? // Stack state information.
? ? CHAR StackCount; ? ? ? ? //IPR??臻g大小
? ? CHAR CurrentLocation; ? ? ? ?//IRP當(dāng)前??臻g
?
? ? // Cancel - packet has been canceled.
? ? BOOLEAN Cancel;
?
? ? // Cancel Irql - Irql at which the cancel spinlock was acquired.
? ? KIRQL CancelIrql;
?
? ? // ApcEnvironment - Used to save the APC environment at the time that the
? ? // packet was initialized.
? ? CCHAR ApcEnvironment;
?
? ? // Allocation control flags.
? ? UCHAR AllocationFlags;
?
? ? // User parameters.
? ? PIO_STATUS_BLOCK UserIosb;
? ? PKEVENT UserEvent;
? ? union {
? ? ? ? struct {
? ? ? ? ? ? union {
? ? ? ? ? ? ? ? PIO_APC_ROUTINE UserApcRoutine;
? ? ? ? ? ? ? ? PVOID IssuingProcess;
? ? ? ? ? ? };
? ? ? ? ? ? PVOID UserApcContext;
? ? ? ? } AsynchronousParameters;
? ? ? ? LARGE_INTEGER AllocationSize;
? ? } Overlay;
?
? ? // CancelRoutine - Used to contain the address of a cancel routine supplied
? ? // by a device driver when the IRP is in a cancelable state.
? ? __volatile PDRIVER_CANCEL CancelRoutine;
?
? ? // Note that the UserBuffer parameter is outside of the stack so that I/O
? ? // completion can copy data back into the user's address space without
? ? // having to know exactly which service was being invoked. ?The length
? ? // of the copy is stored in the second half of the I/O status block. If
? ? // the UserBuffer field is NULL, then no copy is performed.
? ? PVOID UserBuffer;
?
// Kernel structures
?
? ? // The following section contains kernel structures which the IRP needs
? ? // in order to place various work information in kernel controller system
? ? // queues. ?Because the size and alignment cannot be controlled, they are
? ? // placed here at the end so they just hang off and do not affect the
? ? // alignment of other fields in the IRP.
? ? union {
? ? ? ? struct {
? ? ? ? ? ? union {
? ? ? ? ? ? ? ? // DeviceQueueEntry - The device queue entry field is used to
? ? ? ? ? ? ? ? // queue the IRP to the device driver device queue.
? ? ? ? ? ? ? ? KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
?
? ? ? ? ? ? ? ? struct {
? ? ? ? ? ? ? ? ? ? // The following are available to the driver to use in
? ? ? ? ? ? ? ? ? ? // whatever manner is desired, while the driver owns the
? ? ? ? ? ? ? ? ? ? // packet.
? ? ? ? ? ? ? ? ? ? PVOID DriverContext[4];
? ? ? ? ? ? ? ? } ;
? ? ? ? ? ? } ;
? ? ? ? ? ? // Thread - pointer to caller's Thread Control Block.
? ? ? ? ? ? PETHREAD Thread;
?
? ? ? ? ? ? // Auxiliary buffer - pointer to any auxiliary buffer that is
? ? ? ? ? ? // required to pass information to a driver that is not contained
? ? ? ? ? ? // in a normal buffer.
? ? ? ? ? ? PCHAR AuxiliaryBuffer;
?
? ? ? ? ? ? // The following unnamed structure must be exactly identical
? ? ? ? ? ? // to the unnamed structure used in the minipacket header used
? ? ? ? ? ? // for completion queue entries.
? ? ? ? ? ? struct {
? ? ? ? ? ? ? ? // List entry - used to queue the packet to completion queue, among
? ? ? ? ? ? ? ? // others.
? ? ? ? ? ? ? ? LIST_ENTRY ListEntry;
?
? ? ? ? ? ? ? ? union {
? ? ? ? ? ? ? ? ? ? // Current stack location - contains a pointer to the current
? ? ? ? ? ? ? ? ? ? // IO_STACK_LOCATION structure in the IRP stack. ?This field
? ? ? ? ? ? ? ? ? ? // should never be directly accessed by drivers. ?They should
? ? ? ? ? ? ? ? ? ? // use the standard functions.
? ? ? ? ? ? ? ? ? ? struct _IO_STACK_LOCATION *CurrentStackLocation;
?
? ? ? ? ? ? ? ? ? ? // Minipacket type.
? ? ? ? ? ? ? ? ? ? ULONG PacketType;
? ? ? ? ? ? ? ? };
? ? ? ? ? ? };
?
? ? ? ? ? ? // Original file object - pointer to the original file object
? ? ? ? ? ? // that was used to open the file. ?This field is owned by the
? ? ? ? ? ? // I/O system and should not be used by any other drivers.
? ? ? ? ? ? PFILE_OBJECT OriginalFileObject;
? ? ? ? } Overlay;
?
? ? ? ? // APC - This APC control block is used for the special kernel APC as
? ? ? ? // well as for the caller's APC, if one was specified in the original
? ? ? ? // argument list. ?If so, then the APC is reused for the normal APC for
? ? ? ? // whatever mode the caller was in and the "special" routine that is
? ? ? ? // invoked before the APC gets control simply deallocates the IRP.
? ? ? ? KAPC Apc;
?
? ? ? ? // CompletionKey - This is the key that is used to distinguish
? ? ? ? // individual I/O operations initiated on a single file handle.
? ? ? ? PVOID CompletionKey;
? ? } Tail;
} IRP;
typedef IRP *PIRP;
?
上面出現(xiàn)IRP棧空間,是因為一個IRP往往要傳遞n個設(shè)備才能得以完成,而在傳遞過程中會有一些“中間轉(zhuǎn)換”,導(dǎo)致請求的參數(shù)變化。
為了保存這些變換,我們給每次中轉(zhuǎn)都留一個“??臻g”,用于保存中間參數(shù)。
常見的請求:
生成請求:主功能號為IRP_MJ_CREATE
查詢請求:主功能號為IRP_MJ_QUERY_INFORMATION
設(shè)置請求:主功能號為IRP_MJ_SET_INFORMATION
控制請求:主功能號為IRP_MJ_DEVICE_CONTROL或IRP_MJ_INTERNAL_DEVICE_CONTROL
關(guān)閉請求:主功能號為IRP_MJ_CLOSE
?
請求指針:IRP的指針,即PIRP或IRP*
========
Windows內(nèi)核常見數(shù)據(jù)結(jié)構(gòu)(基本類型)
http://laokaddk.blog.51cto.com/368606/338722/?
學(xué)內(nèi)核從基本數(shù)據(jù)結(jié)構(gòu)開始吧,就像學(xué)C語言時從學(xué)習(xí)int,char開始一樣.
只列出目前見到和用到的,其它后面再補(bǔ)充~
常用數(shù)據(jù)結(jié)構(gòu):
數(shù)字:
lkd> dt _ULARGE_INTEGER
ntdll!_ULARGE_INTEGER
? ?+0x000 LowPart ? ? ? ? ?: Uint4B
? ?+0x004 HighPart ? ? ? ? : Uint4B
? ?+0x000 u ? ? ? ? ? ? ? ?: __unnamed
? ?+0x000 QuadPart ? ? ? ? : Uint8B
lkd> dt _LARGE_INTEGER
ntdll!_LARGE_INTEGER
? ?+0x000 LowPart ? ? ? ? ?: Uint4B
? ?+0x004 HighPart ? ? ? ? : Int4B
? ?+0x000 u ? ? ? ? ? ? ? ?: __unnamed
? ?+0x000 QuadPart ? ? ? ? : Int8B
字符串:
lkd> dt _STRING
nt!_STRING
? ?+0x000 Length ? ? ? ? ? : Uint2B
? ?+0x002 MaximumLength ? ?: Uint2B
? ?+0x004 Buffer ? ? ? ? ? : Ptr32 Char
lkd> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
? ?+0x000 Length ? ? ? ? ? : Uint2B
? ?+0x002 MaximumLength ? ?: Uint2B
? ?+0x004 Buffer ? ? ? ? ? : Ptr32 Uint2B
單鏈表頭(看來數(shù)據(jù)結(jié)構(gòu)要學(xué)好啊)::
lkd> dt _SLIST_HEADER
ntdll!_SLIST_HEADER
? ?+0x000 Alignment ? ? ? ?: Uint8B
? ?+0x000 Next ? ? ? ? ? ? : _SINGLE_LIST_ENTRY
? ?+0x004 Depth ? ? ? ? ? ?: Uint2B
? ?+0x006 Sequence ? ? ? ? : Uint2B
鏈表結(jié)點(diǎn):
lkd> dt _KNODE
ntdll!_KNODE
? ?+0x000 ProcessorMask ? ?: Uint4B
? ?+0x004 Color ? ? ? ? ? ?: Uint4B
? ?+0x008 MmShiftedColor ? : Uint4B
? ?+0x00c FreeCount ? ? ? ?: [2] Uint4B
? ?+0x018 DeadStackList ? ?: _SLIST_HEADER //鏈表頭
? ?+0x020 PfnDereferenceSListHead : _SLIST_HEADER
? ?+0x028 PfnDeferredList : Ptr32 _SINGLE_LIST_ENTRY
? ?+0x02c Seed ? ? ? ? ? ? : UChar
? ?+0x02d Flags ? ? ? ? ? ?: _flags
單鏈表的指針:
lkd> dt _SINGLE_LIST_ENTRY
ntdll!_SINGLE_LIST_ENTRY
? ?+0x000 Next ? ? ? ? ? ? : Ptr32 _SINGLE_LIST_ENTRY
雙向鏈表指針:
lkd> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
? ?+0x000 Flink ? ? ? ? ? ?: Ptr32 _LIST_ENTRY
? ?+0x004 Blink ? ? ? ? ? ?: Ptr32 _LIST_ENTRY
內(nèi)核隊列:
lkd> dt _KQUEUE
ntdll!_KQUEUE
? ?+0x000 Header ? ? ? ? ? : _DISPATCHER_HEADER
? ?+0x010 EntryListHead ? ?: _LIST_ENTRY
? ?+0x018 CurrentCount ? ? : Uint4B
? ?+0x01c MaximumCount ? ? : Uint4B
? ?+0x020 ThreadListHead ? : _LIST_ENTRY
?
一個很多地方用到的頭部結(jié)構(gòu):
lkd> dt _DISPATCHER_HEADER
ntdll!_DISPATCHER_HEADER
? ?+0x000 Type ? ? ? ? ? ? : UChar
? ?+0x001 Absolute ? ? ? ? : UChar
? ?+0x002 Size ? ? ? ? ? ? : UChar
? ?+0x003 Inserted ? ? ? ? : UChar
? ?+0x004 SignalState ? ? ?: Int4B
? ?+0x008 WaitListHead ? ? : _LIST_ENTRY
========
IRP結(jié)構(gòu)
?一、IRP 簡介
? ? ? ? IRP是I/O Request Pcaket 的縮寫,即I/O請求包。驅(qū)動與驅(qū)動之間通過 IRP 進(jìn)行通信。而使用驅(qū)動的應(yīng)用層調(diào)用的?
CreatFile,ReadFile,WriteFile,DeviceIoControl 等函數(shù),說到底也是使用 IRP 和驅(qū)動進(jìn)行通信。IRP由I/O管理器根據(jù)用戶態(tài)程序提
出的請求創(chuàng)建并傳給相應(yīng)的驅(qū)動程序。在分層的驅(qū)動程序中,這個過程很復(fù)雜,一個IRP常常要穿越幾層驅(qū)動程序。
二、IRP結(jié)構(gòu)
? ? ? ? IRP功能的復(fù)雜性也決定了IRP結(jié)構(gòu)的復(fù)雜性。正確理解IRP的結(jié)構(gòu)是理解驅(qū)動程序開發(fā)的基礎(chǔ)。另外,IRP的創(chuàng)建是由I/O管理器
在非分頁內(nèi)存進(jìn)行的。
? ? ? ? 一個IRP有兩部分組成:頭部區(qū)域和I/O堆棧位置。
? ? ? ? 1)IRP的頭部區(qū)域是一個固定的部分,起始就是一個IRP結(jié)構(gòu)。
? ? ? ? 2)在這個頭部的后面是一個I/O stack locations,這是一個IO_STACK_LOCATIONS的結(jié)構(gòu)體數(shù) 組,這個數(shù)組中元素的個數(shù)視具
體情況而定。由 IoAllocateIrp( IN CCHAR StackSize , IN BOOLEAN ChargeQuota ) 時的參數(shù) StackSize 決定。而 StackSize 通常
由 IRP 發(fā)往的目標(biāo) DEVICE_OBJECT 的 +30 char StackSize 決定。而這個 StackSize 是由設(shè)備對象連入所在的設(shè)備棧時,根據(jù)在設(shè)備
棧中位置決定的。
? ? ? ?
? ? ? ?下面看看IRP結(jié)構(gòu)(頭部區(qū)域)和IO_STACK_LOCATIONS(I/O堆棧)的結(jié)構(gòu)的定義
? ? ? ?1. IRP結(jié)構(gòu)介紹,結(jié)構(gòu)圖如下,其中灰色部分為不可見區(qū)域,這里主要講解一下可見區(qū)域。
? ? ? ?
? ? ? ?1.1 PMDL ?MdlAddress : 設(shè)備執(zhí)行直接I/O時,指向用戶空間的內(nèi)存描述表
? ? ? ?1.2 ULONG Flags: 包含一些對驅(qū)動程序只讀的標(biāo)志。但這些標(biāo)志與WDM驅(qū)動程序無關(guān)
? ? ? ?1.3 AssociatedIrp.SystemBuffer : SystemBuffer指針指向一個數(shù)據(jù)緩沖區(qū),該緩沖區(qū)位于內(nèi)核模式的非分頁內(nèi)存中I/O管理器
把用戶模式程序發(fā)送給驅(qū)動程序的數(shù)據(jù)復(fù)制到這個緩沖區(qū),這也是創(chuàng)建IRP過程的一部分。對于讀請求,設(shè)備驅(qū)動程序把讀出的數(shù)據(jù)填到
這個緩沖區(qū),然后I/O管理器再把緩沖區(qū)的內(nèi)容復(fù)制到用戶模式緩沖區(qū)。
? ? ? ?1.4 IoStatus : 是一個結(jié)構(gòu)體IO_STATUS_BLOCK, 這個結(jié)構(gòu)體僅包含兩個域,驅(qū)動程序在最終完成請求時設(shè)置這個結(jié)構(gòu)。
IoStatus.Status : 將收到一個NTSTATUS代碼。
IoStatus.Information 的類型為ULONG_PTR,它將收到一個信息值,該信息值的確切含義要取決于具體的IRP類型和請求完成的狀態(tài)。
Information域的一個公認(rèn)用法是用于保存數(shù)據(jù)傳輸操作。某些PnP請求把這個域作為指向另外一個結(jié)構(gòu)的指針,這個結(jié)構(gòu)通常包含查詢
請求的結(jié)果。
? ? ? ? 1.5 RequestorMode將等于一個枚舉常量UserMode或KernelMode,指定原始I/O請求的來源。驅(qū)動程序有時需要查看這個值來決
定是否要信任某些參數(shù)。
? ? ? ? 1.6 PendingReturned(BOOLEAN)如果為TRUE,則表明處理該IRP的最低級派遣例程返回了STATUS_PENDING。完成例程通過參考該
域來避免自己與派遣例程間的潛在競爭。
? ? ? ? 1.7 Cancel(BOOLEAN)如果為TRUE,則表明IoCancelIrp已被調(diào)用,該函數(shù)用于取消這個請求。如果為FALSE,則表明沒有調(diào)用
IoCancelIrp函數(shù)。取消IRP是一個相對復(fù)雜的主題,我將在本章的最后詳細(xì)描述它。
? ? ? ? 1.8 CancelIrql(KIRQL)是一個IRQL值,表明那個專用的取消自旋鎖是在這個IRQL上獲取的。當(dāng)你在取消例程中釋放自旋鎖時應(yīng)
參考這個域。
? ? ? ? 1.9 CancelRoutine(PDRIVER_CANCEL)是驅(qū)動程序取消例程的地址。你應(yīng)該使用IoSetCancelRoutine函數(shù)設(shè)置這個域而不是直接
修改該域。
? ? ? ? 2.0 UserBuffer(PVOID) 對于METHOD_NEITHER方式的IRP_MJ_DEVICE_CONTROL請求,該域包含輸出緩沖區(qū)的用戶模式虛擬地址。
該域還用于保存讀寫請求緩沖區(qū)的用戶模式虛擬地址,但指定了DO_BUFFERED_IO或DO_DIRECT_IO標(biāo)志的驅(qū)動程序,其讀寫例程通常不需
要訪問這個域。當(dāng)處理一個METHOD_NEITHER控制操作時,驅(qū)動程序能用這個地址創(chuàng)建自己的MDL。
? ? ? ?2. IO堆棧
? ? ??
? ? ? MajorFunction(UCHAR)是該IRP的主功能碼
? ? ? MinorFunction(UCHAR)是該IRP的副功能碼
? ? ? Parameters(union)是幾個子結(jié)構(gòu)的聯(lián)合,每個請求類型都有自己專用的參數(shù),而每個子結(jié)構(gòu)就是一種參數(shù)。這些子結(jié)構(gòu)包
括Create(IRP_MJ_CREATE請求)、Read(IRP_MJ_READ請求)、StartDevice(IRP_MJ_PNP的IRP_MN_START_DEVICE子類型),等等。
? ? ? DeviceObject(PDEVICE_OBJECT)是與該堆棧單元對應(yīng)的設(shè)備對象的地址。該域由IoCallDriver函數(shù)負(fù)責(zé)填寫。
? ? ? FileObject(PFILE_OBJECT)是內(nèi)核文件對象的地址,IRP的目標(biāo)就是這個文件對象。驅(qū)動程序通常在處理清除請求
(IRP_MJ_CLEANUP)時使用FileObject指針,以區(qū)分隊列中與該文件對象無關(guān)的IRP。
? ? ? CompletionRoutine(PIO_COMPLETION_ROUTINE)是一個I/O完成例程的地址,該地址是由與這個堆棧單元對應(yīng)的驅(qū)動程序的更上一
層驅(qū)動程序設(shè)置的。你絕對不要直接設(shè)置這個域,應(yīng)該調(diào)用IoSetCompletionRoutine函數(shù),該函數(shù)知道如何參考下一層驅(qū)動程序的堆棧
單元。設(shè)備堆棧的最低一級驅(qū)動程序并不需要完成例程,因為它們必須直接完成請求。然而,請求的發(fā)起者有時確實需要一個完成例程
,但通常沒有自己的堆棧單元。這就是為什么每一級驅(qū)動程序都使用下一級驅(qū)動程序的堆棧單元保存自己完成例程指針的原因。
? ? ? IO堆??偨Y(jié):
1)I/O堆棧位置的主要目的是,保存一個I/O請求的函數(shù)代碼和參數(shù)。
2)I/O堆棧數(shù)量實際上就是參與I/O請求的I/O層的數(shù)量。
3)在一個IRP中,上層驅(qū)動負(fù)責(zé)為下層驅(qū)動設(shè)置堆棧位置指針。
i)驅(qū)動程序可以為每個IRP調(diào)用IoGetCurrentStackLocation來獲得指向其自身堆棧位置的指針,
ii)上層驅(qū)動程序必須調(diào)用IoGetNextIrpStackLocation來獲得指向下層驅(qū)動程序堆棧位置的指針。
因此,上層驅(qū)動可以在傳送IRP給下層驅(qū)動之前設(shè)置堆棧位置的內(nèi)容。
4)上層驅(qū)動調(diào)用IoCallDriver,將DeviceObject成員設(shè)置成下層驅(qū)動目標(biāo)設(shè)備對象。當(dāng)上層驅(qū)動完成IRP時,IoCompletion 函數(shù)被調(diào)用
,I/O管理器傳送給IoCompletion函數(shù)一個指向上層驅(qū)動的設(shè)備對象的指針。
?
=========================================================================================
假設(shè)某過濾驅(qū)動的分層結(jié)構(gòu)如下:
1 FIDO ?<-- 此時你在這里調(diào)用IoAllocateIrp()創(chuàng)建一個先的IRP往下層驅(qū)動FDO傳送
2 FDO
3 PDO
假設(shè)IO堆棧單元有3個,第3個IO堆棧單元表示new_IRP當(dāng)前IO堆棧單元 第2個表示FDO的IO堆棧單元 第1個表示PDO的堆棧單元那么如果要
發(fā)送此IRP到下層驅(qū)動FDO,則預(yù)先初始化new_IRP的FDO的IO堆棧單元,也就是第二個IO堆棧單元。此時new_IRP 的 StackCount = 2,?
CurrentLocation = 3 這里的3表示new_IRP的當(dāng)前IO堆棧單元 如果你需要把此IRP往FDO驅(qū)動傳遞,那么不用初始化這個IO堆棧單元,為
什么呢?
因為IoCallDriver() 內(nèi)部會調(diào)用 類似 IoSetNextIrpStackLocation() 的操作來調(diào)整 CurrentLocation的索引。也就是 索引-1 ;所以
要調(diào)用IoCallDriver() 把new_IRP傳遞到FDO這個下層驅(qū)動,需要先調(diào)用IoGetNextIrpStackLocation() 獲取FDO對應(yīng)的IO堆棧單元,并
進(jìn)行初始化。初始化完成后才能調(diào)用IoCallDriver()此時new_IRP的 CurrentLocation = 2 這個索引才是指向FDO的IO堆棧單元
下面是來自WMD一書的例子:我上面的話就是為了理解下面的代碼片斷
發(fā)往派遣例程
創(chuàng)建完IRP后,你可以調(diào)用IoGetNextIrpStackLocation函數(shù)獲得該IRP第一個堆棧單元的指針。然后初始化這個堆棧單元。在初始化過程
的最后,你需要填充MajorFunction代碼。堆棧單元初始化完成后,就可以調(diào)用IoCallDriver函數(shù)把IRP發(fā)送到設(shè)備驅(qū)動程序:
PDEVICE_OBJECT DeviceObject; ? ? //something gives you this
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
stack->MajorFunction = IRP_MJ_Xxx;
NTSTATUS status = IoCallDriver(DeviceObject, Irp);
?
IRP中的第一個堆棧單元指針被初始化成指向該堆棧單元之前的堆棧單元,因為I/O堆棧實際上是IO_STACK_LOCATION結(jié)構(gòu)數(shù)組,你可以認(rèn)
為這個指針被初始化為指向一個不存在的“-1”元素,因此當(dāng)我們要初始化第一個堆棧單元時我們實際需要的是“下一個”堆棧單元。
IoCallDriver將沿著這個堆棧指針找到第0個表項,并提取我們放在那里的主功能代碼,在上例中為IRP_MJ_Xxx。然后IoCallDriver函數(shù)
將利用DriverObject指針找到設(shè)備對象中的MajorFunction表。IoCallDriver將使用主功能代碼索引這個表,最后調(diào)用找到的地址(派遣
函數(shù))。
你可以把IoCallDriver函數(shù)想象為下面代碼:
NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)
{
? IoSetNextIrpStackLocation(Irp);
? PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
? stack->DeviceObject = device;
? ULONG fcn = stack->MajorFunction;
? PDRIVER_OBJECT driver = device->DriverObject;
? return (*driver->MajorFunction[fcn])(device, Irp);
}
========
總結(jié)
以上是生活随笔為你收集整理的Windows 内核数据结构学习总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程隐藏学习总结
- 下一篇: 用CFree写的Windows SDK