Skip to content

feat(rkp): 加入 --rkp-probe 多变异诊断 - #5

Merged
mcxiaochenn merged 2 commits into
masterfrom
feat/rkp-probe
Oct 18, 2026
Merged

mcxiaochenn merged 2 commits into
masterfrom
feat/rkp-probe

Conversation

@mcxiaochenn

Copy link
Copy Markdown
Owner

背景

PR #4 的修复上线后,真机(小米 17 Pro)的错误变成了:

{"error":{"code":"unparsed","stage":"frame",
  "message":"无法识别 TA 响应帧",
  "raw":{"buffer":40960,"written":0,"status_le":0,
         "head_hex":"0000…0000"}},"ok":false}

日志显示 challenge 已成功获取(RKP 传输工具: curl 之后才失败),所以:

  • 前置检查通过,网络可达,Google 服务端正常
  • send_cmd 返回 0(TA 没有拒绝)
  • 但响应缓冲区被覆盖成零字节(written: 0、status_le: 0)

注意 status_le 是 0 而不是哨兵 0x80000000 的 00000080,说明厂商确实写过前 4 字节,只是没写任何数据。这是「空成功」,既不是帧格式问题,也不是长度字段问题,单次调用无法继续区分原因。

改动

把可疑变量拆开逐项试探,一次装包拿回对照数据。

CallOptions 三个维度:

维度 取值 目的
缓冲区初始化 哨兵 / 全 0 / 前 4 字节写容量 排除「厂商把首字节当容量或状态读」
security_level 1 / 0 排除构造参数不匹配
请求封装 裸 / 长度前缀 排除请求帧约定不同

6 个变体:

变体 level 缓冲区 长度
baseline 1 哨兵 0xa000
zero-buffer 1 全 0 0xa000
capacity-header 1 前 4 字节 = 容量 0xa000
small-buffer 1 全 0 0x2000
level-0 0 全 0 0xa000
length-prefix 1 全 0 0xa000

每个变体独立子进程:重新 dlopen、重新构造对象。某个变体触发崩溃只丢失该组,而不是整轮探测;security_level 不同也无法复用同一个对象。

输出字段:

  • status:send_cmd 返回值
  • written:响应最后一个非零字节位置
  • request_changed:请求缓冲区是否被厂商改写 → 判断 out 参数是否落在了请求缓冲区上
  • request_before / request_after / response_head:各 64 字节 hex

另外,哨兵残留时直接报 frame/abi,不再让上层去猜帧格式。

判读表(已写入 README)

观察 结论
只有 baseline 空、zero-buffer 有数据 哨兵干扰厂商逻辑,默认初始化改为全 0
全部变体 written 均为 0 且 request_changed 为 false 请求没真正到达 TA,优先查 SELinux 与 TA 服务
某变体 request_changed 为 true out 参数落在请求缓冲区上,参数顺序需调整
level-0 有数据 该固件要求 security_level = 0
length-prefix 有数据 请求需要长度前缀
多个变体 crashed: true 厂商对象布局与该固件不匹配

使用

su -c '/data/adb/modules/teeforge_cd/teeforge --rkp-probe --json' > /sdcard/rkp-probe.json

会消耗一次 Google RKP challenge,不写任何产物、不改后端配置。

验证

  • cargo test -p teeforge:111 项通过(新增 5 项:帧封装、写入量、请求改写、变体唯一性)
  • cargo clippy --all-targets、cargo fmt --check:通过
  • 四 ABI release:arm64 922,984 / armv7 662,228 / x86 1,076,904 / x86_64 1,057,176 字节
  • xtask package 2,104,262 字节,verify 通过
  • 主机实跑 --rkp-probe --json:正确返回 prerequisite/arch 阻断(主机非 arm64)

真机探测结果待回传,结论会更新到 ROADMAP。

说明

  • 本轮不改默认调用路径:--rkp-issue 仍是「哨兵 + level 1 + 裸帧」,探测只用于诊断
  • 不改 CLI 语义与数据格式

真机上 challenge 能取到,但 send_cmd 返回 0 且响应缓冲区零字节
(written: 0、status_le: 0),既不是帧格式问题也不是长度字段问题。
这类"空成功"只靠一次调用无法区分原因,因此把可疑变量拆开逐项试探。

- ta 层拆出 CallOptions:缓冲区初始化(哨兵 / 全 0 / 前 4 字节写容量)、
  security_level(1 / 0)、请求封装(裸 / 长度前缀)
- 新增 6 个变体,每个变体在独立子进程中 dlopen 并调用一次,
  某个变体触发崩溃只丢失该组结果
- 记录 status、written、request_changed、response_head,
  request_changed 用于判断 out 参数是否落在请求缓冲区上
- 哨兵残留时直接报 abi,不再让上层去猜帧格式
- 新增 CLI --rkp-probe 与隐藏入口 --rkp-ta-probe,输出 JSON 对照表

主机 111 项测试通过(新增帧封装、写入量、请求改写、变体唯一性)。
四 ABI release:arm64 922,984 字节,包 2,104,262 字节。
设备侧检查排除了权限:keystore2 正常运行,dmesg 里没有任何涉及 teeforge
或 smcinvoke 的 avc 拒绝。公开代码里也找不到第二份 libkeymasterdeviceutils
的调用示例,上游那套「0x40 对象 + 哨兵 + 参数顺序」只是单点观察。

因此再补两个维度:

- CallOptions.object_size:0x40 / 0x80 / 0x100 三个变体。对象存储改用
  16 字节对齐的动态缓冲区,并统一预留 0x100 尾部余量——如果固件对象
  比 0x40 大,构造会写越界,没有余量就会破坏相邻堆数据,反而让探测
  结果失去意义
- 新增 rkp/elf.rs:极简 ELF64 动态符号解析,列出 KeymasterUtils 与
  send_cmd 相关导出符号,随探测输出为 library_symbols,用于判断是否
  存在必须先调用的初始化入口或其它重载;读取失败不影响探测

变体 6 -> 8,README 与 ARCHITECTURE 的判读表同步。

主机 115 项测试通过(新增 4 项 ELF 解析用例)。
四 ABI release:arm64 927,032 字节,包 2,112,878 字节。
@mcxiaochenn

Copy link
Copy Markdown
Owner Author

补充两项诊断维度(提交 583b1ce),变体从 6 个增加到 8 个:

  1. 对象大小:object-0x80 / object-0x100。上游按 0x40 字节分配厂商对象,但那只是单个被观察客户端的大小。若该固件对象更大,构造函数会写越界;现在存储统一预留 0x100 尾部余量,越界落在自己的缓冲区里,不会破坏相邻堆数据——否则探测本身就会改变被测行为。
  2. 库符号表:新增 rkp/elf.rs,解析 libkeymasterdeviceutils.so 的动态符号表,输出 library_symbols。如果固件提供必须先调用的初始化入口或其它 send_cmd 重载,会直接暴露出来,不用再盲试。

设备侧检查结论(供对照):keystore2 正常运行,dmesg 里没有任何涉及 teeforge 或 smcinvoke 的 AVC 拒绝,所以权限不是当前的主因;公开代码中也找不到第二份该库的调用示例,上游那套约定属于单点观察。

主机 115 项测试通过,四 ABI 与打包均已通过。

@mcxiaochenn
mcxiaochenn merged commit a3224b3 into master Oct 18, 2026
1 check passed
@mcxiaochenn
mcxiaochenn deleted the feat/rkp-probe branch October 18, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant