Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)
驱动安装,通讯,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)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Win64 驱动内核编程-1.环境搭建
- 下一篇: 7.PHP Cookie与Session