首页
社区
课程
招聘
[原创]安卓APatch Root方案compat的nofault回退导致被检测
发表于: 2026-9-15 07:17 4264

[原创]安卓APatch Root方案compat的nofault回退导致被检测

2026-9-15 07:17
4264

这条侧信道之所以成立,原因是 KernelPatch 的 compat_strncpy_from_user() 在不走 nofault 原语的内核上会回退(fallback)到普通 strncpy_from_user()——一次会真正触发缺页的用户内存读。

KP 的 supercall 鉴权路径正是用这个函数去读「superkey 指针」所指向的内存

APatch检测原理参考:春秋检测文档 70fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0K9h3&6Y4P5Y4g2F1x3o6W2Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8V1y4Z5N6h3&6I4K9i4g2Q4x3X3c8p5k6i4c8W2j5%4c8G2M7W2)9J5k6q4m8J5L8$3u0D9k6h3#2Q4x3X3c8K6L8$3I4#2N6r3W2G2L8W2)9J5c8W2)9J5x3#2)9J5c8V1k6A6L8r3g2Q4x3V1k6p5L8$3y4Q4x3V1k6C8M7%4g2Q4y4h3k6C8M7q4)9#2k6Y4y4A6k6r3g2U0K9r3q4F1L8X3g2D9i4K6g2X3P5X3R3`.

APatch检测原理:
建议参照【此代码】224K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3q4^5x3e0t1I4i4K6u0r3d9$3g2J5L8X3g2D9f1r3q4@1j5$3S2Q4x3V1k6T1L8r3!0T1i4K6u0r3x3K6f1J5k6r3f1K6y4K6b7%4y4U0V1K6k6o6b7H3x3$3g2T1x3h3p5@1j5X3b7J5j5K6V1^5k6r3x3H3y4r3q4W2j5U0l9I4z5e0f1#2j5g2)9J5c8X3E0W2M7X3&6W2L8q4)9J5c8Y4m8S2N6r3y4Z5i4K6u0r3j5$3!0E0L8h3!0F1i4K6u0r3M7%4g2H3k6i4u0U0j5h3I4D9i4K6u0W2j5H3`.`.阅读
APatch管理器在向kernelpatch框架发起鉴权请求时,kernelpatch会读取APatch管理器所提供的superkey的所在地址(从before函数的udata参数传入);kernelpatch在鉴权前会验证cmd值是否在范围内,在此范围内则允许鉴权,反之不允许。
检测方法:

KernelPatch 注册了一个自己的系统调用 __NR_supercall = 45(arm64 上 45 恰好是 truncate 的编号,所以 magic 路径叫 /system/bin/truncate)。APatch 管理器就是通过它向 KernelPatch 发起鉴权请求、下发 su / KPM 装载等命令。

它的入口是 supercall.c: before():

两个条件决定了这条侧信道能不能成立:

顺序换过代。0.10 / 0.12 一代是反的:先 if (cmd < SUPERCALL_HELLO || cmd > SUPERCALL_MAX) return;,再读密钥。那种顺序下 §1.3 的负 x1 会在门控处被挡掉,根本读不到 x0;本机这一代不存在这个挡板。

kernel/patch/common/utils.c:

(kfunc_call(f, ...) 展开为 if (kf_f) return kf_f(...);,即"有则用并立刻返回"。)

关键就在 kver > VERSION(6, 7, 0) 这道版本门:在 ≤ 6.7.0 的内核上,KP 连试都不试 nofault,直接落到 strncpy_from_user()。挡在这一步的是 KP 自己的版本判断,不是原语缺失——strncpy_from_user_nofault() 在 6.1 的 mm/maccess.c 里就已经存在(6.8 同文件、同实现)。

两者的差别只有一行 pagefault_disable():

于是同一次读,后果完全不同:

⇒ 这条侧信道是"回退"制造出来的,不是 KernelPatch 的设计意图。 KP 在 > 6.7.0 的内核上走 nofault,就不再暴露它;本机 4.19 落在回退分支上,才有得测。KPM 改不了 KP 的这条路径,也无法让本机 KP 去试那个它主动跳过的 nofault,所以只能从调用侧介入(见 §二)。

原生 do_sys_truncate():

同一个 x1 寄存器,在原生 truncate 里是 length,在 KP 里是 cmd 的低 16 位:

⇒ 侧信道只存在于 x1 < 0 这一种参数布局:此时 x0 上发生的任何读取 / 换入都只可能来自 KP 的 superkey 读取(§1.1 的顺序是它的前提)。x1 ≥ 0 两边都读,检测器无法据此区分内核,因此那里必须保持原生语义(否则会破坏 Read control 对照)。

errno 本身就把"有没有读路径"区分开了:x1 < 0 走长度检查、返回 -EINVAL;x1 ≥ 0 才进 user_path_at / getname,路径读不到时返回 -EFAULT。同一个坏指针、两种 errno,正是"负 length 提前返回、压根没读 path"的体现。

检测器(com.chunqiunativecheck,uid 10254)与公开文档涉及下面三条口径。它们针对同一个鉴权路径,可互相印证:

① 懒分配页探测

懒分配:用户态 mmap 一块匿名页后从不触碰 → VMA 有了,PTE 还没有,物理页尚未分配。

把它的地址当作 superkey 指针放进 x0 发起鉴权请求,调用后看该页是否被映射到物理内存:

② 鉴权时延探测

检测器拟定一个在范围内的 cmd 与一个不在范围内的 cmd,分别发起多次、累加求和使比值稳定:

这条口径成立的前提是"cmd 越界会提前返回",即 §1.1 里门控在密钥读取之前的那一代 KP。本机走不到这个差异:密钥先读,且对非受信任调用者而言密钥不匹配就已经 return,cmd 取值影响不到执行路径 ⇒ 比值恒 ≈ 1,该口径在本机不成立(见 §二 L4)。

③ 系统调用参数页被额外读取(对照法)

以"系统调用参数页被额外读取"为判据的侧信道。检测器把目标调用的参数/缓冲区放在一块受监控的用户页上,测量调用前后该页被内核读取的页数:

该条目同时会输出 Argument layouts(内核读取系统调用参数的寄存器布局探测)、多轮一致性 Consistency: n/n、Page size,以及 Probe duration(秒级,采样较重)。

与 Abnormal Environment 的关系:两者针对同一类"鉴权路径"侧信道。仓库里的 KSU/APatch 侧信道说明描述的是"懒分配页是否被映射"与"鉴权时延比值"两种口径;本条是按页读取量做对照的实现,属同一思路的不同变体,可互相印证。

口径换算:报告同时给出页数与 Page size(4096 B),所以 0 → 4 kB 就是 0 → 1 页。判据是"被监控页是否被换入 / 是否发生缺页"(等价 min_flt 增量),不是 smaps 的 Referenced 口径——被监控页是 mmap 后从未触碰的懒分配页,而 Read control 用一次"本来就该读该页"的调用作正对照。

根因既然在"那次读会触发缺页",对策就只有两条路:(a) 让那次读不发生,或 (b) 把它换成不触发缺页的读。(b) 需要改动 KP 自身(KPM 做不到,也无法让本机 KP 去试它主动跳过的 nofault),所以本模块走 (a)。

KP 的 supercall 钩子走 hook_syscalln() → fp_wrap_syscalln() → fp_hook_wrap(),在 hook 链的槽 0;链上的 before 回调按索引 0→N 顺序执行——晚装的挂钩只会落到槽 1,也就是在 KP 之后执行,等发现问题时页已经被读了。

因此本模块用框架 fp_hook() 直接接管 sys_call_table[45] 槽位,把 KP 的链 transit 保存为 backup,正常路径原样调用(KP 功能零回归)。

通道 ② 只在"cmd 越界会让 KP 提前返回"时才有信号。本机 KP 的 cmd 门控在密钥读取之后,且对非受信任调用者而言密钥不匹配就已在门控之前 return——cmd 取值影响不到执行路径,范围内 / 范围外两条路径同源 ⇒ 比值恒 ≈ 1,本机该口径不成立,故 L4 默认关闭(eq=0)。

它只为门控在前的那一代 KP 保留:那里越界分支会提前返回,与正常分支开销差明显。打开后只在越界分支补一次等长(APK_EQ_LEN = 128,等于 KP 的 MAX_KEY_LEN)的读取,让两条分支对齐。读取对象仍是 x0——该分支只在 x1 ≥ 0 时可达,而原生 getname 本来就会读 x0,所以这次读不产生任何新信号。

补充事实:su 走的是 execve 魔法路径,根本不调用 syscall(45),因此与 x1 < 0 这条判定天然无关。

用于覆盖其它参数布局(x2 / x3 上的 memdup_user、KP 的字符串原语等)。每个都要求四道门同时成立:窗口内 + apk_win_task == current + 非受信任 uid + 遍历已被 oracle 证实。

默认全部关闭有两层原因:① 真实靶点(x1 < 0)已由 L1 覆盖,不需要它们;② 关掉后模块不安装任何全局原语钩子,系统级足迹只剩 sys_call_table[45] 一个槽位。需要覆盖别的布局时:先 allow=0,<管理器uid>,再 g1=1 / g2=1。

图片描述

【KPM模块】解决“发现APatch的鉴权密钥”模块
【github开源仓库】799K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6x3N6h3&6Q4x3X3c8a6f1#2)9J5c8V1q4b7d9$3g2&6i4K6g2X3d9r3W2V1k6g2)9#2k6V1E0b7e0b7`.`.

【春秋NativeCheck】796K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6E0K9h3&6Y4P5Y4g2F1x3o6W2Q4x3V1k6o6K9s2g2F1M7h3W2#2i4K6u0V1c8r3g2@1k6h3y4@1L8%4u0Q4x3X3c8b7M7X3!0T1L8r3g2E0i4K6u0V1M7$3!0D9N6i4c8A6L8$3&6Q4x3V1k6@1M7X3g2W2i4K6u0r3L8h3q4A6L8R3`.`.
【KernelPatch】944K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3q4^5x3e0t1I4i4K6u0r3d9$3g2J5L8X3g2D9f1r3q4@1j5$3S2Q4x3V1k6@1M7X3g2W2i4K6u0r3x3K6f1J5k6r3f1K6y4K6b7%4y4U0V1K6k6o6b7H3x3$3g2T1x3h3p5@1j5X3b7J5j5K6V1^5k6r3x3H3y4r3q4W2j5U0l9I4z5e0f1#2j5b7`.`.
【APatch及各分支版本】017K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3q4^5x3e0t1I4i4K6u0r3b7g2m8S2N6r3y4Z5


传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

最后于 2026-9-17 02:38 被Lun_OS编辑 ,原因:
收藏
点赞 ・79
打赏
分享
最新回复 (42)
雪    币: 2543
活跃值: (3992)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2 楼
学习
2026-9-15 10:54
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3 楼
666
2026-9-15 11:17
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4 楼
666
2026-9-15 11:20
0
雪    币: 764
活跃值: (3857)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5 楼
66
2026-9-15 11:23
0
雪    币: 341
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6 楼
666
2026-9-15 11:29
0
雪    币: 48
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7 楼
谢谢楼主分享
2026-9-15 12:23
0
雪    币: 158
活跃值: (5926)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8 楼
学习 
2026-9-15 12:26
0
雪    币: 95
活跃值: (3852)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9 楼
666666666
2026-9-15 15:03
0
雪    币: 110
活跃值: (9656)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10 楼
tql
2026-9-15 15:43
0
雪    币: 9441
活跃值: (6363)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11 楼
看看
2026-9-15 16:06
0
雪    币: 24
活跃值: (657)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12 楼
666
2026-9-15 16:10
0
雪    币: 165
活跃值: (393)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13 楼
看看
2026-9-15 16:17
0
雪    币: 17
活跃值: (1676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14 楼
4.19-才有这个问题,此外这个是两年前的牛头检测发明的,不知道为啥现在还没修
2026-9-15 21:58
0
雪    币: 17
活跃值: (1676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15 楼

看了看是这个提交的问题:1e3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3q4^5x3e0t1I4i4K6u0r3d9$3g2J5L8X3g2D9f1r3q4@1j5$3S2Q4x3V1k6U0L8$3#2E0K9i4c8Q4x3V1k6W2k6U0b7K6y4h3x3@1k6U0W2W2y4e0x3%4k6U0j5H3x3K6c8T1j5U0c8S2x3e0x3^5y4e0V1H3x3o6t1H3x3o6x3#2k6e0b7K6y4U0b7J5i4K6t1$3L8X3u0K6M7q4)9K6b7W2!0q4y4q4!0n7z5g2)9^5b7W2!0q4y4g2)9^5z5g2)9^5c8q4!0q4y4W2)9&6z5q4!0m8c8W2!0q4y4g2!0m8y4g2!0n7c8q4!0q4y4#2)9&6b7g2)9^5y4q4!0q4c8W2!0n7b7#2)9^5b7#2!0q4z5g2)9&6y4#2!0m8c8g2!0q4y4q4!0n7b7g2)9^5y4W2!0q4y4q4!0n7z5q4)9^5b7W2!0q4z5q4!0m8c8W2!0n7y4q4!0q4y4W2)9&6z5q4!0m8c8V1I4f1e0#2!0q4y4g2!0m8c8W2!0n7b7#2!0q4z5q4)9^5y4#2!0n7y4q4!0q4y4W2)9^5z5g2!0n7c8g2!0q4y4q4!0n7z5q4)9^5c8q4!0q4y4g2)9^5z5q4!0n7x3q4!0q4y4#2!0m8b7#2!0m8y4W2!0q4y4g2)9^5c8W2!0n7y4H3`.`.

最后于 2026-9-15 22:22 被mb_bvvcoitr编辑 ,原因:
2026-9-15 22:01
0
雪    币: 3303
活跃值: (6446)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16 楼
666
2026-9-16 00:34
0
雪    币: 51
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17 楼
666
2026-9-16 08:38
0
雪    币: 6833
活跃值: (12062)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18 楼
研究的真好
2026-9-16 09:15
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19 楼
tql
2026-9-16 09:47
0
雪    币: 1901
活跃值: (6053)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
20 楼
666
2026-9-16 10:02
0
雪    币: 7468
活跃值: (7740)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21 楼
66
2026-9-16 10:08
0
雪    币: 1474
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
22 楼
66
2026-9-16 11:43
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23 楼
刚刚好需要
2026-9-16 13:49
0
雪    币: 159
活跃值: (1298)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24 楼
666
2026-9-16 14:07
0
雪    币: 1693
活跃值: (301)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25 楼
mb_bvvcoitr 4.19-才有这个问题,此外这个是两年前的牛头检测发明的,不知道为啥现在还没修

实测6.12也会有,≤ 6.7.0基本都有
AP的ef435c4提交compat_strncpy_from_user() 函数中

if (kver > VERSION(6, 7, 0)) {
    kfunc_call(strncpy_from_user_nofault, dest, src, count);
    kfunc_call(strncpy_from_unsafe_user, dest, src, count);
}

只有当内核版本高于 6.7.0 时,才会尝试 nofault 原语

但也不是小于这个版本就一定会出现这个,还得其他相关因素

最后于 2026-9-16 19:32 被Lun_OS编辑 ,原因:
2026-9-16 19:16
0
游客
登录 | 注册 方可回帖
返回