TCL/雷鸟电视 adb 禁止安装 APK 研究
未完,研究更新中。
探索
型号:
Project Name: 65S515C PRO
Panel: ST6451D02-3
软件版本: V8-T652T03-LF1V186底层平台属于 TCL/雷鸟 系列常见的 T652 平台。
问题:
$ adb shell pm install /sdcard/Download/kodi-21.3-Omega-arm64-v8a.apk
Get install_switch_flag: 0
install apk has be disabled from pm by system default!尝试过的:
adb shell settings put system install_switch_flag 1
adb shell settings put secure install_switch_flag 1
adb shell settings put global install_switch_flag 1
#均正常
adb shell settings get system install_switch_flag
1
adb shell settings get secure install_switch_flag
1
adb shell settings get global install_switch_flag
1经过尝试依旧一样,无果settings put global install_switch_flag 1 确实写成功了settings get global install_switch_flag 返回 1
但是 pm install 仍然打印:
Get install_switch_flag: 0
install apk has be disabled from pm by system default!这说明 pm 根本不是从 Android Settings.Global 读取这个值。
否则它应该打印 1。
也就是说,它调用的是 TCL 自己的配置接口,只是碰巧名字也叫 install_switch_flag。
接着,我尝试了:
mxdyeah@MXD-PC-2
$ adb shell content query --uri content://com.tcl.providers.config
No result found.
mxdyeah@MXD-PC-2
$ adb shell content query --uri content://com.tcl.providers.config/config
No result found.
mxdyeah@MXD-PC-2
$ adb shell content query --uri content://com.tcl.providers.config/settings
No result found.
mxdyeah@MXD-PC-2
$ adb shell content query --uri content://com.tcl.providers.config/global
No result found.
mxdyeah@MXD-PC-2
$ adb shell dumpsys package com.tcl.packageinstaller.service.renew | grep Activity
# 无回显先做些检查:
PS D:\app> adb shell which pm
/system/bin/pm
PS D:\app> adb shell ls -l $(adb shell which pm)
-rwxr-xr-x 1 root shell 34 2009-01-01 00:00 /system/bin/pm
PS D:\app> adb shell pm path android
package:/system/framework/framework-res.apk
PS D:\app> adb shell ls -l /system/framework/services.jar
-rw-r--r-- 1 root root 5035118 2009-01-01 00:00 /system/framework/services.jar
PS D:\app> adb shell ls -l /system/framework/oat
total 4
drwxr-xr-x 2 root root 4096 2009-01-01 00:00 arm确认了,service.jar 在 /system/framework/services.jar
提取出来,反编译,找到了:
private int runInstall() throws RemoteException {
PrintWriter pw = getOutPrintWriter();
InstallParams params = makeInstallParams();
String inPath = getNextArg();
String install_switch_flag = SystemProperties.get("persist.tcl.debug.installapk", "0");
pw.println("Get install_switch_flag: " + install_switch_flag);
if (!"1".equals(install_switch_flag)) {
pw.println("install apk has be disabled from pm by system default!");
return 1;
}
pw.println("install apk has be enabled from pm by user!");
setParamsSize(params, inPath);
int sessionId = doCreateSession(params.sessionParams, params.installerPackageName, params.userId);
if (inPath == null) {
try {
if (params.sessionParams.sizeBytes == -1) {
pw.println("Error: must either specify a package size or an APK file");
return 1;
}
} finally {
if (1 != 0) {
try {
doAbandonSession(sessionId, false);
} catch (Exception e) {
}
}
}
}
if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", false) != 0) {
if (1 != 0) {
try {
doAbandonSession(sessionId, false);
} catch (Exception e2) {
}
}
return 1;
}
if (doCommitSession(sessionId, false) != 0) {
if (1 != 0) {
try {
doAbandonSession(sessionId, false);
} catch (Exception e3) {
}
}
return 1;
}
pw.println("Success");
if (0 != 0) {
try {
doAbandonSession(sessionId, false);
} catch (Exception e4) {
}
}
return 0;
}private int runInstallCreate() throws RemoteException {
PrintWriter pw = getOutPrintWriter();
InstallParams installParams = makeInstallParams();
String install_switch_flag = SystemProperties.get("persist.tcl.debug.installapk", "0");
pw.println("Get install_switch_flag: " + install_switch_flag);
if ("1".equals(install_switch_flag)) {
pw.println("install apk has be enabled from pm by user!");
int sessionId = doCreateSession(installParams.sessionParams, installParams.installerPackageName, installParams.userId);
pw.println("Success: created install session [" + sessionId + "]");
return 0;
}
pw.println("install apk has be disabled from pm by system default!");
return 1;
}
private int runInstallWrite() throws RemoteException {
long sizeBytes = -1;
while (true) {
String opt = getNextOption();
if (opt != null) {
if (opt.equals("-S")) {
sizeBytes = Long.parseLong(getNextArg());
} else {
throw new IllegalArgumentException("Unknown option: " + opt);
}
} else {
int sessionId = Integer.parseInt(getNextArg());
String splitName = getNextArg();
String path = getNextArg();
return doWriteSplit(sessionId, path, sizeBytes, splitName, true);
}
}
}
private int runInstallRemove() throws RemoteException {
PrintWriter pw = getOutPrintWriter();
int sessionId = Integer.parseInt(getNextArg());
String splitName = getNextArg();
if (splitName == null) {
pw.println("Error: split name not specified");
return 1;
}
return doRemoveSplit(sessionId, splitName, true);
}
private int runInstallExisting() throws RemoteException {
PrintWriter pw = getOutPrintWriter();
int userId = 0;
int userId2 = 0;
while (true) {
String opt = getNextOption();
if (opt != null) {
byte b = -1;
int iHashCode = opt.hashCode();
if (iHashCode != -951415743) {
if (iHashCode != 1051781117) {
if (iHashCode != 1333024815) {
if (iHashCode == 1333469547 && opt.equals("--user")) {
b = 0;
}
} else if (opt.equals("--full")) {
b = 3;
}
} else if (opt.equals("--ephemeral")) {
b = 1;
}
} else if (opt.equals("--instant")) {
b = 2;
}
switch (b) {
case 0:
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case 1:
case 2:
int installFlags = userId2 | 2048;
userId2 = installFlags & (-16385);
break;
case 3:
int installFlags2 = userId2 & (-2049);
userId2 = installFlags2 | 16384;
break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
}
} else {
String packageName = getNextArg();
if (packageName != null) {
try {
int res = this.mInterface.installExistingPackageAsUser(packageName, userId, userId2, 0);
if (res == -3) {
throw new PackageManager.NameNotFoundException("Package " + packageName + " doesn't exist");
}
pw.println("Package " + packageName + " installed for user: " + userId);
return 0;
} catch (PackageManager.NameNotFoundException | RemoteException e) {
pw.println(e.toString());
return 1;
}
}
pw.println("Error: package name not specified");
return 1;
}
}
}
private int runSetInstallLocation() throws RemoteException {
String arg = getNextArg();
if (arg == null) {
getErrPrintWriter().println("Error: no install location specified.");
return 1;
}
try {
int loc = Integer.parseInt(arg);
if (!this.mInterface.setInstallLocation(loc)) {
getErrPrintWriter().println("Error: install location has to be a number.");
return 1;
}
return 0;
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: install location has to be a number.");
return 1;
}
}
private int runGetInstallLocation() throws RemoteException {
int loc = this.mInterface.getInstallLocation();
String locStr = "invalid";
if (loc == 0) {
locStr = UiModeManagerService.Shell.NIGHT_MODE_STR_AUTO;
} else if (loc == 1) {
locStr = "internal";
} else if (loc == 2) {
locStr = "external";
}
getOutPrintWriter().println(loc + "[" + locStr + "]");
return 0;
}
public int runMovePackage() throws RemoteException {
String packageName = getNextArg();
if (packageName == null) {
getErrPrintWriter().println("Error: package name not specified");
return 1;
}
String volumeUuid = getNextArg();
if ("internal".equals(volumeUuid)) {
volumeUuid = null;
}
int moveId = this.mInterface.movePackage(packageName, volumeUuid);
int status = this.mInterface.getMoveStatus(moveId);
while (!PackageManager.isMoveStatusFinished(status)) {
SystemClock.sleep(1000L);
status = this.mInterface.getMoveStatus(moveId);
}
if (status == -100) {
getOutPrintWriter().println("Success");
return 0;
}
getErrPrintWriter().println("Failure [" + status + "]");
return 1;
}
public int runMovePrimaryStorage() throws RemoteException {
String volumeUuid = getNextArg();
if ("internal".equals(volumeUuid)) {
volumeUuid = null;
}
int moveId = this.mInterface.movePrimaryStorage(volumeUuid);
int status = this.mInterface.getMoveStatus(moveId);
while (!PackageManager.isMoveStatusFinished(status)) {
SystemClock.sleep(1000L);
status = this.mInterface.getMoveStatus(moveId);
}
if (status == -100) {
getOutPrintWriter().println("Success");
return 0;
}
getErrPrintWriter().println("Failure [" + status + "]");
return 1;
}工厂菜单
探索时,分析了一个包名,意外触发工厂菜单
# 留白一下,终端历史记录好像被我清空了总之,触发后长这样:
TCL/雷鸟电视 adb 禁止安装 APK 研究
https://blog.mxdyeah.com/post/tcl-ban-via-adb-install-apk-analysis
AD: