Compare commits

...

48 Commits

Author SHA1 Message Date
topjohnwu
c2b016370b Make a copy of logcat and use that instead
When Magisk is magic mounting /system/bin, there is a chance that logcat would be temporarily unavailable. Leave a copy and use that for magisklogd
2018-10-20 21:46:12 -04:00
daveyannihilation
fc791b4371 Fix Dark theme to display cards as slightly lighter than background as per Material Design standards. Also redirect colors to app as opposed to calling on framework 2018-10-20 21:13:13 -04:00
topjohnwu
f76bb009f4 Update changelogs 2018-10-20 20:11:09 -04:00
topjohnwu
8a1292b295 Ask permissions to read internal storage 2018-10-20 19:42:46 -04:00
topjohnwu
d7d80d3fc1 Update encryption detection for determining default flags 2018-10-20 17:10:35 -04:00
topjohnwu
41b01003fd Always ACK before doing anything 2018-10-20 16:12:08 -04:00
topjohnwu
6557070ae1 Try to flush database before uninstalling 2018-10-20 15:31:41 -04:00
topjohnwu
e7e580e177 Remove support for Magisk lower than 1500 2018-10-20 15:04:15 -04:00
topjohnwu
dd9ddd2019 Remove unnecessary instruction from Defex hexpatch
Close #489
2018-10-20 00:28:09 -04:00
topjohnwu
74aae523ba Properly support boot image header v1
Close #695
2018-10-20 00:27:56 -04:00
topjohnwu
48c40f9516 Prevent Resources
Fix #619
2018-10-17 19:44:48 -04:00
topjohnwu
e0e7674715 Fix close button in FlashActivity 2018-10-17 15:36:09 -04:00
topjohnwu
e1a65276b9 Switch to general Samsung defex patch
Should be future proof unless code changes
2018-10-17 03:17:24 -04:00
muhammeteminturgut
469adc85ad Update Turkish translations 2018-10-16 21:11:43 -04:00
vvb2060
e1b181ca4e Hide system in MagiskHide list 2018-10-16 21:09:18 -04:00
topjohnwu
a4f0fbf8b7 Switch to butterknife
Finally support AndroidX and obfuscation
2018-10-16 21:00:01 -04:00
topjohnwu
190cdaddf8 Update README 2018-10-16 02:06:07 -04:00
topjohnwu
5c4ba13839 Add installation details 2018-10-16 01:55:28 -04:00
topjohnwu
e62630cf3e Add MagiskHide tutorials 2018-10-15 18:04:51 -04:00
topjohnwu
36fe7846c0 Update documentations
Still WIP
2018-10-15 04:33:42 -04:00
topjohnwu
8d150dd67a Update documentation
Still WIP
2018-10-15 00:46:37 -04:00
topjohnwu
506df00d81 Upgrade AGP 2018-10-12 21:51:58 -04:00
topjohnwu
a9121fa28f Reorganize libutils and cleanups 2018-10-12 21:46:09 -04:00
topjohnwu
d5a56d9e85 Fix bootloop for some devices with two /data
Close #654
2018-10-12 00:54:55 -04:00
topjohnwu
acf7c0c665 Minor reorganization of daemons 2018-10-12 00:50:47 -04:00
topjohnwu
619d48c97a Remove doc changelogs 2018-10-05 17:55:06 -04:00
topjohnwu
2cb198c38c Update README 2018-10-05 17:52:40 -04:00
topjohnwu
e8e39e0f3c Use poll instead of select
Close #637
2018-10-04 15:06:13 -04:00
topjohnwu
37860181d4 Finish su implementation 2018-10-04 14:41:48 -04:00
topjohnwu
d119dd9a0c Rewrite su daemon and client 2018-10-04 04:59:51 -04:00
topjohnwu
09ef19f7ec Code cleanups 2018-10-04 01:49:52 -04:00
topjohnwu
6a06c92fa6 Simplify su_info caches
No more lists. 99.999% it will only handle a single excessive requestor anyways.
2018-10-03 23:31:15 -04:00
topjohnwu
58ae596b0f Require fp auth when toggling su permission if required
Close #656
2018-09-29 02:21:14 -04:00
topjohnwu
f1ca21678d Set boolean when toggling 2018-09-29 02:02:41 -04:00
topjohnwu
d7eeef2c8a Separate fingerprint authentication dialog code 2018-09-29 01:57:51 -04:00
topjohnwu
4f626897f2 Cleanup 2018-09-29 00:28:12 -04:00
topjohnwu
b127e01845 Simplify debug flag propagation 2018-09-28 02:05:55 -04:00
topjohnwu
2118beeb23 Magisk-Modules-Repo now names repo with ID, simplify logic here 2018-09-28 01:58:28 -04:00
topjohnwu
5020cd1bbf Small cleanup 2018-09-28 01:25:43 -04:00
topjohnwu
cce636224c Reorganization 2018-09-27 18:26:41 -04:00
topjohnwu
60b3b8ddce Better incremental builds 2018-09-27 03:56:56 -04:00
topjohnwu
41446ec9ba Separate libutils and libsystemproperties 2018-09-27 03:30:16 -04:00
topjohnwu
df8b047bca Generalize logging interface 2018-09-27 03:11:10 -04:00
topjohnwu
12ced52012 Remove unused flag 2018-09-27 00:30:10 -04:00
topjohnwu
1d53335ae5 Dynamic load libselinux 2018-09-27 00:09:59 -04:00
topjohnwu
971a50d290 Update to Android Studio 3.2 2018-09-25 00:39:49 -04:00
topjohnwu
36dd9106a8 Stable AndroidX 2018-09-21 21:46:09 -04:00
John Wu
0a4ee3ffc7 Update README.MD 2018-09-21 12:01:59 -04:00
141 changed files with 2806 additions and 2702 deletions

View File

@@ -1,5 +1,13 @@
# Magisk
[XDA Announcement Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
[Downloads](https://github.com/topjohnwu/Magisk/releases) | [Documentation](https://topjohnwu.github.io/Magisk/) | [XDA Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
## Introduction
Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 5.0 (API 21). It covers the fundamental parts for Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc.
Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any system integrity verifications used in banking apps, corporation monitoring apps, game cheat detections, and most importantly [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).
## Bug Reports
**Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** **DO NOT** report bugs that is already fixed upstream. Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread.
## Building Environment Requirements
1. Python 3.5+: run `build.py` script
@@ -15,20 +23,12 @@
4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## Bug Reports
**Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** Please **DO NOT** report bugs that is already fixed upstream.
Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread.
## Documentation
[Link to Documentation](docs/README.MD)
## License
```
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,

View File

@@ -79,4 +79,12 @@ dependencies {
fullImplementation 'com.github.topjohnwu:libsu:2.0.2'
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
fullImplementation 'org.kamranzafar:jtar:2.3'
def butterKnifeVersion = '9.0.0-rc1'
if (properties.containsKey('android.injected.invoked.from.ide')) {
fullImplementation "com.jakewharton:butterknife-reflect:${butterKnifeVersion}"
} else {
fullImplementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
fullAnnotationProcessor "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
}
}

View File

@@ -15,16 +15,17 @@ import java.util.Locale;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
public class AboutActivity extends BaseActivity {
Toolbar toolbar;
AboutCardRow appVersionInfo;
AboutCardRow appChangelog;
AboutCardRow appTranslators;
AboutCardRow appSourceCode;
AboutCardRow supportThread;
AboutCardRow twitter;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
@BindView(R.id.app_changelog) AboutCardRow appChangelog;
@BindView(R.id.app_translators) AboutCardRow appTranslators;
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
@BindView(R.id.support_thread) AboutCardRow supportThread;
@BindView(R.id.follow_twitter) AboutCardRow twitter;
@Override
public int getDarkTheme() {
@@ -35,7 +36,7 @@ public class AboutActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ViewBinder.bind(this);
new AboutActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -41,7 +41,6 @@ public class Const {
/* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
"android",
Data.MM().getPackageName(),
"com.google.android.gms"
);
@@ -49,11 +48,6 @@ public class Const {
public static final int USER_ID = Process.myUid() / 100000;
public static final class MAGISK_VER {
public static final int UNIFIED = 1300;
public static final int FBE_AWARE = 1410;
public static final int RESETPROP_PERSIST = 1436;
public static final int MANAGER_HIDE = 1440;
public static final int HIDDEN_PATH = 1460;
public static final int REMOVE_LEGACY_LINK = 1630;
public static final int SEPOL_REFACTOR = 1640;
public static final int FIX_ENV = 1650;

View File

@@ -95,8 +95,7 @@ public class Data {
try {
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
} catch (NumberFormatException ignored) {}
}

View File

@@ -10,12 +10,13 @@ import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
public class DonationActivity extends BaseActivity {
Toolbar toolbar;
AboutCardRow paypal;
AboutCardRow patreon;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.paypal) AboutCardRow paypal;
@BindView(R.id.patreon) AboutCardRow patreon;
@Override
public int getDarkTheme() {
@@ -26,7 +27,7 @@ public class DonationActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donation);
ViewBinder.bind(this);
new DonationActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -31,25 +31,25 @@ import java.util.Locale;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.OnClick;
public class FlashActivity extends BaseActivity {
Toolbar toolbar;
TextView flashLogs;
public LinearLayout buttonPanel;
public Button reboot;
ScrollView sv;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.txtLog) TextView flashLogs;
@BindView(R.id.button_panel) public LinearLayout buttonPanel;
@BindView(R.id.reboot) public Button reboot;
@BindView(R.id.scrollView) ScrollView sv;
private List<String> logs;
void dismiss() {
finish();
}
@OnClick(R.id.reboot)
void reboot() {
Shell.su("/system/bin/reboot").submit();
}
@OnClick(R.id.save_logs)
void saveLogs() {
runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
Calendar now = Calendar.getInstance();
@@ -82,7 +82,8 @@ public class FlashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
ViewBinder.bind(this);
new FlashActivity_ViewBinding(this);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
@@ -133,13 +134,18 @@ public class FlashActivity extends BaseActivity {
new InstallMagisk(this, console, logs, InstallMagisk.SECOND_SLOT_MODE).exec();
break;
case Const.Value.PATCH_BOOT:
runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
() -> new InstallMagisk(this, console, logs,
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec());
new InstallMagisk(this, console, logs,
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
break;
}
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button

View File

@@ -38,7 +38,7 @@ public class MagiskManager extends ContainerApp {
Shell.Config.setInitializer(RootUtils.class);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDB = MagiskDatabaseHelper.getInstance(this);
mDB = MagiskDatabaseHelper.getInstance();
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {

View File

@@ -26,6 +26,7 @@ import androidx.appcompat.widget.Toolbar;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import butterknife.BindView;
public class MainActivity extends BaseActivity
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
@@ -34,9 +35,9 @@ public class MainActivity extends BaseActivity
private int mDrawerItem;
private static boolean fromShortcut = false;
public Toolbar toolbar;
DrawerLayout drawer;
NavigationView navigationView;
@BindView(R.id.toolbar) public Toolbar toolbar;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) NavigationView navigationView;
private float toolbarElevation;
@@ -54,7 +55,7 @@ public class MainActivity extends BaseActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewBinder.bind(this);
new MainActivity_ViewBinding(this);
setSupportActionBar(toolbar);
@@ -118,7 +119,6 @@ public class MainActivity extends BaseActivity
public void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED &&
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)

View File

@@ -27,17 +27,18 @@ import java.io.IOException;
import java.io.OutputStream;
import androidx.annotation.Nullable;
import butterknife.BindView;
public class SuRequestActivity extends BaseActivity {
LinearLayout suPopup;
Spinner timeout;
ImageView appIcon;
TextView appNameView;
TextView packageNameView;
Button grant_btn;
Button deny_btn;
ImageView fingerprintImg;
TextView warning;
@BindView(R.id.su_popup) LinearLayout suPopup;
@BindView(R.id.timeout) Spinner timeout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appNameView;
@BindView(R.id.package_name) TextView packageNameView;
@BindView(R.id.grant_btn) Button grant_btn;
@BindView(R.id.deny_btn) Button deny_btn;
@BindView(R.id.fingerprint) ImageView fingerprintImg;
@BindView(R.id.warning) TextView warning;
private SuConnector connector;
private Policy policy;
@@ -157,7 +158,7 @@ public class SuRequestActivity extends BaseActivity {
}
setContentView(R.layout.activity_request);
ViewBinder.bind(this);
new SuRequestActivity_ViewBinding(this);
appIcon.setImageDrawable(policy.info.loadIcon(pm));
appNameView.setText(policy.appName);

View File

@@ -1,293 +0,0 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.view.View;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.fragments.LogFragment;
import com.topjohnwu.magisk.fragments.MagiskFragment;
import com.topjohnwu.magisk.fragments.MagiskHideFragment;
import com.topjohnwu.magisk.fragments.MagiskLogFragment;
import com.topjohnwu.magisk.fragments.ModulesFragment;
import com.topjohnwu.magisk.fragments.ReposFragment;
import com.topjohnwu.magisk.fragments.SuLogFragment;
import com.topjohnwu.magisk.fragments.SuperuserFragment;
import androidx.core.content.ContextCompat;
public class ViewBinder {
public static void bind(MainActivity target) {
target.drawer = target.findViewById(R.id.drawer_layout);
target.toolbar = target.findViewById(R.id.toolbar);
target.navigationView = target.findViewById(R.id.nav_view);
}
public static void bind(AboutActivity target) {
target.toolbar = target.findViewById(R.id.toolbar);
target.appVersionInfo = target.findViewById(R.id.app_version_info);
target.appChangelog = target.findViewById(R.id.app_changelog);
target.appTranslators = target.findViewById(R.id.app_translators);
target.appSourceCode = target.findViewById(R.id.app_source_code);
target.supportThread = target.findViewById(R.id.support_thread);
target.twitter = target.findViewById(R.id.follow_twitter);
}
public static void bind(DonationActivity target) {
target.toolbar = target.findViewById(R.id.toolbar);
target.paypal = target.findViewById(R.id.paypal);
target.patreon = target.findViewById(R.id.patreon);
}
public static void bind(FlashActivity target) {
target.toolbar = target.findViewById(R.id.toolbar);
target.flashLogs = target.findViewById(R.id.txtLog);
target.buttonPanel = target.findViewById(R.id.button_panel);
target.sv = target.findViewById(R.id.scrollView);
target.reboot = target.findViewById(R.id.reboot);
target.reboot.setOnClickListener(v -> target.reboot());
target.findViewById(R.id.no_thanks).setOnClickListener(v -> target.finish());
target.findViewById(R.id.save_logs).setOnClickListener(v -> target.saveLogs());
}
public static void bind(SuRequestActivity target) {
target.suPopup = target.findViewById(R.id.su_popup);
target.timeout = target.findViewById(R.id.timeout);
target.appIcon = target.findViewById(R.id.app_icon);
target.appNameView = target.findViewById(R.id.app_name);
target.packageNameView = target.findViewById(R.id.package_name);
target.grant_btn = target.findViewById(R.id.grant_btn);
target.deny_btn = target.findViewById(R.id.deny_btn);
target.fingerprintImg = target.findViewById(R.id.fingerprint);
target.warning = target.findViewById(R.id.warning);
}
public static void bind(LogFragment target, View v) {
target.viewPager = v.findViewById(R.id.container);
target.tab = v.findViewById(R.id.tab);
}
public static void unbind(LogFragment target) {
target.viewPager = null;
target.tab = null;
}
public static void bind(MagiskFragment target, View v) {
target.mSwipeRefreshLayout = v.findViewById(R.id.swipeRefreshLayout);
target.coreOnlyNotice = v.findViewById(R.id.core_only_notice);
target.magiskUpdate = v.findViewById(R.id.magisk_update);
target.magiskUpdateIcon = v.findViewById(R.id.magisk_update_icon);
target.magiskUpdateText = v.findViewById(R.id.magisk_update_status);
target.magiskUpdateProgress = v.findViewById(R.id.magisk_update_progress);
target.magiskStatusIcon = v.findViewById(R.id.magisk_status_icon);
target.magiskVersionText = v.findViewById(R.id.magisk_version);
target.safetyNetCard = v.findViewById(R.id.safetyNet_card);
target.safetyNetRefreshIcon = v.findViewById(R.id.safetyNet_refresh);
target.safetyNetStatusText = v.findViewById(R.id.safetyNet_status);
target.safetyNetProgress = v.findViewById(R.id.safetyNet_check_progress);
target.expandLayout = v.findViewById(R.id.expand_layout);
target.ctsStatusIcon = v.findViewById(R.id.cts_status_icon);
target.ctsStatusText = v.findViewById(R.id.cts_status);
target.basicStatusIcon = v.findViewById(R.id.basic_status_icon);
target.basicStatusText = v.findViewById(R.id.basic_status);
target.installOptionCard = v.findViewById(R.id.install_option_card);
target.keepEncChkbox = v.findViewById(R.id.keep_force_enc);
target.keepVerityChkbox = v.findViewById(R.id.keep_verity);
target.installButton = v.findViewById(R.id.install_button);
target.installText = v.findViewById(R.id.install_text);
target.uninstallButton = v.findViewById(R.id.uninstall_button);
v.findViewById(R.id.safetyNet_title).setOnClickListener(v1 -> target.safetyNet());
v.findViewById(R.id.install_button).setOnClickListener(v1 -> target.install());
v.findViewById(R.id.uninstall_button).setOnClickListener(v1 -> target.uninstall());
Context ctx = target.getContext();
target.colorBad = ContextCompat.getColor(ctx, R.color.red500);
target.colorOK = ContextCompat.getColor(ctx, R.color.green500);
target.colorWarn = ContextCompat.getColor(ctx, R.color.yellow500);
target.colorNeutral = ContextCompat.getColor(ctx, R.color.grey500);
target.colorInfo = ContextCompat.getColor(ctx, R.color.blue500);
}
public static void unbind(MagiskFragment target) {
target.mSwipeRefreshLayout = null;
target.coreOnlyNotice = null;
target.magiskUpdate = null;
target.magiskUpdateIcon = null;
target.magiskUpdateText = null;
target.magiskUpdateProgress = null;
target.magiskStatusIcon = null;
target.magiskVersionText = null;
target.safetyNetCard = null;
target.safetyNetRefreshIcon = null;
target.safetyNetStatusText = null;
target.safetyNetProgress = null;
target.expandLayout = null;
target.ctsStatusIcon = null;
target.ctsStatusText = null;
target.basicStatusIcon = null;
target.basicStatusText = null;
target.installOptionCard = null;
target.keepEncChkbox = null;
target.keepVerityChkbox = null;
target.installButton = null;
target.installText = null;
target.uninstallButton = null;
View v = target.getView();
v.findViewById(R.id.safetyNet_title).setOnClickListener(null);
v.findViewById(R.id.install_button).setOnClickListener(null);
v.findViewById(R.id.uninstall_button).setOnClickListener(null);
}
public static void bind(MagiskHideFragment target, View v) {
target.mSwipeRefreshLayout = v.findViewById(R.id.swipeRefreshLayout);
target.recyclerView = v.findViewById(R.id.recyclerView);
}
public static void unbind(MagiskHideFragment target) {
target.mSwipeRefreshLayout = null;
target.recyclerView = null;
}
public static void bind(MagiskLogFragment target, View v) {
target.txtLog = v.findViewById(R.id.txtLog);
target.svLog = v.findViewById(R.id.svLog);
target.hsvLog = v.findViewById(R.id.hsvLog);
target.progressBar = v.findViewById(R.id.progressBar);
}
public static void unbind(MagiskLogFragment target) {
target.txtLog = null;
target.svLog = null;
target.hsvLog = null;
target.progressBar = null;
}
public static void bind(ModulesFragment target, View v) {
target.mSwipeRefreshLayout = v.findViewById(R.id.swipeRefreshLayout);
target.recyclerView = v.findViewById(R.id.recyclerView);
target.emptyRv = v.findViewById(R.id.empty_rv);
v.findViewById(R.id.fab).setOnClickListener(v1 -> target.selectFile());
}
public static void unbind(ModulesFragment target) {
target.mSwipeRefreshLayout = null;
target.recyclerView = null;
target.emptyRv = null;
View v = target.getView();
v.findViewById(R.id.fab).setOnClickListener(null);
}
public static void bind(ReposFragment target, View source) {
target.recyclerView = source.findViewById(R.id.recyclerView);
target.emptyRv = source.findViewById(R.id.empty_rv);
target.mSwipeRefreshLayout = source.findViewById(R.id.swipeRefreshLayout);
}
public static void unbind(ReposFragment target) {
target.recyclerView = null;
target.emptyRv = null;
target.mSwipeRefreshLayout = null;
}
public static void bind(SuLogFragment target, View source) {
target.emptyRv = source.findViewById(R.id.empty_rv);
target.recyclerView = source.findViewById(R.id.recyclerView);
}
public static void unbind(SuLogFragment target) {
target.emptyRv = null;
target.recyclerView = null;
}
public static void bind(SuperuserFragment target, View source) {
target.recyclerView = source.findViewById(R.id.recyclerView);
target.emptyRv = source.findViewById(R.id.empty_rv);
}
public static void unbind(SuperuserFragment target) {
target.emptyRv = null;
target.recyclerView = null;
}
public static void bind(CustomAlertDialog.ViewHolder target, View source) {
target.dialogLayout = source.findViewById(R.id.dialog_layout);
target.buttons = source.findViewById(R.id.button_panel);
target.messageView = source.findViewById(R.id.message);
target.negative = source.findViewById(R.id.negative);
target.positive = source.findViewById(R.id.positive);
target.neutral = source.findViewById(R.id.neutral);
}
public static void bind(AboutCardRow target, View source) {
target.mTitle = source.findViewById(android.R.id.title);
target.mSummary = source.findViewById(android.R.id.summary);
target.mIcon = source.findViewById(android.R.id.icon);
target.mView = source.findViewById(R.id.container);
}
public static void bind(ApplicationAdapter.ViewHolder target, View source) {
target.appIcon = source.findViewById(R.id.app_icon);
target.appName = source.findViewById(R.id.app_name);
target.appPackage = source.findViewById(R.id.package_name);
target.checkBox = source.findViewById(R.id.checkbox);
}
public static void bind(ModulesAdapter.ViewHolder target, View source) {
target.title = source.findViewById(R.id.title);
target.versionName = source.findViewById(R.id.version_name);
target.description = source.findViewById(R.id.description);
target.notice = source.findViewById(R.id.notice);
target.checkBox = source.findViewById(R.id.checkbox);
target.author = source.findViewById(R.id.author);
target.delete = source.findViewById(R.id.delete);
}
public static void bind(PolicyAdapter.ViewHolder target, View source) {
target.appName = source.findViewById(R.id.app_name);
target.packageName = source.findViewById(R.id.package_name);
target.appIcon = source.findViewById(R.id.app_icon);
target.masterSwitch = source.findViewById(R.id.master_switch);
target.notificationSwitch = source.findViewById(R.id.notification_switch);
target.loggingSwitch = source.findViewById(R.id.logging_switch);
target.expandLayout = source.findViewById(R.id.expand_layout);
target.delete = source.findViewById(R.id.delete);
target.moreInfo = source.findViewById(R.id.more_info);
}
public static void bind(ReposAdapter.SectionHolder target, View source) {
target.sectionText = source.findViewById(R.id.section_text);
}
public static void bind(ReposAdapter.RepoHolder target, View source) {
target.title = source.findViewById(R.id.title);
target.versionName = source.findViewById(R.id.version_name);
target.description = source.findViewById(R.id.description);
target.author = source.findViewById(R.id.author);
target.infoLayout = source.findViewById(R.id.info_layout);
target.downloadImage = source.findViewById(R.id.download);
target.updateTime = source.findViewById(R.id.update_time);
}
public static void bind(SuLogAdapter.SectionHolder target, View source) {
target.date = source.findViewById(R.id.date);
target.arrow = source.findViewById(R.id.arrow);
}
public static void bind(SuLogAdapter.LogViewHolder target, View source) {
target.appName = source.findViewById(R.id.app_name);
target.action = source.findViewById(R.id.action);
target.time = source.findViewById(R.id.time);
target.fromPid = source.findViewById(R.id.fromPid);
target.toUid = source.findViewById(R.id.toUid);
target.command = source.findViewById(R.id.command);
target.expandLayout = source.findViewById(R.id.expand_layout);
}
}

View File

@@ -3,8 +3,6 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -17,9 +15,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@@ -29,6 +26,7 @@ import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
@@ -52,25 +50,12 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return new ViewHolder(v);
}
private String getLabel(ApplicationInfo info) {
if (info.labelRes > 0) {
try {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
}
return info.loadLabel(pm).toString();
}
private void loadApps() {
fullList = pm.getInstalledApplications(0);
hideList = Shell.su("magiskhide --ls").exec().getOut();
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
i.remove();
}
}
@@ -78,7 +63,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName);
if (ah == bh) {
return getLabel(a).toLowerCase().compareTo(getLabel(b).toLowerCase());
return Utils.getAppLabel(a, pm).toLowerCase()
.compareTo(Utils.getAppLabel(b, pm).toLowerCase());
} else if (ah) {
return -1;
} else {
@@ -93,7 +79,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ApplicationInfo info = showList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(getLabel(info));
holder.appName.setText(Utils.getAppLabel(info, pm));
holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null);
@@ -122,20 +108,20 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
AsyncTask.THREAD_POOL_EXECUTOR.execute(this::loadApps);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView appIcon;
public TextView appName;
public TextView appPackage;
public CheckBox checkBox;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView appPackage;
@BindView(R.id.checkbox) CheckBox checkBox;
ViewHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new ApplicationAdapter$ViewHolder_ViewBinding(this, itemView);
}
}
private class ApplicationFilter extends Filter {
class ApplicationFilter extends Filter {
private boolean lowercaseContains(String s, CharSequence filter) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
@@ -149,7 +135,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) {
if (lowercaseContains(getLabel(info), filter)
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|| lowercaseContains(info.packageName, filter)) {
showList.add(info);
}

View File

@@ -11,14 +11,15 @@ import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.superuser.Shell;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
@@ -28,6 +29,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
mList = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
@@ -101,19 +103,19 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
return mList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
static class ViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView versionName;
public TextView description;
public TextView notice;
public CheckBox checkBox;
public TextView author;
public ImageView delete;
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@BindView(R.id.description) TextView description;
@BindView(R.id.notice) TextView notice;
@BindView(R.id.checkbox) CheckBox checkBox;
@BindView(R.id.author) TextView author;
@BindView(R.id.delete) ImageView delete;
ViewHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new ModulesAdapter$ViewHolder_ViewBinding(this, itemView);
if (!Shell.rootAccess()) {
checkBox.setEnabled(false);

View File

@@ -10,19 +10,22 @@ import android.widget.Switch;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
@@ -37,6 +40,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
this.pm = pm;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
@@ -62,14 +66,34 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
holder.appName.setText(policy.appName);
holder.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
holder.notificationSwitch.setOnCheckedChangeListener(null);
holder.loggingSwitch.setOnCheckedChangeListener(null);
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
holder.masterSwitch.setOnClickListener(v -> {
boolean isChecked = holder.masterSwitch.isChecked();
Runnable r = () -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
};
if (Data.suFingerprint) {
holder.masterSwitch.setChecked(!isChecked);
FingerprintHelper.showAuthDialog((Activity) v.getContext(), () -> {
holder.masterSwitch.setChecked(isChecked);
r.run();
});
} else {
r.run();
}
});
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
@@ -106,9 +130,6 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
.setNegativeButton(R.string.no_thanks, null)
.setCancelable(true)
.show());
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
// Hide for now
holder.moreInfo.setVisibility(View.GONE);
@@ -119,24 +140,24 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
return policyList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
static class ViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
public TextView appName;
public TextView packageName;
public ImageView appIcon;
public Switch masterSwitch;
public Switch notificationSwitch;
public Switch loggingSwitch;
public ViewGroup expandLayout;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView packageName;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.master_switch) Switch masterSwitch;
@BindView(R.id.notification_switch) Switch notificationSwitch;
@BindView(R.id.logging_switch) Switch loggingSwitch;
@BindView(R.id.expand_layout) ViewGroup expandLayout;
public ImageView delete;
public ImageView moreInfo;
@BindView(R.id.delete) ImageView delete;
@BindView(R.id.more_info) ImageView moreInfo;
private Container container = new Container();
public ViewHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new PolicyAdapter$ViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -12,7 +12,6 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.BaseActivity;
@@ -26,6 +25,7 @@ import java.util.List;
import java.util.Map;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
@@ -163,29 +163,29 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
notifyDataSetChanged();
}
public static class SectionHolder extends RecyclerView.ViewHolder {
static class SectionHolder extends RecyclerView.ViewHolder {
public TextView sectionText;
@BindView(R.id.section_text) TextView sectionText;
SectionHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new ReposAdapter$SectionHolder_ViewBinding(this, itemView);
}
}
public static class RepoHolder extends RecyclerView.ViewHolder {
static class RepoHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView versionName;
public TextView description;
public TextView author;
public LinearLayout infoLayout;
public ImageView downloadImage;
public TextView updateTime;
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@BindView(R.id.description) TextView description;
@BindView(R.id.author) TextView author;
@BindView(R.id.info_layout) LinearLayout infoLayout;
@BindView(R.id.download) ImageView downloadImage;
@BindView(R.id.update_time) TextView updateTime;
RepoHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new ReposAdapter$RepoHolder_ViewBinding(this, itemView);
}
}

View File

@@ -10,7 +10,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
@@ -21,6 +20,7 @@ import java.util.List;
import java.util.Set;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
@@ -116,32 +116,32 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
notifyDataSetChanged();
}
public static class SectionHolder extends RecyclerView.ViewHolder {
static class SectionHolder extends RecyclerView.ViewHolder {
public TextView date;
public ImageView arrow;
@BindView(R.id.date) TextView date;
@BindView(R.id.arrow) ImageView arrow;
SectionHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new SuLogAdapter$SectionHolder_ViewBinding(this, itemView);
}
}
public static class LogViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
static class LogViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
public TextView appName;
public TextView action;
public TextView time;
public TextView fromPid;
public TextView toUid;
public TextView command;
public ViewGroup expandLayout;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.action) TextView action;
@BindView(R.id.time) TextView time;
@BindView(R.id.fromPid) TextView fromPid;
@BindView(R.id.toUid) TextView toUid;
@BindView(R.id.command) TextView command;
@BindView(R.id.expand_layout) ViewGroup expandLayout;
private Container container = new Container();
LogViewHolder(View itemView) {
super(itemView);
ViewBinder.bind(this, itemView);
new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -111,6 +111,7 @@ public class PatchAPK {
repack.delete();
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
mm.mDB.flush();
Data.exportPrefs();
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);

View File

@@ -66,17 +66,15 @@ public class UpdateRepos {
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject rawRepo = jsonArray.getJSONObject(i);
String id = rawRepo.getString("description");
String name = rawRepo.getString("name");
String id = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
Set<String> set = Collections.synchronizedSet(cached);
threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id);
try {
if (repo == null)
repo = new Repo(name);
repo = new Repo(id);
else
set.remove(id);
cached.remove(id);
repo.update(date);
mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
@@ -141,7 +139,7 @@ public class UpdateRepos {
public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = mm.repoDB.getRepoIDSet();
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPage(0)) {

View File

@@ -27,17 +27,18 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import butterknife.BindView;
/**
* @author dvdandroid
*/
public class AboutCardRow extends LinearLayout {
public TextView mTitle;
public TextView mSummary;
public ImageView mIcon;
public View mView;
@BindView(android.R.id.title) TextView mTitle;
@BindView(android.R.id.summary) TextView mSummary;
@BindView(android.R.id.icon) ImageView mIcon;
@BindView(R.id.container) View mView;
public AboutCardRow(Context context) {
this(context, null);
@@ -50,7 +51,7 @@ public class AboutCardRow extends LinearLayout {
public AboutCardRow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.info_item_row, this);
ViewBinder.bind(this, this);
new AboutCardRow_ViewBinding(this, this);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
String title;

View File

@@ -7,10 +7,12 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
public MagiskManager mm;
protected Unbinder unbinder = null;
public BaseFragment() {
mm = Data.MM();
@@ -28,6 +30,13 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (unbinder != null)
unbinder.unbind();
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, this::onActivityResult);

View File

@@ -9,13 +9,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
import butterknife.BindView;
public class CustomAlertDialog extends AlertDialog.Builder {
@@ -27,16 +27,16 @@ public class CustomAlertDialog extends AlertDialog.Builder {
private ViewHolder vh;
public class ViewHolder {
public LinearLayout dialogLayout;
public LinearLayout buttons;
@BindView(R.id.dialog_layout) public LinearLayout dialogLayout;
@BindView(R.id.button_panel) public LinearLayout buttons;
public TextView messageView;
public Button negative;
public Button positive;
public Button neutral;
@BindView(R.id.message) public TextView messageView;
@BindView(R.id.negative) public Button negative;
@BindView(R.id.positive) public Button positive;
@BindView(R.id.neutral) public Button neutral;
ViewHolder(View v) {
ViewBinder.bind(this, v);
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
messageView.setVisibility(View.GONE);
negative.setVisibility(View.GONE);
positive.setVisibility(View.GONE);

View File

@@ -1,11 +1,12 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
@@ -27,23 +28,21 @@ class InstallMethodDialog extends AlertDialog.Builder {
Intent intent;
switch (idx) {
case 1:
if (Data.remoteMagiskVersionCode < 1400) {
SnackbarMaker.make(activity, R.string.no_boot_file_patch_support,
Snackbar.LENGTH_LONG).show();
return;
}
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == BaseActivity.RESULT_OK && data != null) {
Intent i = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
activity.startActivity(i);
}
});
activity.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == Activity.RESULT_OK && data != null) {
Intent i = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
activity.startActivity(i);
}
})
);
break;
case 0:
String filename = Utils.fmt("Magisk-v%s(%d).zip",
@@ -62,16 +61,16 @@ class InstallMethodDialog extends AlertDialog.Builder {
break;
case 3:
new CustomAlertDialog(activity)
.setTitle(R.string.warning)
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent it = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(it);
})
.setNegativeButton(R.string.no_thanks, null)
.show();
.setTitle(R.string.warning)
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent it = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(it);
})
.setNegativeButton(R.string.no_thanks, null)
.show();
break;
default:
}

View File

@@ -5,6 +5,8 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
@@ -26,7 +28,7 @@ public class Policy implements Comparable<Policy>{
this.uid = uid;
packageName = pkgs[0];
info = pm.getApplicationInfo(packageName, 0);
appName = info.loadLabel(pm).toString();
appName = Utils.getAppLabel(info, pm);
}
public Policy(Cursor c, PackageManager pm) throws PackageManager.NameNotFoundException {

View File

@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.Download;
@@ -15,16 +14,14 @@ import java.util.Date;
public class Repo extends BaseModule {
private String repoName;
private Date mLastUpdate;
public Repo(String name) {
repoName = name;
public Repo(String id) {
setId(id);
}
public Repo(Cursor c) {
super(c);
repoName = c.getString(c.getColumnIndex("repo_name"));
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
}
@@ -33,17 +30,14 @@ public class Repo extends BaseModule {
try {
parseProps(props);
} catch (NumberFormatException e) {
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
throw new IllegalRepoException("Repo [" + getId() + "] parse error: " + e.getMessage());
}
if (TextUtils.isEmpty(getId())) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain id");
}
if (getVersionCode() < 0) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
}
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
Logger.debug("Repo [" + repoName + "] is outdated");
Logger.debug("Repo [" + getId() + "] is outdated");
}
}
@@ -55,25 +49,20 @@ public class Repo extends BaseModule {
@Override
public ContentValues getContentValues() {
ContentValues values = super.getContentValues();
values.put("repo_name", repoName);
values.put("last_update", mLastUpdate.getTime());
return values;
}
public String getRepoName() {
return repoName;
}
public String getZipUrl() {
return String.format(Const.Url.ZIP_URL, repoName);
return String.format(Const.Url.ZIP_URL, getId());
}
public String getManifestUrl() {
return String.format(Const.Url.FILE_URL, repoName, "module.prop");
return String.format(Const.Url.FILE_URL, getId(), "module.prop");
}
public String getDetailUrl() {
return String.format(Const.Url.FILE_URL, repoName, "README.md");
return String.format(Const.Url.FILE_URL, getId(), "README.md");
}
public String getLastUpdateString() {

View File

@@ -38,26 +38,30 @@ public class MagiskDatabaseHelper {
private static final String LOG_TABLE = "logs";
private static final String SETTINGS_TABLE = "settings";
private static final String STRINGS_TABLE = "strings";
private static final File MANAGER_DB =
new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
private PackageManager pm;
private SQLiteDatabase db;
private MagiskManager mm;
@NonNull
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
public static MagiskDatabaseHelper getInstance() {
try {
return new MagiskDatabaseHelper(mm);
return new MagiskDatabaseHelper();
} catch (Exception e) {
// Let's cleanup everything and try again
Shell.su("db_clean '*'").exec();
return new MagiskDatabaseHelper(mm);
return new MagiskDatabaseHelper();
}
}
private MagiskDatabaseHelper(MagiskManager context) {
mm = context;
pm = mm.getPackageManager();
db = openDatabase(mm);
private MagiskDatabaseHelper() {
pm = Data.MM().getPackageManager();
init();
}
private void init() {
db = openDatabase();
db.disableWriteAheadLogging();
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
int curVersion = db.getVersion();
@@ -71,48 +75,37 @@ public class MagiskDatabaseHelper {
clearOutdated();
}
private SQLiteDatabase openDatabase(MagiskManager mm) {
final File DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
private SQLiteDatabase openDatabase() {
MagiskManager mm = Data.MM();
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? mm.createDeviceProtectedStorageContext() : mm;
if (!DB_FILE.canWrite()) {
if (!MANAGER_DB.canWrite()) {
if (!Shell.rootAccess()) {
// We don't want the app to crash, create a db and return
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
}
// Cleanup
Shell.su("db_clean " + Const.USER_ID).exec();
if (Data.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
// Super old legacy mode
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
// Legacy mode with FBE aware
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
de.moveDatabaseFrom(mm, "su.db");
}
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
} else {
// Global database
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
mm.deleteDatabase("su.db");
de.deleteDatabase("su.db");
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
// Global database
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
mm.deleteDatabase("su.db");
de.deleteDatabase("su.db");
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
Shell.su("db_setup " + Process.myUid()).exec();
}
// Not using legacy mode, open the mounted global DB
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
return SQLiteDatabase.openOrCreateDatabase(MANAGER_DB, null);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion) {
private void onUpgrade(SQLiteDatabase db, int oldVersion) {
if (oldVersion == 0) {
createTables(db);
oldVersion = 3;
@@ -147,13 +140,13 @@ public class MagiskDatabaseHelper {
}
if (oldVersion == 5) {
setSettings(Const.Key.SU_FINGERPRINT,
mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
Data.MM().prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
++oldVersion;
}
}
// Remove everything, we do not support downgrade
public void onDowngrade(SQLiteDatabase db) {
private void onDowngrade(SQLiteDatabase db) {
Utils.toast(R.string.su_db_corrupt, Toast.LENGTH_LONG);
db.execSQL("DROP TABLE IF EXISTS " + POLICY_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE);
@@ -182,6 +175,11 @@ public class MagiskDatabaseHelper {
"(key TEXT, value INT, PRIMARY KEY(key))");
}
public void flush() {
db.close();
init();
}
public void clearOutdated() {
// Clear outdated policies
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);

View File

@@ -16,7 +16,7 @@ import java.util.Set;
public class RepoDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 3;
private static final int DATABASE_VER = 4;
private static final String TABLE_NAME = "repos";
private SQLiteDatabase mDb;
@@ -40,21 +40,14 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
if (oldVersion < 3) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
"PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
oldVersion = 3;
}
} catch (Exception e) {
e.printStackTrace();
// Reset database
onDowngrade(db, DATABASE_VER, 0);
if (oldVersion != newVersion) {
// Nuke old DB and create new table
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
}
}
@@ -75,8 +68,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
}
public void removeRepo(Repo repo) {
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
notifyAdapter();
removeRepo(repo.getId());
}
public void removeRepo(Iterable<String> list) {

View File

@@ -11,23 +11,23 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
public class LogFragment extends BaseFragment {
public ViewPager viewPager;
public TabLayout tab;
@BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false);
ViewBinder.bind(this, v);
unbinder = new LogFragment_ViewBinding(this, v);
((MainActivity) requireActivity()).toolbar.setElevation(0);
@@ -44,10 +44,4 @@ public class LogFragment extends BaseFragment {
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
}

View File

@@ -20,7 +20,6 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.components.BaseActivity;
@@ -42,6 +41,9 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.cardview.widget.CardView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.OnClick;
public class MagiskFragment extends BaseFragment
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
@@ -49,41 +51,42 @@ public class MagiskFragment extends BaseFragment
private Container expandableContainer = new Container();
private static boolean shownDialog = false;
public SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.swipeRefreshLayout) public SwipeRefreshLayout mSwipeRefreshLayout;
public CardView coreOnlyNotice;
@BindView(R.id.core_only_notice) CardView coreOnlyNotice;
public RelativeLayout magiskUpdate;
public ImageView magiskUpdateIcon;
public TextView magiskUpdateText;
public ProgressBar magiskUpdateProgress;
public ImageView magiskStatusIcon;
public TextView magiskVersionText;
@BindView(R.id.magisk_update) RelativeLayout magiskUpdate;
@BindView(R.id.magisk_update_icon) ImageView magiskUpdateIcon;
@BindView(R.id.magisk_update_status) TextView magiskUpdateText;
@BindView(R.id.magisk_update_progress) ProgressBar magiskUpdateProgress;
@BindView(R.id.magisk_status_icon) ImageView magiskStatusIcon;
@BindView(R.id.magisk_version) TextView magiskVersionText;
public CardView safetyNetCard;
public ImageView safetyNetRefreshIcon;
public TextView safetyNetStatusText;
public ProgressBar safetyNetProgress;
public LinearLayout expandLayout;
public ImageView ctsStatusIcon;
public TextView ctsStatusText;
public ImageView basicStatusIcon;
public TextView basicStatusText;
@BindView(R.id.safetyNet_card) CardView safetyNetCard;
@BindView(R.id.safetyNet_refresh) ImageView safetyNetRefreshIcon;
@BindView(R.id.safetyNet_status) TextView safetyNetStatusText;
@BindView(R.id.safetyNet_check_progress) ProgressBar safetyNetProgress;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.cts_status_icon) ImageView ctsStatusIcon;
@BindView(R.id.cts_status) TextView ctsStatusText;
@BindView(R.id.basic_status_icon) ImageView basicStatusIcon;
@BindView(R.id.basic_status) TextView basicStatusText;
public CardView installOptionCard;
public CheckBox keepEncChkbox;
public CheckBox keepVerityChkbox;
public CardView installButton;
public TextView installText;
public CardView uninstallButton;
@BindView(R.id.install_option_card) CardView installOptionCard;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@BindView(R.id.install_button) CardView installButton;
@BindView(R.id.install_text) TextView installText;
@BindView(R.id.uninstall_button) CardView uninstallButton;
public int colorBad;
public int colorOK;
public int colorWarn;
public int colorNeutral;
public int colorInfo;
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.green500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
public void safetyNet() {
@OnClick(R.id.safetyNet_title)
void safetyNet() {
Runnable task = () -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetRefreshIcon.setVisibility(View.GONE);
@@ -106,7 +109,8 @@ public class MagiskFragment extends BaseFragment
}
public void install() {
@OnClick(R.id.install_button)
void install() {
shownDialog = true;
// Show Manager update first
@@ -119,7 +123,8 @@ public class MagiskFragment extends BaseFragment
new MagiskInstallDialog((BaseActivity) getActivity()).show();
}
public void uninstall() {
@OnClick(R.id.uninstall_button)
void uninstall() {
new UninstallDialog(requireActivity()).show();
}
@@ -128,7 +133,7 @@ public class MagiskFragment extends BaseFragment
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
ViewBinder.bind(this, v);
unbinder = new MagiskFragment_ViewBinding(this, v);
requireActivity().setTitle(R.string.magisk);
expandableContainer.expandLayout = expandLayout;
@@ -145,12 +150,6 @@ public class MagiskFragment extends BaseFragment
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
@Override
public void onRefresh() {
Data.loadMagiskInfo();
@@ -215,11 +214,10 @@ public class MagiskFragment extends BaseFragment
boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasRoot = Shell.rootAccess();
boolean isUpToDate = Data.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(isUpToDate && hasRoot ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(hasRoot ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
int image, color;

View File

@@ -9,7 +9,6 @@ import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.utils.Topic;
@@ -18,11 +17,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber {
public SwipeRefreshLayout mSwipeRefreshLayout;
public RecyclerView recyclerView;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
SearchView search;
private ApplicationAdapter appAdapter;
@@ -39,7 +39,7 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
ViewBinder.bind(this, view);
unbinder = new MagiskHideFragment_ViewBinding(this, view);
appAdapter = new ApplicationAdapter(requireActivity());
recyclerView.setAdapter(appAdapter);
@@ -73,12 +73,6 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
search.setOnQueryTextListener(searchListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
@Override
public int[] getSubscribedTopics() {
return new int[] {Topic.MAGISK_HIDE_DONE};

View File

@@ -17,7 +17,6 @@ import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Download;
@@ -29,19 +28,20 @@ import java.io.IOException;
import java.util.Calendar;
import androidx.annotation.Nullable;
import butterknife.BindView;
public class MagiskLogFragment extends BaseFragment {
public TextView txtLog;
public ScrollView svLog;
public HorizontalScrollView hsvLog;
public ProgressBar progressBar;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@BindView(R.id.progressBar) ProgressBar progressBar;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
ViewBinder.bind(this, view);
unbinder = new MagiskLogFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
txtLog.setTextIsSelectable(true);
return view;
@@ -59,12 +59,6 @@ public class MagiskLogFragment extends BaseFragment {
readLogs();
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
@@ -87,7 +81,7 @@ public class MagiskLogFragment extends BaseFragment {
}
}
public void readLogs() {
private void readLogs() {
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
progressBar.setVisibility(View.GONE);
if (result.getOut().isEmpty())
@@ -99,7 +93,7 @@ public class MagiskLogFragment extends BaseFragment {
});
}
public void saveLogs() {
private void saveLogs() {
Calendar now = Calendar.getInstance();
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
@@ -117,7 +111,7 @@ public class MagiskLogFragment extends BaseFragment {
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
}
public void clearLogs() {
private void clearLogs() {
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
txtLog.setText(R.string.log_is_empty);
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();

View File

@@ -16,7 +16,6 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Module;
@@ -32,14 +31,17 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
import butterknife.OnClick;
public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public SwipeRefreshLayout mSwipeRefreshLayout;
public RecyclerView recyclerView;
public TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
public void selectFile() {
@OnClick(R.id.fab)
void selectFile() {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
@@ -53,7 +55,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false);
ViewBinder.bind(this, view);
unbinder = new ModulesFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
@@ -98,12 +100,6 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_reboot, menu);

View File

@@ -14,7 +14,6 @@ import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.BaseFragment;
@@ -27,12 +26,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
public RecyclerView recyclerView;
public TextView emptyRv;
public SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
private ReposAdapter adapter;
@@ -46,7 +46,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_repos, container, false);
ViewBinder.bind(this, view);
unbinder = new ReposFragment_ViewBinding(this, view);
mSwipeRefreshLayout.setRefreshing(true);
recyclerView.setVisibility(View.GONE);
@@ -120,6 +120,5 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
public void onDestroyView() {
super.onDestroyView();
mm.repoDB.unregisterAdapter();
ViewBinder.unbind(this);
}
}

View File

@@ -3,15 +3,9 @@ package com.topjohnwu.magisk.fragments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -24,7 +18,6 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.FingerprintHelper;
@@ -131,7 +124,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
fingerprint.setSummary(R.string.disable_fingerprint);
}
if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
if (Shell.rootAccess()) {
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
hideManager.setOnPreferenceClickListener((pref) -> {
PatchAPK.hideManager(requireActivity());
@@ -178,8 +171,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory);
generalCatagory.removePreference(hideManager);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
prefScreen.removePreference(magiskCategory);
}
}
@@ -277,54 +268,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.SU_FINGERPRINT:
boolean checked = ((SwitchPreference) preference).isChecked();
((SwitchPreference) preference).setChecked(!checked);
CustomAlertDialog dialog = new CustomAlertDialog(requireActivity());
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
Drawable fingerprint = getResources().getDrawable(R.drawable.ic_fingerprint);
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
Resources.Theme theme = requireActivity().getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
try {
FingerprintHelper helper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
dialog.dismiss();
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
}
};
dialog.setMessage(R.string.auth_fingerprint)
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
.setOnCancelListener(d -> helper.cancel())
.show();
helper.authenticate();
} catch (Exception e) {
e.printStackTrace();
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
}
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
((SwitchPreference) preference).setChecked(checked);
Data.suFingerprint = checked;
mm.mDB.setSettings(key, checked ? 1 : 0);
});
break;
}
return true;

View File

@@ -10,17 +10,17 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class SuLogFragment extends BaseFragment {
public TextView emptyRv;
public RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private SuLogAdapter adapter;
@@ -41,7 +41,7 @@ public class SuLogFragment extends BaseFragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
ViewBinder.bind(this, v);
unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB);
recyclerView.setAdapter(adapter);
@@ -76,10 +76,4 @@ public class SuLogFragment extends BaseFragment {
return true;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
}

View File

@@ -8,7 +8,6 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ViewBinder;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Policy;
@@ -17,11 +16,12 @@ import java.util.List;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class SuperuserFragment extends BaseFragment {
public RecyclerView recyclerView;
public TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
private PackageManager pm;
@@ -29,9 +29,9 @@ public class SuperuserFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
ViewBinder.bind(this, view);
unbinder = new SuperuserFragment_ViewBinding(this, view);
pm = getActivity().getPackageManager();
pm = requireActivity().getPackageManager();
return view;
}
@@ -47,12 +47,6 @@ public class SuperuserFragment extends BaseFragment {
displayPolicyList();
}
@Override
public void onDestroyView() {
super.onDestroyView();
ViewBinder.unbind(this);
}
private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList(pm);

View File

@@ -45,8 +45,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(0)
.build());
}
if (root && Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
.setShortLabel(mm.getString(R.string.magiskhide))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))

View File

@@ -1,17 +1,26 @@
package com.topjohnwu.magisk.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.view.Gravity;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import java.security.KeyStore;
@@ -35,6 +44,56 @@ public abstract class FingerprintHelper {
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
}
public static void showAuthDialog(Activity activity, Runnable onSuccess) {
CustomAlertDialog dialog = new CustomAlertDialog(activity);
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
try {
FingerprintHelper helper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
dialog.dismiss();
onSuccess.run();
}
};
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
Resources.Theme theme = activity.getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
dialog.setMessage(R.string.auth_fingerprint)
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
.setOnCancelListener(d -> helper.cancel())
.show();
helper.authenticate();
} catch (Exception e) {
e.printStackTrace();
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
}
}
protected FingerprintHelper() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class);

View File

@@ -6,6 +6,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
@@ -123,4 +127,17 @@ public class Utils {
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
});
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
}

View File

@@ -42,7 +42,7 @@
android:visibility="gone">
<Button
android:id="@+id/no_thanks"
android:id="@+id/close"
style="?android:borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="50dp"

View File

@@ -1,7 +1,7 @@
### 6.0.0
- Update to latest AndroidX support library
- Fix crashes when online repos contain incomplete metadata
- Optimize BootSigner to use as little memory as possible, prevent OutOfMemoryError
- Support new communication scheme between Magisk v17.2 and Magisk Manager
- Enable excessive obfuscation to prevent APK analysis root detections (still not 100% obfuscated due to backwards compatibility with stable channel)
### 6.0.1
- Update to use new online module's organizing method
- When fingerprint authentication is enabled, toggling root permissions in "Superuser" section now requires fingerprint beforehand
- Fix crashes when entering MagiskHide section on some devices
- Remove support to Magisk version lower than v15.0
- Ask storage permissions before patching stock boot image
- Update dark theme CardView color

View File

@@ -1,5 +1,5 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="modules">Modüller</string>
<string name="downloads">İndir</string>
@@ -62,12 +62,13 @@
<!--About Activity-->
<string name="about">Hakkında</string>
<string name="app_changelog">Uygulama değişiklikleri</string>
<string name="translators">Fatih FIRINCI - Mevlüt TOPÇU</string>
<string name="translators">Fatih FIRINCI - Mevlüt TOPÇU - Muhammet Emin TURGUT</string>
<string name="app_version">Uygulama sürümü</string>
<string name="app_source_code">Kaynak kodu</string>
<string name="donation">Bağış Yapın</string>
<string name="app_translators">Çevirmenler</string>
<string name="support_thread">Destek konusu</string>
<string name="follow_twitter">Beni Twitter\'da takip et</string>
<!--Toasts, Dialogs-->
<string name="close">Kapat</string>
@@ -94,14 +95,19 @@
<string name="hide_manager_toast">Magisk Manager Gizleniyor…</string>
<string name="hide_manager_toast2">Bu biraz zaman alabilir…</string>
<string name="hide_manager_fail_toast">Magisk Manager\'ı Gizleme başarısız oldu…</string>
<string name="open_link_failed_toast">Bağlantıyı açabilecek uygulama bulunamadı</string>
<string name="download_zip_only">Yalnızca Zip Dosyasını İndir</string>
<string name="patch_boot_file">Önyükleme İmaj Dosyasını Yamala</string>
<string name="direct_install">Doğrudan Yükle (Önerilen)</string>
<string name="install_inactive_slot">Pasif yuvaya yükle (OTA\'dan sonra)</string>
<string name="warning">Uyarı</string>
<string name="install_inactive_slot_msg">Cihazınız yeniden başlatıldıktan sonra mevcut pasif yuvaya ZORLA önyüklenecek!\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.\nDevam mı?</string>
<string name="select_method">Yöntem Seçin</string>
<string name="no_boot_file_patch_support">Hedef Magisk sürümü önyükleme imaj dosyasını yamalamayı desteklemiyor</string>
<string name="boot_file_patch_msg">.img veya .img.tar formatında stok önyükleme imajını seçin</string>
<string name="complete_uninstall">Tamamen Kaldır</string>
<string name="restore_img">Önyükleme İmajını Geri Yükle</string>
<string name="restore_img_msg">Geri Yükleniyor…</string>
<string name="restore_done">Yenileme tamamlandı!</string>
<string name="restore_fail">Stok önyükleme yedeği yok!</string>
<string name="proprietary_title">Tescil Kodunu İndirin</string>
@@ -109,8 +115,10 @@
<string name="su_db_corrupt">SU veritabanı bozuk, yeni db oluşturacak</string>
<string name="setup_done">Kurulum tamamlandı</string>
<string name="setup_fail">Kurulum başarısız</string>
<string name="env_fix_title">Ek Kurulum İste</string>
<string name="env_fix_title">Ek Kurulum Gerekli</string>
   <string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
<string name="setup_title">Ek Kurulum</string>
<string name="setup_msg">Ortam Kurulumu Çalışıyor…</string>
<!--Settings Activity -->
<string name="settings_general_category">Genel</string>
@@ -157,6 +165,7 @@
<string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string>
<string name="settings_su_fingerprint_title">Parmak İzi Kimlik Doğrulamayı Etkinleştir</string>
<string name="settings_su_fingerprint_summary">Yetkili kullanıcı isteklerine izin vermek için parmak izi tarayıcısını kullan</string>
<string name="auth_fingerprint">Parmak izini doğrula</string>
<string name="multiuser_mode">Çok Kullanıcılı Mod</string>
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>

View File

@@ -39,11 +39,11 @@
</style>
<style name="CardViewStyle.Dark" parent="CardView">
<item name="cardBackgroundColor">@android:color/background_dark</item>
<item name="cardBackgroundColor">@color/card_background_color_dark</item>
</style>
<style name="CardViewStyle.Light" parent="CardView">
<item name="cardBackgroundColor">@android:color/background_light</item>
<item name="cardBackgroundColor">@color/card_background_color_light</item>
</style>
<style name="AppTheme.StatusBar" parent="AppTheme">

View File

@@ -20,4 +20,8 @@
<color name="flashing_background_color">@android:color/black</color>
<color name="ic_launcher_background">#00AF9C</color>
<!-- Card colors -->
<color name="card_background_color_dark">#ff424242</color>
<color name="card_background_color_light">#ffffffff</color>
</resources>

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0-rc03'
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
@@ -25,8 +25,8 @@ allprojects {
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.2"
androidXVersion = "1.0.0-rc02"
buildToolsVersion = "28.0.3"
androidXVersion = "1.0.0"
}
task clean(type: Delete) {

View File

@@ -170,12 +170,12 @@ def build_binary(args):
header('* Building binaries: ' + ' '.join(args.target))
# Force update logging.h timestamp to trigger recompilation for the flags to make a difference
os.utime(os.path.join('native', 'jni', 'include', 'logging.h'))
os.utime(os.path.join('native', 'jni', 'include', 'flags.h'))
# Basic flags
base_flags = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={} MAGISK_DEBUG={}'.format(config['version'], config['versionCode'],
'' if args.release else '-DMAGISK_DEBUG')
base_flags = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={}'.format(config['version'], config['versionCode'])
if not args.release:
base_flags += ' MAGISK_DEBUG=1'
if 'magisk' in args.target:
# Magisk is special case as it is a dependency of magiskinit

View File

@@ -1,36 +0,0 @@
# Magisk Documentations
(Updated on 2018.1.8) ([Changelog](changelog.md))
## Table of Contents
- [Introduction](#introduction)
- [Tips and Tricks](tips.md)
- [OTA Installation Tips](tips.md#ota-installation-tips)
The following are for developers
- [Procedure Diagram](https://cdn.rawgit.com/topjohnwu/Magisk/7d1082b1cb91db90ed0a29d8b092723fc3d69c58/docs/procedures.html)
- [Magisk Details](details.md)
- [Boot Stages](details.md#boot-stages)
- [Magic Mount Details](details.md#magic-mount-details)
- [Simple Mount Details](details.md#simple-mount-details)
- [Available Tools](tools.md)
- [magiskboot](tools.md#magiskboot)
- [magiskinit](tools.md#magiskinit)
- [magiskpolicy](tools.md#magiskpolicy)
- [magisk](tools.md#magisk)
- [su](tools.md#su)
- [resetprop](tools.md#resetprop)
- [magiskhide](tools.md#magiskhide)
- [Modules](modules.md)
- [Modules and Templates](modules.md#magisk-module-format)
- [Submit Modules to Repo](https://github.com/topjohnwu/Magisk_Repo_Submissions)
- [Tips and Tricks](tips.md)
- [Remove Files](tips.md#remove-files)
- [Remove Folders](tips.md#remove-folders)
## Introduction
Magisk is a suite of open source tools for devices running Android version higher than 5.0 Lollipop (API 21). It establishes an environment which covers most stuffs you need for Android customization, such as root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt patches etc..
Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact, all of which accomplished by only patching the boot image. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any existing system integrity verifications, one of the main target is to hide from [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).

29
docs/README.md Normal file
View File

@@ -0,0 +1,29 @@
# Magisk Documentations
(Updated on 2018.10.16)
- [Tutorials](tutorials.md)
- [OTA Installation](tutorials.md#ota-installation)
- [Best Practices for MagiskHide](tutorials.md#best-practices-for-magiskhide)
The followings are for developers
- [Magisk Details](details.md)
- [File Structure](details.md#file-structure)
- [Magisk Booting Process](details.md#magisk-booting-process)
- [Resetprop](details.md#resetprop)
- [Magic Mount](details.md#magic-mount)
- [Simple Mount](details.md#simple-mount)
- [Miscellaneous](details.md#Miscellaneous)
- [Installation](install.md)
- [Systemless](install.md#systemless)
- [System Only](install.md#system-only)
- [Exploits](install.md#Exploits)
- [Magisk Tools](tools.md)
- [Developer Guides](guides.md)
- [Scripts](guides.md#scripts)
- [Magisk Modules](guides.md#magisk-modules)
- [Magisk Module Template](guides.md#magisk-module-template)
- [Submit Modules to Magisk-Modules-Repo](https://github.com/Magisk-Modules-Repo/submission)
- [Remove Files](guides.md#remove-files)
- [Remove Folders](guides.md#remove-folders)

View File

@@ -1,8 +0,0 @@
# Changelog
- 2017.8.16
- Initial version for Magisk v13.5
- 2017.9.28
- Update applets info to Magisk v14.1
- Add OTA tips
- 2017.1.8
- Update to Magisk v15.2

View File

@@ -1,59 +1,145 @@
## Boot Stages
If you are working on complicated projects, you shall need more control to the whole process. Magisk can run scripts in different boot stages, so you can fine tune exactly what you want to do. It's recommended to read this documentation along with the procedure graph.
# Magisk Details
## File Structure
### Paths in "sbin tmpfs overlay"
sbin tmpfs overlay is the key to hiding Magisk from detection. All Magisk binaries, applets, mirrors, mountpoints, loop devices, and other trivial stuffs are all located in the `tmpfs` mounted on `/sbin`. MagiskHide can just simply unmount `/sbin` and the bind mounts into `/system` and `/vendor` to hide all modifications easily.
- post-fs mode
- **This stage is BLOCKING. Boot process will NOT continue until everything is done, or 10 seconds has passed**
- Happens after most partitions are mounted. `/data` might not be available since `vold` is not started yet
- Magisk will bind mount files under `/cache/magisk_mount/system` and `/cache/magisk_mount/vendor`
- It is only **Simple Mount**, which means it will replace existing files, but cannot add/remove files.
- This part is mostly deprecated (reasons in details)
- post-fs-data mode
- **This stage is BLOCKING. Boot process will NOT continue until everything is done, or 10 seconds has passed**
- Happens after `/data` is ready (including the case when `/data` is encrypted)
- Happens before Zygote and system servers are started (which means pretty much everything)
- `/data/adb/magisk.img` will be merged, trimmed, and mounted to `MOUNTPOINT=/sbin/.core/img`
- Magisk will run scripts under `$MOUNTPOINT/.core/post-fs-data.d`
- Magisk will run scripts: `$MOUNTPOINT/$MODID/post-fs-data.sh` (placed in each module directory)
- Magisk will finally **Magisk Mount** module files
- late_start service mode
- **This stage is NON-BLOCKING, it will run in parallel with other processes**
- Happens when class late_start is triggered
- The daemon will wait for the full `sepolicy` patch before running this stage, so SELinux is guaranteed to be fully patched
- Put time consuming scripts here. Boot process will get stuck if it took too long to finish your tasks in `post-fs-data`
- **It is recommended to run all scripts in this stage**, unless your scripts requires doing stuffs before Zygote is started
- Magisk will run scripts under `$MOUNTPOINT/.core/service.d`
- Magisk will run scripts: `$MOUNTPOINT/$MODID/service.sh` (placed in each module directory)
```
# The actual Magisk binary
/sbin/magisk.bin
# Wrapper script to prevent stupid linker errors
/sbin/magisk
## Magic Mount Details
### Terminology
- **Item**: A folder, file, or symbolic link
- **Leaf**: An item that is on the very end of a directory structure tree. It can be either a file or symbolic link
- **`$MODPATH`**: A variable to represent the path of a module folder
- **Source item**: An item under `$MODPATH/system`, for example, `$MODPATH/system/bin/app_process32` is a source item
- **Existing item**: An item in the actual filesystem, for example, `/system/bin/app_process32` is an existing item
- **Target item**: The corresponding item of a source item. For example, the target item of `$MODPATH/system/bin/app_process32` is `/system/bin/app_process32`
# Other binaries like magiskinit, and all symlinks to
# applets are also directly stored in /sbin, so they
# are all in PATH for apps and shell to access them
Note: A target item **does not** imply it is an existing item. A target item might not exist in the actual filesystem
# Other crazy stuffs are stored in this directory
MAGISKTMP=/sbin/.core
### Policies
- For a source leaf: if its target item is also an existing item, the existing item will be replaced with the source leaf
- For a source leaf: if its target item is not an existing item, the source leaf will be added to the path of its target item
- For any existing item that's not a target item, it will stay intact
# Magisk BusyBox path
BBPATH=$MAGISKTMP/busybox
Above is the rule of thumb. Basically it means that Magic Mount merges the two folders, `$MODPATH/system` into `/system`. A simpler way to understand is to think as the items is dirty copied from `$MODPATH/system` into `/system`.
# The mountpoint of magisk.img
MOUNTPATH=$MAGISKTMP/img
However, an addition rule will override the above policies:
# Mount mirrors for MagicMount
MIRRORDIR=$MAGISKTMP/mirror
# System mirror
SYSTEMMIR=$MIRRORDIR/system
# Vendor mirror, could be a symlink to $SYSTEMMIR/vendor
# if vendor is not a separate partition
VENDORMIR=$MIRRORDIR/vendor
# Mirror to magisk's persist binary directory
BINMIRROR=$MIRRORDIR/bin
- For a source folder containing the file `.replace`, the source folder will be treated as if it is a leaf. That is, the items within the target folder will be completely discarded, and the target folder will be replaced with the source folder.
# Database storing settings and root permissons
# Each user (in the case of multiuser) has a different path
MAGISKDB_MANAGER=$MAGISKTMP/db-<USER>/magisk.db
Directories containing a file named `.replace` will **NOT** be merged, instead it directly replaces the target directory. A simpler way to understand is to think as if it wipes the target folder, and then copies the whole folder to the target path.
# Loop devices to mount ext4 images, the ending number will vary
MAGISKLOOP=$MAGISKTMP/block/loop<XX>
```
### Notes
- If you want to replace files in `/vendor`, please place it under `$MODPATH/system/vendor`. Magisk will handle both cases, whether the vendor partition is separated or not under-the-hood, developers don't need to bother.
- Sometimes, completely replacing a folder is inevitable. For example you want to replace `/system/priv-app/SystemUI` in your stock rom. In stock roms, system apps usually comes with pre-optimized files. If your replacement `SystemUI.apk` is deodexed (which is most likely the case), you would want to replace the whole `/system/priv-app/SystemUI` to make sure the folder only contains the modified `SystemUI.apk` without the pre-optimized files.
- If you are using the [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template), you can create a list of folders you want to replace in the file `config.sh`. The installation scripts will handle the creation of `.replace` files into the listed folders for you.
### Paths in `/data`
Some binaries and files should be stored on non-volatile storages in `/data`. In order to prevent detection from crazy apps, everything has to be stored somewhere safe and undetectable in `/data`. The folder `/data/adb` was chosen because of the following advantages:
## Simple Mount Details
(Note: this part is mostly deprecated, since starting with devices using A/B partitions, there is no longer a dedicated partition for cache because OTAs are applied live at boot. Instead, `/cache` now points to `/data/cache`, which means `post-fs` mode does not have access to `/cache` anymore)
- It is an existing folder on modern Android, so it cannot be used as an indication of the existence of Magisk.
- The permission of the folder is by default `700`, owner as `root`, so non-root processes are unable to enter, read, write the folder in any possible way.
- The folder is labeled with secontext `u:object_r:adb_data_file:s0`, and very few processes have the permission to do any interaction with that secontext.
- The folder is located in *Device encrypted storage*, so it is accessible as soon as data is properly mounted in FBE devices.
Some files require to be mounted much earlier in the boot process, currently known are bootanimation and some libs (most users won't change them). You can simply place your modified files into the corresponding path under `/cache/magisk_mount`. For example, you want to replace `/system/media/bootanimation.zip`, copy your new boot animation zip to `/cache/magisk_mount/system/media/bootanimation.zip`, and Magisk will mount your files in the next reboot. Magisk will **clone all the attributes from the target file**, which includes selinux context, permission mode, owner, group. This means you don't need to worry about the metadata for files placed under `/cache/magisk_mount`: just copy the file to the correct place, reboot then you're done!
There are some drawbacks though (only applies to FBE setups):
- Since the folder is encrypted, it is inaccessible in custom recoveries without any form of decryption (very rare case but still exists).
- FBE is very finicky. If you wiped `/data` in TWRP (which wipes all encryption) and manually create folders that are meant to be encrypted without special treatment, it will result in bootloop.
The solution of both situations above is to use `/data/magisk` when `/data/adb` does not exist. Magisk will handle migration at boot and properly setup the files to the expected location.
```
SECURE_DIR=/data/adb
# Magisk image, storing modules and scripts
MAINIMG=$SECURE_DIR/magisk.img
# The image to store updated modules when installing
# in Magisk Manager since contents in $MAINIMG are not
# safe to be modified live. This image will be merged to
# $MAINIMG in the next reboot
MERGEIMG=$SECURE_DIR/magisk_merge.img
# Database storing settings and root permissions
# This file will be bind mounted to $MAGISKDB_MANAGER
MAGISKDB=$SECURE_DIR/magisk.db
# All magisk related binaries, containing busybox,
# scripts, and magisk binaries. Used in supporting
# module installation, addon.d, Magisk Manager etc.
# This folder will be bind mounted to $BINMIRROR
DATABIN=$SECURE_DIR/magisk
# The location to store simple_mount files
SIMPLEMOUNT=$SECURE_DIR/magisk_simple
```
### Paths in `$MAINIMG`
Each folder in `$MAINIMG` is a Magisk module, except the folder `.core` which stores files that are unrelated to any modules.
```
# The directory storing all non-module files
COREDIR=$MOUNTPATH/.core
# MagiskHide hidelist
HIDELIST=$COREDIR/hidelist
# Folder storing scripts that should be executed in post-fs-data mode
POSTFSDATA_DIR=$COREDIR/post-fs-data.d
# Folder storing scripts that should be executed in service mode
SERVICE_DIR=$COREDIR/service.d
```
### Final Words
The file structure of Magisk is designed in a weird and overly complicated way. But all of these quirks are done to properly support hiding modifications from detection. These design choices are mostly what makes Magisk difficult to implement properly and maintain.
## Magisk Booting Process
### Pre-Init
`magiskinit` will replace `init` as the first program to run. It is responsible for constructing rootfs on system-as-root devices: it parses kernel cmdline, sysfs, device tree fstabs, uevents etc., recreating **early-mount** and clones rootfs files from the system. On traditional devices, it will simply revert `init` to the original one and continue on to the following steps.
It then extracts `init.magisk.rc` and inject imports in `init.rc` for additional startup services, extracts `magisk` to `/sbin`, compile split sepolicy and patch `init` to always load `/sepolicy` instead of other sources (only on Treble devices), and patches `/sepolicy` with the additional rules. Finally, it will execute the original `init` to start the ordinary boot process.
### Startup
This triggers on `post-fs-data` when `/data` is properly decrypted (if required) and mounted. The command `/sbin/magisk --starup` is executed by `init`. The startup stage will remove all traces of Magisk in ramdisk, and do the extremely complicated initialization for sbin tmpfs overlay. After the setup is done, it will execute `/sbin/magisk --post-fs-data` to switch to the `magisk` binary located in `tmpfs` and start "post-fs-data" mode.
### post-fs-data
In this mode, the daemons, `magiskd` and `magisklogd`, will be launched. `$MAINIMG` is resized / merged and mounted to `$MOUNTPOINT`, post-fs-data scripts are executed, and module files are magic mounted.
### late_start
Later in the booting process, the class `late_start` will be triggered, and Magisk "service" mode will be started. In this mode, service scripts are executed, and it will try to install Magisk Manager if it doesn't exist.
## Resetprop
Usually, system properties are designed to only be updated by a single `init` process and read-only to non-root processes. With root you can change properties by sending requests via `property_service` using commands such as `setprop`, but you are still prohibited from changing read-only props (props that start with `ro.` like `ro.build.product`) and deleting properties.
`resetprop` is implemented by distilling out the source code related to system properties from AOSP with modifications to map the property area, or `prop_area`, r/w and some clever hacks to modify the trie structure in ways it wasn't intended, like detaching nodes. In a nut shell, it directly do modifications to `prop_area`, bypassing the need to go through `property_service`. Since we are bypassing `property_service`, there are a few caveats:
- `on property:foo=bar` actions registered in `*.rc` scripts will not be triggered if property changes does not go through `property_service`. The default set property behavior of `resetprop` matches `setprop`, which **WILL** trigger events (implemented by first deleting the property then set it via `property_service`), but there is a flag `-n` to disable it if you need this special behavior.
- persist properties (props that starts with `persist.`, like `persist.sys.usb.config`) are stored in both `prop_area` and `/data/property`. By default, deleting props will **NOT** remove it from persistent storage, meaning the property will be restored after the next reboot; reading props will **NOT** read from persistent storage, as this is the behavior of `getprop`. With the flag `-p`, deleting props will remove the prop in **BOTH** `prop_area` and `/data/property`, and reading props will be read from **BOTH** `prop_area` and persistent storage.
## Magic Mount
I will skip the details in the actual implementation of how Magic Mount works as it will become a lecture, but you can always directly dive into the source code if interested. (`bootstages.c`)
Even though the mounting logic and traversal algorithm is pretty complicated, the final result of Magic Mount is actually pretty simple. For each module, the folder `$MODPATH/system` will be recursively merged into the real `/system`; that is: existing files in the real system will be replaced by the one in modules' system, and new files in modules' system will be added to the real system.
There is one additional trick you can use: if you place an empty file named `.replace` in any of the folders in a module's system, instead of merging the contents, that folder will directly replace the one in the real system. This will be very handy in some cases, for example swapping out a system app.
If you want to replace files in `/vendor`, please place it under `$MODPATH/system/vendor`. Magisk will transparently handle both cases, whether vendor is a separate partition or not.
## Simple Mount
**(Note: this implementation is and will not be fully tested, your mileage may vary)**
Some files might have to be mounted earlier in the boot process like bootanimations. To replace it with Magisk, all you need to do is simply place the modified file into the corresponding path under `$SIMPLEMOUNT`. For example, your goal is to replace `/system/media/bootanimation.zip`; you only need to copy the new zip to `$SIMPLEMOUNT/system/media/bootanimation.zip` and you're done! Magisk will clone all the attributes from the target file to your replaced file before bind mounting, so you don't need to worry about it.
## Miscellaneous
Here are some tidbits in Magisk but unable to be categorized into any sections:
- Socket name randomization: when you call `su`, `magiskhide`, and some commands in `magisk`, it connects to the magisk daemon `magiskd` running in the background. The connections are established through an abstract Unix socket. Any process can go through all active Unix sockets and see if the specifc name used by Magisk is in the list to determine whether `magiskd` is running. Starting from v15.4, the abstract name used in `magiskd` and `magisklogd` are randomized by `magiskinit` on each boot.
- Sevice name randomization: each service started up by `init` will be recorded. Some apps will detect the name of magisk boot services to determine whether Magisk is installed. Starting from v17.2, the service name assigned in `init.magisk.rc` is randomized by `magiskinit`.

129
docs/guides.md Normal file
View File

@@ -0,0 +1,129 @@
# Developer Guides
## Scripts
In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode.
- post-fs-data mode
- This stage is BLOCKING. Boot process is paused before execution is done, or 10 seconds has passed.
- Upside: Executed before Zygote is started (which pretty much means everything)
- Downside: Execution has time limit; blocks boot process
- Environment variable `PATH` is set to `PATH=$BBPATH:/sbin:$SYSTEMMIR/bin:$SYSTEMMIR/xbin:$VENDORMIR/bin`
- **Run scripts in this mode only if necessary!**
- late_start service mode
- This stage is NON-BLOCKING. Your script runs in parallel along with the booting process.
- Upside: No time limit, will not block boot process
- Downside: Scripts are not executed as early as post-fs-data mode
- Environment variable `PATH` is set to `PATH=$BBPATH/sbin:$PATH`
- **This is the recommended stage to run most scripts!**
In Magisk, there are also 2 kinds of scripts: **general scripts** and **module scripts**.
- General Scripts
- Placed in `$COREDIR/post-fs-data.d` or `$COREDIR/service.d`
- Only executed if the script is executable (execution permissions, `chmod +x script.sh`)
- Scripts in `post-fs-data.d` runs in post-fs-data mode, and scripts in `service.d` runs in late_start service mode.
- Will still be executed when **Core-Only** mode is enabled.
- Module Scripts
- Placed in the folder of the module
- Only executed if the module is enabled
- `post-fs-data.sh` runs in post-fs-data mode, and `service.sh` runs in late_start service mode.
- Will NOT be executed when **Core-Only** mode is enabled (all modules are disabled)
### Notes
- Be aware that the 10 seconds time limit in post-fs-data mode is shared by **ALL** post-fs-data operations, including all scripts and **Magic Mount**! If a post-fs-data script is stuck, it will affect magic mount and cause it to not work properly!
- Magisk's internal busybox's path `$BBPATH` is always prepended in `PATH`. This means all commands you call in scripts are always using the Magisk busybox unless the applet is not included, which in that case will fallback to use system included binaries (the most common one should be `chcon` since internal busybox does not support SELinux). This makes sure that your script always run in a predictable environment and always have the full suite of commands to use regardless of which Android version it is running on.
## Magisk Modules
A Magisk module is a folder placed in `magisk.img` with a structure defined below:
```
$MOUNTPOINT
├── .
├── .
|
├── $MODID <--- The folder is named with the ID of the module
│ │
│ │ *** Module Identity ***
│ │
│   ├── module.prop <--- This files stores the metadata of the module
│ │
│ │ *** Status files ***
│ │
│   ├── auto_mount <--- If this file exists, auto mount is enabled
│   ├── disable <--- If this file exists, the module is disabled
│   ├── remove <--- If this file exists, the module will be removed next reboot
│ │
│ │ *** Boot Scripts ***
│ │
│   ├── post-fs-data.sh <--- This script will be executed in post-fs-data
│   ├── service.sh <--- This script will be executed in late_start service
│ │
│ │ *** Resetprop Files ***
│ │
│   ├── system.prop <--- This file will be loaded as system properties by resetprop
│ │
│ │ *** Module contents ***
│ │
│   ├── system <--- If auto mount is enabled, this folder will be Magic Mounted
│   │   ├── .
│   │   ├── .
│   │   └── .
│   ├── vendor <--- Auto generated. A symlink to $MODID/system/vendor
│ │
│ │ *** Others ***
│ │
│   ├── . <--- Any additional files / folders are allowed
│   └── .
|
├── another_module
│   ├── .
│   └── .
├── .
├── .
```
As long as a folder follows the structure above, it will be recognized as a module. The only thing *required* is `module.prop`, other files are based on what your module aims to accomplish.
Here is the **strict** format of `module.prop`:
```
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
minMagisk=<int>
```
- `id` has to match this regular expression: `^[a-zA-Z][a-zA-Z0-9\._-]+$`.
ex: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`
This is the **sole identifier** of your module. You should not change it once published.
- `versionCode` has to be an **integer**. This is used to compare versions
- `minMagisk`: an **integer** set as the target Magisk versionCode (this is also the module's minimum requirement).
If you are creating a new module, you should set this value to `17000`.
- Others that isn't mentioned above can be any **single line** string.
## Magisk Module Template
The **Magisk Module Template** is hosted **[here](https://github.com/topjohnwu/magisk-module-template)**.
It is a template to create a flashable zip to install Magisk Modules. It is designed to be simple to use so that anyone can create their own modules easily. The template itself contains minimal scripting for installation; most of the functions are located externally in [util_functions.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/util_functions.sh), which will be installed along with Magisk, and can be upgraded through a Magisk upgrade without the need of a template update.
Here are some files you would want to know:
- `config.sh`: A simple script used as a configuration file. It is the place to configure which features your module requires. A detailed instructions on how to use the template is directly written in the top of this file.
- `module.prop`: This file is your module's metadata.
- `common/*`: Boot stage scripts and `system.prop`
- `META-INF/com/google/android/update-binary`: The actual installation script. Modify this file for advanced custom behavior
And here are some notes to be aware of:
- The template depends on external Magisk scripts, please correctly specify the `minMagisk` value in `module.prop` so the template can fail fast when an unsupported Magisk is installed.
- **!! Windows users aware !!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad.
## Submit Modules to Magisk-Modules-Repo
Directly go to the [submission link](https://github.com/Magisk-Modules-Repo/submission).
## Remove Files
How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
## Remove Folders
Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers using [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) is to add the folder you want to remove into the `REPLACE` list within `config.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/images/core_only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/images/safetynet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

119
docs/install.md Normal file
View File

@@ -0,0 +1,119 @@
# Installation
(Note: This is not a user tutorial for installing Magisk, this is an explaination of how Magisk can be installed, and a guide for developers to properly deploy Magisk in various different situations)
## Systemless
When a user flashes a Magisk zip in custom recoveries or have boot images patched in Magisk Manager, Magisk is installed in this way. This is the only officially supported method to install Magisk on a device. The systemless method installs Magisk into a boot image's ramdisk CPIO, sometimes require additional patches to the kernel.
With the introduction of `magiskinit`, the systemless installation process has become extremely simple as nearly all setup and patches are done at runtime after the device is booted up. Replacing `init` in `rootfs` with our own implementation is required for rooting system-as-root devices systemless-ly - that is why `magiskinit` was created in the first place.
Here are some background knowledge about system-as-root devices:
- No recovery partition. Recovery and system shares the same kernel in boot, and the ramdisk in the boot image is actually the recovery's ramdisk.
- The root folder (`/`) and `/system` are both stored in the system partition.
- When the device boots up, a bootloader will set flags in cmdline so the kernel can decide where to mount `rootfs`: if booting in recovery mode, mount `rootfs` from ramdisk; if not, mount `rootfs` from system with dm-verity enabled.
To install anything systemless-ly, our only choice is to install Magisk files into the ramdisk in boot image. The soultion used in Magisk is to patch the kernel to always boot as recovery mode regardlessly, and we do the `rootfs` construction and booting ourselves. For more details about how `magiskinit` works, check the **Pre-Init** section of [Magisk Booting Process](details.md#magisk-booting-process).
Here are the bare minimum commands to install Magisk into a stock boot image. Be aware that the actual Magisk installation is far more complicated, the following commands will work but should be treat as proof-of-concepts.
```
# Push 2 binaries, magiskboot and magiskinit to the device
# Assume the 2 binaries are in the current directory and
# both have executing permissions.
# The path to stock boot image, can be a file or an on-device block
BOOTIMAGE=<path to boot>
# First unpack the image
./magiskboot --unpack $BOOTIMAGE
# In normal cases, after unpacking you should get at least kernel and ramdisk.cpio
# Patch ramdisk
./magiskboot --cpio ramdisk.cpio \
"mkdir 000 .backup" \ # create a folder to store our init backup
"mv init .backup/init" \ # backup the original init
"add 750 init magiskinit" # replace init with magiskinit
# Patch kernel to always use ramdisk as rootfs
# You only need to do this on system-as-root devices
./magiskboot --hexpatch kernel \
736B69705F696E697472616D6673 \
77616E745F696E697472616D6673
# Repack the boot image
./magiskboot --repack $BOOTIMAGE
# The patched image should be located in new-boot.img
```
## System Only
**(Note 1: MagiskHide will never work with this installation method)**
**(Note 2: Current tools are not updated to support this yet)**
Installing Magisk to the system partition without patching boot images is only possible if your device is a system-as-root device. It is impossible on traditional devices since we could not patch `sepolicy` without modifying the boot image. The basic concept is the same as systemless, but we do the modifications directly to the root directory in the system image.
This could be useful for rooting Treble GSI system images with Magisk.
```
# Currently not available
```
## Exploits
**(Note: Magisk could only be used as root)**
Occasionally, there would be exploits in certain devices that could lead to full fledged root. On modern Android, it is possible to use MagiskSU if you can gain a shell with the following conditions:
- Effective UID should be privileged (root, or `euid=0`)
- Have the ability to reload `sepolicy` (which 99.9% of the time means SELinux permissive)
Once you got a proper root shell, you should have `magiskinit` somewhere on the device. The basic idea is try to live patch `sepolicy` with `magiskpolicy`, and start `magiskd` with `magisk --daemon`. Here are some examples you could use as a reference.
If dm-verity is enforced (no system r/w allowed)
```
# Assume magiskinit is in current directory
# All commands are required to run on each reboot
# Live patch selinux
ln -s ./magiskinit magiskpolicy
./magiskpolicy --live --magisk "allow magisk * * *"
# Mount tmpfs to /sbin
mount -t tmpfs tmpfs /sbin
chmod 755 /sbin
chcon u:object_r:magisk_file:s0 /sbin
# Add files to /sbin
./magiskpolicy -x magisk /sbin/magisk
cp -a magiskpolicy /sbin
/sbin/magisk --install /sbin
# Launch magisk daemon
/sbin/magisk --daemon
# (Optional) switch back to enforced
setenforce 1
```
If dm-verity is not enforced (can modify system)
```
# Assume magiskinit is in current directory
# The following commands should only need to run once
# Mount system rw
mount -o rw,remount /system
# Add files to system
./magiskinit -x magisk /system/xbin/magisk
cp -a magiskinit /system/xbin
ln -s /system/xbin/magiskinit /system/xbin/magiskpolicy
/system/xbin/magisk --install /system/xbin
# The following commands should run on each reboot
/system/xbin/magiskpolicy --live --magisk "allow magisk * * *"
/system/xbin/magisk --daemon
```

View File

@@ -1,49 +0,0 @@
# Magisk Modules
## Magisk Module Format
A Magisk module is a folder placed in the root folder in `magisk.img`, which has a structure as described below:
```
$MOUNTPOINT
├── .
├── .
├── $MODID <--- The ID of the module, should match with module.prop
│   ├── auto_mount <--- If this file exists, auto mount is enabled
│   ├── disable <--- If this file exists, the module is disabled
│   ├── module.prop <--- This files stores the identity and properties of the module
│   ├── post-fs-data.sh <--- This script will be executed in post-fs-data
│   ├── remove <--- If this file exists, the module will be removed next reboot
│   ├── service.sh <--- This script will be executed in late_start service
│   ├── system.prop <--- This file will be loaded as system props
│   ├── system <--- If auto mount is enabled, Magisk will "Magic Mount" this folder
│   │   ├── .
│   │   ├── .
│   │   └── .
│   ├── vendor <--- Auto generated. A symlink to $MODID/system/vendor
│   ├── . <--- Any other files/folders are allowed
│   └── .
├── another_module
│   ├── .
│   └── .
├── .
├── .
```
You are not required to use my Magisk Module Template to create a module. As long as you place files with the structure above, it will be recognized as a module.
## Magisk Module Template
The **Magisk Module Template** is hosted **[here](https://github.com/topjohnwu/magisk-module-template)**.
It is a template to create a flashable zip to install Magisk Modules. It is designed to be simple to use so that anyone can create their own modules easily. The template itself contains minimal scripting for installation; most of the functions are located externally in [util_functions.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/util_functions.sh), which will be installed along with Magisk, and can be upgraded through a Magisk upgrade without the need of a template update.
Here are some files you would want to know:
- `config.sh`: A simple script used as a configuration file. It is the place to configure which features your module needs/disables. A detailed instructions on how to use the template is also written in this file.
- `module.prop`: This file contains your module's indentity and properties, including name and versions etc.. This file will be used to identify your module on an actual device and in the [Magisk Modules Repo](https://github.com/Magisk-Modules-Repo)
- `common/*`: Boot stage scripts and `system.prop`
- `META-INF/com/google/android/update-binary`: The actual installation script. Modify this file for advanced custom behavior
And here are some notes to be aware of:
- The template depends on external Magisk scripts, please specify the correct `minMagisk` value in `module.prop` with the template version your module is based on, or the minimum Magisk version your module is tested on.
- **Windows users please check here!!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad.
- In `module.prop`, `version` can be an arbitrary string, so any fancy version name (e.g. `ultra-beta-v1.1.1.1`) is allowed. However, `versionCode` **MUST** be an integer. The value is used for version comparison.
- Make sure your module ID **does not contain any spaces**.

View File

@@ -1,53 +0,0 @@
# Tips and Tricks
## OTA Installation Tips
Magisk does modifications systemless-ly, which means applying official OTAs is much simpler. Here I provide a few tutorials for several different kind of devices to apply OTAs and preserve Magisk after the installation if possible.
**This tutorial is only for Magisk v17.0+**
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in any way. Even remounting the partition to rw will tamper block verification!!**
#### Prerequisites
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
<img src="images/disable_auto_ota.png" width="250">
1. When an OTA is available, first go to (Magisk Manager → Uninstall → Restore Images). **Do not reboot or you will have Magisk uninstalled.** This will restore your boot (and dtbo if applicable) back to 100% untouched stock images in order to pass pre-OTA block verifications. **This step is required before doing any of the following steps written below!**
<img src="images/restore_img.png" width="250">
#### Devices with A/B Partitions
Due to the fact that these devices have two separate partitions, it is possible to have the OTA installed to the inactive slot and have Magisk Manager to install Magisk onto the updated partition. The out-of-the-box OTA installation works seamlessly and Magisk can be preserved after the installation.
1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Update).
1. Wait for the installation to be fully done (both step 1 and step 2 of the OTA), **do not press the restart button!!** Instead, go to (Magisk Manager → Install → Install to Inactive Slot) and install Magisk to the slot that the OTA engine just updated.
<img src="images/ota_done.png" width="250"> <img src="images/install_inactive_slot.png" width="250">
1. After installation is done, press the reboot button in Magisk Manager. Under-the-hood Magisk Manager forces your device to switch to the updated slot, bypassing any possible post-OTA verifications.
<img src="images/manager_reboot.png" width="250">
1. After the reboot, your device should be fully updated, and most importantly, Magisk is still installed to the updated system!
#### Devices with FlashFire Support
(If you are using a device with A/B partitions, I **strongly** recommend you to use the method stated above since it uses the stock OTA installation mechanism and will always work under any circumstances)
The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash) app developed by Chainfire is a great app to apply OTAs and preserve root at the same time. However, there is a very high chance that it does not support your device/system combination, and unfortunately the app is no longer maintained by Chainfire so no additonal compatibility would be added in the future.
1. After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.**
1. Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
1. Please use the options shown in the screenshot below. The key point is to disable EverRoot (or it will install SuperSU), and add a new action to flash Magisk zip **after** the OTA update.zip (the update.zip should be auto generated in the previous step).
<img src="images/flashfire.png" width="250">
1. Press the big **Flash** button, after a few minutes it should reboot and updated with Magisk installed.
#### Legacy "Non A/B" Devices - General Case
Unfortunately, there are no real good ways to apply OTAs on these devices. The following tutorial will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. This is a general "best practice".
1. To properly install OTAs, you must have your stock recovery installed on your device. If you have custom recovery installed, you can restore it from your previous backup, or dumps found online, or factory images provided by OEMs.
If you decide to start by installing Magisk without touching your recovery partition, you have a few choices, either way you will end up with a Magisk rooted device, but recovery remains stock untouched:
- If supported, use `fastboot boot <recovery_img>` to boot the custom recovery and install Magisk.
- If you have a copy of your stock boot image dump, install Magisk by patching boot image via Magisk Manager, and manually flash it through download mode / fastboot mode / Odin.
1. Once your device has stock recovery and stock boot image restored, download the OTA. Optionally, once you have downloaded the OTA update zip, you can find a way to copy the zip out, since you are still rooted. Personally, I extract the stock boot image and recovery image from the OTA zip for future usage (to patch via Magisk Manager or restore stock recovery etc.)
1. Apply and reboot your device. This will use the official stock OTA installation mechanism of your device to upgrade your system.
1. Once it's done you will be left with an upgraded, 100% stock, un-rooted device. You will have to manually flash Magisk back. Consider using the methods stated in step 1. to flash Magisk without touching the recovery partition if you want to receive stock OTAs frequently.
## Remove Files
How to remove a file systemless-ly? To actually make the file **disappear** is complicated (possible, not worth the effort). **Replacing it with a dummy file should be good enough**! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
## Remove Folders
Same as mentioned above, actually making the folder **disappear** is not worth the effort. **Replacing it with an empty folder should be good enough**! A handy trick for module developers using [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) is to add the folder you want to remove into the `REPLACE` list within `config.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.

View File

@@ -1,141 +1,136 @@
## Available Tools
Magisk comes with a lot of tools for installation, programs running as a daemon, and utilities for developers. This documentation covers 3 binaries, and many more tools are available as applets. The relation between tools are shown below:
# Magisk Tools
Magisk comes with a huge collections of tools for installation, daemons, and utilities for developers. This documentation covers the 3 binaries and all included applets. The binaries and applets are shown below:
```
magiskboot /* binary */
magiskinit /* binary */
magiskpolicy -> magiskinit
supolicy -> magiskinit /* alias of magiskpolicy */
supolicy -> magiskinit
magisk /* binary */
magiskhide -> magisk
resetprop -> magisk
su -> magisk
imgtool -> magisk
```
Note: The Magisk zip you download only contains `magiskboot` and `magiskinit`. The binary `magisk` is compressed and embedded into `magiskinit`. Push `magiskinit` to your device and run `./magiskinit -x magisk <path>` to extract `magisk` out of the binary.
### magiskboot
A tool to unpack / repack boot images, parse and patch cpio and dtbs, hex patch binaries, compress / decompress with multiple algorithms. It is used to install Magisk into boot images.
A tool to unpack / repack boot images, parse / patch / extract cpio, patch dtb, hex patch binaries, and compress / decompress files with multiple algorithms.
`magiskboot` natively supports (which means it does not call external tools) all popular compression methods including `gzip` (used everywhere for compressing kernel and ramdisk), `lz4` (used to compress kernel in modern devices like Pixel), `lz4_legacy` (legacy LZ4 block format with special metadata used [only on LG](https://events.static.linuxfound.org/sites/events/files/lcjpcojp13_klee.pdf) to compress kernel), `lzma` (LZMA1 algorithm natively supported in Linux kernel, used in some custom kernel to compress ramdisk), `xz` (LZMA2 algorithm, very high compression rate, used in Magisk for high compression mode and storing binaries), and `bzip2` (used in desktop Linux boot images to create bzImage, haven't seen on Android yet).
`magiskboot` natively supports (which means it does not rely on external tools) common compression formats including `gzip`, `lz4`, `lz4_legacy` ([only used on LG](https://events.static.linuxfound.org/sites/events/files/lcjpcojp13_klee.pdf)), `lzma`, `xz`, and `bzip2`.
The concept of `magiskboot` is to keep the images as intact as possible. For unpacking, it extracts the large chunks of data (kernel, ramdisk, second, dtb, extra etc.) and decompress them if possible. When repacking a boot image, the original boot image has to be provided so it can use the original headers (including MTK specific headers) with only changing the necessary entries such as the data chunk sizes, and re-compress all data with the original compression method. The same concept also applies to CPIO patching: it does not extract all files, modify in file system, archive all files back to cpio as usually done to create Linux `initramfs`, instead we do modifications directly in the cpio level in memory without involving any data extraction.
The concept of `magiskboot` is to make boot image modification much simpler. For unpacking, it parses the header and all sections in the image (kernel, ramdisk, second, dtb, extra), detect compression format used in each section, and decompress while extraction. Each extracted sections are raw data, ready for direct modification. For repacking, the original boot image is required so the original headers can be used, changing only the necessary entries such as section sizes and checksum, and finally compress each sections back with the original format.
Command help message:
The tool also supports a variaty of CPIO operations that can modify CPIO archives without any extracting and repacking involved.
```
Usage: magiskboot <action> [args...]
Usage: ./magiskboot <action> [args...]
Supported actions:
--parse <bootimg>
Parse <bootimg> only, do not unpack. Return values:
0:OK 1:error 2:insufficient boot partition size
3:chromeos 4:ELF32 5:ELF64
--unpack <bootimg>
Unpack <bootimg> to kernel, ramdisk.cpio, and if available, second, dtb,
and extra into the current directory. Return values:
0:valid 1:error 2:chromeos 3:ELF32 4:ELF64
--unpack <bootimg>
Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into
the current directory. Return value is the same as --parse
--repack <origbootimg> [outbootimg]
Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory
to [outbootimg], or new-boot.img if not specified.
It will compress ramdisk.cpio with the same method used in <origbootimg>,
or attempt to find ramdisk.cpio.[ext], and repack directly with the
compressed ramdisk file
--repack <origbootimg> [outbootimg]
Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory
to [outbootimg], or new-boot.img if not specified.
It will compress ramdisk.cpio with the same method used in <origbootimg>,
or attempt to find ramdisk.cpio.[ext], and repack directly with the
compressed ramdisk file
--hexpatch <file> <hexpattern1> <hexpattern2>
Search <hexpattern1> in <file>, and replace with <hexpattern2>
--hexpatch <file> <hexpattern1> <hexpattern2>
Search <hexpattern1> in <file>, and replace with <hexpattern2>
--cpio <incpio> [commands...]
Do cpio commands to <incpio> (modifications are done directly)
Each command is a single argument, use quotes if necessary
Supported commands:
rm [-r] ENTRY
Remove ENTRY, specify [-r] to remove recursively
mkdir MODE ENTRY
Create directory ENTRY in permissions MODE
ln TARGET ENTRY
Create a symlink to TARGET with the name ENTRY
mv SOURCE DEST
Move SOURCE to DEST
add MODE ENTRY INFILE
Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists
extract [ENTRY OUT]
Extract ENTRY to OUT, or extract all entries to current directory
test
Test the current cpio's patch status
Return values:
0:stock 1:Magisk 2:unsupported (phh, SuperSU, Xposed)
patch KEEPVERITY KEEPFORCEENCRYPT
Ramdisk patches. KEEP**** are boolean values
backup ORIG [SHA1]
Create ramdisk backups from ORIG
SHA1 of stock boot image is optional
restore
Restore ramdisk from ramdisk backup stored within incpio
magisk ORIG KEEPVERITY KEEPFORCEENCRYPT [SHA1]
Do Magisk patches and backups all in one step
Create ramdisk backups from ORIG
KEEP**** are boolean values
SHA1 of stock boot image is optional
sha1
Print stock boot SHA1 if previously stored
--cpio <incpio> [commands...]
Do cpio commands to <incpio> (modifications are done directly)
Each command is a single argument, use quotes if necessary
Supported commands:
rm [-r] ENTRY
Remove ENTRY, specify [-r] to remove recursively
mkdir MODE ENTRY
Create directory ENTRY in permissions MODE
ln TARGET ENTRY
Create a symlink to TARGET with the name ENTRY
mv SOURCE DEST
Move SOURCE to DEST
add MODE ENTRY INFILE
Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists
extract [ENTRY OUT]
Extract ENTRY to OUT, or extract all entries to current directory
test
Test the current cpio's patch status. Return value:
0:stock 1:Magisk 2:other (phh, SuperSU, Xposed)
patch KEEPVERITY KEEPFORCEENCRYPT
Ramdisk patches. KEEP**** are boolean values
backup ORIG [SHA1]
Create ramdisk backups from ORIG
SHA1 of stock boot image is optional
restore
Restore ramdisk from ramdisk backup stored within incpio
magisk ORIG HIGHCOMP KEEPVERITY KEEPFORCEENCRYPT [SHA1]
Do Magisk patches and backups all in one step
Create ramdisk backups from ORIG
HIGHCOMP, KEEP**** are boolean values
SHA1 of stock boot image is optional
sha1
Print stock boot SHA1 if previously stored
--dtb-<cmd> <dtb>
Do dtb related cmds to <dtb> (modifications are done directly)
Supported commands:
dump
Dump all contents from dtb for debugging
test
Check if fstab has verity/avb flags
Return values:
0:no flags 1:flag exists
patch
Search for fstab and remove verity/avb
--dtb-<cmd> <dtb>
Do dtb related cmds to <dtb> (modifications are done directly)
Supported commands:
dump
Dump all contents from dtb for debugging
test
Check if fstab has verity/avb flags. Return value:
0:no flags 1:flag exists
patch
Search for fstab and remove verity/avb
--compress[=method] <infile> [outfile]
Compress <infile> with [method] (default: gzip), optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: gzip xz lzma bzip2 lz4 lz4_legacy
--compress[=method] <infile> [outfile]
Compress <infile> with [method] (default: gzip), optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: gzip xz lzma bzip2 lz4 lz4_legacy
--decompress <infile> [outfile]
Detect method and decompress <infile>, optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: gzip xz lzma bzip2 lz4 lz4_legacy
--decompress <infile> [outfile]
Detect method and decompress <infile>, optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: gzip xz lzma bzip2 lz4 lz4_legacy
--sha1 <file>
Print the SHA1 checksum for <file>
--sha1 <file>
Print the SHA1 checksum for <file>
--cleanup
Cleanup the current working directory
--cleanup
Cleanup the current working directory
```
### magiskinit
This tool is created to unify Magisk support for both legacy "normal" devices and new `skip_initramfs` devices. The compiled binary will replace `init` in the ramdisk, so things could be done even before `init` is started.
`magiskinit` is responsible for constructing a proper rootfs on devices which the actual rootfs is placed in the system partition instead of ramdisk in `boot.img`, such as the Pixel familiy and most Treble enabled devices, or I like to call it `skip_initramfs` devices: it will parse kernel cmdline, mount sysfs, parse through uevent files to make the system (or vendor if available) block device node, then copy rootfs files from system. For normal "traditional" devices, it will simply swap `init` back to the original one and continue on to the next stage.
With a proper rootfs, `magiskinit` goes on and does all pre-init operations to setup a Magisk environment. It patches rootfs on the fly, providing fundamental support such as patching `init`, `init.rc`, run preliminary `sepolicy` patches, and extracts `magisk` and `init.magisk.rc` (these two files are embedded into `magiskinit`). Once all is done, it will spawn another process (`magiskinit_daemon`) to asynchronously run a full `sepolicy` patch, then starts monitoring the main Magisk daemon to make sure it is always running (a.k.a invincible mode); at the same time, it will execute the original `init` to hand the boot process back.
This binary will replace `init` in the ramdisk of a Magisk patched boot image. It is required for supporting devices using system as root (most A/B devices, plus some odd-balls like Huawei EMUI 9), but the tool is extended to support all traditional devices so the same installation setup could be used on all devices. More details can be found in the **Pre-Init** section in [Magisk Booting Process](details.md#magisk-booting-process).
### magiskpolicy
(This tool is aliased to `supolicy` for compatibility with SuperSU's sepolicy tool)
This tool is an applet of `magiskinit`: once `magiskinit` had finished its mission in the pre-init stage, it will preserve an entry point for `magiskpolicy`. This tool could be used for advanced developers messing with `sepolicy`, a compiled binary containing SELinux rules. Normally Linux server admins directly modifies the SELinux policy sources (`*.te`) and recompile the `sepolicy` binary, but here we directly patch the binary file since we don't have access to the sources.
An applet of `magiskinit`. This tool could be used for advanced developers to modify SELinux policies. In common scenarios like Linux server admins, they would directly modify the SELinux policy sources (`*.te`) and recompile the `sepolicy` binary, but here on Android we directly patch the binary file (or runtime policies).
All processes spawned from the Magisk daemon, including root shells and all its forks, are running in the context `u:r:su:s0`. Magisk splits the built in patches into 2 parts: preliminary and full
- The preliminary patch should allow all Magisk internal procedures to run properly (can be done manually by the `--magisk` option). It also contains quite a few additional patches so most scripts can run in the daemon before the full patch is done
- The full patch adds the rule `allow su * * *` on top of the preliminary rules. This is done because stock Samsung ROMs do not support permissive; adding this rule makes the domain effectively permissive. Due to the concern of greatly increasing the boot time, the Magisk daemon will not wait for this patch to finish until the boot stage `late_start` triggers. What this means is that **only `late_start` service mode is guaranteed to run in a fully patched environment**. For non-Samsung devices it doesn't matter because `u:r:su:s0` is permissive anyways, but for full compatibility, it is **highly recommended to run boot scripts in `late_start` service mode**.
Command help message:
All processes spawned from the Magisk daemon, including root shells and all its forks, are running in the context `u:r:magisk:s0`. The rule used on all Magisk installed systems can be viewed as stock `sepolicy` with these patches: `magiskpolicy --magisk 'allow magisk * * *'`.
```
Usage: magiskpolicy [--options...] [policystatements...]
Usage: magiskpolicy [--options...] [policy statements...]
Options:
--live directly apply patched policy live
--magisk built-in rules for a Magisk selinux environment
--load FILE load policies from <infile>
--save FILE save policies to <outfile>
--live directly apply sepolicy live
--magisk inject built-in rules for a minimal
Magisk selinux environment
--load FILE load policies from FILE
--compile-split compile and load split cil policies
from system and vendor just like init
--save FILE save policies to FILE
If no input file is specified, it will load from current policies
If neither --live nor --save is specified, nothing will happen
If neither --load or --compile-split is specified, it will load
from current live policies (/sys/fs/selinux/policy)
One policy statement should be treated as one parameter;
this means a full policy statement should be enclosed in quotes;
@@ -181,9 +176,7 @@ allow source2 target2 permission-class { all-permissions }
### magisk
The magisk binary contains all the magic of Magisk, providing all the features Magisk has to offer. When called with the name `magisk`, it works as an utility tool with many helper functions, and also the entry point for `init` to start Magisk services. These helper functions are extensively used by the [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) and Magisk Manager.
Command help message:
When the magisk binary is called with the name `magisk`, it works as an utility tool with many helper functions and the entry points for `init` to start Magisk services.
```
Usage: magisk [applet [arguments]...]
@@ -195,27 +188,21 @@ Options:
-V print running daemon version code
--list list all available applets
--install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional
--createimg IMG SIZE create ext4 image. SIZE is interpreted in MB
--imgsize IMG report ext4 image used/total size
--resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB
--mountimg IMG PATH mount IMG to PATH and prints the loop device
--umountimg PATH LOOP unmount PATH and delete LOOP device
--[init service] start init service
--daemon manually start magisk daemon
--[init trigger] start service for init trigger
--unlock-blocks set BLKROSET flag to OFF for all block devices
--restorecon fix selinux context on Magisk files and folders
--clone-attr SRC DEST clone permission, owner, and selinux context
Supported init services:
daemon, post-fs, post-fs-data, service
Supported init triggers:
startup, post-fs-data, service
Supported applets:
su, resetprop, magiskhide
magisk, su, resetprop, magiskhide, imgtool
```
### su
An applet of `magisk`, the MagiskSU entry point, the good old `su` command.
Command help message:
An applet of `magisk`, the MagiskSU entry point. Good old `su` command.
```
Usage: su [options] [-] [user [argument...]]
@@ -228,28 +215,15 @@ Options:
--preserve-environment preserve the entire environment
-s, --shell SHELL use SHELL instead of the default /system/bin/sh
-v, --version display version number and exit
-V display version code and exit,
this is used almost exclusively by Superuser.apk
-V display version code and exit
-mm, -M,
--mount-master run in the global mount namespace,
use if you need to publicly apply mounts
--mount-master force run in the global mount namespace
```
Note: even though the `-Z, --context` option is not listed above, it actually still exists for compatibility with apps using SuperSU. However MagiskSU will silently ignore the option since it's no more relevant.
Note: even though the `-Z, --context` option is not listed above, the option still exists for CLI compatibility with apps designed for SuperSU. However the option is silently ignored since it's no longer relevant.
### resetprop
An applet of `magisk`, an advanced system property manipulation utility. Here's some background knowledge:
System properties are stored in a hybrid trie/binary tree data structure in memory. These properties are allowed to be read by many processes (natively via `libcutils`, in shells via the `getprop` command); however, only the `init` process have direct write access to the memory of property data. `init` provides a `property_service` to accept property update requests and acts as a gatekeeper, doing things such as preventing **read-only** props to be overridden and storing **persist** props to non-volatile storages. In addition, property triggers registered in `*.rc` scripts are also handled here.
`resetprop` is created by pulling out the portion of source code managing properties from AOSP and try to mimic what `init` is doing. With some hackery the result is that we have direct access to the data structure, bypassing the need to go through `property_service` to gain arbitrary control. Here is a small implementation detail: the data structure and the stack-like memory allocation method does not support removing props (they are **designed NOT** to be removed); prop deletion is accomplished by detaching the target node from the tree structure, making it effectively invisible. As we cannot reclaim the memory allocated to store the property, this wastes a few bytes of memory but it shouldn't be a big deal unless you are adding and deleting hundreds of thousands of props over and over again.
Due to the fact that we bypassed `property_service`, there are a few things developer should to be aware of:
- `on property:foo=bar` triggers registered in `*.rc` scripts will not be triggered when props are changed. This could be a good thing or a bad thing, depending on what behavior you expect. The default behavior of `resetprop` matches the original `setprop`, which **WILL** trigger events (implemented by deleting the prop and set the props via `property_service`), but there is a flag (`-n`) to disable it if you need this special behavior.
- persist props are stored both in memory and in `/data/property`. By default, deleting props will **NOT** remove it from persistent storage, meaning the prop will be restored after the next reboot; reading props will **NOT** read from persistent storage, as this is the behavior of normal `getprop`. With the flag `-p` enabled, deleting props will remove the prop **BOTH** in memory and `/data/property`; props will be read from **BOTH** in memory and persistent storage.
Command help message:
An applet of `magisk`. An advanced system property manipulation utility. Check the [Resetprop Details](details.md#resetprop) for more background information.
```
Usage: resetprop [flags] [options...]
@@ -273,8 +247,6 @@ Flags:
### magiskhide
An applet of `magisk`, the CLI to control MagiskHide. Use this tool to communicate with the daemon to change MagiskHide settings.
Command help message:
```
Usage: magiskhide [--options [arguments...] ]
@@ -285,3 +257,18 @@ Options:
--rm PROCESS Remove PROCESS from the hide list
--ls Print out the current hide list
```
### imgtool
An applet of `magisk`, a collection of common commands used to create and manage `ext4` images.
```
Usage: imgtool <action> [args...]
Actions:
create IMG SIZE create ext4 image. SIZE is interpreted in MB
resize IMG SIZE resize ext4 image. SIZE is interpreted in MB
mount IMG PATH mount IMG to PATH and prints the loop device
umount PATH LOOP unmount PATH and delete LOOP device
merge SRC TGT merge SRC to TGT
trim IMG trim IMG to save space
```

93
docs/tutorials.md Normal file
View File

@@ -0,0 +1,93 @@
# Tutorials
## OTA Installation
Magisk does modifications systemless-ly, which means applying official OTAs is much simpler. Here I provide a few tutorials for several different kind of devices to apply OTAs and preserve Magisk after the installation if possible.
**This tutorial is only for Magisk v17.0+**
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in any way. Even remounting the partition to rw will tamper block verification!!**
### Prerequisites
- Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
<p align="center"><img src="images/disable_auto_ota.png" width="250"/></p>
- When an OTA is available, first go to (Magisk Manager → Uninstall → Restore Images). **Do not reboot or you will have Magisk uninstalled.** This will restore your boot (and dtbo if applicable) back to 100% untouched stock images in order to pass pre-OTA block verifications. **This step is required before doing any of the following steps written below!**
<p align="center"><img src="images/restore_img.png" width="300"/></p>
### Devices with A/B Partitions
Due to the fact that these devices have two separate partitions, it is possible to have the OTA installed to the inactive slot and have Magisk Manager to install Magisk onto the updated partition. The out-of-the-box OTA installation works seamlessly and Magisk can be preserved after the installation.
- After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Update).
- Wait for the installation to be fully done (both step 1 and step 2 of the OTA), **do not press the "Restart now" or "Reboot" button!** Instead, go to (Magisk Manager → Install → Install to Inactive Slot) and install Magisk to the slot that the OTA engine just updated.
<p align="center"><img src="images/ota_done.png" width="250"/> <img src="images/install_inactive_slot.png" width="250"/></p>
- After installation is done, press the reboot button in Magisk Manager. Under-the-hood Magisk Manager forces your device to switch to the updated slot, bypassing any possible post-OTA verifications.
<p align="center"><img src="images/manager_reboot.png" width="250"/></p>
- After the reboot, your device should be fully updated, and most importantly, Magisk is still installed to the updated system!
### Devices with FlashFire Support
(If you are using a device with A/B partitions, I **strongly** recommend you to use the method stated above since it uses the stock OTA installation mechanism and will always work under any circumstances)
The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash) app developed by Chainfire is a great app to apply OTAs and preserve root at the same time. However, there is a very high chance that it does not support your device/system combination, and unfortunately the app is no longer maintained by Chainfire so no additonal compatibility would be added in the future.
- After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.**
- Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
- Please use the options shown in the screenshot below. The key point is to disable EverRoot (or it will install SuperSU), and add a new action to flash Magisk zip **after** the OTA update.zip (the update.zip should be auto generated in the previous step).
<p align="center"><img src="images/flashfire.png" width="250"/></p>
- Press the big **Flash** button, after a few minutes it should reboot and updated with Magisk installed.
### Legacy "Non A/B" Devices - General Case
Unfortunately, there are no real good ways to apply OTAs on these devices. The following tutorial will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. This is a general "best practice".
- To properly install OTAs, you must have your stock recovery installed on your device. If you have custom recovery installed, you can restore it from your previous backup, or dumps found online, or factory images provided by OEMs.
If you decide to start by installing Magisk without touching your recovery partition, you have a few choices, either way you will end up with a Magisk rooted device, but recovery remains stock untouched:
- If supported, use `fastboot boot <recovery_img>` to boot the custom recovery and install Magisk.
- If you have a copy of your stock boot image dump, install Magisk by patching boot image via Magisk Manager, and manually flash it through download mode / fastboot mode / Odin.
- Once your device has stock recovery and stock boot image restored, download the OTA. Optionally, once you have downloaded the OTA update zip, you can find a way to copy the zip out, since you are still rooted. Personally, I extract the stock boot image and recovery image from the OTA zip for future usage (to patch via Magisk Manager or restore stock recovery etc.)
- Apply and reboot your device. This will use the official stock OTA installation mechanism of your device to upgrade your system.
- Once it's done you will be left with an upgraded, 100% stock, un-rooted device. You will have to manually flash Magisk back. Consider using the methods stated in step 1. to flash Magisk without touching the recovery partition if you want to receive stock OTAs frequently.
## Best Practices for MagiskHide
There are a lot of people confused about why their setup isn't bypassing detections as expected. Here are some of the best practices to maintain a proper environment for MagiskHide. The guidelines in the following is ordered in progression, you might not need to do everything all the way through, but you should have done previous steps before going on to the next step.
If you've followed all practices below and still cannot succeed, consider switching to the beta channel as new MagiskHide upgrades might have not come to the stable channel yet.
<p align="center"><img src="images/beta_channel.png" width="300"/></p>
<p align="center">Settings → Update Settings → Update Channel</p>
Let's start with SafetyNet.
- Check SafetyNet status with Magisk Manager. Google banned the usage of old APIs, and many "SafetyNet Detection" apps you used were never updated and thus cannot provide correct information. **Trust the SafetyNet check included in Magisk Manager!**
- Start clean: my advice is always the easy route - **use stock ROMs**. If you prefer custom ROMs, choose stable, official builds from well-reputed teams.
- If you cannot even pass **basicIntegrity**, there are some serious issues! Even though in theory Magisk Modules can be hidden by MagiskHide, you should first try enabling *Core-Only Mode* and see if it makes any difference. If it still doesn't pass, the ROM you're using is the problem. If it passes, experiment and narrow down the problematic module(s).
<p align="center"><img src="images/core_only.png" width="250"/></p>
- If **basicIntegrity** passes but **ctsProfile** doesn't, you might be using a ROM that has never passed CTS (OEM betas, previews, China based ROMs etc.). Try out [MagiskHide Props Config](https://forum.xda-developers.com/apps/magisk/module-magiskhide-props-config-t3789228) from @Didgeridoohan and switch a known CTS-passing fingerprint from the massive list included in the module. You will be very surprised by the results!
At this point, SafetyNet should be fully passed. Let's change gears to notorious apps that detects all kinds of weird shit.
<p align="center"><img src="images/safetynet.png" width="300"/></p>
<p align="center">This is just so pleasing to look at, right?</p>
- Add your target app to the MagiskHide list: go to the "MagiskHide" section in Magisk Manager, and check the app you want to hide. **Do not abuse MagiskHide!** I've seen some paranoid people adding *a lot* of unnecessary apps to the list. This behavior is very likely to not only break MagiskHide, **but also Magisk itself or even the whole system** (`magiskd`/`magisklogd` crashes, system bogged down). Remember: with the current MagiskHide implementation, **each additional hide target comes with a large cost**; only enable those you are SURE you needed!
- Hide Magisk Manager: go to (Settings → Hide Magisk Manager) to repackage the app with a random package name.
<p align="center"><img src="images/hide_manager.png" width="300"/></p>
- Remove sensitive apps: after dealing with Magisk Manager, uninstall Xposed Installer, Lucky Patcher, other root managers, anything you think is suspicious.
- Remove sensitive files: starting from Magisk Manager v5.9.0, `/sdcard/MagiskManager` is no longer used. Remove that specific folder if exists, and then remove any files and folders that have sensitive names (e.g. magisk, supersu, xposed etc.) in your internal storage. Yes, there are apps scanning through all your files, are you freaking out now?
- Disable USB debugging (ADB): some apps does not allow your device to have usb debugging enabled. Disable it in developer options.
- Disable developer options: it's just ridiculous at this point.
- Uninstall Magisk Manager: this is the last thing you should try. Sometimes the hiding technique used in Magisk Manager is just not good enough (it is improving, like recently added code obfuscation), try uninstalling and see if it works. If it does, report to me and I will hack up a solution ASAP.

View File

@@ -14,8 +14,7 @@ android {
externalNativeBuild {
ndkBuild {
// Pass arguments to ndk-build.
arguments('B_MAGISK=1', 'B_INIT=1', 'B_BOOT=1', 'B_BXZ=1', 'MAGISK_VERSION=debug',
'MAGISK_VER_CODE=99999', 'MAGISK_DEBUG=-DMAGISK_DEBUG')
arguments('B_MAGISK=1', 'B_INIT=1', 'B_BOOT=1', 'B_BXZ=1', 'MAGISK_DEBUG=1')
}
}
}

View File

@@ -10,13 +10,8 @@ LIBLZ4 := $(EXT_PATH)/lz4/lib
LIBBZ2 := $(EXT_PATH)/bzip2
LIBFDT := $(EXT_PATH)/dtc/libfdt
LIBNANOPB := $(EXT_PATH)/nanopb
LIBSYSTEMPROPERTIES := jni/resetprop/libsystemproperties/include
COMMON_UTILS := \
utils/file.c \
utils/list.c \
utils/misc.c \
utils/vector.c \
utils/xwrap.c
LIBSYSTEMPROPERTIES := jni/systemproperties/include
LIBUTILS := jni/utils/include
########################
# Binaries
@@ -27,23 +22,24 @@ ifdef B_MAGISK
# magisk main binary
include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties
LOCAL_SHARED_LIBRARIES := libsqlite
LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
$(EXT_PATH)/include \
$(LIBSELINUX) \
$(LIBNANOPB) \
$(LIBSYSTEMPROPERTIES)
$(LIBSYSTEMPROPERTIES) \
$(LIBUTILS)
LOCAL_SRC_FILES := \
core/magisk.c \
core/daemon.c \
core/log_daemon.c \
core/bootstages.c \
core/socket.c \
core/db.c \
main.c \
img.c \
daemon/magisk.c \
daemon/daemon.c \
daemon/log_daemon.c \
daemon/bootstages.c \
daemon/socket.c \
daemon/db.c \
magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \
@@ -54,11 +50,8 @@ LOCAL_SRC_FILES := \
su/su.c \
su/connect.c \
su/pts.c \
su/su_daemon.c \
utils/img.c \
$(COMMON_UTILS)
su/su_daemon.c
LOCAL_CFLAGS := -DIS_DAEMON -DSELINUX
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
@@ -69,22 +62,22 @@ ifdef B_INIT
# magiskinit
include $(CLEAR_VARS)
LOCAL_MODULE := magiskinit
LOCAL_STATIC_LIBRARIES := libsepol libxz
LOCAL_STATIC_LIBRARIES := libsepol libxz libutils
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
$(EXT_PATH)/include \
out \
out/$(TARGET_ARCH_ABI) \
$(LIBSEPOL)
$(LIBSEPOL) \
$(LIBUTILS)
LOCAL_SRC_FILES := \
core/magiskinit.c \
init.c \
magiskpolicy/api.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
$(COMMON_UTILS)
magiskpolicy/sepolicy.c
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
@@ -96,28 +89,27 @@ ifdef B_BOOT
# magiskboot
include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := libmincrypt liblzma liblz4 libbz2 libfdt
LOCAL_STATIC_LIBRARIES := libmincrypt liblzma liblz4 libbz2 libfdt libutils
LOCAL_C_INCLUDES := \
jni/include \
$(EXT_PATH)/include \
$(LIBLZMA) \
$(LIBLZ4) \
$(LIBBZ2) \
$(LIBFDT)
$(LIBFDT) \
$(LIBUTILS)
LOCAL_SRC_FILES := \
magiskboot/cpio.c \
magiskboot/main.c \
magiskboot/bootimg.c \
magiskboot/bootimg.cpp \
magiskboot/hexpatch.c \
magiskboot/compress.c \
magiskboot/format.c \
magiskboot/dtb.c \
magiskboot/ramdisk.c \
magiskboot/pattern.c \
$(COMMON_UTILS)
magiskboot/pattern.c
LOCAL_CFLAGS := -DXWRAP_EXIT
LOCAL_LDLIBS := -lz
include $(BUILD_EXECUTABLE)
@@ -144,7 +136,8 @@ include jni/external/busybox/Android.mk
endif
########################
# Externals
# Libraries
########################
include jni/utils/Android.mk
include jni/systemproperties/Android.mk
include jni/external/Android.mk
include jni/resetprop/libsystemproperties/Android.mk

View File

@@ -1,14 +1,17 @@
APP_ABI := armeabi-v7a x86
APP_CFLAGS := -std=gnu99 ${MAGISK_DEBUG} \
-DMAGISK_VERSION="${MAGISK_VERSION}" -DMAGISK_VER_CODE=${MAGISK_VER_CODE}
APP_CFLAGS := -std=gnu11 -DMAGISK_VERSION="${MAGISK_VERSION}" -DMAGISK_VER_CODE=${MAGISK_VER_CODE}
APP_CPPFLAGS := -std=c++14
APP_STL := system
APP_PLATFORM := android-16
APP_CFLAGS += -Wno-implicit-function-declaration
ifdef MAGISK_DEBUG
APP_CFLAGS += -DMAGISK_DEBUG
endif
# Busybox require some additional settings
ifdef B_BB
APP_SHORT_COMMANDS := true
NDK_TOOLCHAIN_VERSION := 4.9
APP_PLATFORM := android-21
APP_CFLAGS += -Wno-implicit-function-declaration
endif

View File

@@ -13,7 +13,6 @@
#include <dirent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "db.h"
@@ -21,7 +20,8 @@
#include "img.h"
#include "daemon.h"
#include "resetprop.h"
#include "magiskpolicy.h"
#include "selinux.h"
#include "flags.h"
static char buf[PATH_MAX], buf2[PATH_MAX];
static struct vector module_list;
@@ -462,7 +462,7 @@ static int prepare_img() {
return 0;
}
void install_apk(const char *apk) {
static void install_apk(const char *apk) {
setfilecon(apk, "u:object_r:"SEPOL_FILE_DOMAIN":s0");
while (1) {
sleep(5);
@@ -486,13 +486,50 @@ void install_apk(const char *apk) {
unlink(apk);
}
static int check_data() {
struct vector v;
vec_init(&v);
file_to_vector("/proc/mounts", &v);
char *line;
int mnt = 0;
vec_for_each(&v, line) {
if (strstr(line, " /data ") && strstr(line, "tmpfs") == NULL) {
mnt = 1;
break;
}
}
vec_deep_destroy(&v);
int data = 0;
if (mnt) {
char *crypto = getprop("ro.crypto.state");
if (crypto != NULL) {
if (strcmp(crypto, "unencrypted") == 0) {
// Unencrypted, we can directly access data
data = 1;
} else {
// Encrypted, check whether vold is started
char *vold = getprop("init.svc.vold");
if (vold != NULL) {
free(vold);
data = 1;
}
}
free(crypto);
} else {
// ro.crypto.state is not set, assume it's unencrypted
data = 1;
}
}
return data;
}
static void *start_magisk_hide(void *args) {
launch_magiskhide(-1);
return NULL;
}
static void auto_start_magiskhide() {
if (!check_and_start_logger())
if (!start_log_daemon())
return;
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
@@ -503,6 +540,27 @@ static void auto_start_magiskhide() {
free(hide_prop);
}
void unlock_blocks() {
DIR *dir;
struct dirent *entry;
int fd, dev, OFF = 0;
if ((dev = xopen("/dev/block", O_RDONLY | O_CLOEXEC)) < 0)
return;
dir = xfdopendir(dev);
while((entry = readdir(dir))) {
if (entry->d_type == DT_BLK) {
if ((fd = openat(dev, entry->d_name, O_RDONLY)) < 0)
continue;
if (ioctl(fd, BLKROSET, &OFF) == -1)
PLOGE("unlock %s", entry->d_name);
close(fd);
}
}
close(dev);
}
/****************
* Entry points *
****************/
@@ -593,21 +651,23 @@ void startup() {
setfilecon("/sbin", "u:object_r:rootfs:s0");
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
// Create wrapper
fd = creat("/sbin/magisk", 0755);
xwrite(fd, wrapper, sizeof(wrapper) - 1);
close(fd);
setfilecon("/sbin/magisk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
// Setup magisk symlinks
// Create applet symlinks
for (int i = 0; applet_names[i]; ++i) {
snprintf(buf, PATH_MAX, "/sbin/%s", applet_names[i]);
xsymlink("/sbin/magisk", buf);
}
// Setup binary and wrapper
fd = creat("/sbin/magisk.bin", 0755);
xwrite(fd, magisk, magisk_size);
close(fd);
free(magisk);
unlink("/sbin/magisk");
fd = creat("/sbin/magisk", 0755);
xwrite(fd, wrapper, sizeof(wrapper) - 1);
close(fd);
setfilecon("/sbin/magisk.bin", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
for (int i = 0; applet[i]; ++i) {
snprintf(buf, PATH_MAX, "/sbin/%s", applet[i]);
xsymlink("/sbin/magisk", buf);
}
setfilecon("/sbin/magisk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
// Setup magiskinit symlinks
fd = creat("/sbin/magiskinit", 0755);
@@ -712,6 +772,9 @@ void startup() {
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
// Preserve a copy of logcat
cp_afc("/system/bin/logcat", MIRRDIR "/bin/logcat");
// Start post-fs-data mode
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", NULL);
}
@@ -726,6 +789,9 @@ void post_fs_data(int client) {
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
// Start log_daemon
start_log_daemon();
LOGI("** post-fs-data mode running\n");
// Allocate buffer
@@ -867,7 +933,7 @@ core_only:
// Check whether we have a valid manager installed
sqlite3 *db = get_magiskdb();
struct db_strings str;
INIT_DB_STRINGS(&str);
memset(&str, 0, sizeof(str));
get_db_strings(db, SU_MANAGER, &str);
if (validate_manager(str.s[SU_MANAGER], 0, NULL)) {
// There is no manager installed, install the stub

View File

@@ -13,17 +13,22 @@
#include <sys/un.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "resetprop.h"
#include "magiskpolicy.h"
#include "selinux.h"
#include "flags.h"
int setup_done = 0;
int seperate_vendor = 0;
static void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred);
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
PLOGE("getsockopt");
}
static void *request_handler(void *args) {
int client = *((int *) args);
free(args);
@@ -67,10 +72,10 @@ static void *request_handler(void *args) {
ls_hide_list(client);
break;
case SUPERUSER:
su_daemon_receiver(client, &credential);
su_daemon_handler(client, &credential);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VER_STR);
write_string(client, xstr(MAGISK_VERSION) ":MAGISK");
close(client);
break;
case CHECK_VERSION_CODE:
@@ -96,7 +101,11 @@ static void *request_handler(void *args) {
return NULL;
}
void main_daemon() {
static void main_daemon() {
android_logging();
#ifndef MAGISK_DEBUG
log_cb.d = nop_log;
#endif
setsid();
setcon("u:r:"SEPOL_PROC_DOMAIN":s0");
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
@@ -107,11 +116,8 @@ void main_daemon() {
xdup2(fd, STDIN_FILENO);
close(fd);
// Start the log monitor
check_and_start_logger();
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_DAEMON);
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (xbind(fd, (struct sockaddr*) &sun, len))
exit(1);
@@ -145,39 +151,24 @@ void main_daemon() {
}
}
/* Connect the daemon, set sockfd, and return if new daemon is spawned */
int connect_daemon2(daemon_t d, int *sockfd) {
int connect_daemon() {
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, d);
*sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(*sockfd, (struct sockaddr*) &sun, len)) {
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(fd, (struct sockaddr*) &sun, len)) {
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "No daemon is currently running!\n");
exit(1);
}
LOGD("client: launching new main daemon process\n");
if (fork_dont_care() == 0) {
LOGD("client: connect fail, try launching new daemon process\n");
close(*sockfd);
switch (d) {
case MAIN_DAEMON:
main_daemon();
break;
case LOG_DAEMON:
log_daemon();
break;
}
close(fd);
main_daemon();
}
while (connect(*sockfd, (struct sockaddr*) &sun, len))
while (connect(fd, (struct sockaddr*) &sun, len))
usleep(10000);
return 1;
}
return 0;
}
int connect_daemon() {
int fd;
connect_daemon2(MAIN_DAEMON, &fd);
return fd;
}

View File

@@ -8,11 +8,6 @@
#include "magisk.h"
#include "db.h"
void INIT_DB_STRINGS(struct db_strings *str) {
for (int i = 0; i < DB_STRING_NUM; ++i)
str->s[i][0] = '\0';
}
static int policy_cb(void *v, int col_num, char **data, char **col_name) {
struct su_access *su = v;
for (int i = 0; i < col_num; i++) {

View File

@@ -8,14 +8,17 @@
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "flags.h"
static int loggable = 0;
int log_daemon_started = 0;
static struct vector log_cmd, clear_cmd;
static int sockfd;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
@@ -106,24 +109,10 @@ static void *logcat_thread(void *args) {
}
}
int check_and_start_logger() {
if (!loggable) {
int fd;
loggable = exec_command_sync("/system/bin/logcat", "-d", "-f", "/dev/null", NULL) == 0;
chmod("/dev/null", 0666);
if (loggable) {
connect_daemon2(LOG_DAEMON, &fd);
write_int(fd, HANDSHAKE);
close(fd);
}
}
return loggable;
}
void log_daemon() {
static void log_daemon() {
setsid();
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, LOG_DAEMON);
socklen_t len = setup_sockaddr(&sun, LOG_SOCKET);
sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (xbind(sockfd, (struct sockaddr*) &sun, len))
exit(1);
@@ -143,11 +132,11 @@ void log_daemon() {
// Construct cmdline
vec_init(&log_cmd);
vec_push_back(&log_cmd, "/system/bin/logcat");
vec_push_back(&log_cmd, MIRRDIR "/bin/logcat");
// Test whether these buffers actually works
const char* b[] = { "main", "events", "crash" };
for (int i = 0; i < 3; ++i) {
if (exec_command_sync("/system/bin/logcat", "-b", b[i], "-d", "-f", "/dev/null", NULL) == 0)
if (exec_command_sync(MIRRDIR "/bin/logcat", "-b", b[i], "-d", "-f", "/dev/null", NULL) == 0)
vec_push_back_all(&log_cmd, "-b", b[i], NULL);
}
chmod("/dev/null", 0666);
@@ -176,10 +165,36 @@ void log_daemon() {
events[HIDE_EVENT].fd = fd;
pthread_mutex_unlock(&lock);
break;
case HANDSHAKE:
default:
close(fd);
break;
}
}
}
int start_log_daemon() {
if (!log_daemon_started) {
if (exec_command_sync(MIRRDIR "/bin/logcat", "-d", "-f", "/dev/null", NULL) == 0) {
if (fork_dont_care() == 0)
log_daemon();
// Wait till we can connect to log_daemon
int fd = connect_log_daemon();
write_int(fd, HANDSHAKE);
close(fd);
log_daemon_started = 1;
}
chmod("/dev/null", 0666);
}
return log_daemon_started;
}
int connect_log_daemon() {
if (!log_daemon_started)
return -1;
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, LOG_SOCKET);
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
while (connect(fd, (struct sockaddr*) &sun, len))
usleep(10000);
return fd;
}

View File

@@ -1,29 +1,24 @@
/* main.c - The multicall entry point
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include "utils.h"
#include "magisk.h"
#include "daemon.h"
#include "selinux.h"
#include "flags.h"
char *argv0;
int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskhide_main, imgtool_main, NULL };
int create_links(const char *bin, const char *path) {
static int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX];
if (bin == NULL) {
xreadlink("/proc/self/exe", self, sizeof(self));
bin = self;
}
int ret = 0;
for (int i = 0; applet[i]; ++i) {
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
for (int i = 0; applet_names[i]; ++i) {
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet_names[i]);
unlink(linkpath);
ret |= symlink(bin, linkpath);
}
@@ -54,8 +49,8 @@ static void usage() {
"\n"
"Supported applets:\n");
for (int i = 0; applet[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
for (int i = 0; applet_names[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet_names[i]);
fprintf(stderr, "\n\n");
exit(1);
}
@@ -64,7 +59,7 @@ int magisk_main(int argc, char *argv[]) {
if (argc < 2)
usage();
if (strcmp(argv[1], "-c") == 0) {
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
printf("%s (%d)\n", xstr(MAGISK_VERSION) ":MAGISK", MAGISK_VER_CODE);
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon();
@@ -83,8 +78,8 @@ int magisk_main(int argc, char *argv[]) {
if (argc == 3) return create_links(NULL, argv[2]);
else return create_links(argv[2], argv[3]);
} else if (strcmp(argv[1], "--list") == 0) {
for (int i = 0; applet[i]; ++i)
printf("%s\n", applet[i]);
for (int i = 0; applet_names[i]; ++i)
printf("%s\n", applet_names[i]);
return 0;
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
unlock_blocks();
@@ -117,39 +112,6 @@ int magisk_main(int argc, char *argv[]) {
return read_int(fd);
}
// Applets
argc--;
argv++;
for (int i = 0; applet[i]; ++i) {
if (strcmp(basename(argv[0]), applet[i]) == 0) {
strcpy(argv0, basename(argv[0]));
return (*applet_main[i])(argc, argv);
}
}
usage();
return 1;
}
int main(int argc, char *argv[]) {
umask(0);
argv0 = argv[0];
if (strcmp(basename(argv0), "magisk.bin") == 0) {
if (argc >= 2) {
// It's calling applets
--argc;
++argv;
}
}
// Applets
for (int i = 0; applet[i]; ++i) {
if (strcmp(basename(argv[0]), applet[i]) == 0) {
strcpy(argv0, basename(argv[0]));
return (*applet_main[i])(argc, argv);
}
}
// Not an applet
return magisk_main(argc, argv);
}

View File

@@ -1,6 +1,8 @@
/* socket.c - All socket related operations
*/
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <endian.h>
@@ -11,18 +13,9 @@
#define ABS_SOCKET_LEN(sun) (sizeof(sun->sun_family) + strlen(sun->sun_path + 1) + 1)
socklen_t setup_sockaddr(struct sockaddr_un *sun, daemon_t d) {
socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name) {
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
const char *name;
switch (d) {
case MAIN_DAEMON:
name = MAIN_SOCKET;
break;
case LOG_DAEMON:
name = LOG_SOCKET;
break;
}
strcpy(sun->sun_path + 1, name);
return ABS_SOCKET_LEN(sun);
}
@@ -37,24 +30,12 @@ int create_rand_socket(struct sockaddr_un *sun) {
return fd;
}
int socket_accept(int serv_fd, int timeout) {
struct timeval tv;
fd_set fds;
int rc;
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(serv_fd, &fds);
do {
rc = select(serv_fd + 1, &fds, NULL, NULL, &tv);
} while (rc < 0 && errno == EINTR);
if (rc < 1) {
PLOGE("select");
exit(-1);
}
return xaccept4(serv_fd, NULL, NULL, SOCK_CLOEXEC);
int socket_accept(int sockfd, int timeout) {
struct pollfd pfd = {
.fd = sockfd,
.events = POLL_IN
};
return xpoll(&pfd, 1, timeout * 1000) <= 0 ? -1 : xaccept4(sockfd, NULL, NULL, SOCK_CLOEXEC);
}
/*
@@ -162,13 +143,15 @@ void send_fd(int sockfd, int fd) {
int read_int(int fd) {
int val;
xxread(fd, &val, sizeof(int));
if (xxread(fd, &val, sizeof(int)) != sizeof(int))
return -1;
return val;
}
int read_int_be(int fd) {
uint32_t val;
xxread(fd, &val, sizeof(val));
if (xxread(fd, &val, sizeof(val)) != sizeof(int))
return -1;
return ntohl(val);
}

View File

@@ -8,11 +8,11 @@ LOCAL_SRC_FILES := stubs/sqlite3_stub.c
include $(BUILD_SHARED_LIBRARY)
# libselinux.so (stub)
include $(CLEAR_VARS)
LOCAL_MODULE:= libselinux
LOCAL_C_INCLUDES := $(LIBSELINUX)
LOCAL_SRC_FILES := stubs/selinux_stub.c
include $(BUILD_SHARED_LIBRARY)
#include $(CLEAR_VARS)
#LOCAL_MODULE:= libselinux
#LOCAL_C_INCLUDES := $(LIBSELINUX)
#LOCAL_SRC_FILES := stubs/selinux_stub.c
#include $(BUILD_SHARED_LIBRARY)
# libxz.a
include $(CLEAR_VARS)

View File

@@ -4,6 +4,8 @@
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
@@ -15,6 +17,7 @@
#include "magisk.h"
#include "utils.h"
#include "img.h"
#include "flags.h"
#define round_size(a) ((((a) / 32) + 2) * 32)
#define SOURCE_TMP "/dev/.img_src"
@@ -97,6 +100,7 @@ static void usage() {
}
int imgtool_main(int argc, char *argv[]) {
cmdline_logging();
if (argc < 2)
usage();
if (strcmp(argv[1], "create") == 0) {

View File

@@ -41,27 +41,21 @@ enum {
HIDE_ITEM_NOT_EXIST,
};
typedef enum {
MAIN_DAEMON,
LOG_DAEMON
} daemon_t;
// daemon.c
void main_daemon();
int connect_daemon();
int connect_daemon2(daemon_t d, int *sockfd);
// log_monitor.c
void log_daemon();
int check_and_start_logger();
extern int log_daemon_started;
int connect_log_daemon();
int start_log_daemon();
// socket.c
socklen_t setup_sockaddr(struct sockaddr_un *sun, daemon_t d);
socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name);
int create_rand_socket(struct sockaddr_un *sun);
int socket_accept(int serv_fd, int timeout);
int socket_accept(int sockfd, int timeout);
int recv_fd(int sockfd);
void send_fd(int sockfd, int fd);
int read_int(int fd);
@@ -79,6 +73,7 @@ void write_key_token(int fd, const char *key, int tok);
* Boot Stages *
***************/
void unlock_blocks();
void startup();
void post_fs_data(int client);
void late_start(int client);
@@ -98,6 +93,6 @@ void ls_hide_list(int client);
* Superuser *
*************/
void su_daemon_receiver(int client, struct ucred *credential);
void su_daemon_handler(int client, struct ucred *credential);
#endif

View File

@@ -8,7 +8,8 @@
* DB Settings *
***************/
#define DB_SETTING_KEYS ((char *[]) { \
#define DB_SETTING_KEYS \
((char *[]) { \
"root_access", \
"multiuser_mode", \
"mnt_ns" \
@@ -49,18 +50,20 @@ struct db_settings {
int v[DB_SETTINGS_NUM];
};
#define DEFAULT_DB_SETTINGS (struct db_settings) { .v = {\
#define DEFAULT_DB_SETTINGS \
(struct db_settings) { .v = { \
ROOT_ACCESS_APPS_AND_ADB, \
MULTIUSER_MODE_OWNER_ONLY, \
NAMESPACE_MODE_REQUESTER \
NAMESPACE_MODE_REQUESTER, \
}}
/**************
* DB Strings *
**************/
#define DB_STRING_KEYS ((char *[]) { \
"requester" \
#define DB_STRING_KEYS \
((char *[]) { \
"requester", \
})
#define DB_STRING_NUM (sizeof(DB_STRING_KEYS) / sizeof(char*))
@@ -74,8 +77,6 @@ struct db_strings {
char s[DB_STRING_NUM][128];
};
void INIT_DB_STRINGS(struct db_strings *str);
/*************
* SU Access *
*************/

View File

@@ -0,0 +1,15 @@
#pragma once
/* Include this header anywhere you access MAGISK_DEBUG, MAGISK_VERSION, MAGISK_VER_CODE.
*
* This file is only for more precise incremental builds. We can make sure code that uses
* external flags are re-compiled by updating the timestamp of this file
* */
#ifndef MAGISK_VERSION
#define MAGISK_VERSION 99.99
#endif
#ifndef MAGISK_VER_CODE
#define MAGISK_VER_CODE 99999
#endif

View File

@@ -1,68 +0,0 @@
/* logging.h - Error handling and logging
*/
#ifndef _LOGGING_H_
#define _LOGGING_H_
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#define str(a) #a
#define xstr(a) str(a)
/**************
* No logging *
**************/
#define LOGD(...)
#define LOGI(...)
#define LOGW(...)
#define LOGE(...)
#define PLOGE(...)
/******************
* Daemon logging *
******************/
#ifdef IS_DAEMON
#undef LOGI
#undef LOGW
#undef LOGE
#undef PLOGE
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "Magisk"
#ifdef MAGISK_DEBUG
#undef LOGD
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#endif
/********************
* Tools Log & Exit *
********************/
#ifdef XWRAP_EXIT
#undef LOGE
#undef PLOGE
#include <stdio.h>
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
#endif
#endif // _LOGGING_H_

View File

@@ -6,18 +6,11 @@
#include "logging.h"
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
#define MAIN_SOCKET "d30138f2310a9fb9c54a3e0c21f58591"
#define LOG_SOCKET "5864cd77f2f8c59b3882e2d35dbf51e4"
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
#ifndef ARG_MAX
#define ARG_MAX 4096
#endif
#define LOGFILE "/cache/magisk.log"
#define UNBLOCKFILE "/dev/.magisk.unblock"
#define TMPSEPOLICY "/dev/.tmp_sepolicy"
#define DISABLEFILE "/cache/.disable_magisk"
#define MAGISKTMP "/sbin/.core"
#define BLOCKDIR MAGISKTMP "/block"
@@ -36,26 +29,26 @@
#define MANAGERAPK DATABIN "/magisk.apk"
#define MAGISKRC "/init.magisk.rc"
// selinuxfs paths
// selinux consts
#define SELINUX_PATH "/sys/fs/selinux"
#define SELINUX_ENFORCE SELINUX_PATH "/enforce"
#define SELINUX_POLICY SELINUX_PATH "/policy"
#define SELINUX_LOAD SELINUX_PATH "/load"
#define SELINUX_CONTEXT SELINUX_PATH "/context"
#define SEPOL_PROC_DOMAIN "magisk"
#define SEPOL_FILE_DOMAIN "magisk_file"
#define MAGISKHIDE_PROP "persist.magisk.hide"
extern char *argv0; /* For changing process name */
#define applet ((char *[]) { "su", "resetprop", "magiskhide", "imgtool", NULL })
#define applet_names ((char *[]) { "magisk", "su", "resetprop", "magiskhide", "imgtool", NULL })
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
int create_links(const char *bin, const char *path);
// Multi-call entrypoints
int magisk_main(int argc, char *argv[]);
int magiskhide_main(int argc, char *argv[]);
int magiskpolicy_main(int argc, char *argv[]);
int su_client_main(int argc, char *argv[]);

View File

@@ -1,4 +1,4 @@
/* magiskinit.c - Pre-init Magisk support
/* init.c - Pre-init Magisk support
*
* This code has to be compiled statically to work properly.
*
@@ -43,15 +43,9 @@
#include "magiskrc.h"
#include "utils.h"
#include "magiskpolicy.h"
#include "daemon.h"
#include "magisk.h"
#ifdef MAGISK_DEBUG
#define VLOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define VLOG(fmt, ...)
#endif
#include "flags.h"
#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
@@ -95,7 +89,7 @@ static void parse_cmdline(struct cmdline *cmd) {
if (cmd->dt_dir[0] == '\0')
strcpy(cmd->dt_dir, DEFAULT_DT_DIR);
VLOG("cmdline: skip_initramfs[%d] slot[%s] dt_dir[%s]\n", cmd->skip_initramfs, cmd->slot, cmd->dt_dir);
LOGD("cmdline: skip_initramfs[%d] slot[%s] dt_dir[%s]\n", cmd->skip_initramfs, cmd->slot, cmd->dt_dir);
}
static void parse_device(struct device *dev, const char *uevent) {
@@ -114,7 +108,7 @@ static void parse_device(struct device *dev, const char *uevent) {
}
}
fclose(fp);
VLOG("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor);
LOGD("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor);
}
static int setup_block(struct device *dev, const char *partname) {
@@ -200,7 +194,7 @@ static int verify_precompiled() {
}
}
closedir(dir);
VLOG("sys_sha[%.*s]\nven_sha[%.*s]\n", sizeof(sys_sha), sys_sha, sizeof(ven_sha), ven_sha);
LOGD("sys_sha[%.*s]\nven_sha[%.*s]\n", sizeof(sys_sha), sys_sha, sizeof(ven_sha), ven_sha);
return memcmp(sys_sha, ven_sha, sizeof(sys_sha)) == 0;
}
@@ -340,6 +334,10 @@ int main(int argc, char *argv[]) {
return dump_magiskrc(argv[3], 0755);
}
#ifdef MAGISK_DEBUG
log_cb.d = vprintf;
#endif
// Prevent file descriptor confusion
mknod("/null", S_IFCHR | 0666, makedev(1, 3));
int null = open("/null", O_RDWR | O_CLOEXEC);

View File

@@ -1,452 +0,0 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libfdt.h>
#include <sys/mman.h>
#include "bootimg.h"
#include "magiskboot.h"
#include "utils.h"
#include "logging.h"
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
// Macros to determine header on-the-go
#define lheader(b, e, o) \
((b)->flags & PXA_FLAG) ? \
(((struct pxa_boot_img_hdr*) (b)->hdr)->e o) : \
(((struct boot_img_hdr*) (b)->hdr)->e o)
#define header(b, e) (lheader(b, e,))
static void dump(void *buf, size_t size, const char *filename) {
if (size == 0)
return;
int fd = creat(filename, 0644);
xwrite(fd, buf, size);
close(fd);
}
static size_t restore(const char *filename, int fd) {
int ifd = xopen(filename, O_RDONLY);
size_t size = lseek(ifd, 0, SEEK_END);
lseek(ifd, 0, SEEK_SET);
xsendfile(fd, ifd, NULL, size);
close(ifd);
return size;
}
static void restore_buf(int fd, const void *buf, size_t size) {
xwrite(fd, buf, size);
}
static void print_hdr(const boot_img *boot) {
fprintf(stderr, "KERNEL [%u]\n", header(boot, kernel_size));
fprintf(stderr, "RAMDISK [%u]\n", header(boot, ramdisk_size));
fprintf(stderr, "SECOND [%u]\n", header(boot, second_size));
fprintf(stderr, "EXTRA [%u]\n", header(boot, extra_size));
fprintf(stderr, "PAGESIZE [%u]\n", header(boot, page_size));
if (!(boot->flags & PXA_FLAG)) {
uint32_t os_version = ((boot_img_hdr*) boot->hdr)->os_version;
if (os_version) {
int a,b,c,y,m = 0;
int version, patch_level;
version = os_version >> 11;
patch_level = os_version & 0x7ff;
a = (version >> 14) & 0x7f;
b = (version >> 7) & 0x7f;
c = version & 0x7f;
fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c);
y = (patch_level >> 4) + 2000;
m = patch_level & 0xf;
fprintf(stderr, "PATCH_LEVEL [%d-%02d]\n", y, m);
}
}
fprintf(stderr, "NAME [%s]\n", header(boot, name));
fprintf(stderr, "CMDLINE [%s]\n", header(boot, cmdline));
fprintf(stderr, "CHECKSUM [");
for (int i = 0; i < ((boot->flags & SHA256_FLAG) ? SHA256_DIGEST_SIZE : SHA_DIGEST_SIZE); ++i)
fprintf(stderr, "%02x", header(boot, id)[i]);
fprintf(stderr, "]\n");
}
static void clean_boot(boot_img *boot) {
munmap(boot->map_addr, boot->map_size);
free(boot->hdr);
free(boot->k_hdr);
free(boot->r_hdr);
free(boot->b_hdr);
memset(boot, 0, sizeof(*boot));
}
#define CHROMEOS_RET 2
#define ELF32_RET 3
#define ELF64_RET 4
#define pos_align() pos = align(pos, header(boot, page_size))
int parse_img(const char *image, boot_img *boot) {
memset(boot, 0, sizeof(*boot));
mmap_ro(image, &boot->map_addr, &boot->map_size);
// Parse image
fprintf(stderr, "Parsing boot image: [%s]\n", image);
for (void *head = boot->map_addr; head < boot->map_addr + boot->map_size; ++head) {
size_t pos = 0;
switch (check_fmt(head, boot->map_size)) {
case CHROMEOS:
// The caller should know it's chromeos, as it needs additional signing
boot->flags |= CHROMEOS_FLAG;
continue;
case DHTB:
boot->flags |= DHTB_FLAG;
boot->flags |= SEANDROID_FLAG;
fprintf(stderr, "DHTB_HDR\n");
continue;
case ELF32:
exit(ELF32_RET);
case ELF64:
exit(ELF64_RET);
case BLOB:
boot->flags |= BLOB_FLAG;
fprintf(stderr, "TEGRA_BLOB\n");
boot->b_hdr = malloc(sizeof(blob_hdr));
memcpy(boot->b_hdr, head, sizeof(blob_hdr));
continue;
case AOSP:
// Read the header
if (((boot_img_hdr*) head)->page_size >= 0x02000000) {
boot->flags |= PXA_FLAG;
fprintf(stderr, "PXA_BOOT_HDR\n");
boot->hdr = malloc(sizeof(pxa_boot_img_hdr));
memcpy(boot->hdr, head, sizeof(pxa_boot_img_hdr));
} else if (memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_MAGIC, 12) == 0
|| memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_NEW_MAGIC, 26) == 0) {
boot->flags |= NOOKHD_FLAG;
fprintf(stderr, "NOOKHD_GREEN_LOADER\n");
head += NOOKHD_PRE_HEADER_SZ - 1;
continue;
} else if (memcmp(((boot_img_hdr*) head)->name, ACCLAIM_MAGIC, 10) == 0) {
boot->flags |= ACCLAIM_FLAG;
fprintf(stderr, "ACCLAIM_BAUWKSBOOT\n");
head += ACCLAIM_PRE_HEADER_SZ - 1;
continue;
} else {
boot->hdr = malloc(sizeof(boot_img_hdr));
memcpy(boot->hdr, head, sizeof(boot_img_hdr));
}
pos += header(boot, page_size);
for (int i = SHA_DIGEST_SIZE; i < SHA256_DIGEST_SIZE; ++i) {
if (header(boot, id)[i]) {
boot->flags |= SHA256_FLAG;
break;
}
}
print_hdr(boot);
boot->kernel = head + pos;
pos += header(boot, kernel_size);
pos_align();
boot->ramdisk = head + pos;
pos += header(boot, ramdisk_size);
pos_align();
boot->second = head + pos;
pos += header(boot, second_size);
pos_align();
boot->extra = head + pos;
pos += header(boot, extra_size);
pos_align();
if (pos < boot->map_size) {
boot->tail = head + pos;
boot->tail_size = boot->map_size - (boot->tail - boot->map_addr);
}
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
if (boot->tail_size >= 16 && memcmp(boot->tail, SEANDROID_MAGIC, 16) == 0) {
boot->flags |= SEANDROID_FLAG;
} else if (boot->tail_size >= 16 && memcmp(boot->tail, LG_BUMP_MAGIC, 16) == 0) {
boot->flags |= LG_BUMP_FLAG;
}
// Search for dtb in kernel
for (uint32_t i = 0; i < header(boot, kernel_size); ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
// Check that fdt_header.totalsize does not overflow kernel image size
uint32_t dt_size = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 4));
if (dt_size > header(boot, kernel_size) - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: size (%u) > remaining (%u)\n",
i, dt_size, header(boot, kernel_size) - i);
continue;
}
// Check that fdt_header.off_dt_struct does not overflow kernel image size
uint32_t dt_struct_offset = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 8));
if (dt_struct_offset > header(boot, kernel_size) - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: struct offset (%u) > remaining (%u)\n",
i, dt_struct_offset, header(boot, kernel_size) - i);
continue;
}
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
uint32_t dt_begin_node = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + dt_struct_offset));
if (dt_begin_node != FDT_BEGIN_NODE) {
fprintf(stderr, "Invalid DTB detection at 0x%x: header tag of first node != FDT_BEGIN_NODE\n", i);
continue;
}
boot->dtb = boot->kernel + i;
boot->dt_size = header(boot, kernel_size) - i;
lheader(boot, kernel_size, = i);
fprintf(stderr, "DTB [%u]\n", boot->dt_size);
break;
}
}
boot->k_fmt = check_fmt(boot->kernel, header(boot, kernel_size));
boot->r_fmt = check_fmt(boot->ramdisk, header(boot, ramdisk_size));
// Check MTK
if (boot->k_fmt == MTK) {
fprintf(stderr, "MTK_KERNEL_HDR\n");
boot->flags |= MTK_KERNEL;
boot->k_hdr = malloc(sizeof(mtk_hdr));
memcpy(boot->k_hdr, boot->kernel, sizeof(mtk_hdr));
fprintf(stderr, "KERNEL [%u]\n", boot->k_hdr->size);
fprintf(stderr, "NAME [%s]\n", boot->k_hdr->name);
boot->kernel += 512;
lheader(boot, kernel_size, -= 512);
boot->k_fmt = check_fmt(boot->kernel, header(boot, kernel_size));
}
if (boot->r_fmt == MTK) {
fprintf(stderr, "MTK_RAMDISK_HDR\n");
boot->flags |= MTK_RAMDISK;
boot->r_hdr = malloc(sizeof(mtk_hdr));
memcpy(boot->r_hdr, boot->ramdisk, sizeof(mtk_hdr));
fprintf(stderr, "RAMDISK [%u]\n", boot->r_hdr->size);
fprintf(stderr, "NAME [%s]\n", boot->r_hdr->name);
boot->ramdisk += 512;
lheader(boot, ramdisk_size, -= 512);
boot->r_fmt = check_fmt(boot->ramdisk, header(boot, ramdisk_size));
}
char fmt[16];
get_fmt_name(boot->k_fmt, fmt);
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
get_fmt_name(boot->r_fmt, fmt);
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt);
return boot->flags & CHROMEOS_FLAG ? CHROMEOS_RET : 0;
default:
continue;
}
}
LOGE("No boot image magic found!\n");
}
int unpack(const char *image) {
boot_img boot;
int ret = parse_img(image, &boot);
int fd;
// Dump kernel
if (COMPRESSED(boot.k_fmt)) {
fd = creat(KERNEL_FILE, 0644);
decomp(boot.k_fmt, fd, boot.kernel, header(&boot, kernel_size));
close(fd);
} else {
dump(boot.kernel, header(&boot, kernel_size), KERNEL_FILE);
}
// Dump dtb
dump(boot.dtb, boot.dt_size, DTB_FILE);
// Dump ramdisk
if (COMPRESSED(boot.r_fmt)) {
fd = creat(RAMDISK_FILE, 0644);
decomp(boot.r_fmt, fd, boot.ramdisk, header(&boot, ramdisk_size));
close(fd);
} else {
dump(boot.ramdisk, header(&boot, ramdisk_size), RAMDISK_FILE);
}
// Dump second
dump(boot.second, header(&boot, second_size), SECOND_FILE);
// Dump extra
dump(boot.extra, header(&boot, extra_size), EXTRA_FILE);
clean_boot(&boot);
return ret;
}
#define file_align() write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - header_off, header(&boot, page_size)))
void repack(const char* orig_image, const char* out_image) {
boot_img boot;
off_t header_off, kernel_off, ramdisk_off, second_off, extra_off;
// Parse original image
parse_img(orig_image, &boot);
// Reset all sizes
lheader(&boot, kernel_size, = 0);
lheader(&boot, ramdisk_size, = 0);
lheader(&boot, second_size, = 0);
lheader(&boot, extra_size, = 0);
boot.dt_size = 0;
fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
// Create new image
int fd = creat(out_image, 0644);
if (boot.flags & DHTB_FLAG) {
// Skip DHTB header
write_zero(fd, 512);
} else if (boot.flags & BLOB_FLAG) {
// Skip blob header
write_zero(fd, sizeof(blob_hdr));
} else if (boot.flags & NOOKHD_FLAG) {
restore_buf(fd, boot.map_addr, NOOKHD_PRE_HEADER_SZ);
} else if (boot.flags & ACCLAIM_FLAG) {
restore_buf(fd, boot.map_addr, ACCLAIM_PRE_HEADER_SZ);
}
// Skip a page for header
header_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, header(&boot, page_size));
// kernel
kernel_off = lseek(fd, 0, SEEK_CUR);
if (boot.flags & MTK_KERNEL) {
// Skip MTK header
write_zero(fd, 512);
}
if (access(KERNEL_FILE, R_OK) == 0) {
if (COMPRESSED(boot.k_fmt)) {
size_t raw_size;
void *kernel_raw;
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
lheader(&boot, kernel_size, = comp(boot.k_fmt, fd, kernel_raw, raw_size));
munmap(kernel_raw, raw_size);
} else {
lheader(&boot, kernel_size, = restore(KERNEL_FILE, fd));
}
}
// dtb
if (access(DTB_FILE, R_OK) == 0) {
lheader(&boot, kernel_size, += restore(DTB_FILE, fd));
}
file_align();
// ramdisk
ramdisk_off = lseek(fd, 0, SEEK_CUR);
if (boot.flags & MTK_RAMDISK) {
// Skip MTK header
write_zero(fd, 512);
}
if (access(RAMDISK_FILE, R_OK) == 0) {
if (COMPRESSED(boot.r_fmt)) {
size_t cpio_size;
void *cpio;
mmap_ro(RAMDISK_FILE, &cpio, &cpio_size);
lheader(&boot, ramdisk_size, = comp(boot.r_fmt, fd, cpio, cpio_size));
munmap(cpio, cpio_size);
} else {
lheader(&boot, ramdisk_size, = restore(RAMDISK_FILE, fd));
}
file_align();
}
// second
second_off = lseek(fd, 0, SEEK_CUR);
if (access(SECOND_FILE, R_OK) == 0) {
lheader(&boot, second_size, = restore(SECOND_FILE, fd));
file_align();
}
// extra
extra_off = lseek(fd, 0, SEEK_CUR);
if (access(EXTRA_FILE, R_OK) == 0) {
lheader(&boot, extra_size, = restore(EXTRA_FILE, fd));
file_align();
}
// Append tail info
if (boot.flags & SEANDROID_FLAG) {
restore_buf(fd, SEANDROID_MAGIC "\xFF\xFF\xFF\xFF", 20);
}
if (boot.flags & LG_BUMP_FLAG) {
restore_buf(fd, LG_BUMP_MAGIC, 16);
}
close(fd);
// Map output image as rw
munmap(boot.map_addr, boot.map_size);
mmap_rw(out_image, &boot.map_addr, &boot.map_size);
// MTK headers
if (boot.flags & MTK_KERNEL) {
boot.k_hdr->size = header(&boot, kernel_size);
lheader(&boot, kernel_size, += 512);
memcpy(boot.map_addr + kernel_off, boot.k_hdr, sizeof(mtk_hdr));
}
if (boot.flags & MTK_RAMDISK) {
boot.r_hdr->size = header(&boot, ramdisk_size);
lheader(&boot, ramdisk_size, += 512);
memcpy(boot.map_addr + ramdisk_off, boot.r_hdr, sizeof(mtk_hdr));
}
// Update checksum
HASH_CTX ctx;
(boot.flags & SHA256_FLAG) ? SHA256_init(&ctx) : SHA_init(&ctx);
uint32_t size = header(&boot, kernel_size);
HASH_update(&ctx, boot.map_addr + kernel_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = header(&boot, ramdisk_size);
HASH_update(&ctx, boot.map_addr + ramdisk_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = header(&boot, second_size);
HASH_update(&ctx, boot.map_addr + second_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = header(&boot, extra_size);
if (size) {
HASH_update(&ctx, boot.map_addr + extra_off, size);
HASH_update(&ctx, &size, sizeof(size));
}
memset(header(&boot, id), 0, 32);
memcpy(header(&boot, id), HASH_final(&ctx),
(boot.flags & SHA256_FLAG) ? SHA256_DIGEST_SIZE : SHA_DIGEST_SIZE);
// Print new image info
print_hdr(&boot);
// Main header
memcpy(boot.map_addr + header_off, boot.hdr,
(boot.flags & PXA_FLAG) ? sizeof(pxa_boot_img_hdr) : sizeof(boot_img_hdr));
if (boot.flags & DHTB_FLAG) {
// DHTB header
dhtb_hdr *hdr = boot.map_addr;
memcpy(hdr, DHTB_MAGIC, 8);
hdr->size = boot.map_size - 512;
SHA256_hash(boot.map_addr + 512, hdr->size, hdr->checksum);
} else if (boot.flags & BLOB_FLAG) {
// Blob headers
boot.b_hdr->size = boot.map_size - sizeof(blob_hdr);
memcpy(boot.map_addr, boot.b_hdr, sizeof(blob_hdr));
}
clean_boot(&boot);
}

View File

@@ -0,0 +1,459 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libfdt.h>
#include <sys/mman.h>
#include <mincrypt/sha.h>
#include <mincrypt/sha256.h>
extern "C" {
#include "bootimg.h"
#include "magiskboot.h"
#include "utils.h"
#include "logging.h"
}
static void dump(void *buf, size_t size, const char *filename) {
if (size == 0)
return;
int fd = creat(filename, 0644);
xwrite(fd, buf, size);
close(fd);
}
static size_t restore(const char *filename, int fd) {
int ifd = xopen(filename, O_RDONLY);
size_t size = lseek(ifd, 0, SEEK_END);
lseek(ifd, 0, SEEK_SET);
xsendfile(fd, ifd, NULL, size);
close(ifd);
return size;
}
static void restore_buf(int fd, const void *buf, size_t size) {
xwrite(fd, buf, size);
}
boot_img::~boot_img() {
munmap(map_addr, map_size);
delete hdr;
delete k_hdr;
delete r_hdr;
delete b_hdr;
}
#define CHROMEOS_RET 2
#define ELF32_RET 3
#define ELF64_RET 4
#define pos_align() pos = align(pos, page_size())
int boot_img::parse_image(const char * image) {
mmap_ro(image, (void **) &map_addr, &map_size);
// Parse image
fprintf(stderr, "Parsing boot image: [%s]\n", image);
for (uint8_t *head = map_addr; head < map_addr + map_size; ++head) {
size_t pos = 0;
switch (check_fmt(head, map_size)) {
case CHROMEOS:
// The caller should know it's chromeos, as it needs additional signing
flags |= CHROMEOS_FLAG;
break;
case DHTB:
flags |= DHTB_FLAG;
flags |= SEANDROID_FLAG;
fprintf(stderr, "DHTB_HDR\n");
break;
case ELF32:
exit(ELF32_RET);
case ELF64:
exit(ELF64_RET);
case BLOB:
flags |= BLOB_FLAG;
fprintf(stderr, "TEGRA_BLOB\n");
b_hdr = new blob_hdr();
memcpy(b_hdr, head, sizeof(blob_hdr));
break;
case AOSP:
// Read the header
if (((boot_img_hdr*) head)->page_size >= 0x02000000) {
flags |= PXA_FLAG;
fprintf(stderr, "PXA_BOOT_HDR\n");
hdr = new boot_img_hdr_pxa();
memcpy(hdr, head, sizeof(boot_img_hdr_pxa));
} else if (memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_MAGIC, 12) == 0
|| memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_NEW_MAGIC, 26) == 0) {
flags |= NOOKHD_FLAG;
fprintf(stderr, "NOOKHD_GREEN_LOADER\n");
head += NOOKHD_PRE_HEADER_SZ - 1;
continue;
} else if (memcmp(((boot_img_hdr*) head)->name, ACCLAIM_MAGIC, 10) == 0) {
flags |= ACCLAIM_FLAG;
fprintf(stderr, "ACCLAIM_BAUWKSBOOT\n");
head += ACCLAIM_PRE_HEADER_SZ - 1;
continue;
} else {
hdr = new boot_img_hdr();
memcpy(hdr, head, sizeof(boot_img_hdr));
}
pos += page_size();
flags |= id()[SHA_DIGEST_SIZE] ? SHA256_FLAG : 0;
print_hdr();
kernel = head + pos;
pos += hdr->kernel_size;
pos_align();
ramdisk = head + pos;
pos += hdr->ramdisk_size;
pos_align();
second = head + pos;
pos += hdr->second_size;
pos_align();
extra = head + pos;
pos += extra_size();
pos_align();
recov_dtbo = head + pos;
pos += recovery_dtbo_size();
pos_align();
if (pos < map_size) {
tail = head + pos;
tail_size = map_size - (tail - map_addr);
}
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
if (tail_size >= 16 && memcmp(tail, SEANDROID_MAGIC, 16) == 0) {
flags |= SEANDROID_FLAG;
} else if (tail_size >= 16 && memcmp(tail, LG_BUMP_MAGIC, 16) == 0) {
flags |= LG_BUMP_FLAG;
}
find_dtb();
k_fmt = check_fmt(kernel, hdr->kernel_size);
r_fmt = check_fmt(ramdisk, hdr->ramdisk_size);
// Check MTK
if (k_fmt == MTK) {
fprintf(stderr, "MTK_KERNEL_HDR\n");
flags |= MTK_KERNEL;
k_hdr = new mtk_hdr();
memcpy(k_hdr, kernel, sizeof(mtk_hdr));
fprintf(stderr, "KERNEL [%u]\n", k_hdr->size);
fprintf(stderr, "NAME [%s]\n", k_hdr->name);
kernel += 512;
hdr->kernel_size -= 512;
k_fmt = check_fmt(kernel, hdr->kernel_size);
}
if (r_fmt == MTK) {
fprintf(stderr, "MTK_RAMDISK_HDR\n");
flags |= MTK_RAMDISK;
r_hdr = new mtk_hdr();
memcpy(r_hdr, ramdisk, sizeof(mtk_hdr));
fprintf(stderr, "RAMDISK [%u]\n", r_hdr->size);
fprintf(stderr, "NAME [%s]\n", r_hdr->name);
ramdisk += 512;
hdr->ramdisk_size -= 512;
r_fmt = check_fmt(ramdisk, hdr->ramdisk_size);
}
char fmt[16];
get_fmt_name(k_fmt, fmt);
fprintf(stderr, "KERNEL_FMT\t[%s]\n", fmt);
get_fmt_name(r_fmt, fmt);
fprintf(stderr, "RAMDISK_FMT\t[%s]\n", fmt);
return flags & CHROMEOS_FLAG ? CHROMEOS_RET : 0;
default:
break;
}
}
LOGE("No boot image magic found!\n");
exit(1);
}
void boot_img::find_dtb() {
for (uint32_t i = 0; i < hdr->kernel_size; ++i) {
if (memcmp(kernel + i, DTB_MAGIC, 4))
continue;
// Check that fdt_header.totalsize does not overflow kernel image size
uint32_t dt_sz = fdt32_to_cpu(*(uint32_t *)(kernel + i + 4));
if (dt_sz > hdr->kernel_size - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: size (%u) > remaining (%u)\n",
i, dt_sz, hdr->kernel_size - i);
continue;
}
// Check that fdt_header.off_dt_struct does not overflow kernel image size
uint32_t dt_struct_offset = fdt32_to_cpu(*(uint32_t *)(kernel + i + 8));
if (dt_struct_offset > hdr->kernel_size - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: "
"struct offset (%u) > remaining (%u)\n",
i, dt_struct_offset, hdr->kernel_size - i);
continue;
}
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
uint32_t dt_begin_node = fdt32_to_cpu(*(uint32_t *)(kernel + i + dt_struct_offset));
if (dt_begin_node != FDT_BEGIN_NODE) {
fprintf(stderr, "Invalid DTB detection at 0x%x: "
"header tag of first node != FDT_BEGIN_NODE\n", i);
continue;
}
dtb = kernel + i;
dt_size = hdr->kernel_size - i;
hdr->kernel_size = i;
fprintf(stderr, "DTB\t\t[%u]\n", dt_size);
break;
}
}
void boot_img::print_hdr() {
fprintf(stderr, "HEADER_VER\t[%u]\n", header_version());
fprintf(stderr, "KERNEL_SZ\t[%u]\n", hdr->kernel_size);
fprintf(stderr, "RAMDISK_SZ\t[%u]\n", hdr->ramdisk_size);
fprintf(stderr, "SECOND_SZ\t[%u]\n", hdr->second_size);
fprintf(stderr, "EXTRA_SZ\t[%u]\n", extra_size());
fprintf(stderr, "RECOV_DTBO_SZ\t[%u]\n", recovery_dtbo_size());
uint32_t ver = os_version();
if (ver) {
int a,b,c,y,m = 0;
int version, patch_level;
version = ver >> 11;
patch_level = ver & 0x7ff;
a = (version >> 14) & 0x7f;
b = (version >> 7) & 0x7f;
c = version & 0x7f;
fprintf(stderr, "OS_VERSION\t[%d.%d.%d]\n", a, b, c);
y = (patch_level >> 4) + 2000;
m = patch_level & 0xf;
fprintf(stderr, "PATCH_LEVEL\t[%d-%02d]\n", y, m);
}
fprintf(stderr, "PAGESIZE\t[%u]\n", page_size());
fprintf(stderr, "NAME\t\t[%s]\n", name());
fprintf(stderr, "CMDLINE\t\t[%s]\n", cmdline());
fprintf(stderr, "CHECKSUM\t[");
for (int i = 0; id()[i]; ++i)
fprintf(stderr, "%02x", id()[i]);
fprintf(stderr, "]\n");
}
int unpack(const char *image) {
boot_img boot;
int ret = boot.parse_image(image);
int fd;
// Dump kernel
if (COMPRESSED(boot.k_fmt)) {
fd = creat(KERNEL_FILE, 0644);
decomp(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size);
close(fd);
} else {
dump(boot.kernel, boot.hdr->kernel_size, KERNEL_FILE);
}
// Dump dtb
dump(boot.dtb, boot.dt_size, DTB_FILE);
// Dump ramdisk
if (COMPRESSED(boot.r_fmt)) {
fd = creat(RAMDISK_FILE, 0644);
decomp(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size);
close(fd);
} else {
dump(boot.ramdisk, boot.hdr->ramdisk_size, RAMDISK_FILE);
}
// Dump second
dump(boot.second, boot.hdr->second_size, SECOND_FILE);
// Dump extra
dump(boot.extra, boot.extra_size(), EXTRA_FILE);
// Dump recovery_dtbo
dump(boot.recov_dtbo, boot.recovery_dtbo_size(), RECV_DTBO_FILE);
return ret;
}
#define file_align() write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - header_off, boot.page_size()))
void repack(const char* orig_image, const char* out_image) {
boot_img boot;
off_t header_off, kernel_off, ramdisk_off, second_off, extra_off;
// Parse original image
boot.parse_image(orig_image);
// Reset sizes
boot.hdr->kernel_size = 0;
boot.hdr->ramdisk_size = 0;
boot.hdr->second_size = 0;
boot.dt_size = 0;
fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
// Create new image
int fd = creat(out_image, 0644);
if (boot.flags & DHTB_FLAG) {
// Skip DHTB header
write_zero(fd, 512);
} else if (boot.flags & BLOB_FLAG) {
// Skip blob header
write_zero(fd, sizeof(blob_hdr));
} else if (boot.flags & NOOKHD_FLAG) {
restore_buf(fd, boot.map_addr, NOOKHD_PRE_HEADER_SZ);
} else if (boot.flags & ACCLAIM_FLAG) {
restore_buf(fd, boot.map_addr, ACCLAIM_PRE_HEADER_SZ);
}
// Skip a page for header
header_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, boot.page_size());
// kernel
kernel_off = lseek(fd, 0, SEEK_CUR);
if (boot.flags & MTK_KERNEL) {
// Skip MTK header
write_zero(fd, 512);
}
if (access(KERNEL_FILE, R_OK) == 0) {
if (COMPRESSED(boot.k_fmt)) {
size_t raw_size;
void *kernel_raw;
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
boot.hdr->kernel_size = comp(boot.k_fmt, fd, kernel_raw, raw_size);
munmap(kernel_raw, raw_size);
} else {
boot.hdr->kernel_size = restore(KERNEL_FILE, fd);
}
}
// dtb
if (access(DTB_FILE, R_OK) == 0)
boot.hdr->kernel_size += restore(DTB_FILE, fd);
file_align();
// ramdisk
ramdisk_off = lseek(fd, 0, SEEK_CUR);
if (boot.flags & MTK_RAMDISK) {
// Skip MTK header
write_zero(fd, 512);
}
if (access(RAMDISK_FILE, R_OK) == 0) {
if (COMPRESSED(boot.r_fmt)) {
size_t cpio_size;
void *cpio;
mmap_ro(RAMDISK_FILE, &cpio, &cpio_size);
boot.hdr->ramdisk_size = comp(boot.r_fmt, fd, cpio, cpio_size);
munmap(cpio, cpio_size);
} else {
boot.hdr->ramdisk_size = restore(RAMDISK_FILE, fd);
}
file_align();
}
// second
second_off = lseek(fd, 0, SEEK_CUR);
if (access(SECOND_FILE, R_OK) == 0) {
boot.hdr->second_size = restore(SECOND_FILE, fd);
file_align();
}
// extra
extra_off = lseek(fd, 0, SEEK_CUR);
if (access(EXTRA_FILE, R_OK) == 0) {
boot.extra_size(restore(EXTRA_FILE, fd));
file_align();
}
// recovery_dtbo
if (access(RECV_DTBO_FILE, R_OK) == 0) {
boot.recovery_dtbo_offset(lseek(fd, 0, SEEK_CUR));
boot.recovery_dtbo_size(restore(RECV_DTBO_FILE, fd));
file_align();
}
// Append tail info
if (boot.flags & SEANDROID_FLAG) {
restore_buf(fd, SEANDROID_MAGIC "\xFF\xFF\xFF\xFF", 20);
}
if (boot.flags & LG_BUMP_FLAG) {
restore_buf(fd, LG_BUMP_MAGIC, 16);
}
close(fd);
// Map output image as rw
munmap(boot.map_addr, boot.map_size);
mmap_rw(out_image, reinterpret_cast<void **>(&boot.map_addr), &boot.map_size);
// MTK headers
if (boot.flags & MTK_KERNEL) {
boot.k_hdr->size = boot.hdr->kernel_size;
boot.hdr->kernel_size += 512;
memcpy(boot.map_addr + kernel_off, boot.k_hdr, sizeof(mtk_hdr));
}
if (boot.flags & MTK_RAMDISK) {
boot.r_hdr->size = boot.hdr->ramdisk_size;
boot.hdr->ramdisk_size += 512;
memcpy(boot.map_addr + ramdisk_off, boot.r_hdr, sizeof(mtk_hdr));
}
// Update checksum
HASH_CTX ctx;
(boot.flags & SHA256_FLAG) ? SHA256_init(&ctx) : SHA_init(&ctx);
uint32_t size = boot.hdr->kernel_size;
HASH_update(&ctx, boot.map_addr + kernel_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = boot.hdr->ramdisk_size;
HASH_update(&ctx, boot.map_addr + ramdisk_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = boot.hdr->second_size;
HASH_update(&ctx, boot.map_addr + second_off, size);
HASH_update(&ctx, &size, sizeof(size));
size = boot.extra_size();
if (size) {
HASH_update(&ctx, boot.map_addr + extra_off, size);
HASH_update(&ctx, &size, sizeof(size));
}
if (boot.header_version()) {
size = boot.recovery_dtbo_size();
HASH_update(&ctx, boot.map_addr + boot.recovery_dtbo_offset(), size);
HASH_update(&ctx, &size, sizeof(size));
}
memset(boot.id(), 0, 32);
memcpy(boot.id(), HASH_final(&ctx),
(boot.flags & SHA256_FLAG) ? SHA256_DIGEST_SIZE : SHA_DIGEST_SIZE);
// Print new image info
boot.print_hdr();
// Main header
memcpy(boot.map_addr + header_off, boot.hdr, boot.hdr_size());
if (boot.flags & DHTB_FLAG) {
// DHTB header
dhtb_hdr *hdr = reinterpret_cast<dhtb_hdr *>(boot.map_addr);
memcpy(hdr, DHTB_MAGIC, 8);
hdr->size = boot.map_size - 512;
SHA256_hash(boot.map_addr + 512, hdr->size, hdr->checksum);
} else if (boot.flags & BLOB_FLAG) {
// Blob headers
boot.b_hdr->size = boot.map_size - sizeof(blob_hdr);
memcpy(boot.map_addr, boot.b_hdr, sizeof(blob_hdr));
}
}

View File

@@ -4,7 +4,7 @@
#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_
typedef struct boot_img_hdr {
struct boot_img_hdr_base {
char magic[8];
uint32_t kernel_size; /* size in bytes */
@@ -15,10 +15,19 @@ typedef struct boot_img_hdr {
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
} __attribute__((packed));
struct boot_img_hdr_v0 : public boot_img_hdr_base {
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
uint32_t extra_size; /* extra blob size in bytes */
/* In header v1, this field is used for header version
* However, on some devices like Samsung, this field is used to store DTB
* We will treat this field differently based on its value */
union {
uint32_t header_version; /* the version of the header */
uint32_t extra_size; /* extra blob size in bytes */
};
/* operating system version and security patch level; for
* version "A.B.C" and patch level "Y-M-D":
@@ -34,20 +43,19 @@ typedef struct boot_img_hdr {
/* Supplemental command line data; kept here to maintain
* binary compatibility with older versions of mkbootimg */
char extra_cmdline[1024];
} __attribute__((packed)) boot_img_hdr ;
} __attribute__((packed));
typedef struct pxa_boot_img_hdr {
char magic[8];
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
uint32_t header_size;
} __attribute__((packed));
uint32_t kernel_size; /* size in bytes */
uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */
uint32_t ramdisk_addr; /* physical load addr */
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
// Default to hdr v1
typedef boot_img_hdr_v1 boot_img_hdr;
// Special Samsung header
struct boot_img_hdr_pxa : public boot_img_hdr_base {
uint32_t extra_size; /* extra blob size in bytes */
uint32_t unknown; /* unknown value */
uint32_t tags_addr; /* physical addr for kernel tags */
@@ -60,7 +68,7 @@ typedef struct pxa_boot_img_hdr {
/* Supplemental command line data; kept here to maintain
* binary compatibility with older versions of mkbootimg */
char extra_cmdline[1024];
} __attribute__((packed)) pxa_boot_img_hdr;
} __attribute__((packed));
/*
** +-----------------+
@@ -74,11 +82,14 @@ typedef struct pxa_boot_img_hdr {
** +-----------------+
** | extra blob | p pages
** +-----------------+
** | recovery dtbo | q pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
** p = (extra_size + page_size - 1) / page_size
** q = (recovery_dtbo_size + page_size - 1) / page_size
**
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
@@ -92,19 +103,19 @@ typedef struct pxa_boot_img_hdr {
** else: jump to kernel_addr
*/
typedef struct mtk_hdr {
struct mtk_hdr {
uint32_t magic; /* MTK magic */
uint32_t size; /* Size of the content */
char name[32]; /* The type of the header */
} __attribute__((packed)) mtk_hdr;
} __attribute__((packed));
typedef struct dhtb_hdr {
struct dhtb_hdr {
char magic[8]; /* DHTB magic */
uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
} __attribute__((packed)) dhtb_hdr;
} __attribute__((packed));
typedef struct blob_hdr {
struct blob_hdr {
char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */
uint32_t datalen; /* 0x00000000 */
uint32_t signature; /* 0x00000000 */
@@ -118,7 +129,7 @@ typedef struct blob_hdr {
uint32_t offset; /* offset in blob where this partition starts */
uint32_t size; /* Size of data */
uint32_t version; /* 0x00000001 */
} __attribute__((packed)) blob_hdr;
} __attribute__((packed));
// Flags
#define MTK_KERNEL 0x0001
@@ -133,16 +144,16 @@ typedef struct blob_hdr {
#define NOOKHD_FLAG 0x0200
#define ACCLAIM_FLAG 0x0400
typedef struct boot_img {
struct boot_img {
// Memory map of the whole image
void *map_addr;
uint8_t *map_addr;
size_t map_size;
// Headers
void *hdr; /* Either boot_img_hdr or pxa_boot_img_hdr */
mtk_hdr *k_hdr; /* MTK kernel header */
mtk_hdr *r_hdr; /* MTK ramdisk header */
blob_hdr *b_hdr; /* Tegra blob header */
boot_img_hdr_base *hdr; /* Android boot image header */
mtk_hdr *k_hdr; /* MTK kernel header */
mtk_hdr *r_hdr; /* MTK ramdisk header */
blob_hdr *b_hdr; /* Tegra blob header */
// Flags to indicate the state of current boot image
uint16_t flags;
@@ -152,18 +163,67 @@ typedef struct boot_img {
format_t r_fmt;
// Pointer to dtb that is appended after kernel
void *dtb;
uint8_t *dtb;
uint32_t dt_size;
// Pointer to end of image
void *tail;
uint8_t *tail;
size_t tail_size;
// Pointers to blocks defined in header
void *kernel;
void *ramdisk;
void *second;
void *extra;
} boot_img;
uint8_t *kernel;
uint8_t *ramdisk;
uint8_t *second;
uint8_t *extra;
uint8_t *recov_dtbo;
~boot_img();
int parse_image(const char *);
void find_dtb();
void print_hdr();
/* Access elements in header */
#define IS_PXA (flags & PXA_FLAG)
#define dyn_access(x) (IS_PXA ? \
static_cast<boot_img_hdr_pxa *>(hdr)->x : \
static_cast<boot_img_hdr_v1 *>(hdr)->x)
#define dyn_get(x, t) t x() const { return dyn_access(x); }
#define dyn_set(x, t) void x(t v) { dyn_access(x) = v; }
#define hdr_get(x, t) t x() const { return IS_PXA ? 0 : static_cast<boot_img_hdr *>(hdr)->x; }
#define hdr_set(x, t) void x(t v) { if (!IS_PXA) static_cast<boot_img_hdr *>(hdr)->x = v; }
dyn_set(extra_size, uint32_t);
dyn_get(page_size, uint32_t);
dyn_get(name, char *);
dyn_get(cmdline, char *);
dyn_get(id, char *);
hdr_get(os_version, uint32_t);
hdr_get(recovery_dtbo_size, uint32_t);
hdr_set(recovery_dtbo_size, uint32_t);
hdr_get(recovery_dtbo_offset, uint32_t);
hdr_set(recovery_dtbo_offset, uint32_t);
uint32_t header_version() {
if (IS_PXA)
return 0;
uint32_t ver = static_cast<boot_img_hdr *>(hdr)->header_version;
// There won't be v4 header any time soon...
// If larger than 4, assume this field will be treated as extra_size
return ver > 4 ? 0 : ver;
}
uint32_t extra_size() {
// If header version > 0, we should treat this field as header_version
return header_version() ? 0 : dyn_access(extra_size);
}
size_t hdr_size() {
return IS_PXA ? sizeof(boot_img_hdr_pxa) : sizeof(boot_img_hdr);
}
};
#endif

View File

@@ -1,5 +1,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <zlib.h>

View File

@@ -1,6 +1,8 @@
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "cpio.h"
#include "logging.h"

View File

@@ -1,6 +1,5 @@
#include <string.h>
#include "bootimg.h"
#include "format.h"
#define MATCH(s) (len >= (sizeof(s) - 1) && memcmp(buf, s, sizeof(s) - 1) == 0)

View File

@@ -4,20 +4,20 @@
#include <sys/types.h>
#include "logging.h"
#include "bootimg.h"
#include "format.h"
#define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio"
#define SECOND_FILE "second"
#define EXTRA_FILE "extra"
#define DTB_FILE "dtb"
#define RECV_DTBO_FILE "recovery_dtbo"
#define NEW_BOOT "new-boot.img"
// Main entries
int unpack(const char *image);
void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to);
int parse_img(const char *image, boot_img *boot);
int cpio_commands(int argc, char *argv[]);
void comp_file(const char *method, const char *from, const char *to);
void decomp_file(char *from, const char *to);

View File

@@ -1,12 +1,14 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <utils.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "magiskboot.h"
#include "cpio.h"
#include "utils.h"
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n",

View File

@@ -7,9 +7,9 @@
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "utils.h"
@@ -63,6 +63,54 @@ static void rm_magisk_prop(const char *name, const char *value, void *v) {
}
}
/* Call func for each process */
static void ps(void (*func)(int)) {
DIR *dir;
struct dirent *entry;
if (!(dir = xopendir("/proc")))
return;
while ((entry = xreaddir(dir))) {
if (entry->d_type == DT_DIR) {
if (is_num(entry->d_name))
func(atoi(entry->d_name));
}
}
closedir(dir);
}
static int check_proc_name(int pid, const char *name) {
char buf[128];
FILE *f;
sprintf(buf, "/proc/%d/comm", pid);
if ((f = fopen(buf, "r"))) {
fgets(buf, sizeof(buf), f);
if (strcmp(buf, name) == 0)
return 1;
} else {
// The PID is already killed
return 0;
}
fclose(f);
sprintf(buf, "/proc/%d/cmdline", pid);
f = fopen(buf, "r");
fgets(buf, sizeof(buf), f);
fclose(f);
if (strcmp(basename(buf), name) == 0)
return 1;
sprintf(buf, "/proc/%d/exe", pid);
if (access(buf, F_OK) != 0)
return 0;
xreadlink(buf, buf, sizeof(buf));
if (strcmp(basename(buf), name) == 0)
return 1;
return 0;
}
static void kill_proc_cb(int pid) {
if (check_proc_name(pid, proc_name))
kill(pid, SIGTERM);

View File

@@ -45,7 +45,7 @@ void launch_magiskhide(int client) {
return;
}
if (!check_and_start_logger()) {
if (!log_daemon_started) {
if (client > 0) {
write_int(client, LOGCAT_DISABLED);
close(client);
@@ -114,6 +114,7 @@ void stop_magiskhide(int client) {
}
int magiskhide_main(int argc, char *argv[]) {
cmdline_logging();
if (argc < 2) {
usage(argv[0]);
}

View File

@@ -20,6 +20,7 @@
#include "daemon.h"
#include "utils.h"
#include "magiskhide.h"
#include "flags.h"
static int sockfd = -1;
@@ -124,83 +125,82 @@ void proc_monitor() {
term_thread(TERM_THREAD);
}
while(1) {
// Connect to the log daemon
connect_daemon2(LOG_DAEMON, &sockfd);
write_int(sockfd, HIDE_CONNECT);
// Connect to the log daemon
sockfd = connect_log_daemon();
if (sockfd < 0)
return;
write_int(sockfd, HIDE_CONNECT);
FILE *log_in = fdopen(sockfd, "r");
char buf[4096];
while (fgets(buf, sizeof(buf), log_in)) {
char *ss = strchr(buf, '[');
int pid, ppid, num = 0;
char *pos = ss, proc[256];
struct stat ns, pns;
FILE *log_in = fdopen(sockfd, "r");
char buf[4096];
while (fgets(buf, sizeof(buf), log_in)) {
char *ss = strchr(buf, '[');
int pid, ppid, num = 0;
char *pos = ss, proc[256];
struct stat ns, pns;
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++num;
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++num;
}
if(sscanf(ss, num == 6 ? "[%*d %d %*d %*d %256s" : "[%*d %d %*d %256s", &pid, proc) != 2)
continue;
// Make sure our target is alive
if (kill(pid, 0))
continue;
// Allow hiding sub-services of applications
char *colon = strchr(proc, ':');
if (colon)
*colon = '\0';
int hide = 0;
pthread_mutex_lock(&hide_lock);
char *line;
vec_for_each(hide_list, line) {
if (strcmp(proc, line) == 0) {
hide = 1;
break;
}
}
pthread_mutex_unlock(&hide_lock);
if (!hide)
continue;
if(sscanf(ss, num == 6 ? "[%*d %d %*d %*d %256s" : "[%*d %d %*d %256s", &pid, proc) != 2)
continue;
ppid = parse_ppid(pid);
read_ns(ppid, &pns);
do {
read_ns(pid, &ns);
if (ns.st_dev == pns.st_dev && ns.st_ino == pns.st_ino)
usleep(50);
else
break;
} while (1);
// Make sure our target is alive
if (kill(pid, 0))
continue;
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1)
continue;
// Allow hiding sub-services of applications
char *colon = strchr(proc, ':');
if (colon)
*colon = '\0';
int hide = 0;
pthread_mutex_lock(&hide_lock);
char *line;
vec_for_each(hide_list, line) {
if (strcmp(proc, line) == 0) {
hide = 1;
break;
}
}
pthread_mutex_unlock(&hide_lock);
if (!hide)
continue;
ppid = parse_ppid(pid);
read_ns(ppid, &pns);
do {
read_ns(pid, &ns);
if (ns.st_dev == pns.st_dev && ns.st_ino == pns.st_ino)
usleep(50);
else
break;
} while (1);
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1)
continue;
// Restore the colon so we can log the actual process name
if (colon)
*colon = ':';
// Restore the colon so we can log the actual process name
if (colon)
*colon = ':';
#ifdef MAGISK_DEBUG
LOGI("proc_monitor: %s (PID=[%d] ns=%llu)(PPID=[%d] ns=%llu)\n",
proc, pid, ns.st_ino, ppid, pns.st_ino);
LOGI("proc_monitor: %s (PID=[%d] ns=%llu)(PPID=[%d] ns=%llu)\n",
proc, pid, ns.st_ino, ppid, pns.st_ino);
#else
LOGI("proc_monitor: %s\n", proc);
LOGI("proc_monitor: %s\n", proc);
#endif
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
if (fork_dont_care() == 0)
hide_daemon(pid);
}
// The other end EOF, restart the connection
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
if (fork_dont_care() == 0)
hide_daemon(pid);
}
}

View File

@@ -4,6 +4,7 @@
*/
#include <stdio.h>
#include <limits.h>
#include "sepolicy.h"
#include "vector.h"
@@ -390,6 +391,7 @@ static void syntax_error_msg() {
}
int magiskpolicy_main(int argc, char *argv[]) {
cmdline_logging();
char *outfile = NULL, *tok, *saveptr;
int magisk = 0;
struct vector rules;

View File

@@ -7,8 +7,6 @@
#include <stdlib.h>
#define ALL NULL
#define SEPOL_PROC_DOMAIN "magisk"
#define SEPOL_FILE_DOMAIN "magisk_file"
// split policy paths
#define PLAT_POLICY_DIR "/system/etc/selinux/"

View File

@@ -1,3 +1,4 @@
#include "magisk.h"
#include "magiskpolicy.h"
#include "sepolicy.h"

41
native/jni/main.c Normal file
View File

@@ -0,0 +1,41 @@
#include <libgen.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "magisk.h"
#include "selinux.h"
int (*applet_main[]) (int, char *[]) =
{ magisk_main, su_client_main, resetprop_main, magiskhide_main, imgtool_main, NULL };
char *argv0;
__attribute__((noreturn)) static void call_applets(int argc, char *argv[]) {
// Applets
for (int i = 0; applet_names[i]; ++i) {
if (strcmp(basename(argv[0]), applet_names[i]) == 0) {
strcpy(argv0, basename(argv[0]));
exit((*applet_main[i])(argc, argv));
}
}
fprintf(stderr, "%s: applet not found\n", argv[0]);
exit(1);
}
int main(int argc, char *argv[]) {
umask(0);
argv0 = argv[0];
dload_selinux();
if (strcmp(basename(argv0), "magisk.bin") == 0 ||
(strcmp(basename(argv[0]), "magisk") == 0
&& argc > 1 && argv[1][0] != '-')) {
--argc;
++argv;
}
call_applets(argc, argv);
}

View File

@@ -10,9 +10,6 @@
extern int prop_verbose;
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (prop_verbose) fprintf(stderr, __VA_ARGS__); }
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
struct prop_t {
char *name;
char value[PROP_VALUE_MAX];

View File

@@ -153,7 +153,7 @@ static void pb_getprop_cb(const char *name, const char *value, void *v) {
void persist_getprop_all(struct read_cb_t *read_cb) {
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
PRINT_D("resetprop: decode with protobuf from [" PERSISTENT_PROPERTY_DIR "/persistent_properties]\n");
LOGD("resetprop: decode with protobuf from [" PERSISTENT_PROPERTY_DIR "/persistent_properties]\n");
PersistentProperties props = PersistentProperties_init_zero;
props.properties.funcs.decode = prop_decode;
props.properties.arg = read_cb;
@@ -196,7 +196,7 @@ char *persist_getprop(const char *name) {
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return NULL;
PRINT_D("resetprop: read prop from [%s]\n", path);
LOGD("resetprop: read prop from [%s]\n", path);
prop.value[read(fd, prop.value, sizeof(PROP_VALUE_MAX))] = '\0'; // Null terminate the read value
close(fd);
}
@@ -231,7 +231,7 @@ bool persist_deleteprop(const char *name) {
pb_ostream_t ostream = create_ostream(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
props.properties.funcs.encode = prop_encode;
props.properties.arg = &v;
PRINT_D("resetprop: encode with protobuf to [" PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp]\n");
LOGD("resetprop: encode with protobuf to [" PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp]\n");
if (!pb_encode(&ostream, PersistentProperties_fields, &props))
return false;
clone_attr(PERSISTENT_PROPERTY_DIR "/persistent_properties", PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
@@ -244,7 +244,7 @@ bool persist_deleteprop(const char *name) {
char path[PATH_MAX];
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
if (unlink(path) == 0) {
PRINT_D("resetprop: unlink [%s]\n", path);
LOGD("resetprop: unlink [%s]\n", path);
return true;
}
}

View File

@@ -50,7 +50,7 @@ static int check_legal_property_name(const char *name) {
return 0;
illegal:
PRINT_E("Illegal property name: [%s]\n", name);
LOGE("Illegal property name: [%s]\n", name);
return 1;
}
@@ -124,7 +124,7 @@ static int prop_cmp(const void *p1, const void *p2) {
static int init_resetprop() {
if (__system_properties_init()) {
PRINT_E("resetprop: Initialize error\n");
LOGE("resetprop: Initialize error\n");
return -1;
}
return 0;
@@ -176,7 +176,7 @@ char *getprop2(const char *name, int persist) {
if (value)
return value;
}
PRINT_D("resetprop: prop [%s] does not exist\n", name);
LOGD("resetprop: prop [%s] does not exist\n", name);
return NULL;
} else {
char value[PROP_VALUE_MAX];
@@ -185,7 +185,7 @@ char *getprop2(const char *name, int persist) {
.cookie = value
};
__system_property_read_callback(pi, callback_wrapper, &read_cb);
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
LOGD("resetprop: getprop [%s]: [%s]\n", name, value);
return strdup(value);
}
}
@@ -218,7 +218,7 @@ int setprop2(const char *name, const char *value, const int trigger) {
ret = __system_property_update(pi, value, strlen(value));
}
} else {
PRINT_D("resetprop: New prop [%s]\n", name);
LOGD("resetprop: New prop [%s]\n", name);
if (trigger) {
ret = __system_property_set(name, value);
} else {
@@ -226,11 +226,11 @@ int setprop2(const char *name, const char *value, const int trigger) {
}
}
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
LOGD("resetprop: setprop [%s]: [%s] by %s\n", name, value,
trigger ? "property_service" : "modifing prop data structure");
if (ret)
PRINT_E("resetprop: setprop error\n");
LOGE("resetprop: setprop error\n");
return ret;
}
@@ -245,7 +245,7 @@ int deleteprop2(const char *name, int persist) {
if (init_resetprop()) return -1;
char path[PATH_MAX];
path[0] = '\0';
PRINT_D("resetprop: deleteprop [%s]\n", name);
LOGD("resetprop: deleteprop [%s]\n", name);
if (persist && strncmp(name, "persist.", 8) == 0)
persist = persist_deleteprop(name);
return __system_property_del(name) && !(persist && strncmp(name, "persist.", 8) == 0);
@@ -253,10 +253,10 @@ int deleteprop2(const char *name, int persist) {
int read_prop_file(const char* filename, const int trigger) {
if (init_resetprop()) return -1;
PRINT_D("resetprop: Load prop file [%s]\n", filename);
LOGD("resetprop: Load prop file [%s]\n", filename);
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
PRINT_E("Cannot open [%s]\n", filename);
LOGE("Cannot open [%s]\n", filename);
return 1;
}
char *line = NULL, *pch;
@@ -292,7 +292,14 @@ int read_prop_file(const char* filename, const int trigger) {
return 0;
}
static int verbose_logging(const char *fmt, va_list ap) {
return prop_verbose ? vfprintf(stderr, fmt, ap) : 0;
}
int resetprop_main(int argc, char *argv[]) {
cmdline_logging();
log_cb.d = verbose_logging;
int trigger = 1, persist = 0;
char *argv0 = argv[0], *prop;

Some files were not shown because too many files have changed in this diff Show More