public static String hasProLicense(Context context) { try { // Get license String[] license = getProLicenseUnchecked(); if (license == null) return null; String name = license[0]; String email = license[1]; String signature = license[2]; // Get bytes byte[] bEmail = email.getBytes("UTF-8"); byte[] bSignature = hex2bytes(signature); if (bEmail.length == 0 || bSignature.length == 0) { Util.log(null, Log.ERROR, "Licensing: invalid file"); return null; } // Verify license boolean licensed = verifyData(bEmail, bSignature, getPublicKey(context)); if (licensed) Util.log(null, Log.INFO, "Licensing: ok for " + name); else Util.log(null, Log.ERROR, "Licensing: invalid for " + name); // Return result if (licensed) return name; } catch (Throwable ex) { Util.bug(null, ex); } return null; }
@Override public boolean onCreate() { try { String packageName = PrivacyManager.class.getPackage().getName(); File out = new File( Environment.getDataDirectory() + File.separator + "data" + File.separator + packageName + File.separator + "meta.xml"); Util.log(null, Log.INFO, "Writing meta=" + out.getAbsolutePath()); InputStream is = getContext().getAssets().open("meta.xml"); OutputStream os = new FileOutputStream(out.getAbsolutePath()); byte[] buffer = new byte[1024]; int read; while ((read = is.read(buffer)) != -1) os.write(buffer, 0, read); is.close(); os.flush(); os.close(); out.setReadable(true, false); } catch (Throwable ex) { Util.bug(null, ex); } return true; }
@Override protected void onCreate(Bundle savedInstanceState) { if (PrivacyService.checkClient()) { // Set theme int userId = Util.getUserId(Process.myUid()); String themeName = PrivacyManager.getSetting(userId, PrivacyManager.cSettingTheme, ""); mThemeId = (themeName.equals("Dark") ? R.style.CustomTheme : R.style.CustomTheme_Light); setTheme(mThemeId); super.onCreate(savedInstanceState); } else { super.onCreate(savedInstanceState); // Privacy client now available setContentView(R.layout.reboot); try { PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); TextView tvVersion = (TextView) findViewById(R.id.tvVersion); tvVersion.setText(pInfo.versionName); } catch (Throwable ex) { Util.bug(null, ex); } // Show reason if (PrivacyService.getClient() == null) { TextView tvRebootClient = (TextView) findViewById(R.id.tvRebootClient); tvRebootClient.setVisibility(View.VISIBLE); Requirements.checkCompatibility(this); } else { TextView tvRebootClient = (TextView) findViewById(R.id.tvRebootVersion); tvRebootClient.setVisibility(View.VISIBLE); Requirements.check(this); } } }
private boolean isAccountAllowed(String accountName, String accountType, int uid) { try { String sha1 = Util.sha1(accountName + accountType); if (PrivacyManager.getSettingBool(uid, Meta.cTypeAccount, sha1, false, true)) return true; } catch (Throwable ex) { Util.bug(this, ex); } return false; }
public static boolean isProEnablerInstalled(Context context) { Version version = getProEnablerVersion(context); if (version != null && isValidProEnablerVersion(version) && hasValidProEnablerSignature(context)) { Util.log(null, Log.INFO, "Licensing: enabler installed"); return true; } Util.log(null, Log.INFO, "Licensing: enabler not installed"); return false; }
public static String[] getProLicenseUnchecked() { // Get license file name String storageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); File licenseFile = new File(storageDir + File.separator + LICENSE_FILE_NAME); if (!licenseFile.exists()) licenseFile = new File(storageDir + File.separator + ".xprivacy" + File.separator + LICENSE_FILE_NAME); // Get imported license file name String importedLicense = getUserDataDirectory(Process.myUid()) + File.separator + LICENSE_FILE_NAME; // Import license file if (licenseFile.exists()) { try { File out = new File(importedLicense); Util.log(null, Log.WARN, "Licensing: importing " + out.getAbsolutePath()); InputStream is = new FileInputStream(licenseFile.getAbsolutePath()); OutputStream os = new FileOutputStream(out.getAbsolutePath()); byte[] buffer = new byte[1024]; int read; while ((read = is.read(buffer)) != -1) os.write(buffer, 0, read); is.close(); os.flush(); os.close(); // Protect license file setPermissions(out.getAbsolutePath(), 0700, Process.myUid(), Process.myUid()); licenseFile.delete(); } catch (Throwable ex) { Util.bug(null, ex); } } // Check license file licenseFile = new File(importedLicense); if (licenseFile.exists()) { // Read license try { IniFile iniFile = new IniFile(licenseFile); String name = iniFile.get("name", ""); String email = iniFile.get("email", ""); String signature = iniFile.get("signature", ""); return new String[] {name, email, signature}; } catch (Throwable ex) { bug(null, ex); return null; } } else Util.log(null, Log.INFO, "Licensing: no license file"); return null; }
@Override protected void after(MethodHookParam param) throws Throwable { String methodName = param.method.getName(); if (methodName.equals("getAddress")) { if (param.getResult() != null && isRestricted(param)) param.setResult(PrivacyManager.getDefacedProp("MAC")); } else Util.log(this, Log.WARN, "Unknown method=" + methodName); }
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { // Check access enforcePermission(); // Get arguments String restrictionName = selection; int uid = values.getAsInteger(COL_UID); String methodName = values.getAsString(COL_METHOD); boolean allowed = !Boolean.parseBoolean(values.getAsString(COL_RESTRICTED)); // Update updateRestriction(uid, restrictionName, methodName, allowed); return 1; // rows } else if (sUriMatcher.match(uri) == TYPE_USAGE) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Get arguments int uid = values.getAsInteger(COL_UID); String restrictionName = values.getAsString(PrivacyProvider.COL_RESTRICTION); String methodName = values.getAsString(COL_METHOD); boolean restricted = false; if (values.containsKey(PrivacyProvider.COL_RESTRICTED)) restricted = values.getAsBoolean(PrivacyProvider.COL_RESTRICTED); long timeStamp = values.getAsLong(PrivacyProvider.COL_USED); Util.log( null, Log.INFO, String.format( "Update usage data %d/%s/%s=%b", uid, restrictionName, methodName, restricted)); // Update usage data if (methodName != null) updateUsage(uid, restrictionName, methodName, restricted, timeStamp); return 1; } else if (sUriMatcher.match(uri) == TYPE_SETTING) { // Check access enforcePermission(); // Get arguments String settingName = selection; // Update setting SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); editor.putString(getSettingPref(settingName), values.getAsString(COL_VALUE)); editor.apply(); setPrefFileReadable(PREF_SETTINGS); return 1; } throw new IllegalArgumentException(uri.toString()); }
public static String getSelfVersionName(Context context) { try { String self = Util.class.getPackage().getName(); PackageManager pm = context.getPackageManager(); PackageInfo pInfo = pm.getPackageInfo(self, 0); return pInfo.versionName; } catch (NameNotFoundException ex) { Util.bug(null, ex); return null; } }
@SuppressLint("NewApi") public static int getAppId(int uid) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) try { // UserHandle: public static final int getAppId(int uid) Method method = (Method) UserHandle.class.getDeclaredMethod("getAppId", int.class); uid = (Integer) method.invoke(null, uid); } catch (Throwable ex) { Util.bug(null, ex); } return uid; }
public static Version getProEnablerVersion(Context context) { try { String proPackageName = context.getPackageName() + ".pro"; PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageInfo(proPackageName, 0); return new Version(pi.versionName); } catch (NameNotFoundException ignored) { } catch (Throwable ex) { Util.bug(null, ex); } return null; }
@Override protected void before(MethodHookParam param) throws Throwable { if (mMethod == Methods.addGeofences || mMethod == Methods.removeGeofences) { if (isRestricted(param)) param.setResult(null); } else if (mMethod == Methods.getLastLocation) { // Do nothing } else if (mMethod == Methods.removeLocationUpdates) { if (isRestricted(param)) removeLocationListener(param); } else if (mMethod == Methods.requestLocationUpdates) { if (isRestricted(param)) replaceLocationListener(param); } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); }
public static void setPermissions(String path, int mode, int uid, int gid) { try { // frameworks/base/core/java/android/os/FileUtils.java Class<?> fileUtils = Class.forName("android.os.FileUtils"); Method setPermissions = fileUtils.getMethod("setPermissions", String.class, int.class, int.class, int.class); setPermissions.invoke(null, path, mode, uid, gid); Util.log( null, Log.WARN, "Changed permission path=" + path + " mode=" + Integer.toOctalString(mode) + " uid=" + uid + " gid=" + gid); } catch (Throwable ex) { Util.bug(null, ex); } }
public static boolean hasLBE() { if (!mHasLBEDetermined) { mHasLBEDetermined = true; try { File apps = new File(Environment.getDataDirectory() + File.separator + "app"); File[] files = (apps == null ? null : apps.listFiles()); if (files != null) for (File file : files) if (file.getName().startsWith("com.lbe.security")) mHasLBE = true; } catch (Throwable ex) { Util.bug(null, ex); } } return mHasLBE; }
@Override protected void after(MethodHookParam param) throws Throwable { if (mMethod == Methods.addGeofences || mMethod == Methods.removeGeofences) { // Do nothing } else if (mMethod == Methods.getLastLocation) { Location location = (Location) param.getResult(); if (location != null && isRestricted(param)) param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); } else if (mMethod == Methods.removeLocationUpdates) { // Do nothing } else if (mMethod == Methods.requestLocationUpdates) { // Do nothing } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); }
@Override protected void after(MethodHookParam param) throws Throwable { if (mMethod != Methods.addGeofence && mMethod != Methods.addNmeaListener && mMethod != Methods.addProximityAlert && mMethod != Methods.removeUpdates && mMethod != Methods.requestLocationUpdates && mMethod != Methods.requestSingleUpdate) if (mMethod == Methods.isProviderEnabled) { if (isRestricted(param)) param.setResult(false); } else if (mMethod == Methods.getGpsStatus) { if (param.getResult() != null && isRestricted(param)) { GpsStatus status = (GpsStatus) param.getResult(); // private GpsSatellite mSatellites[] try { Field mSatellites = status.getClass().getDeclaredField("mSatellites"); mSatellites.setAccessible(true); mSatellites.set(status, new GpsSatellite[0]); } catch (Throwable ex) { Util.bug(null, ex); } } } else if (mMethod == Methods.getLastLocation || mMethod == Methods.getLastKnownLocation) { Location location = (Location) param.getResult(); if (location != null && isRestricted(param)) param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); } else if (mMethod == Methods.getProviders) { if (param.getResult() != null && isRestricted(param)) param.setResult(new ArrayList<String>()); } else if (mMethod == Methods.sendExtraCommand) { if (isRestricted(param)) param.setResult(false); } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); }
private void removeLocationListener(MethodHookParam param) { if (param.args.length >= 1 && param.args[0] != null && LocationListener.class.isAssignableFrom(param.args[0].getClass())) { LocationListener listener = (LocationListener) param.args[0]; synchronized (mListener) { XLocationListener xlistener = mListener.get(listener); if (xlistener == null) Util.log(this, Log.WARN, "Not found count=" + mListener.size()); else { param.args[0] = xlistener; mListener.remove(listener); } } } else param.setResult(null); }
@Override public int delete(Uri uri, String where, String[] selectionArgs) { // Check access enforcePermission(); if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { int rows = 0; // Get argument int uid = Integer.parseInt(selectionArgs[0]); // Method restrictions SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); for (String restrictionName : PrivacyManager.getRestrictions(true)) { for (PrivacyManager.MethodDescription md : PrivacyManager.getMethods(restrictionName)) { rows++; editor.remove(getExceptionPref(uid, restrictionName, md.getMethodName())); } } editor.apply(); setPrefFileReadable(PREF_RESTRICTION); // Group restrictions for (String restrictionName : PrivacyManager.getRestrictions(true)) { rows++; updateRestriction(uid, restrictionName, null, true); } return rows; } else if (sUriMatcher.match(uri) == TYPE_SETTING && selectionArgs == null) { int rows = 0; SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); for (String pref : prefs.getAll().keySet()) { rows++; editor.remove(pref); Util.log(null, Log.INFO, "Removed setting=" + pref); } editor.apply(); setPrefFileReadable(PREF_SETTINGS); return rows; } throw new IllegalArgumentException(uri.toString()); }
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.app, menu); // Launch PackageManager pm = getPackageManager(); if (pm.getLaunchIntentForPackage(mAppInfo.getPackageName()) == null) menu.findItem(R.id.menu_app_launch).setEnabled(false); // Play boolean hasMarketLink = Util.hasMarketLink(this, mAppInfo.getPackageName()); menu.findItem(R.id.menu_app_store).setEnabled(hasMarketLink); return true; }
private void replaceLocationListener(MethodHookParam param) throws Throwable { if (param.args.length >= 2 && param.args[1] != null && LocationListener.class.isAssignableFrom(param.args[1].getClass())) { if (!(param.args[1] instanceof XLocationListener)) { LocationListener listener = (LocationListener) param.args[1]; if (listener != null) { XLocationListener xListener = new XLocationListener(listener); synchronized (mListener) { mListener.put(listener, xListener); Util.log(this, Log.INFO, "Added count=" + mListener.size()); } param.args[1] = xListener; } } } else param.setResult(null); }
private void replaceLocationListener(MethodHookParam param, int arg) throws Throwable { if (param.args.length > arg && param.args[arg] != null && LocationListener.class.isAssignableFrom(param.args[arg].getClass())) { if (!(param.args[arg] instanceof XLocationListener)) { LocationListener listener = (LocationListener) param.args[arg]; if (listener != null) { XLocationListener xListener = new XLocationListener(listener); synchronized (mListener) { mListener.put(listener, xListener); Util.log( this, Log.INFO, "Added count=" + mListener.size() + " uid=" + Binder.getCallingUid()); } param.args[arg] = xListener; } } } else // Intent param.setResult(null); }
@Override protected void before(MethodHookParam param) throws Throwable { if (mMethod == Methods.exec) { // Get programs String[] progs = null; if (param.args.length > 0 && param.args[0] != null) if (String.class.isAssignableFrom(param.args[0].getClass())) progs = new String[] {(String) param.args[0]}; else progs = (String[]) param.args[0]; // Check programs if (progs != null) { String command = TextUtils.join(" ", progs); if ((mCommand == null && !command.contains("sh ") && !command.contains("su ")) || (mCommand != null && command.contains(mCommand + " "))) if (isRestricted(param, mCommand == null ? getMethodName() : mCommand)) param.setThrowable(new IOException()); } } else if (mMethod == Methods.load || mMethod == Methods.loadLibrary) { // Skip pre Android if (Process.myUid() != 0) if (isRestricted(param)) param.setResult(new UnsatisfiedLinkError()); } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); }
@Override protected void before(MethodHookParam param) throws Throwable { if (mMethod == Methods.getNfcAdapter) { if (isRestricted(param)) param.setResult(null); } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); }
@Override @SuppressWarnings("unchecked") protected void after(XParam param) throws Throwable { if (mMethod != Methods.addOnAccountsUpdatedListener && mMethod != Methods.removeOnAccountsUpdatedListener) { int uid = Binder.getCallingUid(); if (mMethod == Methods.blockingGetAuthToken) { if (param.args.length > 0 && param.args[0] != null) { Account account = (Account) param.args[0]; if (param.getResult() != null && isRestrictedExtra(param, account == null ? null : account.name)) if (!isAccountAllowed(account, uid)) param.setResult(null); } } else if (mMethod == Methods.getAccounts) { if (param.getResult() != null && isRestricted(param)) { Account[] accounts = (Account[]) param.getResult(); param.setResult(filterAccounts(accounts, uid)); } } else if (mMethod == Methods.getAccountsByType || mMethod == Methods.getAccountsByTypeForPackage) { if (param.args.length > 0) if (param.getResult() != null && isRestrictedExtra(param, (String) param.args[0])) { Account[] accounts = (Account[]) param.getResult(); param.setResult(filterAccounts(accounts, uid)); } } else if (mMethod == Methods.getAccountsByTypeAndFeatures) { if (param.args.length > 0) if (param.getResult() != null && isRestrictedExtra(param, (String) param.args[0])) { AccountManagerFuture<Account[]> future = (AccountManagerFuture<Account[]>) param.getResult(); param.setResult(new XFutureAccount(future, uid)); } } else if (mMethod == Methods.getAuthenticatorTypes) { if (param.getResult() != null && isRestricted(param)) param.setResult(new AuthenticatorDescription[0]); } else if (mMethod == Methods.getAuthToken) { if (param.args.length > 0) { Account account = (Account) param.args[0]; if (param.getResult() != null && isRestrictedExtra(param, account == null ? null : account.name)) { AccountManagerFuture<Bundle> future = (AccountManagerFuture<Bundle>) param.getResult(); param.setResult(new XFutureBundle(future, uid)); } } } else if (mMethod == Methods.getAuthTokenByFeatures) { if (param.getResult() != null && isRestricted(param, (String) param.args[0])) { AccountManagerFuture<Bundle> future = (AccountManagerFuture<Bundle>) param.getResult(); param.setResult(new XFutureBundle(future, uid)); } } else if (mMethod == Methods.hasFeatures) { if (param.args.length > 0 && param.args[0] != null) { Account account = (Account) param.args[0]; if (param.getResult() != null && isRestrictedExtra(param, account == null ? null : account.name)) if (!isAccountAllowed(account, uid)) param.setResult(new XFutureBoolean()); } } else Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); } }
@Override @SuppressWarnings("unchecked") protected void before(XParam param) throws Throwable { if (mMethod == Methods.addOnAccountsUpdatedListener) { if (param.args.length > 0 && param.args[0] != null) if (isRestricted(param)) { int uid = Binder.getCallingUid(); OnAccountsUpdateListener listener = (OnAccountsUpdateListener) param.args[0]; XOnAccountsUpdateListener xListener; synchronized (mListener) { xListener = mListener.get(listener); if (xListener == null) { xListener = new XOnAccountsUpdateListener(listener, uid); mListener.put(listener, xListener); Util.log(this, Log.WARN, "Added count=" + mListener.size() + " uid=" + uid); } } param.args[0] = xListener; } } else if (mMethod == Methods.removeOnAccountsUpdatedListener) { if (param.args.length > 0 && param.args[0] != null) synchronized (mListener) { OnAccountsUpdateListener listener = (OnAccountsUpdateListener) param.args[0]; XOnAccountsUpdateListener xListener = mListener.get(listener); if (xListener != null) { param.args[0] = xListener; Util.log( this, Log.WARN, "Removed count=" + mListener.size() + " uid=" + Binder.getCallingUid()); } } } else if (mMethod == Methods.getAccountsByTypeAndFeatures) { if (param.args.length > 2 && param.args[2] != null) if (isRestrictedExtra(param, (String) param.args[0])) { AccountManagerCallback<Account[]> callback = (AccountManagerCallback<Account[]>) param.args[2]; param.args[2] = new XAccountManagerCallbackAccount(callback, Binder.getCallingUid()); } } else if (mMethod == Methods.getAuthToken) { if (param.args.length > 0) { Account account = (Account) param.args[0]; for (int i = 0; i < param.args.length; i++) if (param.args[i] instanceof AccountManagerCallback<?>) if (isRestricted(param, account == null ? null : account.name)) { AccountManagerCallback<Bundle> callback = (AccountManagerCallback<Bundle>) param.args[i]; param.args[i] = new XAccountManagerCallbackBundle(callback, Binder.getCallingUid()); } } } else if (mMethod == Methods.getAuthTokenByFeatures) { if (param.args.length > 0) for (int i = 0; i < param.args.length; i++) if (param.args[i] instanceof AccountManagerCallback<?>) if (isRestricted(param, (String) param.args[0])) { AccountManagerCallback<Bundle> callback = (AccountManagerCallback<Bundle>) param.args[i]; param.args[i] = new XAccountManagerCallbackBundle(callback, Binder.getCallingUid()); } } else if (mMethod == Methods.hasFeatures) { if (param.args.length > 0) { Account account = (Account) param.args[0]; for (int i = 0; i < param.args.length; i++) if (param.args[i] instanceof AccountManagerCallback<?>) if (isRestricted(param, account == null ? null : account.name)) { AccountManagerCallback<Boolean> callback = (AccountManagerCallback<Boolean>) param.args[i]; param.args[i] = new XAccountManagerCallbackBoolean(callback); } } } }