package biz.bokhorst.xprivacy; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import android.os.Binder; import android.util.Log; import android.content.ComponentName; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; public class XPackageManager extends XHook { private Methods mMethod; private String mClassName; private static final String cClassName = "android.app.ApplicationPackageManager"; private XPackageManager(Methods method, String restrictionName) { super(restrictionName, method.name().replace("Srv_", ""), method.name()); mMethod = method; mClassName = "com.android.server.pm.PackageManagerService"; } private XPackageManager(Methods method, String restrictionName, String className) { super(restrictionName, method.name(), null); mMethod = method; mClassName = className; } public String getClassName() { return mClassName; } // @formatter:off // public List<ApplicationInfo> getInstalledApplications(int flags) // public List<PackageInfo> getInstalledPackages(int flags) // public String[] getPackagesForUid(int uid) // public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) // public List<PackageInfo> getPreferredPackages(int flags) // public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) // public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) // public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) // public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, Intent intent, int flags) // public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) // public List<ResolveInfo> queryIntentServices(Intent intent, int flags) // frameworks/base/core/java/android/app/ApplicationPackageManager.java // http://developer.android.com/reference/android/content/pm/PackageManager.html // public int checkPermission(String permName, String pkgName) // public int checkUidPermission(String permName, int uid) // public java.lang.String[] getPackagesForUid(int uid) // public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) // public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName caller, android.content.Intent[] specifics, java.lang.String[] specificTypes, android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) // public java.util.List<android.content.pm.ResolveInfo> queryIntentReceivers(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) // public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) // public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) // public java.util.List<android.content.pm.ApplicationInfo> getPersistentApplications(int flags) // public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String processName, int uid, int flags) // public java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int flags) // public android.content.pm.ParceledListSlice getInstalledPackages(int flags, int userId) // public android.content.pm.ParceledListSlice getPackagesHoldingPermissions(java.lang.String[] permissions, int flags, int userId) // public android.content.pm.ParceledListSlice getInstalledApplications(int flags, int userId) // public PackageInfo getPackageInfo(String packageName, int flags, int userId) // public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/pm/PackageManagerService.java // @formatter:on // @formatter:off private enum Methods { getInstalledApplications, getInstalledPackages, getPackagesForUid, getPackagesHoldingPermissions, getPreferredActivities, getPreferredPackages, queryBroadcastReceivers, queryContentProviders, queryIntentActivities, queryIntentActivityOptions, queryIntentContentProviders, queryIntentServices, checkPermission, checkUidPermission, Srv_getPackageInfo, Srv_getApplicationInfo, Srv_getInstalledApplications, Srv_getInstalledPackages, Srv_getPackagesForUid, Srv_getPackagesHoldingPermissions, Srv_getPersistentApplications, Srv_getPreferredPackages, Srv_queryContentProviders, Srv_queryIntentActivities, Srv_queryIntentActivityOptions, Srv_queryIntentContentProviders, Srv_queryIntentReceivers, Srv_queryIntentServices }; // @formatter:on public static List<XHook> getInstances(String className, boolean server) { List<XHook> listHook = new ArrayList<XHook>(); if (!cClassName.equals(className)) { if (className == null) className = cClassName; if (server) { listHook.add(new XPackageManager(Methods.Srv_getPackageInfo, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getApplicationInfo, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getInstalledApplications, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getInstalledPackages, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getPackagesForUid, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getPackagesHoldingPermissions, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getPersistentApplications, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_getPreferredPackages, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryContentProviders, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryIntentActivities, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryIntentActivityOptions, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryIntentContentProviders, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryIntentReceivers, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.Srv_queryIntentServices, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.checkPermission, PrivacyManager.cSystem)); listHook.add(new XPackageManager(Methods.checkUidPermission, PrivacyManager.cSystem)); } else { listHook.add(new XPackageManager(Methods.getInstalledApplications, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.getInstalledPackages, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.getPackagesForUid, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.getPackagesHoldingPermissions, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.getPreferredActivities, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.getPreferredPackages, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryBroadcastReceivers, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryContentProviders, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryIntentActivities, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryIntentActivityOptions, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryIntentContentProviders, PrivacyManager.cSystem, className)); listHook.add(new XPackageManager(Methods.queryIntentServices, PrivacyManager.cSystem, className)); } } return listHook; } @Override protected void before(XParam param) throws Throwable { if (mMethod == Methods.getPreferredActivities) if (param.args.length > 1) if (isRestricted(param)) { param.args[0] = new ArrayList<IntentFilter>(); param.args[1] = new ArrayList<ComponentName>(); param.setResult(0); } } @Override @SuppressWarnings("unchecked") protected void after(XParam param) throws Throwable { switch (mMethod) { case Srv_getPackageInfo: case Srv_getApplicationInfo: if (param.args.length > 0 && param.args[0] instanceof String && param.getResult() != null) { // Allow own package name int uid = -1; if (mMethod == Methods.Srv_getPackageInfo) { PackageInfo pInfo = (PackageInfo) param.getResult(); if (pInfo.applicationInfo != null) uid = pInfo.applicationInfo.uid; } else if (mMethod == Methods.Srv_getApplicationInfo) { ApplicationInfo aInfo = (ApplicationInfo) param.getResult(); uid = aInfo.uid; } if (uid == Binder.getCallingUid()) return; // Prevent recursion String packageName = (String) param.args[0]; if (!XPackageManager.class.getPackage().getName().equals(packageName)) if (isRestrictedExtra(param, packageName)) if (!isPackageAllowed(uid, packageName)) param.setResult(null); } break; case Srv_getInstalledApplications: if (param.getResult() != null) if (isRestricted(param)) { Method mGetList = param.getResult().getClass().getDeclaredMethod("getList"); List<ApplicationInfo> listAppInfo = (List<ApplicationInfo>) mGetList.invoke(param.getResult()); Constructor<?> constructor = param.getResult().getClass().getConstructor(List.class); param.setResult(constructor.newInstance(filterApplicationInfo(listAppInfo))); } break; case Srv_getInstalledPackages: case Srv_getPackagesHoldingPermissions: if (param.getResult() != null) if (isRestricted(param)) { Method mGetList = param.getResult().getClass().getDeclaredMethod("getList"); List<PackageInfo> listPkgInfo = (List<PackageInfo>) mGetList.invoke(param.getResult()); Constructor<?> constructor = param.getResult().getClass().getConstructor(List.class); param.setResult(constructor.newInstance(filterPackageInfo(listPkgInfo))); } break; case getPackagesForUid: case Srv_getPackagesForUid: if (param.args.length > 0 && param.args[0] instanceof Integer && param.getResult() != null) { int uid = (Integer) param.args[0]; if (uid != Binder.getCallingUid()) if (isRestrictedExtra(param, Integer.toString(uid))) { List<String> lstResult = new ArrayList<String>(); if (param.getResult() instanceof String[]) for (String packageName : (String[]) param.getResult()) if (isPackageAllowed(Binder.getCallingUid(), packageName)) lstResult.add(packageName); if (lstResult.size() == 0) param.setResult(null); else param.setResult(lstResult.toArray(new String[0])); } } break; case Srv_getPersistentApplications: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterApplicationInfo((List<ApplicationInfo>) param.getResult())); break; case Srv_getPreferredPackages: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterPackageInfo((List<PackageInfo>) param.getResult())); break; case Srv_queryIntentActivities: case Srv_queryIntentActivityOptions: case Srv_queryIntentContentProviders: case Srv_queryIntentReceivers: case Srv_queryIntentServices: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterResolveInfo((List<ResolveInfo>) param.getResult())); break; case getInstalledApplications: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterApplicationInfo((List<ApplicationInfo>) param.getResult())); break; case getPreferredActivities: break; case getInstalledPackages: case getPackagesHoldingPermissions: case getPreferredPackages: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterPackageInfo((List<PackageInfo>) param.getResult())); break; case queryBroadcastReceivers: case queryIntentActivities: case queryIntentActivityOptions: case queryIntentContentProviders: case queryIntentServices: if (param.getResult() != null) if (isRestricted(param)) param.setResult(filterResolveInfo((List<ResolveInfo>) param.getResult())); break; case queryContentProviders: case Srv_queryContentProviders: if (param.args.length > 1 && param.args[1] instanceof Integer && param.getResult() != null) { int uid = (Integer) param.args[1]; if (uid != Binder.getCallingUid()) { String processName = (String) param.args[0]; if (isRestrictedExtra(param, processName)) param.setResult(filterProviderInfo((List<ProviderInfo>) param.getResult())); } } break; case checkPermission: if (param.args.length > 1 && param.args[0] instanceof String && param.args[1] instanceof String) { String permName = (String) param.args[0]; String pkgName = (String) param.args[1]; int resultOfCheck = (Integer) param.getResult(); if (resultOfCheck != PackageManager.PERMISSION_GRANTED) return; // Get uid int uid; Class<?> clazz = param.thisObject.getClass(); try { // public int getPackageUid(String packageName, int userId) Method mGetPackageUid = clazz.getDeclaredMethod("getPackageUid", String.class, int.class); mGetPackageUid.setAccessible(true); int userId = Util.getUserId(Binder.getCallingUid()); uid = (Integer) mGetPackageUid.invoke(param.thisObject, pkgName, userId); } catch (NoSuchMethodException ignored) { // public int getPackageUid(String packageName) Method mGetPackageUid = clazz.getDeclaredMethod("getPackageUid", String.class); mGetPackageUid.setAccessible(true); uid = (Integer) mGetPackageUid.invoke(param.thisObject, pkgName); } checkPermission(param, uid, permName); } break; case checkUidPermission: if (param.args.length > 1 && param.args[0] instanceof String && param.args[1] instanceof Integer) { String permName = (String) param.args[0]; int uid = (Integer) param.args[1]; int resultOfCheck = (Integer) param.getResult(); if (resultOfCheck == PackageManager.PERMISSION_GRANTED) checkPermission(param, uid, permName); } break; } } private void checkPermission(XParam param, int uid, String permName) throws Throwable { if ("android.permission.CAMERA".endsWith(permName)) if (getRestricted(uid, PrivacyManager.cMedia, "Camera.permission")) param.setResult(PackageManager.PERMISSION_DENIED); if ("android.permission.RECORD_AUDIO".endsWith(permName)) if (getRestricted(uid, PrivacyManager.cMedia, "Record.Audio.permission")) param.setResult(PackageManager.PERMISSION_DENIED); if ("android.permission.RECORD_VIDEO".endsWith(permName)) if (getRestricted(uid, PrivacyManager.cMedia, "Record.Video.permission")) param.setResult(PackageManager.PERMISSION_DENIED); if (PrivacyManager.getSettingBool(0, PrivacyManager.cSettingPermMan, false)) { permName = permName.replace("android.permission.", ""); if (isRestrictedExtra(uid, getRestrictionName(), getMethodName(), permName)) param.setResult(PackageManager.PERMISSION_DENIED); } } private List<ApplicationInfo> filterApplicationInfo(List<ApplicationInfo> original) { ArrayList<ApplicationInfo> result = new ArrayList<ApplicationInfo>(); for (ApplicationInfo appInfo : original) if (isPackageAllowed(appInfo.uid, appInfo.packageName)) result.add(appInfo); return result; } private List<PackageInfo> filterPackageInfo(List<PackageInfo> original) { ArrayList<PackageInfo> result = new ArrayList<PackageInfo>(); for (PackageInfo pkgInfo : original) if (isPackageAllowed(pkgInfo.applicationInfo == null ? 0 : pkgInfo.applicationInfo.uid, pkgInfo.packageName)) result.add(pkgInfo); return result; } private List<ProviderInfo> filterProviderInfo(List<ProviderInfo> original) { ArrayList<ProviderInfo> result = new ArrayList<ProviderInfo>(); for (ProviderInfo provInfo : original) if (isPackageAllowed(provInfo.applicationInfo == null ? 0 : provInfo.applicationInfo.uid, provInfo.packageName)) result.add(provInfo); return result; } private List<ResolveInfo> filterResolveInfo(List<ResolveInfo> original) { ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(); for (ResolveInfo resInfo : original) if (resInfo.activityInfo != null && resInfo.activityInfo.applicationInfo != null) if (isPackageAllowed(resInfo.activityInfo.applicationInfo.uid, resInfo.activityInfo.applicationInfo.packageName)) result.add(resInfo); return result; } public static boolean isPackageAllowed(int puid, String packageName) { int uid = Binder.getCallingUid(); if (puid == uid) return true; if (packageName == null) { Util.log(null, Log.WARN, "isPackageAllowed uid=" + uid + " package=" + packageName); Util.logStack(null, Log.WARN); return false; } boolean allowed = PrivacyManager.getSettingBool(-uid, Meta.cTypeApplication, packageName, false); boolean blacklist = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingBlacklist, false); if (blacklist) allowed = !allowed; if (allowed) Util.log(null, Log.INFO, "Allowing package=" + packageName); return allowed; } }