Skip to content
HoerirePublic

About

自由启停(Guard)· 前台应用触发式禁用工具

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

62 Commits

Folders and files

Repository files navigation

Guard

Android 应用冻结/启停工具,基于 root 权限实现事件驱动冻结、进程级隐藏与残留清理。

核心架构

两段式架构:Java 应用负责配置、授权与部署,原生 Guard 进程专职「前台监听 + 系统级禁用」,职责清晰、互不干扰。另有脚本管理模块,支持以 root 权限运行 SH 脚本与编译后的二进制文件。

事件驱动冻结

Guard 的核心设计哲学是事件驱动——不轮询、不常驻、不浪费。通过系统事件日志感知前台应用变化,按需处理,空闲时进程开销趋近于零。

为什么事件驱动更优

传统冻结方案存在根本缺陷:要么定时轮询浪费资源,要么依赖系统服务暴露自身。Guard 的事件驱动架构从根本上规避了这些问题:

方案 原理 致命缺陷
定时轮询 每隔 N 秒检查前台应用 持续占用 CPU,轮询间隔内冻结不生效,频繁唤醒耗电
AccessibilityService 监听无障碍事件 需特殊权限,系统设置中可见,容易被检测,部分 ROM 限制后台运行
后台常驻 Service 持续运行检测逻辑 需前台通知(Android 8+),内存常驻,被系统杀死后冻结失效
Guard 事件驱动 logcat 事件日志 + poll 阻塞 无上述所有缺陷

事件驱动的三大优势

1. 零开销空闲

Guard 进程用 poll() 阻塞在 logcat 管道上,当没有前台切换事件时,进程完全休眠:

  • CPU 占用:0%(poll 是内核级阻塞,不消耗任何 CPU 时间片)
  • 内存占用:仅 native 进程基础开销(几 MB),无 Java 虚拟机、无 Service 对象
  • 耗电:零(不唤醒 CPU,不触发 wakelock)
  • 无前台通知、无后台 Service、无定时器、无 AlarmManager

2. 毫秒级响应

事件驱动意味着前台切换的瞬间就触发冻结,没有任何轮询延迟:

  • 用户打开目标应用 → 系统 wm_on_resume_called 事件 → Guard poll() 返回 → 提取组件名 → 匹配目标 → 批量 pm disable
  • 全链路耗时 < 1 秒,其中 pm disable 是主要开销
  • 对比定时轮询方案:若轮询间隔 5 秒,最坏情况冻结延迟 5 秒,期间目标应用已可正常运行被冻结的应用

3. 配置即改即生效

事件驱动的热重载机制使配置更新零延迟:

  • 每次事件触发时,Guard 用 stat() 检查配置文件(O(1) 系统调用,仅读 inode 元数据)
  • 若修改时间或文件大小变化 → 热重载全部配置
  • 用户在界面勾选/取消应用 → 写入 config.txt → 下一次事件即可生效
  • 无需重启服务、无需重新部署、无需重新授权

监听机制

  1. 应用以 root 权限拉起原生 Guard 进程,二者通过标准输出管道通信,运行日志实时回传界面
  2. Guard 进程 fork 子进程执行 logcat -b events -v brief -T 1,仅过滤两类窗口事件:
    • wm_on_resume_called:Activity 的 onResume 被调用
    • wm_on_top_resumed_gained_called:窗口获得前台焦点
  3. 父进程用 poll() 阻塞等待管道数据,事件驱动消费,无 busy loop
  4. poll 设置 2 秒超时,用于周期性检查小窗状态是否已退出(见「小窗状态检测」)
  5. 其余事件(*:S)全部静默——logcat 只推送这两类事件给 Guard,I/O 开销极小

前台识别的灵活性

组件名匹配支持三种形态,适配 Android 系统的各种组件命名方式:

组件形态 示例 匹配方式
纯包名 com.target.app 直接比较
包名/类名 com.target.app/.MainActivity 取 / 前部分比较
完整类名 com.target.app/com.target.app.MainActivity 取 / 前部分比较

配合前缀匹配,用户只需配置包名即可,无需关心目标应用的具体 Activity 类名。目标应用更新、更换入口 Activity 都不影响匹配。

包名变体匹配:当配置的目标包名无法精确匹配前台组件时,自动尝试去掉包名最后一段重新匹配。例如配置 bin.mt.plus.canary(金丝雀版),前台组件 bin.mt.plus.MainLightIcon(正式版)也能命中——取 bin.mt.plus 作为公共前缀匹配,适配同一应用不同版本/变体共存的情况。

输入法窗口过滤:组件名包含非 ASCII 字符(如输入法窗口 l.ۡ᩸᩷)或格式异常(少于 2 个点号)时直接跳过,避免输入法弹出被误判为离开目标应用。

幂等保证与状态校验

  • 全局 active 标志保证操作幂等:已禁用时不会重复 pm disable,已恢复时不会重复 pm enable
  • 每次禁用/恢复后回读应用状态(pm list packages -d / -e),命令失败或状态未变输出警告日志
  • Guard 退出时 cleanup() 恢复所有被修改过的应用,确保任何退出方式都不留半禁用状态

小窗状态检测

当目标应用处于小窗(Picture-in-Picture)模式时,用户切到其他应用不应触发恢复——目标仍在小窗中运行,禁用列表应保持生效。

Guard 通过双重来源检测小窗状态:

  1. dumpsys window 中包含 pip 关键字
  2. dumpsys activity activities 中包含 pip 或 picture.in.picture 关键字

任一来源命中即判定为小窗激活。检测时机分两种:

  • 事件触发时:非目标应用进入前台,若检测到小窗激活,保持禁用状态不恢复
  • 轮询超时时:poll 2 秒超时后检查,若之前在小窗中、现已退出小窗,立即触发恢复

空配置处理

配置文件不存在时,Guard 仅创建带注释的空配置(不含预设应用),目标和禁用列表由 UI 勾选后写入。空配置下 Guard 正常启动并监听事件,前台事件输出「等待配置」提示,用户勾选应用后下次事件即触发热重载生效——无需重启服务。

事件驱动效率总结

维度 指标
空闲 CPU 0%(poll 内核阻塞)
空闲内存 ~几 MB(native 进程)
空闲耗电 零(不唤醒 CPU)
响应延迟 < 1 秒(事件即时触发)
配置更新 即改即生效(stat 热重载)
后台痕迹 无 Service、无通知、无定时器
幂等保证 active 标志 + 状态回读校验
退出恢复 cleanup() 恢复所有修改
小窗感知 双来源检测 + 轮询补检
输入法过滤 非 ASCII + 格式校验双重过滤

隐藏设计(脚本管理)

Guard 的隐藏能力集中在脚本管理模块。脚本管理提供轻量终端,以 root 权限执行 SH 脚本和 ELF 二进制,执行完毕后通过两段式进程清理彻底抹除执行痕迹。

脚本管理的隐藏目标

脚本管理的核心设计目标:执行完毕后,系统中不留任何可被检测的进程链。

具体表现为:

  1. 无残留进程链:脚本执行时产生的 su → sh → 脚本/二进制 → 子进程链条,在清理后被完全杀灭。任何进程扫描工具(ps、top、htop)都无法发现脚本执行过的痕迹
  2. 包装层剥离:当用户执行 ELF 二进制时,可选择「保留用户程序」模式——清理时仅杀灭包装层(su、sh、终端进程),用户程序以孤儿进程继续独立运行,与 Guard 无进程树关联
  3. 逃逸进程追杀:通过 setsid、nohup、管道 |、daemon 等方式脱离原进程组的进程,cgroup 扫描会将其找出并杀灭
  4. 日志自管理:所有操作日志存储在应用内,不写入系统 logcat 持久缓冲区,可随时一键清除
  5. pm 方式优势:相比 am force-stop,pm disable 使应用处于 disabled 状态,应用自身无法感知被冻结(不会触发 ANR 检测),解冻后正常启动无副作用

脚本管理功能

  • 通过 root 浏览系统各目录(含 /data 等受限路径),自动识别 ELF 二进制并直接执行,SH 脚本按脚本运行
  • 注入完整环境变量(PATH、HOME、LANG、TERM)
  • 交互式运行窗口实时回显输出,底部输入框可随时向进程发送数字或内容,满足二进制程序的选择/输入需求
  • 输出自动过滤 ANSI 颜色与转义码,避免乱码
  • 轻量终端支持常驻 root shell、逐行输入与直接运行脚本

清理技术实现

脚本或二进制执行完毕后,Guard 执行两段式进程清理,确保无残留:

Phase 1:快速杀(~0.1 秒,杀灭 95% 进程)

根据执行模式选择最快路径:

  • SH 脚本(不保留):kill -9 -PGID 一次内核调用杀灭整个进程组(含子进程、孙进程),这是最快的进程清理方式,仅一次 syscall
  • ELF 二进制(保留用户程序):pgrep -P 精确查找 su/sh 的直接子进程列表,跳过需要保留的二进制 PID,逐个 kill -9

Phase 1 覆盖正常进程树中的绝大多数进程。

Phase 2:cgroup 扫描补杀(~0.3 秒,杀灭逃逸进程)

针对通过 setsid、nohup、管道 |、daemon 等方式脱离原进程组的逃逸进程:

  1. 读取 /proc/self/cgroup 获取当前 cgroup 路径和 UID 段(/uid_XXXXX)
  2. 遍历 /proc/[0-9]*,读取每个进程的 cgroup 文件
  3. 匹配条件:cgroup 路径精确匹配或包含相同 UID 段
  4. 直接 kill -9(SIGKILL),跳过 app 自身、preservePid、已知 PID、$$、$PPID

性能对比

优化点 传统方案 Guard
信号策略 SIGTERM → sleep 1s → SIGKILL 直接 SIGKILL
扫描轮次 两轮(TERM + KILL) 一轮
sleep 有(1 秒) 无
cgroup 解析 awk + sort + head + cut 管道 grep 单命令
总耗时 5-10 秒 ~0.2 秒

清理触发时机

  • 用户点击「清理」按钮:立即输出日志,后台执行两段式清理,关闭弹窗
  • 脚本自然结束:执行线程 finally 块自动触发清理
  • 进程异常退出:finally 块兜底清理,确保任何情况下都不残留

自身禁用

Guard 自身(com.example.guard)可被加入禁用列表。由于守护进程是独立运行的 ELF 二进制(进程名 Guard),pm disable 仅禁用 Guard 的 UI(图标隐藏、界面不可打开),后台守护进程不受影响。用户进入目标应用时,Guard UI 随其他禁用列表应用一同被冻结;离开目标应用后自动恢复。

日志管理

  • 持久化存储:所有运行日志写入应用私有目录 guard.log,下次启动自动加载,查看禁用期间发生的事件
  • 大小限制:日志文件上限 64K,超出时自动截断保留后半部分(按行对齐),避免无限增长
  • 实时尾随:服务运行时启动文件尾随线程,实时读取二进制写入日志文件的新内容并显示
  • 会话分隔:每次启动输出会话分隔标记,便于区分不同会话的日志
  • 静默启动:启动时不重复输出 root 授权和加载应用数量的提示,避免日志冗余
  • 包名显示:勾选应用时日志输出应用名称和包名,浮窗提示也显示包名

下载

从 Releases 页面获取最新 APK。

使用要求

  • Android 设备已 root(Magisk / KernelSU / 其他 root 方案均可)
  • arm64-v8a 架构
  • 需要授予 root 权限

架构

  • 纯 Java 单 Activity 实现,无原生框架依赖
  • 原生组件:guard.c(C 语言),通过 logcat -b events 监听 + poll() 事件驱动 + pm disable/enable 系统级禁用
  • 编译产物:classes.dex + 资源 + guard 二进制(arm64-v8a)
  • 签名:内置证书自签名

License

MIT

About

自由启停(Guard)· 前台应用触发式禁用工具

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages