windows驱动学习_Second
一.内核空间和用户空间
1.在单个进程内的编程特点:
(1)可以自由使用通用寄存器,不用关心这些寄存器被其他进程修改。不同进程看似各自拥有一套通用寄存器。
(2)原则上可以自由使用0-N范围内的内存空间,N的大小取决于操作系统的位数,每个进程的用户空间内存是隔离的。
(3)通过操作系统约定的方式与其他进程共享其他资源。
因此,在单个进程内编程只需要定义和使用本进程所需要的资源,并编写代码操作这些资源,不需要关心其他进程。
2.进程的空间实际上被分成两部分。一部分供进程独立使用,称为用户空间;另一部分容纳操作系统的内核,称为内核空间或称为系统空间。
3.用户空间是各个进程隔离的,但是内核空间是共享的。
4.内核空间是收到硬件保护的,比如x86架构下R0层(Ring 0)的代码才可以访问内核空间,普通应用程序编译出来之后都运行的R3层,R3层的代码要调用R0层的功能时,一般通过操作系统提供的一个入口来实现。这样应用程序既可以调用操作系统内核中提供的功能,又不至于得到随意修改内核的权利。
5.内核模块位于内核空间,而内核空间又被所有的进程共享。因此内核模块实际上是位于任何一个进程空间中。但是任意一段代码的任意一次执行,一定是位于某个进程空间中的。这个进程是哪个可以通过PsGetCurrentProcessId函数能获得当前进程的进程号。
二.数据类型
1.使用宏NT_SUCCESS()可以判断一个返回值是否成功,如果成功,毫无疑问返回STATUS_SUCESS。(返回值定义存在ntstatus.h中)。
2.UNICODE_STRING是宽字符,双字节的,可直接用DbgPrint打印,用%wZ。
三.重要的数据结构
1.驱动对象
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;
和编写一个应用程序,windows直接从main()函数开始执行来生成一个进程不同,内核模块并不生成一个进程,只写一组回调函数让windows来调用,而且这组回调函数必须符合windows内核规定。如上述结构体中,这一组回调函数包括“普通分发函数” MajorFunction和“快速分发函数” FastIoDispatch,这些函数用来处理和发送给这个内核模块的请求。
2.设备对象
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _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; // See above: DO_...
ULONG Characteristics; // See ntioapi: FILE_...
__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;
//
// 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;
在内核世界里,大部分“消息”都以请求IRP的方式传递,而设备对象是唯一可以接受请求的实体,任何一个请求都是发送给某个设备对象的。
四.请求
大部分请求以IRP的形式发送,IRP是一个内核数据结构,这个结构表示无数种实际请求。一个IRP往往要传递n个设备才能得以完成,在传递过程中,有可能会有一些“中间变换”,导致请求的参数变化,为了保存这种参数变化,给每次“中转”都留一个“栈空间”,用来保存中间参数。
五.函数
内核中printf、scanf及fopen、fclose、fwrite、fread、malloc、free、strdup这些函数无法使用,因为在内核没有控制台,而且读\写文件也不那么轻松。但是sprintf、strlen、strcpy、wcslen、wcscpy、memcpy、memset都是可以的。
六.代码的中断级
Passive级和Dispatch级
七.特殊代码
#pragma alloc_text 用来指定某个函数的可执行代码在编译出来后再sys文件中的位置;INIT节的特点是在初始化完毕后就被释放,不再占用内存空间;PAGE节的特点是位于可以进行分页交换的内存空间,这些空间在内存紧张时可以被交换到硬盘上已节省内存;PAGELK节加载后位于不可分页交换的内存空间。(放在PAGE节的函数不可以在Dispatch级调用,因为这种函数调用可能诱发缺页中断,但是缺页中断处理不能再Dispatch级完成,一般用PAGED_CODE()进行测试,如果发现当前中断级为Dispatch级,则程序直接报异常)