Windows内核开发-2-开始内核开发-2-

第一个驱动程序:

直接采用vs2019中的Empty WDM Driver 模块创建:

初始的项目文件夹中有一个Driver Files里面会有一个.inf的文件,没用直接删除就好,然后在源文件里面创建一个.cpp的源文件。

DriverEntry和Unload Routines

DriverEntry:

每个驱动都有一个入口点,叫做DriverEntry,就好比平常写的C/C++代码里面的main函数。DriverEntry是由一个叫做IRQL_PASSIVE_LEVEL(0)的系统进程调用出来的。DriverEntry函数原型:

NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING
RegistryPath);

代码原型里的_In _是源代码注释语言(SAL)中的一部分,用来描述函数如何使用其参数,SAL对于编译器来说可以直接忽略,但是对程序员很有帮助。

相关链接:Understanding SAL | Microsoft Docs

这里的最小的DriverEntry示例可以只返回一个状态,比如:

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
return STATUS_SUCCESS;
}

该DriverEntry函数包含在<ntddk.h>头文件中,但是添加了头文件后仍然是编译失败的,因为编译器会把警告当场错误来报错,但是不建议删除该功能,因为有时候警告就是会导致错误诞生:

可以对应修改这些警告,比如这里将形参删除,但是这样仅对于C++好用,因为C++有函数重载,所以这里用不上。这里有一个很经典的解决办法,就是采用一个宏函数:

UNREFERENCED_PARAMETER();

这样就可以暂时解决掉前面的报错说形参没有使用了:

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

但是这样仍然不行:

可以很明显得看出,编译器没问题,但是Linker链接器出了问题,DriverEntry是一个C函数,必须用C的linker来link,但是这里我们采用的是C++的默认,所以必须给该函数设置为C的默认LInker才行

extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

这样最简单的驱动程序的源代码就写好了,就相当于C语言中的:

#include<stdio.h>

int main()
{

return 0;
}

Unload Routines:

这是一个驱动的卸载函数,就相当于C++中类的析构函数一样,当驱动被卸载的时候就会自动调用该函数。在DriverEntry函数中创建的东西需要由Unload Routines来释放,这就非常像C++类中的构造函数和析构函数的关系了。如果没有该函数来释放驱动加载时所开辟的内容就会导致泄露,直到下次电脑重启时内核产生的泄露才会清楚。

该函数的函数指针,必须在DriverEntry给DriverEntry的参数DriverObject中的DriverUnload字段赋值才行。Unload函数和DriverEntry函数一样都需要接受一个_In _ PDRIVER_OBJECT DriverObject 参数,但是Unload函数不需要返回值,直接用void 定义就好。

比如:

#include <ntddk.h>

void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
}

extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

就是一个非常简单但是可以用的驱动了。

部署驱动程序Deploying the Driver

前面已经写好了一个驱动程序,但是我们还需要把它跑起来,驱动程序不像平时写的普通程序一样,采用IDE就可以正常使用了,需要将其加载到系统里面,通常为了避免风险,采用虚拟机来部署驱动程序。

安装驱动程序就像是在User用户态安装服务.exe一样,需要调用CreateService API或者采用现有的工具,这里采用比较常用的Sc.exe来进行部署内核驱动。

注册驱动

采用管理员权限的命令行:

sc create sample type=kernel binPath=C:\DriverTest\MyDriver3.sys

其中 sample是创建的名字,然后type表示创建的权限,binPath后面的是驱动的路径。如果没问天会弹出一个成功的标识符。

并且可以在注册表里面查到:

使用Win+R的弹出框里面输入regedit.exe,查看路径\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\sample,这就是我们刚刚写好且用sc创造的驱动:

启动驱动

前面是注册了该驱动,还得使用它。这里也和User用户态的服务Service类似,需要采用StartService API来使用或者采用一些软件,这里也可以用Sc.exe来继续使用。

sc start sample

这里的sample就是前面注册的驱动的名字。

但是这个通常会失效,因为对于64bit位的windows系统,加载驱动必须得要有驱动的签名才行,这里为了学习方便,避开签名这个东西,可以直接把系统置为测试版本。

bcdedit /set testsigning on

如果你要生成除了Windows10以外的版本,可以在项目属性里面配置你的驱动要部署在的系统环境里面:

最后再使用前面sc来加载驱动时会看到一个关于驱动的输出:

有了这个输出就表明我们的驱动已经成功加载了,可以使用Process Explorer工具来确认是否加载成功(下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer

这里的驱动名称是你自己的sys驱动名称。

卸载驱动

不用了将驱动程序卸下,同样的可以采用 ControlService API或者Sc.exe来处理。Sc指令:

sc stop sample

就OK了。

简单跟踪

为了确保函数有确切被调用,这里提供一种基础的跟踪办法来确保函数被使用,驱动采用KdPrint这个宏来输出类似于printf风格的文本,该宏的内容可以被内核的调试器,或者其它工具查看到。

KdPrint这个宏只在debug模式下采用,它的底层调用的其实是DbgPrint 内核Kernel API。

下面更新一下DriverEntry和Unload函数:

void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
KdPrint(("Sample driver Unload called! \n"));
}

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);
KdPrint(("Sample driver initialized successfully\n !"));

return STATUS_SUCCESS;
}

需要注意的是该宏函数在调用时采用了两个括号,因为它是一个宏函数,但是又显然它是可以接受任意变量的,由于宏函数不能接受可变的变量参数,所以编译器实际上调用的是DbgPrint函数。这里理解不了没关系,先这样用着就行。

重新生成驱动并加载来查看这些Print信息,这里需要采用一个内核的调试器才行,但是为了方便,先采用一个系统的内部工具:DebugView来查看。在使用DebugView之前,需要先给它在注册表里面配置内容不然用不上。

在\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\里新建一个Key(项)Key名为DebugPrintFilter,并且添加一个DWORD类型的值名为DEFAULT,这个DEFAULT要和默认的一个值区别开来,后面那个默认的值是注册表中的每一个项都有的,那个值暂时先不用管,然后给该名为DEFAULT类型为DWORD的变量赋值为8,如下图所示:

然后下载DebugView(DebugView - Windows Sysinternals | Microsoft Docs),并用管理员身份打开它,然后再在Capture选项中去掉Capture Win32 和Capture Global Win32,选中Capture Kernel:

这样,再使用Sc.exe来重新加载驱动就可以看到KdPrint打印的内容了。

总结Summary

这里明白了如何写一个驱动,以及如何再电脑上部署和查看驱动的消息,算是驱动入门了。

Windows内核开发-2-开始内核开发-2-内核开发入门的更多相关文章

  1. 《Windows内核安全与驱动开发》 5.1&5.2 内核与应用方面的编程

    <Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发>  5.1&5.2 内核与应用方面的编程 一.生成控制设备 如果一个驱 ...

  2. 【Android 系统开发】 编译 Android文件系统 u-boot 内核 并烧写到 OK-6410A 开发板上

    博客地址 : http://blog.csdn.net/shulianghan/article/details/40299813  本篇文章中用到的工具源码下载 : -- ok-6410A 附带的 A ...

  3. 内核开发知识第一讲.内核中的数据类型.重要数据结构.常用内核API函数.

    一丶内核中的数据类型 在内核中.程序的编写不能简单的用基本数据类型了. 因为操作系统不同.很有可能造成数据类型的长度不一.而产生重大问题.所以在内核中. 数据类型都一定重定义了. 数据类型 重定义数据 ...

  4. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  5. 关于一次配合开发工作而产生的服务器内核参数问题(Android 网络问题)

    关于一次配合开发工作而产生的服务器内核参数问题(Android 网络问题) 问题转载(本人与作者遇到了同样的问题) 问题描述 问题描述:在这几年的Android开发中,遇到了一个困扰我好久的问题,有时 ...

  6. VELT-0.1.5开发:使用kgdb调试Linux内核【转】

    转自:http://demo.netfoucs.com/lights_joy/article/details/44106589 VELT的全称是Visual EmbedLinuxTools,它是一个与 ...

  7. 【基于mini2440开发板的交叉编译环境及内核树配置.

    在学习linux驱动开发过程中,交叉编译环境的配置及内核树的生成无疑是对linux不是十分了解的新人面前的一堵墙.高高大大的墙...笔者在初探这一方向时,就在这2个问题上苦恼了很久.查阅无数资料,大多 ...

  8. 从Windows角度看Mac OS X上的软件开发

    如果原来从事Windows软件开发,想跨足或转换至Mac OS X环境,需要知道那些东西?有什么知识技能可以快速运用在Mac OS X环境上的?这两个问题应该是Windows开发者进入Mac OS X ...

  9. Windows提高_2.3第三部分:内核区同步

    第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常 ...

  10. 李洪强iOS开发之【零基础学习iOS开发【01-前言】03-前景和难易度分析

    一.iOS开发的前景 2012年3月份,苹果公司的市值已经突破5000亿美元,成为世界上市值最大的公司.5000亿是神马概念呢? 可以帮助陷入欧债危机的8个国家偿还债务 可以买下35个天安门广场.34 ...

随机推荐

  1. Epicor Advanced Unit of Measure

    作为先进的ERP系统,Epicor 不断发展以解决业务问题.2020 年 10 月,Epicor ERP 10.2.700 的一项强大的新功能高级计量单位 (UoM) 已投入生产. 发行说明看似简单, ...

  2. newbee-mall开源项目被慕课网拿去做课程,然后我毫不知情,这又是什么骚操作?

    万万没想到,这种事情会发生在我身上. 之前写过<开源囧事>系列而且已经写了四篇,四次开源囧事如下: <开源囧事(一)捅娄子了,写个bug被国家信息安全漏洞共享平台抓到了?> & ...

  3. jQuery选择器中的特殊符号和关键字

    一般情况下,在jQuery选择器中,我们很少会用到诸如"."."#"."("."["等特殊字符,因为根据W3C规定,HT ...

  4. THINKPHP_(8)_修改TP源码,支持基于多层关联的任一字段进行排序

    之前博文 前述博文THINKPHP_(1)_修改TP源码,支持对中文字符串按拼音进行排序,其解决的主要问题是,对于查询出的think\collection数据,按指定字段对数据进行排序,从而在页面上进 ...

  5. 自动驾驶传感器比较:激光雷达(LiDAR) vs. 雷达(RADAR)

    自动驾驶传感器比较:激光雷达(LiDAR) vs. 雷达(RADAR) 据麦姆斯咨询报道,2032年全球范围内自动驾驶汽车的产量将高达2310万辆,未来该市场的复合年增长率(CAGR)高达58%.届时 ...

  6. TensorFlow csv读取文件数据(代码实现)

    TensorFlow csv读取文件数据(代码实现) 大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 Tens ...

  7. YOLOv4没交棒,但YOLOv5来了!

    YOLOv4没交棒,但YOLOv5来了! 前言 4月24日,YOLOv4来了! 5月30日,"YOLOv5"来了! 这里的 "YOLOv5" 是带有引号的,因为 ...

  8. DMS是临时解决方案?

    DMS是临时解决方案? Who Says DMS Is an Interim Solution? 现在是认真对待DMS驱动程序监控系统的时候了. 特斯拉(Tesla)在台湾高速公路上撞上翻倒卡车的镜头 ...

  9. CUDA刷新器:CUDA编程模型

    CUDA刷新器:CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA,CUDA刷新器,并行编程 这是CUDA更新系列的第四篇文章,它的目标是 ...

  10. jquery给动态生成的元素绑定事件,on函数

    首先先解释一下什么是动态生成的元素:动态生成的元素即我们用jquery的内部插入函数append()所生成的html代码.相对的也有静态生成的元素:即直接编写在页面的html代码. 下面通过例子来讲解 ...