近期在做微信數(shù)據(jù)備份項目時缀壤,所探索出來的一些“黑科技”,可以實現(xiàn)靜默卸載第三方app纠亚、靜默清除第三方app的數(shù)據(jù)塘慕、靜默降級安裝app等功能
首先這些都需要系統(tǒng)簽名才有這個能力,其次蒂胞,通過反射去調(diào)用系統(tǒng)的隱藏api去實現(xiàn)這些功能图呢。隱藏api,顧名思義骗随,普通情況下肯定是調(diào)用不到的蛤织,所以需要一些特殊的方法去調(diào)用它。
0.反射方法
這里先提供反射的通用方法鸿染,后面示例代碼將從這里調(diào)用指蚜,可做一個util工具類使用。
public class ReflectUtil {
// 通過反射去調(diào)用target類下的實例方法
public static Object callObjectMethod(Object target, String method, Class<?>[] parameterTypes, Object... values)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<? extends Object> clazz = target.getClass();
Method declaredMethod = clazz.getDeclaredMethod(method, parameterTypes);
return declaredMethod.invoke(target, values);
}
//通過反射去拿到實例對象
public static Object callStaticObjectMethod(Class<?> clazz, String method, Class<?>[] parameterTypes, Object... values)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Method declaredMethod = clazz.getDeclaredMethod(method, parameterTypes);
declaredMethod.setAccessible(true);
return declaredMethod.invoke(null, values);
}
}
1.卸載
首先安裝卸載的相關邏輯存在系統(tǒng)的framework層涨椒,路徑為/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java摊鸡,即PMS。
通過翻android源碼(這里推薦一個在線看源碼的網(wǎng)站http://androidxref.com/)蚕冬,可以知道卸載的方法為deletePackageAsUser免猾,如下圖:
可以看到該方法所需參數(shù)有packageName、versionCode播瞳、observer(通過aidl獲取該類掸刊,后續(xù)會提到)、userId赢乓、flags忧侧。
參數(shù)中的IPackageDeleteObserver類,是一個aidl回調(diào)通知接口牌芋,在源碼中找到這個接口:
package android.content.pm;
/**
* API for deletion callbacks from the Package Manager.
*
* {@hide}
*/
oneway interface IPackageDeleteObserver {
void packageDeleted(in String packageName, in int returnCode);
}
將該aidl文件按照以上Package:android.content.pm的層級關系放置于項目源碼中蚓炬,會自動生成class文件供調(diào)用。生成文件如下圖:
成功生成這個之后躺屁,就可以使用反射實現(xiàn)卸載操作了肯夏。直接上代碼:
//卸載操作
try {
// 拿到PackageName對應的PackageInfo
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(yourPackageName, 0);
PackageDeleteObserver observer = new PackageDeleteObserver();
//通過反射拿到IBinder對象
IBinder pgmService = (IBinder) ReflectUtil.callStaticObjectMethod(Class.forName("android.os.ServiceManager"), "getService", new Class[]{String.class}, "package");
//通過反射拿到IPackageManager對象
Object mIPm = ReflectUtil.callStaticObjectMethod(Class.forName("android.content.pm.IPackageManager$Stub"),
"asInterface", new Class[]{IBinder.class}, pgmService);
//通過反射使用IPackageManager接口調(diào)用到PackageManagerService里的deletePackageAsUser方法
//其中后面兩個0,第一個是傳入userId犀暑,一般傳入0即可驯击,不同的系統(tǒng)會有不同的分身userid,第二個是flag耐亏,也是傳入0即可徊都。
ReflectUtil.callObjectMethod(mIPm, "deletePackageAsUser",
new Class[]{String.class, int.class, IPackageDeleteObserver.class, int.class, int.class},
yourPackageName, packageInfo.versionCode, observer, 0, 0);
} catch (Exception e) {
}
//重寫IPackageDeleteObserver的packageDeleted方法,監(jiān)聽應用卸載情況
private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
@Override
public void packageDeleted(String packageName, int returnCode) {
Log.d(TAG, "packageName:"+packageName+" returnCode:"+returnCode);
if (returnCode == 1) {
//returnCode = 1即成功
}
}
}
然后再在AndroidManifest.xml文件里添加兩行權限聲明:
<uses-permission android:name="android.permission.DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
即可靜默卸載第三方app广辰。
2.清除數(shù)據(jù)
清除數(shù)據(jù)同樣是調(diào)用了PackageManagerService里的方法暇矫,方法名為clearApplicationUserData主之,源碼如下:
可以看到該方法需要傳入packageName、IPackageDataObserver李根、userId等參數(shù)槽奕,其中IPackageDataObserver同上面的IPackageDeleteObserver接入方法,代碼如下:
package android.content.pm;
/**
* API for package data change related callbacks from the Package Manager.
* Some usage scenarios include deletion of cache directory, generate
* statistics related to code, data, cache usage(TODO)
* {@hide}
*/
oneway interface IPackageDataObserver {
void onRemoveCompleted(in String packageName, boolean succeeded);
}
編譯后可正常拿到IPackageDataObserver.class房轿,接下來即可通過反射調(diào)用clearApplicationUserData方法了粤攒,代碼如下:
//清數(shù)據(jù)操作
try {
DeleteUserDataObserver observer = new DeleteUserDataObserver();
IBinder pgmService = (IBinder) ReflectUtil.callStaticObjectMethod(Class.forName("android.os.ServiceManager"),
"getService", new Class[]{String.class}, "package");
Object mIPm = ReflectUtil.callStaticObjectMethod(Class.forName("android.content.pm.IPackageManager$Stub"),
"asInterface", new Class[]{IBinder.class}, pgmService);
ReflectUtil.callObjectMethod(mIPm, "clearApplicationUserData",
new Class[]{String.class, IPackageDataObserver.class, int.class},
yourPackageName, observer, 0);
} catch (Exception e) {
}
//實現(xiàn)IPackageDataObserver
private class DeleteUserDataObserver extends IPackageDataObserver.Stub {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded) {
Log.d(TAG, "packageName:" + packageName + " isSuccess:" + succeeded);
}
}
此操作需要聲明如下權限:
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
3.降級安裝
這個就厲害了,在leader的啟示下囱持,原來adb命令都是在源碼工程中能找到對應的執(zhí)行方法琼讽。adb中有一個命令可以實現(xiàn)降級安裝adb install -r -d
通過在源碼中溯源,具體過程就不展示了洪唐,最終調(diào)用了PackageInstaller.SessionParams中的setRequestDowngrade方法钻蹬,具體如下圖:
可以看到這同樣是一個hide的api,同樣需要用反射去調(diào)用凭需。在安裝過程中拿到SessionParams的實例對象问欠,然后使用反射調(diào)用上述方法,傳入true粒蜈,即可實現(xiàn)降級安裝顺献,具體安裝流程可參考其他博客,這里給出反射代碼:
PackageInstaller.SessionParams sp =
new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
ReflectUtil.callObjectMethod(sp, "setRequestDowngrade",
new Class[]{boolean.class}, true);
4.總結(jié)
源碼是最好的老師枯怖,當有卡殼的地方時注整,看源碼往往能解決99%的問題。例如在靜默卸載過程中度硝,我們app已經(jīng)成功調(diào)用了反射方法肿轨,且沒有catch到異常,但是一直沒實現(xiàn)效果蕊程,且observer中沒有打印出東西椒袍,后面看了miui的源碼,發(fā)現(xiàn)在miui的framework層設置了一個白名單藻茂,只有白名單內(nèi)的app有權限去調(diào)用卸載方法驹暑,于是在白名單中增加了我們的app,編譯后即可正常實現(xiàn)效果辨赐。