恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
普通用户打开内核驱动:设备ACL与服务权限配置指南
首页
资讯中心
/
普通用户打开内核驱动:设备ACL与服务权限配置指南
普通用户打开内核驱动:设备ACL与服务权限配置指南
发布时间:2026/9/26 16:57:45
先拿实物说话内核驱动安装成功后普通用户拿CreateFile去打开\\.\xxx十有八九返回的是错误码 5拒绝访问就算你已经把服务启动起来也一样。这个现象我帮人排查过好几轮需求方往往就一句“让普通用户也能打开内核驱动”可“打开”这个词在驱动项目里至少能拆出三层意思加载驱动文件、启动驱动服务、打开驱动设备。每一层对应一把不同的锁拿错钥匙当然开不了门。这篇把我实际用过、验证过能跑的配置和思路写清楚适合正在做设备驱动、系统工具或者企业终端软件的开发者参考。1. 先理解“普通用户打开内核驱动”到底卡在哪1.1 三种常见场景对应三种不同的“锁”我平时接到的相关需求基本都能归到下面三类里你们可以先对号入座第一类普通用户想直接加载驱动。比如双击一个 exe里面调用sc create或StartService想把.sys拉进内核。这类需求从一开始就是撞墙的因为加载驱动的操作涉及系统级特权不属于普通用户可以触碰的范围。第二类驱动已经装好了但它的启动类型是“手动”普通用户希望自己控制这个服务的启停。这类问题卡在服务控制管理器SCM的权限上默认情况下标准用户连查询服务配置都要受限更别说sc start。第三类驱动服务已经跑起来了普通用户运行业务程序去CreateFile打开设备对象却拿不到句柄。这一类问题最隐蔽也最容易被忽视因为很多人只会盯着服务和驱动文件完全没想过驱动设备对象上还挂着一个独立的安全描述符。我发现实际项目里第三类占了八成以上。服务能跑驱动也加载了但设备对象默认只允许系统账户和管理员访问普通用户过不去。1.2 Windows 权限模型里那几道关卡Windows 对内核驱动的保护并不是只有一道墙而是层层叠叠的几层理解这个结构比直接抄代码更重要。第一层驱动加载特权SeLoadDriverPrivilege。这个特权默认只授予管理员和系统账户普通用户根本没有。这就意味着“让普通用户在运行时把驱动加载进内核”这条路基本走不通正确做法是把加载动作放在安装阶段由管理员完成一次。第二层服务对象的安全描述符。每个驱动服务在 SCM 里也是一个对象有独立的 DACL。普通用户想启动、停止、修改这个服务都必须具备对应权限位。很多时候管理员装完驱动就完了服务 ACL 完全没调普通用户自然操作不了。第三层设备对象的安全描述符。驱动执行IoCreateDevice创建出的设备对象默认安全设置只授权 SYSTEM 和管理员。即使服务已经以 SYSTEM 身份加载了驱动普通用户去CreateFile一样会被挡在门外。这一层被绝大多数教程忽略。第四层就是常见的 UAC。普通用户跑程序时用的是标准令牌管理员跑程序时是完整管理员令牌两者对内核驱动相关资源的可见性和可访问性都不同。即便你改了 ACL如果测试时用的是管理员身份也测不出真实效果。上面四层里驱动加载层是系统级红线普通用户永远不该直接碰服务层和设备层才是“让普通用户打开驱动”真正要做文章的地方。2. 方案选型三条路线怎么选2.1 方案 A安装时提权运行时零权限推荐这个思路是我在项目里用得最多、也觉得最干净的安装阶段由管理员完成驱动文件的复制、服务创建、服务启动把驱动做成开机自动加载然后只把“设备访问权限”放开给普通用户。普通用户后续只需要打开设备对象通信不用碰任何服务管理操作。这样做的本质是“一次提权长期免管理员”。驱动常驻内核用户态程序不需要跟 SCM 打交道也不需要动不动弹 UAC。设备对象的安全描述符在驱动创建时指定把普通用户所在的组加进去即可。优势非常明显一是运行路径最短普通用户拿不到任何服务控制权二是驱动生命周期稳定不会出现用户随手把服务停了导致其他业务挂掉的情况三是安全风险相对可控你只开了设备访问接口服务管理权限仍然攥在管理员手里。缺点也有驱动必须跟着系统启动或者至少保证业务跑起来之前它已经在运行。对有些按需加载的轻量工具来说常驻驱动会显得重了一点但绝大多数企业终端场景开机常驻是能接受的。2.2 方案 B通过服务 ACL 放行普通用户启停如果你的驱动不适合常驻确实需要普通用户按需启动和停止那就得动服务对象的安全描述符。Windows 允许你用 SDDL 修改服务 DACL单独给某个用户或组授出SERVICE_START、SERVICE_STOP权限。操作命令很简单以管理员执行sc sdset MyDriver D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;RPWP;;;BU)这串 SDDL 看起来复杂实际就是在默认服务权限的基础上给BU内置 Users 组追加了RP启动和WP停止两个权限位。改完之后普通用户执行sc start MyDriver就能把服务拉起来。这个方案有个必须提醒的坑普通用户既然能把驱动服务停掉就有可能在某些情况下导致系统其他模块出问题。如果驱动服务的停用会影响系统稳定或数据完整性我强烈不建议把 STOP 权限也放开只给 START 就够了。另外普通用户启动服务时没有管理员令牌服务本身以 SYSTEM 身份运行这中间的身份切换有时候会让人误以为启动失败实际上只是权限位没给够。2.3 方案 C用 SYSTEM 代理间接加载第三种路线比较重但安全边界最清晰普通用户完全不接触驱动甚至不知道驱动的存在。由一个以 SYSTEM 身份运行的常驻服务负责加载驱动、与设备对象通信普通用户程序通过命名管道、RPC 或者本地回环 TCP 向这个代理服务发请求。这个方案适合驱动功能非常敏感、不能直接暴露给普通用户 IOCTL 的场景。比如驱动里有写物理内存、读写任意进程内存的接口这种能力落到普通用户手里就是灾难。代理层可以在用户态和内核态之间做一次请求过滤只转发业务需要的少量操作。代价是工程量大需要额外写一个用户态服务还要处理通信协议、异常恢复、请求校验。对大部分需求来说方案 A 已经足够方案 C 有点杀鸡用牛刀但如果你想做产品化发布方案 C 往往是更稳妥的长期选择。2.4 三种方案怎么选一张对比表维度方案 A 常驻驱动设备 ACL方案 B 服务 ACL 放行启停方案 C SYSTEM 代理实现复杂度低低高普通用户操作路径直接打开设备先启动服务再打开设备连接代理服务管理员介入频率仅安装时安装时配置 ACL安装时安全风险暴露驱动 IOCTL暴露服务启停权限相对最低适合场景业务软件需要长期与驱动通信按需加载的工具型驱动敏感能力必须收口我个人的倾向非常明确先问自己驱动是不是必须频繁被打开。如果是方案 A如果只是偶尔用一次方案 B 也能接受如果里面包含高危操作那就别犹豫直接上方案 C。3. 实操让普通用户成功打开驱动3.1 第一步给设备对象设置普通用户可访问的安全描述符这一步是整个方案的核心。驱动在创建设备对象时都有机会指定安全描述符只是很多驱动代码没写所以用了系统默认的“仅管理员可访问”。如果你用 KMDF 写驱动事情很简单在EvtDriverDeviceAdd回调里调用WdfDeviceInitAssignSDDLString就行NTSTATUS MyEvtDeviceAdd( WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit ) { DECLARE_CONST_UNICODE_STRING(sddlDevice, LD:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;BU)); NTSTATUS status WdfDeviceInitAssignSDDLString(DeviceInit, sddlDevice); if (!NT_SUCCESS(status)) { return status; } // 继续创建设备对象、配置队列…… }这串 SDDL 拆开看很直白D:P表示这是一个受保护的 DACL不从父对象继承额外权限A;;GA;;;SY给 SYSTEM 完全控制A;;GA;;;BA给内置管理员组完全控制A;;GA;;;BU给内置 Users 组完全控制。加上BU之后所有登录到本机的普通用户都能CreateFile打开这个设备对象了。注意这里用的是BUBuiltin Users不是WDEveryone。WD会把访客、匿名账户都算进去安全上不可控。如果你还在维护老的 WDM 驱动也有对应的做法要用RtlCreateSecurityDescriptor、RtlSetDaclSecurityDescriptor配合ObSetSecurityObjectByDescriptor来设置设备对象的安全描述符代码绕一些效果一样。能迁移到 KMDF 的尽量迁移省心很多。3.2 第二步用 INF 和 sc.exe 配合调整服务访问权设备 ACL 只是解决了“打开设备”的问题如果你的驱动不是自启动普通用户还需要能把服务拉起来。这一节就可以和方案 B 结合着看。INF 文件里创建驱动服务的段可以带上服务安全描述符。示例[MyDriver.ServiceInst] DisplayName My Kernel Driver ServiceType 1 StartType 3 ErrorControl 1 ServiceBinary %12%\MyDriver.sys Security D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;RP;;;BU)这里的Security是服务对象的安全描述符不是设备对象的。我给BU只加了RP启动权限没给停止权限原因前面说过普通用户能把驱动拉起来干活但没资格随手关掉。很多情况下 INF 已经安装完了你不想重新卸载安装一遍那就直接用sc.exe改。管理员开终端执行sc sdset MyDriver D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;RP;;;BU)执行成功后普通用户就能用sc start MyDriver启动服务了。你可以让普通用户跑一下sc query MyDriver验证如果之前连查询都拒绝现在能查到状态说明 ACL 已经生效。也许有人会问INF 里设了 Security为什么还要sc sdset因为如果驱动早就装上去了INF 的 Security 不会反向覆盖已经存在的服务 DACL只能通过sc sdset补。两种都是常用手段看你在什么阶段操作。3.3 第三步普通用户侧验证访问是否真正放行配置完了不要急着写业务代码先用一个最小测试没准能救你一晚上的时间。让普通用户身份的进程跑下面这段HANDLE hDevice CreateFileW( L\\\\.\\MyDriver, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (hDevice INVALID_HANDLE_VALUE) { wprintf(Lopen failed, error %u\n, GetLastError()); } else { wprintf(Lopen succeeded\n); CloseHandle(hDevice); }注意这里有个非常隐蔽的坑如果你想验证的是普通用户权限就必须确保测试进程是以标准用户令牌运行的。不少开发者在管理员账户里开着“以管理员身份运行”的终端去跑测试程序结果当然一切正常然后打包给终端用户又报 5最后发现是测试环境自欺欺人。想看设备对象的安全描述符到底配成了什么样推荐用 Sysinternals 的 WinObj打开后找到\Device\下的对应设备右键看安全属性ACL 里是否包含 Users 组一目了然。这个工具比反复改代码重启驱动高效太多。3.4 实操中的参数怎么选好几个参数会影响整体体验我直接给结论StartType是驱动服务启动方式开发测试用 3手动方便生产环境配合方案 A 建议改 2自动这样普通用户连sc start都省了。设备对象 ACL 用BU还是AU取决于你有多信任本机用户BU是内置 Users登录本机的普通用户都在里面AU是 Authenticated Users范围略宽。如果驱动服务的 IOCTL 比较敏感更推荐单独创建一个安全组比如MyDriverUsers把需要放行的账号加进去SDDL 里写该组的 SID别图省事直接全员放开。DeviceIoControl访问方式建议设置为FILE_ANY_ACCESS? 其实设备访问权限在GENERIC_READ | GENERIC_WRITE层面已经限定了一部分但要注意创建设备时指定的DeviceType和Characteristics也会影响访问检查比如某些设备默认要求FILE_READ_DATA权限。写驱动时留意这一点避免 ACL 已经放行、设备属性又把用户卡死。4. 常见问题与排查实录4.1 错误 5ACL 明明改了还是拒绝访问这是我最常被问到的问题。排查顺序基本是固定的先确定驱动服务在不在运行。驱动没加载、设备对象不存在CreateFile会得到ERROR_FILE_NOT_FOUND而不是 5你既然得到 5说明设备对象存在但访问被拒。然后确认测试进程的令牌是不是标准用户。用runas /trustlevel:0x20000启动测试程序可以模拟标准用户环境不用反复切换账号。最后再去 WinObj 里看设备对象 DACL确认 Users 组确实在里面。注意有些旧驱动可能在DriverEntry里又调用了一次IoCreateDevice或者WdfDeviceCreate后一次创建把前面设置的安全描述符覆盖掉了排查时别漏。另一个经常踩的雷是驱动暴露的是设备接口Device Interface而你在 INF 里设置的AddInterface段有自己的权限设置和设备对象 DACL 是两层东西。普通用户可能对设备对象有权限但对设备接口符号链接没有打开\\.\GUID路径时照样报 5。这种情况下要么让应用直接打开设备对象路径要么同步调整接口的安全描述符。4.2 驱动签名与测试模式普通用户也会撞上的墙如果你在 64 位系统上跑未签名驱动错误码往往不是 5而是打开服务失败、加载失败之类的 577 错误甚至直接给出“无法验证数字签名”。很多团队调试阶段会开测试签名模式管理员执行bcdedit /set testsigning on重启后驱动就能加载。注意这个操作只影响签名校验不影响权限模型普通用户能不能打开设备还是取决于前面说的 ACL 配置。测试签名模式在 Windows 10/11 上能用但生产环境必须换成正式签名的驱动否则企业安全策略会把机器卡得死死的。还有一类问题是“签名有效但加载失败”这通常不是签名本身的问题而是驱动不兼容签名策略。可以在事件查看器里看 System 日志下 Kernel-PnP 的事件里面会有具体拒绝原因。4.3 HVCI 与“内存完整性”导致驱动罢工Windows 11 和较新的 Windows 10 默认开启“内存完整性HVCI”功能它利用虚拟化安全强制内核驱动必须符合更严格的兼容性要求。很多老驱动平时没事一旦系统开了 HVCI加载直接报 1275 错误这跟权限毫无关系。排查方法很简单去 Windows 安全中心看“设备安全性”里的“内核隔离”开关。如果确认是 HVCI 拦的要么更新驱动到支持 HVCI 的版本要么在测试环境临时关闭该功能。注意生产环境千万不能要求终端用户为了你这个驱动去关系统安全功能该适配的就老老实实适配。4.4 别踩的坑乱改 Everyone 的灾难级后果网上有些文章会教你把设备 SDDL 写成D:P(A;;GA;;;WD)也就是 Everyone 完全控制。这种写法在测试环境跑得通但如果驱动里存在任何 IOCTL 漏洞就等于把整个后门开放给包括匿名用户在内的所有人。更离谱的操作是干脆关掉 UAC、把标准用户加进 Administrators 组、或者把驱动文件所在的目录权限改成 Everyone 可写。这些做法都是拿整机安全性换一时的顺畅后面企业安全审计时每一项都能被单独拎出来扣分。我见过太多项目因为这种偷懒式配置后面被迫返工。正确的思路永远是给最少的人开最少的口子。驱动本身在内核态运行权限比普通 exe 大得多给它配 ACL 时脑子里要当成“给一个能读写内存的程序配权限”来对待而不是“给一个文档打开权限”。5. 权限放开之后驱动的安全底线和经验5.1 设备对象 ACL 不该“一股脑放开”有些人看完前面的示例直接把BU写进所有驱动这是不健康的。BU放行的对象是“这台机器上所有能登录的普通用户”如果你的软件是单机小工具还好如果是企业内部分发给大量终端的软件建议建一个专属组比如Domain Users的子集或者本地安全组然后把 SDDL 里BU换成那个组的 SID。SDDL 里写 SID 时用S-1-5-...的格式别直接写组名组名在不同语言版本系统上可能不一样硬编码名称容易出幺蛾子。把设备访问权限收敛到业务真正需要的用户范围这是成本最低的安全措施。另外如果驱动支持多个设备实例或者动态创建命名设备每个设备对象创建时都要保证安全描述符被正确设置最容易漏的是那些辅助设备对象比如用于调试、性能统计的私有设备它们往往比主设备更容易让人忽视也更容易被攻击者利用。5.2 IOCTL 处理中的用户态输入校验ACL 放行普通用户后IOCTL 接口就是驱动最核心的攻击面。普通用户传来的每一个字节都不可信这是内核开发的铁律。我建议开发时做三层校验第一层使用METHOD_BUFFERED这类安全传输方式尽量避免直接映射用户缓冲区第二层在 dispatch 例程里检查输入输出缓冲区长度防止越界读写长度不匹配直接返回STATUS_INVALID_BUFFER_SIZE第三层如果是必须使用直接 I/O 的场景对用户态传入的指针做ProbeForRead/ProbeForWrite绝不要裸着解引用。还有一点经验很重要IOCTL 控制码里的访问权限位要填对。比如CTL_CODE(FileDevice, Function, Method, Access)如果驱动功能是只读的Access就写FILE_READ_DATA用户态应用就用GENERIC_READ打开设备。别图省事给所有 IOCTL 都标GENERIC_READ | GENERIC_WRITE | FILE_ANY_ACCESS这等于把功能全部开放给任何能打开设备的人。5.3 架构层面的经验一次提权长期免管理员回看整篇文章最想传递的一句话是让普通用户“打开内核驱动”不是让他真的去执行加载驱动的动作而是把驱动部署做到位之后让他能以普通权限正常使用驱动的能力。具体到系统设计上我推荐这样一个组合安装程序运行一次完成驱动文件复制、服务创建、服务启动、设备 ACL 设置驱动服务的StartType设为自动让它在登录前就跑起来设备对象 SDDL 只放行必要的用户组业务程序完全不碰 SCM API只管CreateFile和DeviceIoControl。这套组合的好处是普通用户从头到尾只需要双击业务软件不需要理解服务、驱动、UAC 这些概念出问题的概率最小排错也最方便。我实际踩过最大的坑是把 SDDL 配好之后普通用户依然打不开设备。折腾一整晚最后用 WinObj 打开设备对象属性一看发现驱动里有两条创建设备的路径第二条把第一条设置的安全描述符覆盖掉了。从那以后我做驱动都会在测试环节专门加一个“标准用户令牌打开设备”的自动化用例确保不是凭感觉觉得没问题。最后一个实用小技巧验证的时候不需要反复注销切换账号用管理员终端跑runas /trustlevel:0x20000 cmd.exe弹出的命令窗口就是标准用户权限在里面运行测试程序十分钟能验证完所有权限配置效率比来回切用户高得多。