Windows内核-7-IRP和派遣函数
Windows内核-7-IRP和派遣函数
IRP以及派遣函数是Windows中非常重要的概念。IRP 是I/O Request Pocket的简称,意思是I/O操作的请求包,Windows中所有User和Kernel之间的交流都会被封装成一个IRP结构体,然后不同的IRP会被派遣到不同的派遣函数里面,通过派遣函数来实现I/O操作。
IRP
typedef struct _IRP {
CSHORT Type;
USHORT Size;
PMDL MdlAddress;
ULONG Flags;
union {
struct _IRP *MasterIrp;
__volatile LONG IrpCount;
PVOID SystemBuffer;
} AssociatedIrp;
LIST_ENTRY ThreadListEntry;
IO_STATUS_BLOCK IoStatus;
KPROCESSOR_MODE RequestorMode;
BOOLEAN PendingReturned;
CHAR StackCount;
CHAR CurrentLocation;
BOOLEAN Cancel;
KIRQL CancelIrql;
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;
PVOID UserBuffer;
union {
struct {
union {
KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
struct {
PVOID DriverContext[4];
};
};
PETHREAD Thread;
PCHAR AuxiliaryBuffer;
struct {
LIST_ENTRY ListEntry;
union {
struct _IO_STACK_LOCATION *CurrentStackLocation;
ULONG PacketType;
};
};
PFILE_OBJECT OriginalFileObject;
} Overlay;
KAPC Apc;
PVOID CompletionKey;
} Tail;
} IRP;
IRP这种机制类似于Windows的消息机制,驱动在接受到IRP之后会根据IRP的不同类型分配给不同类型的派遣函数来处理IRP。
IRP不是单独的,只要创建了IRP就会跟着创建IRP的I/O栈,有一个栈是给内核驱动用的:
驱动需要调用IoGetCurrentIrpStackLocation函数来获取内驱驱动对应的I/O栈。
例如:
auto stack = IoGetCurrentIrpStackLocation(Irp)
该API返回一个IO_STACK_LOCATION 结构体:
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
union {
...
...
...
...
...
} Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
这个结构体有两个重要的属性,分别是MajorFunction和MinorFunction,分别记录了IRP的主类型和子类型,操作系统根据MajorFunction来将IRP派遣到不同的派遣函数里面去处理,而还可以继续利用MinorFunction来判断更多的内容,基本上MajorFunction用的比较多。
设置IRP和派遣函数
在DriverEntry驱动对象里面有一个函数指针数组MajorFunction就是来记录派遣函数的地址,来让派遣函数和IRP一一对应,这个数组里面采用宏定义来将每个 IRP和派遣函数通过数组的索引来一一对应:
#define IRP_MJ_CREATE 0x00
#define IRP_MJ_CREATE_NAMED_PIPE 0x01
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
#define IRP_MJ_QUERY_INFORMATION 0x05
#define IRP_MJ_SET_INFORMATION 0x06
#define IRP_MJ_QUERY_EA 0x07
#define IRP_MJ_SET_EA 0x08
#define IRP_MJ_FLUSH_BUFFERS 0x09
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
#define IRP_MJ_DIRECTORY_CONTROL 0x0c
#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
#define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f
#define IRP_MJ_SHUTDOWN 0x10
#define IRP_MJ_LOCK_CONTROL 0x11
#define IRP_MJ_CLEANUP 0x12
#define IRP_MJ_CREATE_MAILSLOT 0x13
#define IRP_MJ_QUERY_SECURITY 0x14
#define IRP_MJ_SET_SECURITY 0x15
#define IRP_MJ_POWER 0x16
#define IRP_MJ_SYSTEM_CONTROL 0x17
#define IRP_MJ_DEVICE_CHANGE 0x18
#define IRP_MJ_QUERY_QUOTA 0x19
#define IRP_MJ_SET_QUOTA 0x1a
#define IRP_MJ_PNP 0x1b
这些每一个都有特定的意思,比较常用的有:
IRP_MJ_CREATE //创建 和CreateFile对应
IRP_MJ_READ //读取 和ReadFile对应
IRP_MJ_WRITE //写入 和WriteFile对应
IRP_MJ_CLOSE //关闭 和CloseFile对应
所有的派遣函数都有一个原型:
typedef NTSTATUS DRIVER_DISPATCH (
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp);
//typedef可以省去,名字自己取
IRP传递流程
I/O系统是以设备对象为中心,而不是以驱动对象为中心的。IRP可以在设备对象中传来传去:
但是不管是在怎么传递,最后都必须把这个IRP请求结束,给它完成。
完成必备操作:
NTSTATUS SysMonRead(PDEVICE_OBJECT, PIRP Irp) {
Irp->IoStatus.Status = status;//设置状态
Irp->IoStatus.Information = count;//统计处理的字节数
IoCompleteRequest(Irp, IO_NO_INCREMENT);//完成IO操作的必须返回函数
return status;
}
IoCompleteRequest这个API需要返回一个IRP大家应该没问题,但是第二个参数就比较复杂了,第二个参数是返回之后的线程级,这是因为一个线程在执行IRP的时候会等待。
这里以ReadFile为例,ReadFile函数会调用ntdll中的NtReadFile函数,然后ntdll中的NtReadFile函数又调用内核的NtReadFile函数,然后内核的NtReadFile函数创建关于Read这个类型的IO_MJ_READ类型的IRP再将它发送到内核的对应IRP的派遣函数里面,然后等待IRP对应派遣函数执行完之后再返回。所以需要设置当返回后线程又重新执行了的线程级。
User操作设备对象
前面我们说了,User只能通过符号链接来操作设备对象进行I/O交互,所以User只需要在User的API下面把通常使用的文件路径改成符号链接就好了。
比如:
CreateFile(L"\\\\.\\test", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr)
注意C语言是有转义字符的,所以这个第一个参数看起来怪怪的。
读写方式
IRP和派遣函数是用来进行I/O操作的,I/O操作就是读写,所以很自然的有了这个读写方式的环境。
一般读写方式有三种:缓冲I/O,直接I/O,和其它方式。三种方式对应的Flags分别是DO_BUFFERED_IO、DO_DIRECT_IO和0。都在DeviceObject设备对象里面添加。
例如:
DeviceObject->Flags |= 0;
注意:并不是直接赋值
缓冲I/O
读写操作通常是WriteFile和ReadFile这类API,这类API会要求添加缓冲区指针和缓冲区大小作为函数参数,然后WriteFile/ReadFile将这段内存的数据传递给驱动程序。由于这段缓冲区是用户模式的内存地址,所以直接使用会非常危险,因为Windows是多进程操作系统,有可能在你用的时候就被别的进程改了,所以直接使用非常危险。
而缓冲I/O的原理就是,在内核模式下开辟一个缓冲空间来存储该缓冲区内容,然后读取的时候先放到内核缓冲区里面,再来进行复制和赋值操作。
比如:当调用ReadFile/WriteFile时,操作系统提供内核模式下的一段地址来存放User在WriteFile里面配置的Buffer,然后当IRP请求结束,内核区域的Buffer就会被拷贝到User/Kernel里面。
1 I/O 管理器从非分页池中分配一个与用户缓冲区大小相同的缓冲区。它将指向这个新缓冲区的指针存储在 IRP 的 AssociatedIrp->SystemBuffer 成员中。 (缓冲区大小可以在当前 I/O 堆栈位置的 Parameters.Read.Length 或 Parameters.Write.Length 中找到。)
2 对于写请求,I/O 管理器将用户的缓冲区复制到系统缓冲区。
3 现在才调用驱动程序的调度例程。驱动程序可以直接使用系统缓冲区指针而无需任何检查,因为缓冲区在系统空间中(它的地址是绝对的 - 从任何进程上下文中都一样),并且在任何 IRQL 中,因为缓冲区是从非分页池分配的,所以它不能被调出。
4 一旦驱动完成IRP(IoCompleteRequest),I/O管理器(对于读请求)将系统缓冲区复制回用户的缓冲区(复制的大小由IRP中设置的IoStatus.Information字段决定)司机)。
5 最后,I/O 管理器释放系统缓冲区。
图文讲解一下ReadFile的整体流程:(WriteFile以此类推)
这里是User下一个Buffer缓冲区:
操作系统知道是缓冲I/O后开辟了一个内核缓冲区,然后由WriteFile/ReadFile创建的Irp->AssociatedIrp.SystemBuffer来存储记录
然后驱动访问系统空间往里面写东西
操作系统将系统空间的内容拷贝到User缓冲区里。
拷贝完了,释放系统空间内存。
派遣函数中可以通过Irp的Parameters.Read.Length来知道User请求多少字节,也可以通过Parameters.Write.Length来知道要写入多少字节,但是真正执行了多少是通过Irp的IoStatus.Information字段来返回,所以WriteFile/ReadFile函数中各个参数是意义就可以解释通透了。
但是这样的缺点是会造成内存空间开销,因为内核空间是公有的大家都得用,而且由于大量数据的复制也会影响效率,所以针对比较小的内容采用缓冲I/O可以采取这种办法,别的还是用直接I/O吧。
//缓冲I/O的例子:
NTSTATUS SysMonRead(PDEVICE_OBJECT, PIRP Irp) {
auto status = STATUS_SUCCESS;
auto stack = IoGetCurrentIrpStackLocation(Irp);//得到当前的IRP栈
auto ulReadLength = stack->Parameters.Read.Length;//得到要读取的字节数
//完成IRP操作
Irp->IoStatus.Status = status;//设置IRP完成状态
Irp->IoStatus.Information = ulReadLength;//设置实际完成的IRP字节数
memset(Irp->AssociatedIrp.SystemBuffer, 0xAA, ulReadLength);//拷贝内容到系统地址空间
IoCompleteRequest(Irp, IO_NO_INCREMENT);//完成IRP操作
return status;
}
User下的就不用写了吧,hh,偷个懒。
直接I/O
直接I/O用了另一个办法来规避风险。
1 I/O 管理器确保用户的缓冲区有效。
2 将其映射到物理内存中,然后将缓冲区锁定在内存中,因此在另行通知之前无法将其调出。这解决了缓冲区访问的问题之一——不会发生页面错误,因此在任何 IRQL 中访问缓冲区都是安全的。
3 I/O 管理器构建内存描述符列表 (MDL),这是一种知道缓冲区如何映射到 RAM 的数据结构。该数据结构的地址存储在 IRP 的 MdlAddress 字段中。
4 此时,驱动程序调用派遣函数。因为用户的缓冲区被锁定在 RAM 中,不能从任意线程访问。所以当驱动程序需要访问缓冲区时,它必须调用将同一用户缓冲区映射到系统地址的函数。由于该地址被锁进了系统中所以,该地址在任何进程上下文中都是有效的。所以本质上,我们得到了到同一个缓冲区的两个映射。一个来自原始地址(仅在请求者进程的上下文中有效),另一个来自系统空间,始终有效。要调用的 API 是 MmGetSystemAddressForMdlSafe,传递由 I/O 管理器构建的 MDL。返回值是系统地址。
5 User或Kernel进行缓冲区修改:
6结束: 一旦驱动程序完成请求,I/O 管理器删除第二个映射(到系统空间),释放 MDL 并解锁用户缓冲区,因此它可以像任何其他用户模式内存一样正常分页。
这里全程没有用到拷贝,完完全全就是通过地址映射User->内存,然后Kernel修改内存等同于直接修改User的缓冲区。
直接I/O在用户态下:
直接I/O操作系统会在User下用MDL这个数据结构来存储相关信息:
大小保存在MDL->ByteCount里,然后这段虚拟内存的首地址在MDL->StartVa里,实际缓冲区的首地址相当于起始地址的偏移地址在ByteOffset里,DDK里面封装了几个宏来方便用
#define MmGetMdlByteCount(Mdl) ((Mdl)->MyteCount)
#define MmGetMdlByteOffset(Mdl) ((Mdl)->ByteOffset)
#define MmGetMdlVirtualAddress(Mdl) (PVOID)((PCHAR)(Mdl)->StartVa)+(Mdl)->ByteOffset)
直接I/O在Kernel下
Kernel下比较简单粗暴直接用了一个宏来得到MDL在内核模式下的地址映射
MmGetSystemAddressForMdlSafe();
然后通过前面缓冲I/O用到的一些Read的length或者Write的Length直接通过这个地址拷贝就行了。
直接I/O例子
NTSTATUS TestRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead\n"));
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulReadLength = stack->Parameters.Read.Length;
KdPrint(("ulReadLength:%d\n",ulReadLength));
ULONG mdl_length = MmGetMdlByteCount(pIrp->MdlAddress);
PVOID mdl_address = MmGetMdlVirtualAddress(pIrp->MdlAddress);
ULONG mdl_offset = MmGetMdlByteOffset(pIrp->MdlAddress);
KdPrint(("mdl_address:0X%08X\n",mdl_address));
KdPrint(("mdl_length:%d\n",mdl_length));
KdPrint(("mdl_offset:%d\n",mdl_offset));
if (mdl_length!=ulReadLength)
{
pIrp->IoStatus.Information = 0;
status = STATUS_UNSUCCESSFUL;
}else
{
//ÓÃMmGetSystemAddressForMdlSafe
PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
KdPrint(("kernel_address:0X%08X\n",kernel_address));
memset(kernel_address,0XAA,ulReadLength);
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
}
pIrp->IoStatus.Status = status;
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave TestRead\n"));
return status;
}
其它
其它的I/O读写方式用得非常少,而且很麻烦,这里就不介绍了。
IO设备控制操作
除了常用的ReadFile,WriteFile,CreateFile,CloseFile这类操作外,还可以通过另一个API DeviceIoControl来操作设备。DeviceIoControl会创建一个IRP_MJ_DEVICE_CONTROL类型的IRP,别的和其它的IRP以及派遣函数是一样的。
DeviceIoControl和驱动交互
DeviceIoControl除了可以被用来读写还可以用在其它操作上。
BOOL DeviceIoControl(
HANDLE hDevice, //设备对象句柄
DWORD dwIoControlCode, //控制码
LPVOID lpInBuffer, //输入缓冲区
DWORD nInBufferSize, //输入缓冲区大小
LPVOID lpOutBuffer, //输出缓冲区
DWORD nOutBufferSize, //输出缓冲区大小
LPDWORD lpBytesReturned, //实际返回字节数 这个对应这Irp->IoStatus.Information
LPOVERLAPPED lpOverlapped //是否OVERLAP操作
);
dwIoControlCode是I/O控制码,也叫IOCTL值。
Windows有一些内置的I/O控制码可以选用:(在官方文档上可以获取:DeviceIoControl function (ioapiset.h) - Win32 apps | Microsoft Docs
同样的我们也可以自己定义,Windows提供了控制码的定义规定:
在ddk头文件里面有一个宏定义方便我们使用:
#define CTL_CODE( DeviceType, Function, Method, Access ) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
DeviceType(31-16):设备对象的类型,这个和IoCreateDevice时设置的设备对象类型一样。
Access(15-14):访问权限,如果没有特殊要求,一般采用FILE_ANY_ACCESS
Funciton:0X000~0X7FF由微软保留,0x800~0xFFF由程序员自己定义
Method:操作模式,以下四种之一:
1 METHOD_BUFFERED: 使用缓冲区方式操作
2 METHOD_IN_DIRECT: 使用直接写方式操作
3 METHOD_OUT_DIRECT: 使用直接读方式操作
4 METHOD_NEITHER: 使用其它方式操作
缓冲内存模式IOCTL
DeviceIoControl的缓冲读取和前面的缓冲I/O有一点不一样,前面的流程都一样,都是复制到系统进程的缓冲区里面,然后这个缓冲区地址可以由Irp->AssociatedIrp.SystemBuffer来获取。不一样的是DeviceIoControl会传入输入和输出两个缓冲区,但是两个缓冲区对应的是一个地址,因为如果是输入就是输出到Kernel里,Kernel可以进行操作后,再把这个缓冲区修改了然后作为输出,输出到User里。
首先定义一个自己的IOCTL码
#define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN,0X800,METHOD_BUFFERED,FILE_ANY_ACCESS)
在内核状态下要使用IOCTL需要添加ntddk头文件,在User下需要添加winioctl.h头文件
NTSTATUS DeviceIoControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
auto stack = IoGetCurrentIrpStackLocation(pIrp);
auto status = STATUS_SUCCESS;
//得到输入缓冲区大小
auto cbIn = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
auto cbOut = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL控制码
auto code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch (code)
{
case IOCTL_TEST1:
{
//处理输入给内核的缓冲区内容
UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i = 0; i < cbIn; i++)
{
KdPrint(("%x\n", InputBuffer[i]));
}
//处理输出给User的缓冲区内容
UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
memset(OutputBuffer, 0xAA, cbOut);
info = cbOut;
break;
}
default:
{
status = STATUS_INVALID_VARIANT;
break;
}
}
pIrp->IoStatus.Information = info;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
直接内存模式IOCTL
直接内存模式IOCTL也和直接I/O有稍许区别,直接IOCTL中的输入缓冲区就是前面的缓冲内存模式下的缓冲区,直接开辟一个系统缓冲区,但是输出缓冲区就是前面的直接I/O,通过地址映射来得到的地址。所以直接内存模式的IOCTL需要分两种来处理,一种是输入缓冲区当缓冲I/O处理,另一种是输出缓冲区当直接I/O来处理。
这里需要说明一下直接模式的
METHOD_IN_DIRECT: 使用直接写方式操作
METHOD_OUT_DIRECT: 使用直接读方式操作
这两种方式的区别,在调用DeviceIoControl的时候是会指定打开的模式是只读还是只写,还是可读可写,就对应着这两种,如果是只读,那么只有METHOD_IN_DIRECT编写的IOCTL控制代码才会识别才会有用,以此类推。
直接内存模式IOCTL的例子:
1 先创建IOCTL
#define IOCTL_TEST1 CTL_CODE(FILE_DEVICE_UNKNOWN,0X801,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
2 编写对应IOCTL的派遣函数
NTSTATUS DeviceIoControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
auto stack = IoGetCurrentIrpStackLocation(pIrp);
auto status = STATUS_SUCCESS;
//得到输入缓冲区大小
auto cbIn = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
auto cbOut = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL控制码
auto code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch (code)
{
case IOCTL_TEST1:
{
//处理输入给内核的缓冲区内容
UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i = 0; i < cbIn; i++)
{
KdPrint(("%x\n", InputBuffer[i]));
}
//处理输出给User的缓冲区内容
UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
memset(OutputBuffer, 0xAA, cbOut);
info = cbOut;
break;
}
case IOCTL_TEST2:
{
//当是IOCTL_TEST2的IOCTL时的代码逻辑
auto InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (int i = 0; i < cbIn; i++)
{
KdPrint(("%X\n", InputBuffer[i]));
}
auto OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
memset(OutputBuffer, 0xAA, cbOut);
info = cbOut;
break;
}
default:
{
status = STATUS_INVALID_VARIANT;
break;
}
}
pIrp->IoStatus.Information = info;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
其它内存模式IOCTL
其它模式用的很少,而且很麻烦,这里也不介绍了。
总结
不管是DeviceIoControl还是ReadFile,WriteFile其实都是用作User和Kernel交互的API,其中的IRP是自带的数据结构体,里面保存了要交互的东西的信息。DeviceIoControl更像输入东西给Kernel让Kernel通过根据输入的内容和控制码来执行命令的一个东西,通过I/O来控制Kernel执行一些代码流程;而ReadFille/WriteFile可能用得更多的是在单纯的交互数据上面。
Windows内核-7-IRP和派遣函数的更多相关文章
- IRP 与 派遣函数
什么是派遣函数: 派遣函数是 WIndows 驱动程序中的重要概念.驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的.也就是说,派遣函数是用来处理驱动程序提交过来的 I ...
- windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数
windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数 1.KeRaiseIrql函数 这个 KeRaiseIrql() 只是简单地调用 hal 模块的 KfRa ...
- 派遣函数IRP
派遣函数是Windows驱动程序中的重要概念.驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的. 用户模式下所有对驱动程序的I/O请求,全部由操作系统转换为一个叫做IR ...
- 《Windows驱动开发技术详解》之派遣函数
驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的.用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”到不同 ...
- Windows内核 基本数据结构
驱动对象: 每个驱动程序都会有唯一的驱动对象与之对应,并且这个驱动对象是在驱动加载时被内核中的对象管理程序所创建的.驱动对象用DRIVER_OBJECT数据结构表示,它作为驱动的一个实例被内核加载,并 ...
- Windows内核开发之串口过滤
学习了几个月的内核编程,现在对Windows驱动开发又了更加深入的认识,特别是对IRP的分层处理逻辑有了深入认识. 总结起来就几句话: 当irp下来的时候,你要根据实际情况,进行处理 1> 无处 ...
- [11]Windows内核情景分析---设备驱动
设备驱动 设备栈:从上层到下层的顺序依次是:过滤设备.类设备.过滤设备.小端口设备[过.类.过滤.小端口] 驱动栈:因设备堆栈原因而建立起来的一种堆栈 老式驱动:指不提供AddDevice的驱动,又叫 ...
- Windows驱动派遣函数的学习
//派遣处理例程的介绍: //IPR简介: //IRP全称(I/O Request Package),即输入输出请求包.他是windows驱动的重要概念,用户模式下所有对驱动程序的I/O请求,全部由操 ...
- 几个常用内核函数(《Windows内核情景分析》)
参考:<Windows内核情景分析> 0x01 ObReferenceObjectByHandle 这个函数从句柄得到对应的内核对象,并递增其引用计数. NTSTATUS ObRefer ...
随机推荐
- 生成python 依赖文件
1.生成requirement 文件的两种方式 1. 'pip freeze ' > requirements.txt --会将环境中的依赖包全都加入(不推荐) 2. 项目用到生成依赖 '# 安 ...
- python中进程详解
1:pdb调试:基于命令行的调试工具,非常类似gnu和gdb调试,以下是常用的调试命令: 可以python -m pdb xxx.py(你的py文件名)进入命令行调试模式 命令 简写命令 作用 bea ...
- Spring Security OAuth2 远程命令执行漏洞(CVE-2016-4977)
影响版本: 2.0.0-2.0.9 1.0.0-1.0.5 poc地址 https://github.com/vulhub/vulhub/blob/master/spring/CVE-2016-497 ...
- Python的round()函数与数学的四舍五入的区别
print(round(0.5))>>0print(round(1.5))>>2print(round(2.5))>>2整数部分为偶 小数为0.5 向下取整 0也是 ...
- BurpSuite 2020.5安装教程
Burpsuite2020.5安装教程 Burpsuite2020.5需要在Java11的环境下才可正常运行. 所以首先安装Java11: 安装Java11 Java SE的安装非常简单,直接下一步, ...
- 防火墙和SElinux简单配置
1.查看防火墙状态 # firewall-cmd --state & #systemctl status firewalld.service 2.停止与开启firew ...
- Unity 不规则按钮实现
1.先重写Image类,实现对Image图形范围的重写: 2.对不规则按钮添加Polygon Collider2D组件,调整大小圈中要点击的范围: 3.将重写的Image类添加到不规则按钮上时,需要移 ...
- JavaGUI三种布局管理器FlowLayout,BorderLayout,GridLayout的使用
三种布局管理器 流式布局FlowLayout package GUI; import java.awt.*; import java.awt.event.WindowAdapter; import j ...
- MySQL学习01(初识MySQL)
初识MySQL 只会写代码的是码农:学好数据库,基本能混口饭吃:在此基础上再学好操作系统和计算机网络,就能当一个不错的程序员.如果能再把离散数学.数字电路.体系结构.数据结构/算法.编译原理学通透,再 ...
- Linux下基于SQLite3 实现商店商品管理系统
文章目录 一.SQLite相关C接口 重要接口 打开数据库 插入信息 查询 二.程序要求 三.程序说明 四.实现代码 水果店账单管理系统 一.SQLite相关C接口 如果第一次直接在命令行安装sqli ...