目标:
- 用户一键使用: 驱动加载(将驱动的安装、启动、停止、卸载的服务放到R3程序中来实现,作为可发布程序使用)
- 通过驱动的control回调函数和控制码来实现通讯
0x01 项目管理
环境整理:
创建一个空白项目,主要是为了用空白项目的解决方案
把Ring3和驱动加入到解决方案,环境里删掉刚创建的空白项目即可,就不用频繁的切换环境
把Ring3和驱动的输出都放到bin目录下(两个都需要设置),便于实验
整理后的效果
0x02 实验验证: 创建服务
2.1 验证过程
Ring3.cpp
#include <stdio.h>
#include <windows.h>
/*
如果没有R3得程序怎么加载呢?
微软有个inf文件,这个东西可以安装,在微软的官方文档中有说明inf内容怎么写;
OS会解析inf这个配置文件,帮助动态加载驱动
例: 如果硬件需要开机启动加载,微软再开机时会自动加载inf文件
驱动加载的方式:
1. inf文件来加载
2. 动态加载(服务)
创建服务就是安装驱动(很多软件都可以使用打印机,那么就可调用打印机的驱动,驱动的角色是服务),也就是需要4个api就可以完成这些工作;
创建服务
启动服务
停止服务
卸载服务
*/
void showErr(const char *name)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
printf("[%s] Error %s", name, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
int LoadDriver(const char *pszPath, const char *pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__,hSCM);
hService = CreateService(hSCM, pszServiceName, pszServiceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pszPath, NULL, NULL, NULL, NULL, NULL);
if (hService == NULL)
{
showErr("CreateService");
goto SAFE_EXIT; /* 使用了goto之后,使用局部变量,必须先定义后使用 */
}
printf("[%s] CreateService Ok hService:%p \n", __FUNCTION__, hService);
/* CreateService 使用分析
SC_HANDLE CreateService(
SC_HANDLE hSCManager, // handle to SCM database 服务是由服务管理器来管理,这里拿到服务管理器;SCM(service control manager) ,因为是本地,前两个为NULL,最后一个是权限,如果选择所有权限,就是既能创建又能删除
LPCTSTR lpServiceName, // name of service to start
LPCTSTR lpDisplayName, // display name
DWORD dwDesiredAccess, // type of access to service
DWORD dwServiceType, // type of service
DWORD dwStartType, // when to start service 五种启动方式,选第3个,重启系统后就不再加载
DWORD dwErrorControl, // severity of service failure
LPCTSTR lpBinaryPathName, // name of binary file
LPCTSTR lpLoadOrderGroup, // name of load ordering group
LPDWORD lpdwTagId, // tag identifier
LPCTSTR lpDependencies, // array of dependency names
LPCTSTR lpServiceStartName, // account name
LPCTSTR lpPassword // account password);
*/
iRet = 0;
SAFE_EXIT:
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
return iRet;
}
int UnLoadDriver(const char *name)
{
return 0;
}
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
if (LoadDriver("sample2.sys", "51asmMark") < 0)
return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
if (UnLoadDriver("51asmMark") < 0)
return 0;
#if 0
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
printf("hFile:%p err:%p\n", hFile, GetLastError());
// 读写设备
char acBuf[60] = { 0 };
printf("acBuf:%p\n", acBuf); /* 驱动要输出R3的缓存地址,这里作为对照 */
DWORD dwBytes = 0;
BOOL bRet = ReadFile(hFile, acBuf, sizeof(acBuf), &dwBytes, NULL);
printf("bRet:%d dwBytes:%d acBuf:%s\n", bRet, dwBytes, acBuf);
// 控制设备
// 关闭设备
#endif
system("pause");
return 0;
}
2.2 问题分析
期间遇到一个问题,折腾老半天,无效,如下图:
解决: 拒绝访问,是权限不足,关闭vs2019,用管理员的权限运行(感谢前辈的博客);
2.3 结果分析
服务创建成功了
2.4 原理分析
那么问题来了, creatServer 这个api做了什么?
其实在OS中记录下驱动的信息,regedit 注册表中看一下,注册表的每一个表象,都是创建的服务creatServer的参数,因此开机时,OS遍历注册表 就可以遍历驱动
也就是可以通过这个目录遍历所有系统安装的驱动
0x03 启动服务
3.1 出现问题1
问题1: 第二次创建时,提示服务已经存在了???
因为此时在创建服务失败后并没有判断服务是否已经存在了,那么就需要根据错误码判断服务是否存在,如果服务存在,那就直接打开服务就行; 下图中左侧打印出错误码,dwErr;
Ring.cpp 中的一个函数,解决这个问题
int LoadDriver(const char *pszPath, const char *pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__,hSCM);
hService = CreateService(hSCM, pszServiceName, pszServiceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pszPath, NULL, NULL, NULL, NULL, NULL);
if (hService == NULL)
{
//if (GetLastError() == 1073) // 当前服务已经存在,那就直接打开用
if (GetLastError() == ERROR_SERVICE_EXISTS)
{
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
}
else
{
showErr("CreateService");
goto SAFE_EXIT;
}
}
printf("[%s] CreateService Ok hService:%p \n", __FUNCTION__, hService);
/* CreateService 使用分析
SC_HANDLE CreateService(
SC_HANDLE hSCManager, // handle to SCM database 服务是由服务管理器来管理,这里拿到服务管理器;SCM(service control manager) ,因为是本地,前两个为NULL,最后一个是权限,如果选择所有权限,就是既能创建又能删除
LPCTSTR lpServiceName, // name of service to start
LPCTSTR lpDisplayName, // display name
DWORD dwDesiredAccess, // type of access to service
DWORD dwServiceType, // type of service
DWORD dwStartType, // when to start service 五种启动方式,选第3个,重启系统后就不再加载
DWORD dwErrorControl, // severity of service failure
LPCTSTR lpBinaryPathName, // name of binary file
LPCTSTR lpLoadOrderGroup, // name of load ordering group
LPDWORD lpdwTagId, // tag identifier
LPCTSTR lpDependencies, // array of dependency names
LPCTSTR lpServiceStartName, // account name
LPCTSTR lpPassword // account password);
*/
if (!StartService(hService, NULL, NULL)) /* 后两个参数,因为是驱动的服务,所以三环这里给NULL */
{
showErr("StartService");
goto SAFE_EXIT;
}
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
3.2 出现问题2
问题2:找不到指定文件
1、 不能再win10上运行
2、 驱动一直没有卸载过,卸载函数没写过
3.3 卸载实验验证
Ring3.cpp的UnLoadDriver函数
int UnLoadDriver(const char * pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
SERVICE_STATUS oldState = { 0 };
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__, hSCM);
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
/*
1. DeleteService之前,如果服务正在运行怎么办? 那就是先暂停服务
2 .OS 的peding状态:向硬件发送的请求还没有完成,此时驱动卸不掉,停不掉(如果驱动中的派遣函数没有IoCompleteRequest完成请求,就会有这种情况)
*/
if (!ControlService(hService, SERVICE_CONTROL_STOP, &oldState))
{
if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) // 服务没有启动的时候 判断错误码1062,那就启动服务;
{
printf("[%s] ControlService not active hService:%p \n", __FUNCTION__, hService);
}
else if (GetLastError() == ERROR_DEPENDENT_SERVICES_RUNNING) // peding 状态
{
printf("[%s] Please reboot system hService:%p \n", __FUNCTION__, hService);
goto SAFE_EXIT;
}
else
{
showErr("ControlService");
goto SAFE_EXIT;
}
}
printf("[%s] ControlService stop service Ok hService:%p \n", __FUNCTION__, hService);
if (!DeleteService(hService))
{
showErr("DeleteService");
goto SAFE_EXIT;
}
printf("[%s] DeleteService Ok hService:%p \n", __FUNCTION__, hService);
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
3.4 卸载结果分析
卸载成功,但是创建找不到指定文件
3.5 极端情况(加载器不会考虑这种情况)
3.5.1
极端情况分析: 有可能停掉驱动,但是驱动的回调函数还没有完成 IoCompleteRequest,这时的线程被挂起,服务停不掉,也会报错;
NTSTATUS DispatchRead(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
/* 验证这里是R3的缓存地址,输出一下 */
DbgPrint("51[asm][%s:%d] Irp->UserBuffer:%p", __FUNCTION__, __LINE__, Irp->UserBuffer);
RtlCopyMemory(Irp->UserBuffer,g_zMsg,sizeof(g_zMsg));
Irp->IoStatus.Status = STATUS_SUCCESS; /* 告诉R3返回的状态 */
Irp->IoStatus.Information = sizeof(g_zMsg); /* 告诉R3读取的字节数 */
IoCompleteRequest(Irp, IO_NO_INCREMENT); /* 如果驱动中的派遣函数没有IoCompleteRequest完成请求,R3就会有这peding状态的情况 */
return STATUS_SUCCESS; /* R3的ReadFile的返回值 */
}
3.5.2
OS 的peding状态:向硬件发送的请求还没有完成,此时驱动卸不掉,停不掉
3.6 实验验证:加载卸载
Ring3.cpp
#include <stdio.h>
#include <windows.h>
/*
如果没有R3得程序怎么加载呢?
微软有个inf文件,这个东西可以安装,在微软的官方文档中有说明inf内容怎么写;
OS会解析inf这个配置文件,帮助动态加载驱动
例: 如果硬件需要开机启动加载,微软再开机时会自动加载inf文件
驱动加载的方式:
1. inf文件来加载
2. 动态加载(服务)
创建服务就是安装驱动(很多软件都可以使用打阴界,那么就可调用打印机的驱动,驱动的角色是服务),也就是需要4个api就可以完成这些工作;
创建服务
启动服务
停止服务
卸载服务
*/
void showErr(const char *name)
{
LPVOID lpMsgBuf;
DWORD dwErr = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
printf("[%s] Error Code:%d Msg:%s", name, dwErr, (LPCTSTR)lpMsgBuf);
//MessageBox(NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION);
// Free the buffer.
LocalFree(lpMsgBuf);
}
int LoadDriver(const char *pszPath, const char *pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__,hSCM);
hService = CreateService(hSCM, pszServiceName, pszServiceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pszPath, NULL, NULL, NULL, NULL, NULL);
if (hService == NULL)
{
//if (GetLastError() == 1073) // 当前服务已经存在,那就直接打开用
if (GetLastError() == ERROR_SERVICE_EXISTS)
{
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
}
else
{
showErr("CreateService");
goto SAFE_EXIT;
}
}
printf("[%s] CreateService Ok hService:%p \n", __FUNCTION__, hService);
/* CreateService 使用分析
SC_HANDLE CreateService(
SC_HANDLE hSCManager, // handle to SCM database 服务是由服务管理器来管理,这里拿到服务管理器;SCM(service control manager) ,因为是本地,前两个为NULL,最后一个是权限,如果选择所有权限,就是既能创建又能删除
LPCTSTR lpServiceName, // name of service to start
LPCTSTR lpDisplayName, // display name
DWORD dwDesiredAccess, // type of access to service
DWORD dwServiceType, // type of service
DWORD dwStartType, // when to start service 五种启动方式,选第3个,重启系统后就不再加载
DWORD dwErrorControl, // severity of service failure
LPCTSTR lpBinaryPathName, // name of binary file
LPCTSTR lpLoadOrderGroup, // name of load ordering group
LPDWORD lpdwTagId, // tag identifier
LPCTSTR lpDependencies, // array of dependency names
LPCTSTR lpServiceStartName, // account name
LPCTSTR lpPassword // account password);
*/
if (!StartService(hService, NULL, NULL)) /* 后两个参数,因为是驱动的服务,所以三环这里给NULL */
{
showErr("StartService");
goto SAFE_EXIT;
}
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
int UnLoadDriver(const char * pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
SERVICE_STATUS oldState = { 0 };
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__, hSCM);
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
/*
1. DeleteService之前,如果服务正在运行怎么办? 那就是先暂停服务
2 .OS 的peding状态:向硬件发送的请求还没有完成,此时驱动卸不掉,停不掉(如果驱动中的派遣函数没有IoCompleteRequest完成请求,就会有这种情况)
*/
if (!ControlService(hService, SERVICE_CONTROL_STOP, &oldState))
{
if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) // 服务没有启动的时候 判断错误码1062
{
printf("[%s] ControlService not active hService:%p \n", __FUNCTION__, hService);
}
else if (GetLastError() == ERROR_DEPENDENT_SERVICES_RUNNING) // peding 状态
{
printf("[%s] Please reboot system hService:%p \n", __FUNCTION__, hService);
goto SAFE_EXIT;
}
else
{
showErr("ControlService");
goto SAFE_EXIT;
}
}
printf("[%s] ControlService stop service Ok hService:%p \n", __FUNCTION__, hService);
if (!DeleteService(hService))
{
showErr("DeleteService");
goto SAFE_EXIT;
}
printf("[%s] DeleteService Ok hService:%p \n", __FUNCTION__, hService);
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
#if 1
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
printf("hFile:%p err:%p\n", hFile, GetLastError());
// 读写设备
char acBuf[60] = { 0 };
printf("acBuf:%p\n", acBuf); /* 驱动要输出R3的缓存地址,这里作为对照 */
DWORD dwBytes = 0;
BOOL bRet = ReadFile(hFile, acBuf, sizeof(acBuf), &dwBytes, NULL);
printf("bRet:%d dwBytes:%d acBuf:%s\n", bRet, dwBytes, acBuf);
// 控制设备
// 关闭设备
#endif
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
3.7 实验结果分析
启动服务的时候,系统路径找不到,验证有误,驱动路径有问题,修改为绝对路径试试;
3.8 实验验证:路径修改为绝对路径
修改为绝对路径(虚拟机中驱动的绝对路径)
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
// 读写设备
char acBuf[60] = { 0 };
printf("acBuf:%p\n", acBuf); /* 驱动要输出R3的缓存地址,这里作为对照 */
DWORD dwBytes = 0;
BOOL bRet = ReadFile(hFile, acBuf, sizeof(acBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("ReadFile");
}
else
{
printf("bRet:%d dwBytes:%d acBuf:%s\n", bRet, dwBytes, acBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
3.9 结果分析
成功获取到hello,成功加载和卸载;
加载工具的实现已完成;
0x04 通过control来通讯
控制码: 驱动的DispatchRead被调用了,用户发来了请求,那么驱动怎么才能知道用户想要干嘛? 是遍历文件?遍历进程? …. 于是需要用控制码来标注一下,R3想要让驱动具体做什么,就发送对应的控制码;
R3和R0要分开,驱动中不直接访问三环的内存地址,而是在内核中申请一段内存,请求完成时,把内存拷贝给R3
问题: 读了数据又想写数据怎么办?
假如说R3写数据的请求是关闭某个进程,那么需要传给驱动一个pid, 同时R3需要一个结果,判断这个进程有没有关闭成功。
readfile+缓冲区,writefile+缓冲区,这就需要两个缓冲区来做交互,并且这两个回调一般是给硬件设备用的;
如果使用控制,那可以一次传两个缓冲区,可以同时读写数据,相对更加方便;
4.1 实验验证:能否调用驱动control函数
Ring3.cpp 中的main函数
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
#if 0
// 读写设备
char acBuf[60] = { 0 };
printf("acBuf:%p\n", acBuf); /* 驱动要输出R3的缓存地址,这里作为对照 */
DWORD dwBytes = 0;
BOOL bRet = ReadFile(hFile, acBuf, sizeof(acBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("ReadFile");
}
else
{
printf("bRet:%d dwBytes:%d acBuf:%s\n", bRet, dwBytes, acBuf);
}
#endif
char szInBuf[MAXBYTE] = { 0 };
char szOutBuf[MAXBYTE] = { 0 };
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, 100, szInBuf, sizeof(szInBuf), szOutBuf, sizeof(szOutBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szOutBuf:%s\n", bRet, dwBytes, szOutBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
4.2 结果分析
调用了control,
4.2.1
NT框架的设计问题:irp里没有R3的控制码
系统采用分层驱动的方式,例如:键盘是两个驱动加载组成的;
例: 有线键盘 不同usb接口键盘 逻辑大量重复
那就: 一个负责接口驱动,一个负责键盘逻辑驱动
4.3 实验验证:驱动中获取R3地址
Ring3.cpp 的main函数
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
char szInBuf[MAXBYTE] = { 0 };
char szOutBuf[MAXBYTE] = { 0 };
printf("szInBuf:%p szOutBuf:%p size:%d\n", szInBuf, szOutBuf, MAXBYTE); /* 驱动要输出R3的缓存地址,这里作为对照验证 */
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, 100, szInBuf, sizeof(szInBuf), szOutBuf, sizeof(szOutBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szOutBuf:%s\n", bRet, dwBytes, szOutBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
sample.cpp 的DispatchControl函数
NTSTATUS DispatchControl(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
/*
类似函数调用,要给下一层驱动传参数,参数:有的分开,有的共用;
共用参数放到了IRP里了;
不共用的参数,都会有自己的内存空间==》"IRP堆栈" ( 缓冲区大小 控制码......)
然后要向IO管理器获取到IRP堆栈(要的是这层驱动的irp堆栈地址),参数给irp,因为IRP堆栈就放在IRP后面;
IoGetCurrentIrpStackLocation(Irp); 传参是irp,因为irp堆栈放到irp后面;
但是不知道拿到那一层,自己不能去拿到这个地址,这是系统来拿,R3的其他参数都在这里main
不同的回调函数会有不同的行为,IoGetCurrentIrpStackLocation返回值里是共用体;
*/
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
PVOID pUserBuffer = Irp->UserBuffer;
PVOID pType3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
ULONG uInputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
ULONG uOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pUserBuffer:%p pType3InputBuffer:%p", __FUNCTION__, __LINE__, uIoControlCode, pUserBuffer, pType3InputBuffer);
DbgPrint("51[asm][%s:%d] uInputBufferLength:%d uOutputBufferLength:%d", __FUNCTION__, __LINE__, uInputBufferLength, uOutputBufferLength);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
/*
1. 这个函数表示 请求是否结束
2. 第一个参数:Irp(I/O Request Packet) 把所有的请求参数封装到这个结构体了
3. 第二个参数,让挂起的线程快速恢复,把线程的优先级往上提;
4. 当前IO_NO_INCREMENT: 不要提升优先级,什时候唤醒就什么时候结束
5. 除了入口函数和卸载函数外,其他全部函数都是一样的,要这样处理;
*/
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
//return IoCallDriver(Irp); // 分层驱动,这里进入下一层驱动来获取需要的值
}
4.4 现象分析:
这里只拿到一个缓冲区,另外一个缓冲区拿不到了,这就是为什么不要用R3的缓冲区,内核返回R3的缓冲区是有安全风险的
OS处理的原理:
R3输出缓冲区给驱动的过程:OS申请一个R0级别的内存,先把R3输出缓冲区的数据拷贝到OS里,驱动从R0申请的内存中再读数据; 驱动读取完数据之后要返回,路径相反;
而readfile参数是三环的,从内核里读数据,数据会被覆盖,无法读到数据,这是系统缓冲区;这就是为什么不能使用readfile
所以驱动中拿到这几个R3的数据没有意义的,是无效的参数;
4.5 实验验证: 内核读写数据的正确姿势
sample.cpp 的DispatchControl函数
NTSTATUS DispatchControl(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
/*
类似函数调用,要给下一层驱动传参数,参数:有的分开,有的共用;
共用参数放到了IRP里了;
不共用的参数,都会有自己的内存空间==》"IRP堆栈" ( 缓冲区大小 控制码......)
然后要向IO管理器获取到IRP堆栈(要的是我这层驱动的irp堆栈地址),参数给irp,因为IRP堆栈就放在IRP后面;
IoGetCurrentIrpStackLocation(Irp); 传参是irp,因为irp堆栈放到irp后面;
但是不知道拿到那一层,自己不能去拿到这个地址,这是系统来拿,R3的其他参数都在这里main
不同的回调函数会有不同的行为,IoGetCurrentIrpStackLocation返回值里是共用体;
*/
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
//PVOID pUserBuffer = Irp->UserBuffer;
//PVOID pType3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
//ULONG uInputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//ULONG uOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pUserBuffer:%p pType3InputBuffer:%p", __FUNCTION__, __LINE__, uIoControlCode, pUserBuffer, pType3InputBuffer);
//DbgPrint("51[asm][%s:%d] uInputBufferLength:%d uOutputBufferLength:%d", __FUNCTION__, __LINE__, uInputBufferLength, uOutputBufferLength);
/*
内核中读写数据:
获取输出缓冲区的大小(地址在高2G),
数据的读写都是在这里进行
系统会让这里的缓冲区大小和R3输出缓冲区一样大
*/
PVOID pSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG uLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
DbgPrint("51[asm][%s:%d] pSystemBuffer:%p uLength:%d", __FUNCTION__, __LINE__, pSystemBuffer, uLength);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
/*
1. 这个函数表示 请求是否结束
2. 第一个参数:Irp(I/O Request Packet) 把所有的请求参数疯转到这个结构体了
3. 第二个参数,让挂起的线程快速恢复,把线程的优先级往上提;
4. 当前IO_NO_INCREMENT: 不要提升优先级,什时候唤醒就什么时候结束
5. 除了入口函数和卸载函数外,其他全部函数都是一样的,要这样处理;
*/
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
//return IoCallDriver(Irp); // 分层驱动,这里进入下一层驱动来获取需要的值
}
4.6 结果分析
输出了高2G的地址
4.7 尝试读写内核数据
R3传给驱动"hello",驱动回复"word".
Ring3.cpp 的main函数
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
char szInBuf[MAXBYTE] = { 0 };
char szOutBuf[MAXBYTE] = { "hello" };
printf("szInBuf:%p szOutBuf:%p size:%d\n", szInBuf, szOutBuf, MAXBYTE); /* 驱动要输出R3的缓存地址,这里作为对照验证 */
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, 100, szInBuf, sizeof(szInBuf), szOutBuf, sizeof(szOutBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szInBuf:%s\n", bRet, dwBytes, szInBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
sample.cpp 的DispatchControl函数
NTSTATUS DispatchControl(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
/*
类似函数调用,要给下一层驱动传参数,参数:有的分开,有的共用;
共用参数放到了IRP里了;
不共用的参数,都会有自己的内存空间==》"IRP堆栈" ( 缓冲区大小 控制码......)
然后要向IO管理器获取到IRP堆栈(要的是我这层驱动的irp堆栈地址),参数给irp,因为IRP堆栈就放在IRP后面;
IoGetCurrentIrpStackLocation(Irp); 传参是irp,因为irp堆栈放到irp后面;
但是不知道拿到那一层,自己不能去拿到这个地址,这是系统来拿,R3的其他参数都在这里main
不同的回调函数会有不同的行为,IoGetCurrentIrpStackLocation返回值里是共用体;
*/
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
//PVOID pUserBuffer = Irp->UserBuffer;
//PVOID pType3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
//ULONG uInputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//ULONG uOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pUserBuffer:%p pType3InputBuffer:%p", __FUNCTION__, __LINE__, uIoControlCode, pUserBuffer, pType3InputBuffer);
//DbgPrint("51[asm][%s:%d] uInputBufferLength:%d uOutputBufferLength:%d", __FUNCTION__, __LINE__, uInputBufferLength, uOutputBufferLength);
/*
内核中读写数据:
获取输出缓冲区的大小(地址在高2G),
数据的读写都是在这里进行
系统会让这里的缓冲区大小和R3输出缓冲区一样大
不要在Ring0下访问Ring3地址,会产生安全问题
*/
PVOID pSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG uLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pSystemBuffer:%p uLength:%d", __FUNCTION__, __LINE__,
uIoControlCode, pSystemBuffer, uLength);
switch (uIoControlCode)
{
case 100:
DbgPrint("51[asm][%s:%d] Enum Process pid:%s", __FUNCTION__, __LINE__, pSystemBuffer);
RtlStringCchCopyA((LPSTR)pSystemBuffer, uLength, "word"); //如果要向R3写数据,也是写入这个缓存中
break;
case 200:
break;
default:
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = uLength; // 返回值的长度要返回去
/*
1. 这个函数表示 请求是否结束
2. 第一个参数:Irp(I/O Request Packet) 把所有的请求参数疯转到这个结构体了
3. 第二个参数,让挂起的线程快速恢复,把线程的优先级往上提;
4. 当前IO_NO_INCREMENT: 不要提升优先级,什时候唤醒就什么时候结束
5. 除了入口函数和卸载函数外,其他全部函数都是一样的,要这样处理;
*/
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
//return IoCallDriver(Irp); // 分层驱动,这里进入下一层驱动来获取需要的值
}
4.8 结果分析
数据没有读到,也没有返回回去,这是因为控制码的原因,控制码的定义是有规范的
微软定义一个宏 CTL_CODE ,来表示控制码,可查看文档;
4.9 控制码讲解
编写控制函数的第一步就是要定义控制码(也就是定义一套自己的控制码)
控制码从3环的API
可以看出它是一个整型;而且控制码是有规范的,不能胡乱定义.
这么定义控制码是很麻烦的,所以微软提供了一个宏来完成控制码的定义
示例:
#define IOCTL_GET_REG1 CTL_CODE(DeviceType, Function, Method, Access) 只需要填写上这四个参数就行了
DeviceType:(DeviceType)设备类型;内核驱动就写FILE_DEVICE_UNKNOWN就行了
Function:(FunctionCode)控制码;可以定义自己的控制码,但是要大于0x800;(0x800以下被微软保留使用了)
Method:(TransferType);通信方式
Access:(RequiredAccess)访问权限;就是读写的权限,一般给所有权限FILE_ANY_ACCESS;
定义好以后就是这样
#define IOCTL_GET_REG1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_NEITHER,FILE_ANY_ACCESS)
#define IOCTL_GET_REG2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_NEITHER,FILE_ANY_ACCESS)
控制码会越来越多,所以一般我们还会再套一个宏,方便后期的修改.
#define MY_CTL_CODE(code) CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_NEITHER,FILE_ANY_ACCESS)
#define IOCTL_GET_REG1 MY_CTL_CODE(0x801)
#define IOCTL_GET_REG2 MY_CTL_CODE(0x802)
或者再多加一个宏
#define MY_CODE_BASE 0x800
#define MY_CTL_CODE(code) CTL_CODE(FILE_DEVICE_UNKNOWN,MY_CODE_BASE + code,METHOD_NEITHER,FILE_ANY_ACCESS)
#define IOCTL_GET_REG1 MY_CTL_CODE(0)
#define IOCTL_GET_REG2 MY_CTL_CODE(1)
控制码不按格式定义的话,驱动可能收不到,或者收到后直接系统蓝屏
4.10 实验验证:通过控制码的数据读写
Ring3.cpp 的main函数和添加的宏
#include <stdio.h>
#include <windows.h>
#include <WinIoCtl.h> /* use CTL_CODE */
// 参数2:Values of less than 0x800 are reserved for Microsoft
// 参数3:METHOD_BUFFERED表示操作系统来创建缓冲区
#define IOCTL_ENUM_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED , FILE_ANY_ACCESS)
// 中间部分省略 ...
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
char szInBuf[MAXBYTE] = { 0 };
char szOutBuf[MAXBYTE] = { "hello" };
printf("szInBuf:%p szOutBuf:%p size:%d\n", szInBuf, szOutBuf, MAXBYTE); /* 驱动要输出R3的缓存地址,这里作为对照验证 */
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, IOCTL_ENUM_PROCESS, szInBuf, sizeof(szInBuf), szOutBuf, sizeof(szOutBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szInBuf:%s szOutBuf:%s\n", bRet, dwBytes, szInBuf, szOutBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
sample.h
#pragma once
#include <ntddk.h>
#include <Wdm.h>
#include <Ntstrsafe.h>
#define DEVICE_NAME L"\\Device\\51asmMark"
#define SYMBOL_NAME L"\\DosDevices\\51asmMark"
//#define SYMBOL_NAME L"\\??\\51asmMark"
// 参数2:Values of less than 0x800 are reserved for Microsoft
// 参数3:METHOD_BUFFERED表示操作系统来创建缓冲区
#define IOCTL_ENUM_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED , FILE_ANY_ACCESS)
VOID Unload(__in struct _DRIVER_OBJECT* DriverObject);
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
NTSTATUS DispatchCreat(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchClose(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchRead(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchWrite(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchControl(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
sample.cpp 的DispatchControl函数
NTSTATUS DispatchControl(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
/*
类似函数调用,要给下一层驱动传参数,参数:有的分开,有的共用;
共用参数放到了IRP里了;
不共用的参数,都会有自己的内存空间==》"IRP堆栈" ( 缓冲区大小 控制码......)
然后要向IO管理器获取到IRP堆栈(要的是我这层驱动的irp堆栈地址),参数给irp,因为IRP堆栈就放在IRP后面;
IoGetCurrentIrpStackLocation(Irp); 传参是irp,因为irp堆栈放到irp后面;
但是不知道拿到那一层,自己不能去拿到这个地址,这是系统来拿,R3的其他参数都在这里main
不同的回调函数会有不同的行为,IoGetCurrentIrpStackLocation返回值里是共用体;
*/
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
//PVOID pUserBuffer = Irp->UserBuffer;
//PVOID pType3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
//ULONG uInputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//ULONG uOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pUserBuffer:%p pType3InputBuffer:%p", __FUNCTION__, __LINE__, uIoControlCode, pUserBuffer, pType3InputBuffer);
//DbgPrint("51[asm][%s:%d] uInputBufferLength:%d uOutputBufferLength:%d", __FUNCTION__, __LINE__, uInputBufferLength, uOutputBufferLength);
/*
内核中读写数据:
获取输出缓冲区的大小(地址在高2G),
数据的读写都是在这里进行
系统会让这里的缓冲区大小和R3输出缓冲区一样大
不要在Ring0下访问Ring3地址,会产生安全问题
*/
PVOID pSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG uLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pSystemBuffer:%p uLength:%d", __FUNCTION__, __LINE__,
uIoControlCode, pSystemBuffer, uLength);
switch (uIoControlCode)
{
case IOCTL_ENUM_PROCESS:
DbgPrint("51[asm][%s:%d] Enum Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
RtlStringCchCopyA((LPSTR)pSystemBuffer, uLength, "word"); //如果要向R3写数据,也是写入这个缓存中
break;
case IOCTL_KILL_PROCESS:
DbgPrint("51[asm][%s:%d] Kill Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
break;
default:
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = uLength; // 返回值的长度要返回去
/*
1. 这个函数表示 请求是否结束
2. 第一个参数:Irp(I/O Request Packet) 把所有的请求参数疯转到这个结构体了
3. 第二个参数,让挂起的线程快速恢复,把线程的优先级往上提;
4. 当前IO_NO_INCREMENT: 不要提升优先级,什时候唤醒就什么时候结束
5. 除了入口函数和卸载函数外,其他全部函数都是一样的,要这样处理;
*/
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
//return IoCallDriver(Irp); // 分层驱动,这里进入下一层驱动来获取需要的值
}
4.11 结果分析
驱动没有操作R3的地址,缓冲区地址是高2G,并且成功输出"word"
Ring3.cpp 的main函数
也可以这么做,这个做法类似 管道 ,匿名管道;可进可出
int main()
{
// 部分省略 ....
char szBuf[MAXBYTE] = { "hello" };
printf("szBuf:%p size:%d\n", szBuf, MAXBYTE); /* 驱动要输出R3的缓存地址,这里作为对照验证 */
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, IOCTL_ENUM_PROCESS, szBuf, sizeof(szBuf), szBuf, sizeof(szBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szBuf:%s\n", bRet, dwBytes, szBuf);
}
// 部分省略 ....
return 0;
}
4.12 同步问题
- 在驱动里操作全局变量也是会有同步问题的
- 可以直接用临界区来搞定
- 必须上个同步,要不然有两个线程同时操作就完了
KeEnterCriticalRegion(); //进入临界区
// ... 这里就是操作全局变量,对象的代码
KeLeaveCriticalRegion(); //退出临界区
//可以获取一下进程ID,来测试一下
PsGetCurrentProcessId();
KeEnterCriticalRegion();
switch (uIoControlCode)
{
case IOCTL_ENUM_PROCESS:
DbgPrint("51[asm][%s:%d] Enum Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
RtlStringCchCopyA((LPSTR)pSystemBuffer, uLength, "word"); //如果要向R3写数据,也是写入这个缓存中
break;
case IOCTL_KILL_PROCESS:
DbgPrint("51[asm][%s:%d] Kill Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
break;
default:
break;
}
KeLeaveCriticalRegion();
0x05 完整代码记录
Ring3.cpp
#include <stdio.h>
#include <windows.h>
#include <WinIoCtl.h> /* use CTL_CODE */
// 参数2:Values of less than 0x800 are reserved for Microsoft
// 参数3:METHOD_BUFFERED表示操作系统来创建缓冲区
#define IOCTL_ENUM_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED , FILE_ANY_ACCESS)
struct ProcessInfo
{
int pid;
char name[256];
};
/*
如果没有R3得程序怎么加载呢?
微软有个inf文件,这个东西可以安装,在微软的官方文档中有说明inf内容怎么写;
OS会解析inf这个配置文件,帮助动态加载驱动
例: 如果硬件需要开机启动加载,微软再开机时会自动加载inf文件
驱动加载的方式:
1. inf文件来加载
2. 动态加载(服务)
创建服务就是安装驱动(很多软件都可以使用打阴界,那么就可调用打印机的驱动,驱动的角色是服务),也就是需要4个api就可以完成这些工作;
创建服务
启动服务
停止服务
卸载服务
*/
void showErr(const char *name)
{
LPVOID lpMsgBuf;
DWORD dwErr = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
printf("[%s] Error Code:%d Msg:%s", name, dwErr, (LPCTSTR)lpMsgBuf);
//MessageBox(NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION);
// Free the buffer.
LocalFree(lpMsgBuf);
}
int LoadDriver(const char *pszPath, const char *pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__,hSCM);
hService = CreateService(hSCM, pszServiceName, pszServiceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pszPath, NULL, NULL, NULL, NULL, NULL);
if (hService == NULL)
{
//if (GetLastError() == 1073) // 当前服务已经存在,那就直接打开用
if (GetLastError() == ERROR_SERVICE_EXISTS)
{
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
}
else
{
showErr("CreateService");
goto SAFE_EXIT;
}
}
printf("[%s] CreateService Ok hService:%p \n", __FUNCTION__, hService);
/* CreateService 使用分析
SC_HANDLE CreateService(
SC_HANDLE hSCManager, // handle to SCM database 服务是由服务管理器来管理,这里拿到服务管理器;SCM(service control manager) ,因为是本地,前两个为NULL,最后一个是权限,如果选择所有权限,就是既能创建又能删除
LPCTSTR lpServiceName, // name of service to start
LPCTSTR lpDisplayName, // display name
DWORD dwDesiredAccess, // type of access to service
DWORD dwServiceType, // type of service
DWORD dwStartType, // when to start service 五种启动方式,选第3个,重启系统后就不再加载
DWORD dwErrorControl, // severity of service failure
LPCTSTR lpBinaryPathName, // name of binary file
LPCTSTR lpLoadOrderGroup, // name of load ordering group
LPDWORD lpdwTagId, // tag identifier
LPCTSTR lpDependencies, // array of dependency names
LPCTSTR lpServiceStartName, // account name
LPCTSTR lpPassword // account password);
*/
if (!StartService(hService, NULL, NULL)) /* 后两个参数,因为是驱动的服务,所以三环这里给NULL */
{
showErr("StartService");
goto SAFE_EXIT;
}
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
int UnLoadDriver(const char * pszServiceName)
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
int iRet = -1;
SERVICE_STATUS oldState = { 0 };
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
showErr("OpenSCManager");
return 0;
}
printf("[%s] OpenSCManager Ok hSCM:%p \n", __FUNCTION__, hSCM);
hService = OpenService(hSCM, pszServiceName, SERVICE_ALL_ACCESS);
if (hService == NULL)
{
showErr("OpenService");
goto SAFE_EXIT;
}
printf("[%s] OpenService Ok hService:%p \n", __FUNCTION__, hService);
/*
1. DeleteService之前,如果服务正在运行怎么办? 那就是先暂停服务
2 .OS 的peding状态:向硬件发送的请求还没有完成,此时驱动卸不掉,停不掉(如果驱动中的派遣函数没有IoCompleteRequest完成请求,就会有这种情况)
*/
if (!ControlService(hService, SERVICE_CONTROL_STOP, &oldState))
{
if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) // 服务没有启动的时候 判断错误码1062,那就启动服务;
{
printf("[%s] ControlService not active hService:%p \n", __FUNCTION__, hService);
}
else if (GetLastError() == ERROR_DEPENDENT_SERVICES_RUNNING) // peding 状态
{
printf("[%s] Please reboot system hService:%p \n", __FUNCTION__, hService);
goto SAFE_EXIT;
}
else
{
showErr("ControlService");
goto SAFE_EXIT;
}
}
printf("[%s] ControlService stop service Ok hService:%p \n", __FUNCTION__, hService);
if (!DeleteService(hService))
{
showErr("DeleteService");
goto SAFE_EXIT;
}
printf("[%s] DeleteService Ok hService:%p \n", __FUNCTION__, hService);
iRet = 0;
SAFE_EXIT:
if (hService != NULL)
{
if (CloseServiceHandle(hService) == 0)
{
showErr("CloseServiceHandle");
}
}
if (hSCM != NULL)
{
if (CloseServiceHandle(hSCM) == 0)
{
showErr("CloseServiceHandle");
}
}
return iRet;
}
int main()
{
printf("[%s] Install Driver ... \n", __FUNCTION__);
LoadDriver("C:\\Documents and Settings\\cobb\\桌面\\sample2.sys", "51asmMark");
//if (LoadDriver("sample2.sys", "51asmMark") < 0)
// return 0;
printf("[%s] Install Driver Ok \n", __FUNCTION__);
// 打开设备, 这里文件的路径就是在驱动中创建的设备名字
HANDLE hFile = CreateFile("\\\\.\\\\51asmMark", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
showErr("CreateFile");
}
else
{
printf("hFile:%p \n", hFile);
#if 0
// 读写设备
char acBuf[60] = { 0 };
printf("acBuf:%p\n", acBuf); /* 驱动要输出R3的缓存地址,这里作为对照 */
DWORD dwBytes = 0;
BOOL bRet = ReadFile(hFile, acBuf, sizeof(acBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("ReadFile");
}
else
{
printf("bRet:%d dwBytes:%d acBuf:%s\n", bRet, dwBytes, acBuf);
}
#endif
char szBuf[MAXBYTE] = { "hello" };
printf("szBuf:%p size:%d\n", szBuf, MAXBYTE); /* 驱动要输出R3的缓存地址,这里作为对照验证 */
DWORD dwBytes = 0; /* 缓冲区写进去的数据个数 */
BOOL bRet = DeviceIoControl(hFile, IOCTL_ENUM_PROCESS, szBuf, sizeof(szBuf), szBuf, sizeof(szBuf), &dwBytes, NULL);
if (!bRet)
{
showErr("DeviceIoControl");
}
else
{
// 显示结果
printf("bRet:%d dwBytes:%d szBuf:%s\n", bRet, dwBytes, szBuf);
}
// 控制设备
// 关闭设备
CloseHandle(hFile);
}
UnLoadDriver("51asmMark");
printf("[%s] UnLoadDriver Driver Ok \n", __FUNCTION__);
system("pause");
return 0;
}
sample.h
#pragma once
#include <ntddk.h>
#include <Wdm.h>
#include <Ntstrsafe.h>
#define DEVICE_NAME L"\\Device\\51asmMark"
#define SYMBOL_NAME L"\\DosDevices\\51asmMark"
//#define SYMBOL_NAME L"\\??\\51asmMark"
// 参数2:Values of less than 0x800 are reserved for Microsoft
// 参数3:METHOD_BUFFERED表示操作系统来创建缓冲区
#define IOCTL_ENUM_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED , FILE_ANY_ACCESS)
VOID Unload(__in struct _DRIVER_OBJECT* DriverObject);
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
NTSTATUS DispatchCreat(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchClose(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchRead(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchWrite(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
NTSTATUS DispatchControl(struct _DEVICE_OBJECT* DeviceObject, struct _IRP* Irp);
/*
IRP_MJ_DEVICE_CONTROL: 读写可以控制摄像头的打开关闭,控制则可以控制其他的,如:摄像头的旋转...[除了硬件厂商,其他人可不知道怎么让摄像头旋转]
是用来写不通用的功能; 读写的参数需要传一个缓冲区和一个长度,而control的参数,微软不知道传什么参数
control就是要传一些只有硬件厂家才能解析的数据
显卡(NVIDIA)的驱动安装之后,R3上有界面,设置显卡的一些特性,会调用DispatchControl这个函数,这里可以做一些硬件独有的功能;
有一种软件可以拿到硬件的各种信息:有没有可能把control的参数逆清楚,模仿参数发送数据,这样就可以拿到硬件的各种信息了
前几天在闲鱼买了太Macmini主机,最担心的就是有人通过这种方式来修改主机里的信息,如:8+256的主机刷成32+2t的,买家需要反复擦亮眼睛奥;
*/
sample.cpp
#include "sample.h"
char g_zMsg[] = "hello"; // 代表OS的任何数据
VOID Unload(__in struct _DRIVER_OBJECT* DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
/* 删除符号链接 */
UNICODE_STRING strDosDeviceName;
RtlInitUnicodeString(&strDosDeviceName, SYMBOL_NAME);
IoDeleteSymbolicLink(&strDosDeviceName);
/* 驱动对象和设备对象绑定之后,设备对象会被记录到这里 DriverObject->DeviceObject; 需要判断对象是否绑定成功*/
if (DriverObject->DeviceObject != NULL)
{
IoDeleteDevice(DriverObject->DeviceObject);
DbgPrint("51[asm][%s:%d] IoDeleteDevice OK!",__FUNCTION__,__LINE__);
}
DbgPrint("51[asm][%s:%d] Unload", __FUNCTION__, __LINE__);
}
NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject, /* PDRIVER_OBJECT 这个结构体保存了驱动的所有信息 */
PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS Status = STATUS_SUCCESS;
/* 注册派遣函数 */
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreat;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;
/* rtl: run time library C库(运行库) 微软重新做的C库,这个库更加安全; 这里做字符串使用的初始化 */
UNICODE_STRING userDevName;
RtlInitUnicodeString(&userDevName, DEVICE_NAME);
/* 绑定设备 */
PDEVICE_OBJECT pDeviceObj = NULL;
Status = IoCreateDevice(DriverObject, 0, &userDevName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE | FILE_WRITE_ONCE_MEDIA, FALSE, &pDeviceObj);
if (!NT_SUCCESS(Status)) /* 函数返回值统一是Status,这里安全性检查 */
{
DbgPrint("51[asm][%s:%d] IoCreateDevice err status:%p", __FUNCTION__, __LINE__, Status);
DriverObject->DriverUnload = Unload; /* 卸载函数中一定要删除设备,否则卸载后无法再次启动,提示设备名已存在 */
return Status;
}
/* IoCreateDevice 参数填写注意事项:
PDEVICE_OBJECT pDeviceObj = NULL; // 结构体里的 PVOID DeviceExtension; 可以申请空间大小
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject, // 驱动对象
IN ULONG DeviceExtensionSize, // 设备扩展: 微软定义了一个结构体 PDEVICE_OBJECT,如果硬件希望在结构体中增加一些成员,这里需要写成员的大小,OS会申请一段内存空间,那就就可以无限的给这个结构体增加成员;这里是额外申请一些空间
IN PUNICODE_STRING DeviceName OPTIONAL, // 给设备名字是必须唯一的,注册的名字和其他的冲突了,会创建失败;可以给个NULL,微软会随机一个名字; 文档中说明设备名必须是有格式的: \Device\DeviceName
// 注意: PUNICODE_STRING 这里的字符串Unicode字符串,字符串需要经过处理
IN DEVICE_TYPE DeviceType, // 选择为止设备就行,但是一定得给,否则无法通讯 FILE_DEVICE_UNKNOWN
IN ULONG DeviceCharacteristics, // 权限
IN BOOLEAN Exclusive, // 关键参数,是否独占的方式, 独占: 只允许一个人打开这个设备 (杀毒软件会独占)
OUT PDEVICE_OBJECT * DeviceObject
);
*/
/* 创建符号链接 */
UNICODE_STRING strDosDeviceName;
RtlInitUnicodeString(&strDosDeviceName, SYMBOL_NAME);
Status = IoCreateSymbolicLink(&strDosDeviceName, &userDevName); /* R3使用的名字在文档中选:MS-DOS Device Names R0使用的名字在文档中选:NT Device Names */
if (!NT_SUCCESS(Status))
{
/* 驱动对象和设备对象绑定之后,设备对象会被记录到这里 DriverObject->DeviceObject; 需要判断对象是否绑定成功*/
if (DriverObject->DeviceObject != NULL)
{
/* 删除符号链接 */
IoDeleteSymbolicLink(&strDosDeviceName);
IoDeleteDevice(DriverObject->DeviceObject);
DbgPrint("51[asm][%s:%d] IoCreateSymbolicLink err!", __FUNCTION__, __LINE__);
}
return Status;
}
DriverObject->DriverUnload = Unload;
DbgPrint("51[asm][%s:%d] hello word! DriverEntry:%p,Unload:%p,&status:%p,pDeviceObj:%p", __FUNCTION__, __LINE__, DriverEntry, Unload, &Status,&pDeviceObj);
return Status;
}
NTSTATUS DispatchCreat(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
// 这样返回的数据才是完整的
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchClose(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
// 这样返回的数据才是完整的
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchRead(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
/* 验证这里是R3的缓存地址,输出一下 */
DbgPrint("51[asm][%s:%d] Irp->UserBuffer:%p", __FUNCTION__, __LINE__, Irp->UserBuffer);
RtlCopyMemory(Irp->UserBuffer,g_zMsg,sizeof(g_zMsg));
Irp->IoStatus.Status = STATUS_SUCCESS; /* 告诉R3返回的状态 */
Irp->IoStatus.Information = sizeof(g_zMsg); /* 告诉R3读取的字节数 */
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS; /* R3的ReadFile的返回值 */
}
NTSTATUS DispatchWrite(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
// 这样返回的数据才是完整的
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchControl(_DEVICE_OBJECT* DeviceObject, _IRP* Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("51[asm][%s:%d]", __FUNCTION__, __LINE__);
/*
类似函数调用,要给下一层驱动传参数,参数:有的分开,有的共用;
共用参数放到了IRP里了;
不共用的参数,都会有自己的内存空间==》"IRP堆栈" ( 缓冲区大小 控制码......)
然后要向IO管理器获取到IRP堆栈(要的是我这层驱动的irp堆栈地址),参数给irp,因为IRP堆栈就放在IRP后面;
IoGetCurrentIrpStackLocation(Irp); 传参是irp,因为irp堆栈放到irp后面;
但是不知道拿到那一层,自己不能去拿到这个地址,这是系统来拿,R3的其他参数都在这里main
不同的回调函数会有不同的行为,IoGetCurrentIrpStackLocation返回值里是共用体;
*/
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
//PVOID pUserBuffer = Irp->UserBuffer;
//PVOID pType3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
//ULONG uInputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//ULONG uOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pUserBuffer:%p pType3InputBuffer:%p", __FUNCTION__, __LINE__, uIoControlCode, pUserBuffer, pType3InputBuffer);
//DbgPrint("51[asm][%s:%d] uInputBufferLength:%d uOutputBufferLength:%d", __FUNCTION__, __LINE__, uInputBufferLength, uOutputBufferLength);
/*
内核中读写数据:
获取输出缓冲区的大小(地址在高2G),
数据的读写都是在这里进行
系统会让这里的缓冲区大小和R3输出缓冲区一样大
不要在Ring0下访问Ring3地址,会产生安全问题
*/
PVOID pSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG uLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
DbgPrint("51[asm][%s:%d] uIoControlCode:%08x pSystemBuffer:%p uLength:%d", __FUNCTION__, __LINE__,
uIoControlCode, pSystemBuffer, uLength);
switch (uIoControlCode)
{
case IOCTL_ENUM_PROCESS:
DbgPrint("51[asm][%s:%d] Enum Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
RtlStringCchCopyA((LPSTR)pSystemBuffer, uLength, "word"); //如果要向R3写数据,也是写入这个缓存中
break;
case IOCTL_KILL_PROCESS:
DbgPrint("51[asm][%s:%d] Kill Process pid:%p", __FUNCTION__, __LINE__, pSystemBuffer);
break;
default:
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = uLength; // 返回值的长度要返回去
/*
1. 这个函数表示 请求是否结束
2. 第一个参数:Irp(I/O Request Packet) 把所有的请求参数疯转到这个结构体了
3. 第二个参数,让挂起的线程快速恢复,把线程的优先级往上提;
4. 当前IO_NO_INCREMENT: 不要提升优先级,什时候唤醒就什么时候结束
5. 除了入口函数和卸载函数外,其他全部函数都是一样的,要这样处理;
*/
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
//return IoCallDriver(Irp); // 分层驱动,这里进入下一层驱动来获取需要的值
}