OnePlus 8T 无线渗透与 AI 实战指南

简体中文 · English concise edition

从会用工具,到理解系统、协议、证据与模型;所有实操以这台真实手机为实验平台。

一台手机里的分层安全与 AI 实验室

先说目标:你要成为哪种人?

不是背下十条 nmap 参数的人,也不是把 AI 的答案直接粘进终端的人。读完并完成本书实验后,你应当能够:

本书的学习循环只有五步:

观察、假设、实验、证据、复盘

flowchart LR
    O["观察:发生了什么?"] --> H["假设:哪一层可能解释它?"]
    H --> E["实验:只改变一个变量"]
    E --> V["证据:日志、包、哈希、时间线"]
    V --> R["复盘:结论能迁移到别处吗?"]
    R --> O

怎么使用 GitHub Pages 和内部 Wiki?

两者不是二选一,而是“教材层”和“研究层”:

入口 主要内容 什么时候看
GitHub Pages / 本主教材 已校对的原理、推荐顺序、安全实验、当前可靠结论 第一次学习、按 12 周路线推进、复习知识框架
内部 Wiki 原始来源摘要、现场证据、互斥假设、矛盾、失败路线和仍在变化的设备状态 排障、追问“为什么”、核验教材结论、开展新研究

推荐循环:先在 Pages 学会一个概念 → 在手机上完成实验 → 把原始证据和疑问写入内部 Wiki → 等结论稳定后再整理回 Pages。 Pages 负责让你学得顺,Wiki 负责让你研究得深;不要从未经校对的原始日志开始背命令,也不要把 Pages 的简化图当成全部证据。

导航

安全与授权边界

下面的实验只允许用于:

  1. 你自己的 OnePlus 8T、路由器、电脑和测试账号;
  2. 明确隔离的实验 SSID/VLAN;
  3. 你自己编译的 APK、训练 APK、CTF;
  4. 有书面授权且范围、时间和目标明确的测试。

本书不会提供邻居 Wi-Fi、公共网络、第三方账号、钓鱼、持久化驻留、凭据窃取、射频干扰或 GPS 欺骗的操作步骤。无线电主动发射还受频谱法规约束,“设备是自己的”不自动等于“可以任意发射”。

[!important] AI 不能替你判断授权。目标清单、授权书和停止条件必须由人确认。


第一部分:认识你的实验平台

1. 当前设备基线

本文命令以 2026-08-07 的实际状态为准:

当前实现 角色
硬件 OnePlus 8T,arm64 电池供电的移动实验平台
ROM LineageOS 21 / Android 14 接近 AOSP 的 Android 用户空间
root Magisk 30.7 受策略控制的真实 Android root
终端 Termux 0.118.3 F-Droid release Android App UID 下的命令环境
Linux Kali,经 PRoot-Distro 运行 Linux 用户态工具箱,不是虚拟机
网络 Wi-Fi + CMFA VpnService always-on、非 lockdown 的路由/DNS 层
AI CLI OpenCode 1.18.13 glibc arm64 在 Kali 内调用模型和工具
动态分析 Frida 17.17.0 / Objection 1.12.5 只在实验时启动
移动 AI UI Happy preview 1.7.0 连接本机 127.0.0.1:3005
USB configfs HID 脚本 自有测试机上的安全键盘演示

最终安全状态:Magisk 模块目录为空,没有全局 CA、APEX tmpfs 覆盖或 DNS mount;Frida 不自启;GLM key 位于 Kali 私有 0600 文件;Happy API 默认只监听 loopback。

2. 系统架构:五种“权力”不要混在一起

flowchart TB
    HW["OnePlus 8T 硬件\nWi-Fi / USB / NFC / BLE"]
    K["Android Linux 内核\n驱动 / SELinux / capabilities"]
    A["LineageOS 21\nFramework / App Sandbox / VpnService"]
    M["Magisk 30.7\n真实 su 与启动阶段"]
    T["Termux release\nUID 10205"]
    P["PRoot-Distro\nptrace 路径转换"]
    KL["Kali 用户空间\n工具、glibc、Python"]
    C["CMFA\ntun0 / DNS / 代理规则"]
    F["Frida server\nroot,按需启动"]
    H["Happy App ↔ 127.0.0.1:3005"]

    HW --> K --> A
    K --> M
    A --> T --> P --> KL
    A --> C
    M --> F
    T --> H

OnePlus 8T 手机实验室的权限与用户空间剖面

把这张图当作“权限地图”:琥珀色电梯表示 Magisk 取得的真实 Android root;TERMUX UID → PROOT → KALI USERSPACE 是用户空间通道,碰到 capability 锁仍会被挡住;VPN ROUTE 负责改道流量,不负责开权限门。

关键区别:

PRoot-Distro 官方说明其核心是通过 ptrace 拦截系统调用并改写路径;它不是带 namespace、cgroup、seccomp 的容器隔离。参见 PRoot-Distro 工作原理

3. 五分钟健康检查

在 Termux 中:

selfcheck-lineage.sh
oc --version
stack status
proot-distro list

预期核心输出:

✓ Kali 内官方 glibc OpenCode 可执行
✓ Kali DNS 正常(不依赖 Magisk 模块)
✓ GLM key 位于私有 0600 文件
结果: 3 通过, 0 失败

从电脑的 ADB 中:

adb devices
adb shell su -c id
adb shell "su -c 'magisk --sqlite \"SELECT * FROM settings;\"'"
adb shell settings get secure always_on_vpn_app
adb shell settings get secure always_on_vpn_lockdown
adb shell ip -brief addr show tun0

当前预期:

练习:不要只说“网络坏了”

将故障拆成四层:

flowchart LR
    L1["链路\nWi-Fi 是否关联"] --> L2["路由\n是否有 default route"]
    L2 --> L3["名称\nDNS 是否能解析"]
    L3 --> L4["应用\nTLS/API 是否成功"]

分别找一个命令验证,不允许用下一层的失败代替上一层结论。


第二部分:Linux、Android 与网络基本功

4. 终端基本功:先读,再改

4.1 三个环境的提示符

环境 进入方式 适合做什么
Android shell adb shell getpropdumpsyspmlogcat
Termux 打开 App 启动 PRoot、Happy、文件编排
Kali proot-distro login kali glibc 工具、Python、协议分析、AI CLI

不要把命令盲目跨环境复制。pm 是 Android 命令,apt 属于 Kali,pkg 属于 Termux。

4.2 每次实验都建立证据目录

在 Kali 中:

LAB="$HOME/labs/$(date +%Y%m%d-%H%M)-topic"
mkdir -p "$LAB"/{notes,logs,pcap,artifacts}
date -Ins | tee "$LAB/notes/start-time.txt"
uname -a | tee "$LAB/logs/uname.txt"

实验结束:

find "$LAB" -type f ! -name SHA256SUMS -print0 \
  | sort -z | xargs -0 sha256sum > "$LAB/SHA256SUMS"

为什么要哈希?因为“我分析的文件”和“你复核的文件”必须是同一个比特序列。

4.3 管道不是魔法

cmd wifi status | tee wifi-status.txt
grep -i rssi wifi-status.txt

| 把前一个进程的 stdout 接到后一个进程的 stdin;tee 同时写文件和屏幕。遇到空结果,先问:数据在 stdout、stderr,还是根本没有产生?

5. 从一条请求理解 TCP/IP

一次 App 请求不是“联网”两个字,而是一条链:

sequenceDiagram
    participant App as Android App
    participant VPN as CMFA / tun0
    participant DNS as DNS 172.19.0.2
    participant TCP as TCP/IP
    participant TLS as TLS
    participant API as 远端 API
    App->>VPN: 请求访问域名
    VPN->>DNS: 查询 A/AAAA
    DNS-->>VPN: 返回 IP
    VPN->>TCP: 建立连接
    TCP->>TLS: ClientHello / ServerHello
    TLS->>API: 加密 HTTP 请求
    API-->>App: 加密响应经原路返回

一次请求从 App、VPN、DNS、TCP、TLS 到 Wi-Fi 和远端 API 的旅程

图中青色是出站请求,琥珀色是返回数据。关键观察:VPN 是手机内部的虚拟路由层;最终承载数据离开手机的仍是 Wi-Fi 物理链路。TLS 在 TCP 之上保护应用数据,两者不是同一层。

实操 1:观察当前 Wi-Fi 与路由

adb shell cmd wifi status
adb shell ip -brief addr show wlan0
adb shell ip route
adb shell ip -brief addr show tun0

回答:

  1. wlan0 的地址属于哪个 CIDR?
  2. default route 指向哪里?
  3. tun0 为什么没有物理 MAC?
  4. VPN 存在时,流量为何仍要经过 wlan0 发射?

实操 2:DNS 分层验证

在 Kali:

cat /etc/resolv.conf
getent ahostsv4 open.bigmodel.cn
dig open.bigmodel.cn A
curl -I --max-time 10 https://open.bigmodel.cn/

判断规则:

这台机器曾经误用 Magisk DNS 模块;最终方案是 CMFA DNS + Kali resolver 文件,没有系统 mount。

不要用一张 VPN 首页截图证明“网络已经修好”:按钮文案、常驻通知和真实转发状态可能不同步。真正的联网证据必须同时覆盖路由、DNS 和应用请求三层;本书因此不把某次现场截图当作长期状态证明。

实操 3:用本机 HTTP 服务理解 socket

在 Kali 终端 A:

python3 -m http.server 8765 --bind 127.0.0.1

在 Kali 终端 B:

curl -v http://127.0.0.1:8765/
ss -ltnp | grep 8765

观察三件事:监听地址、端口、进程。把 127.0.0.1 改成 0.0.0.0 会扩大暴露面,所以本机服务默认使用 loopback。

6. 为什么 Kali 有 tcpdump 却抓不了包?

现场实测:

tcpdump -D
tcpdump -i lo -c 1

第二条会提示需要 CAP_NET_RAW。原因不是“tcpdump 坏了”,而是:

flowchart LR
    B["tcpdump 二进制存在"] --> S["请求 packet socket"]
    S --> C{"宿主进程有 CAP_NET_RAW?"}
    C -- "否:Termux UID + PRoot" --> D["Permission denied"]
    C -- 是 --> P["获得原始包"]

因此本书采用三种抓包来源:

  1. 授权实验 AP/电脑上的 Wireshark;
  2. Android VPN capture 类工具对本机 App 流量抓包;
  3. 已有 PCAP 拷入 Kali,用 tshark -r 离线分析。

PRoot 中优先使用不要求 raw socket 的 nmap -sT -Pn,不要默认复制需要原始 SYN 包的 -sS


第三部分:无线网络——从电波到协议

7. 先建立无线分层模型

flowchart TB
    RF["物理层:频率、带宽、调制、SNR"]
    MAC["802.11 MAC:管理/控制/数据帧"]
    SEC["链路安全:WPA2/WPA3、4-way handshake"]
    IP["IP:地址、路由、ICMP"]
    TRANS["TCP/UDP:端口、可靠性、拥塞"]
    APP["DNS/HTTP/TLS/QUIC"]
    RF --> MAC --> SEC --> IP --> TRANS --> APP

同一份信息从 RF、802.11、WPA2、IP 到 TLS 的表示变化

这是一条“接收路径”:电波先被无线芯片还原成 802.11 帧,链路层完成 WPA2 处理后才露出 IP 包,应用最终在 TLS 会话中解释数据。墙会衰减信号,噪声底则决定信号是否容易辨认——所以 RSSI 强不等于通信质量必然好。

7.1 RSSI 和 SNR

实操 4:做一张自己的室内信号地图

在房间选 5 个点,每个点运行:

adb shell cmd wifi status

记录 RSSI、frequency、link speed、位置和时间。不要自动化到失去观察:先手记 5 点,再思考墙体、人体遮挡和 2.4/5 GHz 的差异。

输出一张表:

点位 RSSI 频率 链路速率 障碍物 假设
路由器旁          
一堵墙后          
两堵墙后          

进阶:重复三次,计算均值和离散程度。一次读数不能代表稳定状态。

8. 802.11 帧:无线不只是“以太网在空气里”

802.11 常见帧类型:

类别 示例 你在分析什么
管理帧 Beacon、Probe、Authentication、Association、Deauthentication 网络发现与状态机
控制帧 ACK、RTS、CTS 介质访问和可靠性
数据帧 QoS Data 上层 IP 数据

这台 OnePlus 8T 当前内核/驱动组合没有可用的 monitor + injection 路径。普通 wlan0 只暴露关联后的接口,不会给你邻居网络的原始 802.11 帧。手机适合做控制、记录和离线分析;原始采集放在你自己的实验 AP 与支持 monitor 的外部采集端。

实验台搭建

flowchart LR
    AP["自有实验 AP\nSSID: LAB-8T\n独立访客 VLAN"]
    P["OnePlus 8T\n控制 / App / AI / 分析"]
    C["旧手机或笔记本\n实验客户端"]
    S["支持 monitor 的采集电脑\n只监听实验信道"]
    AP --- P
    AP --- C
    AP -. "授权原始帧采集" .- S
    S -->|"PCAP 拷入"| P

实验 AP 不连接工作/家庭敏感网段;SSID、BSSID 和测试密码不要复用真实网络。

实操 5:离线识别管理帧与异常断连

把自己的实验 PCAP 复制进 Kali 后:

capinfos lab-wifi.pcapng
tshark -r lab-wifi.pcapng -Y 'wlan.fc.type == 0' \
  -T fields -e frame.time_relative -e wlan.fc.type_subtype -e wlan.sa -e wlan.da \
  | head -30

只做检测:列出 deauthentication 帧及 reason code。

tshark -r lab-wifi.pcapng -Y 'wlan.fc.type_subtype == 0x000c' \
  -T fields -e frame.time_relative -e wlan.sa -e wlan.da -e wlan.reason_code

思考:一次正常漫游可能有 deauth;大量、周期性、多源伪造才更像异常。检测必须结合时序、信号和上下文。

9. WPA2 四次握手:PCAP 里没有明文密码

简化关系:

sequenceDiagram
    participant AP as AP / Authenticator
    participant STA as Client / Supplicant
    AP->>STA: Message 1: ANonce
    STA->>AP: Message 2: SNonce + MIC
    AP->>STA: Message 3: GTK + MIC
    STA->>AP: Message 4: ACK + MIC

WPA2 四次握手交换 nonce、MIC 与组密钥确认,而不是传送明文密码

图里的两块“工作台”表示 AP 与客户端使用共同的先验材料和双方 nonce 独立推导出匹配的会话密钥。MIC 是消息完整性校验,不是麦克风;握手线上没有一份可直接读出的明文密码。

WPA2-Personal 的 PMK 来源可概括为:

PMK = PBKDF2-HMAC-SHA1(passphrase, SSID, 4096, 256 bits)

PCAP 提供的是验证候选口令所需的握手材料,不是可直接“解密出来”的密码。

实操 6:用你自己的已知口令理解离线验证

仅对 LAB-8T 使用一个专用测试口令,在采集电脑上获取你自己的握手 PCAP。复制到手机后:

tshark -r lab-wpa2.pcapng -Y eapol
printf '%s\n' wrong-one '你的实验口令' wrong-two > lab-wordlist.txt
aircrack-ng -w lab-wordlist.txt lab-wpa2.pcapng

这里的目标不是跑巨型字典,而是回答:候选口令怎样经 KDF 和握手 MIC 被验证?随后删除包含测试口令的临时 wordlist。

你真正应该学会的防御结论

10. BLE、NFC 与 SDR:别把“能扫描”说成“能嗅探”

BLE

adb shell dumpsys bluetooth_manager

Android App 可以主动扫描广告、连接你自己的 BLE 设备并访问 GATT;这不等于能被动看到别的两台设备之间的连接事件。被动链路嗅探需要专用硬件和授权实验。

练习:拿一块你自己的 BLE 开发板,记录 service、characteristic、property(read/write/notify)及 UUID 的语义;先读协议,再写值。

NFC

adb shell dumpsys nfc

内置 NFC 控制器适合 Android API 的标签读写和卡模拟实验,不等同于 Proxmark3 的原始射频、调制与旁路分析能力。

SDR

本机当前没有可用的 RTL-SDR 内核驱动路径。可以把其它设备合法采集的 IQ 文件复制进 Kali,学习频谱、瀑布图和调制;不提供主动干扰/欺骗步骤。


第四部分:Android App 安全与动态分析

11. Android 安全模型

flowchart TB
    APK["APK:代码、资源、Manifest、签名"]
    UID["安装时分配 App UID"]
    SB["Linux DAC + SELinux Sandbox"]
    IPC["Binder / Intent / ContentProvider"]
    NET["Network Security Config / TLS"]
    KEY["Keystore / 文件加密"]
    APK --> UID --> SB
    SB --> IPC
    SB --> NET
    SB --> KEY

把 Android APK 当作一座有图纸、围墙、桥梁和运行时轨迹的城市

左侧是静态观察:从 Manifest、代码和资源画出城市图纸;中间的 UID 与 SELinux 是不同的检查层;Binder 是跨进程桥梁,TLS 是出站保护通道;右侧 Frida 显微镜只在受控实验期间观察运行时,不是一个万能解锁按钮。

APK 签名主要证明更新链和发布者连续性,不代表代码“安全”;root 能突破许多 App 沙箱边界,但 SELinux、硬件密钥和服务端校验仍可能限制你。

12. 实操案例:解释 Happy 的 Google Play 报错

Play 版曾出现如下页面:

Play 版 PairIP 授权错误

当时其它手机能正常使用同一 Wi-Fi,因此“网络坏了”不是充分解释。证据链是:

adb shell dumpsys activity activities | grep -m1 mResumedActivity
adb logcat -d | grep -iE 'pairip|license|play store|com.ex3ndr.happy'

前台进入 com.pairip.licensecheck.LicenseActivity,且设备没有 Play Store。结论:这是 Play 分发包装的授权依赖,不是 DNS 或 Wi-Fi 故障。

解决方案是固定官方源码提交自行构建 preview flavor:

adb shell pm list packages | grep happy
adb shell cmd package resolve-activity --brief com.slopus.happy.preview
adb shell am start -W -n com.slopus.happy.preview/.MainActivity

第一次自编 preview 包虽然绕过了 PairIP,却在点击“创建账户”时继续失败。不要把“能打开首页”当成服务可用;真实证据是 logcat:

adb logcat -c
adb shell am start -W -n com.slopus.happy.preview/.MainActivity
# 在手机点击“创建账户”后:
adb logcat -d | grep -iE 'CLEARTEXT|ERR_NETWORK|127.0.0.1:3005'

当时的决定性错误是:

CLEARTEXT communication to 127.0.0.1 not permitted by network security policy

原因是 preview 包 target SDK 36,而 Android 14 默认阻止明文 HTTP。这里不能为了省事设置全局 android:usesCleartextTraffic="true";正确修复是在 preview flavor 中加入 per-app Network Security Config:

<network-security-config>
    <base-config cleartextTrafficPermitted="false" />
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="false">127.0.0.1</domain>
        <domain includeSubdomains="false">localhost</domain>
    </domain-config>
</network-security-config>

这条策略只允许 App 访问本机回环 HTTP;其他主机的 HTTP 仍被拒绝,HTTPS 和外部服务器连接不受影响。公开仓库不分发本机签名 APK。复现时应从 Happy 上游源码固定一个已审阅的 commit,在 preview flavor 中加入上述最小白名单,记录构建环境与产物 SHA-256 后自行签名。覆盖安装可保留原有数据:

adb install -r <你从固定源码构建并签名的-preview.apk>

修复后的验收不能停在首页。实际点击“创建账户”后,server 日志中 /v1/auth 返回 200,后续 /v1/sessions/v1/machines/v1/feed 也返回 200,App 进入“终端 / 已连接”:

Happy preview 连接本机 127.0.0.1:3005

Happy preview 完成账户创建并连接本机服务

logcat 中仍会有 requires the Google Play Store, but it is missing 的可选服务警告,但它没有阻塞本机认证。学习点:同一个 UI 错误可能来自网络、分发授权、Android 明文策略、依赖缺失或应用逻辑。先找前台 Activity、客户端 logcat 与服务端请求日志,再下结论。

13. 静态观察:从 Manifest 开始

对你自己编译或明确授权的包:

PKG=com.slopus.happy.preview
adb shell pm path "$PKG"
adb shell dumpsys package "$PKG" | less
adb shell cmd package resolve-activity --brief "$PKG"

关心:

如果把 APK 拉到 Kali:

adb shell pm path "$PKG"
# 将输出的 base.apk 路径交给 adb pull;不要假设每个应用只有一个 split。

进阶目标不是“搜到字符串”,而是画出输入 → 信任边界 → 敏感操作 → 输出的数据流。

14. 动态观察:Frida/Objection 最小安全闭环

Frida 官方 Android 文档将 rooted device + frida-server 作为入门模式;Objection 1.12.x 的主入口是 start,不是旧教程里的 explore。参考 Frida AndroidObjection 使用说明

14.1 启动 server:只监听 loopback

在 Termux:

su -c '/data/adb/frida/frida-server -l 127.0.0.1:27042 -D'

检查:

su -c 'ss -ltnp | grep 27042'

不要把它监听在 0.0.0.0,也不要放在 world-readable 的 /data/local/tmp 常驻。

14.2 在 Kali 连接

proot-distro login kali
export PATH=/root/.local/bin:$PATH
frida --version
objection version
frida-ps -H 127.0.0.1:27042

以本机自编 Happy preview 做只读实验:

objection -N -h 127.0.0.1 -P 27042 \
  -n com.slopus.happy.preview start

在 Objection REPL 中:

env
android hooking list activities

完成后:

exit
su -c 'pkill -x frida-server'
su -c 'pidof frida-server || true'

14.3 学会 hook,而不是背 hook

以一个你自己的训练类为例:

Java.perform(function () {
  const Gate = Java.use('com.example.lab.LicenseGate');
  Gate.isAllowed.implementation = function () {
    const original = this.isAllowed();
    console.log('isAllowed original = ' + original);
    return original;
  };
});

第一步只记录原值,不修改行为。你需要理解:类加载器、方法重载、Java ↔ JNI、实例方法的 this、进程生命周期。先观察再修改,才能区分 hook 错了还是应用逻辑真的如此。

15. Android 14 TLS 调试:定向,不全局

旧方案把用户 CA 全局提升为系统 CA,或 tmpfs 覆盖 Conscrypt APEX,会扩大整机 MITM 面。最终策略:

flowchart TD
    Q{"你拥有 APK 源码吗?"}
    Q -- 是 --> D["debug build + Network Security Config debug-overrides"]
    Q -- 否,但有明确测试授权 --> F["临时 Frida/Objection 定向观察"]
    Q -- 否且无授权 --> X["停止"]
    D --> C["仅调试证书 / 仅目标 App"]
    F --> C

自有源码推荐:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

debug-overrides 仅在 android:debuggable=true 时生效,release 会忽略。参见 Android Network Security Configuration

对明确授权、无法重编译的目标,Objection 中可临时尝试:

android sslpinning disable

测试完成立即退出并停止 Frida。银行、支付、他人账号和生产 App 不作为练习目标。


第五部分:Magisk、启动链与可恢复工程

16. Magisk 在启动链的什么位置?

flowchart LR
    BL["Bootloader"] --> BI["boot image / ramdisk"]
    BI --> MI["Magisk init"]
    MI --> PFD["post-fs-data\n阻塞、早期"]
    PFD --> Z["Zygote / Android Framework"]
    Z --> LS["late_start service\n非阻塞"]
    LS --> BC["boot-complete\n计数归零"]

Magisk 官方说明 post-fs-data 是阻塞阶段,常规脚本更适合 late_start service;模块目录位于 /data/adb/modules。参考 Magisk Developer Guides

17. Safe mode 案例:为什么“事后是 0”不矛盾?

这台设备曾经每次启动都像进入 Magisk safe mode。源码条件可简化为:

boot counter threshold OR safemode property OR key combo

错误做法是只 patch check_key_combo(),因为它没有证明实际命中哪条分支。更关键的是:启动完成会把计数器清零,因此事后看到 0 不能反证早期从未为 1 或更高。

Magisk 启动早期计数器的时间线取证

图中 THRESHOLDPROPERTYKEY COMBO 是并列候选触发分支;没有现场证据时不能擅自指定具体按键。下方的时间戳说明同一次启动里“早期为 1”和“完成后为 0”可以同时为真,决定性信息是何时采样

受控复测两次都观察到:

启动早期 bootloop=1
boot-complete 后 bootloop=0
safe-mode properties 为空
模块没有被禁用

实操 7:早期时间线采样

在电脑上执行,只做观察:

adb reboot
adb wait-for-device

for i in $(seq 1 45); do
  printf '%s boot_completed=' "$(date -Ins)"
  adb shell getprop sys.boot_completed | tr -d '\r'
  adb shell "su -c 'magisk --sqlite \"SELECT * FROM settings;\"'" 2>/dev/null || true
  sleep 1
done | tee magisk-early-boot.log

再补:

adb shell getprop persist.sys.safemode
adb shell getprop ro.sys.safemode
adb shell "su -c 'find /data/adb/modules -maxdepth 2 -name disable -print'"

分析原则:时间戳优先于事后快照;一个变量的最终值不能描述完整状态机。

紧急退路

Magisk 官方 FAQ 建议模块导致 bootloop 时可用 safe-mode 按键组合;ADB 可用时,magisk --remove-modules 会删除全部模块并重启。它是破坏性救援命令,不是日常诊断命令。参见 Magisk FAQ

18. 备份:能恢复才叫备份

当前主机保留:

Kali 密文:

backups/2026-08-06-termux-migration/kali-before-fdroid.tar.gz.age
SHA-256: 4e6715729c628f505e4d6f331aca6405e602882ff69268d70b35329cb7a7271a

私钥存 macOS Keychain service:com.oneplus8t.kali-backup.age

恢复前先验证密文哈希,再在 macOS zsh 中:

identity=$(security find-generic-password \
  -a lazy -s com.oneplus8t.kali-backup.age -w)
age -d -i <(printf '%s\n' "$identity") \
  -o kali-before-fdroid.tar.gz \
  backups/2026-08-06-termux-migration/kali-before-fdroid.tar.gz.age
unset identity
gzip -t kali-before-fdroid.tar.gz

不要在没验证目标目录和 UID 前直接覆盖手机数据。


第六部分:USB 与物理接口

19. BadUSB 的底层:描述符 + report

USB gadget 让手机作为 USB 设备向电脑声明自己的功能。HID 键盘的关键不是“运行了一条命令”,而是:

sequenceDiagram
    participant Phone as OnePlus 8T gadget
    participant Host as 自有测试电脑
    Phone->>Host: Device/Configuration/HID descriptors
    Host-->>Phone: 枚举为键盘
    Phone->>Host: 8-byte HID report: modifier + keycodes
    Phone->>Host: 全零 report:释放按键

这台设备使用:

20. 实操 8:只在文本编辑器里打一句话

准备:

  1. 使用你自己的离线电脑;
  2. 手动打开一个空白文本编辑器并聚焦;
  3. 不使用 shell、运行框、浏览器或登录页面;
  4. 手机上脚本已部署为 root:root 0700。

从 ADB 触发时必须脱离会断开的会话:

adb shell su -c \
  'setsid sh /data/local/tmp/badusb-ducky.sh "BadUSB OK - OnePlus 8T" >/dev/null 2>&1 &'

脚本会:互斥加锁 → 保存 g1 UDC → 解绑 g1 → 建 g2 HID → 发按键 → 拆 g2 → 恢复 g1。

复盘:

本书不提供绕锁屏、下载执行、凭据获取或驻留 payload。


第七部分:AI 大模型——从“会问”到“会验证”

21. Transformer 到底在做什么?

flowchart LR
    T["文本 / 日志 / 代码"] --> TOK["Tokenizer\n切成 token"]
    TOK --> EMB["Embedding\n向量表示"]
    EMB --> ATT["Self-Attention\n按上下文混合信息"]
    ATT --> FFN["Feed-Forward\n非线性变换"]
    FFN --> L["多层堆叠"]
    L --> P["下一个 token 的概率分布"]
    P --> OUT["采样/选择并继续生成"]

它不是数据库检索器,也不是形式证明器。它擅长从上下文中压缩模式并生成后续 token,因此:

22. OpenCode:手机上的 AI 工具调用层

当前启动链:

flowchart LR
    U["Termux 用户"] --> OC["$PREFIX/bin/oc"]
    OC --> PD["PRoot-Distro login kali"]
    PD --> BIN["/opt/opencode-1.18.13/opencode"]
    BIN --> CFG["模型配置"]
    CFG --> KEY["/root/.secrets/glm-api-key\n0600"]
    BIN --> API["模型 API,经 CMFA"]

基本操作:

selfcheck-lineage.sh
oc --version
oc

最小非交互验证:

oc run '只回复:模型链路正常'

不要执行:

cat /root/.secrets/glm-api-key

“我能读出 secret”不是健康检查。健康检查只验证文件存在、权限正确和实际 API 调用成功。

OpenCode 的模型/Provider 配置会变化,参考 OpenCode ConfigModels

23. Happy:把 AI 会话带到手机 UI

在 Termux:

stack up
stack status
stack log
stack cli
stack down

架构:

flowchart LR
    APP["Happy preview App"] <-->|"E2E 会话 / loopback"| API["Happy server-light\n127.0.0.1:3005"]
    API <--> CLI["Happy CLI"]
    CLI <--> AGENT["Codex / Claude Code"]

server 不设开机自启是有意设计:用时 stack up,不用时 stack down;默认不监听 Wi-Fi/LAN。Happy 官方组件关系可参考 slopus/happy

当前 preview APK 是本机专用构建。因为 server 使用 http://127.0.0.1:3005,App 的 Network Security Config 仅对白名单回环地址允许明文,其他 HTTP 地址仍默认拒绝。快速回归:

stack up
stack status
# App 点击“创建账户”或重新打开后
stack log
adb logcat -d | grep -iE 'CLEARTEXT|ERR_NETWORK'

成功标准不是“页面能启动”,而是 App 顶部显示“已连接”,且 stack log 中真实 API 请求返回 200。APK、签名文件、旧失败包和本机服务配置属于私有实验材料;公开报告只保留上游 commit、补丁、构建命令、产物 SHA-256 与脱敏证据。

24. AI 辅助安全研究的正确姿势

24.1 证据契约

给模型的输入应包含:

授权范围:自有 OnePlus 8T 与隔离 LAB-8T SSID
目标:解释 DNS 失败,不修改设备
环境:LineageOS 21 / Android 14 / CMFA / Kali PRoot
观察:原始命令与输出(已去除 token、IP、账号)
限制:只给诊断假设和只读验证命令
要求:区分事实、推断和未知;为每个假设给证伪条件

好问题:

下面是 ip route、resolv.conf、getent 和 curl 的脱敏输出。
请按链路/路由/DNS/TCP/TLS 分层,列出最多 3 个假设。
每个假设给一个只读验证步骤、预期结果和反例。
不要假设手机有 SIM,不要建议修改全局 CA 或 Magisk 模块。

坏问题:

网络坏了,给我一条命令修好。

24.2 模型输出要经过三道门

flowchart LR
    M["模型建议"] --> A{"授权范围内?"}
    A -- 否 --> STOP["停止"]
    A -- 是 --> R{"可逆、单变量?"}
    R -- 否 --> REDESIGN["缩小实验"]
    R -- 是 --> E["执行并采集证据"]
    E --> C{"结果支持假设?"}
    C -- 否 --> H["更新假设"]
    C -- 是 --> DOC["写入 Wiki"]

24.3 AI 适合做的五件事

  1. logcat 聚类成时间线;
  2. 为 PCAP 写只读 tshark 过滤器;
  3. 把 Frida 观察脚本解释成调用链;
  4. 根据证据提出互斥假设;
  5. 把实验结果整理成 Wiki、复盘题和下一步。

不应交给 AI:授权判断、secret 管理、不可逆刷写、未知目标上的自动利用、对模型输出无审查地执行 root 命令。

25. 实操 9:让 AI 帮你分析,而不是替你猜

选择“实操 2:DNS 分层验证”的脱敏输出:

  1. 保存原始输出;
  2. 手工写一个初始假设;
  3. 让 OpenCode 给三个互斥假设;
  4. 只执行只读验证;
  5. 比较你和模型遗漏了什么;
  6. 将最终结论写进 Wiki,并链接原始实验记录。

评分标准不是“模型一次答对”,而是:错误假设能否被最小成本证伪。


第八部分:综合项目——从工具使用者到研究者

26. 九个递进项目

等级 项目 交付物 证明你掌握了什么
L0 设备健康报告 命令、版本、哈希、截图 会复现环境
L1 DNS 故障树 四层验证与反例 会分层诊断
L1 室内 RSSI 地图 5 点 × 3 次观测 理解信号统计
L2 自有 PCAP 解剖 管理帧/EAPOL/时序笔记 理解 802.11 状态机
L2 本机 socket 实验 listen/connect/loopback 图 理解端口和暴露面
L3 Happy 授权错误复盘 Activity + logcat 证据链 区分网络与应用故障
L3 自有 APK 动态地图 Activity、类、调用图 会用 Frida 观察
L4 Magisk 启动时间线 早期/完成状态对照 理解状态机和启动阶段
L5 AI 辅助研究报告 事实/推断/未知 + 可证伪实验 会把模型当研究助手

27. 12 周学习路线

第 1–2 周:系统与终端

第 3–4 周:TCP/IP 与无线物理

第 5–6 周:802.11 与防御分析

第 7–8 周:Android 安全

第 9–10 周:root、启动链与恢复

第 11–12 周:AI 研究闭环

28. “精通”不是工具数量

你达到进阶水平时,应当能回答:

如果只能回答“换个脚本试试”,还没学会;如果能从内核、协议、状态机和证据解释,就开始具备举一反三的能力。


第九部分:日常速查与故障树

29. 日常启动顺序

# Termux
selfcheck-lineage.sh
stack up                 # 需要 Happy 时才启动
oc --version
proot-distro login kali

结束:

stack down
su -c 'pkill -x frida-server' 2>/dev/null || true

30. 故障速查

现象 第一证据 不要先做 合理下一步
Kali DNS 失败 ip routegetent 装 Magisk DNS 模块 查 CMFA/tun0/resolver
OpenCode 不能跑 selfcheck-lineage.sh 换 musl 包 查 glibc binary 与 PRoot
tcpdump permission denied capability 错误 反复 sudo 外部采集或 Android VPN capture
Objection 找不到 /root/.local/bin 全局 pip install export PATH=/root/.local/bin:$PATH
Frida 连不上 server PID/端口/版本 开放到 0.0.0.0 对齐 17.17.0、loopback 检查
Happy 提示 Play Store 前台 Activity/logcat 改 Wi-Fi/DNS 使用自编 preview 包
Happy 首页能开、创建账户失败 logcat 是否含 CLEARTEXT 全局允许 HTTP 安装带 loopback NSC 的 preview 包
Happy 页面连不上 stack status/log 监听整个 LAN stack up,检查 127.0.0.1:3005 与 /v1/auth 状态码
Magisk 模块全禁用 早期 DB/props/disable 文件 patch 二进制 采启动时间线
BadUSB 后 ADB 不回 g1/g2 UDC 状态 并发重跑脚本 重启/物理重插并复核锁

31. 绝不复制回来的旧方案


官方资料与继续学习

项目配套入口

[!note] 工具参数会变,底层原理变化慢。升级前先读 release note、备份、记录版本和哈希;遇到差异时以 --help 与官方文档为准,不以旧博客截图为准。


结业模板

每个项目用下面的格式收尾:

## 问题

## 授权与范围

## 环境和版本

## 原始观察

## 互斥假设

## 最小实验

## 证据与哈希

## 结论(事实 / 推断 / 未知)

## 反例

## 可迁移原则

## 下一步

当你能持续产出这种报告时,你就不再是“运行脚本的人”,而是在做真正的安全工程与研究。