Apk静默安装之调用系统隐藏API

文章目录

    • 0x00 问题
    • 0x01 调用系统隐藏API
    • 0x02 SystemHideAPI
      • SystemHideAPI.java
      • Android.mk
      • Build
      • classes.jar
    • 0x03 总结
    • 0xFF 参考

0x00 问题

何为隐藏的API?

简单来说就是在源码中被用 @hide 标记的代码块,比如常量,方法等。以常用的 ActivityManager.java 为例,里面就有大量的隐藏API:

@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {
...
     /**
     * System property to enable task snapshots.
     * @hide
     */
    public final static boolean ENABLE_TASK_SNAPSHOTS;
...
    /**
     * Return the default limit on the number of recents that an app can make.
     * @hide
     */
    static public int getDefaultAppRecentsLimitStatic() {
        return getMaxRecentTasksStatic() / 6;
    }
...
}

以上代码来自 AOSP 源码,我们下载的 Android SDK 源码中是找不到这些代码的。

而用 Android Studio 进行系统App的开发的我们,有时候难免会调用到系统隐藏的API,那我们怎么办呢?

0x01 调用系统隐藏API

如果你之前做过功课,你会发现网上有很多通过反射来调用系统API的例子,既然反射能调到,说明这些方法在我们的运行环境中是存在的。那这些方法从哪里加载的呢?这就要提到设备中的一个jar:/system/framework/framework.jar,他和我们 SDK 中加载的 android.jar 的区别就是他里面包含了这些隐藏的API。到这里我们就得出两种调用系统隐藏API的方法:

  • 反射

    这个最常见了,如:

    public static boolean silentInstall(PackageManager packageManager, String apkPath) {
        Class<?> pmClz = packageManager.getClass();
        try {
            Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);
            method.setAccessible(true);
            method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    
  • 替换 SDK 中的 android.jar

    这个就厉害了,反射一次只可以反射一个方法,这种方式所有系统的隐藏API都可以直接调用到。Github上有个一个项目 android-hidden-api ,在这里我们可以下载别人收集的带隐藏API的 android.jar

个人不是很喜欢反射,就研究了下第二种方法,但是很遗憾,我没成功(可能是因为我的build工具版本比较高)。那么还有其他方法吗?

由于我们有系统的源码,因此我们可否从源码入手?简单总结了一个思路:创建一个项目,在这个项目中对要调用的系统隐藏API做一些封装,然后在AOSP中编译生成jar包,最后将这个jar包拷贝到 Android Studio 中使用。下面以App的静默安装为例,说明此思路的具体实现。

0x02 SystemHideAPI

这里有一个前提,系统隐藏API可以通过各种方法调到,但是能不能调成功却是另外一回事,因为有些隐藏API不是普通权限的App可以调用的。若要升级你的App为系统App,可以按照下面几步操作:

  • AndroidManifest.xml 中添加

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.your.pkg"
    android:sharedUserId="android.uid.system">
    ...
    manifest>
    
  • 系统签名
    使用系统密钥给App签名。一般位于: build/target/product/security/ 中。 需要signapk.jar,命令如下:
    java -jar signapk.jar platform.x509.pem platform.pk8 in.apk out.apk

  • 安装
    直接推到 /system/app/ 或者 /system/priv-app/下面,重启即可。

下面回到我们App静默安装的例子。

bash-3.2$ tree
vendor
|-- ...
|-- SystemHideAPI
|   |-- Android.mk
|   `-- com
|       `-- your
|           `-- pkg
|               `-- utils
|                   `-- SystemHideAPI.java

SystemHideAPI.java

public class SystemHideAPI {
    /**
     * Silent install
     * @param pm
     * @param apkPath
     * @return
     */
    public static boolean silentInstall(PackageManager pm, String apkPath, OnInstallResultListener listener) {
        if(null == pm || TextUtils.isEmpty(apkPath)){
            return false;
        }
        pm.installPackage(Uri.fromFile(new File(apkPath)), new PackageInstallObserver() {
            @Override
            public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) {
                if (null != listener) {
                    listener.onResult(basePackageName, returnCode, msg);
                }
            }
        }, PackageManager.INSTALL_REPLACE_EXISTING, "");
        return true;
    }

    /**
     * App install callback
     */
    public interface OnInstallResultListener {
        /**
         * App install callback
         *
         * @param pkg
         * @param returnCode
         * @param msg
         */
        void onResult(String pkg, int returnCode, String msg);
    }
}

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := SystemHideAPI
LOCAL_MODULE_TAGS := optional
LOCAL_JACK_ENABLED := disabled

include $(BUILD_STATIC_JAVA_LIBRARY)

Build

mma

classes.jar

生成的jar文件位于下面目录:

out/target/common/obj/JAVA_LIBRARIES/SystemAPI_intermediates/

➜  SystemHideAPI_intermediates tree -L 1
.
├── anno
├── classes
├── classes-full-debug.jar
├── classes.jar
└── link_type

2 directories, 3 files
➜  SystemHideAPI_intermediates

最后,将生成的 classes.jar 拷贝到Android Studio项目,使用 SystemHideAPI 中的方法,即可正常编译,运行了。

0x03 总结

相比反射,这种方式的灵活性更大,如上面静默安装的例子,如果我想监听安装的回调,还需要去反射 PackageInstallObserver 这个类(这个类也是hide)。如果还需要其他隐藏的API,只需要增加响应的方法即可。给我们在AndroidStuido 中调用系统隐藏API带来很大方便

0xFF 参考

  1. https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces?hl=zh-cn
  2. https://github.com/skylot/jadx/releases
  3. https://github.com/anggrayudi/android-hidden-api
  4. https://blog.csdn.net/pshiping2014/article/details/79549680
  5. https://stackoverflow.com/questions/17035271/what-does-hide-mean-in-the-android-source-code

你可能感兴趣的:(android,AOSP)