Windows系统之CFG(ControlFlowGuard)"

admin 2026-10-22 13:09:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

Windows系统之CFG (Control Flow Guard)

原创

ITPAPA
ITPAPA

ITPAPA

2026年10月19日 18:06
江苏

去阅读

CFG 控制流保护

CFG 由编译器、链接器和 Windows 运行时共同实现。在受插桩的间接调用前检查目标是否合法,以降低函数指针被破坏后跳向任意地址的风险。

主要描述 Windows 用户态原生 C C++ 应用的 CFG。内核 CFG 的实现与保护环境不同;本文不将用户态设置直接套用到驱动。

逻辑架构与请求流

图 1  配置进入执行组件,请求经过检查后产生结果与后续处理。图示为职责关系,非固定进程或调用栈。

保护边界

CFG 验证合法目标集合,不保证目标函数符合业务授权或精确的调用类型。它不等同于影子堆栈,也不能通过给旧 EXE 勾选策略就补齐所有缺失的调用点插桩。

02 运行逻辑与关键参数

1  编译器用 /guard:cf 为相关间接调用生成检查;链接器汇总可合法调用的目标,写入 Load Config 目录和 Guard 标志。不能仅验证主 EXE。[1]

2  系统加载 EXE DLL 时,结合模块元数据维护合法目标状态。动态代码需遵循受支持 API 与目标有效性约束,不应篡改保护数据结构。

3  调用点先交给 Guard 检查或分派路径,判断候选目标是否在允许集合;通过后才跳转。具体内部位图布局不作为跨版本接口。

4  非法目标可导致快速失败。结合异常子码、间接调用指令、寄存器中目标地址及模块列表,定位被破坏的指针或不兼容的跳板。

关键参数与判读

参数或对象 位置或类型 功能与判读
/guard:cf 编译及链接 开启插桩与元数据生成
GuardFlags PE Load Config 记录是否插桩及目标信息
GuardCFFunctionTable 与 Count PE 元数据 合法函数目标表及数量
StrictMode 进程 CFG 策略 要求加载 DLL 支持 CFG;只可收紧
SetProcessValidCallTargets Win32 API 按约束登记动态代码合法目标

参数表区分编译属性、配置意图与运行状态。字段存在不等于当前版本可写;SDK 结构中保留位不得自行赋值。

03 底层对象与实现机制

插桩位置决定实际覆盖

CFG 的调用点检查由编译生成,合法目标信息由链接整合。打开进程策略不会把已经存在的全部机器指令重新编译,因此需逐模块和逐调用路径检查。直接调用、间接调用、尾调用及优化后的路径在反汇编中可能不同,不能靠搜索一条固定指令判断全部覆盖。

目标元数据与运行时状态

Load Config 的 GuardCFFunctionTable、Count、GuardFlags 和检查或分派入口描述映像支持信息。磁盘元数据供加载流程使用,运行态有效目标状态还受动态代码 API 和映像装载卸载影响。它不应被当成可写白名单文件;任何依赖内部位图偏移的解析工具都需版本约束。[D1]

动态代码需要完整协作

SetProcessValidCallTargets 接收目标区域及 CFG\_CALL\_TARGET\_INFO 列表,偏移按 API 要求排序并满足对齐等条件,目标内存也需满足调用条件。[D2] 它只登记合法间接目标,不负责替调用者编译插桩、维护回调生命周期或允许 ACG 已拒绝的执行内存。

对象和证据对照

对象或数据结构 技术职责 可观测与判读
GuardCFFunctionTable 与 Count 映像合法函数目标描述 检查结构长度 架构与表界限
GuardCFCheckFunctionPointer 检查入口 不固定内部函数地址
GuardCFDispatchFunctionPointer 检查加分派入口 结合真实反汇编分析
CFG\_CALL\_TARGET\_INFO Offset 与 Flags 偏移针对指定区域而非随意绝对地址
FAST\_FAIL 子码 异常细分原因 与调用指令及目标地址同时核对

04 执行时序与检查边界

沿一次请求依次观察输入、状态、检查位置、输出和失败去向。以下为职责级时序,不把图中角色强行对应为固定函数或独立进程。

图 2  请求和结果的时序示意。自指箭头表示角色内部处理,失败去向不必是可恢复的业务返回。

最后一步仅表示失败终止控制流,实际快速失败不会像普通函数错误那样恢复调用方继续执行。

有效目标集合是地址集合,不证明调用方具有业务权限,也不表达所有精确函数类型约束;这正是理解 XFG 增强价值的起点。

观测与证明的区别

静态属性证明构建或配置意图,运行查询证明当前对象状态,事件和转储证明某次路径发生过什么。至少让两类证据相互支持;当接口失败、符号不匹配或负载未覆盖时,明确标记未知。

05 组件文件与基础诊断

文件或对象 职责 排障关注点
目标 EXE DLL 检查每个模块的 Guard 信息 静态覆盖证据
%SystemRoot%\System32\
tdll.dll 运行时 Guard 路径与异常 符号名称随构建变化
进程合法目标状态 运行态内存数据 不是可编辑配置文件
PDB 与构建日志 还原调用点和函数原型 需与故障二进制对应

以下以提升权限的 Windows PowerShell 5.1 为常见查询环境。dumpbin 和 signtool 需 Visual Studio 或 WDK;WinDbg 命令在调试会话执行。预先准备可写的 C 盘 Lab 测试目录,示例路径、进程名与域账号按现场替换,导出文件避免覆盖原证据。

Get-ProcessMitigation -System
Get-ProcessMitigation -Name demo.exe
Get-ProcessMitigation -Name demo.exe -RunningProcesses
dumpbin /headers C:\Lab\demo.exe
dumpbin /loadconfig C:\Lab\demo.exe

WinDbg

!analyze -v
.exr -1
.ecxr
kv

事件与错误解释

0xC0000409 是多种快速失败共享的异常码,并不只表示 CFG 或栈溢出。必须读 FAST\_FAIL 子码与调用链;不要仅按异常码建立“CFG 误报”规则。Application 1000/1001 用于时间关联。

诊断资料可能包含账号、文件路径与业务数据。保存原始证据,分析使用副本;共享前只保留定位问题所需信息。

06 实操实验与结果解释

证明一次合法回调确实经过 CFG 插桩

将以下完整无害样例保存为callback\_demo.cpp。使用运行时选择与noinline保留可检查的间接调用;最终仍需反汇编确认优化结果。命令在Visual Studio x64 Native Tools的CMD执行。

实验约定  先准备C盘Lab目录与测试数据。C++样例使用匹配架构的Visual Studio和Windows SDK;源码与命令以代码注释区分。样例需在目标Windows环境编译或执行后填写实测记录。

include

using Fn=int(\)(int);
\\_declspec(noinline) int Add(int x){return x+1;}
\
\declspec(noinline) int Sub(int x){return x-1;}
\
\_declspec(noinline) int Invoke(Fn volatile\
f,int x){
  return (\f)(x);
}
int main(int argc,char\
\*) {
  Fn f=argc>1 ? Add : Sub;
  printf(\"%d\
\",Invoke(&f,3)); return 0;
}
// CMD 编译和静态检查
// cl /W4 /O2 /guard:cf /Zi callback\_demo.cpp /link /guard:cf /DEBUG
// dumpbin /loadconfig callback\_demo.exe
// dumpbin /disasm callback\_demo.exe > C:\Lab\callback-disasm.txt
// WinDbg 根据匹配PDB定位 Invoke 并查看最终调用指令

预期结果与证据限制

1  必须同时看到编译链接参数、Guard 元数据和真实调用路径。若优化使回调变为直接调用,换一个运行时可变场景。

2  合法回调正常执行是兼容证据;非法样例仅在专用实验程序使用。生产不把错误指针写入进程来验证。

3  SetProcessValidCallTargets 失败应记录 GetLastError、区域权限、区域长度、Offset 和 Flags,不能直接将失败解释为恶意调用。

07 验证流程与故障分流

验证 1

构建:编译一个有普通函数指针回调的程序,保存 /guard:cf 编译与链接参数,检查 loadconfig。预期存在有效 Guard 元数据。

验证 2

合法路径:调用类型匹配且目标有效的回调;覆盖所有插件入口。预期正常执行且没有 Guard 失败。

验证 3

非法路径:仅在无害实验程序中将回调指向非目标地址,保存故障转储。预期在检查阶段失败;用子码与反汇编证明路径。

常见问题判断表

现象 优先检查 处理方向
快速失败 核对 FAST\_FAIL 子码 再定位目标地址
插件装载失败 查 StrictMode 与模块属性 更新不支持 CFG 的 DLL
显示开启但覆盖不足 检查调用点插桩 重编译依赖链

排障证据与验收记录

先记录 Windows 版本与构建、处理器架构、相关软件和驱动版本、策略来源及最近变更。随后对齐时间、进程或设备标识、文件哈希、错误码和日志正文;缺少关联时保留为待验证假设。

验收至少保留配置结果、重启或重启应用后的运行状态、合法工作负载结果、无害负向测试结果以及恢复验证。远程环境缺少物理条件的项目应列为未测,不以命令执行成功代替功能通过。

08 互操作版本与性能分析

关联对象 机制关系 现场决策
StrictMode 要求 DLL 支持 CFG 装载失败与调用目标失败分开
ACG 可能禁止生成动态执行页 目标登记不解除动态代码限制
CET 约束函数返回路径 CFG通过不代表返回地址未被破坏
混合模块 缺插桩模块降低可见覆盖 逐模块建立清单和更新计划

版本和平台

性能比较应覆盖间接调用密集型路径,记录同编译优化、同输入和相同模块组合,避免用启动时间代替稳态成本。

覆盖和证据

JIT 或探针需同时满足内存权限、有效目标、展开和生命周期要求,不能只增加一个目标登记调用。

性能与恢复

StrictMode 运行中收紧后不能随意撤销;恢复兼容需重新启动进程并应用正确初始策略。

量化验证方法

以同设备、同版本、同输入和同运行模式做前后对照,记录样本数、预热、吞吐、P95或P99延迟、CPU与失败率。计时比较前先确认目标保护实际运行,避免把功能未启动或业务降级误认为优化成功。涉及启动、登录或外设的功能还应记录状态转换和时间线。

09 典型排障案例与最佳实践

以下为按机制设计的演练案例,用于说明证据链与处理方法,不代表公开披露的真实事故。

案例 1  注入式性能插件导致进程退出

证据  转储为 Guard 相关快速失败,回调目标在插件生成的跳板。

根因判断  插件未按 CFG 目标规则实现动态代码,或跳板已释放。

处理  升级插件,使用受支持的动态代码与目标登记机制;检查生命周期。

复测  保持 CFG 开启,循环加载卸载插件并执行压力测试。

案例 2  开启严格模式后 DLL 加载失败

证据  DLL 无 Guard 标志,失败发生在装载阶段而非回调处。

根因判断  严格模式拒绝缺少 CFG 的模块。

处理  推动供应商重编译,区分 StrictMode 与普通 CFG 兼容行为。

复测  验证全部依赖具备 CFG,并覆盖延迟加载模块。

部署与恢复原则

将 CFG 设为原生模块构建基线,在 CI 中保存 PE 检查结果。StrictMode 应在完整插件清单验证后单独推进。故障修复以消除错误目标或兼容实现为目标,不批量关闭全系统 CFG。

处理顺序为先收集证据,再最小范围复现,优先修复应用、驱动或策略缺口,最后复测保护与业务。确需临时兼容配置时,记录责任人、作用对象、到期时间和恢复条件,验证后明确撤销。

10 深入排障演练与根因验证

本章为机制推导的完整演练,不是实际事故复盘。步骤中的预期与假设必须由现场证据验证,不能直接作为客户环境结论。

1  复现条件:插件可加载,但连续启用关闭探针后偶发快速失败。

2  排除伪证:0xC0000409 本身不是结论,先核对具体 FAST\_FAIL 原因与调用点。

3  目标证据:寄存器中的候选地址落在已释放动态代码区,而不是已登记且仍存活的入口。

4  根因链:探针卸载释放跳板后,其他线程仍保留函数指针。登记曾成功也不能保证未来目标仍存在。

5  修复:统一回调替换和撤销同步,等待所有使用者退出,按 API 生命周期移除或释放目标。

6  复测:保留 CFG,循环安装卸载并增加并发采样,覆盖异常取消路径。

7  验收:不再出现悬空目标;符号、二进制与调试输出一致,不能以关闭 CFG 后不崩溃作为修复证明。

提交研发或供应商的最小资料集

提供稳定复现步骤、系统构建、架构、对象版本与哈希、有效策略及运行状态、原始错误和相同时间范围日志。涉及崩溃补充匹配转储与符号,涉及驱动补充INF CAT SYS和设备实例,涉及认证保留协议与账号类型。报告分开写观察事实、根因假设、修复动作与复测结果。

11 配置部署与恢复

1  开发侧同时启用编译和链接 /guard:cf,并检查每个模块 Load Config。管理侧在漏洞利用防护检查目标程序 CFG 配置。

2  严格模式单独测试,确认全部静态、延迟和动态加载依赖兼容;不要修改 Guard 目标位图来维持旧式跳板。

现场实施顺序

保留基线

导出有效策略及相关软件和驱动清单;标记系统构建、时间和变更单。先确认本地与集中管理是否会互相覆盖。

小范围验证

选择硬件和应用组合具有代表性的测试设备,按验证章节分别检查配置、运行和行为。只使用无害样本及测试数据。

分组推广

对审计发现逐项处理,再按机型、应用组合及业务窗口逐步扩大。监控启动失败、崩溃、认证失败和业务延迟等实际指标。

恢复与复测

通过原管理来源恢复明确的旧策略;涉及锁定或签名策略时遵循专用流程。重启或重启应用后重新验收,不以设置值变化作为恢复成功。

主要参考资料

以微软公开文档为主要依据,引用编号对应本册条目。技术推导、诊断顺序和演练方案属于运维设计;具体默认值和支持范围以目标构建及当前厂商资料为准。

[1] Control Flow Guard

https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard

[2] CFG policy structure

https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-process\_mitigation\_control\_flow\_guard\_policy

[3] Load Config structure

https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image\_load\_config\_directory64

12 技术参考与术语速查

正文中的数字引用保留原版基础资料;D编号对应下列深化资料。架构推导、实验设计和诊断方法用于解释机制,不构成未公开内部接口的稳定承诺。

[D1] Load Config structure

https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image\_load\_config\_directory64

[D2] SetProcessValidCallTargets

https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-setprocessvalidcalltargets

本册关键对象速查

术语或对象 使用时先确认
GuardCFFunctionTable 与 Count 检查结构长度 架构与表界限
GuardCFCheckFunctionPointer 不固定内部函数地址
GuardCFDispatchFunctionPointer 结合真实反汇编分析
CFG\_CALL\_TARGET\_INFO 偏移针对指定区域而非随意绝对地址
FAST\_FAIL 子码 与调用指令及目标地址同时核对

独立使用检查

先检查适用架构和版本,再确认工具与权限。配置意图、实际运行、具体触发事件分开记录。源文件、符号和策略ID按版本固定;未知字段或失败查询保留原始错误。完成修复后复测目标功能与关联业务,并撤销临时例外。

Windows系统之CFG(ControlFlowGuard)" 网络安全文章

Windows系统之CFG(ControlFlowGuard)"

本文系统讲解Windows用户态CFG控制流保护的实现机制与运维实践,涵盖编译链接插桩、合法目标表生成、运行检查流程、关键参数判读、底层对象、执行时序、组件诊断、实操实验、验证流程、排障案例及配置部署恢复。强调CFG不等同影子堆栈,需逐模块检查插桩覆盖,StrictMode需单独验证,故障处理应收集证据、最小复现、优先修复缺口,不批量关闭全系统CFG。
Telegram向美国提供用户数据,三项信息如何成为情报线索" 网络安全文章

Telegram向美国提供用户数据,三项信息如何成为情报线索"

文章披露Telegram在杜罗夫被捕后调整隐私政策,向美国执法部门提供用户账户识别码、电话号码和IP地址记录。2024年美方数据请求履行量激增至900项,涉及2253人。这些基础资料虽不含聊天内容,但可与其他数据交叉比对,用于身份识别、关系追踪和情报分析,警示加密通信不等于完全隐私保护。
Windows系统之DEP(DataExecutionPrevention)" 网络安全文章

Windows系统之DEP(DataExecutionPrevention)"

本文详细介绍了Windows系统的数据执行保护(DEP)机制,包括其运行逻辑、关键参数、实现机制、执行时序、检查边界、组件文件与基础诊断、验证流程与故障分流、互操作版本与性能分析、典型排障案例与最佳实践、深入排障演练与根因验证、配置部署与恢复等方面,旨在帮助读者全面了解和掌握DEP技术。
AUTOSARComM模块详解:PNC与Channel双状态机、网络管理及映射关系" 网络安全文章

AUTOSARComM模块详解:PNC与Channel双状态机、网络管理及映射关系"

本文详解AUTOSARComM模块的PNC与Channel双状态机、网络管理及映射关系。核心要点包括:PNC状态机含4种状态,Channel状态机含3种主状态;通信禁止功能分禁止唤醒总线与限制通信两类;NmVariant分FULL、PASSIVE、LIGHT、NONE四种类型;User、PNC与Channel呈交叉树状映射关系。文章提供可操作的配置建议,对汽车网络通信管理设计具有参考价值。
评论:0   参与:  0