九彩盒子-九彩固件JCHZ升级提示逻辑分析,以及关闭办法
适用机型:天邑TY1613/Ty1608/新魔百和 CM201-2 / CM211-2(ZG 代工)刷九彩类固件
现象:使用一段时间(数十次开关机)后,开机突然弹出"系统升级"提示界面,遥控器按键全部失灵(连主页键都无反应),强制卡死该界面
参考:《九彩固件升级提示破解》(恩山)、《九彩固件升级提示揭秘》(吾爱)、《九彩盒子固件升级提示关闭思路》(吾爱),完整链接见文末"参考链接"
本文在上述帖子的基础上,对两个"定时炸弹" APK 做了完整的逆向分析,并给出无需反编译的 ADB 关闭办法
一、元凶定位(实测证据)
弹窗出现时开启 ADB(盒子与电脑同网段):
adb connect 盒子IP:5555
adb shell dumpsys activity activities | findstr mResumedActivity
实测输出:
mResumedActivity: ActivityRecord{... u0 com.ex.service/.Txtactivity t500}
- 当前焦点被
com.ex.service/.Txtactivity 劫持,这就是"升级提示"白屏界面
- 该 Activity 来自
/system/priv-app/SystemManager.apk
- dumpsys 中还能看到另一颗"潜伏弹":
com.WiFi.dete(来自 /system/priv-app/WiFidetection/WiFidetection.apk)
⚠️ 注意:SystemManager 的包名因固件版本而异——恩山帖实测为 com.exs.service,本机实测为 com.ex.service;WiFi 检测包名恩山帖为 com.WiFi.connect,本机为 com.WiFi.dete。操作前务必以自己 dumpsys 的输出为准,可用下面命令确认:
adb shell pm list packages | findstr /i "ex. wifi."
二、逆向分析:两颗"定时炸弹"的完整触发链
反编译两个 APK(jadx/NP管理器转 Java 查看)后发现,两个 APK 是同一套代码的复刻,仅阈值不同:
| 对比项 |
SystemManager.apk |
WiFidetection.apk |
| 包名 |
com.ex.service(另有固件版本为 com.exs.service) |
com.WiFi.dete(另有固件版本为 com.WiFi.connect) |
| 安装路径 |
/system/priv-app/SystemManager.apk 和 /system/app/SystemManager/SystemManager.apk(两份!) |
/system/priv-app/WiFidetection/WiFidetection.apk |
| 计数存储 |
SharedPreferences boot_count 的 count 键 |
完全相同 |
| 触发阈值 |
开机满 70 次(smali 0x46) |
开机满 80 次(smali 0x50) |
| 弹窗界面 |
.Txtactivity |
同 |
关键源码(反编译所得,jadx 转 Java)
① SystemManager.apk —— com/ex/service/BootReceiver.java(计数器,阈值 70):
public class BootReceiver extends BroadcastReceiver {
private final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";
private final String ACTION_BOOT_PROGRESS = "android.intent.action.BOOT_PROGRESS";
private final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
private final String ACTION_MEDIA_EJECT = "android.intent.action.MEDIA_EJECT";
private final String ACTION_MEDIA_MOUNTED = "android.intent.action.MEDIA_MOUNTED";
private final String ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED";
private final String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED";
private final String ACTION_REBOOT = "android.intent.action.REBOOT";
public void onReceive(Context context, Intent intent) {
// 监听 8 种开机/介质挂载相关广播,命中任意一种即计数(此处省略一长串 equals 判断)
if (!"android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
// ...MEDIA_MOUNTED / MEDIA_UNMOUNTED / MEDIA_EJECT / MEDIA_REMOVED /
// LOCKED_BOOT_COMPLETED / BOOT_PROGRESS / REBOOT 依次判断...
}
SharedPreferences sharedPreferences = context.getSharedPreferences("boot_count", 0);
String str = "count";
int i = sharedPreferences.getInt(str, 0) + 1; // 每次开机 +1
Editor edit = sharedPreferences.edit();
edit.putInt(str, i);
edit.apply();
if (i >= 70) { // ← 阈值:开机满 70 次触发
intent = new ComponentName(context, MainActivity.class);
intent.addFlags(268435456); // FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent);
}
}
}
② WiFidetection.apk —— com/WiFi/dete/BootReceiver.java(计数器,阈值 80):
public class BootReceiver extends BroadcastReceiver {
// ... ACTION_* 常量与广播判断部分和上面完全一致(略) ...
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = context.getSharedPreferences("boot_count", 0);
String str = "count";
int i = sharedPreferences.getInt(str, 0) + 1;
Editor edit = sharedPreferences.edit();
edit.putInt(str, i);
edit.apply();
if (i >= 80) { // ← 唯一差异:阈值改为 80
intent = new ComponentName(context, MainActivity.class);
intent.addFlags(268435456);
context.startActivity(intent);
}
}
}
两个 MainActivity / MyService / Txtactivity 的代码则逐字节一致(仅包名不同),触发链源码如下:
// MainActivity.java —— 隐形跳板:拉起服务后立即自杀
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setVisible(false); // 对用户不可见
startService(new ComponentName(this, MyService.class));
finish();
}
}
// MyService.java —— 真正的弹窗发起者
public class MyService extends Service {
public int onStartCommand(Intent intent, int i, int i2) {
intent = new ComponentName(this, Txtactivity.class);
intent.addFlags(268435456);
startActivity(intent); // 弹出"升级提示"界面
return 1; // START_STICKY:被杀也会重启
}
}
完整触发链(两个 APK 相同):
开机广播(BOOT_COMPLETED / LOCKED_BOOT_COMPLETED / MEDIA_MOUNTED / REBOOT 等 8 种)
↓
BootReceiver.onReceive
读取 boot_count → count+1 → 写回(每次开机 +1)
if (count >= 阈值) → 启动 MainActivity
↓
MainActivity(setVisible(false) 隐形,启动完服务立即 finish,吞 HOME 键)
↓ startService()
MyService.onStartCommand
↓ startActivity()
Txtactivity("升级提示"界面,劫持遥控)
遥控失灵的原因——Txtactivity.onKeyDown 明确吞掉了按键:
public boolean onKeyDown(int i, KeyEvent keyEvent) {
if (i == 3) { // KEYCODE_HOME:直接吞掉
return true;
}
// BACK(4) / MENU(82) / 176 返回 false,其余交给父类
return (i == 4 || i == 82 || i == 176) ? false : super.onKeyDown(i, keyEvent);
}
关键代码位置:
com/ex/service/BootReceiver.java 第 44 行:if (i >= 70)
com/WiFi/dete/BootReceiver.java 第 44 行:if (i >= 80)
这解释了恩山帖子作者"改完 SystemManager 重启后杯具依旧"的原因——另一颗 80 次阈值的炸弹还在。两个包必须一起处理。
三、关闭办法
方法 1:ADB 冻结(推荐,免反编译,5 分钟搞定)
无需反编译、无需 root 固件(多数盒子 shell 权限即可),打开弹窗状态下 ADB 依然可用。
💡 下方命令以本机实测包名 com.ex.service / com.WiFi.dete 为例;若你的固件是 com.exs.service / com.WiFi.connect 等其他变体,请替换为实际包名(用第一节的 pm list packages | findstr 命令确认):
adb connect 盒子IP:5555
# ① 干掉当前弹窗(force-stop 杀进程 → 界面消失、遥控恢复)
adb shell am force-stop com.ex.service
# ② 清零计数器(pm clear 删除 /data/data/com.ex.service/,计数归零)
adb shell pm clear com.ex.service
# ③ 冻结两个"定时炸弹"(不再响应开机广播,计数器永久停摆)
adb shell pm disable-user --user 0 com.ex.service
adb shell pm disable-user --user 0 com.WiFi.dete
验证(输出中能看到这两个包即为已禁用):
adb shell pm list packages -d
说明:
disable-user 只是冻结(stopped 状态收不到开机广播),不删文件,随时可用 pm enable 包名 恢复
- 若提示
SecurityException 权限不足,尝试 adb shell su -c "pm disable-user --user 0 包名"
- 这两个包是固件作者埋的增值业务中间件,第三方固件下禁用不影响正常使用;万一以后有功能异常,
pm enable 即可解冻
- 也可以更进一步直接删除(同恩山帖):
adb shell rm /system/priv-app/SystemManager.apk(需 root + remount),但冻结更稳妥可逆
方法 2:反编译修改 smali(治本,需要ROOT权限,可回写固件包)
⚠️ 注意:jadx / NP管理器"转JAVA"得到的 Java 源码只能看不能回编译,修改必须针对 apktool 反编译出的 smali:
apktool d SystemManager.apk -o SM_smali
apktool d WiFidetection.apk -o WF_smali
定位修改点(以 SystemManager 为例,路径 smali/com/ex/service/BootReceiver.smali):
# 原代码:const/16 v3, 0x46 (0x46 = 70)
# 改为: const/16 v3, 0x2710 (0x2710 = 10000,约 27 年)
- 推荐只改数值:阈值
0x46→0x2710(SystemManager)、0x50→0x2710(WiFidetection),不动跳转指令,避免 smali 条件跳转"反逻辑"的坑(恩山帖作者也是改 if-lt→if-ne + 阈值置 0,但吐槽了 smali 逻辑要以转 Java 后为准)
- 也可以学评论区思路把阈值改成极大值,或干脆删掉整个 APK
回编译 → 签名 → 推回(注意 SystemManager 有两份,都要换):
apktool b SM_smali -o SystemManager_fixed.apk
apktool b WF_smali -o WiFidetection_fixed.apk
# 用 uber-apk-signer 签名后:
adb shell su -c "mount -o remount,rw /system"
adb push SystemManager_fixed.apk /system/priv-app/SystemManager.apk
adb push SystemManager_fixed.apk /system/app/SystemManager/SystemManager.apk
adb push WiFidetection_fixed.apk /system/priv-app/WiFidetection/WiFidetection.apk
adb shell su -c "chmod 644 /system/priv-app/SystemManager.apk /system/app/SystemManager/SystemManager.apk /system/priv-app/WiFidetection/WiFidetection.apk"
adb reboot
替换后记得 pm clear com.ex.service / pm clear com.WiFi.dete 清掉已累计的计数。
四、总结
| 要点 |
内容 |
| 元凶 |
SystemManager.apk(70 次,包名 com.ex.service / com.exs.service)+ WiFidetection.apk(80 次,包名 com.WiFi.dete / com.WiFi.connect),两个都要处理 |
| 机制 |
每次开机广播计数 +1,达标后隐形 Activity 启动"升级提示"界面并吞掉遥控按键 |
| 最快关闭 |
pm clear 清计数 + pm disable-user 冻结,ADB 三条命令搞定 |
| 治本 |
apktool 改 smali 阈值 0x46/0x50 → 0x2710,回编译替换(priv-app 和 app 两处) |
参考链接
- 《九彩固件升级提示破解》 —— 恩山无线论坛
https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=8347567&fromuid=302888
- 《九彩固件升级提示揭秘》 —— 吾爱破解论坛
https://www.52pojie.cn/thread-1883700-1-1.html
- 《九彩盒子固件升级提示关闭思路》 —— 吾爱破解论坛
https://www.52pojie.cn/thread-1975588-1-1.html
(出处:恩山无线论坛、吾爱破解论坛;本文含个人实测逆向分析)