Add hide Magisk Manager feature

This commit is contained in:
topjohnwu
2017-08-22 03:01:54 +08:00
parent ea6552615d
commit 657f4ab303
24 changed files with 296 additions and 14 deletions

View File

@ -53,6 +53,9 @@ public class MagiskFragment extends Fragment
public static final String SHOW_DIALOG = "dialog";
private static final String UNINSTALLER = "magisk_uninstaller.sh";
private static final String UTIL_FUNCTIONS= "util_functions.sh";
private static int expandHeight = 0;
private static boolean mExpanded = false;
@ -176,8 +179,8 @@ public class MagiskFragment extends Fragment
.setMessage(R.string.uninstall_magisk_msg)
.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
try {
InputStream in = magiskManager.getAssets().open(MagiskManager.UNINSTALLER);
File uninstaller = new File(magiskManager.getCacheDir(), MagiskManager.UNINSTALLER);
InputStream in = magiskManager.getAssets().open(UNINSTALLER);
File uninstaller = new File(magiskManager.getCacheDir(), UNINSTALLER);
FileOutputStream out = new FileOutputStream(uninstaller);
byte[] bytes = new byte[1024];
int read;
@ -186,8 +189,8 @@ public class MagiskFragment extends Fragment
}
in.close();
out.close();
in = magiskManager.getAssets().open(MagiskManager.UTIL_FUNCTIONS);
File utils = new File(magiskManager.getCacheDir(), MagiskManager.UTIL_FUNCTIONS);
in = magiskManager.getAssets().open(UTIL_FUNCTIONS);
File utils = new File(magiskManager.getCacheDir(), UTIL_FUNCTIONS);
out = new FileOutputStream(utils);
while ((read = in.read(bytes)) != -1) {
out.write(bytes, 0, read);
@ -207,8 +210,8 @@ public class MagiskFragment extends Fragment
public void onFinish() {
progress.setMessage(getString(R.string.reboot_countdown, 0));
Shell.getShell(getActivity()).su_raw(
"mv -f " + uninstaller + " /cache/" + MagiskManager.UNINSTALLER,
"mv -f " + utils + " /data/magisk/" + MagiskManager.UTIL_FUNCTIONS,
"mv -f " + uninstaller + " /cache/" + UNINSTALLER,
"mv -f " + utils + " /data/magisk/" + UTIL_FUNCTIONS,
"reboot"
);
}

View File

@ -32,8 +32,6 @@ public class MagiskManager extends Application {
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
public static final String TMP_FOLDER_PATH = "/dev/tmp";
public static final String MAGISK_PATH = "/magisk";
public static final String UNINSTALLER = "magisk_uninstaller.sh";
public static final String UTIL_FUNCTIONS= "util_functions.sh";
public static final String INTENT_SECTION = "section";
public static final String INTENT_VERSION = "version";
public static final String INTENT_LINK = "link";

View File

@ -4,6 +4,7 @@ import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
@ -13,6 +14,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.HideManager;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.utils.Logger;
@ -98,6 +100,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
multiuserMode = (ListPreference) findPreference("multiuser_mode");
namespaceMode = (ListPreference) findPreference("mnt_ns");
SwitchPreference reauth = (SwitchPreference) findPreference("su_reauth");
Preference hideManager = findPreference("hide");
setSummary();
@ -105,6 +108,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
if (getActivity().getApplicationInfo().uid > 99999) {
prefScreen.removePreference(magiskCategory);
prefScreen.removePreference(suCategory);
generalCatagory.removePreference(hideManager);
}
// Remove re-authentication option on Android O, it will not work
@ -117,6 +121,11 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
return true;
});
hideManager.setOnPreferenceClickListener((pref) -> {
new HideManager(getActivity()).exec();
return true;
});
if (!BuildConfig.DEBUG) {
prefScreen.removePreference(developer);
}

View File

@ -0,0 +1,72 @@
package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import android.content.pm.PackageManager;
import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File;
import java.util.List;
public class HideManager extends ParallelTask<Void, Void, Boolean> {
public HideManager(Context context) {
super(context);
}
@Override
protected void onPreExecute() {
getMagiskManager().toast(R.string.hide_manager_toast, Toast.LENGTH_SHORT);
}
@Override
protected Boolean doInBackground(Void... voids) {
MagiskManager magiskManager = getMagiskManager();
if (magiskManager == null)
return false;
// Generate a new unhide app with random package name
File unhideAPK = new File(magiskManager.getCacheDir(), "unhide.apk");
String pkg = ZipUtils.generateUnhide(magiskManager, unhideAPK);
// Install the application
List<String> ret = getShell().su("pm install " + unhideAPK + ">/dev/null && echo true || echo false");
unhideAPK.delete();
if (!Utils.isValidShellResponse(ret) || !Boolean.parseBoolean(ret.get(0)))
return false;
try {
// Allow the application to gain root by default
PackageManager pm = magiskManager.getPackageManager();
int uid = pm.getApplicationInfo(pkg, 0).uid;
Policy policy = new Policy(uid, pm);
policy.policy = Policy.ALLOW;
policy.notification = false;
policy.logging = false;
magiskManager.suDB.addPolicy(policy);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
// Hide myself!
getShell().su_raw("pm hide " + magiskManager.getPackageName());
return true;
}
@Override
protected void onPostExecute(Boolean b) {
MagiskManager magiskManager = getMagiskManager();
if (magiskManager == null)
return;
if (!b) {
magiskManager.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
}
super.onPostExecute(b);
}
}

View File

@ -35,6 +35,7 @@ import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.receivers.ManagerUpdate;
import java.io.File;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@ -288,4 +289,25 @@ public class Utils {
return locales;
}
public static String genPackageName(String prefix, int length) {
StringBuilder builder = new StringBuilder(length);
builder.append(prefix);
length -= prefix.length();
SecureRandom random = new SecureRandom();
String base = "abcdefghijklmnopqrstuvwxyz";
String alpha = base + base.toUpperCase();
String full = alpha + "0123456789..........";
char next, prev = '\0';
for (int i = 0; i < length; ++i) {
if (prev == '.' || i == length - 1 || i == 0) {
next = alpha.charAt(random.nextInt(alpha.length()));
} else {
next = full.charAt(random.nextInt(full.length()));
}
builder.append(next);
prev = next;
}
return builder.toString();
}
}

View File

@ -1,6 +1,7 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.text.TextUtils;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@ -65,6 +66,7 @@ public class ZipUtils {
// File name in assets
private static final String PUBLIC_KEY_NAME = "public.certificate.x509.pem";
private static final String PRIVATE_KEY_NAME = "private.key.pk8";
private static final String UNHIDE_NAME = "unhide.apk";
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
@ -82,6 +84,45 @@ public class ZipUtils {
public native static void zipAdjust(String filenameIn, String filenameOut);
public static String generateUnhide(Context context, File output) {
File temp = new File(context.getCacheDir(), "temp.apk");
String pkg = "";
try {
JarInputStream source = new JarInputStream(context.getAssets().open(UNHIDE_NAME));
JarOutputStream dest = new JarOutputStream(new FileOutputStream(temp));
JarEntry entry;
int size;
byte buffer[] = new byte[4096];
while ((entry = source.getNextJarEntry()) != null) {
dest.putNextEntry(new JarEntry(entry.getName()));
if (TextUtils.equals(entry.getName(), "AndroidManifest.xml")) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((size = source.read(buffer)) != -1) {
baos.write(buffer, 0, size);
}
byte xml[] = baos.toByteArray();
pkg = Utils.genPackageName("com.", 20);
for (int i = 0; i < 20; ++i) {
xml[424 + i] = (byte) pkg.charAt(i);
}
dest.write(xml);
} else {
while((size = source.read(buffer)) != -1) {
dest.write(buffer, 0, size);
}
}
}
source.close();
dest.close();
signZip(context, temp, output, false);
temp.delete();
} catch (IOException e) {
e.printStackTrace();
return pkg;
}
return pkg;
}
public static void removeTopFolder(InputStream in, File output) throws IOException {
try {
JarInputStream source = new JarInputStream(in);
@ -102,7 +143,7 @@ public class ZipUtils {
continue;
}
dest.putNextEntry(new JarEntry(path));
while((size = source.read(buffer, 0, 2048)) != -1) {
while((size = source.read(buffer)) != -1) {
dest.write(buffer, 0, size);
}
}