# BotGuard v2 检测后专家团测评报告
> 评审对象:BotGuard-Android v2.0.0(单模块架构,20 个 Kotlin 文件,约 2821 行)
> 评审日期:2026-07-15
> 评审方法:逐行代码审查 + 架构分析 + 检测能力矩阵评估
---
## 综合评分:7.4 / 10
| 维度 | 评分 | v1 对比 | 一句话结论 |
|------|------|---------|-----------|
| 安全研究有效性 | 7.5 | +1.5 | IoC 真正接入了,网络层补上了,但 6 项检测存在 API 失效问题 |
| Android 底层准确性 | 6.5 | +1.0 | /proc/net/tcp 解析正确,但多个系统 API 在 minSdk=26 下已失效 |
| 恶意软件检测覆盖 | 8.0 | +2.0 | 45 项检测 + 6 家族 IoC + 网络层 = 覆盖面显著提升 |
| 软件架构质量 | 8.0 | +1.5 | 单模块 + 手动 DI 大幅简化,但缺少协程和缓存 |
| 质量保障 | 5.5 | -0.5 | 零测试,无 Gradle Wrapper,无法构建验证 |
| 用户体验 | 8.0 | +1.5 | Compose UI 完整,进度展示 + 结果卡片 + 报告全文 |
**v1 → v2 进步总结**:v1 的三个致命问题(IoC 未接入 / 网络层缺失 / 无 Root 覆盖率低)全部修复。v2 的问题是"API 选错了"而非"架构选错了"——修复成本远低于 v1。
---
## 第一部分:问题清单(按优先级)
### P0 — 严重(会产出错误结果或死代码)
#### P0-1:A3-04 `getRunningServices()` 在 API 26+ 只返回自身服务
**文件**:PersistenceModule.kt:141
**问题**:`ActivityManager.getRunningServices(Int.MAX_VALUE)` 自 Android Oreo (API 26) 起**只返回调用者自身应用的服务**。由于 minSdk = 26,此方法永远不会返回第三方应用的前台服务。
**影响**:A3-04(前台服务保活检测)是**完全的死代码**,在任何目标设备上都不会产出有意义的 Finding。
**修复方向**:改用 `UsageStatsManager.queryEvents()` 检测长时间运行的前台应用,或通过遍历 `/proc/<pid>/cmdline` + `/proc/<pid>/status` 中的 `State` 字段判断后台进程。
#### P0-2:A5-04 `INSTALL_NON_MARKET_APPS` 已废弃,始终返回 1
**文件**:BehaviorModule.kt:108
**问题**:`Settings.Secure.INSTALL_NON_MARKET_APPS` 在 API 17 已废弃,在 Android 8+ 系统上**始终返回 1**(或抛出异常被 catch 为 false)。Android 8+ 改为按应用授予安装权限(`REQUEST_INSTALL_PACKAGES`)。
**影响**:在部分设备上会**始终触发误报**,在另一些设备上会**始终不触发**(取决于 OEM 实现)。
**修复方向**:改用 `PackageManager.getInstalledApplications()` + 检查 `REQUEST_INSTALL_PACKAGES` 权限,列出哪些第三方应用可安装 APK。
#### P0-3:A3-06 `canDrawOverlays()` 逻辑错误 — 检查的是 BotGuard 自己
**文件**:PersistenceModule.kt:189
**问题**:`Settings.canDrawOverlays(ctx.appContext)` 返回的是**当前应用(BotGuard)**是否拥有悬浮窗权限,而非每个被遍历的应用。因此 `overlayApps` 列表要么包含所有非系统应用(当 BotGuard 自己有权限时),要么为空。这个变量是死代码,后续逻辑用的是 `declaredOverlay`(仅检查声明,不检查授权)。
**影响**:A3-06 只检测"声明了权限"但不检测"是否已授权",会产生大量误报(很多正规应用声明 SYSTEM_ALERT_WINDOW 但用户未授权)。
**修复方向**:使用 `AppOpsManager.checkOpNoThrow(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName)` 逐应用检查实际授权状态。
#### P0-4:A5-05 CPU 负载检测读取的是开机以来的累积值
**文件**:BehaviorModule.kt:125-153
**问题**:读取 `/proc/stat` 一次,用 `busy / (busy + idle)` 计算 CPU 使用率。但 `/proc/stat` 的数值是**开机以来的累积 jiffies**,不是瞬时负载。如果设备已开机 10 小时,即使最近 5 分钟 CPU 满载,计算出的使用率仍可能很低。
**影响**:A5-05 几乎无法检测到突发性的挖矿/DDoS 行为,只能检测到长期高负载的极端情况。
**修复方向**:采样两次 `/proc/stat`(间隔 1-2 秒),计算 delta 值。或改读 `/proc/loadavg`(1 分钟平均负载),虽然粒度粗但至少是近期数据。
#### P0-5:ScannerEngine 永远不 emit Error 事件
**文件**:ScannerEngine.kt:46-49
**问题**:模块执行异常被 catch 后 emit `ModuleDone(moduleId, 0)`,而非 `Error`。`ScanEvent.Error` 定义了但**从未被 emit**。
**影响**:如果某个模块崩溃(如 IocMatcher 未初始化、权限拒绝),用户看不到任何错误提示,只看到该模块 0 项发现,误以为"安全"。
**修复方向**:在 catch 块中 emit `ScanEvent.Error("模块 $moduleId 执行失败: ${e.message}")`,或至少在 ScanResult 中添加 `errors: List<ModuleError>` 字段记录失败模块。
#### P0-6:无 Gradle Wrapper
**问题**:项目根目录缺少 `gradlew`、`gradlew.bat`、`gradle/wrapper/gradle-wrapper.properties`、`gradle/wrapper/gradle-wrapper.jar`。
**影响**:项目**无法直接构建**。开发者必须手动安装正确版本的 Gradle (8.x) 并配置版本匹配。CI/CD 也无法自动化。
**修复方向**:运行 `gradle wrapper --gradle-version 8.7` 生成 wrapper 文件。
#### P0-7:零测试文件
**问题**:整个项目没有任何 `src/test/` 或 `src/androidTest/` 目录,零测试代码。
**影响**:无法验证检测逻辑正确性,无法防止回归。v1 至少有 RiskScoreTest.kt 和 DetectionModuleTest.kt。
**修复方向**:至少补充以下测试:
- `RiskScoreTest`:评分边界值 + IoC 加分 + 链加分
- `IocMatcherTest`:CIDR 匹配 + 精确 IP + 进程名 + 白名单
- `NetworkModuleTest`:/proc/net/tcp 解析 + hex 地址转换
- `ReportGeneratorTest`:JSON 格式完整性
---
### P1 — 高(显著准确性/能力缺陷)
#### P1-1:IPv6 连接完全被丢弃
**文件**:NetworkModule.kt:105-113
**问题**:`parseHexAddr()` 对 IPv6 地址(>8 hex 字符)返回 `"0.0.0.0"`。随后 `parseTcpFile()` 中 `if (remoteIp != "0.0.0.0" && remotePort != 0)` 过滤掉了所有 IPv6 连接。
**影响**:如果 C2 服务器使用 IPv6 地址,或者设备通过 IPv6 连接 C2,**完全无法检测**。现代 Android 设备大量使用 IPv6(尤其是移动网络)。
**修复方向**:实现 IPv6 hex 地址解析(16 字符 → 8 组 hex16 → IPv6 地址),或至少标记为 "IPv6 connection" 不丢弃。
#### P1-2:A7-07 DNS 检测检查的是 TCP 53 端口
**文件**:NetworkModule.kt:268
**问题**:检查 `remotePort == 53` 的 TCP 连接,但 DNS 主要使用 UDP。`/proc/net/tcp` 只包含 TCP 连接。DNS-over-TCP 相对罕见(区域传输、大响应、DoT)。
**影响**:A7-07(异常 DNS 查询)几乎不会触发,因为普通 DNS 查询走 UDP 不在此文件中。
**修复方向**:改读 `/proc/net/udp` 和 `/proc/net/udp6`,或改用 `ConnectivityManager.getLinkProperties()` 获取当前 DNS 服务器列表,检查是否被篡改。
#### P1-3:A7-04 异常外连只排除 10.x 私有网段
**文件**:NetworkModule.kt:199
**问题**:过滤 `!it.remoteIp.startsWith("10.")` 但**未过滤** `192.168.x.x` 和 `172.16-31.x.x`。
**影响**:局域网内连接(路由器管理页、NAS、投屏等)会被计入"异常外连",导致**误报率大幅上升**。
**修复方向**:添加完整的 RFC 1918 私有地址过滤,包括 127.0.0.0/8、169.254.0.0/16。
#### P1-4:A2-06 动态代码加载检测是死代码
**文件**:AppModule.kt:123-134
**问题**:检测逻辑是检查 `appInfo.nativeLibraryDir` 是否包含 "tmp"。正常应用的 nativeLibraryDir 格式为 `/data/app/<pkg>/lib/arm64`,**不可能包含 "tmp"**。
**影响**:A2-06 **永远不会触发**,是死代码。
**修复方向**:改用 `ApplicationInfo.FLAG_EXTERNAL_STORAGE` + 检查 `classes.dex` 文件数量 + 扫描 `/data/data/<pkg>/` 下的 `.dex`/`.so` 文件(需要 root 或自身的私有目录)。
#### P1-5:A2-05 标题"签名异常"但实际检测的是 FLAG_DEBUGGABLE
**文件**:AppModule.kt:206-220
**问题**:检测项 ID 为 A2-05,标题"签名异常",但实际逻辑是检查 `FLAG_DEBUGGABLE`。这是两个完全不同的检测维度。代码注释也承认"在 NO_ROOT 下只能检查 FLAG_DEBUGGABLE"。
**影响**:用户看到"签名异常"的标题会误解检测结果。FLAG_DEBUGGABLE 和签名异常是不同的问题。
**修复方向**:拆分为两个检测项:A2-05a(可调试应用)和 A2-05b(签名异常,需 ROOT 或通过 PackageManager.getPackageInfo + GET_SIGNATURES 检查自签名)。或者直接改标题为"可调试应用"。
#### P1-6:RunMode 硬编码为 NO_ROOT,ADB/ROOT 模式形同虚设
**文件**:MainActivity.kt:60
**问题**:`container.scannerEngine.scan(RunMode.NO_ROOT)` 硬编码。RunMode 定义了 `canReadSystemPartition()` 和 `canListAllProcesses()`,但只有 `canReadSystemPartition()` 被 A3 使用,且永远返回 false。`canListAllProcesses()` **从未被任何模块调用**。
**影响**:ADB 和 ROOT 模式完全不可用。即使用户通过 ADB 运行,也不会获得额外检测能力。
**修复方向**:1) 在 ScanScreen 添加模式选择器;2) A5-07/A7-08 的 /proc 遍历在 ADB/ROOT 下可读取更多进程信息;3) A3 的 system 分区检查在 ROOT 下启用。
#### P1-7:检测模块同步阻塞,A2 扫描数百应用会卡住
**文件**:ScannerEngine.kt:43
**问题**:`module.scan(scanContext)` 是同步调用。A2 AppModule 遍历所有已安装应用(通常 100-300 个),每个应用调用 `getLaunchIntentForPackage()`、`getPackageInfo(GET_PERMISSIONS)` 等 IPC,可能耗时数秒。在此期间 Flow 被阻塞,UI 不更新。
**影响**:扫描过程中 UI 可能出现卡顿(虽然 Flow 在协程中,但 `scan()` 内部的耗时操作不 yield)。
**修复方向**:在 ScannerEngine 中为每个模块调用包裹 `withContext(Dispatchers.IO)`,或在模块内部定期 `yield()`。
#### P1-8:重复的 /proc 遍历和重复的 getInstalledApplications 调用
**问题**:
- A5-07 `checkBackgroundProcesses()` 遍历 /proc 匹配 IoC 进程
- A7-08 `checkIocProcessNetwork()` 再次遍历 /proc 匹配 IoC 进程(完全相同的逻辑)
- A2 `getInstalledPackages()` 获取全部包
- A3-05 `getInstalledApplications(0)` 获取全部应用
- A3-06 `getInstalledApplications(0)` 再次获取全部应用
- A3-08 `getInstalledApplications(0)` 第三次获取全部应用
**影响**:每次扫描执行 3-4 次完整的包列表 IPC 调用 + 2 次完整 /proc 遍历。在 200+ 应用的设备上可能额外耗时 2-3 秒。
**修复方向**:在 ScanContext 中缓存 `getInstalledPackages()` 结果;A5-07 和 A7-08 合并为一次 /proc 遍历,结果共享。
#### P1-9:进度条使用 hashCode 取模,会倒退
**文件**:MainActivity.kt:79-80
**问题**:`progress()` 使用 `(moduleId.hashCode() % total).toFloat() / total` 计算进度。不同模块 ID 的 hashCode 是固定的但无序的,例如 `"A5".hashCode() % 5` 可能是 3,`"A2".hashCode() % 5` 可能是 1。进度条会从 60% 跳到 20%。
**影响**:用户体验差,进度条不可信。
**修复方向**:在 ScannerEngine 中维护模块索引计数器,emit `ModuleStart(moduleIndex, totalModules, ...)`。
---
### P2 — 中(改进建议)
| # | 问题 | 文件 | 描述 |
|---|------|------|------|
| P2-1 | AndroidManifest 使用平台主题 | AndroidManifest.xml:22 | `@android:style/Theme.Material.Light.NoActionBar` 而非 Material3 主题,部分 Compose 组件可能渲染异常 |
| P2-2 | 无报告导出/分享功能 | ResultScreen.kt | 报告只能在内置滚动文本中查看,无法保存或分享 |
| P2-3 | 无扫描历史持久化 | - | 结果是易失的,关闭 App 即丢失,无 Room/SharedPreferences |
| P2-4 | Frida 端口检查不精确 | RootEnvModule.kt:196 | `tcp.contains("69A2")` 可能匹配 IP 地址部分而非端口,应解析后按端口比对 |
| P2-5 | A2-02 隐藏图标误报风险 | AppModule.kt:96 | 很多合法应用(SDK、组件、桌面小部件)无 launcher icon,白名单可能不全 |
| P2-6 | RootEnvModule 未使用 IocMatcher | RootEnvModule.kt:23 | 是唯一不接收 IocMatcher 的模块,可利用 IoC 文件路径增强 Root 检测 |
| P2-7 | 未启用 ProGuard/R8 | build.gradle.kts:22 | `isMinifyEnabled = false`,APK 体积大且代码未优化 |
| P2-8 | A3-08 VPN 检测逻辑混乱 | PersistenceModule.kt:251-262 | `vpnApps` 映射为 boolean 列表再 count,逻辑可读性差,且 `svc.permission` 检查方式不标准 |
---
## 第二部分:六位专家详细评审
### 专家一:安全研究员(威胁检测有效性)
**评分:7.5 / 10**
**正面评价:**
- IoC 匹配器设计优秀,支持精确 IP、CIDR 网段、域名通配符、进程名、APK 包名、文件路径六维匹配
- 6 个僵尸网络家族 IoC 全量加载并实际被 4 个模块调用 20+ 次,这是 v1 到 v2 最大的进步
- A7 网络审计模块的 C2 IP 命中检测(A7-01)是核心价值——直接证明设备正在与已知 C2 通信
- A7-08 IoC 进程的网络活动关联检测很有价值,将进程身份与网络行为关联
**问题:**
- A7-07 DNS 检测形同虚设(P1-2),DNS 查询走 UDP 不在 /proc/net/tcp 中
- 缺少 DNS 解析层检测——无法检测设备是否正在解析 DGA 域名(IocMatcher 有 DGA 正则但无人调用 `matchDomain()`)
- A5-05 CPU 检测方法错误(P0-4),无法检测突发负载
- IoC 数据是静态的,没有在线情报更新机制(设计文档中提到了 VirusTotal/AbuseIPDB 但未实现)
- 缺少 SSL 证书 pinning 检测——部分 C2 使用自签名证书,可通过 `/proc/net/tcp` + 端口 443 + IP 匹配推断
**建议:**
1. 读取 `/proc/net/udp` 和 `/proc/net/udp6` 补充 UDP 连接检测
2. 实现 `matchDomain()` 的调用路径——通过 `ConnectivityManager.getLinkProperties()` 获取 DNS 服务器列表,检查是否指向已知恶意 DNS
3. 添加 `readProcLoadavg()` 方法到 ScanContext,替换 /proc/stat 单次采样
---
### 专家二:Android 底层工程师(系统 API 准确性)
**评分:6.5 / 10**
**正面评价:**
- `/proc/net/tcp` 解析逻辑正确:hex 小端 IP 地址转换、端口 hex 解析、state 码识别
- `/proc` 遍历读取 `cmdline` 的方式正确,`trim('\u0000')` 处理空字节分隔符
- `Settings.Global.getInt()` / `Settings.Secure.getString()` 使用方式正确
- `AppOpsManager.unsafeCheckOpNoThrow()` 用于检查 PACKAGE_USAGE_STATS 权限是正确做法
- SystemProperties 通过反射访问是合理的 fallback
**问题:**
- `getRunningServices()` 自 API 26 限制为自身应用(P0-1)——这是最严重的 API 误用
- `INSTALL_NON_MARKET_APPS` 自 API 17 废弃(P0-2)——在 minSdk=26 的项目中使用已废弃 API
- `canDrawOverlays()` 是全局检查不是 per-app(P0-3)——API 语义理解错误
- `getInstallerPackageName()` 在 API 30+ 部分受限,代码有 fallback 到 `getInstallSourceInfo()`,但 `getInstallSourceInfo()` 也需要 `QUERY_ALL_PACKAGES` 权限
- `getInstalledPackages(GET_PERMISSIONS or GET_SERVICES)` 在 200+ 应用的设备上内存消耗大,可能触发 `TransactionTooLargeException`
- `unsafeCheckOpNoThrow()` 在 API 29+ 才可用,但 minSdk=26,需要降级为 `checkOpNoThrow()`(deprecated 但可用)
**建议:**
1. 全面排查所有 API 的 `@since` / `@Deprecated` 注解
2. `unsafeCheckOpNoThrow()` 替换为 `checkOpNoThrow()` 并添加 `@Suppress("DEPRECATION")`
3. `getInstalledPackages` 分批获取或添加 try-catch 处理 `TransactionTooLargeException`
4. `getRunningServices()` 替换方案:遍历 `/proc/<pid>/cmdline` + 读取 `/proc/<pid>/stat` 中的 `State` 字段判断是否为后台进程
---
### 专家三:恶意软件分析师(检测覆盖度)
**评分:8.0 / 10**
**正面评价:**
- 6 家族 IoC(Kimwolf/Vo1d/BTMOB/Rafel/Anubis/Cerberus)覆盖了 Android 僵尸网络的主要威胁
- 检测维度全面:行为 → 应用 → 持久化 → 网络 → Root 环境,5 层纵深
- A7-01 C2 IP 命中 + A7-08 IoC 进程网络活动 = 可直接判定设备为僵尸节点
- A3-09 文件路径 IoC 检测能直接发现 Vo1d 的 `/system/bin/vo1d` 和 Kimwolf 的 `/data/local/tmp/kwd`
- 白名单机制完善(系统应用 50+、无图标应用 25+、高耗电应用 30+),有效降低误报
**问题:**
- DGA 域名检测(IocMatcher.matchDomain)实现了但**无人调用**——Anubis 和 Cerberus 都使用 DGA
- 缺少 SMS 恢发检测——BTMOB 和 Anubis 通过 SMS 传播,可检测发送大量短信的应用
- 缺少辅助功能滥用深度检测——Anubis/Cerberus 核心能力依赖 AccessibilityService,A3-02 仅检查是否启用但未检查具体行为
- A6-05 Xposed 检测的堆栈检查方法(主动抛异常检查 stackTrace)在现代 LSPosed 上可能失效(LSPosed 使用 Riru/Zygisk 注入,不在 Java 堆栈中留痕)
- 缺少 Magisk Hide / Zygisk 检测——现代 Root 工具会隐藏自身,`/sbin/.magisk` 检测可能被绕过
**建议:**
1. 在 NetworkModule 或新模块中实现 DNS 层检测,调用 `matchDomain()`
2. 添加 `SmsManager` 检测——检查是否有应用在短时间内发送大量短信(通过 `ContentResolver` 查询 `content://sms/sent`)
3. A6-05 增加 native 层检测——读取 `/proc/self/maps` 检查 `libxposed` 或 `libriru` 的内存映射
---
### 专家四:软件架构师(代码质量与可维护性)
**评分:8.0 / 10**
**正面评价:**
- 单模块架构大幅简化——从 v1 的 12 个 Gradle 模块 + Hilt DI 降为 1 个模块 + 手动 DI,编译速度和可维护性显著提升
- `AppContainer` 手动 DI 干净简洁,依赖关系一目了然
- `ScanModule` 接口设计合理,新模块只需实现 3 个属性 + 1 个方法
- `IocMatcher` 单例 + companion object get() 是 Android 上标准的惰性初始化模式
- `ScannerEngine` 使用 Flow 推送进度,UI 层可响应式更新
- 数据模型设计清晰:Finding → ScanResult → RiskScore 层次分明
- `ReportGenerator` 支持文本和 JSON 双格式,JSON 格式结构完整
**问题:**
- **无协程支持**:所有 `scan()` 方法是同步的,`ScannerEngine` 在 Flow 内同步调用,长时间操作会阻塞
- **无缓存**:`getInstalledApplications()` 被调用 3-4 次,/proc 遍历 2 次,应在 `ScanContext` 中缓存
- **错误处理薄弱**:ScannerEngine 吞掉所有异常(P0-5),模块内也有大量空 catch 块
- **ScanContext 职责膨胀**:既有系统服务引用又有 /proc 读取方法,混合了"上下文"和"工具方法"两个职责
- **RootEnvModule 不接收 IocMatcher**:与其他 4 个模块不一致,破坏了统一的依赖注入模式
- **IocMatcher 内部状态可变**:`cidrBlocks`、`suspiciousPorts` 等用 `mutableListOf`/`mutableSetOf`,虽然在 init 后只读,但类型签名不表达不可变性
**建议:**
1. 将 `ScanModule.scan()` 改为 `suspend fun scan(ctx: ScanContext): List<Finding>`,内部可 `withContext(Dispatchers.IO)`
2. 在 ScanContext 添加 `lazy { packageManager.getInstalledPackages(...) }` 缓存
3. 将 /proc 读取方法提取到 `ProcReader` 工具类
4. RootEnvModule 接收 IocMatcher 参数,统一 DI 模式
5. IocMatcher 内部用 `toList()` / `toSet()` 在 init 结束时转为不可变
---
### 专家五:质量保障工程师(测试与可构建性)
**评分:5.5 / 10**
**正面评价:**
- 代码风格统一,Kotlin 惯用法使用得当
- 每个 Finding 都有 `id`/`moduleId`/`attAckId`/`confidence`/`evidence`,可追溯性好
- `IocMatcher.stats()` 提供调试统计,方便验证 IoC 数据加载
- ReportGenerator 的 JSON 输出格式完整,包含所有字段
**问题:**
- **零测试**(P0-7)——最严重的问题。没有任何单元测试或 instrumented test
- **无 Gradle Wrapper**(P0-6)——项目无法开箱构建
- **无 CI/CD 配置**——v1 有 `.github/workflows/ci.yml`,v2 没有任何 CI 配置
- **无 lint 配置**——v1 有 `detekt.yml`,v2 无静态分析配置
- **无 proguard 规则**——`proguard-rules.pro` 是空文件,虽然 `isMinifyEnabled = false` 暂时无影响
- **错误处理不可观测**——所有 catch 块都是空的 `catch (e: Exception) { }`,无法诊断问题
- **无日志**——没有任何 `Log.d/w/e` 调用,调试困难
**建议:**
1. 生成 Gradle Wrapper:`gradle wrapper --gradle-version 8.7`
2. 补充核心测试:
- `RiskScoreTest`:空列表 / 全 SAFE / CRITICAL 命中 / IoC 加分 / 链加分 / 上限 100
- `IocMatcherTest`:精确 IP / CIDR / 域名 / 进程名 / 白名单
- `NetworkModuleTest`:hex 地址解析 / 状态码过滤 / 空文件处理
3. 添加 CI 配置(至少 `./gradlew test`)
4. 在关键 catch 块添加 `Log.w(TAG, "...", e)`
5. 添加 `detekt` 或 `ktlint` 静态分析
---
### 专家六:用户体验设计师(交互与可用性)
**评分:8.0 / 10**
**正面评价:**
- ScanScreen 设计简洁:图标 + 标题 + IoC 统计 + 扫描按钮 + 模块列表
- 扫描中有进度条和当前模块名
- ResultScreen 层次清晰:风险评分卡 → 统计摘要 → IoC 命中卡 → 检测发现列表 → 完整报告
- FindingCard 设计好:风险色边框 + 标题 + 描述 + IoC 标记 + ATT&CK ID + 证据 + 建议
- 空结果有友好的绿色"✓ 未发现异常"卡片
- 底部标注"本地离线运行 · 无需联网"建立用户信任
**问题:**
- 进度条不可信(P1-9)——会倒退
- 无报告导出/分享功能(P2-2)——用户无法保存结果
- 无扫描历史(P2-3)——无法对比多次扫描
- 无权限引导——`PACKAGE_USAGE_STATS` 需要用户手动在设置中授予,但 UI 无引导
- 无模式选择——用户无法选择 ADB/ROOT 模式(P1-6)
- IoC 统计信息直接展示原始文本,普通用户难以理解
- 检测发现列表无筛选/搜索——如果 20+ 项发现,滚动很长
**建议:**
1. 修复进度条为顺序递增
2. 添加"分享报告"按钮(`Intent.ACTION_SEND`)
3. 添加"导出 JSON"按钮(写入 `getExternalFilesDir()`)
4. 在首次扫描前检查 `PACKAGE_USAGE_STATS` 权限,引导用户到设置页
5. 添加模式选择器(NO_ROOT / ADB / ROOT)
6. 检测发现列表按风险等级分组,支持折叠
---
## 第三部分:检测能力矩阵
| 检测项 | 模块 | 无Root有效 | IoC接入 | API正确 | 综合评价 |
|--------|------|-----------|---------|---------|---------|
| A5-01 ADB 调试 | 行为 | ✅ | — | ✅ | 正常 |
| A5-02 无线 ADB | 行为 | ✅ | — | ✅ | 正常 |
| A5-03 开发者选项 | 行为 | ✅ | — | ✅ | 正常 |
| A5-04 未知来源 | 行为 | ✅ | — | ❌ 废弃API | **需修复** |
| A5-05 CPU 负载 | 行为 | ✅ | — | ❌ 方法错误 | **需修复** |
| A5-06 电池温度 | 行为 | ✅ | — | ✅ | 正常 |
| A5-07 后台进程 | 行为 | ✅ | ✅ matchProcess | ✅ | 正常 |
| A5-08 屏幕锁 | 行为 | ✅ | — | ✅ | 正常 |
| A2-01 IoC 包名 | 应用 | ✅ | ✅ matchPackage | ✅ | 正常 |
| A2-02 隐藏图标 | 应用 | ✅ | ✅ isNoIconApp | ✅ | 误报风险 |
| A2-03 伪装系统 | 应用 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A2-04 危险权限 | 应用 | ✅ | — | ✅ | 正常 |
| A2-05 签名/调试 | 应用 | ✅ | — | ⚠️ 标题不符 | 需修正 |
| A2-06 动态加载 | 应用 | ✅ | — | ❌ 死代码 | **需修复** |
| A2-07 多进程 | 应用 | ✅ | — | ✅ | 正常 |
| A2-08 非官方来源 | 应用 | ✅ | — | ✅ | 正常 |
| A2-09 后台服务 | 应用 | ✅ | — | ✅ | 正常 |
| A3-01 设备管理器 | 持久化 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A3-02 无障碍服务 | 持久化 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A3-03 开机自启 | 持久化 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A3-04 前台服务 | 持久化 | ❌ | ✅ isSystemApp | ❌ API限制 | **需修复** |
| A3-05 电池白名单 | 持久化 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A3-06 覆盖窗口 | 持久化 | ✅ | ✅ isSystemApp | ❌ 逻辑错误 | **需修复** |
| A3-07 通知监听 | 持久化 | ✅ | ✅ isSystemApp | ✅ | 正常 |
| A3-08 VPN 服务 | 持久化 | ✅ | ✅ isSystemApp | ⚠️ 逻辑混乱 | 可优化 |
| A3-09 IoC 文件 | 持久化 | ✅ | ✅ matchFile | ✅ | 正常 |
| A3-10 tmp 残留 | 持久化 | ✅ | — | ✅ | 正常 |
| A3-11 system 分区 | 持久化 | ROOT only | ✅ matchFile | ✅ | 正常 |
| A3-12 init.d | 持久化 | ROOT only | — | ✅ | 正常 |
| A7-01 C2 IP 连接 | 网络 | ✅ | ✅ matchC2Ip | ✅ | **核心价值** |
| A7-02 可疑端口 | 网络 | ✅ | ✅ getSuspiciousPorts | ✅ | 正常 |
| A7-03 Tor/代理 | 网络 | ✅ | — | ✅ | 正常 |
| A7-04 异常外连 | 网络 | ✅ | — | ⚠️ 过滤不全 | 需修复 |
| A7-05 系统代理 | 网络 | ✅ | — | ✅ | 正常 |
| A7-06 VPN 活跃 | 网络 | ✅ | — | ✅ | 正常 |
| A7-07 DNS 异常 | 网络 | ✅ | — | ❌ TCP/UDP | **需修复** |
| A7-08 IoC进程网络 | 网络 | ✅ | ✅ matchProcess | ✅ | 正常 |
| A6-01 Root 二进制 | Root | ✅ | — | ✅ | 正常 |
| A6-02 系统签名 | Root | ✅ | — | ✅ | 正常 |
| A6-03 模拟器 | Root | ✅ | — | ✅ | 正常 |
| A6-04 调试器 | Root | ✅ | — | ✅ | 正常 |
| A6-05 Xposed | Root | ✅ | — | ⚠️ 堆栈检测 | 可优化 |
| A6-06 Frida | Root | ✅ | — | ⚠️ 端口检查 | 可优化 |
| A6-07 SELinux | Root | ✅ | — | ✅ | 正常 |
| A6-08 Verified Boot | Root | ✅ | — | ✅ 反射 | 正常 |
**统计:**
- 正常运行:33 项(73%)
- 需修复:7 项(16%)— P0 级 4 项 + P1 级 3 项
- 可优化:5 项(11%)
- 无 Root 有效:43/45 项(96%)— 仅 A3-11/A3-12 需 ROOT
---
## 第四部分:v1 → v2 改进对比
| 维度 | v1 | v2 | 变化 |
|------|----|----|------|
| 文件数 | 73 | 40 | -45% |
| 代码行数 | 4971 | 2821 | -43% |
| Gradle 模块 | 12 | 1 | -92% |
| DI 方式 | Hilt (注解处理) | 手动 AppContainer | 零编译开销 |
| IoC 调用次数 | 0 | 20+ | 从无到有 |
| 网络检测 | 无 | 8 项 | 从无到有 |
| 无Root有效率 | 65% | 96% | +31pp |
| 检测项总数 | 37 | 45 | +22% |
| 拼写 bug | 6 处 | 0 | 全部修复 |
| 测试文件 | 3 | 0 | **退步** |
| CI/CD | 有 | 无 | **退步** |
| Gradle Wrapper | 有 | 无 | **退步** |
---
## 第五部分:修复路线图
### Phase 2.1 — P0 修复(预计 1 天)
| 优先级 | 任务 | 涉及文件 | 预估 |
|--------|------|---------|------|
| P0-1 | A3-04 替换 getRunningServices() | PersistenceModule.kt | 2h |
| P0-2 | A5-04 替换 INSTALL_NON_MARKET_APPS | BehaviorModule.kt | 1h |
| P0-3 | A3-06 修复 canDrawOverlays 逻辑 | PersistenceModule.kt | 1h |
| P0-4 | A5-05 CPU 改用 /proc/loadavg 或双采样 | BehaviorModule.kt + ScanContext.kt | 2h |
| P0-5 | ScannerEngine emit Error 事件 | ScannerEngine.kt | 0.5h |
| P0-6 | 生成 Gradle Wrapper | 项目根目录 | 0.5h |
| P0-7 | 补充核心测试 | 新建 test/ | 4h |
### Phase 2.2 — P1 修复(预计 1.5 天)
| 优先级 | 任务 | 涉及文件 | 预估 |
|--------|------|---------|------|
| P1-1 | IPv6 地址解析 | NetworkModule.kt | 2h |
| P1-2 | A7-07 改读 /proc/net/udp | NetworkModule.kt + ScanContext.kt | 1h |
| P1-3 | A7-04 完整私有地址过滤 | NetworkModule.kt | 0.5h |
| P1-4 | A2-06 重写动态加载检测 | AppModule.kt | 2h |
| P1-5 | A2-05 拆分或改标题 | AppModule.kt | 0.5h |
| P1-6 | RunMode 选择器 UI | ScanScreen.kt + MainActivity.kt | 2h |
| P1-7 | scan() 改 suspend | ScanModule.kt + 所有模块 + ScannerEngine.kt | 3h |
| P1-8 | ScanContext 缓存包列表 | ScanContext.kt + 各模块 | 2h |
| P1-9 | 进度条修复 | ScannerEngine.kt + MainActivity.kt | 1h |
### Phase 2.3 — P2 优化(预计 1 天)
| 优先级 | 任务 | 预估 |
|--------|------|------|
| P2-1 | Material3 主题 | 1h |
| P2-2 | 报告导出/分享 | 1h |
| P2-3 | 扫描历史 (SharedPreferences) | 2h |
| P2-4 | Frida 端口精确检查 | 0.5h |
| P2-5 | A2-02 白名单扩充 | 1h |
| P2-6 | RootEnvModule 接入 IocMatcher | 1h |
| P2-7 | 启用 ProGuard | 0.5h |
| P2-8 | A3-08 VPN 逻辑重构 | 1h |
---
## 第六部分:总评
### 核心进步
v2 相比 v1 是**架构层面的成功重构**。三个致命问题全部解决:
1. **IoC 真正接入了** — 4 个模块 20+ 次调用,A7-01 C2 IP 命中 + A7-08 IoC 进程网络活动 = 可直接判定僵尸节点
2. **网络层补上了** — A7 NetworkModule 是新增的核心价值模块,/proc/net/tcp 解析正确
3. **无 Root 覆盖率从 65% 提升到 96%** — A5 全部 8 项支持 NO_ROOT,仅 A3-11/A3-12 需 ROOT
代码量减少 43%,架构大幅简化,可维护性显著提升。
### 核心问题
v2 的问题是"**API 选错了**"而非"架构选错了"。7 项 P0 问题中:
- 3 项是 Android API 废弃/限制(getRunningServices / INSTALL_NON_MARKET_APPS / canDrawOverlays)
- 1 项是算法错误(CPU 累积值而非瞬时值)
- 1 项是错误处理缺失
- 2 项是工程基建缺失(Gradle Wrapper / 测试)
这些都是局部修复,不需要重构架构。P0 修完后预计评分可达 8.5+。
### 最终判定
**v2 是可用的 MVP 基础**,但**不可直接发布**。P0 问题修复后可进入真机测试阶段。
> 评分:7.4 / 10
> 建议:修复全部 P0 + 50% P1 后重新评审
---
## 修复状态更新(2026-07-15)
**全部 P0(7项)+ P1(9项)+ P2(8项)修复已完成。** 同时补充了 CI/CD 和代码深化。
### Phase 2.1 — P0 修复 ✅
| 任务 | 状态 | 修复方案 |
|------|------|---------|
| P0-1 A3-04 getRunningServices | ✅ | 改用 /proc/<pid>/cmdline 遍历匹配包名 |
| P0-2 A5-04 INSTALL_NON_MARKET_APPS | ✅ | 替换为 REQUEST_INSTALL_PACKAGES 权限检查 |
| P0-3 A3-06 canDrawOverlays 逻辑 | ✅ | 改用 AppOpsManager.checkOpNoThrow 逐应用检查 |
| P0-4 A5-05 CPU 累积值 | ✅ | 改用 /proc/loadavg + CPU 核心数计算负载率 |
| P0-5 ScannerEngine 不 emit Error | ✅ | catch 中 emit Error + 记录到 ScanResult.errors |
| P0-6 无 Gradle Wrapper | ✅ | 创建 gradlew + gradlew.bat + wrapper.properties |
| P0-7 零测试 | ✅ | 60+ 测试用例(RiskScore/ProcNetParser/IocMatcher/ReportGenerator)|
### Phase 2.2 — P1 修复 ✅
| 任务 | 状态 | 修复方案 |
|------|------|---------|
| P1-1 IPv6 丢弃 | ✅ | ProcNetParser 支持 IPv6 hex 解析 + 零压缩 |
| P1-2 DNS TCP 53 | ✅ | 新增 /proc/net/udp + /proc/net/udp6 解析 |
| P1-3 私有地址过滤 | ✅ | 完整 RFC 1918 + IPv6 私有地址 |
| P1-4 A2-06 死代码 | ✅ | FLAG_EXTERNAL_STORAGE + splitNames 检测 |
| P1-5 A2-05 标题不符 | ✅ | 拆分为 A2-05a 可调试 + A2-05b 自签名 |
| P1-6 RunMode 硬编码 | ✅ | ScanScreen 模式选择器 + 三选一 RadioButton |
| P1-7 scan() 同步阻塞 | ✅ | 全部模块 scan() 改为 suspend + withContext(Dispatchers.IO) |
| P1-8 重复调用 | ✅ | ScanContext 三层缓存(应用/包/proc) |
| P1-9 进度条倒退 | ✅ | moduleIndex/totalModules 顺序递增 |
### Phase 2.3 — P2 优化 ✅
| 任务 | 状态 |
|------|------|
| P2-1 Material3 主题 | ✅ themes.xml + BotGuardTheme Compose 主题 |
| P2-2 报告导出/分享 | ✅ Intent.ACTION_SEND + JSON 导出 |
| P2-3 扫描历史 | ✅ SharedPreferences 持久化 |
| P2-4 Frida 端口精确 | ✅ ProcNetParser 解析后按端口匹配 |
| P2-5 白名单扩充 | ✅ 25→80+ 条,覆盖国产 ROM/输入法/SDK |
| P2-6 RootEnvModule IoC | ✅ 构造函数注入 IocMatcher |
| P2-7 ProGuard/R8 | ✅ isMinifyEnabled=true + 完整规则 |
| P2-8 A3-08 VPN 逻辑 | ✅ 直接收集包名,非 boolean 列表 |
### CI/CD 补充 ✅
- detekt 1.23.6 静态分析 + config/detekt.yml
- GitHub Actions 流水线:lint→test→build→JSON校验
- 4 个 Job 串行依赖链,上传 APK + 测试报告
### 代码深化 ✅
- **A7-09** DNS 服务器篡改检测(获取系统 DNS 列表,检查私有/未知 DNS)
- **A7-10** 非标准 DNS 查询目标检测(分析 /proc/net/udp 的 53 端口连接)
- **A6-09** 运行时 Hook 框架检测(/proc/self/maps 内存映射分析)
- **A6-01 增强** Magisk 模块目录 + Magisk 进程检测
- **A6-05 增强** /proc/self/maps 检查 libxposed/libriru/libzygisk 注入
### 当前检测能力
- **47 项检测**(原 45 + A7-09/A7-10 + A6-09,减去合并项)
- **5 个检测模块**
- **无 Root 有效:45/47 项(96%)**
- **评分(修复后预估):8.5+ / 10**