登录  | 立即注册

游客您好!登录后享受更多精彩

查看: 129|回复: 3

Win64驱动开发教程02-编译和测试内核HelloWorld

[复制链接]
回帖奖励 58 断点币 回复本帖可获得 2 断点币奖励! 每人限 1 次(中奖概率 50%)

68

主题

2

回帖

91

积分

网站编辑

积分
91
发表于 2025-1-8 21:55:33 | 显示全部楼层 |阅读模式

配置好驱动测试环境后,就可以正式编写驱动了。市面上讲解驱动开发的书籍汗牛充栋,但讲得较为太复杂,让初学者不好理解。本文从一个简单的 hello,world 驱动(驱动模板)讲起,力求讲解得简单明了,让大家好理解。
本文主角:
1. DbgView。DbgView 是查看程序调试输出的工具,由美国高富帅 Mark Russinovich 编写(不得不说,此人长得帅,编程技术又牛,让多少男人羡慕妒忌,让多少女人一见倾心)。
下载地址:http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
2.KmdMgr。KmdMgr 是一个由俄国人编写的驱动加载工具。比起国内那些乱七八糟的驱动加载工具,它的特点是可以与驱动进行通信(虽然无法设置 I/O 缓冲区)。





编写驱动:
以下是一个我写的 WIN64 驱动模板

  1. //【0】包含的头文件,可以加入系统或自己定义的头文件
  2. #include <ntddk.h>
  3. #include <windef.h>
  4. #include <stdlib.h>
  5. //【1】定义符号链接,一般来说修改为驱动的名字即可
  6. #define DEVICE_NAME L"\\Device\\KrnlHW64"
  7. #define LINK_NAME L"\\DosDevices\\KrnlHW64"
  8. #define LINK_GLOBAL_NAME L"\\DosDevices\\Global\\KrnlHW64"
  9. //【2】定义驱动功能号和名字,提供接口给应用程序调用
  10. #define IOCTL_IO_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800,
  11. METHOD_BUFFERED, FILE_ANY_ACCESS)
  12. #define IOCTL_SAY_HELLO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801,
  13. METHOD_BUFFERED, FILE_ANY_ACCESS)
  14. //【3】驱动卸载的处理例程
  15. VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
  16. {
  17. UNICODE_STRING strLink;
  18. DbgPrint("[KrnlHW64]DriverUnload\n");
  19. RtlInitUnicodeString(&strLink, LINK_NAME);
  20. IoDeleteSymbolicLink(&strLink);
  21. IoDeleteDevice(pDriverObj->DeviceObject);
  22. }
  23. //【4】IRP_MJ_CREATE对应的处理例程,一般不用管它
  24. NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  25. {
  26. DbgPrint("[KrnlHW64]DispatchCreate\n");
  27. pIrp->IoStatus.Status = STATUS_SUCCESS;
  28. pIrp->IoStatus.Information = 0;
  29. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  30. return STATUS_SUCCESS;
  31. }
  32. //【5】IRP_MJ_CLOSE对应的处理例程,一般不用管它
  33. NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  34. {
  35. DbgPrint("[KrnlHW64]DispatchClose\n");
  36. pIrp->IoStatus.Status = STATUS_SUCCESS;
  37. pIrp->IoStatus.Information = 0;
  38. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  39. return STATUS_SUCCESS;
  40. }
  41. //【6】IRP_MJ_DEVICE_CONTROL对应的处理例程,驱动最重要的函数之一,一般走正常途径调
  42. 用驱动功能的程序,都会经过这个函数
  43. NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  44. {
  45. NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  46. PIO_STACK_LOCATION pIrpStack;
  47. ULONG uIoControlCode;
  48. PVOID pIoBuffer;
  49. ULONG uInSize;
  50. ULONG uOutSize;
  51. DbgPrint("[KrnlHW64]DispatchIoctl\n");
  52. pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  53. //控制码
  54. uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
  55. //输入输出缓冲区
  56. pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
  57. //输入区域大小
  58. uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
  59. //输出区域大小
  60. uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  61. switch(uIoControlCode)
  62. {
  63. //在这里加入接口
  64. case IOCTL_IO_TEST:
  65. {
  66. DWORD dw=0;
  67. //获得输入的内容
  68. memcpy(&dw,pIoBuffer,sizeof(DWORD));
  69. //使用输入的内容
  70. dw++;
  71. //输出处理的结果
  72. memcpy(pIoBuffer,&dw,sizeof(DWORD));
  73. //处理成功,返回非STATUS_SUCCESS会让DeviveIoControl返回失败
  74. status = STATUS_SUCCESS;
  75. break;
  76. }
  77. case IOCTL_SAY_HELLO:
  78. {
  79. DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n");
  80. status = STATUS_SUCCESS;
  81. break;
  82. }
  83. }
  84. if(status == STATUS_SUCCESS)
  85. pIrp->IoStatus.Information = uOutSize;
  86. else
  87. pIrp->IoStatus.Information = 0;
  88. pIrp->IoStatus.Status = status;
  89. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  90. return status;
  91. }
  92. //【7】驱动加载的处理例程,里面进行了驱动的初始化工作
  93. NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING
  94. pRegistryString)
  95. {
  96. NTSTATUS status = STATUS_SUCCESS;
  97. UNICODE_STRING ustrLinkName;
  98. UNICODE_STRING ustrDevName;
  99. PDEVICE_OBJECT pDevObj;
  100. //初始化驱动例程
  101. pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
  102. pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
  103. pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
  104. pDriverObj->DriverUnload = DriverUnload;
  105. //创建驱动设备
  106. RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
  107. status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN,
  108. 0, FALSE, &pDevObj);
  109. if(!NT_SUCCESS(status)) return status;
  110. if(IoIsWdmVersionAvailable(1, 0x10))
  111. RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
  112. else
  113. RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
  114. //创建符号链接
  115. status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
  116. if(!NT_SUCCESS(status))
  117. {
  118. IoDeleteDevice(pDevObj);
  119. return status;
  120. }
  121. //走到这里驱动实际上已经初始化完成,下面添加的是功能初始化的代码
  122. DbgPrint("[KrnlHW64]DriverEntry\n");
  123. return STATUS_SUCCESS;
  124. }
复制代码



如果你懒得认真看完上面的代码,也没问题,我总结几句:1.DriverEntry 就是驱动的main 函数,驱动加载后会从 DriverEntry 开始执行。2.驱动类似 DLL,可以提供接口给应用程序调用,不过以导出函数的方式,而是用一套专门的通信函数 DeviceIoControl。关于应用程序与驱动程序通信,后面会讲。
编译驱动:
1.打开『x64 Free Build Environment』:

1.png

2.切换到源码目录(假设源码目录是:z:\sys),并输入 BUILD (或者bld)编译:

2.png

3.如果看到『1 executable built』字眼,则证明编译成功。
4.驱动的编译跟目录下的 source 文件有关系,比如本例中,它的内容如下(注意不要手贱把空行去掉了,否则可能会导致无法编译):
TARGETNAME=KrnlHW64 <-驱动的文件的名称,一般来说修改这个就行了
TARGETTYPE=DRIVER <-编译的类型
TARGETPATH=obj
INCLUDES=.\
SOURCES = MyDriver.c <-多个 C 文件时,把所有 C 文件的名称分成多行写






测试驱动前的准备:
1.以管理员权限运行 DBGVIEW。
2.把 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug
Print Filter 的 Default 值修改为 ffffffff
3.打开 DBGVIEW 并把以下选项全部勾上:

3.png

标准的驱动测试方法:
1.打开虚拟机,进入双机调试的环境(忘记了就参考上节课的内容)。


2.运行 KmdMgr.exe,把 SYS 拖动到文本框里。

4.png

3.点击“Register”和“Run”按钮,看看输出是否提示成功。如果成功会有类似的输出:

5.png


4.运行 ARK工具,点击内核模块,查看驱动是否已经存在于内核里了:

6.png

5.在 CODE 处输入 222004(为什么是 222004 而不是 801?这个后面会讲到,这里先卖一个关子。但这个数值可以使用 calc_ctl_code.exe 算出来,既输入 801,可以输出 222004),点击“I/O Control”按钮,如果成功会有类似的输出:

7.png

6. 点击“Unregister”和“Stop”按钮,如果成功会有类似的输出:

8.png

很显然,用标准方法测试一个驱动是很麻烦且很耗时的。双机调试非常占用系统资源,虽然我的电脑配置较好(2600K+16GB 内存),但是在操作虚拟机时,仍然感到了明显的卡顿。
下面介绍一种用特殊工具测试驱动的方法,无需双机调试,甚至无需使用虚拟机。
用 WIN64UDL 测试驱动:
1.运行 WIN64UDL。
2.把驱动文件拖进 WIN64UDL 里,然后按下 ENTER 加载。
3.再按一次 ENTER 卸载驱动。

9.png

最后,再补充一种非常麻烦的方法,此方法也算是标准方法之一,适用于没有虚拟机或无法进行双机调试的时候。由于非常麻烦,所以不推荐使用。说实话,谁用此方法测试驱动,绝对是脑门被驴踢了。
1.开启测试模式。管理员权限运行 CMD,输入:bcdedit -set testsigning on。


2.重启计算机
3.用 dseo13b给驱动程序添加测试签名。方法很简单,运行 deso13b,一路 NEXT,当出现这个对话框时,选择“Sign a System File”再点 NEXT:

10.png

4.用任意工具加载驱动。


0

主题

2

回帖

7

积分

新手上路

积分
7
发表于 2025-1-9 01:23:43 | 显示全部楼层
感谢分享!
回复

使用道具 举报

0

主题

15

回帖

28

积分

新手上路

积分
28
发表于 2025-1-9 19:57:23 | 显示全部楼层
study study study
回复

使用道具 举报

0

主题

4

回帖

27

积分

新手上路

积分
27
发表于 5 天前 | 显示全部楼层

回帖奖励 +2 断点币

感谢楼主的慷慨分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|断点社区 |网站地图

GMT+8, 2025-1-18 10:01 , Processed in 0.092738 second(s), 30 queries .

Powered by XiunoBBS

Copyright © 2001-2025, 断点社区.

快速回复 返回顶部 返回列表