驱动安装,通讯,Hello World

开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回。

驱动程序Hello World

之前总结了驱动环境的搭建,这里就直接继续之前搭建好的环境创建项目,打开vs2015创建一个驱动项目:

写代码之前先配置下编译选项:

然后添加一个项目文件main.c(注意后缀是.c,前面名字无所谓可以不叫main),里面的内容如下(下面模板代码来源于网络,作者:胡文亮,我是在看他的资料学习,感谢这位前辈。)

/*
WIN64驱动开发模板
作者:Tesla.Angela
*/ //【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);
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");
//获得IRP里的关键数据
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
//这个就是传说中的控制码
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//输入和输出的缓冲区(DeviceIoControl的InBuffer和OutBuffer都是它)
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
//EXE发送传入数据的BUFFER长度(DeviceIoControl的nInBufferSize)
uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//EXE接收传出数据的BUFFER长度(DeviceIoControl的nOutBufferSize)
uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch(uIoControlCode)
{
//在这里加入接口
case IOCTL_IO_TEST:
{
DWORD dw=0;
//输入
memcpy(&dw,pIoBuffer,sizeof(DWORD));
//使用
dw++;
//输出
memcpy(pIoBuffer,&dw,sizeof(DWORD));
//返回通信状态
status = STATUS_SUCCESS;
break;
}
case IOCTL_SAY_HELLO:
{
DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n");
status = STATUS_SUCCESS;
break;
}
}
//这里设定DeviceIoControl的*lpBytesReturned的值(如果通信失败则返回0长度)
if(status == STATUS_SUCCESS)
pIrp->IoStatus.Information = uOutSize;
else
pIrp->IoStatus.Information = 0;
//这里设定DeviceIoControl的返回值是成功还是失败
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;
//创建一个设备
RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if(!NT_SUCCESS(status)) return status;
//判断支持的WDM版本,其实这个已经不需要了,纯属WIN9X和WINNT并存时代的残留物
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;
}

然后右键编译工程,会出现这个错误提示:

然后把这个文件删除:

继续编译,还是不会过:继续改另一个配置文件选项:



然后就可以了:



然后是驱动安装程序:

目前如果没有仔细看上面的那个基本模板代码,需要回去仔细看下。大体了解细节,尤其是里面的注释。

看完代码了,接下来是驱动安装。

驱动安装和服务安装,如果之前写过安装服务的代码看驱动安装代码会很熟悉,都是采用SCM安装。具体流程是:

然后这个函数要仔细看定义:

更详细的细节之前看MSDN吧。

然后还是把资料上的模板代码直接拿过来,注意目前先用这个模板,因为这个安装模板是和上面的那个Hello World对应的,等看懂之后了,在自己定义相关驱动安装和驱动程序的代码,也可以自己写个模板,这里先不自己随便定义,可能会导致链接名字,驱动名字,还有驱动通讯的那个地方的细节自己弄乱了。这个博客看完了再回头定义自己的就行。

创建C++工程,然后直接添加安装代码和安装测试代码:

功能代码:

/*============================
Drvier Control Class (SCM way)
============================*/ #pragma comment(lib,"advapi32.lib") class cDrvCtrl
{
public:
cDrvCtrl()
{
m_pSysPath = NULL;
m_pServiceName = NULL;
m_pDisplayName = NULL;
m_hSCManager = NULL;
m_hService = NULL;
m_hDriver = INVALID_HANDLE_VALUE;
}
~cDrvCtrl()
{
CloseServiceHandle(m_hService);
CloseServiceHandle(m_hSCManager);
CloseHandle(m_hDriver);
}
public:
DWORD m_dwLastError;
PCHAR m_pSysPath;
PCHAR m_pServiceName;
PCHAR m_pDisplayName;
HANDLE m_hDriver;
SC_HANDLE m_hSCManager;
SC_HANDLE m_hService;
public:
BOOL Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName);
BOOL Start();
BOOL Stop();
BOOL Remove();
BOOL Open(PCHAR pLinkName);
BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes);
private:
BOOL GetSvcHandle(PCHAR pServiceName);
DWORD CTL_CODE_GEN(DWORD lngFunction);
protected:
//null
}; BOOL cDrvCtrl::GetSvcHandle(PCHAR pServiceName)
{
m_pServiceName = pServiceName;
m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (NULL == m_hSCManager)
{
m_dwLastError = GetLastError();
return FALSE;
}
m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS);
if (NULL == m_hService)
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
else
{
return TRUE;
}
} BOOL cDrvCtrl::Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName)
{
m_pSysPath = pSysPath;
m_pServiceName = pServiceName;
m_pDisplayName = pDisplayName;
m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (NULL == m_hSCManager)
{
m_dwLastError = GetLastError();
return FALSE;
}
m_hService = CreateServiceA(m_hSCManager,m_pServiceName,m_pDisplayName,
SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,
m_pSysPath,NULL,NULL,NULL,NULL,NULL);
if (NULL == m_hService)
{
m_dwLastError = GetLastError();
if (ERROR_SERVICE_EXISTS == m_dwLastError)
{
m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS);
if (NULL == m_hService)
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
}
else
{
CloseServiceHandle(m_hSCManager);
return FALSE;
}
}
return TRUE;
} BOOL cDrvCtrl::Start()
{
if (!StartServiceA(m_hService,NULL,NULL))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE;
} BOOL cDrvCtrl::Stop()
{
SERVICE_STATUS ss;
GetSvcHandle(m_pServiceName);
if (!ControlService(m_hService,SERVICE_CONTROL_STOP,&ss))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE; } BOOL cDrvCtrl::Remove()
{
GetSvcHandle(m_pServiceName);
if (!DeleteService(m_hService))
{
m_dwLastError = GetLastError();
return FALSE;
}
return TRUE;
} BOOL cDrvCtrl::Open(PCHAR pLinkName)//example: \\\\.\\xxoo
{
if (m_hDriver != INVALID_HANDLE_VALUE)
return TRUE;
m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(m_hDriver != INVALID_HANDLE_VALUE)
return TRUE;
else
return FALSE;
} BOOL cDrvCtrl::IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes)
{
DWORD dw;
BOOL b=DeviceIoControl(m_hDriver,CTL_CODE_GEN(dwIoCode),InBuff,InBuffLen,OutBuff,OutBuffLen,&dw,NULL);
if(RealRetBytes)
*RealRetBytes=dw;
return b;
} DWORD cDrvCtrl::CTL_CODE_GEN(DWORD lngFunction)
{
return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
}

测试代码:

// DriverInstall.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <string>
#include <windows.h>
#include "ScmDrvCtrl.h" #pragma warning(disable:4996) #pragma comment(lib,"user32.lib") using namespace std; void GetAppPath(char *szCurFile) //最后带斜杠
{
GetModuleFileNameA(0, szCurFile, MAX_PATH);
for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--)
{
if (szCurFile[i] == '\\')
{
szCurFile[i + 1] = '\0';
break;
}
}
} int main()
{
BOOL b;
cDrvCtrl dc;
//设置驱动名称
char szSysFile[MAX_PATH] = { 0 };
char szSvcLnkName[] = "KrnlHW64";;
GetAppPath(szSysFile);
strcat(szSysFile, "KrnlHW64.sys");
//安装并启动驱动
b = dc.Install(szSysFile, szSvcLnkName, szSvcLnkName);
b = dc.Start();
printf("LoadDriver=%d\n", b);
//“打开”驱动的符号链接
dc.Open("\\\\.\\KrnlHW64");
//使用控制码控制驱动(0x800:传入一个数字并返回一个数字)
DWORD x = 100, y = 0, z = 0;
dc.IoControl(0x800, &x, sizeof(x), &y, sizeof(y), &z);
printf("INPUT=%ld\nOUTPUT=%ld\nReturnBytesLength=%ld\n", x, y, z);
//使用控制码控制驱动(0x801:在DBGVIEW里显示HELLOWORLD)
dc.IoControl(0x801, 0, 0, 0, 0, 0);
//关闭符号链接句柄
CloseHandle(dc.m_hDriver);
//停止并卸载驱动
b = dc.Stop();
b = dc.Remove();
printf("UnloadDriver=%d\n", b);
getchar();
return 0;
}

然后就直接本地尝试调试安装一次,结果先是这个:

不用管它,点击全部允许,但是还是会发现

还是装不上,其实是肯定装不上的。原因是64位机器需要的强制签名加载驱动:

注意最后面那句,各种系统的防护已经被攻克,他没有说win10,不过目前已经亲测win10也已经攻克,之后有机会再说这些东西,这里只讨论正常安装。想在win64上安装无签名驱动也可以,如果是win7,直接cmd 输入  bcdedit /set testsigning on ,然后重启电脑(win7是这样,win10貌似是前面要多输入一条命令,如果是要win10的话自己搜下吧,同时还有其他改设置关掉无签名驱动提示等的设置,加载测试驱动程序,需要的也可以找找,网上很多)。设置后上面的配置之后,重启电脑:

有的时候会有测试模式水印,有的时候没有,可能提示不是正版的话会没这个水印,不过这不重要,只要相面的那个cmd命令执行成功,重启电脑就可以加载无签名64位驱动了。

接下来测试下我们上面的那一套代码(所有程序都右键管理员启动):

先打开dbgview全都选上:

然后启动安装程序:

OK驱动加载成功,并且通讯测试成功。这里基本模板就算完事了,之后就是一些常用的驱动开发,后续再整理。

Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)的更多相关文章

  1. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  2. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  3. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  4. Win64 驱动内核编程-18.SSDT

    SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...

  5. Win64 驱动内核编程-10.突破WIN7的PatchGuard

    突破WIN7的PatchGuard WIN64 有两个内核保护机制,KPP 和 DSE.KPP 阻止我们 PATCH 内核,DSE 拦截我们加载驱动.当然 KPP 和 DSE 并不是不可战胜的,WIN ...

  6. Win64 驱动内核编程-11.回调监控进线程句柄操作

    无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...

  7. Win64 驱动内核编程-9.系统调用、WOW64与兼容模式

    系统调用.WOW64与兼容模式 这种东西都是偏向于概念的,我就把资料上的东西整理下粘贴过来,资料来源于胡文亮,感谢这位前辈. WIN64 的系统调用比 WIN32 要复杂很多,原因很简单,因为 WIN ...

  8. Win64 驱动内核编程-5.内核里操作文件

    内核里操作文件 RING0 操作文件和 RING3 操作文件在流程上没什么大的区别,也是"获得文件句柄->读/写/删/改->关闭文件句柄"的模式.当然了,只能用内核 A ...

  9. Win64 驱动内核编程-34.对抗与枚举MiniFilter

    对抗与枚举MiniFilter MiniFilter 是目前杀毒软件用来实现"文件系统自我保护"和"文件实时监控"的方法. 由于 MiniFilter 模型简单 ...

随机推荐

  1. SHELL编程概念&变量剖析

    一.shell软件概念和应用场景 1) 学习Linux技术,不是为了学习系统安装.命令操作.用户权限.配置IP.网络管理,学习Linux技术重点:基于Linux系统部署和维护各种应用软件.程序(Apa ...

  2. ASP.NET Core扩展库之日志

        上一篇我们对Xfrogcn.AspNetCore.Extensions扩展库功能进行了简单的介绍,从这一篇文章开始,我将逐步介绍扩展库中的核心功能.     日志作为非业务的通用领域基础功能, ...

  3. Java基础:常用基础dos命令

    打开cmd的方式1.开始+系统+命令提示符2.win键+R 输入cmd 打开控制台3.在任意的文件夹下,按住shift键+鼠标右键点击,在此处打开命令提示行4.在资源管理器的地址栏前面加上cmd路径 ...

  4. vue离开页面销毁滚动事件

    methods:{ handleFun(){    /**销毁处理*/ } }, beforeDestroy(){ window.removeEventListener("scroll&qu ...

  5. java实现一个点餐系统

    转载于blog.csdn.net/weixin_44219955 项目大体框架 菜品类(菜品id,菜品名,菜品类型,上架时间,单价,月销售,总数量) 管理员类(管理员id,账号,密码) 客户类(客户i ...

  6. CSS盒子的尺寸

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  7. Azure Front Door(三)启用 Web Application Firewall (WAF) 保护Web 应用程序,拒绝恶意攻击

    一,引言 上一篇我们利用 Azure Front Door 为后端 VM 部署提供流量的负载均衡.因为是演示实例,也没有实际的后端实例代码,只有一个 "Index.html" 的静 ...

  8. Java之常用API

    API概述 什么是API API (Application Programming Interface) :应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类,这些 ...

  9. Java中的泛型 - 细节篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的泛型 - 细节篇>,希望对大家有帮助,谢谢 细心的观众朋友们可能发现了,现在的标题不再是入门篇,而是各种详细篇,细节篇: 是因为之 ...

  10. JS基础学习第三天

    条件分支语句switch语句语法: 1234567891011121314 switch(条件表达式){ case 表达式: 语句... break; case 表达式: 语句... break; c ...