跳转到主内容
极星编程网:以代码为星,赴技术山海!

c++怎么在Windows下监控特定文件夹内权限变更的系统日志【底层】

需先启用“审核对象访问”策略并为目标文件夹配置SACL,再以管理员权限通过EvtSubscribe订阅Security日志中Event ID 4670事件,解析其SDDL字段比对ACL差异来识别具体权限变更。 用 Windows API 监控文件夹权限变更需要开启审计策略 Windows 不会默认记录文件或文件夹的权限修改事件(比如
SetAcl
、
SetSecurityInfo
调用),必须提前配置本地或域策略,否则你在事件日志里根本看不到相关条目。 关键操作是启用「对象访问」审计,并为具体文件夹设置 SACL(系统访问控制列表)——不是 DACL。很多人卡在这一步,以为调用
ReadEventLog
就能直接捞到日志,结果返回空。 打开「组策略编辑器」(
gpedit.msc
) → 计算机配置 → Windows 设置 → 安全设置 → 本地策略 → 审核策略 → 启用「审核对象访问」(成功/失败都勾) 右键目标文件夹 → 属性 → 安全 → 高级 → 审计 → 添加新条目:选主体为「Everyone」,勾选「更改权限」和「取得所有权」这两项(其他如「创建文件/文件夹」按需) 注意:SACL 修改后,后续所有对该文件夹的权限变更才会被记录;历史操作不会补录 从 Security 日志读取权限变更事件得过滤 Event ID 4670 权限变更在 Windows 事件日志中固定对应
Event ID 4670
(“权限已更改”),它属于
Security
日志,不是
Application
或
System
。直接用
OpenEventLog
打开
"Security"
即可,但要注意权限问题。 普通用户进程默认无权读取
Security
日志,必须以管理员身份运行,或在 manifest 中声明
requireAdministrator
。否则
OpenEventLog
返回
NULL
,
GetLastError()
是
ERROR_ACCESS_DENIED
。 立即学习 “ C++免费学习笔记(深入) ”;
Event ID 4670
的
SubjectUserName
字段标识谁改的权限,
ObjectName
是被修改的路径,
PrivilegeList
通常为空,重点看
AccessList
(含旧/新 DACL 的 SDDL 表示) 日志字段是 XML 格式,推荐用
EvtQuery
+
EvtRender
(Windows Vista+)替代老旧的
ReadEventLog
,后者不支持结构化数据提取 不要轮询整个日志:用
QUERY_EVENT_LOG_START_AT
配合最后读到的
RecordNumber
增量读取,否则性能极差 用
EvtSubscribe
实现实时响应比轮询更可靠 轮询
Security
日志有延迟且易漏事件(尤其高并发时),Windows 提供基于回调的订阅机制
EvtSubscribe
,能真正实时捕获新到达的
4670
事件。 C知道 CSDN推出的一款AI技术问答工具 下载 但必须传入正确的查询字符串,且处理线程需保持活跃——回调函数在你指定的线程上下文中执行,如果主线程退出,订阅自动失效。 查询字符串示例:
*[System[(EventID=4670) and TimeCreated[timediff(@SystemTime) <= 300000]]]
(5 分钟内) 回调函数里用
EvtRender
解析
EventRecordId
、
TimeCreated
、
ObjectName
等字段,避免手动解析 XML 注意内存泄漏:每次
EvtRender
返回的缓冲区需用
LocalFree
释放;
EvtClose
必须成对调用 若程序长期运行,建议每小时重建一次订阅(
EvtClose
后重
EvtSubscribe
),避免句柄泄漏或事件积压 权限变更日志不包含完整 ACL 差分,只能靠 SDDL 对比
Event ID 4670
日志只记录变更前后的完整 SDDL 字符串(在
AccessList
字段),不提供「哪些 ACE 被增删」这种差分信息。想定位具体哪条权限被改,必须自己解析两段 SDDL 并逐条比对。 别指望
ConvertStringSecurityDescriptorToSecurityDescriptor
后直接 memcmp——SDDL 顺序不保证一致,且可能含冗余空格或注释。稳妥做法是用
GetSecurityDescriptorDacl
提取 DACL,再遍历
ACE
结构体比较
Trustee
、
AccessMask
、
AceType
。 SDDL 解析失败常见于日志字段被截断(长路径或复杂权限),此时
EvtRender
返回
ERROR_INSUFFICIENT_BUFFER
,需重试并增大缓冲区 某些第三方工具(如 PowerShell 的
Get-Acl
)输出的 SDDL 和事件日志中的格式略有差异,不能直接字符串比对 如果文件夹启用了继承,日志里的 SDDL 会包含
I
标志,但不会告诉你继承源是谁——这得额外查父目录的 DACL 真正难的不是监听,是把一段 SDDL 变成人类可读的「用户 A 被移除了写入权限」。这部分逻辑没有现成 API,得自己写解析和比对逻辑,而且要覆盖所有 ACE 类型(ALLOW、DENY、OBJECT_ALLOW 等)。

相关文章