特权服务
crates/argus-service 提供可选的 Windows 服务;设置入口见权限。服务以 LocalSystem 运行,不创建窗口或 WebView。目前承接手动进程操作、自动规则中的操作、按需每进程网络、磁盘读写与 CPU 内核传感器采集、句柄查询、服务控制和断开 IPv4 TCP 连接;其他采集仍在 Tauri 宿主执行。
模块
| 模块 | 职责 |
|---|---|
lib.rs |
服务与安装助手启动分流、宿主调用入口、开发版与发布版隔离 |
install.rs |
SCM 状态查询、UAC 安装助手、受保护副本、安装/更新/启动/卸载与更新失败回退 |
server.rs |
SCM 入口、运行/停止状态、停止事件和退出清理 |
pipe.rs |
本机命名管道、有限长消息、连接身份校验、异步 I/O 与取消 |
protocol.rs |
版本化操作白名单、参数校验和 argus-core 调用 |
security.rs |
SID、管道 ACL、调用者令牌、文件及目录权限检查 |
network.rs |
服务侧的 ETW 租期、累计字节数、计数来源与代次 |
disk.rs |
服务侧的完整进程信息磁盘快照(只含 PID、创建时间和累计字节数);宿主侧的磁盘读取线程,只把最新快照交给采样线程换算速率 |
sensors/ |
服务侧 PawnIO 模块、固定 CPU 寄存器配置、温度与功耗换算;宿主异步读取及过期缓存清理 |
driver/ |
手动开启内核传感器时的驱动准备;固定安装包、下载与签名校验、独立 UAC 助手、SCM 身份与运行状态检查 |
background.rs |
宿主侧的规则线程和网络读取线程;只把最新结果交给采样线程 |
handles.rs |
句柄查询辅助进程的启动、Job 管理、超时续扫、部分结果回收与本地降级 |
taskmgr/ |
系统任务管理器替换、恢复状态机、独立启动器和界面就绪握手 |
安装与授权
服务名称为 ArgusPrivileged,开发版为 ArgusPrivilegedDev。宿主仅在设置页的明确操作后通过 ShellExecuteExW 的 runas 请求管理员授权;取消 UAC 时不退出界面,也不更改服务。助手在 Tauri 和单实例插件之前处理参数,避免被已运行的界面吞掉。
助手把自身程序复制到系统已知文件夹 Program Files 下的 Argus Privileged(开发版为 Argus Privileged Dev),以带引号的绝对路径注册自动启动的 LocalSystem 服务。目录、可执行文件和 owner.sid 的所有者为 Administrators,只有 Administrators 与 SYSTEM 可写;拒绝目录重解析点、不可信所有者和其他账户的写入权限。不依赖安装目录或免安装版所在的位置,复制操作不覆盖已有文件。
复制的来源是正在提权运行的 argus.exe 本身,受保护目录只能保证复制之后的副本不被篡改。安装版位于 Program Files,只有管理员能写入,来源可信;免安装版若放在标准用户可写的文件夹,本机的恶意程序可以在授权前替换它,被替换的程序会以管理员身份运行并被注册为 SYSTEM 服务。这是已知且接受的风险(免安装版不校验来源),用户文档提示使用安装版或把免安装版放在仅管理员可写的位置。
owner.sid 记录安装时界面进程的 Windows 账户,即使 UAC 输入了另一个管理员的凭据也不改变授权对象。这是对账户的持续授权:同一账户的其他中等及以上完整性级别的本机程序可以调用固定的管理操作,并不把可执行文件名当作身份凭证。这些操作本身作用于整台电脑:结束任意非关键进程、启停任意服务并修改启动类型(只保护 Argus 自己的服务)、断开任意 IPv4 TCP 连接、以 SYSTEM 身份查询文件占用,因此授权等同于让该账户获得部分管理员能力,界面的安装确认框会逐项列出。不能借此执行任意程序、写入任意文件、更改服务可执行路径或加载驱动。其他账户、低完整性进程,以及受限令牌和 AppContainer 令牌(同一账户的沙盒进程)都被拒绝;授权对象只能是用户账户的 SID(S-1-5-21-… 或 Microsoft Entra 的 S-1-12-1-…),不接受组与内置 SID。
更新先生成新副本,再停止服务并切换 SCM 路径;配置或启动失败时尝试恢复旧路径和原来的运行状态。停止时既等待 SCM 的停止状态,也等待服务进程退出,避免删除仍被映射的可执行文件。只有确认 SCM 不再引用某个副本才清理它。卸载检查同名服务确实指向 Argus 的专用目录,停止并删除服务,再删除已知文件;不递归删除目录。失败时保留仍被引用的副本。
服务与 GUI 分开安装:安装程序的安装后钩子检测到服务的注册表项时,以 argus.exe --argus-service-refresh 在安装程序已有的管理员权限下更新服务,授权账户沿用服务目录中 owner.sid 记录的账户,失败不影响安装。这一步与设置页的“更新服务”走同一个助手:任务管理器替换处于启用状态时,先把替换所用的启动器、恢复程序和界面副本更新到新版本,IFEO 的值不变,替换保持开启,再更新服务,见系统任务管理器替换。更新失败时,GUI 首次启动检测到服务版本不一致会询问是否更新服务。卸载 GUI 时,NSIS 卸载程序检测到服务的注册表项就询问是否一并卸载;同意后以 argus.exe --argus-service-remove 走与设置页相同的 UAC 助手(先恢复任务管理器),取消授权或失败时由用户选择继续卸载 GUI 或停止。升级安装中“先卸载再安装”的就地卸载不触发这一步。
IPC 边界
- 管道为
\\.\pipe\Argus.Privileged.v1,开发版为\\.\pipe\Argus.Privileged.Dev.v1,拒绝远程连接。第一个实例独占创建,下一监听实例在移交当前连接前创建,避免管道名称被抢占。 - ACL 只允许 SYSTEM、提升后的 Administrators 和授权账户访问。授权账户只获得读写数据、读属性与同步权限,不获得
FILE_CREATE_PIPE_INSTANCE。原因见 Microsoft 的命名管道访问权限说明。 - 客户端用 SCM 的服务 PID 核对管道服务端 PID,并持有进程同步句柄,避免 PID 复用。服务只给授权账户开放自身进程的查询和同步权限。客户端限制为身份识别级别,不能被管道服务端借去执行操作。
- 服务读取请求后通过管道模拟身份,检查实际令牌的账户 SID 和完整性级别,随后必定恢复自身身份;恢复失败则终止服务。不会信任请求里自报的账户或 PID。
- 协议为第 9 版,保留管道名称和第 1 版 Ping 信封,以便发现旧服务并提示更新。其他请求拒绝不匹配的版本、未知操作和未知字段。请求上限仍为 16 KiB,回复上限为 1 MiB,以容纳网络累计计数和句柄结果;最多同时处理 8 个请求。读取超时 5 秒,手动操作请求超时 30 秒,后台规则、网络、磁盘及传感器请求超时 2 秒,句柄请求超时 20 秒(连接等待另限 1 秒)。I/O 取消后等待完成,再释放缓冲区。
- 进程操作必须同时提供 PID 与正的启动时间,沿用
actions::open_instance的校验。结束进程不接受绕过系统关键进程检查的force参数;不允许客户端结束自身或服务。服务控制也不能停止、更改 Argus 特权服务自身。 - 进程操作、规则与服务控制等请求发出后丢失回复时不自动回退到本地重放,因为操作可能已经执行。协议版本不同的旧服务是例外:自第 1 版起,服务在执行前就拒绝版本不匹配的请求并关闭管道。这是协议的约束,此后各版本的服务都不得为了兼容旧宿主而执行其他版本的非 Ping 请求:自 0.2.0 起宿主依赖它在本地回退,服务执行后再关闭管道时,旧宿主(例如未更新的免安装版)会把请求在本地再执行一次。
pipe.rs的测试以真实子进程核对其他版本的优先级与结束请求不被执行。客户端读到管道关闭时,在仍持有服务进程句柄(PID 不会被复用)的情况下,以第 1 版信封向同一进程补发 Ping;对方报告其他版本,即可确定请求没有执行。此时宿主按本地权限执行,权限不足时返回ActionError::ServiceOutdated,界面的失败提示附“更新服务”按钮;这个错误只由宿主产生,服务不会发送,协议不变;规则在该服务进程退出前也改为本地执行。周期性的网络、磁盘和传感器读取不补发 Ping。未安装、未授权给当前账户或未运行时,宿主继续使用原有的标准权限操作。只读查询和按恢复记录幂等执行的任务管理器恢复具有单独的回退策略,见对应章节。
系统任务管理器替换
系统任务管理器替换只通过服务提供固定的恢复操作,开启走管理员维护助手;更新与卸载先恢复系统入口。状态机、独立程序与验证边界见系统任务管理器替换。
句柄查询
FindHandles 是只读请求,使用同一套账户与完整性级别校验。服务不可用或只读请求失败时,宿主可以安全地改用本地查询,同时保留服务错误;它不会为一次查询触发 UAC。已安装旧版服务的用户需在设置中更新服务。
每次扫描启动自身程序的辅助模式,在 Tauri、单实例插件与采样器初始化之前分流。子进程先等待一个启动字节,父进程将它放入关闭即终止的 Job 后才放行;Job 限制为一个进程、256 MiB 私有内存。子进程不创建窗口,工作目录固定为系统目录,避免自己制造待查目录的占用。
argus-core::handles 用 NtQuerySystemInformation(SystemExtendedHandleInformation) 读取句柄表,并根据自己持有的 NUL 句柄动态识别 File 类型索引。结构布局参考 phnt,读取前检查返回长度与条目数。源进程打开后核对创建时间,再用 DuplicateHandle 复制句柄;复核类型、排除管道和设备后,以 NtQueryObject 读取名称。只关闭复制件,不关闭来源句柄。
文件优先比较卷号与文件 ID,识别硬链接;不提供文件 ID 时退回名称匹配。目录按路径分隔符边界匹配自身及其后代,不将 folder-other 算作 folder。结果包含 PID、启动时间、进程名、首个匹配路径和句柄数,Restart Manager 的结果仍用于补充加载模块等引用。
复制、名称查询、文件信息和关闭句柄都在辅助进程中进行。它在查询前发出进度游标并逐项返回匹配;父进程通过 PeekNamedPipe 读取已有字节,避免另起一个可能永久阻塞的读线程。单个游标 500 ms 无进度就终止辅助进程,保留已发现项,并从游标之后重新启动,最多重启 4 次。准备阶段限 3 秒,控制器总预算 15 秒;辅助进程本身也有 12 秒预算。操作系统进程创建和收尾可能使实际耗时略超预算,但不会无限等待文件查询。超时或提前退出均返回部分结果。
每个父进程一次只运行一个句柄扫描;最多检查 200,000 个可查询/跳过的候选,返回 128 个进程,长名称与路径会截短显示。拒绝网络、设备、相对路径、数据流和路径穿越;目标只映射到 \Device\HarddiskVolume…,并以 OBJ_DONT_REPARSE 打开,避免 SYSTEM 被目录联接引到远程共享。链接目标可另行选择;界面仍保留 Restart Manager 的结果和限制说明。
扫描是随系统变化的快照,句柄存在也不等于禁止删除;保护进程、权限不足、关闭/复用中的句柄和仅剩映射的文件都可能使结果不完整。界面按 PID 与启动时间合并来源,结束进程时继续使用查询时的实例信息。
自动规则与网络采样
规则匹配在宿主的 RuleRunner 线程进行。采样线程只复制 PID、启动时间和名称;规则线程忙时,新快照替换尚未处理的旧快照。配置与统计的锁不会跨越 RPC,用户保存规则或查看结果无需等待慢服务。保存新配置后,已经发出的操作可能完成,但旧批次的剩余操作会跳过,旧结果也不会写回新配置的统计。
每个匹配使用一个 ApplyRule 请求,先设置效率模式,再设置显式优先级,两步都校验进程实例。一次传输失败后,本批次其余目标记为失败,不继续等待相同服务的超时;不自动重放不确定的操作。下一次规则保存或服务 PID/可用状态变化时重新检查已有进程,并清空上一轮统计。
服务首次收到 NetworkSnapshot 才启动独立 ETW 会话:发布版 Argus Service Network,开发版 Argus Service Network (dev)。服务启动时清理这个名字的残留会话,15 秒没有读取请求就关闭会话,退出时等待消费者线程结束;不会停止 GUI 的本地会话。ETW 启动失败或消费者退出时返回不可用,失败后的启动尝试间隔至少 30 秒。
网络回复包括来源 epoch、来源时钟(微秒)、最多 8192 个 PID 的累计收发字节与计数代次。只有服务的网络线程从 tracker 取走原始增量;不同客户端读取同一份累计计数,互不消耗数据。30 秒不活跃的条目回收,再出现时获得新代次;超过容量或计数溢出时关闭该来源并返回不可用。
宿主的 NetworkClient 只保留一个待执行读取和一个完成结果。本地 ETW 可用时优先使用本地结果;否则采样线程读取后台缓存、按来源时钟计算速率,再交给界面和历史。来源变化或观察到 PID 启动时间变化时重新建立基线,重复读取同一快照不重复计算字节。后台通信通常使网络数据落后一个刷新周期;缓存超过两个采样间隔(取本次与上一次采样中较长的节拍)加 2 秒即失效。服务断线返回空结果后隐藏网络列,恢复后的第一份数据只作基线,避免虚假峰值。
每进程磁盘读写
DiskSnapshot {} 无参数,回复仅包含来源单调时钟及最多 8192 个进程的 PID、创建时间、累计读取和写入字节数,不传输完整进程信息中的路径、命令行或账户字段;查询失败时回复 DiskFailed,只含失败类别与 NTSTATUS。后备来源为 SystemFullProcessInformation 的磁盘计数扩展,与通用 I/O 分开;不创建跟踪会话,不加载驱动,不修改系统计数开关。
普通进程查询已提供有效磁盘计数时,宿主直接使用该结果,不发起服务请求。其余情况下,DiskClient 在独立线程读取;管理员权限下查询完整信息,标准权限下通过已授权服务查询。采样线程只读取缓存并计算速率。缓存超过两个采样间隔(取本次与上一次采样中较长的节拍,窗口回到前台的第一拍仍使用上一个后台拍取得的快照)加 2 秒、服务不可用、计数器回退或进程身份变化时不复用旧值;首次观察和长间隔(超过 35 秒,最长的后台间隔 30 秒加上采样时刻的抖动)后重新建立基线。上一份快照中没有、创建时间又晚于该快照取时的进程以零为基线(来源时钟为 GetTickCount64,按本机墙上时间换算),因此新进程在下一份快照即有速率,与普通进程查询的做法一致。表头与应用分组的合计只累加有读数的进程,全部缺失时才显示为缺失,缺失值不作为零值计算。
读取失败时,宿主日志按原因各记一次(每次运行最多 8 条),只含类别与错误码:本地或服务的查询失败时为 NTSTATUS、返回长度超出缓冲区、记录未按 8 字节对齐、记录越界、进程 ID 超出范围或进程过多,并注明是哪一方的查询;此外还有服务回复没有快照、回复类型不符,或请求失败的 Windows 错误码。服务本身不写日志,查询失败的类别与 NTSTATUS 随回复带回,宿主记作服务查询失败的原因;回复既无快照也无原因(服务没有可用的查询)时记为服务没有返回快照。没有服务、也未以管理员身份运行时不算失败,不记日志。解析在服务内进行,只做边界检查后的定长读取,不会因异常数据崩溃。
CPU 内核传感器
协议版本为 9,旧版服务需要更新。SensorSnapshot {} 无参数;多余字段一律拒绝。回复包含固定类型的温度、电压、电流、功耗、频率、限制、每核读数、风扇转速及主板电源轨和温度,不能指定模块路径、寄存器地址、任意 IOCTL 或写操作。沿用原有账户及令牌校验。
模块固定为 PawnIO.Modules 0.2.11 的 IntelMSR、AMDFamily10、AMDFamily17、RyzenSMU 与 LpcIO,编译时嵌入签名二进制。vendor/pawnio 包含固定 SHA-256、上游版本、完整对应源码及 LGPL 许可证;许可生成脚本同时检查文件摘要。CPU 调用仅包含寄存器读取及固定的 SMU 表定位、刷新和读取;SMN 与 SMU 的访问通过 Global\Access_PCI 与其他监控工具协调,等待不超过 20 ms。模块本身具有的其他入口不向 IPC 开放。
设置默认关闭。服务首次收到请求后打开已经安装的 PawnIO 设备,采集线程不安装或启动驱动。模块加载失败重试间隔为 30 秒;检测到驱动出现或版本改变后立即重试,15 秒没有请求即释放模块。Intel 按处理器组设置并恢复采集线程的亲和性,单次最多读取 256 个逻辑处理器,循环预算为 500 ms。AMD 仅在单插槽配置读取节点 0;未知 CCD / 电压布局不猜测。服务读取等待有 800 ms 上限,GUI 读取在独立线程进行,采样线程不等待 RPC。过期缓存被丢弃。
手动开启开关时,宿主先确认特权服务运行且已授权,再检查 PawnIO;需要安装或启动时,以 --argus-sensor-driver-manage 启动独立 UAC 助手。该模式不接受额外参数、安装路径、下载地址或自定义服务名。固定包为 PawnIO 2.2.0,地址、大小与 SHA-256 存于 vendor/pawnio/installer.json。下载仅允许 HTTPS 及固定 GitHub 发布域名,大小与完整摘要匹配后再通过 WinVerifyTrust 验证签名和证书吊销状态。
安装程序写入 Program Files 下只允许管理员与 SYSTEM 修改的暂存目录,执行前保持文件只读句柄,阻止替换;执行参数固定为 -install -silent。不同 Argus 实例共用安装锁,等待安装程序退出后仅清理本次创建的安装文件与目录,不中途结束驱动安装。启动只接受 PawnIO 内核服务,核对受保护的官方驱动位置及最低版本,不覆盖同名异类服务。兼容版本还需核对该服务对应的 PnP 设备:服务处于运行状态而设备缺失、未启动或存在故障时,不能视为就绪;设备被禁用时提示在设备管理器启用后重试。设备枚举带 CM_GETIDLIST_DONOTGENERATE,保证查询不会创建旧式设备节点。启动完成后用固定的版本 IOCTL 确认设备可访问。取消、失败或重启要求不保存开启状态。关闭开关与卸载 Argus 都不停止或卸载其他监控工具也可能使用的 PawnIO。具体传感器语义见硬件数据。
官方 2.0.1 安装包中的签名驱动没有 VERSIONINFO,不能以缺少版本资源认定为异类。driver/legacy.rs 仅按文件大小与完整 SHA-256 识别已知的 x64、ARM64 官方文件,缺少版本资源的其他文件仍拒绝。迁移还要求驱动位于默认 Program Files\PawnIO,两个注册表视图中存在的卸载信息均指向此目录、版本属于 2.0,且注册表键的所有者可信。
Windows 默认的卸载项权限可包含 Terminal Server User 写权限。执行旧版卸载程序前,助手临时改名原记录,在原位置创建仅管理员与 SYSTEM 可写的副本,复制并重新校验全部值后移除原记录。更换键对象使已有写句柄无法修改新副本;复制失败时尝试恢复原记录,名称被占用时保留备份并停止。非预期子项、注册表链接或在原位置抢先创建的其他键会使迁移停止,不递归删除未知注册表内容。
官方 2.2.0 安装器仅支持从 2.1.0 直接升级,因此 2.0 迁移先准备并验证新旧两个安装包,再重新检查现有安装,使用固定的 2.0.1 安装器执行 -uninstall -silent。不执行注册表中的卸载命令。旧版安装器的地址、大小与摘要固定在 legacy-installer.json,同样验证 Windows 签名;子进程的 TEMP/TMP 指向受保护暂存目录,避免旧版卸载程序跨卷移动文件失败。旧版返回的 NTSTATUS 转换为 Win32 错误;只有卸载成功且旧服务已不存在才安装 2.2.0,服务仍在使用或待删除时停止并提示重启,不结束其他监控程序、不强制删除服务。
开启失败时,宿主向 argus.log 附加一次只读诊断快照:当前权限、特权服务状态与授权结果、PawnIO 服务类型与状态、启动类型、驱动路径所属类别、权限检查所在层级、数值版本、版本资源大小与语言/代码页、原始文件名是否符合预期、是否匹配官方旧版摘要、缺少版本资源的驱动文件大小与 SHA-256、PnP 设备数量与启动数量、问题代码和待重启标记,以及校验失败阶段和错误码。缺少版本资源时只对不超过 1 MiB 的文件计算摘要,超出上限仍拒绝;摘要一致也须通过路径和卸载信息校验。设备标记为待重启时,即使服务仍运行也要求重启后再启用。快照不加载或启动驱动;记录驱动实际路径、被拒的父目录或文件、失败 API、所有者类别、ACE 索引、类型、标志及权限掩码,不记录设备实例标识、账户名或资源中的自由文本。路径限长并转义。操作当时的失败与随后查询到的当前状态分别记录,避免重查结果掩盖原始原因。
驱动路径检查覆盖文件及各级父目录。即使驱动文件本身只读,父目录允许普通用户修改时仍会拒绝启用。错误信息分别指出驱动存储目录可写、驱动路径不安全或安装暂存目录不安全,便于管理员定位权限问题;检查过程只读。
SMU 只按内嵌精确版本布局解析,字段有限值和单位范围校验;总线短暂繁忙时最多复用 3 秒内的读数;完整表与物理地址不进入 IPC。布局来源及许可证位于 vendor/cpu-layouts。每核能量按物理核心计数,APERF/MPERF 按逻辑处理器采样后合并;计数重置、单位变化和过长间隔重新建立基线,SMT 共享的功耗不重复相加。
Nuvoton 传感器访问使用 Global\Access_ISABUS.HTP.Method,等待不超过 20 ms。芯片 ID、BAR、厂商 ID 和固定寄存器均校验后才读取;只暂时调整配置选择器、银行选择器和监测 I/O 门控,随后恢复原值。接口不提供 PWM、风扇曲线或任意端口写入。风扇按硬件通道编号输出。主板电压和温度仅在主板厂商、型号及芯片 ID 匹配时输出;电压使用对应分压,温度先核对多路复用源,再读取固定备用寄存器。不会主动启用电池监测。
验证
cargo test -p argus-service 使用临时命名管道和自己创建的子进程验证通信、身份与参数校验、进程实例保护、规则执行顺序、大回复、断线和取消;模拟慢执行器与累计计数验证并发、规则重查、PID 复用和过期缓存。测试不会安装服务、启动真实 ETW 会话或弹出 UAC。
cargo run -p argus-service --example sampling_bridge --release 测量真实采样上新增的队列提交与缓存读取开销,不配置规则或安装服务。
cargo run -p argus-service --example handle_lockers --release -- <绝对路径> 通过同样的隔离流程打印句柄查询结果,不安装服务或请求提权。回归测试覆盖辅助进程终止、超时续扫、结果保留和按实例合并。
诊断传递
服务在内存中保存最近 32 条失败记录,每条最多 4096 个字符;Ping 回复通过可选字段携带记录,宿主按服务进程身份和序号去重后写入 argus.log。旧版服务可不返回该字段,旧版宿主忽略该字段,不改变协议版本。网络或传感器读取失败时,后台客户端每分钟至多额外查询一次诊断,正常采样不增加请求;驱动打开、模块加载和 ETW 失败保留原始错误码,模块的可选寄存器读取仅记首次失败。
提权助手在 GUI 日志初始化前运行,失败详情写入 %ProgramFiles%\Argus Sensor Setup 下受管理员权限保护的报告。宿主以启动时取得的进程句柄核对 PID、创建时间和退出码,并检查目录与文件权限后读取;报告不可用时同时保留助手退出码和报告读取错误。每份报告上限 256 KiB,最多保留最近 16 份。设置页和主程序仍只需提供常规诊断日志。报告写入失败不改变原操作结果,也不会放宽驱动或目录的安全检查。