1. (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4。内核程序的编写有一定的规则:

    • 不能调用windows运用层API函数
    • 很多C标准函数失去意义,如printf,fopen,fwrite等,它们有专门的内核函数
    • 很多单纯的C标准,如string.h(不涉及I/O及网络等)等还是适用
    • 可以使用标准C语言,但是用浮点数之前要特殊处理
    • WDK提供大量System Routine以供调用,相当于运用层给程序员提供的API
    • 在内核空间可以做任何事情,可访问任何进程空间地址,可以修改windows内核原有函数
    • Sys文件可以使用IDA进行反汇编
  2. 调试环境已经搭建好,现在来搭建一下编译环境,使用VS系列,VC++因为编译器版本太低了,不支持我安装的WDK,听说是有人解决了这个问题,没找了,实在不想在环境上面折腾得要命。使用的是VS2005,VS2012应该也类似,对于一些注意的先记录下,以后要是有用2012的冲动试试再说。

    • 打开VS2005,新建win32控制台运用程序,建立一个空项目DriverTest。将驱动源文件first.c复制过来,在项目中打开
    • 进入配置管理器,新建一个解决方案配置,比如说“DriverDebug”
    • 选择“工具”->“选项”->“在项目和解决方案”的“VC++目录”中选择“包含文件”,添加:WDk安装目录和版本\inc\crt,WDk安装目录和版本\inc\ddk,WDk安装目录和版本\inc\api。注意将它们置顶(置顶后可能对编译其它类型工程有问题,比如标准CPP控制台程序,需要将“$(VCInstallDir)include”置顶,所以最好让WDK排它后面)
    • 选择“库文件”,添加:WDk安装目录和版本\lib\wxp\i386,置顶。
    • 进入配置属性,展开“C/C++”选项卡,在“常规”中将警告等级设置为2级(可选),将“调试信息格式”设置为“C7兼容”(可选)
    • 编辑“预处理器”定义为:WIN32=100;_X86_=1;WINVER=0x501;DBG=1(必选)
    • 在“代码生成”关闭“缓冲区安全检查”(可选),“运行时库”设置为“多线程调试”或者“多线程”(建议)
    • 在“高级”中修改调用约定为“__stdcall”(必选)
    • 进入“链接器”选项卡,在“常规”中修改“输出文件”后缀为.sys(必选),“启用增量链接”设置为“否”(建议)
    • 在“输入”中的“附加依赖项”添加:wdm.lib(WDM式驱动)或者ntoskrnl.lib(NT式驱动)(必选),“忽略所有默认库”选择为“是”(可选)
    • VS2008及以上版本中,设置“清单文件”的“启用用户帐号控制(UAC)”设置为“否”(必选)
    • 在“系统”中选择“子系统”为:控制台(必选),将“驱动程序”设置为:驱动程序(必选)
    • 在“高级”中将“输入点”填写为:DriverEntry(必选),VS2008以上版本将“随机基址”和“数据执行保护(DEP)”设置为:默认值(必选)
    • 在“命令行”的“附加选项”的“添加开关”填写:/SECTION:INIT,D /IGNORE:4078(建议)
    • 编译以后在工程目录的driverdebug目录下将生成DriverTest.sys。这本书,真的不适合内核开发的入门呀。。。。为了读它,要找N多相关资料。。。这里将会有很大东西不是原书上的!
  3. First.c源代码分析:DriverEntry为驱动入库函数,相当于win32编程中的main函数,接受两个参数:

    • PDRIVER_OBJECT:结构体指针,用于传递驱动对象,由I/O管理器传递进来。只要有驱动程序,就会有驱动对象DRIVER_OBJECT,I/O管理器调用nt!IopLoadDriver 读取注册表,获得驱动程序文件.SYS 的路径,将这个文件载入内存。之后会调用 nt!ObCreateObject 创建一个驱动对象,并初始化这个驱动对象。DRIVER_OBJECT结构体是驱动对象存在的形式
    • PUNICODE_STRING:结构体指针,用来指向驱动负责的注册表,也就是驱动程序在注册表中的路径
    • 入库函数一般需要放在INIT标志的内存,指明该函数只是在加载时载入内存,加载完成后该函数可从内存中卸载,使用语句(当然,不加也不会有错误。):
    •  #define INITCODE code_seg(“INIT”)
      
       #pragma INITCODE
    • driver->DriverUnload = DriverUnload(功能:驱动卸载)用于指定回调函数,DbgPrint输出,可以用KdPrint宏取代,后者是对前者的封装,在测试版中输出信息,而在发行版中什么都不做,但是要注意格式为KdPrint((“……”))形式(即一定要双括号)
  4. DebugView的使用:打开软件,在capture(捕获)菜单栏开启Capture Kernel(捕获内核)以及Capture Events(捕获事件)。然后使用InstDrv加载驱动并且运行,就可以看到驱动的DbgPrint或者KdPrint输出信息

  5. R3与RO联系:Win32是纯正的windows子系统,提供大量API函数,这些API分为2类,即:USER函数,GDI函数,KERNEL函数。NT native API是可由用户模式和核心模式程序调用的NT系统服务接口,它们直接由NT操作系统实现。SSDT是系统服务描述表,这个表把R3的API和R0的API联系起来,它不仅是一个地址索引表,还包含一些其他的信息,如:地址所有基地址、服务函数个数。

    • KERNEL函数通过SYSENTER查找SSDT表从运用层函数切换到内核层函数
    • USR函数和GDI函数通过SYSENTER查找Shadow SSDT表从运用层函数切换到内核层函数
  6. WinDbg指令:

    • g:运行
    • u:反汇编,如u ddk(模块名)!DriverEntry则表示反汇编入库函数
    • bp:下执行断点int 3(CC),如bp ddk(模块名)!DDK_Unload
    • bl:列举断点,列表中第一项表示序号,第二项的是e表示执行断点,是d表示禁用断点
    • bd:禁用断点,如bd 0(序号)
    • bc:清除断点,如bc 0(序号),这3个断点操作在菜单栏edit的breakpoints中可以看到
    • a:编辑汇编代码,如a  f8ef48c3(地址)进入交互模式,然后输入修改后的汇编代码如:xor        eax,eax,持续输入汇编代码可以改写之后的汇编语句
    • d:查看寄存器指向的地址,如d esp
    • dd:直接查看内存地址,如dd 054efc14
    • poi:类似C语言指针操作符*,如dd poi 054efc14查看054efc14保存的地址值对应的地址中的内容
    • dt:查看内核数据结构,如dt nt!_DRIVER_OBJECT可查看DRIVER_OBJECT结构体
  7. 添加驱动设备(编程的话,大部分函数都在Microsoft文档中可以看到(安装WDK的时候后在help栏有ducmentation,其中有离线版),做两个引例吧,这些东西记不住也没必要去记):

     //_stdcall
     #include <ntddk.h>
     #define INITCODE code_seg("INIT")
     #define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/
     #pragma INITCODE /*指的代码运行后 就从内存释放掉*/
     NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject)
     {
         NTSTATUS status;
         PDEVICE_OBJECT pDevObj;/*用来返回创建设备结构体的指针*/
         //设备对象(DEVICE_OBJECT)由驱动创建。一个驱动可以创建多个设备对象。通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象。一个驱动创建的所有设备对象链成一条链。该驱动的驱动对象可以找到这个链,一个设备对象也可以找到创建它的驱动的驱动对象。DEVICE_OBJECT是设备对象存在的形式
    
         //创建设备名称
         UNICODE_STRING devName;
         UNICODE_STRING symLinkName; // 结构体,包含了宽字节字符缓冲区与其长度
         RtlInitUnicodeString(&devName,L"\\Device\\testDDK_Device");//*对devName初始化字串为 "\\Device\\testDDK_Device"
         //这个宽字节的路径“\\Device\\ ”部分不能改变,后面是设备名称
    
         //创建设备对象
         status = IoCreateDevice( pDriverObject,\    //驱动程序对象指针。在入库函数DriverEntry过程里接收
             ,\    //指定驱动程序为设备扩展对象定义的结构体大小
             &devName,\    //设备名称,必须是完整的设备路径名,设置为NULL则是无名设备
             FILE_DEVICE_UNKNOWN,\    //设备类型
             , TRUE,\    //驱动程序的其它信息以及指定设备是否是独占的,TRUE则是
             &pDevObj);//输出,用来保存PDEVICE_OBJECT结构体指针,这个指针指向设备对象自身
         if (!NT_SUCCESS(status))
         {
                 if (status==STATUS_INSUFFICIENT_RESOURCES)
                 {
                     KdPrint(("资源不足 STATUS_INSUFFICIENT_RESOURCES"));
                 }
                 if (status==STATUS_OBJECT_NAME_EXISTS )
                 {
                     KdPrint(("指定对象名存在"));
                 }
                 if (status==STATUS_OBJECT_NAME_COLLISION)
                 {
                     KdPrint(("//对象名有冲突"));
                 }
             KdPrint(("设备创建失败...++++++++"));
             return status;
         }
         KdPrint(("设备创建成功...++++++++"));
         // IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中
    
         pDevObj->Flags |= DO_BUFFERED_IO;
         //创建符号链接
    
         //驱动程序虽然有了设备名称,但是这种设备名称只能在内核态可见,而对于应用程序是不可见的,因此,驱动需要要暴露一个符号链接,该链接指向真正的设备名称
         RtlInitUnicodeString(&symLinkName,L"\\??\\TestLinkName");
         //这个宽字节的路径“\\??\\ ”部分不能改变,后面是符号链接名称,这里在《天书夜读》中使用的是“\\DosDevices\\”,暂时持疑问态度
    
         status = IoCreateSymbolicLink( &symLinkName,// Unicode字符串指针,是一个用户态可见的名称
         &devName );// Unicode字符串指针,是驱动程序创建的设备对象名称。
         if (!NT_SUCCESS(status)) /*status等于0*/
         {
                 IoDeleteDevice( pDevObj );//删除驱动设备
                 return status;
         }
         return STATUS_SUCCESS;
     }
    
     #pragma  PAGECODE
     VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject); //前置说明 卸载例程
     NTSTATUS ddk_DispatchRoutine_CONTROL(IN    PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数
    
     NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
     {
         __asm  ;
         KdPrint(("驱动成功被加载.. "));
         //jmp指令
          CreateMyDevice(pDriverObject);//为驱动对象创建一个设备
    
         pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
    
          pDriverObject->DriverUnload=DDK_Unload;//卸载驱动对象,这里并没有真正卸载掉,因为没有删除所有的设备以及符号链接
         return STATUS_SUCCESS;
     }
     VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
     {
         PDEVICE_OBJECT pDev;//用来取得要删除设备对象
         UNICODE_STRING symLinkName; // 
    
         pDev=pDriverObject->DeviceObject;//从驱动对象取得设备对象,所有设备对象连成一条链,这里假定只有一个设备
         IoDeleteDevice(pDev); //删除设备
    
         //取符号链接名字
         RtlInitUnicodeString(&symLinkName,L"\\??\\TestLinkName");
         //删除符号链接
         IoDeleteSymbolicLink(&symLinkName);
         KdPrint(("驱动成功被卸载... ")); //sprintf,printf
         //取得要删除设备对象
         //删掉所有设备
         DbgPrint("卸载成功");
     }
    
     //分类处理
     NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp)
     {
         PIO_STACK_LOCATION     irpsp = IoGetCurrentIrpStackLocation(pIrp);
         // IoGetCurrentIrpStackLocation得到调用者堆栈的指针
         switch(irpsp->MajorFunction)
         {
         case  IRP_MJ_CREATE:
             KdPrint(("IRP_MJ_CREATE"));
             break;
         case  IRP_MJ_CLOSE:
             KdPrint(("IRP_MJ_CLOSE"));
             break;
         case  IRP_MJ_READ:
             KdPrint(("IRP_MJ_READ"));
             break;
         case  IRP_MJ_WRITE:
             KdPrint(("IRP_MJ_WRITE"));
             break;
         case  IRP_MJ_DEVICE_CONTROL:
             KdPrint(("IRP_MJ_DEVICE_CONTROL"));
             break;
         default:
             KdPrint(("其它处理"));
         }
    
         pIrp->IoStatus.Information=;//设置IRP操作的字节数为0,这里无实际意义
         pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态
         IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理
         KdPrint(("离开派遣函数\n"));//调试信息
         return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致
     }
  8. DDK_Unload函数。

  9. IRP(I/O request package)是操作系统内核的一个数据结构。应用程序(.exe)与驱动程序(.sys)进行通信需要通过IRP包。当上层应用程序需要与驱动通信的时候,通过调用一定的API函数,IO管理器针对不同的API产生不同的IRP,IRP被传递到驱动内部不同的分发函数进行处理(DisPatch Function)。对于不会处理的IRP包需要提供一个默认的分发函数来处理。IRP分为很多种,被用于与windows程序交互的有以下五种:

    • #define     IRP_MJ_CREATE   0x00                   //CreateFile会产生此IRP
    • #define     IRP_MJ_CLOSE      0x02                   //CloseHandle会产生此IRP
    • #define     IRP_MJ_READ        0x03                   //ReadFile会产生此IRP
    • #define     IRP_MJ_WRITE      0x04                   //WriteFile会产生此IRP
    • #define    IRP_MJ_CONTROL 0x0E                  //DeviceIoControl会产生此IRP
  10. 其它还有IRP_MJ_PNP(即插即用0x1b,NT驱动不支持,WDM驱动支持)、IRP_MJ_POWER(电源管理0x16)、IRP_MJ_SYSTEM_CONTROL(系统控制0x17)等等。使用过程:

    • 创建IRP处理函数(有点类似MFC的回调函数机制)
    • 在驱动入口(DriveEntry)对IRP函数进行注册,在驱动对象(DriverObject)的MajorFunction字段中被指派,这个字段是一个数组,用以上的IRP宏定义进行偏移
    • 细化IRP函数
  11. 注册和细化派遣函数

    • 方式一:注册到同一个派遣函数再分类处理
    • 方式二:分开注册,对不同IRP包注册不同的派遣函数来处理

《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建的更多相关文章

  1. 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

    1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...

  2. Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...

  3. Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2 ...

  4. windows平台CodeBlocks MinGW C++11开发环境搭建

    前言: 本文是以单独下载codeblock编辑器跟MinGW编译器这种方式进行安装,下载带MinGW编译器的codeblocks版本安装配置方式跟这个类似. 一: 下载并安装MinGW 这个参考我写的 ...

  5. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  6. 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建

    1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...

  7. windows下Qt5.2 for android开发环境搭建

    windows下Qt5.2 forAndroid开发环境配置 1.下载安装Qt 5.2.0 for Android (Windows 32-bit)   http://qt-project.org/d ...

  8. Windows 7下Node.js Web开发环境搭建笔记

    Node.js是什么? 我们看看百科里怎么说的?JavaScript是一种运行在浏览器的脚本,它简单,轻巧,易于编辑,这种脚本通常用于浏览器的前端编程,但是一位开发者Ryan有一天发现这种前端式的脚本 ...

  9. 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序

    ---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...

随机推荐

  1. 一个高性能、轻量级的分布式内存队列系统--beanstalk

    Beanstalk是一个高性能.轻量级的.分布式的.内存型的消息队列系统.最初设计的目的是想通过后台异步执行耗时的任务来降低高容量Web应用系统的页面访问延迟.其实Beanstalkd是典型的类Mem ...

  2. Mac上搭建基于Github的Hexo博客

    Mac 上搭建基于Github的hexo博客 博客地址:往事亦如风的博客 hexo官方文档 本来想搭一个自己的博客,但是因为服务器真心买不起,所以就使用gitpages搭建一个免费的博客. 环境配置 ...

  3. 读Zepto源码之fx_methods模块

    fx 模块提供了 animate 动画方法,fx_methods 利用 animate 方法,提供一些常用的动画方法.所以 fx_methods 模块依赖于 fx 模块,在引入 fx_methods ...

  4. FPGA与数字信号处理

    过去十几年,通信与多媒体技术的快速发展极大地扩展了数字信号处理(DSP)的应用范围.眼下正在发生的是,以更高的速度和更低的成本实现越来越复杂的算法,这是针对高级信息服更高带宽以及增强的多媒体处理能力等 ...

  5. 我的three.js学习记录(二)

    通过上一篇文章我的three.js学习记录(一)基本上是入门了three.js,但是这不够3D,这次我希望能把之前做的demo弄出来,然后通过例子来分析操作步骤. 1. 示例 上图是之前做的一个dem ...

  6. XtraReports 入门教程

    一个链接:http://www.cnblogs.com/springSky/tag/XtraReports%20%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/ 与之相同功能 ...

  7. PyCharm 2017 官网 下载 安装 设置 配置 (主题 字体 字号) 使用 入门 教程

    一.安装 Python 3.6 首先,要安装好 Python 3.6.如果你还没有安装,可以参考咪博士之前的教程 Python 3.6.3 官网 下载 安装 测试 入门教程 (windows) 二.官 ...

  8. 关于Java和JavaScript对字符串截取处理的总结

    在JavaWeb开发中,经常需要对字符串进行处理,包括Java语言和JS语言,总是容易弄混淆,这里简单对比一下两种语言对于字符串截取方法. 一.先看Java public class StringDe ...

  9. JSTL标签库的基本教程之核心标签库(一)

    JSTL介绍 Java Server Pages Standard Tag Libray(JSTL):JSP标准标签库,它封装了JSP应用的通用核心功能.JSTL支持通用的.结构化的任务,比如迭代,条 ...

  10. 在Git上如何强推代码规范

    引言 最近参加了“前端规范制定topic”小组,小组成员一起制定了html.css.js.es6.vue和react等规范,但规范制定好了怎么进行推广去强制执行呢,已知我们的项目都是用git做管理的, ...