WIN64内核编程基础班(作者:胡文亮)   https://www.dbgpro.com/x64driver

  我们先从一份“简历”说起:

  姓名:X86或80x86

  性别:?

  出生年月:1978

  出生地点:美国

  所属公司:主要是INTEL和AMD

  主要历史(摘自维基百科):x86架构于1978年推出的Intel 8086中央处理器中首度出

  现,它是从Intel 8008处理器中发展而来的,而8008则是发展自Intel 4004的。8086

  在三年后为IBM PC所选用,之后x86便成为了个人电脑的标准平台,成为了历来最成功

  的CPU架构。其他公司也有制造x86架构的处理器,计有Cyrix(现为威盛电子所收购)、

  恩益禧集团、IBM、IDT以及Transmeta。Intel以外最成功的制造商为AMD,其早先产品

  Athlon系列处理器的市场份额仅次于Intel Pentium。8086是16位处理器;直到1985

  年32位的80386的开发,这个架构都维持是16位。接着一系列的处理器表示了32位架

  构的细微改进,推出了数种的扩充,直到2003年AMD对于这个架构发展了64位的扩充,

  并命名为AMD64。后来英特尔也推出了与之兼容的处理器,并命名为Intel 64。两者一般

  被统称为x86-64或x64,开创了x86的64位时代。

  在X86的“简历”里,我们摘出一段重要的话:2003年AMD对于这个架构发展了64位

  的扩充,并命名为AMD64。后来英特尔也推出了与之兼容的处理器,并命名为Intel 64。两

  者一般被统称为x86-64或x64,开创了x86的64位时代。也就是说,如果要学习WIN64内

  核编程,就必须拥有2003年以后的CPU!不像学习WIN32内核编程一样,随便一台运行XP

  的奔腾3笔记本也行!但实际情况是,基本上只有2005年以后的CPU才支持X64指令集;

  到2008年之后,CPU才普遍含有X64指令集;到2010年之后,CPU才普遍含有X64指令集

  和支持VT-X技术(没有VT-X技术就无法运行WIN64虚拟机)。

  鉴于中国的实际情况,应该很多人手里还有酷睿2的笔记本。一般来说,T5XXX以下的

  CPU是没有X64指令集的;T7XXX以下的CPU是不支持VT-X的。只有T7XXX以上的CPU,才

  有X64指令集和支持VT-X。而2010年之后的Core i系列的CPU,都有X64指令集和支持

  VT-X了。台式机方面也差不多,CORE 2似乎只有比较高端的E8000或者Q8000以上才有X64

  指令集和支持VT-X技术。AMD则比较厚道,Athlon X2 245之类的低端CPU都有X64指令

  集和支持AMD-V技术(等于是AMD的VT-X技术)。

  总结来说,如果你用的CPU是CORE I系列的,就可以了,如果不是的话,可以用CPUZ检测一下,看看是否支持X64和VT-X。如果发现不支持VT-X的话,看看是不是BIOS里没

  有打开,一般主板的默认设置里,VT-X都是关闭的。

  说完CPU,说说内存。内存经历过两次大跌大涨,现在(2013年11月)又在价格的顶

  峰,真是让人心碎。不过再让人心碎的价格,为了学习技术,大家也只能忍受了。一句话,

  学习WIN64内核编程至少需要8GB的内存,如果要多开虚拟机,推荐16GB。否则在双机调

  试时卡死(鼠标移动变成了“飘动”的),会让人非常愤怒。

  配置好驱动测试环境后,就可以正式编写驱动了。市面上讲解驱动开发的书籍汗牛充栋,

  但讲得较为太复杂,让初学者不好理解。本文从一个简单的hello,world驱动(驱动模板)

  讲起,力求讲解得简单明了,让大家好理解。

  本文主角:

  1.DbgView。DbgView是查看程序调试输出的工具,由美国高富帅Mark Russinovich编写

  (不得不说,此人长得帅,编程技术又牛,让多少男人羡慕妒忌,让多少女人一见倾心)。

  下载地址:http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

  2.KmdMgr。KmdMgr是一个由俄国人编写的驱动加载工具。比起国内那些乱七八糟的驱动加

  载工具,它的特点是可以与驱动进行通信(虽然无法设置I/O缓冲区)。下载地址:

  https://www.assembla.com/code/L2h/subversion/nodes/LowLevel/KmdManager.exe?_for

  mat=raw&rev=1

  3.WIN64AST。作者自行开发的64位ARK类工具。在本章中用来查看驱动是否加载成功。在

  后续章节还有其他的用途。下载地址:www.win64ast.com。

  4.WIN64UDL。作者自行开发的驱动加载工具,能在正常模式下加载没有签名的驱动。因为这

  个工具,被人举报滥用签名,最终导致价值15000人民币的数字签名被吊销。下载地址:

  http://www.m5home.com/bbs/thread-7845-1-1.html

  编写驱动:

  以下是一个我写的WIN64驱动模板(代码中已经加了详细的注释,完整工程文件可以在

  论坛上下载):

  //【0】包含的头文件,可以加入系统或自己定义的头文件

  #include<ntddk.h>

  #include<windef.h>

  #include<stdlib.h>

  //【1】定义符号链接,一般来说修改为驱动的名字即可

  #define DEVICE_NAME L"\\Device\\KrnlHW64"

  #define LINK_NAME L"\\DosDevices\\KrnlHW64"

  #define LINK_GLOBAL_NAME L"\\DosDevices\\Global\\KrnlHW64"

  //【2】定义驱动功能号和名字,提供接口给应用程序调用

  #define IOCTL_IO_TEST CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,

  METHOD_BUFFERED,FILE_ANY_ACCESS)

  #define IOCTL_SAY_HELLO CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,

  METHOD_BUFFERED,FILE_ANY_ACCESS)

  //【3】驱动卸载的处理例程

  VOID DriverUnload(PDRIVER_OBJECT pDriverObj)

  {

  UNICODE_STRING strLink;

  DbgPrint("[KrnlHW64]DriverUnload\n");

  RtlInitUnicodeString(&strLink,LINK_NAME);

  IoDeleteSymbolicLink(&strLink);

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  IoDeleteDevice(pDriverObj->DeviceObject);

  }

  //【4】IRP_MJ_CREATE对应的处理例程,一般不用管它

  NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj,PIRP pIrp)

  {

  DbgPrint("[KrnlHW64]DispatchCreate\n");

  pIrp->IoStatus.Status=STATUS_SUCCESS;

  pIrp->IoStatus.Information=0;

  IoCompleteRequest(pIrp,IO_NO_INCREMENT);

  return STATUS_SUCCESS;

  }

  //【5】IRP_MJ_CLOSE对应的处理例程,一般不用管它

  NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj,PIRP pIrp)

  {

  DbgPrint("[KrnlHW64]DispatchClose\n");

  pIrp->IoStatus.Status=STATUS_SUCCESS;

  pIrp->IoStatus.Information=0;

  IoCompleteRequest(pIrp,IO_NO_INCREMENT);

  return STATUS_SUCCESS;

  }

  //【6】IRP_MJ_DEVICE_CONTROL对应的处理例程,驱动最重要的函数之一,一般走正常途径调

  用驱动功能的程序,都会经过这个函数

  NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj,PIRP pIrp)

  {

  NTSTATUS status=STATUS_INVALID_DEVICE_REQUEST;

  PIO_STACK_LOCATION pIrpStack;

  ULONG uIoControlCode;

  PVOID pIoBuffer;

  ULONG uInSize;

  ULONG uOutSize;

  DbgPrint("[KrnlHW64]DispatchIoctl\n");

  pIrpStack=IoGetCurrentIrpStackLocation(pIrp);

  //控制码

  uIoControlCode=pIrpStack->Parameters.DeviceIoControl.IoControlCode;

  //输入输出缓冲区

  pIoBuffer=pIrp->AssociatedIrp.SystemBuffer;

  //输入区域大小

  uInSize=pIrpStack->Parameters.DeviceIoControl.InputBufferLength;

  //输出区域大小

  uOutSize=pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

  switch(uIoControlCode)

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  {

  //在这里加入接口

  case IOCTL_IO_TEST:

  {

  DWORD dw=0;

  //获得输入的内容

  memcpy(&dw,pIoBuffer,sizeof(DWORD));

  //使用输入的内容

  dw++;

  //输出处理的结果

  memcpy(pIoBuffer,&dw,sizeof(DWORD));

  //处理成功,返回非STATUS_SUCCESS会让DeviveIoControl返回失败

  status=STATUS_SUCCESS;

  break;

  }

  case IOCTL_SAY_HELLO:

  {

  DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n");

  status=STATUS_SUCCESS;

  break;

  }

  }

  if(status==STATUS_SUCCESS)

  pIrp->IoStatus.Information=uOutSize;

  else

  pIrp->IoStatus.Information=0;

  pIrp->IoStatus.Status=status;

  IoCompleteRequest(pIrp,IO_NO_INCREMENT);

  return status;

  }

  //【7】驱动加载的处理例程,里面进行了驱动的初始化工作

  NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING

  pRegistryString)

  {

  NTSTATUS status=STATUS_SUCCESS;

  UNICODE_STRING ustrLinkName;

  UNICODE_STRING ustrDevName;

  PDEVICE_OBJECT pDevObj;

  //初始化驱动例程

  pDriverObj->MajorFunction[IRP_MJ_CREATE]=DispatchCreate;

  pDriverObj->MajorFunction[IRP_MJ_CLOSE]=DispatchClose;

  pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL]=DispatchIoctl;

  pDriverObj->DriverUnload=DriverUnload;

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  //创建驱动设备

  RtlInitUnicodeString(&ustrDevName,DEVICE_NAME);

  status=IoCreateDevice(pDriverObj,0,&ustrDevName,FILE_DEVICE_UNKNOWN,

  0,FALSE,&pDevObj);

  if(!NT_SUCCESS(status))return status;

  if(IoIsWdmVersionAvailable(1,0x10))

  RtlInitUnicodeString(&ustrLinkName,LINK_GLOBAL_NAME);

  else

  RtlInitUnicodeString(&ustrLinkName,LINK_NAME);

  //创建符号链接

  status=IoCreateSymbolicLink(&ustrLinkName,&ustrDevName);

  if(!NT_SUCCESS(status))

  {

  IoDeleteDevice(pDevObj);

  return status;

  }

  //走到这里驱动实际上已经初始化完成,下面添加的是功能初始化的代码

  DbgPrint("[KrnlHW64]DriverEntry\n");

  return STATUS_SUCCESS;

  }

  如果你懒得认真看完上面的代码,也没问题,我总结几句:1.DriverEntry就是驱动的

  main函数,驱动加载后会从DriverEntry开始执行。2.驱动类似DLL,可以提供接口给应用

  程序调用,不过以导出函数的方式,而是用一套专门的通信函数DeviceIoControl。关于应

  用程序与驱动程序通信,后面会讲。

  编译驱动:

  1.打开『x64 Free Build Environment』:

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

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  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并把以下选项全部勾上:

  标准的驱动测试方法:

  1.打开虚拟机,进入双机调试的环境(忘记了就参考上节课的内容)。

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

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

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

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

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  5.在CODE处输入222004(为什么是222004而不是801?这个后面会讲到,这里先卖一个关

  子。但这个数值可以使用calc_ctl_code.exe算出来,既输入801,可以输出222004),点

  击“I/O Control”按钮,如果成功会有类似的输出:

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

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  很显然,用标准方法测试一个驱动是很麻烦且很耗时的。双机调试非常占用系统资源,

  虽然我的电脑配置较好(2600K+16GB内存),但是在操作虚拟机时,仍然感到了明显的卡顿。

  下面介绍一种用特殊工具测试驱动的方法,无需双机调试,甚至无需使用虚拟机。

  用WIN64UDL测试驱动:

  1.运行WIN64UDL。

  2.把驱动文件拖进WIN64UDL里,然后按下ENTER加载。

  3.再按一次ENTER卸载驱动。

  最后,再补充一种非常麻烦的方法,此方法也算是标准方法之一,适用于没有虚拟机或

  无法进行双机调试的时候。由于非常麻烦,所以不推荐使用。说实话,谁用此方法测试驱动,

  绝对是脑门被驴踢了。

  1.开启测试模式。管理员权限运行CMD,输入:bcdedit-set testsigning on。

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  2.重启计算机

  3.用dseo13b(下载地址:http://files.ngohq.com/ngo/dseo/dseo13b.exe)给驱动程序添

  加测试签名。方法很简单,运行deso13b,一路NEXT,当出现这个对话框时,选择“Sign a

  System File”再点NEXT:

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

----------------------------

  说完基本环境配置,这篇就稍微轻松点了,可以听听我吹牛侃大山,谈谈WIN64内核编

  程的基本规则。在WIN32环境下,大家都能乱来,随便加载各种驱动,进行各种挂钩和DKOM,

  各种穷凶极恶的手段粉墨登场。把好端端的WINDOWS弄得连七八糟,严重影响了系统安全。

  虽说为编程爱好者提供了表演的舞台,但是苦了那些把电脑当工具的人。于是那段时间,

  “LINUX和MAC OS比WINDOWS安全可靠”的谣言四起(似乎也不是谣言),大有把WINDOWS

  和不安全划上等号之势。

  为此,微软很生气,后果很严重。从WINDOWS 2003 X64开始,微软开始对WIN64系统

  增加限制,增强系统安全。总体来说有两条,一是KPP(Kernel Patch Protection,内核补

  丁保护),二是DSE(Driver Signature Enforcement,驱动签名强制)。WINDOWS 2003 X64

  只有KPP,从VISTA开始有了DSE。KPP利用PatchGuard技术检查内核有没有被“打补丁”

  (不仅检查内核函数有没有被HOOK,也包括一些关键的内核结构体有没有被修改,比如进

  程链表PsActiveProcessLinks有没有被摘链),如果发现被“打补丁”,则直接引发0x109

  蓝屏(CRITICAL_STRUCTURE_CORRUPTION,直译为关键结构损毁)。DSE则是拒绝加载不包含

  正确签名的驱动(包括伪造签名和测试签名)。多说一句,总有人把KPP把PatchGuard划等

  号,其实二者是不等的。KPP是机制,PatchGuard是实现。就好比CIA是机构,CIA的特工

  才是一系列“黑色行动”的执行者。

  说完正儿八经的,说点通地气的话。实际上KPP和DSE并非铁板一块,二是各有漏洞可

  钻的。KPP保护不了内核所有的部分,只保护了几个驱动:NTOSKRNL.EXE、HAL.DLL、CI.DLL、

  NDIS.SYS等(当然,NTOS部分包括了IDT、GDT、MSR等)。对一些较为上层的驱动,比如

  FAT32和NTFS作IRP HOOK,PG是不管的。而DSE则在某些条件下不启动,比如在PE环境

  下;或者说有些时候管得不严格,比如在测试模式下,允许含有测试签名的驱动加载。总结

  一句:进行WIN64内核编程的时候,别想用API HOOK解决问题;当发布含有WIN64驱动的

  时候,记得给“证书签发机构”交保护费(购买正规数字签名)。不过,这两项限制让很多

  黑客乃至安全公司大为不满,各种过KPP和DSE的方法层出不穷。目前,VISTA、WIN7、WIN8、

  WIN8.1的KPP和DSE已全部被攻破。

  编程的时候,大家基本都是需要使用API的。在RING3下使用WINAPI,在RING0下则

  使用内核API。特别注意的是,内核编程是无法使用WINAPI的(当然,也有特殊的方法调

  用,不过非标准方法,这里略过不提)。什么叫做内核API呢,就是虚拟地址位于内核空间

  的API。不管是不是微软的内核模块,也不管导出没导出,只要知道地址和每个参数的含义,

  就能调用。不过,我们写程序大多时候都是使用微软模块(NTOSKRNL、HAL等)导出的API。

  例如:ZwOpenProcess,IoCreateFile等。

  内核编程用内核API,而自然也有内核结构体。其实“内核结构体”这个说法不太妥当,

  因为结构体是不分场合甚至不分系统的。但这么说大家也能理解是什么意思,就是内核编程

  中常用的结构体。这种结构体又分为两种,一种是“万年不变”的,一种是每个系统都不同

  的。“万年不变”的结构体通常能在MSDN上查到,比如CLIENT_ID、IO_STATUS_BLOCK;每个

  系统都不同的结构体通常在MSDN上查不到,但是存在于符号文件里,比如EPROCESS、ETHREAD。

  我们编程的时候,尽量只使用“万年不变”的结构体,不使用每个系统都不同的结构体。当

  非要使用不可的时候,必须根据系统版本定义制定成员的偏移量。如果发现未知的系统版

  本,则提示并退出。如果不这样做,等着BSOD吧。

  WIN64内核编程基础班(作者:胡文亮;QQ:1923208126)

  内核编程的基本规矩不是一篇能讲完的,下面几篇会细化讲解,这篇只是个引子。

WIN64内核编程-的基础知识的更多相关文章

  1. python六十七课——网络编程(基础知识了解)

    网络编程: 什么是网络编程? 网络:它是一种隐形的媒介:可以将多台计算机使用(将它们连接到一起) 网络编程:将多台计算机之间可以相互通信了(做数据交互) 一旦涉及到网络编程,划分为两个方向存在,一方我 ...

  2. C#复习笔记(5)--C#5:简化的异步编程(异步编程的基础知识)

    异步编程的基础知识 C#5推出的async和await关键字使异步编程从表面上来说变得简单了许多,我们只需要了解不多的知识就可以编写出有效的异步代码. 在介绍async和await之前,先介绍一些基础 ...

  3. java第九节 网络编程的基础知识

    /** * * 网络编程的基础知识 * 网络协议与TCP/IP * IP地址和Port(端口号) * 本地回路的IP地址:127.0.0.1 * 端口号的范围为0-65535之间,0-1023之间的端 ...

  4. 编程必备基础知识|计算机组成原理篇(09):CPU的控制器和运算器

    计算机基础方面的知识,对于一些非科班出身的同学来讲,一直是他们心中的痛,而对于科班出身的同学,很多同学在工作之后,也意识到自身所学知识的不足与欠缺,想回头补补基础知识.关于计算机基础的课程很多,内容繁 ...

  5. [转] linux操作系统下c语言编程入门--基础知识

    点击阅读原文 这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将会学到以下内容: 1. 源程序编译        2. Makefile的编写        3. 程序库 ...

  6. 01网络编程(基础知识+OSI七层协议+TCP与UDP)

    目录 01 网络编程 一.软件开发架构 1.1 CS架构 1.2 BS架构 二.网络理论前戏 2.1 简介 2.2 常见硬件 三.OSI七层协议(五层) 3.1 七层协议 3.2 五层协议 3.3 知 ...

  7. TCP/UDP网络编程的基础知识与基本示例(windows和Linux)

    一.TCP编程的一般步骤 服务器端: 1.创建一个socket,用函数socket() 2.绑定IP地址.端口等信息到socket上,用函数bind() 3.开启监听,用函数listen() 4.接收 ...

  8. Socket编程 - 网络基础知识

    API编程部分:http://www.cnblogs.com/Jimmy1988/p/7895213.html 1. 协议简介 此处,我们主要介绍Linux编程常用的三种协议(TCP/UDP/IP), ...

  9. Windows c++面向对象与可视化编程的基础知识

    1.Windows的程序设计语言:Visual C++,Visual Basic ,Visual c#都是“面向对象”的程序设计语言; 2.Windows的程序设计的对象:是Windows的规范部件, ...

随机推荐

  1. WPF 基础 - 图片之界面截图

    1. 功能 系统截图. 2. 实现 2.1 思路 控件继承自 System.Windows.Media.Visual, 通过 System.Windows.Media.Imaging.RenderVi ...

  2. C#开发BIMFACE系列37 网页集成开发1:审图系统中加载模型或图纸

    系列目录     [已更新最新开发文章,点击查看详细] 在之前的<C#开发BIMFACE系列>中主要介绍了BIMFACE平台提供的服务端API接口的封装开发与测试过程. 服务端API测试通 ...

  3. python tempfile 创建临时目录

    一.tempfile介绍 该模块创建临时文件和目录.它适用于所有支持的平台.TemporaryFile,NamedTemporaryFile,TemporaryDirectory,和SpooledTe ...

  4. 攻防世界 reverse 2ex1

    2ex1 CISCN-2018-Quals mark 1 import base64 2 3 std_base= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk ...

  5. 阿里二面,面试官居然把 TCP 三次握手问的这么细致

    TCP 的三次握手和四次挥手,可以说是老生常谈的经典问题了,通常也作为各大公司常见的面试考题,具有一定的水平区分度.看似是简单的面试问题,如果你的回答不符合面试官期待的水准,有可能就直接凉凉了. 本文 ...

  6. cordova app打包apk签名

    首先执行:ionic cordova build android --prod --release,执行完会在以下目录生成apk文件( --prod 用以压缩) 然后使用keytool生成keysto ...

  7. Kafka 消息存储机制

    Kafka 消息以 Partition 作为存储单元,那么在 Partition 内消息是以什么样的格式存储的呢,如何处理 Partition 中的消息,又有哪些安全策略来保证消息不会丢失呢,这一篇我 ...

  8. Python是啥?为什么这么多职业人和学生就算报班也要学它?!

    嗨,大家好 这里是汐仔 首先我们先来考究一下近几年的头条和新闻. 1.早在2018年python就已经被纳入高考之一了 2.Python加入全国计算机等级考试,从2018年九月起新增为大学计算机二级考 ...

  9. Linux apt命令使用 以及 文本流和重定向

    apt (Advanced Packaging Tool) 是一个在Debian和Ubuntu中的Shell前端软件包管理器. apt命令执行需要超级管理员权限(root). apt语法 apt [o ...

  10. 一文吃透jQuery选择器!

    1 jQuery选择器 jQuery选择器用于选择DOM元素进行相应操作,允许通过多种方式选择,包括标签名,属性名,类名,id等对元素进行选择,基于CSS选择器.jQuery中所有的选择器都以$符号开 ...