先看结论与判断条件
- 工程判断:若加固前后的 PendingIntent 可变性标志出现差异,运行行为和风险边界可能随之变化;是否由加固引起须通过产物差异与运行日志确认。
- 请求码与 Intent 字段共同决定 PendingIntent 身份,加固后常量值变化会导致跨组件匹配失败。
- 工程判断:可把 setComponent 或 setPackage 是否保留列为加固后检查项;这是面向显式目标的防御性建议,不能替代对实际 Intent 路由的设备验证。
- 进程重建场景下,PendingIntent 必须正确恢复参数并遵守 FLAG_ONE_SHOT 等生命周期标志。
- 工程判断:TaskStackBuilder 与父 Activity 声明发生差异时,返回导航可能偏离基线;是否出现断裂应由冷启动和进程重建用例确认。
- 工程判断:静态扫描创建点并配合设备端 instrumented test,可作为 PendingIntent 回归检查方案;它只能发现已覆盖路径上的差异,不能证明所有缺陷都已排除。
加固为什么会导致 PendingIntent 行为变化
加固过程会修改应用清单、字节码或资源,这有可能改变 PendingIntent 创建时依赖的常量、类引用和清单属性。例如,混淆器可能重命名组件类,导致原本显式声明的目标 Activity 类名在 createPendingIntent 中失效,从而退化为隐式 Intent,放大重定向风险。同时,加固工具为了适配不同 Android 版本,可能自动插入或剥离 flags,常常忽略 Android 12 引入的 FLAG_IMMUTABLE 要求,从而在运行时抛出 IllegalArgumentException。
PendingIntent 作为系统进程持有的令牌,其内部字段(action、data、category、extras)若在加固后与接收方预期不一致,就会造成匹配失败或返回非预期的目标页。此外,加固对资源或者 AndroidManifest.xml 的压缩重新签名,可能清除 parentActivityName 或元数据,直接影响通知点击后的返回栈构建。这些变更通常在静态分析中无法完全暴露,必须通过回归测试验证。
通知跳转和 PendingIntent 的回归要围绕可变性标志、请求码常量、组件显式性、进程重建行为以及任务返回栈五个维度展开。跟踪这些维度可以系统定位加固产生的偏差,避免线上通知无法跳转、跳转到错误页面或者被恶意程序重定向。后续章节将逐一说明每个维度的回归方案与决策依据。
| 变更来源 | 可能受影响的属性 | 引发的回归风险 | 验证时机 |
|---|---|---|---|
| 代码混淆 | 目标组件类名、请求码常量值 | 组件解析失败、身份不匹配 | 加固后自动化扫描 |
| Manifest 重签名/压缩 | 父 Activity 声明、intent-filter 导出设置 | 返回栈构建错误、隐式 Intent 劫持 | 安装后 instrumented test |
| 字节码优化 | flags 插入/删除、extras 处理顺序 | 崩溃 / ILlegalArgumentException,一次性令牌失效 | 运行时日志与通知交互测试 |
| SDK 适配变更 | targetSdkVersion 相关行为 | 自动添加的 FLAG_IMMUTABLE 可能导致跨版本兼容问题 | 多 API 级别设备回归 |
- 确认加固工具链是否声明对 PendingIntent 创建的已知修改
- 比较加固前 APK 与加固后 APK 的 Manifest 差异
- 提取原始代码中的所有 PendingIntent 创建点作为回归基线
可变性标志的回归验证
从 Android 12 开始,系统要求所有 PendingIntent 必须显式声明 FLAG_MUTABLE 或 FLAG_IMMUTABLE,否则直接抛出异常。加固可能移除源码中的 setFlag 调用,或由于字节码优化而未能加入预期标志,导致运行到特定分支时一瞬间崩溃。根据 PendingIntent 安全风险指南,省略可变性标志可能让恶意应用通过修改 extras 劫持通知动作,因此验证可变性既是稳定性也是安全回归。
回归的第一步是静态扫描所有 PendingIntent 创建点,确认是否都调用了 setFlags 并包含可变性标志。可使用下一节提供的诊断脚本,自动检测 getActivity、getBroadcast、getService 和 getForegroundService 的调用。脚本会根据源码目录检查调用了 PendingIntent 方法后是否补充了 setFlags 且包含 IMMUTABLE 或 MUTABLE。缺少的创建点会被标记为高危。
若静态扫描无法触及某些动态代理代码,则需要通过 instrumented test 调用真实通知发送路径,并捕获 IllegalArgumentException 以验证标志合规。测试应覆盖所有通知渠道入口,因为不同入口可能使用不同的 PendingIntent 构造方式。一个完整的回归方案还需要判断原则来指导加固后如何选择正确的可变性值,避免不合规的同时保持跨版本兼容。
| 接收方类型 | 是否传递可变 extras | Android 版本要求 | 推荐标志 |
|---|---|---|---|
| 启动本应用 Activity | 否 | 12+ | FLAG_IMMUTABLE |
| 启动本应用 Activity | 是 | 12+ | FLAG_MUTABLE |
| 发送广播给本应用 Receiver | 否 | 12+ | FLAG_IMMUTABLE |
| 发送广播给三方应用 | 需三方修改 extras | 12+ | FLAG_MUTABLE |
| 启动前台/后台服务 | 否 | 12+ | FLAG_IMMUTABLE |
- 扫描所有 PendingIntent 创建点是否调用 setFlags
- 所有未包含 IMMUTABLE 或 MUTABLE 的调用均视为回归未通过
- 在多版本虚拟设备上检查是否有运行时异常
- 确认发送方和接收方对可变性预期一致
请求码的角色与身份匹配
PendingIntent 身份由其类型(getActivity、getBroadcast 等)、请求码和底层 Intent 的 action、data、category、authorities 等字段共同决定。系统利用此身份判断两个 PendingIntent 是否相同以进行更新或比较。加固后,请求码若从常量表达式中取出,一旦常量值被混淆或重新计算,就会造成身份偏差。例如,原本 updateCurrent 策略因为请求码变化变成创建新令牌,导致前一个令牌失效,用户反复点击通知只执行一次。
回归验证必须提取加固前所有请求码常量和对应 Intent 字段,并在加固后确认这些值在源码或字节码层面保持不变。若请求码来自动态计算,则需在 instrumented test 中重复生成并比较新旧令牌的 hash。Android PendingIntent API 明确指示,仅当两个令牌的请求码和 Intent 字段匹配时才判为同一身份,因此任何微小偏离都可能打断交互。
请求码回归还应关注跨组件的匹配:发送方使用 PendingIntent.FLAG_UPDATE_CURRENT 期望替换旧令牌,而接收方在 onReceive 或 onCreate 中提取 requestCode 做分支逻辑。不匹配会导致接收方走入错误分支或完全忽略回调。验证时需模拟从发送方到接收方的完整链路,确保 requestCode 语义一致,尤其在 FCM 或 push 触发的通知场景中,这种错误往往难以定位。
| 检查项 | 加固前基线 | 加固后当前值 | 风险 |
|---|---|---|---|
| getActivity 请求码 | 1001 | 1001(混淆后常量未变) | 低 |
| getBroadcast 请求码 | 动态计算来自 notificationId | 计算公式可疑变化 | 高 |
| Intent action 字符串 | com.example.ACTION_VIEW | 未变 | 低 |
| Intent data URI | content://.../item?id=5 | URI encode 方式改变 | 中 |
- 提取并持久化加固前所有 PendingIntent 创建点的请求码与 Intent 字段快照
- 加固后再次比对同一创建点的字段值
- 使用 FLAG_NO_CREATE 测试回收旧令牌是否仍然有效
组件显式性:防止隐式 Intent 劫持
隐式 Intent 在不指定目标组件时,依赖系统解析最佳匹配 Activity 或 BroadcastReceiver,这为劫持提供了可能。加固若清除了原始代码中 setComponent 或 setPackage 调用,原本显式安全的 PendingIntent 就会退化为隐式,从而允许恶意应用通过声明相同的 intent-filter 拦截令牌。Android 安全指南明确指出,所有用于敏感操作的 PendingIntent 应当显示设置目标包名或组件名,加固回归必须保证这种显式性不被破坏。
扫描创建点时,除了检查可变性标志,还要校验 Intent 上的显式声明。脚本可检测 Intent 对象是否调用了 setComponent、setClassName 或 setPackage,并针对未使用任何显式声明方法的 PendingIntent 创建点抛出警告。对于 API 低于显式要求版本的情况,也应标记为潜在风险,因为低版本设备仍可能受到劫持攻击。
回归还应覆盖组件在 Manifest 中的声明状态,确认加固没有将导出的组件错误更改为非导出,反之亦然。虽然 Manifest 的静态结构不是 PendingIntent 劫持的直接入口,但导出属性的改变会扩大隐式 Intent 的攻击面。可以使用 aapt 工具比较加固前后组件的 exported 属性,并将差异纳入回归检查清单。
| PendingIntent 类型 | 是否调用 setComponent | 是否调用 setPackage | 风险等级 |
|---|---|---|---|
| getActivity | 是 | 否 | 安全 |
| getActivity | 否 | 是 | 中(需确保包名正确) |
| getBroadcast | 是 | 否 | 安全 |
| getBroadcast | 否 | 否 | 高危(易被三方接收器拦截) |
- 扫描所有创建点,确认显式调用 setComponent 或 setPackage
- 加固前后 Manifest 中目标组件的 exported 属性差异检查
- 在注入恶意应用的环境中测试隐式 PendingIntent 是否可被劫持(仅限测试环境)
进程重建与 PendingIntent 恢复
应用进程被系统杀死后重建,PendingIntent 由系统继续持有,重建后必须能够正确恢复其携带的 extras 和状态。加固可能改变了 Application 或启动 Activity 的初始化逻辑,导致重建时从 Intent 中提取 PendingIntent 附属数据的代码执行到不兼容分支,例如反序列化 extras 时类型转换失败。此外,进程退出原因对于分析此类问题至关重要,Android ApplicationExitInfo 可提供退出时间、原因和 ANR trace,辅助判断退出是否由 PendingIntent 回调异常触发。
回归测试需要在自动化环境中模拟进程死亡与重建。通过 instrumented test 发送通知并让应用进入后台,然后使用 adb shell am kill 或模拟系统内存压力触发进程终止,再点击通知验证 PendingIntent 能否正常传递到重建后的目标 Activity。必须检验 extras 的完整性、FLAG_ONE_SHOT 的一次性以及接收方是否仍然履行了正确动作。
进程重建的边界条件还包括系统恢复后 Activity 堆栈的重建。如果 PendingIntent 依靠 taskAffinity 或 launchMode 进行定向,加固后的 Manifest 变更可能使得恢复后的栈与原始设计不一致,导致回退键行为异常。这些场景需要与返回栈回归合并验证,并使用 UI Automator 或 Espresso 进行端到端检查,而不只是单元测试。
| 场景 | 触发方式 | 关键验证点 | 预期行为 |
|---|---|---|---|
| 通知点击后应用从冷启动重建 | 系统杀死后台进程后点击通知 | extras 内容、目标 Activity | 正确还原页面和业务数据 |
| 应用在前台时收到 PendingIntent 广播 | 进程异常退出后系统重发广播 | onReceive 中 requestCode 与 extras | 接收器按新进程逻辑处理 |
| 发送 FLAG_ONE_SHOT 后进程重建 | 杀死进程前已使用完毕的 PendingIntent 不应再次生效 | 再次触发时行为 | 静默失败不重新执行 |
| Application 扩展类初始化崩溃 | 在 Application.onCreate 抛出异常模拟 | PendingIntent 是否导致启动循环 | 应用最终进入恢复状态或安全退出 |
- 收集并分析 ApplicationExitInfo 日志排除因 PendingIntent 导致的异常退出
- 构造进程死亡并唤醒的 instrumented test 流程
- 验证所有 PendingIntent 生命周期标志(ONE_SHOT、CANCEL_CURRENT)在进程重建后仍然生效
通知返回栈的回归验证
通知点击后的返回栈体验直接关系到导航逻辑是否正确。Android 提供 TaskStackBuilder 和 PendingIntent.getActivities 来构建人工返回栈,加固可能改变父 Activity 的类名或缺失父声明,导致返回栈只包含目标 Activity 而无法回到上级页面。Navigation 深链库生成的 PendingIntent 也依赖相同的路由参数,加固后一旦参数编码或路由表变化,通知点击就会导致“目的地未知”的崩溃。
验证通知返回栈需要使用 instrumented test 结合 NotificationManager 发送完整通知,并模拟用户点击。通过 Espresso 或 UI Automator 检查 Activity 序列,确保从通知入口向回导航时能够依次回到主界面或其他预期 Activity。对于使用 Navigation 组件的应用,必须额外测试 deeplink 的解析与参数一致,但不能扩大到普通域名验证,只集中在通知触发的特定路由。
返回栈回归还应检查 PendingIntent 栈构建时是否加入了 NEW_TASK 和 CLEAR_TASK 等 flags,这些 flags 的缺失或冗余都会改变栈行为。加固若篡改了 Intent 组合会形成意料外的任务亲和性(taskAffinity)变化。最终,返回栈验证应形成基于场景的判断原则,涵盖常用通知类型如聊天消息、事件提醒、系统升级推送等,保证每一种栈结构都经过测试。
| 通知类型 | 期望返回栈深度 | 核心参数验证 | 失败表现 |
|---|---|---|---|
| 聊天单聊消息 | 2(聊天页 -> 会话列表) | 联系人 ID、消息 ID | 返回时空白或直接退出应用 |
| 内容更新推送 | 3(详情页 -> 列表页 -> 主页) | 文章 ID、分类 | 点击后停在启动页未进入详情 |
| 账户安全提醒 | 1(仅安全中心) | alertType | 缺少 NEW_TASK 导致崩溃 |
| 无返回栈意图(直接退到桌面) | 0 | FLAG_ACTIVITY_NEW_TASK 与 CLEAR_TASK 组合 | 多次返回出现空白页 |
- 用 UI 自动化验证各通知类型点击后的返回栈序列
- 比较加固前后 TaskStackBuilder 构建的 Intent 数组差异
- 确保父 Activity 声明未被加固移除或更名
自动化扫描脚本实现与诊断
手工检查 PendingIntent 创建点在大型项目中极易遗漏,因此需要一个可集成的扫描脚本。该脚本只读源码或反编译后的 Java 文件,搜索 PendingIntent 的工厂方法调用并分析其上下文,检查可变性标志与组件显式性。依据检查结果生成报告,若发现高危缺失则返回非零退出码,可作为持续集成流水线的回归门禁。脚本不依赖真实设备,仅做静态诊断,可以快速覆盖大量代码路径。
脚本设计为 Python 命令行工具,接收源码目录作为参数,递归查找 .java 或 .kt 文件。使用正则表达式匹配 getActivity、getBroadcast、getService 和 getForegroundService 的调用点,并向后跟踪在一定行数内是否调用了 setFlags 且包含 IMMUTABLE 或 MUTABLE。同时,检查 Intent 对象是否在创建后调用了 setComponent、setClassName 或 setPackage 方法。只有同时满足可变性与显式性要求的创建点才视为通过。
它以 Python 3 编写,仅使用标准库,失败条件直接基于实际扫描结果:当任一 PendingIntent 创建点缺失可变性标志或组件显式调用时,脚本将打印详细警告并返回状态码 1,否则返回 0。所有警告信息明确指明文件路径和行号,方便工程师定位。该工具仅做诊断,不进行任何文件修改,符合安全回归的只读要求。
| 输出类型 | 触发条件 | 含义 | 建议动作 |
|---|---|---|---|
| [高危] ... 缺少 FLAG_... | 创建点未调用 setFlags 或标志不全 | 可能导致崩溃或被篡改 | 补全 setFlags 调用 |
| [警告] ... Intent 未显式... | Intent 未调用 setComponent/setPackage | 可能被恶意应用劫持 | 增加显式目标声明 |
| 错误:'...' 不是有效的目录 | 输入参数路径不存在 | 无法执行扫描 | 检查命令行参数路径 |
| 回归未通过... | 检测到至少一个违规点 | CI 流水线应阻断 | 修复代码后重新运行 |
- 确保脚本具有执行权限
- 在 CI 环境中配置源码目录参数
- 将脚本退出码作为构建成功与否的判断依据
#!/usr/bin/env python3
"""扫描源码目录中所有 PendingIntent 创建点,校验 flags 与目标组件显式性。
失败条件:任一创建点缺少 FLAG_IMMUTABLE 或 FLAG_MUTABLE,或未显式设置目标组件。
用法:python3 scan_pi.py <src_dir>"""
import sys, os, re
def find_pi_creations(content):
"""返回找到的 PendingIntent 创建调用的列表,每项为 (line_num, call_type)"""
pattern = r'PendingIntent\.(getActivity|getBroadcast|getService|getForegroundService)\s*\('
return [(m.start() + 1, m.group(1)) for m in re.finditer(pattern, content)]
def check_flags(content, start_line, end_line):
"""检查指定行范围内是否调用了 setFlags 并包含 IMMUTABLE 或 MUTABLE"""
region = content.splitlines()[start_line-1:end_line]
region_text = '\n'.join(region)
has_flags = re.search(r'\.setFlags\s*\([^)]*PendingIntent\.FLAG_(IMMUTABLE|MUTABLE)', region_text)
return bool(has_flags)
def check_explicit_target(content, start_line, end_line):
"""检查同一范围内 Intent 对象是否显式设置 component 或 package"""
region = content.splitlines()[start_line-1:end_line]
region_text = '\n'.join(region)
has_explicit = re.search(r'intent\.(setComponent|setClassName|setPackage)\s*\(', region_text)
return bool(has_explicit)
def main():
if len(sys.argv) != 2:
print("Usage: python3 scan_pi.py <source_directory>")
sys.exit(2)
src_dir = sys.argv[1]
if not os.path.isdir(src_dir):
print(f"错误:'{src_dir}' 不是有效的目录")
sys.exit(2)
has_violation = False
for root, _, files in os.walk(src_dir):
for fname in files:
if not fname.endswith(('.java', '.kt')):
continue
path = os.path.join(root, fname)
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
creations = find_pi_creations(content)
if not creations:
continue
lines = content.splitlines()
for line_num, call_type in creations:
window_end = min(line_num + 8, len(lines))
if not check_flags(content, line_num, window_end):
print(f"[高危] {path}:{line_num} {call_type} 缺少 FLAG_IMMUTABLE 或 FLAG_MUTABLE")
has_violation = True
if not check_explicit_target(content, line_num, window_end):
print(f"[警告] {path}:{line_num} {call_type} Intent 未显式设置目标组件")
has_violation = True
if has_violation:
print("\n回归未通过:存在不满足加固后 PendingIntent 安全要求的创建点。")
sys.exit(1)
else:
print("扫描完成:所有 PendingIntent 创建点均通过回归校验。")
sys.exit(0)
if __name__ == '__main__':
main()综合回归流程与决策矩阵
将可变性标志、请求码、组件显式性、进程重建和返回栈五个维度整合后,可以形成一条严谨的加固回归流程。首先,从加固前 APK 提取所有 PendingIntent 相关代码片段和 Manifest 快照作为基线。加固完成后,立即运行自动化扫描脚本,快速筛选出缺失标志或显式目标的创建点,这是第一道门禁。只有通过的代码才能进入设备级测试,避免无效耗时。
设备级回归依托 instrumented test 构建场景:通知发送、进程重建、返回栈交互。结合前一节诊断脚本的输出,对高风险点编写专项测试,确保 flags 和组件声明在运行时表现正确。所有测试必须在多个 API 级别和厂商设备上执行,以覆盖厂商对 PendingIntent 行为的潜在修改。最后,建立决策矩阵,帮助团队快速判定回归问题类别和修复优先级,而不是靠调试堆栈逐一猜测。
决策矩阵将常见现象、检测手段、影响范围和修复建议系统化,减少修复时间并降低线上风险。将回归脚本和 instrumented test 集成至 CI 后,每一次加固产出都必须通过该流水线,确保 PendingIntent 相关行为零退化。这种方法基于公开 Android 平台语义和一手资料,能够被任何团队复现并维护。
| 问题现象 | 检测方法 | 影响维度 | 修复方向 |
|---|---|---|---|
| 通知点击崩溃 IllegalArgumentException | 静态扫描 + 运行时日志 | 可变性标志缺失 | 补全 setFlags 并显式声明 Immutable 或 Mutable |
| 旧通知更新失效或重复弹出 | 比较请求码与 Intent 字段快照 | 身份匹配与请求码常量 | 恢复原始请求码或调整为 FLAG_UPDATE_CURRENT |
| 通知可能被三方应用截获 | 扫描组件显式性 + 劫持测试 | 组件显式性与导出属性 | 增加 setComponent 并关闭不必要的导出 |
| 进程重建后通知无法跳转 | 模拟进程死亡 instrumented test | 进程恢复与 extras 反序列化 | 确保 extras 类型兼容或升级序列化逻辑 |
| 点击通知无法返回上级页面 | UI 自动化检查返回栈 | TaskStackBuilder 与父 Activity | 补全父 Activity 声明或修正栈构造代码 |
- 加固前建立 PendingIntent 基线快照
- 执行自动化扫描脚本作为第一道 CI 门禁
- 运用决策矩阵对 instrumentation 测试结果快速仲裁
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| PendingIntent 的可变性、目标组件和一次性标志会改变重放与重定向风险。 | PendingIntent security risks | 正确 flags 不保证通知返回栈和业务状态恢复正确。 |
| PendingIntent 身份由类型、请求码和底层 Intent 的特定字段共同决定,并由系统在进程外持有。 | PendingIntent API | API 身份规则不替代应用对 extras、用户状态和目标权限的验证。 |
| Navigation 深链会生成或匹配路由参数,并影响冷启动与任务返回栈。 | Navigation deep links | 路由匹配成功不代表参数可信或目标页面已完成业务授权。 |
| Manifest 定义组件、权限、intent filter、SDK 约束和应用元数据。 | Android app manifest | 静态清单不能证明运行时访问控制没有被业务代码放宽。 |
| 依赖真实 Android 运行时、组件和系统 API 的语义应通过设备端 instrumented test 验证。 | Android instrumented tests | 单一设备通过不能代表完整 API、ABI 和厂商矩阵。 |
| ApplicationExitInfo 可提供进程退出原因、ANR trace,并在新版本返回 Native tombstone。 | Android ApplicationExitInfo | 退出记录仍需与同一版本、时间窗和用户路径关联。 |
| 加固后的 PendingIntent 可能因为组件混淆或 Manifest 变更而丧失显式目标,导致隐式意图可被劫持。 | PendingIntent security risks | 仅指出显式性缺失的危险,不能证明系统一定会选择恶意接收方。 |
| TaskStackBuilder 构建的通知返回栈依赖父 Activity 声明,加固对 Manifest 的修改可能断裂返回路径。 | Navigation deep links | 深链接路由不变不代表 PendingIntent 内部栈结构未变。 |
工程常见问题
加固后为什么必须给 PendingIntent 加 FLAG_IMMUTABLE?
Android 12 及以上系统强制要求所有 PendingIntent 声明可变性,否则抛出异常。加固可能去除了原始标志,导致运行时崩溃,而且缺少 IMMUTABLE 会使通知动作可被外部应用篡改 extras,构成安全风险。
如何高效检查加固后 PendingIntent 请求码是否变化?
提取加固前代码中所有 PendingIntent 创建点的请求码常量和 Intent 字段,加固后进行比对;结合 instrumented test 用 FLAG_NO_CREATE 测试旧令牌有效性,可确认身份是否一致。
进程被杀死后 PendingIntent 的 extras 会丢失吗?
由系统持有的 PendingIntent 在进程重建后 extras 仍然存在,但若加固改变了 extras 的序列化结构或接收方反序列化逻辑,就会导致提取失败。使用 ApplicationExitInfo 分析进程退出原因有助于定位是否由此引发。
隐式 PendingIntent 在加固后如何快速扫描?
可使用自动化脚本搜索 PendingIntent 创建点,检查是否调用了 setComponent、setClassName 或 setPackage;未调用任何显式方法的高危创建点会输出警告,应全部修复为显式目标。
返回栈测试需要覆盖哪些典型通知类型?
应至少覆盖聊天、内容推送、提醒和特殊业务通知,验证点击后返回栈深度、目标 Activity 与期望一致,并检查是否使用了 TaskStackBuilder 或 PendingIntent.getActivities 正确构建栈。
自动化诊断脚本的失败条件是什么?
脚本仅做静态分析,如果发现任一 PendingIntent 创建点缺失可变性标志或未显式设置目标组件,就会返回非零退出码,并打印文件路径和行号,确保 CI 流水线能据此阻断不合规加固产出。