实现驱动加载器并且和驱动通讯(提示:内容稍长)
本文最后更新于 253 天前,其中的信息可能已经有所发展或是发生改变。

file

 
 
目标:

  1. 用户一键使用: 驱动加载(将驱动的安装、启动、停止、卸载的服务放到R3程序中来实现,作为可发布程序使用)
  2. 通过驱动的control回调函数和控制码来实现通讯

 
 
 

0x01 项目管理

环境整理:

创建一个空白项目,主要是为了用空白项目的解决方案

把Ring3和驱动加入到解决方案,环境里删掉刚创建的空白项目即可,就不用频繁的切换环境

把Ring3和驱动的输出都放到bin目录下(两个都需要设置),便于实验

file

 
整理后的效果

file

 

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 问题分析

期间遇到一个问题,折腾老半天,无效,如下图:

file

解决: 拒绝访问,是权限不足,关闭vs2019,用管理员的权限运行(感谢前辈的博客);

 

2.3 结果分析

服务创建成功了

file

 

2.4 原理分析

那么问题来了, creatServer 这个api做了什么?

其实在OS中记录下驱动的信息,regedit 注册表中看一下,注册表的每一个表象,都是创建的服务creatServer的参数,因此开机时,OS遍历注册表 就可以遍历驱动

也就是可以通过这个目录遍历所有系统安装的驱动

file

 

0x03 启动服务

3.1 出现问题1

问题1: 第二次创建时,提示服务已经存在了???

因为此时在创建服务失败后并没有判断服务是否已经存在了,那么就需要根据错误码判断服务是否存在,如果服务存在,那就直接打开服务就行; 下图中左侧打印出错误码,dwErr;

file

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、 驱动一直没有卸载过,卸载函数没写过

file

 

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 卸载结果分析

卸载成功,但是创建找不到指定文件

file

 

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状态:向硬件发送的请求还没有完成,此时驱动卸不掉,停不掉

file

 

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 实验结果分析

启动服务的时候,系统路径找不到,验证有误,驱动路径有问题,修改为绝对路径试试;

file

 

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,成功加载和卸载;

加载工具的实现已完成;

file

 

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,

file

 
4.2.1

NT框架的设计问题:irp里没有R3的控制码

系统采用分层驱动的方式,例如:键盘是两个驱动加载组成的;

file

例: 有线键盘 不同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的数据没有意义的,是无效的参数;

file

 

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的地址

file

 

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 结果分析

数据没有读到,也没有返回回去,这是因为控制码的原因,控制码的定义是有规范的

file

微软定义一个宏 CTL_CODE ,来表示控制码,可查看文档;

 

4.9 控制码讲解

编写控制函数的第一步就是要定义控制码(也就是定义一套自己的控制码)

控制码从3环的API可以看出它是一个整型;而且控制码是有规范的,不能胡乱定义.

file

这么定义控制码是很麻烦的,所以微软提供了一个宏来完成控制码的定义

示例:
#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"

file

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); // 分层驱动,这里进入下一层驱动来获取需要的值
}

兼容xp的插件安装

YY-Thunks – 让兼容 Windows 更轻松

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇