Windows内核开发-2-开始内核开发-2-内核开发入门
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-内核开发入门的更多相关文章
- 《Windows内核安全与驱动开发》 5.1&5.2 内核与应用方面的编程
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 5.1&5.2 内核与应用方面的编程 一.生成控制设备 如果一个驱 ...
- 【Android 系统开发】 编译 Android文件系统 u-boot 内核 并烧写到 OK-6410A 开发板上
博客地址 : http://blog.csdn.net/shulianghan/article/details/40299813 本篇文章中用到的工具源码下载 : -- ok-6410A 附带的 A ...
- 内核开发知识第一讲.内核中的数据类型.重要数据结构.常用内核API函数.
一丶内核中的数据类型 在内核中.程序的编写不能简单的用基本数据类型了. 因为操作系统不同.很有可能造成数据类型的长度不一.而产生重大问题.所以在内核中. 数据类型都一定重定义了. 数据类型 重定义数据 ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- 关于一次配合开发工作而产生的服务器内核参数问题(Android 网络问题)
关于一次配合开发工作而产生的服务器内核参数问题(Android 网络问题) 问题转载(本人与作者遇到了同样的问题) 问题描述 问题描述:在这几年的Android开发中,遇到了一个困扰我好久的问题,有时 ...
- VELT-0.1.5开发:使用kgdb调试Linux内核【转】
转自:http://demo.netfoucs.com/lights_joy/article/details/44106589 VELT的全称是Visual EmbedLinuxTools,它是一个与 ...
- 【基于mini2440开发板的交叉编译环境及内核树配置.
在学习linux驱动开发过程中,交叉编译环境的配置及内核树的生成无疑是对linux不是十分了解的新人面前的一堵墙.高高大大的墙...笔者在初探这一方向时,就在这2个问题上苦恼了很久.查阅无数资料,大多 ...
- 从Windows角度看Mac OS X上的软件开发
如果原来从事Windows软件开发,想跨足或转换至Mac OS X环境,需要知道那些东西?有什么知识技能可以快速运用在Mac OS X环境上的?这两个问题应该是Windows开发者进入Mac OS X ...
- Windows提高_2.3第三部分:内核区同步
第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常 ...
- 李洪强iOS开发之【零基础学习iOS开发【01-前言】03-前景和难易度分析
一.iOS开发的前景 2012年3月份,苹果公司的市值已经突破5000亿美元,成为世界上市值最大的公司.5000亿是神马概念呢? 可以帮助陷入欧债危机的8个国家偿还债务 可以买下35个天安门广场.34 ...
随机推荐
- 使用goland调试远程代码
前言 很多时候我们都在window上使用goland,并直接使用goland调试go代码. 但是很多时候我们的程序运行在Linux服务器上,虽然可以通过dlv命令行进行手动打断点调试,但是太麻烦了. ...
- MongoDB学习笔记:快速入门
MongoDB学习笔记:快速入门 一.MongoDB 简介 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.M ...
- AlexeyAB DarkNet YOLOv3框架解析与应用实践(五)
AlexeyAB DarkNet YOLOv3框架解析与应用实践(五) RNNs in Darknet 递归神经网络是表示随时间变化的数据的强大模型.为了更好地介绍RNNs,我强烈推荐Andrej K ...
- MindInsight张量可视设计介绍
MindInsight张量可视设计介绍 特性背景 张量可视,能够帮助用户直观查看训练过程中的Tensor值,既支持以直方图的形式呈现Tensor的变化趋势,也支持查看某次step的具体Tensor值. ...
- 中继TensorRT集成
中继TensorRT集成 介绍 NVIDIA TensorRT是用于优化深度学习推理的库.这种集成将使尽可能多的算子从Relay转移到TensorRT,从而无需调整调度,即可在NVIDIA GPU上提 ...
- Yolov4性能分析(上)
Yolov4性能分析(上) 一.目录 实验测试 1) 测试介绍 2) Test 3) Train 二. 分析 1.实验测试 1. 1 实验测试方法 Yolov4训练train实验方法(Darkn ...
- 137. 只出现一次的数字 II
2021-04-30 LeetCode每日一题 链接:https://leetcode-cn.com/problems/single-number-ii/ 方法1:使用map记录每个数出现的次数,再找 ...
- Pandas高级教程之:处理text数据
目录 简介 创建text的DF String 的方法 columns的String操作 分割和替换String String的连接 使用 .str来index extract extractall c ...
- N沟通场效应管深度图解(1)工作原理及Multisim实例仿真
场效应晶体管(Field Effect Transistor, FET)简称场效应管,是一种由多数载流子参与导电的半导体器件,也称为单极型晶体管,它主要分型场效应管(Junction FET, JFE ...
- 微信sdk上传图片大小1k,损坏的问题以及微信上传图片需要的配置
微信公众号的appid和appsecret有问题,会导致上传图片大小为1k这个问题 微信上传图片需要设置公众号的'JS接口安全域名'