Here's me.

2018年6月

Android 系统内有一个名为 “service“ 的命令,在终端内输入 service -h,可以得知其用法:

Usage: service [-h|-?]
    service list
    service check SERVICE
    service call SERVICE CODE [i32 INT | s16 STR] ...
Options:
  i32: Write the integer INT into the send parcel.
  s16: Write the UTF-16 string STR into the send parcel.

运行 service list 得到可用列表:
photo_2018-06-08_21-32-20.jpg

我们本需要的运行的是

IPackageManager.setApplicationEnabledSetting(String,int,int,String);

查阅上面的 list,知道了对应的 service 是 package 。

有趣的是, service call SERVICE CODE [i32 INT | s16 STR] 中的 CODE,在不同安卓机器上是不同的,但我们可以通过反射获取到:

@SuppressLint("PrivateApi")
    private static int getCode() {
        Class localObject = Class.forName("android.content.pm.IPackageManager$Stub");
        Field localField = localObject.getDeclaredField("TRANSACTION_setApplicationEnabledSetting");
        return localField.getInt(localObject);
    }
在 Android 9 以上,无法使用非 SDK 接口,所以我们使用另一个奇技淫巧来读取。这里不再赘述。

获取到 CODE 之后即可构造字符串

str = "service call package " + CODE + " s16 " + packageName + " i32 " + (freeze ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER : PackageManager.COMPONENT_ENABLED_STATE_ENABLED);

然后到随便一个是 UID.SHELL 或是 ROOT 权限的进程里跑就行了。

Q&A:
Q:为什么不直接用 “pm disable-user package Name”呢?
A:很慢。

当然了,也可以使用 service 命令做一些奇技淫巧的事情,比如不申请权限就能获取 IMEI,不申请权限就能发送短信等等。但是你的应用得有 ROOT 或是 SHELL 权限。

使用此原理的软件有:小黑屋,冰箱。

这两天在研究安卓应用如何去冻结其他应用.别跟我说用 adb shell pm disable,慢死了.直接使用 packageManager.setApplicationEnabledSetting() 是不可以的,会抛出可爱的异常. 那么应该怎么做呢,看了一下源码,若有所思. setApplicationEnabledSetting() 方法只允许 UID.SHELL/UID.ROOT 或是 UID.SYSTEM 的应用去调用,如果我们的应用 UID 成为了以上这些不就好了吗?

方法一

我们可以在应用程序的 AndroidManifest.xml 中的 manifest 节点中加入android:sharedUserId="android_.uid.shell" 这个属性, 并使用系统签名,应用的UID即可成为 UID.SHELL.(一些应用的提权模式就是这样做的).但是这样做有一些局限性,比如我们必须使用系统签名,否则会安装不上;这样子做之后会有一些安全问题,在这里就不细讲了.总了个之,不建议这样做.

方法二

app_process 源码 有云: "Usage: app_process [java-options] cmd-dir start-class-name [options]\n" 我们可以通过 app_process 启动一个进程,此进程的 UID 可以是 SHELL/ROOT,满足我们的需要. 简单实践一下: 编写一个简单的 Android 程序,包名 gs.https.shell,类名 shell

public class shell {
  public static void main(String[] args) {
    iPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    iPackageManager.setApplicationEnabledSetting("com.example.hhhh", PackageManager.COMPONENT_ENABLED_STATE_DISABLED,0,0,null);
  }
}
IPackageManager 属于 @hide API,我们需要一些奇技淫巧才可以访问,详情.

运行一下子:

pmpath=pm path gs.https.shell
path=${pmpath:8}
CLASSPATH=$path nohup app_process /system/bin gs.https.shell.shell

发现 com.example.hhhh 已经被冻结.

我们当然可以发现这样直接调用 API 冻结应用比申请 ROOT 然后 pm disable 快得多.因此我们应该多使用 Shizuku Manager , 黑阈 , 小黑屋等直接调用 API 的应用,毕竟更快嘛