开发环境 环境配置 1、配置使用的是VS2019
前提条件:
安装WDK之前一定要看清楚,对应当前VS2019安装的windows SDK版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 次函数可能设置的时间会比较长,虚拟机内存越大,耗时越长 vmxPid:虚拟机进程ID vmware-vmx.exe的ID MajorVersion:虚拟机系统的主版本 MinorVersion:虚拟机系统的次版本 BuildNumber :虚拟机系统的内部版本号 例如:WIN7 MajorVersion=6 MinorVersion=1 BuildNumber=7600 或 7601 例如:WIN10 MajorVersion=10 MinorVersion=0 BuildNumber=18326 代表1903 ----------------------------------------- Windows 11(21H2) 22000 Windows 10(21H1) 19043 Windows 10(20H2) 19042 Windows 10(2004) 19041 Windows 10(1909) 18363 Windows 10(1903) 18362 Windows 10(1809) 17763 Windows 10(1803) 17134 Windows 10(1709) 16299 Windows 10(1703) 15063 Windows 10(1607) 14393 Windows 10(1511) 10586 Windows 10 (1507) 10240 Windows 8.1(更新1) MajorVersion = 6 MinorVersion = 3 BuildNumber = 9600 Windows 8.1 MajorVersion = 6 MinorVersion = 3 BuildNumber = 9200 Windows 8 MajorVersion = 6 MinorVersion = 2 BuildNumber = 9200
根据对应的小版本号去寻找并且安装WDK,因为我系统是win10部操作系统版本号是1904,比如图中我安装的SDK小版本号是19041 ,并且我用的VS2019,这个版本号比较特殊,在官网寻找,发现需要用到win10 2004版本的WDK。
之后下载WDK进行安装,默认安装位置即可。
项目配置 新建项目选择Empty WDM Driver,新建完成后。配置属性页,如果配置所有平台出现,左侧栏少东西,那你就删掉ARM那几个平台
配置代码片段,方便快速开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns ="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet" > <CodeSnippet Format ="1.0.0" > <Header > <Title > DriverMain</Title > <Shortcut > DriverMain</Shortcut > <Description > DriverMain</Description > <Author > Microsoft Corporation</Author > <SnippetTypes > <SnippetType > Expansion</SnippetType > <SnippetType > SurroundsWith</SnippetType > </SnippetTypes > </Header > <Snippet > <Code Language ="cpp" > <![CDATA[ #include <ntifs.h> VOID DriverUnload(PDRIVER_OBJECT pDriver) { } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg) { pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; } ]]> </Code > </Snippet > </CodeSnippet > </CodeSnippets >
新建一个文件,命名为DriverMain.snippet。然后添加进代码片段管理器
编写第一个驱动程序 驱动的话用C开发比较好,因为用C++开发出来的驱动在某些平台会出现蓝屏。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <ntifs.h> VOID DriverUnload (PDRIVER_OBJECT pDriver) { DbgBreakPoint(); } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { DbgPrintEx(0 ,0 ,"第一个驱动测试程序" ); DbgBreakPoint(); pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
编译后得到.sys文件放入win7虚拟机测试。
驱动管理程序进行注册,然后运行。
调试驱动程序 1、配置调试 驱动程序调试不如应用程序调试方便,主要因为驱动程序运行在操作系统内核中,直接调试内核程序难度大。
驱动程序调试需要使用特殊的调试器,如WinDbg,且调试过程通常涉及虚拟机和技术如断点和单步执行。
驱动程序调试时,无法在当前开发环境中直接调试,需要将驱动文件部署到新的操作系统环境中进行
原来我们是手动修改windbg软件的快捷图标信息来配置和虚拟机进行双机调试的,这次我们 VirtualKD 来简化配置过程,提升效率。
具体压缩包目录是这样:
其中target目录是分别针对32位和64位系统的,在对应位数的虚拟机,我们把对应位数的target目录复制到虚拟机内,然后在虚拟机内安装vminstall.exe,这样在启动时会多一个引导项名字是含有Redux。
[!NOTE]
其中虚拟机含win7以上需要按F8,强制禁用驱动签名检测
安装完成后就重新启动。进入对应生成的引导项,启动系统。同时vmmon64.exe是主程序,双击打开。
第一次可能需要配置windbg/KD debugger Path 路径。
2、PDB文件
PDB文件是在我们编译工程的时候产生的,它是和对应的模块(exe或dll)一起生成出来的
每个模块编译的时候都可以生成自己的PDB文件。比如.exe/.dll/.sys等等
3、windbg如何找到pdb文件 对于虚拟机系统的PDB,我们可以通过微软的符号表服务器自动下载。
1 2 SRV*D:\Symbols\XP*http://msdl.microsoft.com/download/symbols kd>.reload
但是对于我们自己写的驱动 呢,却无法从微软的符号表服务器自动下载
1 2 SRV*D:\Symbols\XP*http://msdl.microsoft.com/download/symbols;D:\Projects\_01 HelloDriver\Driver kd>.reload
内核编程基础 1、内核API的使用 在应用层编程我们可以使用WINDOWS提供的各种API函数,只要导入头文件<windows.h>就可以了,但是在内核编程的时候,我们不能像在Ring3那样直接使用。微软为内核程序提供了专用的API,只要在程序中包含相应的头文件就可以使用了,如:#include <ntddk.h> (假设你已经正确安装了WDK)
其中在Windows Vista 版本的 WDK 开始,使用Ntifs.h 作为头文件。Ntifs.h 包含 Ntddk.h ,而 Ntddk.h 又包含 Wdm.h
在应用层编程的时候,我们通过MSDN来了解函数的详细信息,在内核编程的时候,要使用WDK自己的帮助文档
[!NOTE]
在老版本WDK中安装完还会自带帮助文档,但是新版的WDK中不会有文档,需要自己去微软官网查询
2、未导出函数的使用 WDK说明文档中只包含了内核模块导出的函数,对于未导出的函数,则不能直接使用。 如果要使用未导出的函数,只要自己定义一个函数指针,并且为函数指针提供正确的函数地址就可以使用了。
有两种办法都可以获取为导出的函数地址:
先确定,在哪个内核模块,通过遍历你想要找的这个内核函数,搜索特征码可以找到我们想要找的这个未导出的。
案例: 这里我们将会给出一个示例,再搜特征码的时候,一定要用固定不变的硬编码当作特征码,单一条件判断可能会出现重复的函数特征。
系统我用的是WIN7 ,这里我们拿PspTerminateAllProcessesInJob
举例
我们通过pDriver 拿到pDriver->DriverSection 这里可以获取当前加载的所有驱动模块。
这里指向一个链表,而链表里的存储节点结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; struct _ACTIVATION_CONTEXT * EntryPointActivationContext; PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
之后通过遍历链表,对比BaseDllName
在10-10-12分页中需要拿到ntoskrnl.exe ,如果是2-9-9-12分页中,则是ntkrnlpa.exe
当成功拿到需要的节点后,我们获得当前模块的基址pCurrentNode->DllBase
从当前基址开始遍历,获取内存和我们的特征码对比。
定义一个函数指针,创建PspTerminateAllProcessesInJob 函数。
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include <ntifs.h> #include <ntstrsafe.h> typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; struct _ACTIVATION_CONTEXT * EntryPointActivationContext ; PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; typedef BOOLEAN PspTerminateAllProcessesInJob ( PEJOB Job, NTSTATUS Status, BOOLEAN IncCounter ) ;VOID DriverUnload (PDRIVER_OBJECT pDriver) { } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { PLDR_DATA_TABLE_ENTRY pListHead = NULL ; PLDR_DATA_TABLE_ENTRY pCurrentNode = NULL ; ULONG code_sp1 = 0x8b55ff8b ; ULONG code_sp2 = 0xf8e483ec ; ULONG code_sp3 = 0x530cec83 ; ULONG code_sp4 = 0x56085d8b ; ULONG code_sp5 = 0x24358b64 ; PspTerminateAllProcessesInJob* ps = NULL ; UNICODE_STRING moudleName1 = { 0 }; RtlInitUnicodeString(&moudleName1, L"ntoskrnl.exe" ); DbgPrintEx(77 , 0 , "驱动加载\n" ); pListHead = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection; pCurrentNode = pListHead->InLoadOrderLinks.Flink; SIZE_T i = 0 ; while (pListHead != pCurrentNode) { if (RtlCompareUnicodeString(&moudleName1, &pCurrentNode->BaseDllName, TRUE) == 0 ) { DbgPrintEx(77 , 0 , "已经找到--%S\r\n" , pCurrentNode->BaseDllName.Buffer); PVOID moudleBase = pCurrentNode->DllBase; PVOID sizeOfImage = pCurrentNode->SizeOfImage; for (SIZE_T t = moudleBase;t <((ULONG)moudleBase + (ULONG)sizeOfImage);t++) { ULONG code1 = *(PULONG)t; ULONG code2 = *((PULONG)(t + 4 * 1 )); ULONG code3 = *((PULONG)(t + 4 * 2 )); ULONG code4 = *((PULONG)(t + 4 * 3 )); ULONG code5 = *((PULONG)(t + 4 * 4 )); if (code1 == code_sp1 && code2 == code_sp2 && code3 == code_sp3 && code4 == code_sp4 && code5 == code_sp5) { DbgBreakPoint(); DbgPrint("Target Function address is:%x\n" , t); ps = (PspTerminateAllProcessesInJob*)t; ps(); return STATUS_SUCCESS; } } } pCurrentNode = (PLDR_DATA_TABLE_ENTRY)pCurrentNode->InLoadOrderLinks.Flink; } pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
PDB调试文件,就相当于内核里的一份完整的说明书。里边不但包含了。导出函数的相关说明也包含了未导出函数的相关说明,我们可以自己解析它的PDB文件,就像windbg解析它的PDB文件的一样,我们也可以这种方式可以更加灵活的得到。
3、基本数据类型 在内核编程的时候,强烈建议大家遵守WDK的编码习惯,不要这样写:unsigned long length;
习惯使用WDK自己的类型
WDK 的写法
等同于
ULONG
unsigned long
UCHAR
unsigned char
UINT
unsigned int
VOID
void
PULONG
unsigned long*
PUCHAR
unsigned char*
PUNIT
unsigned int*
PVOID
void*
[!NOTE]
1.建议使用WDK定义的数据类型以确保代码可移植性。
2.使用ULONG等类型定义以适应不同编译平台的宽度要求。
4、返回值 大部分内核函数的返回值都是NTSTATUS类型,内核API函数通常返回NTSTATUS,用于指示成功或失败
分别表示一下常见几种情况:
宏
返回值
含义
STATUS_SUCCESS
0x00000000
成功
STATUS_INVALID_PARAMETER
0xC000000D
参数无效
STATUS_BUFFER_OVERFLOW
0x80000005
缓冲区长度不够
当你调用的内核函数,如果返回的结果不是STATUS_SUCCESS,就说明函数执行中遇到了问题,具体是什么问题,可以在ntstatus.h文件中查看。
5、内核中的异常处理 在内核中,一个小小的错误就可能导致蓝屏,比如:读写一个无效的内存地址。为了让自己的内核程序更加健壮,强烈建议大家在编写内核程序时,使用异常处
Windows提供了结构化异常处理机制,一般的编译器都是支持的
1 2 3 4 5 6 __try{ } __except(filter_value) { }
出现异常时,可根据filter_value 的值来决定程序该如果执行,当filter_value 的值为
宏名称
实际值
含义
EXCEPTION_EXECUTE_HANDLER
1
代码进入except块
EXCEPTION_CONTINUE_SEARCH
0
不处理异常,由上一层调用函数处理
EXCEPTION_CONTINUE_EXECUTION
-1
回去继续执行错误处的代码
6、常用的内核内存函数
C语言
内核中
malloc
ExAllocatePool
memset
RtlFillMemory
memcpy
RtlMoveMemory
free
ExFreePool
7、内核字符串种类 CHAR(char)相当于ANSI_STRING,WCHAR相当于UNICODE_STRING
ANSI_STRING 字符串的定义如下
1 2 3 4 5 6 typedef struct _STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; }STRING;
UNICODE_STRING 字符串的定义如下
1 2 3 4 5 6 typedef struct _UNICODE_STRING { USHORT Length; USHORT MaxmumLength; PWSTR Buffer; } UNICODE_STRING;
内核中避免使用传统字符串以防止蓝屏
推荐使用升级版的字符串结构体,如ANSI_STRING 和UNICODE_STRING
8、内核字符串常用函数 字符串常用的功能无非就是:创建、复制、比较以及转换等
ANSI_STRING字符串
UNICODE_STRING字符串
含义
RtlInitAnsiString
RtlInitUnicodeString
创建
RtlCopyString
RtlCopyUnicodeString
复制
RtlCompareString
RtlCompareUnicoodeString
比较
RtlAnsiStringToUnicodeString
RtlUnicodeStringToAnsiString
转换
内核空间与内核模块 1、内核空间
内核模块定义全局变量:在不同进程中查看
在2g中所有模块共享一块内存
现在我设置一个全局变量在驱动里,然后注册运行通过dbgview查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <ntifs.h> ULONG a = 0x12345678 ; VOID DriverUnload (PDRIVER_OBJECT pDriver) { DbgBreakPoint(); } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { DbgPrint("%x\n" , &a); pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
现在我们随便找一个进程,通过.process来选择进程查看,我们发现在dbgview的内存中存在那个全局变量的值
证实了在4GB的内存空间中高2G是共享的,高2g就是内核空间。
2、内核模块
硬件种类繁多,不可能做一个兼容所有硬件的内核,所以,微软提供规定的接口格式,让硬件驱动人员安装规定的格式编写“驱动程序” 。
这些驱动程序每一个都是一个模块,称为“内核模块”,都可以加载到内核中,都遵守PE结构。但本质上讲,任意一个.sys文件与内核文件没有区别。
3、DRIVER_OBJECT 每个内核模块都有一个对应的结构体,来描述这个模块在内核中的:位置、大小、名称等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kd> dt _DRIVER_OBJECT nt!_DRIVER_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 _DEVICE_OBJECT +0x008 Flags : Uint4B +0x00c DriverStart : Ptr32 Void +0x010 DriverSize : Uint4B +0x014 DriverSection : Ptr32 Void +0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING +0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH +0x02c DriverInit : Ptr32 long +0x030 DriverStartIo : Ptr32 void +0x034 DriverUnload : Ptr32 void +0x038 MajorFunction : [28 ] Ptr32 long
4、打印DRIVER_OBJECT地址 1 DbgPrint("DRIVER_OBJECT对象地址:%x \r\n" ,driver);
通过DRIVER_OBJECT找到当前模块信息:
1 2 3 DbgPrint("驱动名称:%ws \r\n" ,driver->DriverName.Buffer); DbgPrint("模块基址:%x \r\n" ,driver->DriverStart); DbgPrint("模块大小:%x \r\n" ,driver->DriverSize);
5、遍历内核模块
dt _DRIVER_OBJECT (地址)
dt _LDR_DATA_TABLE_ENTRY (DriverSection)地址
dt _LDR_DATA_TABLE_ENTRY (InLoadOrderLinks.Flink)地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 kd> dt _LDR_DATA_TABLE_ENTRY 0x87ba7430 nt!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x83f82850 - 0x87a114c8 ] //双向链表 +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0xffffffff - 0xffffffff ] +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x1c - 0x0 ] +0x018 DllBase : 0x8d600000 Void +0x01c EntryPoint : 0x8d604000 Void +0x020 SizeOfImage : 0x6000 +0x024 FullDllName : _UNICODE_STRING "\??\C:\Users\Administrator\Desktop\MyDriver1.sys" +0x02c BaseDllName : _UNICODE_STRING "MyDriver1.sys" +0x034 Flags : 0x49104000 +0x038 LoadCount : 1 +0x03a TlsIndex : 0x6d +0x03c HashLinks : _LIST_ENTRY [ 0x0 - 0xabfc ] +0x03c SectionPointer : (null) +0x040 CheckSum : 0xabfc +0x044 TimeDateStamp : 0x490057 +0x044 LoadedImports : 0x00490057 Void +0x048 EntryPointActivationContext : (null) +0x04c PatchInformation : 0x86344c99 Void +0x050 ForwarderLinks : _LIST_ENTRY [ 0x0 - 0x6000 ] +0x058 ServiceTagLinks : _LIST_ENTRY [ 0x67277d47 - 0x79004d ] +0x060 StaticLinks : _LIST_ENTRY [ 0x720044 - 0x760069 ] +0x068 ContextInformation : 0x00720065 Void +0x06c OriginalBase : 0x2e0031 +0x070 LoadTime : _LARGE_INTEGER 0x00000073`00790073
0环与3环通信(常规方式) 1、设备对象 我们在开发窗口程序的时候,消息被封装成一个结构体:MSG
在内核开发时,消息被封装成另外一个结构体:IRP(I/O Request Package)
在窗口程序中,能够接收消息 的只能是窗口对象 。在内核中,能够接收IRP消息 的只能是设备对象
2、创建设备对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 UNICODE_STRING Devicename; RtlInitUnicodeString(&Devicename,L"\\Device\\MyDevice" ); IoCreateDevice( pDriver, 0 ,&Devicename, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj );
3、设置交互数据的方式 1 pDeviceObj->Flags |= DO_BUFFERED_IO;
缓冲区方式读写(DO_BUFFERED_IO) :操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中。
直接方式读写(DO_DIRECT_IO) :操作系统会将用户模式下的缓冲区锁住。然后操作系统将这段缓冲区在内核模式地址再次映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。缺点就是要单独占用物理页面。
其他方式读写(在调用IoCreateDevice创建设备后对pDevObj->Flags即不设置DO_BUFFERED_IO也不设置DO_DIRECT_IO此时就是其他方式) :在使用其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲区地址。在驱动程序中,直接操作应用程序的缓冲区地址是很危险的。只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式。
4、创建符号链接 1 2 3 4 5 RtlInitUnicodeString(&SymbolicLinkName,L"\\??\\MyTestDriver" ); IoCreateSymbolicLink(&SymbolicLinkName,&Devicename);
[!NOTE]
1、设备名称的作用是给内核对象用的,如果要在Ring3访问,必须要有符号链接其实就是一个别名,没有这个别名,在Ring3不可见。
2、内核模式下,符号链接是以“??\”开头的,如C 盘就是“??\C:”
3、而在用户模式下,则是以“\.\”开头的,如C 盘就是“\.\C:“
5、IRP与派遣函数
在3环会将事件封装澄一个MSG对象,窗口对象接收,然后触发对应的回调函数。
同样在0环也是会将事件封装澄一个IRP对象,设备对象接收,然后触发对应的派遣函数。
6、IRP的类型
IRP类型
来源
作用
IRP_MJ_CREATE
CreateFile
打开设备
IRP_MJ_READ
ReadFile
设备读取数据
IRP_MJ_WRITE
WriteFile
设备写入数据
IRP_MJ_CLOSE
CloseHandle
关闭设备
其他类型:
IRP类型
来源
IRP_MJ_DEVICE_CONTROL
DeviceControl函数会产生此IRP
IRP_MJ_POWER
在操作系统处理电源消息时,产生次IRP
IRP_MJ_SHUTDOWN
关闭系统前会产生此IRP
7、派遣函数注册位置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kd> dt _DRIVER_OBJECT nt!_DRIVER_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 _DEVICE_OBJECT +0x008 Flags : Uint4B +0x00c DriverStart : Ptr32 Void +0x010 DriverSize : Uint4B +0x014 DriverSection : Ptr32 Void +0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING +0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH +0x02c DriverInit : Ptr32 long +0x030 DriverStartIo : Ptr32 void +0x034 DriverUnload : Ptr32 void +0x038 MajorFunction : [28 ] Ptr32 long
8、注册派遣函数 派遣函数种类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #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 #define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete.... #define IRP_MJ_MAXIMUM_FUNCTION 0x1b
注册派遣函数格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 NTSTATUS DriverEntry ( 。。。。) { pDriverObject->DriverUnload = 卸载函数; pDriverObject->MajorFunction[IRP_MJ_CREATE] = 派遣函数1 ; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = 派遣函数2 ; pDriverObject->MajorFunction[IRP_MJ_WRITE] = 派遣函数3 ; pDriverObject->MajorFunction[IRP_MJ_READ] = 派遣函数4 ; pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = 派遣函数5 ; pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = 派遣函数6 ; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 派遣函数7 ; pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = 派遣函数8 ; pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = 派遣函数9 ; } IRP_MJ_MAXIMUM_FUNCTION 派遣函数的最大值
9、派遣函数的格式 1 2 3 4 5 6 7 8 9 10 11 12 NTSTATUS MyDispatchFunction (PDEVICE_OBJECT pDevObj, PIRP pIrp) { pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0 ; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; }
10、通过IRP_MJ_DEVICE_CONTROL交互数据 应用层调用DeviceControl函数会产生此IRP.
代码: Demo代码 驱动代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <ntifs.h> #define Deivce_Name L"\\Device\\hackflame1" #define Symbol_Name L"\\??\\hackflame1" NTSTATUS dispatchCreate (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgBreakPoint(); DbgPrint("dispatchCreate执行成功\n" ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS dispatchClose (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgBreakPoint(); DbgPrint("dispatchClose执行成功\n" ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS dispatchDeviceControl (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgBreakPoint(); DbgPrint("dispatchDeviceControl执行成功\n" ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID DriverUnload (PDRIVER_OBJECT pDriver) { UNICODE_STRING symName; RtlInitUnicodeString(&symName, Symbol_Name); IoDeleteSymbolicLink(&symName); if (pDriver->DeviceObject) IoDeleteDevice(pDriver->DeviceObject); DbgPrintEx(77 , 0 , "驱动卸载完成" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { UNICODE_STRING deviceName = { 0 }; UNICODE_STRING symName = { 0 }; RtlInitUnicodeString(&deviceName, Deivce_Name); RtlInitUnicodeString(&symName, Symbol_Name); PDEVICE_OBJECT pDevice = NULL ; NTSTATUS status = IoCreateDevice(pDriver, 0 , &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice); if (!NT_SUCCESS(status)) { KdPrintEx((77 , 0 , "[db]:%x\r\n" , status)); return status; } status = IoCreateSymbolicLink(&symName, &deviceName); if (!NT_SUCCESS(status)) { IoDeleteDevice(pDriverObj); KdPrintEx((77 , 0 , "[db]:%x\r\n" , status)); return status; } pDevice->Flags &= ~DO_DEVICE_INITIALIZING; pDevice->Flags |= DO_BUFFERED_IO; pDriver->MajorFunction[IRP_MJ_CREATE] = dispatchCreate; pDriver->MajorFunction[IRP_MJ_CLOSE] = dispatchClose; pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatchDeviceControl; pDriver->DriverUnload = DriverUnload; return status; }
3环代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> #include <windows.h> #include <winioctl.h> int main () { HANDLE hfile = CreateFileA("\\\\.\\hackflame1" , GENERIC_READ | GENERIC_WRITE,0 , 0 , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0 ); DWORD status = GetLastError(); if (!hfile) { printf ("status:%d\n" , status); system("pause" ); return 0 ; } printf ("status:%d\n" , status); CloseHandle(hfile); }
驱动通信案例代码 驱动代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 #include <ntifs.h> #define OPER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define Deivce_Name L"\\Device\\hackflame1" #define Symbol_Name L"\\??\\hackflame1" NTSTATUS dispatchCreate (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgPrint("dispatchCreate执行成功\n" ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS dispatchClose (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgPrint("dispatchClose执行成功\n" ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS dispatchDeviceControl (PDEVICE_OBJECT DeviceObject, PIRP Irp) { DbgBreakPoint(); DbgPrint("dispatchDeviceControl执行成功\n" ); ULONG uRead, uWrite; NTSTATUS retStatus = NULL ; PIO_STACK_LOCATION pCurrentStack = IoGetCurrentIrpStackLocation(Irp); ULONG code = pCurrentStack->Parameters.DeviceIoControl.IoControlCode; PVOID pIoBuffer = Irp->AssociatedIrp.SystemBuffer; switch (code) { case OPER: memcpy (&uRead,pIoBuffer,4 ); DbgPrint("uRead: %x \n" , uRead); uWrite = 0x123456 ; memcpy (pIoBuffer, &uWrite, 4 ); Irp->IoStatus.Information = 4 ; retStatus = STATUS_SUCCESS; break ; default : break ; } Irp->IoStatus.Status = retStatus == STATUS_SUCCESS ? retStatus : STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID DriverUnload (PDRIVER_OBJECT pDriver) { UNICODE_STRING symName; RtlInitUnicodeString(&symName, Symbol_Name); IoDeleteSymbolicLink(&symName); if (pDriver->DeviceObject) IoDeleteDevice(pDriver->DeviceObject); DbgPrintEx(77 , 0 , "驱动卸载完成" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { UNICODE_STRING deviceName = { 0 }; UNICODE_STRING symName = { 0 }; RtlInitUnicodeString(&deviceName, Deivce_Name); RtlInitUnicodeString(&symName, Symbol_Name); PDEVICE_OBJECT pDevice = NULL ; NTSTATUS status = IoCreateDevice(pDriver, 0 , &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice); if (!NT_SUCCESS(status)) { KdPrintEx((77 , 0 , "[db]:%x\r\n" , status)); return status; } status = IoCreateSymbolicLink(&symName, &deviceName); if (!NT_SUCCESS(status)) { IoDeleteDevice(pDevice); KdPrintEx((77 , 0 , "[db]:%x\r\n" , status)); return status; } pDevice->Flags &= ~DO_DEVICE_INITIALIZING; pDevice->Flags |= DO_BUFFERED_IO; pDriver->MajorFunction[IRP_MJ_CREATE] = dispatchCreate; pDriver->MajorFunction[IRP_MJ_CLOSE] = dispatchClose; pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatchDeviceControl; pDriver->DriverUnload = DriverUnload; return status; }
三环代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <windows.h> #include <winioctl.h> #define OPER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) HANDLE g_hDevice; int main () { HANDLE g_hDevice = CreateFileA("\\\\.\\hackflame1" , GENERIC_READ | GENERIC_WRITE,0 , 0 , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0 ); DWORD code = GetLastError(); if (code != 0 ) { printf ("status:%d\n" , code); system("pause" ); return 0 ; } LPVOID inBuffer , outBuffer; inBuffer = (LPVOID)0x223456 ; DWORD size = 4 ; DWORD dwLen; DeviceIoControl(g_hDevice, OPER,&inBuffer,0x10 ,&outBuffer,0x10 ,&dwLen,NULL ); printf ("outBuffer:%x \n" , outBuffer); CloseHandle(g_hDevice); return 0 ; }