首页
社区
课程
招聘
[原创]编写驱动加载程序
2019-2-27 23:25 17753

[原创]编写驱动加载程序

2019-2-27 23:25
17753

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

一般加载驱动的步骤为

  • 打开服务控制器
  • 根据任务条件创建服务
  • 打开设备或服务
  • 设置设备或者服务的状态.
  • 清理工作(关闭服务或者设备的句柄)

需要的函数:
通过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

最后,代码也贴在github一份。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞5
打赏
分享
最新回复 (14)
雪    币: 6124
活跃值: (4056)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
黑洛 1 2019-2-28 00:06
2
0
其实@ZhuHuiBeiShadiao展示了一种效果非常好的“地下加载”的方式:https://github.com/ZhuHuiBeiShaDiao/NewHideDriverEx
最后于 2019-2-28 00:07 被黑洛编辑 ,原因:
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
邓dg 2019-2-28 21:15
3
0
好几把厉害
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xuwenfeng 2019-3-2 16:55
4
0
不错,来深信服工作么?  工作地点,长沙或者深圳。
雪    币: 13567
活跃值: (1195)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
严启真 2019-3-3 18:51
5
0
高手啊
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
git_89805HAC-Li 2019-12-1 22:06
6
0
返回 错误代码577 可安装 无法启动;
有啥办法吗。。。。
雪    币: 195
活跃值: (71)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
快乐的萌小萌 2019-12-2 08:32
7
0
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
一曲沙丶天涯 2020-3-26 14:28
8
0

0

最后于 2020-8-18 15:26 被一曲沙丶天涯编辑 ,原因:
雪    币: 49
活跃值: (236)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
化魔 2021-1-9 20:13
9
0
留下脚印,方便查找
雪    币: 1050
活跃值: (1067)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
NewMai 1 2021-10-28 12:20
10
0
雪    币: 73
活跃值: (893)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hixhi 2021-10-28 16:40
11
0
对于有inf文件的这种驱动,此方法有用么
雪    币: 52
活跃值: (56)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wwwkx 2021-10-30 20:38
12
0
留个脚印,好牛逼的样子
雪    币: 474
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
yazigegeda 2023-10-30 13:00
13
0
这个好像是隐藏已经加载的驱动吧?
雪    币: 474
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
yazigegeda 2023-10-30 13:03
14
0
这个好像是隐藏已经加载的驱动吧?
雪    币: 474
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
yazigegeda 2023-10-30 13:03
15
0
我回复二楼的为啥引用不了奇怪了是bug吗
游客
登录 | 注册 方可回帖
返回