编写自己的驱动加载程序

最近想自己写个驱动加载程序。(平常用的加载程序不能拖动选择文件)
于是梳理一下驱动加载的两种方法
代码和程序也贴在github一份。

一般加载驱动的步骤为
打开服务控制器
根据任务条件创建服务
打开设备或服务
设置设备或者服务的状态.
清理工作(关闭服务或者设备的句柄)
需要的函数:
通过OpenSCManager函数打开SCM,获取其句柄
通过CreateService函数利用SCM句柄创建一个服务
通过ControlService传入的标志位安装启动服务等等。

两步安装驱动:

SC_HANDLE sh = OpenSCManager(
  NULL,   // 机器名称,NULL表示本机.
NULL,     // 设备管理器数据库,NULL表示默认值
  SC_MANAGER_ALL_ACCESS    // 打开的权限
);
SC_HANDLE m_hServiceDDK = CreateService(
                sh,//SCManager句柄
                DriverName.c_str(),//驱动服务名称
                DriverName.c_str(),//驱动服务显示名称
                SERVICE_ALL_ACCESS,//访问权限
                SERVICE_KERNEL_DRIVER,//服务类型(驱动程序)
                SERVICE_DEMAND_START,//启动方式(需要时启动,注册表驱动程序的Start值)
                SERVICE_ERROR_IGNORE,//错误忽略
                szFilePath,//驱动程序文件路径
                NULL,//加载组命令
                NULL,//TagId
                NULL,//依存关系
                NULL,//服务启动名
                NULL);//密码

这两个函数做完后,核心的安装就完成了。
启动驱动

BOOL WINAPI StartService(
      _In_     SC_HANDLE hService,
      _In_     DWORD     dwNumServiceArgs,
      _In_opt_ LPCTSTR   *lpServiceArgVectors
);

控制状态,停止驱动

m_hServiceDDK = OpenService(sh, DriverName.c_str(), SERVICE_STOP);
        SERVICE_STATUS svcsta = { 0 };
        BOOL bRet = ControlService(m_hServiceDDK, SERVICE_CONTROL_STOP, &svcsta);

卸载驱动

m_hServiceDDK = OpenService(sh, DriverName.c_str(), SERVICE_STOP | DELETE);
DeleteService(m_hServiceDDK);

在返回结果异常的判断GetLastError()中,加入了几个常见的判断

ERROR_SERVICE_ALREADY_RUNNING
ERROR_SERVICE_NOT_FOUND
ERROR_SERVICE_NEVER_STARTED
ERROR_SERVICE_NOT_ACTIVE
ERROR_SERVICE_DOES_NOT_EXIST
代码比较长,先贴一下安装代码,其余的可以在附件查看

 复制代码 隐藏代码
void CDriverLoaderDlg::OnBnClickedButtonInstall()
{

    if (wcslen(szFilePath)==0)
    {
            Msg(_T("请选择文件"));
            return;
    }
    size_t pos = wstring(szFilePath).find_last_of('\\');
    DriverName.assign(wstring(szFilePath).substr(pos + 1));

    sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!sh)
    {
            Msg(_T("打开服务控制器失败,请检查是否以管理员权限运行"));
            CloseServiceHandle(sh);
            return;
    }
    Msg(CString(DriverName.c_str()));
    SC_HANDLE m_hServiceDDK = CreateService(
            sh,//SMC句柄
            DriverName.c_str(),//驱动服务名称(驱动程序的在注册表中的名字)
            DriverName.c_str(),//驱动服务显示名称(注册表驱动程序的DisplayName值)
            SERVICE_ALL_ACCESS,//权限(所有访问权限)
            SERVICE_KERNEL_DRIVER,//服务类型(驱动程序)
            SERVICE_DEMAND_START,//启动方式(需要时启动,注册表驱动程序的Start值)
            SERVICE_ERROR_IGNORE,//错误控制(忽略,注册表驱动程序的ErrorControl值)
            szFilePath,//服务的二进制文件路径(驱动程序文件路径, 注册表驱动程序的ImagePath值)
            NULL,//加载组命令
            NULL,//TagId
            NULL,//依存关系
            NULL,//服务启动名
            NULL);//密码
    if (!m_hServiceDDK)
    {
            if (GetLastError() == ERROR_SERVICE_EXISTS)
            {
                    Msg(_T("驱动已经存在"));
                    if(!m_hServiceDDK)m_hServiceDDK = OpenService(sh, DriverName.c_str(), SERVICE_ALL_ACCESS);
            }
            else {
                    TCHAR msg[100];
                    wprintf_s(msg, "安装失败,错误码 %p", GetLastError());
                    Msg(msg);

                    Msg(_T("Error while Install ,error code:" + GetLastError()));
                    MessageBox(NULL, DriverName.c_str(),  MB_OK);
            }

    }
    else {
            Msg(_T("驱动安装成功!"));
    }
    CloseServiceHandle(sh);
    CloseServiceHandle(m_hServiceDDK);
}

效果(目标平台win10)
最后和普通的monitor加载的效果是一样的。

编外:
在写MFC的时候,为了实现拖放文件来获取文件路径的功能,众所周知添加AcceptFile等属性即可,但是我在vs2017&win10的开发环境下却没有效果,后来查询得知,可能是被win10的安全措施屏蔽了。需要在初始化的时候加上如下代码
复制代码 隐藏代码
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);//解决Win10下的拖放文件问题

ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);

由于打开服务管理器需要特权,我们在vs的配置调试->属性->链接器->清单文件中

把UAC执行级别改成如图所示即可。
通过ZwSetSystemInformation加载驱动
这个是RootKits的技术。
ZwSetSystemInformation函数是个未公开的函数,调用38号则会加载驱动。
原型如下

typedef NTSTATUS(__stdcall *ZwSetSystemInformation)(
        IN DWORD SystemInformationClass,
        IN OUT PVOID SystemInformation,
        IN ULONG SystemInformationLength
    );

我们需要手动获取函数的地址

RtlInitUnicodeString,ZwSetSystemInformation

这种方法的特性是比较简便隐秘,相比上一个方法不会在进程中主动查找服务管理器等敏感API。但是,这种方法没有提供卸载的特性,驱动加载后,只能通过重启系统来卸载。

const INT SystemLoadAndCallImage = 38;
typedef struct  _UNICODE_STRING {

        USHORT Length;
        USHORT MaximumLength;
        PWCH   Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _SYSTEM_LOAD_AND_CALL_IMAGE {
        UNICODE_STRING ModuleName;
} SYSTEM_LOAD_AND_CALL_IMAGE, *PSYSTEM_LOAD_AND_CALL_IMAGE;

typedef void(*RTLINITUNICODESTRING)(
        PUNICODE_STRING DestinationString,
        PCWSTR          SourceString
        );

typedef NTSTATUS(__stdcall *ZwSetSystemInformation)(
        IN DWORD SystemInformationClass,
        IN OUT PVOID SystemInformation,
        IN ULONG SystemInformationLength
        );

bool load(PWCHAR path) {
        SYSTEM_LOAD_AND_CALL_IMAGE Images;
        RTLINITUNICODESTRING RtlInitUnicodeString;
        ZwSetSystemInformation fZwSetSystemInformation;
        if (!(RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlInitUnicodeString")))
                return false;
        if (!(fZwSetSystemInformation = (ZwSetSystemInformation)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "ZwSetSystemInformation")))
                return false;
        RtlInitUnicodeString(&(Images.ModuleName), path);
        if (!NT_SUCCESS(fZwSetSystemInformation(SystemLoadAndCallImage, &Images, sizeof(SYSTEM_LOAD_AND_CALL_IMAGE))))
                return false;
        return true;
}
//注意path的格式,和普通的ring0读设备的路径一个问号不同的是
//这里有两个问号,以\\??\\开头,后面是驱动路径。例如\\??\\C:\\YOURDRIVERPATH.sys

最后,virscan安全检测一下

源代码和二进制文件
https://github.com/pcy190/DriverKit/tree/master/DriverLoader

网盘链接
下载: https://www.lanzous.com/i3a4yhi 密码:happy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞天的大鹅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值