Compare commits

...

123 Commits
v16.0 ... v16.6

Author SHA1 Message Date
topjohnwu
375cd0e42b Bump Magisk Manager version 2018-07-08 06:56:47 +08:00
topjohnwu
3934821436 Don't clean output 2018-07-08 00:02:18 +08:00
topjohnwu
c3b473e4bc Fix get_outfd in embedded mode 2018-07-07 17:48:05 +08:00
topjohnwu
7ed2c077de Support deodexed ROM on Oreo 2018-07-07 01:37:04 +08:00
topjohnwu
1283167595 Maintain our own set of loop devices 2018-07-07 01:32:58 +08:00
topjohnwu
23c2e22910 Update image functions 2018-07-06 22:04:06 +08:00
topjohnwu
f44b2dbd45 Rename log_monitor -> log_daemon 2018-07-06 07:57:18 +08:00
topjohnwu
46ee2c3f4e Improve handshake between the 2 daemons 2018-07-06 07:51:17 +08:00
topjohnwu
5d5ec08566 Test the log buffers before running command 2018-07-06 07:32:16 +08:00
topjohnwu
c3a6179a21 Update scripts 2018-07-05 17:29:13 +08:00
topjohnwu
ef175e3cbe Open log file in append mode 2018-07-04 23:46:40 +08:00
topjohnwu
4de51d93ef Tweak info when installing 2018-07-04 23:46:16 +08:00
topjohnwu
e7a2144def Optimize magiskhide to work with the log daemon 2018-07-04 01:52:23 +08:00
topjohnwu
52a2c6958b Optimize log daemon 2018-07-03 22:25:39 +08:00
topjohnwu
70243d7a47 Add fallback to parse block from fstabs 2018-07-03 18:28:44 +08:00
topjohnwu
b5b8c4b725 First find blocks by-name, then fallback to parsing sysfs 2018-07-03 02:57:57 +08:00
topjohnwu
6c4d81b1e9 Invincible mode implemented in magisklogd 2018-07-03 01:38:19 +08:00
topjohnwu
c88dc8795b Single log file 2018-07-02 22:48:26 +08:00
topjohnwu
a8030c39b1 Separate logging into its own daemon 2018-07-02 22:11:28 +08:00
topjohnwu
7243b9e72f Improve log_monitor implementation 2018-07-01 18:18:12 +08:00
topjohnwu
d149af9628 Fix bootloop when upgrading from older Magisk 2018-07-01 14:58:31 +08:00
topjohnwu
c0ac2d540b Update build.py to build Magisk Manager
Sorry I forgot to commit this change :p
2018-06-27 16:26:48 +08:00
topjohnwu
528634d755 Remove unused code 2018-06-27 06:04:16 +08:00
topjohnwu
3283439fd4 New uninstaller 2018-06-27 06:00:01 +08:00
topjohnwu
c8216f9bc5 Fix uninstaller 2018-06-26 22:41:03 +08:00
topjohnwu
e579f314a6 Fix MagiskSU force denying root access 2018-06-26 18:46:18 +08:00
Jat
d1a7372bd2 fix a bug when $ABILONG is arm64-v8a 2018-06-26 05:03:07 +08:00
topjohnwu
e837bdc8ad Update BusyBox
BusyBox is unable to run properly on non-root applications due to seccomp introduced in Android 8.0.
The SDK-21 libc.a has system call wrappers that uses the system calls on the whitelist, so binaries compiled with the updated libc can work properly.
2018-06-25 16:11:31 +08:00
topjohnwu
7265450e2e Precise free space calculation for magisk.img
1. Introduce new applet: imgtool for better separation from the main program
2. Actually mount the image and check statvfs for free space in the image

This shall eliminate any possible module installation failure from image resizing issues.
2018-06-22 06:18:06 +08:00
topjohnwu
058dbc9f9e Add more indentation 2018-06-21 18:11:43 +08:00
topjohnwu
daf9b019c6 More elaborate bb_setup 2018-06-21 16:39:10 +08:00
topjohnwu
14eebd582f Source addon.d script from data 2018-06-21 11:54:21 +08:00
topjohnwu
9a8eeacee8 Fix output of addon.d, and add support for addon.d-v2 2018-06-21 10:53:49 +08:00
topjohnwu
45b0bf5bc5 Remove unnecessary variable 2018-06-21 01:48:46 +08:00
topjohnwu
88db822c43 Get device blocks with sysfs 2018-06-21 01:37:08 +08:00
Sheryl Hohman
fbf3588fdf fix typos 2018-06-19 14:28:09 -07:00
osm0sis
a82ef6bd35 MagiskHide: add ro.boot.vbmeta.device_state=locked 2018-06-19 14:23:06 -07:00
npes87184
312466aaf8 Prevent setting zero over than bound
The &cmd will return a pointer which point to a pointer of cmdline.
It is a memory address which is usually 8 bytes in 64 bits machine.

However, the struct cmdline is 4 bytes. This will cause setting zero
beyond the bound.

Below is a simple example to show the differentiation:

struct cmdline {
        char skip_initramfs;
        char slot[3];
};

static void parse_cmdline(struct cmdline *cmd)
{
        printf("%lu\n", sizeof(*cmd)); /* 4 */
        printf("%lu\n", sizeof(&cmd)); /* 8 */
}

int main()
{
        struct cmdline cmd;
        parse_cmdline(&cmd);
        return 0;
}

This patch prevents this.

Signed-off-by: npes87184 <npes87184@gmail.com>
2018-06-19 14:22:11 -07:00
John Wu
c0ca99f4b4 Make sure APK exists before signing AVB 1.0
Check all possible APK locations before actually running the verification
2018-06-19 14:19:38 -07:00
XiNGRZ
196f15d240 Fix survival script for AVB 1.0 signed boot image 2018-06-19 14:19:38 -07:00
topjohnwu
bfddef2671 Bump Magisk Manager 2018-06-20 04:55:34 +08:00
topjohnwu
44395e8ff0 Fix root loss issue when MagiskHide is enabled
In previous implementations, proc_monitor checks whether the mount namespace of an app is actually separated from zygote using a list generated at startup.
However, for some unknown reason, some devices (e.g. Samsung) has multiple zygote servers running in the background.
This means that app processes spawned from the unlisted zygotes are not checked whether the separation is done or not, causing MagiskHide unmount stuffs in the namespace of zygote, and since zygote is the "mother" of all apps, all apps will no longer have root access.

Since I'm not sure of the reason why multiple zygotes exists, so instead of checking the namespace against a list, compare the current namespace against the parent process's namespace.
This will make sure the namespace is NOT the same as the parent process, which is supposed to be the zygote server.
2018-06-20 04:55:34 +08:00
topjohnwu
835ece5469 Update default flag logic, fix S9/S9+ DTB patches 2018-06-18 01:40:56 +08:00
topjohnwu
d93fc67a75 Improve detecting device status 2018-06-17 17:59:24 +08:00
topjohnwu
838f3cc01e Simplify hiding conditions 2018-06-17 05:16:52 +08:00
topjohnwu
4d5841332a Change to applet name to prevent showing magisk.bin 2018-06-17 01:47:55 +08:00
topjohnwu
9b41976252 Preserve last log 2018-06-17 01:38:58 +08:00
topjohnwu
d08fd0561a Remove invincible mode 2018-06-17 01:28:29 +08:00
topjohnwu
a6958ac139 Fix adbd on GSIs 2018-06-17 01:25:27 +08:00
topjohnwu
d7d76f54cc Install stub APK when needed 2018-06-14 05:09:54 +08:00
topjohnwu
970a2e87b3 Bundle in stub APK into magiskinit 2018-06-14 02:54:38 +08:00
topjohnwu
cabaae8403 MagiskSU improvements 2018-06-14 02:54:33 +08:00
topjohnwu
f2064a84ed Move database logic outside of MagiskSU 2018-06-13 04:34:05 +08:00
topjohnwu
6db27c7758 Allow file constructed JarMap getOutputStream 2018-06-12 02:57:46 +08:00
topjohnwu
3f83919e09 Fix bootloops when flashing Magisk after data wipe on FBE devices 2018-06-11 02:26:18 +08:00
topjohnwu
72a5b83544 Support patching dtb placed in extra section (S9/S9+) 2018-06-10 20:36:18 +08:00
topjohnwu
d2e8ecc646 Fix error return code when patching dtb 2018-06-10 20:36:18 +08:00
topjohnwu
30eb4074cb Update binary dump method 2018-06-10 16:55:00 +08:00
Jason A. Donenfeld
9929e7d8e8 Remove update file on boot
It's not important to check the return value of unlink(2) or even verify
that the file exists. If this code is running, it means the system has
rebooted, and thus the update file, if any, should be removed so that
MagiskManager doesn't keep displaying the same message. We also handle
this before we handle "disable" so that disabled modules don't keep
requesting a reboot to update.
2018-06-10 03:02:56 +08:00
Alexandre Boeglin
f6ee252572 magiskboot: Check dtb header content to avoid false positives 2018-06-10 03:02:13 +08:00
topjohnwu
90d218ebc8 Update SafetyNet extension implementation 2018-06-10 02:35:03 +08:00
topjohnwu
b0a5dbb4c2 Push to SDK 28 2018-06-09 15:47:16 +08:00
topjohnwu
0abdfda5a2 Fix busybox issues 2018-06-09 15:45:56 +08:00
msdx321
a7ceb04cb7 Fix early mount on S9/S9+
Unlike other common OEMs, Samsung use uppper case partition name.

e.g: /dev/block/platform/11120000.ufs/by-name/SYSTEM

This will cause setup_block() fails to find a match partition.
Thus, we should use strcasecmp instead of strcmp.

Signed-off-by: msdx321 <msdx321@gmail.com>
2018-06-03 17:02:30 +08:00
topjohnwu
274efb49e7 Fix modules installation error 2018-06-03 17:01:10 +08:00
Andrew Gunnerson
b3cd83bbca magiskinit: Only unmount /system and /vendor if it was mounted by magisk
This fixes an issue where if /system or /vendor was already mounted
prior to magiskinit running, then they would get unmounted.

Signed-off-by: Andrew Gunnerson <andrewgunnerson@gmail.com>
2018-06-03 15:25:38 +08:00
topjohnwu
b8bd83ba05 Update busybox to 1.28.4 2018-06-03 15:17:31 +08:00
topjohnwu
34dcf49fbc Update restorecon implementation 2018-06-03 14:43:03 +08:00
topjohnwu
ef2f8d485b Add key alias option to config.prop 2018-05-27 14:59:08 +08:00
topjohnwu
9fb9212b0a Add stub apk support 2018-05-27 14:55:24 +08:00
topjohnwu
f31a24b16d Update setenv functions (also fixes uninstalling) 2018-05-26 23:14:09 +08:00
topjohnwu
b436bce565 Minor optimizations 2018-05-26 21:25:59 +08:00
topjohnwu
886286a819 Disable config ondemand when using Gradle > 4.6 2018-05-26 17:35:02 +08:00
topjohnwu
6d93831488 Fix zipsigner when using external keys 2018-05-20 15:24:47 +08:00
topjohnwu
bcdadc6581 Update busybox 2018-05-20 02:34:06 +08:00
topjohnwu
36448191b7 Fix applet invocation 2018-05-20 00:49:48 +08:00
topjohnwu
be5be108c3 Fix build all 2018-05-19 16:53:00 +08:00
topjohnwu
c9ca42aaa9 Support fixing Magisk environment 2018-05-13 18:14:44 +08:00
topjohnwu
c0e2f44092 Use wrapper script to prevent crazy LD_XXX flags 2018-05-13 14:32:21 +08:00
topjohnwu
1412fcbb22 Update sepolicy rules 2018-05-13 14:30:41 +08:00
topjohnwu
9b445d89a1 Add extract feature to update-binary 2018-05-13 14:26:28 +08:00
topjohnwu
c3c78428c4 Use lower API level for static binaries 2018-05-13 05:22:46 +08:00
topjohnwu
c6d2bf577f Massive building system rewrite 2018-05-13 03:04:40 +08:00
topjohnwu
25703c1750 Do not force LD_LIBRARY_PATH in recovery 2018-05-06 01:49:01 +08:00
topjohnwu
3a9a3ed184 Bump Magisk Manager version 2018-04-29 15:20:41 +08:00
topjohnwu
88fae36b8a Hide sub-services of apps for hiding
Close #383
2018-04-29 15:10:35 +08:00
topjohnwu
fc9d4034a9 Fix installation in custom recoveries 2018-04-29 14:04:18 +08:00
topjohnwu
cecc0b932d Remove some traits 2018-04-29 12:34:36 +08:00
topjohnwu
0faed7159c Add invincible mode back 2018-04-29 12:17:28 +08:00
topjohnwu
fb491cfdcf Add Protobuf support to resetprop 2018-04-29 01:20:48 +08:00
topjohnwu
fc706dcb40 Bump busybox to 1.28.3 2018-04-22 14:28:16 +08:00
topjohnwu
a2c1b024f3 Use 32-bit binaries only 2018-04-22 14:13:27 +08:00
Frieder Bluemle
3d865394d7 Update Gradle wrapper to 4.6 2018-04-22 03:09:02 +08:00
topjohnwu
76ef1d0d86 Cleanup sepolicy rules 2018-04-22 03:06:40 +08:00
topjohnwu
9484ec0c17 Massive refactoring
Remove post-fs mode
2018-04-22 02:16:56 +08:00
topjohnwu
614c552e55 Improve daemon startup 2018-04-21 20:16:59 +08:00
topjohnwu
7db3d84ba2 Forgot to update the default file secontext 2018-04-21 13:20:42 +08:00
topjohnwu
87f6018468 Massive sepolicy refactor 2018-04-15 03:18:18 +08:00
topjohnwu
9194c50590 Update build.gradle 2018-04-15 03:17:28 +08:00
topjohnwu
7ff45974c6 Upstream selinux 2018-04-14 17:18:29 +08:00
topjohnwu
2533a4fc4a Fix APK installation on Android P 2018-04-08 03:22:22 +08:00
topjohnwu
42284c5efb Test logcat instead of checking logd 2018-04-08 02:12:40 +08:00
topjohnwu
7d7686da33 Update Magisk Manager 2018-03-28 15:23:55 +08:00
topjohnwu
65e455ef0b Update Android gradle plugin 2018-03-28 02:43:03 +08:00
topjohnwu
ac05e2f2e2 Fix tail size calculation
Close #381
2018-03-27 00:45:18 +08:00
topjohnwu
787f7b3035 Remove backwards compatibility symlinks
These links cause magiskhide unable to work ideally and add complications. I think I gave enough time for migration
2018-03-27 00:35:59 +08:00
topjohnwu
31bd642b80 Update to busybox 1.28.2 2018-03-26 22:12:04 +08:00
topjohnwu
f0bac6b154 Resetprop small refactor 2018-03-26 21:21:48 +08:00
topjohnwu
cc7e74ca11 Cleanup build.gradle 2018-03-26 03:53:06 +08:00
topjohnwu
e8a44646b8 Update Magisk Manager 2018-03-18 12:34:07 +08:00
topjohnwu
ae97d011ae Change MagiskHide state if logd is disabled 2018-03-18 12:17:10 +08:00
imswebra
1b7657a374 tips.md Grammar Fix 2018-03-18 12:16:57 +08:00
topjohnwu
5665e04014 Force using system binaries 2018-03-17 21:42:42 +08:00
topjohnwu
bb70385a42 Update Magisk Manager 2018-03-11 08:37:13 +08:00
topjohnwu
9855877b03 Update rules for Android P 2018-03-11 08:36:20 +08:00
topjohnwu
76c9188fae Android P renamed nonplat_properties 2018-03-11 02:53:57 +08:00
topjohnwu
e4e5269836 Android P have no make_ext4fs, use mke2fs as fallback 2018-03-11 02:52:24 +08:00
topjohnwu
9e737df534 Update high compression mode detection logic 2018-03-10 15:55:55 +08:00
Shaka Huang
0b3192c4d5 Check dtb even if kernel is not available
By the flow of unpacking boot image of Chrome OS there will be no kernel file but an dtb image. In that case the dtb image won’t be added when repacking boot image.

Signed-off-by: Shaka Huang <shakalaca@gmail.com>
2018-03-03 20:57:55 +08:00
Shaka Huang
968e6237bd Fix error parsing MTK boot.img
Should be copy & paste error:

1. boot->r_fmt should be re-checked instead of boot->k_fmt once MTK header was found in ramdisk.

2. ramdisk_size should be restored instead of kernel_size when uncompressed ramdisk was found.

3. Correct header of ramdisk

Signed-off-by: Shaka Huang <shakalaca@gmail.com>
2018-03-03 20:57:37 +08:00
worstperson
d780b5a0e4 Add support for the Nook Tablet, Acclaim
Also changed occurences of NOOK with NOOKHD
2018-03-03 20:55:44 +08:00
worstperson
3e48427eaf Add support for the new NOOK_MAGIC
The new cmdline value that's been in use since Marshmallow
2018-03-03 20:55:44 +08:00
worstperson
31360c34ed Set NOOK_PRE_HEADER_SZ from 0xFFFFF to 0x100000
All applicable Nook HD/HD+ roms are using this offset
2018-03-03 20:55:44 +08:00
71 changed files with 2886 additions and 2125 deletions

6
.gitignore vendored
View File

@@ -2,9 +2,13 @@ out
*.zip *.zip
*.jks *.jks
*.apk *.apk
config.prop
# Manually dumped jars
snet/libs
# Built binaries # Built binaries
ziptools/zipadjust native/out
# Android Studio / Gradle # Android Studio / Gradle
*.iml *.iml

3
.gitmodules vendored
View File

@@ -25,3 +25,6 @@
[submodule "xz"] [submodule "xz"]
path = native/jni/external/xz path = native/jni/external/xz
url = https://github.com/xz-mirror/xz.git url = https://github.com/xz-mirror/xz.git
[submodule "nanopb"]
path = native/jni/external/nanopb
url = https://github.com/nanopb/nanopb.git

View File

@@ -1,24 +1,19 @@
# Magisk # Magisk
## How to build Magisk ## Building Environment Requirements
#### Building has been tested on 3 major platforms: macOS, Ubuntu, Windows 10
### Environment Requirements
1. Python 3.5+: run `build.py` script 1. Python 3.5+: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips 2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
3. Latest Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder 3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or specify custom path `ANDROID_NDK` 4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or optionally specify a custom path `ANDROID_NDK`
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes 5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
### Instructions and Notes ## Building Notes and Instructions
1. Magisk can be built with the latest NDK (r16 as of writing), however binaries released officially will be built with NDK r10e due to ELF incompatibilities with the binaries built from the newer NDKs. 1. Building is tested on macOS, Ubuntu, and Windows 10 using the latest stable NDK and NDK r10e. Officially released binaries were built with NDK r10e.
2. The easiest way to setup the environment is by importing the folder as an Android Studio project. The IDE will download required components and construct the environment for you. You still have to set the `ANDROID_HOME` environment variable to point to the SDK path. 2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
3. 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` 3. 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`
4. Build everything with `build.py`, don't directly call `gradlew` or `ndk-build`, since most requires special setup / dependencies. 4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `--release` flag), you need a Java Keystore file `release-key.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
5. By default, `build.py` will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place a Java Keystore file at `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually). 5. The SafetyNet extension pack requires the full Magisk Manager as a `compileOnly` dependency. Build the **release** APK, convert it back to Java `.class` files (I use [dex2jar](https://github.com/pxb1988/dex2jar)), and place the converted JAR under `snet/libs` before compiling.
## License ## License

2
app

Submodule app updated: 3f38579529...b885ccbd63

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.1.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
@@ -19,9 +19,16 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url "https://jitpack.io" }
} }
} }
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.0"
supportLibVersion = "27.1.1"
}
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

349
build.py
View File

@@ -4,8 +4,8 @@ import os
import subprocess import subprocess
if os.name == 'nt': if os.name == 'nt':
from colorama import init import colorama
init() colorama.init()
def error(str): def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n') print('\n' + '\033[41m' + str + '\033[0m' + '\n')
@@ -16,7 +16,7 @@ def header(str):
# Environment checks # Environment checks
if not sys.version_info >= (3, 5): if not sys.version_info >= (3, 5):
error('Requires Python >= 3.5') error('Requires Python 3.5+')
if 'ANDROID_HOME' not in os.environ: if 'ANDROID_HOME' not in os.environ:
error('Please add Android SDK path to ANDROID_HOME environment variable!') error('Please add Android SDK path to ANDROID_HOME environment variable!')
@@ -24,7 +24,7 @@ if 'ANDROID_HOME' not in os.environ:
try: try:
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except FileNotFoundError: except FileNotFoundError:
error('Please install Java and make sure \'java\' is available in PATH') error('Please install JDK and make sure \'java\' is available in PATH')
import argparse import argparse
import multiprocessing import multiprocessing
@@ -41,13 +41,21 @@ if 'ANDROID_NDK' in os.environ:
else: else:
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
cpu_count = multiprocessing.cpu_count()
archs = ['armeabi-v7a', 'x86']
def mv(source, target): def mv(source, target):
print('mv: {} -> {}'.format(source, target)) try:
shutil.move(source, target) shutil.move(source, target)
except:
pass
def cp(source, target): def cp(source, target):
print('cp: {} -> {}'.format(source, target)) try:
shutil.copyfile(source, target) shutil.copyfile(source, target)
print('cp: {} -> {}'.format(source, target))
except:
pass
def rm(file): def rm(file):
try: try:
@@ -72,143 +80,199 @@ def zip_with_msg(zipfile, source, target):
zipfile.write(source, target) zipfile.write(source, target)
def build_all(args): def build_all(args):
build_binary(args)
build_apk(args) build_apk(args)
build_binary(args)
zip_main(args) zip_main(args)
zip_uninstaller(args) zip_uninstaller(args)
build_snet(args)
def collect_binary():
for arch in archs:
mkdir_p(os.path.join('native', 'out', arch))
for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox', 'b64xz']:
source = os.path.join('native', 'libs', arch, bin)
target = os.path.join('native', 'out', arch, bin)
mv(source, target)
def build_binary(args): def build_binary(args):
header('* Building Magisk binaries') # If nothing specified, build everything
try:
targets = args.target
except:
targets = []
# Force update logging.h timestamp to trigger recompilation if len(targets) == 0:
targets = ['magisk', 'magiskinit', 'magiskboot', 'busybox', 'b64xz']
header('* Building binaries: ' + ' '.join(targets))
# 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', 'logging.h'))
debug_flag = '' if args.release else '-DMAGISK_DEBUG' # Basic flags
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag) base_flags = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={} MAGISK_DEBUG={}'.format(config['version'], config['versionCode'],
'' if args.release else '-DMAGISK_DEBUG')
# Prebuild if 'magisk' in targets:
proc = subprocess.run('{} -C native PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True) # Magisk is special case as it is a dependency of magiskinit
proc = subprocess.run('{} -C native {} B_MAGISK=1 -j{}'.format(ndk_build, base_flags, cpu_count), shell=True)
if proc.returncode != 0:
error('Build Magisk binary failed!')
collect_binary()
old_platform = False
flags = base_flags
if 'b64xz' in targets:
flags += ' B_BXZ=1'
old_platform = True
if old_platform:
proc = subprocess.run('{} -C native OLD_PLAT=1 {} -j{}'.format(ndk_build, flags, cpu_count), shell=True)
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
other = False
flags = base_flags
if 'magiskinit' in targets:
for arch in archs:
bin_file = os.path.join('native', 'out', arch, 'magisk')
if not os.path.exists(bin_file):
error('Build "magisk" before building "magiskinit"')
with open(os.path.join('native', 'out', arch, 'binaries_arch_xz.h'), 'w') as out:
with open(bin_file, 'rb') as src:
xz_dump(src, out, 'magisk_xz')
stub_apk = os.path.join(config['outdir'], 'stub-release.apk')
if not os.path.exists(stub_apk):
error('Build release stub APK before building "magiskinit"')
with open(os.path.join('native', 'out', 'binaries_xz.h'), 'w') as out:
with open(stub_apk, 'rb') as src:
xz_dump(src, out, 'manager_xz')
flags += ' B_INIT=1'
other = True
if 'magiskboot' in targets:
flags += ' B_BOOT=1'
other = True
if 'busybox' in targets:
flags += ' B_BB=1'
other = True
if other:
proc = subprocess.run('{} -C native {} -j{}'.format(ndk_build, flags, cpu_count), shell=True)
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
def sign_apk(source, target):
# Find the latest build tools
build_tool = os.path.join(os.environ['ANDROID_HOME'], 'build-tools',
sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1])
proc = subprocess.run([os.path.join(build_tool, 'zipalign'), '-vpf', '4', source, target], stdout=subprocess.DEVNULL)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk binary failed!') error('Zipalign Magisk Manager failed!')
print('') # Find apksigner.jar
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']: apksigner = ''
mkdir_p(os.path.join('out', arch)) for root, dirs, files in os.walk(build_tool):
with open(os.path.join('out', arch, 'dump.h'), 'w') as dump: if 'apksigner.jar' in files:
dump.write('#include "stdlib.h"\n') apksigner = os.path.join(root, 'apksigner.jar')
mv(os.path.join('native', 'libs', arch, 'magisk'), os.path.join('out', arch, 'magisk')) break
with open(os.path.join('out', arch, 'magisk'), 'rb') as bin: if not apksigner:
dump.write('const uint8_t magisk_dump[] = "') error('Cannot find apksigner.jar in Android SDK build tools')
dump.write(''.join("\\x{:02X}".format(c) for c in lzma.compress(bin.read(), preset=9)))
dump.write('";\n')
print('') proc = subprocess.run('java -jar {} sign --ks release-key.jks --ks-pass pass:{} --ks-key-alias {} --key-pass pass:{} {}'.format(
apksigner, config['keyStorePass'], config['keyAlias'], config['keyPass'], target), shell=True)
proc = subprocess.run('{} -C native {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk binary failed!') error('Release sign Magisk Manager failed!')
print('')
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
for binary in ['magiskinit', 'magiskboot', 'b64xz', 'busybox']:
try:
mv(os.path.join('native', 'libs', arch, binary), os.path.join('out', arch, binary))
except:
pass
def build_apk(args): def build_apk(args):
header('* Building Magisk Manager') header('* Building Magisk Manager')
mkdir(os.path.join('app', 'src', 'main', 'assets')) source = os.path.join('scripts', 'util_functions.sh')
for script in ['magisk_uninstaller.sh', 'util_functions.sh']: target = os.path.join('app', 'src', 'full', 'res', 'raw', 'util_functions.sh')
source = os.path.join('scripts', script) cp(source, target)
target = os.path.join('app', 'src', 'main', 'assets', script)
cp(source, target)
if args.release: if args.release:
if not os.path.exists('release_signature.jks'): if not os.path.exists('release-key.jks'):
error('Please generate a java keystore and place it in \'release_signature.jks\'') error('Please generate a java keystore and place it in \'release-key.jks\'')
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True) proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk Manager failed!') error('Build Magisk Manager failed!')
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-unsigned.apk') unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'release', 'app-full-release-unsigned.apk')
aligned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-aligned.apk') release = os.path.join(config['outdir'], 'app-release.apk')
release = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release.apk') sign_apk(unsigned, release)
header('Output: ' + release)
# Find the latest build tools
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
rm(aligned)
rm(release)
proc = subprocess.run([
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'),
'-v', '-p', '4', unsigned, aligned], stdout=subprocess.DEVNULL)
if proc.returncode != 0:
error('Zipalign Magisk Manager failed!')
# Find apksigner.jar
apksigner = ''
for root, dirs, files in os.walk(os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool)):
if 'apksigner.jar' in files:
apksigner = os.path.join(root, 'apksigner.jar')
break
if not apksigner:
error('Cannot find apksigner.jar in Android SDK build tools')
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
apksigner, 'release_signature.jks', release, aligned), shell=True)
if proc.returncode != 0:
error('Release sign Magisk Manager failed!')
rm(unsigned) rm(unsigned)
rm(aligned)
mkdir('out') unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'stub', 'release', 'app-stub-release-unsigned.apk')
target = os.path.join('out', 'app-release.apk') release = os.path.join(config['outdir'], 'stub-release.apk')
print('') sign_apk(unsigned, release)
mv(release, target) header('Output: ' + release)
rm(unsigned)
else: else:
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True) proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk Manager failed!') error('Build Magisk Manager failed!')
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk') source = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'debug', 'app-full-debug.apk')
mkdir('out') target = os.path.join(config['outdir'], 'app-debug.apk')
target = os.path.join('out', 'app-debug.apk')
print('')
mv(source, target) mv(source, target)
header('Output: ' + target)
source = os.path.join('app', 'build', 'outputs', 'apk', 'stub', 'debug', 'app-stub-debug.apk')
target = os.path.join(config['outdir'], 'stub-debug.apk')
mv(source, target)
header('Output: ' + target)
def build_snet(args): def build_snet(args):
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True) proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build snet extention failed!') error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk') source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
mkdir('out') target = os.path.join(config['outdir'], 'snet.apk')
target = os.path.join('out', 'snet.apk') # Re-compress the whole APK for smaller size
print('') with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
mv(source, target) with zipfile.ZipFile(source) as zin:
for item in zin.infolist():
zout.writestr(item.filename, zin.read(item))
rm(source)
header('Output: ' + target)
def xz_dump(src, out, var_name):
out.write('const static unsigned char {}[] = {{'.format(var_name))
for i, c in enumerate(lzma.compress(src.read(), preset=9)):
if i % 16 == 0:
out.write('\n')
out.write('0x{:02X},'.format(c))
out.write('\n};\n')
out.flush()
def gen_update_binary(): def gen_update_binary():
update_bin = [] update_bin = []
binary = os.path.join('out', 'armeabi-v7a', 'b64xz') binary = os.path.join('native', 'out', 'armeabi-v7a', 'b64xz')
if not os.path.exists(binary): if not os.path.exists(binary):
error('Please build \'binary\' before zipping!') error('Please build \'binary\' before zipping!')
with open(binary, 'rb') as b64xz: with open(binary, 'rb') as b64xz:
update_bin.append('#! /sbin/sh\nEX_ARM=\'') update_bin.append('#! /sbin/sh\nEX_ARM=\'')
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read())) update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('out', 'x86', 'b64xz') binary = os.path.join('native', 'out', 'x86', 'b64xz')
with open(binary, 'rb') as b64xz: with open(binary, 'rb') as b64xz:
update_bin.append('\'\nEX_X86=\'') update_bin.append('\'\nEX_X86=\'')
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read())) update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('out', 'armeabi-v7a', 'busybox') binary = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
with open(binary, 'rb') as busybox: with open(binary, 'rb') as busybox:
update_bin.append('\'\nBB_ARM=') update_bin.append('\'\nBB_ARM=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii')) update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
binary = os.path.join('out', 'x86', 'busybox') binary = os.path.join('native', 'out', 'x86', 'busybox')
with open(binary, 'rb') as busybox: with open(binary, 'rb') as busybox:
update_bin.append('\nBB_X86=') update_bin.append('\nBB_X86=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii')) update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
@@ -234,14 +298,14 @@ def zip_main(args):
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# Binaries # Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]: for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for binary in ['magiskinit', 'magiskboot']: for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('out', lib_dir, binary) source = os.path.join('native', 'out', lib_dir, binary)
target = os.path.join(zip_dir, binary) target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# APK # APK
source = os.path.join('out', 'app-release.apk' if args.release else 'app-debug.apk') source = os.path.join(config['outdir'], 'app-release.apk' if args.release else 'app-debug.apk')
target = os.path.join('common', 'magisk.apk') target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
@@ -254,14 +318,14 @@ def zip_main(args):
source = os.path.join('scripts', 'util_functions.sh') source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script: with open(source, 'r') as script:
# Add version info util_functions.sh # Add version info util_functions.sh
util_func = script.read().replace( util_func = script.read().replace('#MAGISK_VERSION_STUB',
'#MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode)) 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(config['version'], config['versionCode']))
target = os.path.join('common', 'util_functions.sh') target = os.path.join('common', 'util_functions.sh')
print('zip: ' + source + ' -> ' + target) print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func) zipf.writestr(target, util_func)
# addon.d.sh # addon.d.sh
source = os.path.join('scripts', 'addon.d.sh') source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('addon.d', '99-magisk.sh') target = os.path.join('common', 'addon.d.sh')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# Prebuilts # Prebuilts
@@ -271,8 +335,10 @@ def zip_main(args):
# End of zipping # End of zipping
output = os.path.join('out', 'Magisk-v{}.zip'.format(args.versionString)) output = os.path.join(config['outdir'], 'Magisk-v{}.zip'.format(config['version']) if config['prettyName'] else
'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_adjust_zip(unsigned, output) sign_adjust_zip(unsigned, output)
header('Output: ' + output)
def zip_uninstaller(args): def zip_uninstaller(args):
header('* Packing Uninstaller Zip') header('* Packing Uninstaller Zip')
@@ -286,19 +352,16 @@ def zip_uninstaller(args):
print('zip: ' + target) print('zip: ' + target)
zipf.writestr(target, gen_update_binary()) zipf.writestr(target, gen_update_binary())
# updater-script # updater-script
source = os.path.join('scripts', 'uninstaller_loader.sh') source = os.path.join('scripts', 'magisk_uninstaller.sh')
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script') target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# Binaries # Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]: for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
source = os.path.join('out', lib_dir, 'magiskboot') for bin in ['magisk', 'magiskboot']:
target = os.path.join(zip_dir, 'magiskboot') source = os.path.join('native', 'out', lib_dir, bin)
zip_with_msg(zipf, source, target) target = os.path.join(zip_dir, bin)
zip_with_msg(zipf, source, target)
source = os.path.join('scripts', 'magisk_uninstaller.sh')
target = 'magisk_uninstaller.sh'
zip_with_msg(zipf, source, target)
# Scripts # Scripts
# util_functions.sh # util_functions.sh
@@ -316,11 +379,13 @@ def zip_uninstaller(args):
# End of zipping # End of zipping
output = os.path.join('out', 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))) output = os.path.join(config['outdir'], 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))
if config['prettyName'] else 'magisk-uninstaller.zip')
sign_adjust_zip(unsigned, output) sign_adjust_zip(unsigned, output)
header('Output: ' + output)
def sign_adjust_zip(unsigned, output): def sign_adjust_zip(unsigned, output):
signer_name = 'zipsigner-2.1.jar' signer_name = 'zipsigner-2.2.jar'
jarsigner = os.path.join('utils', 'build', 'libs', signer_name) jarsigner = os.path.join('utils', 'build', 'libs', signer_name)
if not os.path.exists(jarsigner): if not os.path.exists(jarsigner):
@@ -339,40 +404,56 @@ def sign_adjust_zip(unsigned, output):
def cleanup(args): def cleanup(args):
if len(args.target) == 0: if len(args.target) == 0:
args.target = ['binary', 'java', 'zip'] args.target = ['native', 'java']
if 'binary' in args.target: if 'native' in args.target:
header('* Cleaning binaries') header('* Cleaning native')
subprocess.run(ndk_build + ' -C native PRECOMPILE=true clean', shell=True) subprocess.run(ndk_build + ' -C native B_MAGISK=1 B_INIT=1 B_BOOT=1 B_BXZ=1 B_BB=1 clean', shell=True)
subprocess.run(ndk_build + ' -C native clean', shell=True) shutil.rmtree(os.path.join('native', 'out'), ignore_errors=True)
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
if 'java' in args.target: if 'java' in args.target:
header('* Cleaning java') header('* Cleaning java')
subprocess.run('{} app:clean snet:clean utils:clean'.format(os.path.join('.', 'gradlew')), shell=True) subprocess.run('{} app:clean snet:clean utils:clean'.format(os.path.join('.', 'gradlew')), shell=True)
for f in os.listdir('out'):
if '.apk' in f:
rm(os.path.join('out', f))
if 'zip' in args.target: def parse_config():
header('* Cleaning zip files') c = {}
for f in os.listdir('out'): with open('config.prop', 'r') as f:
if '.zip' in f: for line in [l.strip(' \t\r\n') for l in f]:
rm(os.path.join('out', f)) if line.startswith('#') or len(line) == 0:
continue
prop = line.split('=')
c[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
if 'version' not in c or 'versionCode' not in c:
error('"version" and "versionCode" is required in "config.prop"')
try:
c['versionCode'] = int(c['versionCode'])
except ValueError:
error('"versionCode" is required to be an integer')
if 'prettyName' not in c:
c['prettyName'] = 'false'
c['prettyName'] = c['prettyName'].lower() == 'true'
if 'outdir' not in c:
c['outdir'] = 'out'
mkdir_p(c['outdir'])
return c
config = parse_config()
parser = argparse.ArgumentParser(description='Magisk build script') parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('--release', action='store_true', help='compile Magisk for release') parser.add_argument('--release', action='store_true', help='compile Magisk for release')
subparsers = parser.add_subparsers(title='actions') subparsers = parser.add_subparsers(title='actions')
all_parser = subparsers.add_parser('all', help='build everything and create flashable zip with uninstaller') all_parser = subparsers.add_parser('all', help='build everything (binaries/apks/zips)')
all_parser.add_argument('versionString')
all_parser.add_argument('versionCode', type=int)
all_parser.set_defaults(func=build_all) all_parser.set_defaults(func=build_all)
binary_parser = subparsers.add_parser('binary', help='build Magisk binaries') binary_parser = subparsers.add_parser('binary', help='build binaries. target: magisk magiskinit magiskboot busybox b64xz')
binary_parser.add_argument('versionString') binary_parser.add_argument('target', nargs='*')
binary_parser.add_argument('versionCode', type=int)
binary_parser.set_defaults(func=build_binary) binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK') apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
@@ -382,14 +463,12 @@ snet_parser = subparsers.add_parser('snet', help='build snet extention for Magis
snet_parser.set_defaults(func=build_snet) snet_parser.set_defaults(func=build_snet)
zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip') zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
zip_parser.add_argument('versionString')
zip_parser.add_argument('versionCode', type=int)
zip_parser.set_defaults(func=zip_main) zip_parser.set_defaults(func=zip_main)
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller') uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
uninstaller_parser.set_defaults(func=zip_uninstaller) uninstaller_parser.set_defaults(func=zip_uninstaller)
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip') clean_parser = subparsers.add_parser('clean', help='cleanup. target: binary java zip')
clean_parser.add_argument('target', nargs='*') clean_parser.add_argument('target', nargs='*')
clean_parser.set_defaults(func=cleanup) clean_parser.set_defaults(func=cleanup)

15
config.prop.sample Normal file
View File

@@ -0,0 +1,15 @@
# The version string and version code of Magisk
version=
versionCode=
outdir=out
# Whether use pretty names for zips, e.g. Magisk-v${version}.zip, Magisk-uninstaller-${date}.zip
# The default output names are magisk-${release/debug/uninstaller}.zip
prettyName=false
# These pwds are passed to apksigner for release-key.jks. Necessary when building release apks
# keyPass is the pwd for the specified keyAlias
keyStorePass=
keyAlias=
keyPass=

View File

@@ -1,11 +1,11 @@
# Tips and Tricks # Tips and Tricks
## OTA Installation Tips ## OTA Installation Tips
Magisk do 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. 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 v14.1+** **This tutorial is only for Magisk v14.1+**
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in anyway, even remounting the partition to rw will tamper block verification!!** **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 #### Prerequisites
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement. 1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
@@ -16,10 +16,10 @@ Magisk do modifications systemless-ly, which means applying official OTAs is muc
#### Devices with A/B Partitions #### Devices with A/B Partitions
(Includes Pixel family) (Includes Pixel family)
Due to the fact that these devices have two separate partitions and the OTA installation happens live when the system is still running, these devices have the best support: the out-of-the-box OTA installation works seamlessly and Magisk will be preserved after the installation. Due to the fact that these devices have two separate partitions, and the OTA installation happens live while the system is still running, these devices have the best support: the out-of-the-box OTA installation works seamlessly and Magisk will be preserved after the installation.
1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates) 1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates).
1. Once the installation passed step 1 and starting step 2, go to Magisk Manager → Install → Install to Second Slot. This will install Magisk into the second boot image slot, which is the updated slot. 1. Once the installation has passed step 1 and is starting step 2, go to Magisk Manager → Install → Install to Second Slot. This will install Magisk into the second boot image slot, which is the updated slot.
<img src="images/ota_step2.png" width="250"> <img src="images/install_second_slot.png" width="250"> <img src="images/ota_step2.png" width="250"> <img src="images/install_second_slot.png" width="250">
1. Let the OTA finish its job. After a reboot, the bootloader will switch to the updated system. Magisk should still be installed since we already patched the new boot image. 1. Let the OTA finish its job. After a reboot, the bootloader will switch to the updated system. Magisk should still be installed since we already patched the new boot image.
@@ -27,7 +27,7 @@ Due to the fact that these devices have two separate partitions and the OTA inst
(Includes Pixel family, Nexus family, Samsung devices) (Includes Pixel family, Nexus family, Samsung devices)
(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) (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, whether it supports your device/system combination depends on the application itself, and support may also change in the future. If you face any issues, please directly [report to Chainfire](https://forum.xda-developers.com/general/paid-software/flashfire-t3075433). 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, whether it supports your device/system combination depends on the application itself, and support may change in the future. If you face any issues, please directly [report to Chainfire](https://forum.xda-developers.com/general/paid-software/flashfire-t3075433).
1. After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.** 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. Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
@@ -39,10 +39,10 @@ The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash
Unfortunately, there are no real good ways to apply OTAs on all devices. Also, the tutorial provided below will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. Here I share my personal experience with HTC U11. Unfortunately, there are no real good ways to apply OTAs on all devices. Also, the tutorial provided below will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. Here I share my personal experience with HTC U11.
1. To properly install OTAs, you should 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. 1. To properly install OTAs, you should 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 remain stock untouched: 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 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 - 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 have stock recovery and stock boot image restored, download the OTA. Optionally, once you downloaded the OTA update zip, you can find a way to copy the zip out since you are still rooted. Personally I will 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. 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. 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. 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.
@@ -50,4 +50,4 @@ If you decide to start by installing Magisk without touching your recovery parti
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. 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 ## Remove Folders
Same as mentioned above, actually making the folder to **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 correspond 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`. 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

@@ -20,3 +20,6 @@ org.gradle.parallel=true
# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property. # When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property.
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon. # The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
org.gradle.daemon=true org.gradle.daemon=true
# Configuration on demand is not supported by the current version of the Android Gradle plugin since you are using Gradle version 4.6 or above
org.gradle.configureondemand=false

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Mon Dec 04 11:24:34 CST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

100
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env sh
############################################################################## ##############################################################################
## ##
@@ -6,42 +6,6 @@
## ##
############################################################################## ##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" PRG="$0"
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`" APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -85,7 +89,7 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n` MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +154,19 @@ if $cygwin ; then
esac esac
fi fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules # Escape application args
function splitJvmOpts() { save () {
JVM_OPTS=("$@") for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
} }
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS APP_ARGS=$(save "$@")
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

14
gradlew.bat vendored
View File

@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail goto fail
:init :init
@rem Get command-line arguments, handling Windowz variants @rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args :win9xME_args
@rem Slurp the command line arguments. @rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%* set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute :execute
@rem Setup the command line @rem Setup the command line

View File

@@ -1,7 +1,8 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
android { android {
compileSdkVersion 27 compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
externalNativeBuild { externalNativeBuild {
ndkBuild { ndkBuild {
@@ -12,8 +13,9 @@ android {
defaultConfig { defaultConfig {
externalNativeBuild { externalNativeBuild {
ndkBuild { ndkBuild {
// Passes an optional argument to ndk-build. // Pass arguments to ndk-build.
arguments "GRADLE=true" arguments('B_MAGISK=1', 'B_INIT=1', 'B_BOOT=1', 'MAGISK_VERSION=debug',
'MAGISK_VER_CODE=99999', 'MAGISK_DEBUG=-DMAGISK_DEBUG')
} }
} }
} }

View File

@@ -9,6 +9,7 @@ LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
LIBLZ4 := $(EXT_PATH)/lz4/lib LIBLZ4 := $(EXT_PATH)/lz4/lib
LIBBZ2 := $(EXT_PATH)/bzip2 LIBBZ2 := $(EXT_PATH)/bzip2
LIBFDT := $(EXT_PATH)/dtc/libfdt LIBFDT := $(EXT_PATH)/dtc/libfdt
LIBNANOPB := $(EXT_PATH)/nanopb
UTIL_SRC := utils/cpio.c \ UTIL_SRC := utils/cpio.c \
utils/file.c \ utils/file.c \
utils/img.c \ utils/img.c \
@@ -22,32 +23,35 @@ UTIL_SRC := utils/cpio.c \
# Binaries # Binaries
######################## ########################
ifneq "$(or $(PRECOMPILE), $(GRADLE))" "" ifdef B_MAGISK
# magisk main binary # magisk main binary
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := magisk LOCAL_MODULE := magisk
LOCAL_SHARED_LIBRARIES := libsqlite libselinux LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_STATIC_LIBRARIES := libnanopb
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
jni/include \ jni/include \
jni/magiskpolicy \
$(EXT_PATH)/include \ $(EXT_PATH)/include \
$(LIBSELINUX) $(LIBSELINUX) \
$(LIBNANOPB)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/magisk.c \ core/magisk.c \
core/daemon.c \ core/daemon.c \
core/log_monitor.c \ core/log_daemon.c \
core/bootstages.c \ core/bootstages.c \
core/socket.c \ core/socket.c \
core/db.c \
magiskhide/magiskhide.c \ magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \ magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \ magiskhide/hide_utils.c \
resetprop/resetprop.cpp \ resetprop/persist_props.c \
resetprop/resetprop.c \
resetprop/system_properties.cpp \ resetprop/system_properties.cpp \
su/su.c \ su/su.c \
su/activity.c \ su/activity.c \
su/db.c \
su/pts.c \ su/pts.c \
su/su_daemon.c \ su/su_daemon.c \
su/su_socket.c \ su/su_socket.c \
@@ -59,7 +63,7 @@ include $(BUILD_EXECUTABLE)
endif endif
ifndef PRECOMPILE ifdef B_INIT
# magiskinit # magiskinit
include $(CLEAR_VARS) include $(CLEAR_VARS)
@@ -68,13 +72,13 @@ LOCAL_STATIC_LIBRARIES := libsepol liblzma
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
jni/include \ jni/include \
jni/magiskpolicy \ jni/magiskpolicy \
../out/$(TARGET_ARCH_ABI) \ out \
out/$(TARGET_ARCH_ABI) \
$(LIBSEPOL) \ $(LIBSEPOL) \
$(LIBLZMA) $(LIBLZMA)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/magiskinit.c \ core/magiskinit.c \
core/socket.c \
magiskpolicy/api.c \ magiskpolicy/api.c \
magiskpolicy/magiskpolicy.c \ magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \ magiskpolicy/rules.c \
@@ -84,6 +88,10 @@ LOCAL_SRC_FILES := \
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
endif
ifdef B_BOOT
# magiskboot # magiskboot
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot LOCAL_MODULE := magiskboot
@@ -110,10 +118,10 @@ LOCAL_CFLAGS := -DXWRAP_EXIT
LOCAL_LDLIBS := -lz LOCAL_LDLIBS := -lz
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
# 32-bit static binaries endif
ifndef GRADLE # Do not run gradle sync on these binaries
ifneq ($(TARGET_ARCH_ABI), x86_64) ifdef B_BXZ
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
# b64xz # b64xz
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := b64xz LOCAL_MODULE := b64xz
@@ -122,13 +130,14 @@ LOCAL_C_INCLUDES := $(LIBLZMA)
LOCAL_SRC_FILES := b64xz.c LOCAL_SRC_FILES := b64xz.c
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
# Busybox
include jni/external/busybox/Android.mk
endif
endif
endif endif
# Precompile ifdef B_BB
# Busybox
include jni/external/busybox/Android.mk
endif endif
######################## ########################

View File

@@ -1,5 +1,10 @@
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a APP_ABI := x86 armeabi-v7a
APP_PLATFORM := android-21 APP_CFLAGS := -std=gnu99 ${MAGISK_DEBUG} \
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99 -DMAGISK_VERSION="${MAGISK_VERSION}" -DMAGISK_VER_CODE=${MAGISK_VER_CODE}
APP_CPPFLAGS := -std=c++11 APP_CPPFLAGS := -std=c++11
APP_SHORT_COMMANDS := true APP_SHORT_COMMANDS := true
ifdef OLD_PLAT
APP_PLATFORM := android-9
else
APP_PLATFORM := android-21
endif

View File

@@ -16,9 +16,11 @@
#include <selinux/selinux.h> #include <selinux/selinux.h>
#include "magisk.h" #include "magisk.h"
#include "db.h"
#include "utils.h" #include "utils.h"
#include "daemon.h" #include "daemon.h"
#include "resetprop.h" #include "resetprop.h"
#include "magiskpolicy.h"
static char *buf, *buf2; static char *buf, *buf2;
static struct vector module_list; static struct vector module_list;
@@ -112,11 +114,11 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
* setenvs * * setenvs *
***********/ ***********/
static void bb_setenv(struct vector *v) { static void set_path(struct vector *v) {
for (int i = 0; environ[i]; ++i) { for (int i = 0; environ[i]; ++i) {
if (strncmp(environ[i], "PATH=", 5) == 0) { if (strncmp(environ[i], "PATH=", 5) == 0) {
snprintf(buf, PATH_MAX, "PATH=%s:%s", BBPATH, strchr(environ[i], '=') + 1); vec_push_back(v, strdup("PATH=" BBPATH ":/sbin:" MIRRDIR "/system/bin:"
vec_push_back(v, strdup(buf)); MIRRDIR "/system/xbin:" MIRRDIR "/vendor/bin"));
} else { } else {
vec_push_back(v, strdup(environ[i])); vec_push_back(v, strdup(environ[i]));
} }
@@ -124,15 +126,6 @@ static void bb_setenv(struct vector *v) {
vec_push_back(v, NULL); vec_push_back(v, NULL);
} }
static void pm_setenv(struct vector *v) {
for (int i = 0; environ[i]; ++i) {
if (strncmp(environ[i], "CLASSPATH=", 10) != 0)
vec_push_back(v, strdup(environ[i]));
}
vec_push_back(v, strdup("CLASSPATH=/system/framework/pm.jar"));
vec_push_back(v, NULL);
}
/*********** /***********
* Scripts * * Scripts *
***********/ ***********/
@@ -151,7 +144,7 @@ static void exec_common_script(const char* stage) {
if (access(buf2, X_OK) == -1) if (access(buf2, X_OK) == -1)
continue; continue;
LOGI("%s.d: exec [%s]\n", stage, entry->d_name); LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL); int pid = exec_command(0, NULL, set_path, "sh", buf2, NULL);
if (pid != -1) if (pid != -1)
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
} }
@@ -168,7 +161,7 @@ static void exec_module_script(const char* stage) {
if (access(buf2, F_OK) == -1 || access(buf, F_OK) == 0) if (access(buf2, F_OK) == -1 || access(buf, F_OK) == 0)
continue; continue;
LOGI("%s: exec [%s.sh]\n", module, stage); LOGI("%s: exec [%s.sh]\n", module, stage);
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL); int pid = exec_command(0, NULL, set_path, "sh", buf2, NULL);
if (pid != -1) if (pid != -1)
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
} }
@@ -314,11 +307,11 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_LNK(child)) { if (IS_LNK(child)) {
// Copy symlinks directly // Copy symlinks directly
cp_afc(buf2, buf); cp_afc(buf2, buf);
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
LOGI("creat_link: %s <- %s\n",buf, buf2); LOGI("creat_link: %s <- %s\n",buf, buf2);
#else #else
LOGI("creat_link: %s\n", buf); LOGI("creat_link: %s\n", buf);
#endif #endif
} else { } else {
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name); snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
bind_mount(buf2, buf); bind_mount(buf2, buf);
@@ -359,7 +352,7 @@ static void simple_mount(const char *path) {
DIR *dir; DIR *dir;
struct dirent *entry; struct dirent *entry;
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, path); snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, path);
if (!(dir = opendir(buf))) if (!(dir = opendir(buf)))
return; return;
@@ -377,7 +370,7 @@ static void simple_mount(const char *path) {
free(new_path); free(new_path);
} else if (entry->d_type == DT_REG) { } else if (entry->d_type == DT_REG) {
// Actual file path // Actual file path
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, buf2); snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, buf2);
// Clone all attributes // Clone all attributes
clone_attr(buf2, buf); clone_attr(buf2, buf);
// Finally, mount the file // Finally, mount the file
@@ -435,33 +428,44 @@ static int prepare_img() {
rm_rf(buf); rm_rf(buf);
continue; continue;
} }
snprintf(buf, PATH_MAX, "%s/%s/update", MOUNTPOINT, entry->d_name);
unlink(buf);
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name); snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
if (access(buf, F_OK) == 0) if (access(buf, F_OK) == 0)
continue; continue;
vec_push_back(&module_list, strdup(entry->d_name)); vec_push_back(&module_list, strdup(entry->d_name));
} }
} }
closedir(dir); closedir(dir);
// Trim image if (trim_img(MAINIMG, MOUNTPOINT, magiskloop))
umount_image(MOUNTPOINT, magiskloop); return 1;
free(magiskloop); free(magiskloop);
trim_img(MAINIMG);
// Remount them back :)
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
free(magiskloop);
// Fix file selinux contexts
fix_filecon();
return 0; return 0;
} }
void fix_filecon() { void install_apk(const char *apk) {
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC); setfilecon(apk, "u:object_r:"SEPOL_FILE_DOMAIN":s0");
restorecon(dirfd, 0); while (1) {
close(dirfd); sleep(5);
LOGD("apk_install: attempting to install APK");
int apk_res = -1, pid;
pid = exec_command(1, &apk_res, NULL, "/system/bin/pm", "install", "-r", apk, NULL);
if (pid != -1) {
int err = 0;
while (fdgets(buf, PATH_MAX, apk_res) > 0) {
LOGD("apk_install: %s", buf);
err |= strstr(buf, "Error:") != NULL;
}
waitpid(pid, NULL, 0);
close(apk_res);
// Keep trying until pm is started
if (err)
continue;
break;
}
}
unlink(apk);
} }
/**************** /****************
@@ -473,61 +477,222 @@ static void unblock_boot_process() {
pthread_exit(NULL); pthread_exit(NULL);
} }
void post_fs(int client) { static const char wrapper[] =
LOGI("** post-fs mode running\n"); "#!/system/bin/sh\n"
// ack "unset LD_LIBRARY_PATH\n"
write_int(client, 0); "unset LD_PRELOAD\n"
close(client); "exec /sbin/magisk.bin \"${0##*/}\" \"$@\"\n";
// Uninstall or core only mode void startup() {
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0) if (!check_data())
goto unblock; unblock_boot_process();
// Allocate buffer if (access(SECURE_DIR, F_OK) != 0) {
buf = xmalloc(PATH_MAX); /* If the folder is not automatically created by the system,
buf2 = xmalloc(PATH_MAX); * do NOT proceed further. Manual creation of the folder
* will cause bootloops on FBE devices. */
LOGE(SECURE_DIR" is not present, abort...");
unblock_boot_process();
}
simple_mount("/system"); // No uninstaller or core-only mode
simple_mount("/vendor"); if (access(DISABLEFILE, F_OK) != 0) {
// Allocate buffer
buf = xmalloc(PATH_MAX);
buf2 = xmalloc(PATH_MAX);
unblock: simple_mount("/system");
unblock_boot_process(); simple_mount("/vendor");
}
LOGI("** Initializing Magisk\n");
// Unlock all blocks for rw
unlock_blocks();
LOGI("* Creating /sbin overlay");
DIR *dir;
struct dirent *entry;
int root, sbin, fd;
char buf[PATH_MAX];
void *magisk, *init;
size_t magisk_size, init_size;
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
// Remove some traits of Magisk
unlink("/init.magisk.rc");
// GSIs will have to override /sbin/adbd with /system/bin/adbd
if (access("/sbin/adbd", F_OK) == 0 && access("/system/bin/adbd", F_OK) == 0) {
umount2("/sbin/adbd", MNT_DETACH);
cp_afc("/system/bin/adbd", "/sbin/adbd");
}
// Create hardlink mirror of /sbin to /root
mkdir("/root", 0750);
clone_attr("/sbin", "/root");
full_read("/sbin/magisk", &magisk, &magisk_size);
unlink("/sbin/magisk");
full_read("/sbin/magiskinit", &init, &init_size);
unlink("/sbin/magiskinit");
root = xopen("/root", O_RDONLY | O_CLOEXEC);
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
link_dir(sbin, root);
close(sbin);
// Mount the /sbin tmpfs overlay
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
chmod("/sbin", 0755);
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
fd = creat("/sbin/magisk.bin", 0755);
xwrite(fd, magisk, magisk_size);
close(fd);
free(magisk);
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);
}
// Setup magiskinit symlinks
fd = creat("/sbin/magiskinit", 0755);
xwrite(fd, init, init_size);
close(fd);
free(init);
setfilecon("/sbin/magiskinit", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
for (int i = 0; init_applet[i]; ++i) {
snprintf(buf, PATH_MAX, "/sbin/%s", init_applet[i]);
xsymlink("/sbin/magiskinit", buf);
}
// Create symlinks pointing back to /root
dir = xfdopendir(root);
while((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
symlinkat(buf, sbin, entry->d_name);
}
close(sbin);
close(root);
// Alternative binaries paths
char *alt_bin[] = { "/cache/data_bin", "/data/.magisk",
"/data/data/com.topjohnwu.magisk/install",
"/data/user_de/0/com.topjohnwu.magisk/install", NULL };
char *bin_path = NULL;
for (int i = 0; alt_bin[i]; ++i) {
if (access(alt_bin[i], F_OK) == 0) {
bin_path = alt_bin[i];
break;
}
}
if (bin_path) {
rm_rf(DATABIN);
cp_afc(bin_path, DATABIN);
rm_rf(bin_path);
}
// Remove legacy stuffs
rm_rf("/data/magisk");
unlink("/data/magisk.img");
unlink("/data/magisk_debug.log");
// Create directories in tmpfs overlay
xmkdirs(MIRRDIR "/system", 0755);
xmkdir(MIRRDIR "/bin", 0755);
xmkdir(BBPATH, 0755);
xmkdir(MOUNTPOINT, 0755);
xmkdir(BLOCKDIR, 0755);
LOGI("* Mounting mirrors");
struct vector mounts;
vec_init(&mounts);
file_to_vector("/proc/mounts", &mounts);
char *line;
int skip_initramfs = 0;
// Check whether skip_initramfs device
vec_for_each(&mounts, line) {
if (strstr(line, " /system_root ")) {
bind_mount("/system_root/system", MIRRDIR "/system");
skip_initramfs = 1;
} else if (!skip_initramfs && strstr(line, " /system ")) {
sscanf(line, "%s", buf);
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s <- %s\n", MIRRDIR "/system", buf);
#else
LOGI("mount: %s\n", MIRRDIR "/system");
#endif
} else if (strstr(line, " /vendor ")) {
seperate_vendor = 1;
sscanf(line, "%s", buf);
xmkdir(MIRRDIR "/vendor", 0755);
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
#else
LOGI("mount: %s\n", MIRRDIR "/vendor");
#endif
}
free(line);
}
vec_destroy(&mounts);
if (!seperate_vendor) {
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
#ifdef MAGISK_DEBUG
LOGI("link: %s <- %s\n", MIRRDIR "/vendor", MIRRDIR "/system/vendor");
#else
LOGI("link: %s\n", MIRRDIR "/vendor");
#endif
}
xmkdirs(DATABIN, 0755);
bind_mount(DATABIN, MIRRDIR "/bin");
if (access(MIRRDIR "/bin/busybox", X_OK) == 0) {
LOGI("* Setting up internal busybox");
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
// Start post-fs-data mode
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", NULL);
} }
void post_fs_data(int client) { void post_fs_data(int client) {
// ack // ack
write_int(client, 0); write_int(client, 0);
close(client); close(client);
if (!is_daemon_init && !check_data())
goto unblock;
// Start the debug log // If post-fs-data mode is started, it means startup succeeded
start_debug_full_log(); setup_done = 1;
LOGI("** post-fs-data mode running\n"); LOGI("** post-fs-data mode running\n");
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
full_patch_pid = exec_command(0, NULL, NULL, "/sbin/magiskpolicy", "--live", "allow "SEPOL_PROC_DOMAIN" * * *", NULL);
// Allocate buffer // Allocate buffer
if (buf == NULL) buf = xmalloc(PATH_MAX); buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX); buf2 = xmalloc(PATH_MAX);
vec_init(&module_list); vec_init(&module_list);
// Initialize
if (!is_daemon_init)
daemon_init();
// uninstaller
if (access(UNINSTALLER, F_OK) == 0) {
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
setenv("BOOTMODE", "true", 1);
exec_command(0, NULL, bb_setenv, "sh", UNINSTALLER, NULL);
return;
}
// Merge, trim, mount magisk.img, which will also travel through the modules // Merge, trim, mount magisk.img, which will also travel through the modules
// After this, it will create the module list // After this, it will create the module list
if (prepare_img()) if (prepare_img())
goto core_only; // Mounting fails, we can only do core only stuffs goto core_only; // Mounting fails, we can only do core only stuffs
restorecon();
chmod(SECURE_DIR, 0700);
// Run common scripts // Run common scripts
LOGI("* Running post-fs-data.d scripts\n"); LOGI("* Running post-fs-data.d scripts\n");
exec_common_script("post-fs-data"); exec_common_script("post-fs-data");
@@ -614,8 +779,6 @@ core_only:
} }
auto_start_magiskhide(); auto_start_magiskhide();
unblock:
unblock_boot_process(); unblock_boot_process();
} }
@@ -625,13 +788,24 @@ void late_start(int client) {
write_int(client, 0); write_int(client, 0);
close(client); close(client);
if (access(SECURE_DIR, F_OK) != 0) {
// It's safe to create the folder at this point if the system didn't create it
xmkdir(SECURE_DIR, 0700);
}
if (!setup_done) {
// The setup failed for some reason, reboot and try again
exec_command_sync("/system/bin/reboot", NULL);
return;
}
// Allocate buffer // Allocate buffer
if (buf == NULL) buf = xmalloc(PATH_MAX); if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX); if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done // Wait till the full patch is done
wait_till_exists(PATCHDONE); if (full_patch_pid > 0)
unlink(PATCHDONE); waitpid(full_patch_pid, NULL, 0);
// Run scripts after full patch, most reliable way to run scripts // Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n"); LOGI("* Running service.d scripts\n");
@@ -645,32 +819,21 @@ void late_start(int client) {
exec_module_script("service"); exec_module_script("service");
core_only: core_only:
// Install Magisk Manager if exists
if (access(MANAGERAPK, F_OK) == 0) { if (access(MANAGERAPK, F_OK) == 0) {
// Install Magisk Manager if exists
rename(MANAGERAPK, "/data/magisk.apk"); rename(MANAGERAPK, "/data/magisk.apk");
setfilecon("/data/magisk.apk", "u:object_r:su_file:s0"); install_apk("/data/magisk.apk");
while (1) { } else {
sleep(5); // Check whether we have a valid manager installed
int apk_res = -1, pid; sqlite3 *db = get_magiskdb();
pid = exec_command(1, &apk_res, pm_setenv, struct db_strings str;
"app_process", INIT_DB_STRINGS(&str);
"/system/bin", "com.android.commands.pm.Pm", get_db_strings(db, SU_MANAGER, &str);
"install", "-r", "/data/magisk.apk", NULL); if (validate_manager(str.s[SU_MANAGER], 0, NULL)) {
if (pid != -1) { // There is no manager installed, install the stub
int err = 0; exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk", NULL);
while (fdgets(buf, PATH_MAX, apk_res) > 0) { install_apk("/data/magisk.apk");
LOGD("apk_install: %s", buf);
err |= strstr(buf, "Error:") != NULL;
}
waitpid(pid, NULL, 0);
close(apk_res);
// Keep trying until pm is started
if (err)
continue;
break;
}
} }
unlink("/data/magisk.apk");
} }
// All boot stage done, cleanup everything // All boot stage done, cleanup everything
@@ -678,6 +841,4 @@ core_only:
free(buf2); free(buf2);
buf = buf2 = NULL; buf = buf2 = NULL;
vec_deep_destroy(&module_list); vec_deep_destroy(&module_list);
stop_debug_full_log();
} }

View File

@@ -19,8 +19,11 @@
#include "utils.h" #include "utils.h"
#include "daemon.h" #include "daemon.h"
#include "resetprop.h" #include "resetprop.h"
#include "magiskpolicy.h"
int is_daemon_init = 0, seperate_vendor = 0; int setup_done = 0;
int seperate_vendor = 0;
int full_patch_pid = -1;
static void *request_handler(void *args) { static void *request_handler(void *args) {
int client = *((int *) args); int client = *((int *) args);
@@ -36,7 +39,6 @@ static void *request_handler(void *args) {
case ADD_HIDELIST: case ADD_HIDELIST:
case RM_HIDELIST: case RM_HIDELIST:
case LS_HIDELIST: case LS_HIDELIST:
case POST_FS:
case POST_FS_DATA: case POST_FS_DATA:
case LATE_START: case LATE_START:
if (credential.uid != 0) { if (credential.uid != 0) {
@@ -75,16 +77,17 @@ static void *request_handler(void *args) {
write_int(client, MAGISK_VER_CODE); write_int(client, MAGISK_VER_CODE);
close(client); close(client);
break; break;
case POST_FS:
post_fs(client);
break;
case POST_FS_DATA: case POST_FS_DATA:
post_fs_data(client); post_fs_data(client);
break; break;
case LATE_START: case LATE_START:
late_start(client); late_start(client);
break; break;
case HANDSHAKE:
/* Do NOT close the client, make it hold */
break;
default: default:
close(client);
break; break;
} }
return NULL; return NULL;
@@ -106,184 +109,48 @@ void auto_start_magiskhide() {
free(hide_prop); free(hide_prop);
} }
void daemon_init() { void main_daemon() {
is_daemon_init = 1;
// Magisk binaries
char *bin_path = NULL;
if (access("/cache/data_bin", F_OK) == 0)
bin_path = "/cache/data_bin";
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
bin_path = "/data/data/com.topjohnwu.magisk/install";
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
if (bin_path) {
rm_rf(DATABIN);
cp_afc(bin_path, DATABIN);
rm_rf(bin_path);
}
// Migration
rm_rf("/data/magisk");
unlink("/data/magisk.img");
unlink("/data/magisk_debug.log");
chmod("/data/adb", 0700);
LOGI("* Creating /sbin overlay");
DIR *dir;
struct dirent *entry;
int root, sbin;
char buf[PATH_MAX], buf2[PATH_MAX];
char magisk_name[10], init_name[10];
// Setup links under /sbin
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdir("/root", 0755);
chmod("/root", 0755);
root = xopen("/root", O_RDONLY | O_CLOEXEC);
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
link_dir(sbin, root);
unlink("/sbin/magisk");
close(sbin);
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
chmod("/sbin", 0755);
setfilecon("/sbin", "u:object_r:rootfs:s0");
dir = xfdopendir(root);
while((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
xsymlink(buf, buf2);
}
gen_rand_str(magisk_name, sizeof(magisk_name));
snprintf(buf, PATH_MAX, "/root/%s", magisk_name);
unlink("/sbin/magisk");
rename("/root/magisk", buf);
xsymlink(buf, "/sbin/magisk");
for (int i = 0; applet[i]; ++i) {
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
xsymlink(buf, buf2);
}
gen_rand_str(init_name, sizeof(init_name));
snprintf(buf, PATH_MAX, "/root/%s", init_name);
unlink("/sbin/magiskinit");
rename("/root/magiskinit", buf);
for (int i = 0; init_applet[i]; ++i) {
snprintf(buf2, PATH_MAX, "/sbin/%s", init_applet[i]);
xsymlink(buf, buf2);
}
close(root);
// Backward compatibility
xsymlink(DATABIN, "/data/magisk");
xsymlink(MAINIMG, "/data/magisk.img");
xsymlink(MOUNTPOINT, "/magisk");
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
LOGI("* Mounting mirrors");
struct vector mounts;
vec_init(&mounts);
file_to_vector("/proc/mounts", &mounts);
char *line;
int skip_initramfs = 0;
// Check whether skip_initramfs device
vec_for_each(&mounts, line) {
if (strstr(line, " /system_root ")) {
xmkdirs(MIRRDIR "/system", 0755);
bind_mount("/system_root/system", MIRRDIR "/system");
skip_initramfs = 1;
break;
}
}
vec_for_each(&mounts, line) {
if (!skip_initramfs && strstr(line, " /system ")) {
sscanf(line, "%s", buf);
xmkdirs(MIRRDIR "/system", 0755);
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s <- %s\n", MIRRDIR "/system", buf);
#else
LOGI("mount: %s\n", MIRRDIR "/system");
#endif
} else if (strstr(line, " /vendor ")) {
seperate_vendor = 1;
sscanf(line, "%s", buf);
xmkdirs(MIRRDIR "/vendor", 0755);
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
#else
LOGI("mount: %s\n", MIRRDIR "/vendor");
#endif
}
free(line);
}
vec_destroy(&mounts);
if (!seperate_vendor) {
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
#ifdef MAGISK_DEBUG
LOGI("link: %s <- %s\n", MIRRDIR "/vendor", MIRRDIR "/system/vendor");
#else
LOGI("link: %s\n", MIRRDIR "/vendor");
#endif
}
xmkdirs(MIRRDIR "/bin", 0755);
bind_mount(DATABIN, MIRRDIR "/bin");
LOGI("* Setting up internal busybox");
xmkdirs(BBPATH, 0755);
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
void start_daemon() {
setsid(); setsid();
setcon("u:r:su:s0"); setcon("u:r:"SEPOL_PROC_DOMAIN":s0");
umask(0);
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC); int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
xdup2(fd, STDOUT_FILENO); xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO); xdup2(fd, STDERR_FILENO);
close(fd); close(fd);
fd = xopen("/dev/zero", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
close(fd);
// Block user signals // Start the log monitor
loggable = exec_command_sync("/system/bin/logcat", "-d", "-f", "/dev/null", NULL) == 0;
if (loggable) {
connect_daemon2(LOG_DAEMON, &fd);
write_int(fd, HANDSHAKE);
close(fd);
}
struct sockaddr_un sun;
fd = setup_socket(&sun, MAIN_DAEMON);
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
exit(1);
xlisten(fd, 10);
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
// Change process name
strcpy(argv0, "magiskd");
// Block all user signals
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
sigaddset(&block_set, SIGUSR1); sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2); sigaddset(&block_set, SIGUSR2);
pthread_sigmask(SIG_SETMASK, &block_set, NULL); pthread_sigmask(SIG_SETMASK, &block_set, NULL);
struct sockaddr_un sun; // Ignore SIGPIPE
fd = setup_socket(&sun); struct sigaction act;
memset(&act, 0, sizeof(act));
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun))) act.sa_handler = SIG_IGN;
exit(1); sigaction(SIGPIPE, &act, NULL);
xlisten(fd, 10);
// Start the log monitor
monitor_logs();
if ((is_daemon_init = (access(MAGISKTMP, F_OK) == 0))) {
// Restart stuffs if the daemon is restarted
exec_command_sync("logcat", "-b", "all", "-c", NULL);
auto_start_magiskhide();
start_debug_log();
} else if (check_data()) {
daemon_init();
}
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
// Change process name
strcpy(argv0, "magisk_daemon");
// Unlock all blocks for rw
unlock_blocks();
// Loop forever to listen for requests // Loop forever to listen for requests
while(1) { while(1) {
@@ -296,26 +163,38 @@ void start_daemon() {
} }
} }
/* Connect the daemon, and return a socketfd */ /* Connect the daemon, set sockfd, and return if new daemon is spawned */
int connect_daemon() { int connect_daemon2(daemon_t d, int *sockfd) {
struct sockaddr_un sun; struct sockaddr_un sun;
int fd = setup_socket(&sun); *sockfd = setup_socket(&sun, d);
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) { if (connect(*sockfd, (struct sockaddr*) &sun, sizeof(sun))) {
// If we cannot access the daemon, we start a daemon in the child process if possible
if (getuid() != UID_ROOT || getgid() != UID_ROOT) { if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "No daemon is currently running!\n"); fprintf(stderr, "No daemon is currently running!\n");
exit(1); exit(1);
} }
if (xfork() == 0) { if (fork_dont_care() == 0) {
LOGD("client: connect fail, try launching new daemon process\n"); LOGD("client: connect fail, try launching new daemon process\n");
close(fd); close(*sockfd);
start_daemon(); switch (d) {
case MAIN_DAEMON:
main_daemon();
break;
case LOG_DAEMON:
log_daemon();
break;
}
} }
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) while (connect(*sockfd, (struct sockaddr*) &sun, sizeof(sun)))
usleep(10000); usleep(10000);
return 1;
} }
return 0;
}
int connect_daemon() {
int fd;
connect_daemon2(MAIN_DAEMON, &fd);
return fd; return fd;
} }

158
native/jni/core/db.c Normal file
View File

@@ -0,0 +1,158 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#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++) {
if (strcmp(col_name[i], "policy") == 0)
su->policy = (policy_t) atoi(data[i]);
else if (strcmp(col_name[i], "logging") == 0)
su->log = atoi(data[i]);
else if (strcmp(col_name[i], "notification") == 0)
su->notify = atoi(data[i]);
}
LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", su->policy, su->log, su->notify);
return 0;
}
static int settings_cb(void *v, int col_num, char **data, char **col_name) {
struct db_settings *dbs = v;
int key = -1, value;
for (int i = 0; i < col_num; ++i) {
if (strcmp(col_name[i], "key") == 0) {
for (int k = 0; k < DB_SETTINGS_NUM; ++k) {
if (strcmp(data[i], DB_SETTING_KEYS[k]) == 0)
key = k;
}
} else if (strcmp(col_name[i], "value") == 0) {
value = atoi(data[i]);
}
}
if (key >= 0) {
dbs->v[key] = value;
LOGD("magiskdb: query %s=[%d]\n", DB_SETTING_KEYS[key], value);
}
return 0;
}
static int strings_cb(void *v, int col_num, char **data, char **col_name) {
struct db_strings *dbs = v;
int key = -1;
char *value;
for (int i = 0; i < col_num; ++i) {
if (strcmp(col_name[i], "key") == 0) {
for (int k = 0; k < DB_STRING_NUM; ++k) {
if (strcmp(data[i], DB_STRING_KEYS[k]) == 0)
key = k;
}
} else if (strcmp(col_name[i], "value") == 0) {
value = data[i];
}
}
if (key >= 0) {
strcpy(dbs->s[key], value);
LOGD("magiskdb: query %s=[%s]\n", DB_STRING_KEYS[key], value);
}
return 0;
}
sqlite3 *get_magiskdb() {
sqlite3 *db = NULL;
if (access(MAGISKDB, R_OK) == 0) {
// Open database
int ret = sqlite3_open_v2(MAGISKDB, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) {
LOGE("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
db = NULL;
}
}
return db;
}
int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs) {
if (db == NULL)
return 1;
char *err;
if (key > 0) {
char query[128];
sprintf(query, "SELECT key, value FROM settings WHERE key=%d", key);
sqlite3_exec(db, query, settings_cb, dbs, &err);
} else {
sqlite3_exec(db, "SELECT key, value FROM settings", settings_cb, dbs, &err);
}
if (err) {
LOGE("sqlite3_exec: %s\n", err);
return 1;
}
return 0;
}
int get_db_strings(sqlite3 *db, int key, struct db_strings *str) {
if (db == NULL)
return 1;
char *err;
if (key > 0) {
char query[128];
sprintf(query, "SELECT key, value FROM strings WHERE key=%d", key);
sqlite3_exec(db, query, strings_cb, str, &err);
} else {
sqlite3_exec(db, "SELECT key, value FROM strings", strings_cb, str, &err);
}
if (err) {
LOGE("sqlite3_exec: %s\n", err);
return 1;
}
return 0;
}
int get_uid_policy(sqlite3 *db, int uid, struct su_access *su) {
if (db == NULL)
return 1;
char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(NULL));
sqlite3_exec(db, query, policy_cb, su, &err);
if (err) {
LOGE("sqlite3_exec: %s\n", err);
return 1;
}
return 0;
}
int validate_manager(char *pkg, int userid, struct stat *st) {
if (st == NULL) {
struct stat stat;
st = &stat;
}
// Prefer DE storage
const char *base = access("/data/user_de", F_OK) == 0 ? "/data/user_de" : "/data/user";
char app_path[128];
sprintf(app_path, "%s/%d/%s", base, userid, pkg[0] ? pkg : "xxx");
if (stat(app_path, st)) {
// Check the official package name
sprintf(app_path, "%s/%d/"JAVA_PACKAGE_NAME, base, userid);
if (stat(app_path, st)) {
LOGE("su: cannot find manager");
memset(st, 0, sizeof(*st));
pkg[0] = '\0';
return 1;
} else {
// Switch to official package if exists
strcpy(pkg, JAVA_PACKAGE_NAME);
}
}
return 0;
}

View File

@@ -0,0 +1,166 @@
/* log_daemon.c - A dedicated daemon to monitor logcat
*
* A universal logcat monitor for many usages. Add listeners to the list,
* and the new log line will be sent through sockets to trigger
* asynchronous events without polling
*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
int loggable = 1;
static struct vector log_cmd, clear_cmd;
static int sockfd;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
enum {
HIDE_EVENT,
LOG_EVENT
};
struct log_listener {
int fd;
int (*filter) (const char*);
};
static int am_proc_start_filter(const char *log) {
return strstr(log, "am_proc_start") != NULL;
}
static int magisk_log_filter(const char *log) {
return !am_proc_start_filter(log);
}
static struct log_listener events[] = {
{ /* HIDE_EVENT */
.fd = -1,
.filter = am_proc_start_filter
},
{ /* LOG_EVENT */
.fd = -1,
.filter = magisk_log_filter
}
};
#define EVENT_NUM (sizeof(events) / sizeof(struct log_listener))
static void sigpipe_handler(int sig) {
close(events[HIDE_EVENT].fd);
events[HIDE_EVENT].fd = -1;
}
static void *monitor_thread(void *args) {
// Block SIGPIPE to prevent interruption
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, SIGPIPE);
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
// Give the main daemon some time before we monitor it
sleep(5);
int fd;
char b;
while (1) {
fd = connect_daemon();
write_int(fd, HANDSHAKE);
// This should hold unless the daemon is killed
read(fd, &b, sizeof(b));
// The main daemon crashed, spawn a new one
close(fd);
}
}
static void *logcat_thread(void *args) {
int log_fd = -1, log_pid;
char line[4096];
while (1) {
// Start logcat
log_pid = exec_array(0, &log_fd, NULL, (char **) vec_entry(&log_cmd));
FILE *logs = fdopen(log_fd, "r");
while (fgets(line, sizeof(line), logs)) {
if (line[0] == '-')
continue;
size_t len = strlen(line);
pthread_mutex_lock(&lock);
for (int i = 0; i < EVENT_NUM; ++i) {
if (events[i].fd > 0 && events[i].filter(line))
write(events[i].fd, line, len);
}
pthread_mutex_unlock(&lock);
}
fclose(logs);
log_fd = -1;
kill(log_pid, SIGTERM);
waitpid(log_pid, NULL, 0);
LOGI("magisklogd: logcat output EOF");
// Clear buffer
log_pid = exec_array(0, NULL, NULL, (char **) vec_entry(&clear_cmd));
waitpid(log_pid, NULL, 0);
}
}
void log_daemon() {
setsid();
struct sockaddr_un sun;
sockfd = setup_socket(&sun, LOG_DAEMON);
if (xbind(sockfd, (struct sockaddr*) &sun, sizeof(sun)))
exit(1);
xlisten(sockfd, 10);
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") logger started\n");
strcpy(argv0, "magisklogd");
// Set SIGPIPE handler
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = sigpipe_handler;
sigaction(SIGPIPE, &act, NULL);
// Setup log dumps
rename(LOGFILE, LOGFILE ".bak");
events[LOG_EVENT].fd = xopen(LOGFILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC | O_APPEND, 0644);
// Construct cmdline
vec_init(&log_cmd);
vec_push_back(&log_cmd, "/system/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)
vec_push_back_all(&log_cmd, "-b", b[i], NULL);
}
vec_dup(&log_cmd, &clear_cmd);
vec_push_back_all(&log_cmd, "-v", "threadtime", "-s", "am_proc_start", "Magisk", "*:F", NULL);
vec_push_back(&log_cmd, NULL);
vec_push_back(&clear_cmd, "-c");
vec_push_back(&clear_cmd, NULL);
// Start worker threads
pthread_t thread;
pthread_create(&thread, NULL, monitor_thread, NULL);
pthread_detach(thread);
xpthread_create(&thread, NULL, logcat_thread, NULL);
pthread_detach(thread);
while(1) {
int fd = xaccept4(sockfd, NULL, NULL, SOCK_CLOEXEC);
switch(read_int(fd)) {
case HIDE_CONNECT:
pthread_mutex_lock(&lock);
close(events[HIDE_EVENT].fd);
events[HIDE_EVENT].fd = fd;
pthread_mutex_unlock(&lock);
break;
case HANDSHAKE:
default:
close(fd);
break;
}
}
}

View File

@@ -1,211 +0,0 @@
/* log_monitor.c - New thread to monitor logcat
*
* A universal logcat monitor for many usages. Add listeners to the list,
* and the pointer of the new log line will be sent through pipes to trigger
* asynchronous events without polling
*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include "magisk.h"
#include "utils.h"
#include "resetprop.h"
extern int is_daemon_init;
int logd = 0;
static int am_proc_start_filter(const char *log) {
return strstr(log, "am_proc_start") != NULL;
}
static int magisk_log_filter(const char *log) {
char *ss;
return (ss = strstr(log, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V');
}
static int magisk_debug_log_filter(const char *log) {
return strstr(log, "Magisk") != NULL;
}
struct log_listener log_events[] = {
{ /* HIDE_EVENT */
.fd = -1,
.filter = am_proc_start_filter
},
{ /* LOG_EVENT */
.fd = -1,
.filter = magisk_log_filter
},
{ /* DEBUG_EVENT */
.fd = -1,
.filter = magisk_debug_log_filter
}
};
#ifdef MAGISK_DEBUG
static int debug_log_pid = -1, debug_log_fd = -1;
#endif
static void check_logd() {
char *prop = getprop("init.svc.logd");
if (prop != NULL) {
free(prop);
logd = 1;
} else {
LOGD("log_monitor: logd not started, disable logging");
logd = 0;
}
}
static void *logger_thread(void *args) {
int log_fd = -1, log_pid;
char line[4096];
LOGD("log_monitor: logger start");
while (1) {
if (!logd) {
// Disable all services
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
close(log_events[i].fd);
log_events[i].fd = -1;
}
return NULL;
}
// Start logcat
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-b", "main", "-v", "threadtime", "-s", "am_proc_start", "-s", "Magisk", NULL);
while (fdgets(line, sizeof(line), log_fd)) {
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
if (log_events[i].fd > 0 && log_events[i].filter(line)) {
char *s = strdup(line);
xwrite(log_events[i].fd, &s, sizeof(s));
}
}
if (kill(log_pid, 0))
break;
}
// Cleanup
close(log_fd);
log_fd = -1;
kill(log_pid, SIGTERM);
waitpid(log_pid, NULL, 0);
// Clear buffer before restart
exec_command_sync("logcat", "-b", "events", "-b", "main", "-c", NULL);
check_logd();
}
// Should never be here, but well...
return NULL;
}
static void *magisk_log_thread(void *args) {
// Buffer logs before we have data access
struct vector logs;
vec_init(&logs);
int pipefd[2];
if (xpipe2(pipefd, O_CLOEXEC) == -1)
return NULL;
// Register our listener
log_events[LOG_EVENT].fd = pipefd[1];
LOGD("log_monitor: magisk log dumper start");
FILE *log = NULL;
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
if (!is_daemon_init) {
vec_push_back(&logs, strdup(line));
} else {
if (log == NULL) {
// Dump buffered logs to file
log = xfopen(LOGFILE, "w");
setbuf(log, NULL);
char *tmp;
vec_for_each(&logs, tmp) {
fprintf(log, "%s", tmp);
free(tmp);
}
vec_destroy(&logs);
}
fprintf(log, "%s", line);
}
}
return NULL;
}
static void *debug_magisk_log_thread(void *args) {
FILE *log = xfopen(DEBUG_LOG, "a");
setbuf(log, NULL);
int pipefd[2];
if (xpipe2(pipefd, O_CLOEXEC) == -1)
return NULL;
LOGD("log_monitor: debug log dumper start");
// Register our listener
log_events[DEBUG_EVENT].fd = pipefd[1];
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
fprintf(log, "%s", line);
return NULL;
}
/* Start new threads to monitor logcat and dump to logfile */
void monitor_logs() {
pthread_t thread;
check_logd();
if (logd) {
// Start log file dumper before monitor
xpthread_create(&thread, NULL, magisk_log_thread, NULL);
pthread_detach(thread);
// Start logcat monitor
xpthread_create(&thread, NULL, logger_thread, NULL);
pthread_detach(thread);
}
}
void start_debug_full_log() {
#ifdef MAGISK_DEBUG
if (logd) {
// Log everything initially
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "threadtime", NULL);
close(debug_log_fd);
}
#endif
}
void stop_debug_full_log() {
#ifdef MAGISK_DEBUG
// Stop recording the boot logcat after every boot task is done
if (debug_log_pid > 0) {
kill(debug_log_pid, SIGTERM);
waitpid(debug_log_pid, NULL, 0);
// Start debug thread
start_debug_log();
}
#endif
}
void start_debug_log() {
#ifdef MAGISK_DEBUG
if (logd) {
pthread_t thread;
// Start debug thread
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
pthread_detach(thread);
}
#endif
}

View File

@@ -12,7 +12,8 @@
char *argv0; char *argv0;
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL }; int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskhide_main, imgtool_main, NULL };
int create_links(const char *bin, const char *path) { int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX]; char self[PATH_MAX], linkpath[PATH_MAX];
@@ -33,8 +34,8 @@ static void usage() {
fprintf(stderr, fprintf(stderr,
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n" "Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
"\n" "\n"
"Usage: %s [applet [arguments]...]\n" "Usage: magisk [applet [arguments]...]\n"
" or: %s [options]...\n" " or: magisk [options]...\n"
"\n" "\n"
"Options:\n" "Options:\n"
" -c print current binary version\n" " -c print current binary version\n"
@@ -42,21 +43,16 @@ static void usage() {
" -V print running daemon version code\n" " -V print running daemon version code\n"
" --list list all available applets\n" " --list list all available applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n" " --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
" --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n" " --daemon manually start magisk daemon\n"
" --imgsize IMG report ext4 image used/total size\n" " --[init trigger] start service for init trigger\n"
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
" --mountimg IMG PATH mount IMG to PATH and prints the loop device\n"
" --umountimg PATH LOOP unmount PATH and delete LOOP device\n"
" --[init service] start init service\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n" " --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
" --restorecon fix selinux context on Magisk files and folders\n" " --restorecon fix selinux context on Magisk files and folders\n"
" --clone-attr SRC DEST clone permission, owner, and selinux context\n" " --clone-attr SRC DEST clone permission, owner, and selinux context\n"
"\n" "\n"
"Supported init services:\n" "Supported init triggers:\n"
" daemon, post-fs, post-fs-data, service\n" " startup, post-fs-data, service\n"
"\n" "\n"
"Supported applets:\n" "Supported applets:\n");
, argv0, argv0);
for (int i = 0; applet[i]; ++i) for (int i = 0; applet[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet[i]); fprintf(stderr, i ? ", %s" : " %s", applet[i]);
@@ -64,102 +60,78 @@ static void usage() {
exit(1); exit(1);
} }
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);
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION);
char *v = read_string(fd);
printf("%s\n", v);
free(v);
return 0;
} else if (strcmp(argv[1], "-V") == 0) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION_CODE);
printf("%d\n", read_int(fd));
return 0;
} else if (strcmp(argv[1], "--install") == 0) {
if (argc < 3) usage();
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]);
return 0;
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
unlock_blocks();
return 0;
} else if (strcmp(argv[1], "--restorecon") == 0) {
restorecon();
return 0;
} else if (strcmp(argv[1], "--clone-attr") == 0) {
if (argc < 4) usage();
clone_attr(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "--daemon") == 0) {
int fd = connect_daemon();
write_int(fd, DO_NOTHING);
return 0;
} else if (strcmp(argv[1], "--startup") == 0) {
startup();
return 0;
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
int fd = connect_daemon();
write_int(fd, POST_FS_DATA);
return read_int(fd);
} else if (strcmp(argv[1], "--service") == 0) {
int fd = connect_daemon();
write_int(fd, LATE_START);
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[]) { int main(int argc, char *argv[]) {
umask(0);
argv0 = argv[0]; argv0 = argv[0];
if (strcmp(basename(argv[0]), "magisk") == 0) { if (strcmp(basename(argv0), "magisk.bin") == 0) {
if (argc < 2) usage(); if (argc >= 2) {
if (strcmp(argv[1], "-c") == 0) {
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION);
char *v = read_string(fd);
printf("%s\n", v);
free(v);
return 0;
} else if (strcmp(argv[1], "-V") == 0) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION_CODE);
printf("%d\n", read_int(fd));
return 0;
} else if (strcmp(argv[1], "--install") == 0) {
if (argc < 3) usage();
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]);
return 0;
} else if (strcmp(argv[1], "--createimg") == 0) {
if (argc < 4) usage();
int size;
sscanf(argv[3], "%d", &size);
return create_img(argv[2], size);
} else if (strcmp(argv[1], "--imgsize") == 0) {
if (argc < 3) usage();
int used, total;
if (get_img_size(argv[2], &used, &total)) {
fprintf(stderr, "Cannot check %s size\n", argv[2]);
return 1;
}
printf("%d %d\n", used, total);
return 0;
} else if (strcmp(argv[1], "--resizeimg") == 0) {
if (argc < 4) usage();
int used, total, size;
sscanf(argv[3], "%d", &size);
if (get_img_size(argv[2], &used, &total)) {
fprintf(stderr, "Cannot check %s size\n", argv[2]);
return 1;
}
if (size <= used) {
fprintf(stderr, "Cannot resize smaller than %dM\n", used);
return 1;
}
return resize_img(argv[2], size);
} else if (strcmp(argv[1], "--mountimg") == 0) {
if (argc < 4) usage();
char *loop = mount_image(argv[2], argv[3]);
if (loop == NULL) {
fprintf(stderr, "Cannot mount image!\n");
return 1;
} else {
printf("%s\n", loop);
free(loop);
return 0;
}
} else if (strcmp(argv[1], "--umountimg") == 0) {
if (argc < 4) usage();
umount_image(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
unlock_blocks();
return 0;
} else if (strcmp(argv[1], "--restorecon") == 0) {
fix_filecon();
return 0;
} else if (strcmp(argv[1], "--clone-attr") == 0) {
if (argc < 4) usage();
clone_attr(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "--daemon") == 0) {
if (xfork() == 0)
start_daemon();
return 0;
} else if (strcmp(argv[1], "--post-fs") == 0) {
int fd = connect_daemon();
write_int(fd, POST_FS);
return read_int(fd);
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
int fd = connect_daemon();
write_int(fd, POST_FS_DATA);
return read_int(fd);
} else if (strcmp(argv[1], "--service") == 0) {
int fd = connect_daemon();
write_int(fd, LATE_START);
return read_int(fd);
} else {
// It's calling applets // It's calling applets
--argc; --argc;
++argv; ++argv;
@@ -168,10 +140,12 @@ int main(int argc, char *argv[]) {
// Applets // Applets
for (int i = 0; applet[i]; ++i) { for (int i = 0; applet[i]; ++i) {
if (strcmp(basename(argv[0]), applet[i]) == 0) if (strcmp(basename(argv[0]), applet[i]) == 0) {
strcpy(argv0, basename(argv[0]));
return (*applet_main[i])(argc, argv); return (*applet_main[i])(argc, argv);
}
} }
fprintf(stderr, "%s: applet not found\n", basename(argv[0])); // Not an applet
return 1; return magisk_main(argc, argv);
} }

View File

@@ -39,7 +39,9 @@
#include <lzma.h> #include <lzma.h>
#include <cil/cil.h> #include <cil/cil.h>
#include "dump.h" #include "binaries_xz.h"
#include "binaries_arch_xz.h"
#include "magiskrc.h" #include "magiskrc.h"
#include "utils.h" #include "utils.h"
#include "magiskpolicy.h" #include "magiskpolicy.h"
@@ -55,8 +57,6 @@
extern policydb_t *policydb; extern policydb_t *policydb;
int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL }; int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL };
static char RAND_SOCKET_NAME[sizeof(SOCKET_NAME)];
static int SOCKET_OFF = -1;
struct cmdline { struct cmdline {
char skip_initramfs; char skip_initramfs;
@@ -73,7 +73,7 @@ struct device {
static void parse_cmdline(struct cmdline *cmd) { static void parse_cmdline(struct cmdline *cmd) {
// cleanup // cleanup
memset(cmd, 0, sizeof(&cmd)); memset(cmd, 0, sizeof(*cmd));
char cmdline[4096]; char cmdline[4096];
mkdir("/proc", 0555); mkdir("/proc", 0555);
@@ -131,7 +131,7 @@ static int setup_block(struct device *dev, const char *partname) {
buffer[size] = '\0'; buffer[size] = '\0';
close(fd); close(fd);
parse_device(dev, buffer); parse_device(dev, buffer);
if (strcmp(dev->partname, partname) == 0) { if (strcasecmp(dev->partname, partname) == 0) {
snprintf(dev->path, sizeof(dev->path), "/dev/block/%s", dev->devname); snprintf(dev->path, sizeof(dev->path), "/dev/block/%s", dev->devname);
found = 1; found = 1;
break; break;
@@ -321,7 +321,15 @@ static int unxz(const void *buf, size_t size, int fd) {
static int dump_magisk(const char *path, mode_t mode) { static int dump_magisk(const char *path, mode_t mode) {
unlink(path); unlink(path);
int fd = creat(path, mode); int fd = creat(path, mode);
int ret = unxz(magisk_dump, sizeof(magisk_dump), fd); int ret = unxz(magisk_xz, sizeof(magisk_xz), fd);
close(fd);
return ret;
}
static int dump_manager(const char *path, mode_t mode) {
unlink(path);
int fd = creat(path, mode);
int ret = unxz(manager_xz, sizeof(manager_xz), fd);
close(fd); close(fd);
return ret; return ret;
} }
@@ -335,70 +343,24 @@ static int dump_magiskrc(const char *path, mode_t mode) {
static void patch_socket_name(const char *path) { static void patch_socket_name(const char *path) {
void *buf; void *buf;
char name[sizeof(MAIN_SOCKET)];
size_t size; size_t size;
mmap_rw(path, &buf, &size); mmap_rw(path, &buf, &size);
if (SOCKET_OFF < 0) { for (int i = 0; i < size; ++i) {
for (int i = 0; i < size; ++i) { if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
if (memcmp(buf + i, socket_name, sizeof(SOCKET_NAME)) == 0) { gen_rand_str(name, sizeof(name));
SOCKET_OFF = i; memcpy(buf + i, name, sizeof(name));
break; i += sizeof(name);
} }
if (memcmp(buf + i, LOG_SOCKET, sizeof(LOG_SOCKET)) == 0) {
gen_rand_str(name, sizeof(name));
memcpy(buf + i, name, sizeof(name));
i += sizeof(name);
} }
} }
gen_rand_str(RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
memcpy(buf + SOCKET_OFF, RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
munmap(buf, size); munmap(buf, size);
} }
static void magisk_init_daemon() {
setsid();
// Full patch
sepol_allow("su", ALL, ALL, ALL);
// Wait till init cold boot done
while (access("/dev/.coldboot_done", F_OK))
usleep(1);
int null = open("/dev/null", O_RDWR | O_CLOEXEC);
dup3(null, STDIN_FILENO, O_CLOEXEC);
dup3(null, STDOUT_FILENO, O_CLOEXEC);
dup3(null, STDERR_FILENO, O_CLOEXEC);
if (null > STDERR_FILENO)
close(null);
// Transit our context to su (mimic setcon)
int fd, crap;
fd = open("/proc/self/attr/current", O_WRONLY);
write(fd, "u:r:su:s0", 9);
close(fd);
// Dump full patch to kernel
dump_policydb(SELINUX_LOAD);
close(creat(PATCHDONE, 0));
destroy_policydb();
// Keep Magisk daemon always alive
while (1) {
struct sockaddr_un sun;
fd = setup_socket(&sun);
memcpy(sun.sun_path + 1, RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
usleep(10000); /* Wait 10 ms after each try */
/* Should hold forever */
read(fd, &crap, sizeof(crap));
/* If things went here, it means the other side of the socket is closed
* We restart the daemon again */
close(fd);
if (fork_dont_care() == 0) {
execv("/sbin/magisk", (char *[]) { "magisk", "--daemon", NULL } );
exit(1);
}
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
umask(0); umask(0);
@@ -410,6 +372,8 @@ int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "-x") == 0) { if (argc > 1 && strcmp(argv[1], "-x") == 0) {
if (strcmp(argv[2], "magisk") == 0) if (strcmp(argv[2], "magisk") == 0)
return dump_magisk(argv[3], 0755); return dump_magisk(argv[3], 0755);
else if (strcmp(argv[2], "manager") == 0)
return dump_manager(argv[3], 0644);
else if (strcmp(argv[2], "magiskrc") == 0) else if (strcmp(argv[2], "magiskrc") == 0)
return dump_magiskrc(argv[3], 0755); return dump_magiskrc(argv[3], 0755);
} }
@@ -424,14 +388,8 @@ int main(int argc, char *argv[]) {
if (null > STDERR_FILENO) if (null > STDERR_FILENO)
close(null); close(null);
// Extract and link files // Backup self
mkdir("/overlay", 0000); link("/init", "/init.bak");
dump_magiskrc("/overlay/init.magisk.rc", 0750);
mkdir("/overlay/sbin", 0755);
dump_magisk("/overlay/sbin/magisk", 0755);
patch_socket_name("/overlay/sbin/magisk");
mkdir("/overlay/root", 0755);
link("/init", "/overlay/root/magiskinit");
struct cmdline cmd; struct cmdline cmd;
parse_cmdline(&cmd); parse_cmdline(&cmd);
@@ -444,7 +402,7 @@ int main(int argc, char *argv[]) {
if (cmd.skip_initramfs) { if (cmd.skip_initramfs) {
// Clear rootfs // Clear rootfs
excl_list = (char *[]) { "overlay", ".backup", NULL }; excl_list = (char *[]) { "overlay", ".backup", "init.bak", NULL };
frm_rf(root); frm_rf(root);
} else if (access("/ramdisk.cpio.xz", R_OK) == 0) { } else if (access("/ramdisk.cpio.xz", R_OK) == 0) {
// High compression mode // High compression mode
@@ -473,6 +431,9 @@ int main(int argc, char *argv[]) {
* Early Mount * Early Mount
* ************/ * ************/
int mounted_system = 0;
int mounted_vendor = 0;
// If skip_initramfs or using split policies, we need early mount // If skip_initramfs or using split policies, we need early mount
if (cmd.skip_initramfs || access("/sepolicy", R_OK) != 0) { if (cmd.skip_initramfs || access("/sepolicy", R_OK) != 0) {
char partname[32]; char partname[32];
@@ -499,12 +460,15 @@ int main(int argc, char *argv[]) {
xmount("/system_root/system", "/system", NULL, MS_BIND, NULL); xmount("/system_root/system", "/system", NULL, MS_BIND, NULL);
} else { } else {
xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL); xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL);
mounted_system = 1;
} }
// Mount vendor // Mount vendor
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot); snprintf(partname, sizeof(partname), "vendor%s", cmd.slot);
if (setup_block(&dev, partname) == 0) if (setup_block(&dev, partname) == 0) {
xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL); xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
mounted_vendor = 1;
}
} }
/* ************* /* *************
@@ -513,30 +477,32 @@ int main(int argc, char *argv[]) {
// Only patch rootfs if not intended to run in recovery // Only patch rootfs if not intended to run in recovery
if (access("/etc/recovery.fstab", F_OK) != 0) { if (access("/etc/recovery.fstab", F_OK) != 0) {
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC); int fd;
mv_dir(overlay, root);
close(overlay); // Handle ramdisk overlays
rmdir("/overlay"); fd = open("/overlay", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
mv_dir(fd, root);
close(fd);
rmdir("/overlay");
}
patch_ramdisk(); patch_ramdisk();
patch_sepolicy(); patch_sepolicy();
close(STDIN_FILENO); // Dump binaries
close(STDOUT_FILENO); dump_magiskrc("/init.magisk.rc", 0750);
close(STDERR_FILENO); dump_magisk("/sbin/magisk", 0755);
patch_socket_name("/sbin/magisk");
if (fork_dont_care() == 0) { rename("/init.bak", "/sbin/magiskinit");
strcpy(argv[0], "magiskinit");
close(root);
magisk_init_daemon();
}
} }
// Clean up // Clean up
close(root); close(root);
if (!cmd.skip_initramfs) if (mounted_system)
umount("/system"); umount("/system");
umount("/vendor"); if (mounted_vendor)
umount("/vendor");
// Finally, give control back! // Finally, give control back!
execv("/init", argv); execv("/init", argv);

View File

@@ -8,19 +8,25 @@
#include "utils.h" #include "utils.h"
#include "magisk.h" #include "magisk.h"
char socket_name[] = SOCKET_NAME;
/* Setup the address and return socket fd */ /* Setup the address and return socket fd */
int setup_socket(struct sockaddr_un *sun) { int setup_socket(struct sockaddr_un *sun, daemon_t d) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(sun, 0, sizeof(*sun)); memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL; sun->sun_family = AF_LOCAL;
sun->sun_path[0] = '\0'; sun->sun_path[0] = '\0';
memcpy(sun->sun_path + 1, socket_name, sizeof(SOCKET_NAME)); 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 fd; return fd;
} }
/* /*
* Receive a file descriptor from a Unix socket. * Receive a file descriptor from a Unix socket.
* Contributed by @mkasick * Contributed by @mkasick

View File

@@ -1,4 +1,4 @@
LOCAL_PATH:= $(call my-dir) LOCAL_PATH := $(call my-dir)
# libsqlite.so (stub) # libsqlite.so (stub)
include $(CLEAR_VARS) include $(CLEAR_VARS)
@@ -28,6 +28,16 @@ LOCAL_SRC_FILES := \
mincrypt/sha256.c mincrypt/sha256.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libnanopb.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libnanopb
LOCAL_C_INCLUDES := $(LIBNANOPB)
LOCAL_SRC_FILES := \
nanopb/pb_common.c \
nanopb/pb_decode.c \
nanopb/pb_encode.c
include $(BUILD_STATIC_LIBRARY)
# libfdt.a # libfdt.a
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt LOCAL_MODULE:= libfdt
@@ -165,7 +175,7 @@ LOCAL_SRC_FILES := \
xz/src/liblzma/simple/simple_encoder.c \ xz/src/liblzma/simple/simple_encoder.c \
xz/src/liblzma/simple/sparc.c \ xz/src/liblzma/simple/sparc.c \
xz/src/liblzma/simple/x86.c xz/src/liblzma/simple/x86.c
LOCAL_CFLAGS += -DHAVE_CONFIG_H -std=c99 LOCAL_CFLAGS += -DHAVE_CONFIG_H -Wno-implicit-function-declaration
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libsepol.a # libsepol.a

1
native/jni/external/nanopb vendored Submodule

View File

@@ -8,7 +8,9 @@
#include <sys/un.h> #include <sys/un.h>
#include <sys/socket.h> #include <sys/socket.h>
extern int is_daemon_init, seperate_vendor; extern int setup_done;
extern int seperate_vendor;
extern int full_patch_pid;
// Commands require connecting to daemon // Commands require connecting to daemon
enum { enum {
@@ -16,14 +18,15 @@ enum {
SUPERUSER, SUPERUSER,
CHECK_VERSION, CHECK_VERSION,
CHECK_VERSION_CODE, CHECK_VERSION_CODE,
POST_FS,
POST_FS_DATA, POST_FS_DATA,
LATE_START, LATE_START,
LAUNCH_MAGISKHIDE, LAUNCH_MAGISKHIDE,
STOP_MAGISKHIDE, STOP_MAGISKHIDE,
ADD_HIDELIST, ADD_HIDELIST,
RM_HIDELIST, RM_HIDELIST,
LS_HIDELIST LS_HIDELIST,
HIDE_CONNECT,
HANDSHAKE
}; };
// Return codes for daemon // Return codes for daemon
@@ -31,23 +34,33 @@ enum {
DAEMON_ERROR = -1, DAEMON_ERROR = -1,
DAEMON_SUCCESS = 0, DAEMON_SUCCESS = 0,
ROOT_REQUIRED, ROOT_REQUIRED,
LOGD_DISABLED, LOGCAT_DISABLED,
HIDE_IS_ENABLED, HIDE_IS_ENABLED,
HIDE_NOT_ENABLED, HIDE_NOT_ENABLED,
HIDE_ITEM_EXIST, HIDE_ITEM_EXIST,
HIDE_ITEM_NOT_EXIST, HIDE_ITEM_NOT_EXIST,
}; };
typedef enum {
MAIN_DAEMON,
LOG_DAEMON
} daemon_t;
// daemon.c // daemon.c
void start_daemon(); void main_daemon();
int connect_daemon(); int connect_daemon();
int connect_daemon2(daemon_t d, int *sockfd);
void auto_start_magiskhide(); void auto_start_magiskhide();
void daemon_init();
// log_monitor.c
extern int loggable;
void log_daemon();
// socket.c // socket.c
int setup_socket(struct sockaddr_un *sun); int setup_socket(struct sockaddr_un *sun, daemon_t d);
int recv_fd(int sockfd); int recv_fd(int sockfd);
void send_fd(int sockfd, int fd); void send_fd(int sockfd, int fd);
int read_int(int fd); int read_int(int fd);
@@ -59,10 +72,9 @@ void write_string(int fd, const char* val);
* Boot Stages * * Boot Stages *
***************/ ***************/
void post_fs(int client); void startup();
void post_fs_data(int client); void post_fs_data(int client);
void late_start(int client); void late_start(int client);
void fix_filecon();
/************** /**************
* MagiskHide * * MagiskHide *

123
native/jni/include/db.h Normal file
View File

@@ -0,0 +1,123 @@
#ifndef DB_H
#define DB_H
#include <sqlite3.h>
#include <sys/stat.h>
/***************
* DB Settings *
***************/
#define DB_SETTING_KEYS ((char *[]) { \
"root_access", \
"multiuser_mode", \
"mnt_ns" \
})
#define DB_SETTINGS_NUM (sizeof(DB_SETTING_KEYS) / sizeof(char*))
// Settings indices
enum {
ROOT_ACCESS = 0,
SU_MULTIUSER_MODE,
SU_MNT_NS
};
// Values for root_access
enum {
ROOT_ACCESS_DISABLED = 0,
ROOT_ACCESS_APPS_ONLY,
ROOT_ACCESS_ADB_ONLY,
ROOT_ACCESS_APPS_AND_ADB
};
// Values for multiuser_mode
enum {
MULTIUSER_MODE_OWNER_ONLY = 0,
MULTIUSER_MODE_OWNER_MANAGED,
MULTIUSER_MODE_USER
};
// Values for mnt_ns
enum {
NAMESPACE_MODE_GLOBAL = 0,
NAMESPACE_MODE_REQUESTER,
NAMESPACE_MODE_ISOLATE
};
struct db_settings {
int v[DB_SETTINGS_NUM];
};
#define DEFAULT_DB_SETTINGS (struct db_settings) { .v = {\
ROOT_ACCESS_APPS_AND_ADB, \
MULTIUSER_MODE_OWNER_ONLY, \
NAMESPACE_MODE_REQUESTER \
}}
/**************
* DB Strings *
**************/
#define DB_STRING_KEYS ((char *[]) { \
"requester" \
})
#define DB_STRING_NUM (sizeof(DB_STRING_KEYS) / sizeof(char*))
// Strings indices
enum {
SU_MANAGER = 0
};
struct db_strings {
char s[DB_STRING_NUM][128];
};
void INIT_DB_STRINGS(struct db_strings *str);
/*************
* SU Access *
*************/
typedef enum {
QUERY = 0,
DENY = 1,
ALLOW = 2,
} policy_t;
struct su_access {
policy_t policy;
int log;
int notify;
};
#define DEFAULT_SU_ACCESS (struct su_access) { \
.policy = QUERY, \
.log = 1, \
.notify = 1 \
}
#define SILENT_SU_ACCESS (struct su_access) { \
.policy = ALLOW, \
.log = 0, \
.notify = 0 \
}
#define NO_SU_ACCESS (struct su_access) { \
.policy = DENY, \
.log = 0, \
.notify = 0 \
}
/********************
* Public Functions *
********************/
sqlite3 *get_magiskdb();
int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs);
int get_db_strings(sqlite3 *db, int key, struct db_strings *str);
int get_uid_policy(sqlite3 *db, int uid, struct su_access *su);
int validate_manager(char *pkg, int userid, struct stat *st);
#endif //DB_H

View File

@@ -46,25 +46,6 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, 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)) #define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
enum {
HIDE_EVENT,
LOG_EVENT,
DEBUG_EVENT
};
struct log_listener {
int fd;
int (*filter) (const char*);
};
extern struct log_listener log_events[];
extern int logd;
void monitor_logs();
void start_debug_full_log();
void stop_debug_full_log();
void start_debug_log();
#endif #endif
/******************** /********************

View File

@@ -7,37 +7,40 @@
#include "logging.h" #include "logging.h"
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK" #define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
#define SOCKET_NAME "d30138f2310a9fb9c54a3e0c21f58591" #define MAIN_SOCKET "d30138f2310a9fb9c54a3e0c21f58591"
#define LOG_SOCKET "5864cd77f2f8c59b3882e2d35dbf51e4"
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
#ifndef ARG_MAX #ifndef ARG_MAX
#define ARG_MAX 4096 #define ARG_MAX 4096
#endif #endif
#define LOGFILE "/cache/magisk.log" #define LOGFILE "/cache/magisk.log"
#define DEBUG_LOG "/data/adb/magisk_debug.log"
#define UNBLOCKFILE "/dev/.magisk.unblock" #define UNBLOCKFILE "/dev/.magisk.unblock"
#define PATCHDONE "/dev/.magisk.patch.done"
#define DISABLEFILE "/cache/.disable_magisk" #define DISABLEFILE "/cache/.disable_magisk"
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
#define CACHEMOUNT "/cache/magisk_mount"
#define MAGISKTMP "/sbin/.core" #define MAGISKTMP "/sbin/.core"
#define BLOCKDIR MAGISKTMP "/block"
#define MIRRDIR MAGISKTMP "/mirror" #define MIRRDIR MAGISKTMP "/mirror"
#define BBPATH MAGISKTMP "/busybox" #define BBPATH MAGISKTMP "/busybox"
#define MOUNTPOINT MAGISKTMP "/img" #define MOUNTPOINT MAGISKTMP "/img"
#define COREDIR MOUNTPOINT "/.core" #define COREDIR MOUNTPOINT "/.core"
#define HOSTSFILE COREDIR "/hosts" #define HOSTSFILE COREDIR "/hosts"
#define HIDELIST COREDIR "/hidelist" #define HIDELIST COREDIR "/hidelist"
#define MAINIMG "/data/adb/magisk.img" #define SECURE_DIR "/data/adb"
#define DATABIN "/data/adb/magisk" #define MAINIMG SECURE_DIR "/magisk.img"
#define DATABIN SECURE_DIR "/magisk"
#define MAGISKDB SECURE_DIR "/magisk.db"
#define SIMPLEMOUNT SECURE_DIR "/magisk_simple"
#define MANAGERAPK DATABIN "/magisk.apk" #define MANAGERAPK DATABIN "/magisk.apk"
#define MAGISKRC "/init.magisk.rc" #define MAGISKRC "/init.magisk.rc"
// selinuxfs paths // selinuxfs paths
#define SELINUX_PATH "/sys/fs/selinux/" #define SELINUX_PATH "/sys/fs/selinux"
#define SELINUX_ENFORCE SELINUX_PATH "enforce" #define SELINUX_ENFORCE SELINUX_PATH "/enforce"
#define SELINUX_POLICY SELINUX_PATH "policy" #define SELINUX_POLICY SELINUX_PATH "/policy"
#define SELINUX_LOAD SELINUX_PATH "load" #define SELINUX_LOAD SELINUX_PATH "/load"
#define SELINUX_CONTEXT SELINUX_PATH "/context"
// split policy paths // split policy paths
#define PLAT_POLICY_DIR "/system/etc/selinux/" #define PLAT_POLICY_DIR "/system/etc/selinux/"
@@ -51,11 +54,10 @@
extern char *argv0; /* For changing process name */ extern char *argv0; /* For changing process name */
#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL }) #define applet ((char *[]) { "su", "resetprop", "magiskhide", "imgtool", NULL })
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL }) #define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]); extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
extern char socket_name[]; /* Workaround compiler bug pre NDK r13 */
int create_links(const char *bin, const char *path); int create_links(const char *bin, const char *path);
@@ -63,13 +65,7 @@ int create_links(const char *bin, const char *path);
int magiskhide_main(int argc, char *argv[]); int magiskhide_main(int argc, char *argv[]);
int magiskpolicy_main(int argc, char *argv[]); int magiskpolicy_main(int argc, char *argv[]);
int su_client_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]);
#ifdef __cplusplus
extern "C" {
#endif
int resetprop_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]);
#ifdef __cplusplus int imgtool_main(int argc, char *argv[]);
}
#endif
#endif #endif

View File

@@ -1,18 +1,19 @@
#include "magisk.h"
#include "magiskpolicy.h"
const char magiskrc[] = const char magiskrc[] =
// Triggers // Triggers
"on post-fs\n" "on post-fs\n"
" start logd\n" " start logd\n"
" start magisk_pfs\n"
" wait /dev/.magisk.unblock 10\n"
"\n" "\n"
"on post-fs-data\n" "on post-fs-data\n"
" load_persist_props\n" " load_persist_props\n"
" rm /dev/.magisk.unblock\n" " rm "UNBLOCKFILE"\n"
" start magisk_pfsd\n" " start magisk_startup\n"
" wait /dev/.magisk.unblock 10\n" " wait "UNBLOCKFILE" 10\n"
" rm /dev/.magisk.unblock\n" " rm /dev/.magisk.unblock\n"
"\n" "\n"
@@ -20,25 +21,19 @@ const char magiskrc[] =
"service magisk_daemon /sbin/magisk --daemon\n" "service magisk_daemon /sbin/magisk --daemon\n"
" user root\n" " user root\n"
" seclabel u:r:su:s0\n" " seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
" oneshot\n" " oneshot\n"
"\n" "\n"
"service magisk_pfs /sbin/magisk --post-fs\n" "service magisk_startup /sbin/magisk --startup\n"
" user root\n" " user root\n"
" seclabel u:r:su:s0\n" " seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
" oneshot\n" " oneshot\n"
"\n" "\n"
"service magisk_pfsd /sbin/magisk --post-fs-data\n" "service magisk_service /sbin/magisk --service\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
"\n"
"service magisk_service /sbin/magisk --service\n"
" class late_start\n" " class late_start\n"
" user root\n" " user root\n"
" seclabel u:r:su:s0\n" " seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
" oneshot\n" " oneshot\n"
; ;

View File

@@ -4,10 +4,6 @@
#ifndef _RESETPROP_H_ #ifndef _RESETPROP_H_
#define _RESETPROP_H_ #define _RESETPROP_H_
#ifdef __cplusplus
extern "C" {
#endif
int prop_exist(const char *name); int prop_exist(const char *name);
int setprop(const char *name, const char *value); int setprop(const char *name, const char *value);
int setprop2(const char *name, const char *value, const int trigger); int setprop2(const char *name, const char *value, const int trigger);
@@ -16,10 +12,6 @@ char *getprop2(const char *name, int persist);
int deleteprop(const char *name); int deleteprop(const char *name);
int deleteprop2(const char *name, const int persist); int deleteprop2(const char *name, const int persist);
int read_prop_file(const char* filename, const int trigger); int read_prop_file(const char* filename, const int trigger);
void getprop_all(void (*callback)(const char*, const char*)); void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@@ -83,7 +83,8 @@ void ps(void (*func)(int));
void ps_filter_proc_name(const char *filter, void (*func)(int)); void ps_filter_proc_name(const char *filter, void (*func)(int));
void unlock_blocks(); void unlock_blocks();
void setup_sighandlers(void (*handler)(int)); void setup_sighandlers(void (*handler)(int));
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...); int exec_array(int err, int *fd, void (*setenv)(struct vector *), char *const *argv);
int exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, ...);
int exec_command_sync(char *const argv0, ...); int exec_command_sync(char *const argv0, ...);
int bind_mount(const char *from, const char *to); int bind_mount(const char *from, const char *to);
void get_client_cred(int fd, struct ucred *cred); void get_client_cred(int fd, struct ucred *cred);
@@ -122,7 +123,7 @@ int setattrat(int dirfd, const char *pathname, struct file_attr *a);
int fsetattr(int fd, struct file_attr *a); int fsetattr(int fd, struct file_attr *a);
void fclone_attr(const int sourcefd, const int targetfd); void fclone_attr(const int sourcefd, const int targetfd);
void clone_attr(const char *source, const char *target); void clone_attr(const char *source, const char *target);
void restorecon(int dirfd, int force); void restorecon();
int mmap_ro(const char *filename, void **buf, size_t *size); int mmap_ro(const char *filename, void **buf, size_t *size);
int mmap_rw(const char *filename, void **buf, size_t *size); int mmap_rw(const char *filename, void **buf, size_t *size);
void fd_full_read(int fd, void **buf, size_t *size); void fd_full_read(int fd, void **buf, size_t *size);
@@ -133,17 +134,12 @@ void write_zero(int fd, size_t size);
// img.c // img.c
#define round_size(a) ((((a) / 32) + 2) * 32)
#define SOURCE_TMP "/dev/source"
#define TARGET_TMP "/dev/target"
int create_img(const char *img, int size); int create_img(const char *img, int size);
int get_img_size(const char *img, int *used, int *total);
int resize_img(const char *img, int size); int resize_img(const char *img, int size);
char *mount_image(const char *img, const char *target); char *mount_image(const char *img, const char *target);
void umount_image(const char *target, const char *device); int umount_image(const char *target, const char *device);
int merge_img(const char *source, const char *target); int merge_img(const char *source, const char *target);
void trim_img(const char *img); int trim_img(const char *img, const char *mount, char *loop);
// pattern.c // pattern.c

View File

@@ -14,11 +14,12 @@ struct vector {
void vec_init(struct vector *v); void vec_init(struct vector *v);
void vec_push_back(struct vector *v, void *p); void vec_push_back(struct vector *v, void *p);
void vec_push_back_all(struct vector *v, void *p, ...);
void *vec_pop_back(struct vector *v); void *vec_pop_back(struct vector *v);
void vec_sort(struct vector *v, int (*compar)(const void *, const void *)); void vec_sort(struct vector *v, int (*compar)(const void *, const void *));
void vec_destroy(struct vector *v); void vec_destroy(struct vector *v);
void vec_deep_destroy(struct vector *v); void vec_deep_destroy(struct vector *v);
struct vector *vec_dup(struct vector *v); void vec_dup(struct vector *v, struct vector *vv);
#define vec_size(v) (v)->size #define vec_size(v) (v)->size
#define vec_cap(v) (v)->cap #define vec_cap(v) (v)->cap

View File

@@ -1,6 +1,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <libfdt.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "bootimg.h" #include "bootimg.h"
@@ -124,10 +125,16 @@ int parse_img(const char *image, boot_img *boot) {
fprintf(stderr, "PXA_BOOT_HDR\n"); fprintf(stderr, "PXA_BOOT_HDR\n");
boot->hdr = malloc(sizeof(pxa_boot_img_hdr)); boot->hdr = malloc(sizeof(pxa_boot_img_hdr));
memcpy(boot->hdr, head, sizeof(pxa_boot_img_hdr)); memcpy(boot->hdr, head, sizeof(pxa_boot_img_hdr));
} else if (memcmp(((boot_img_hdr*) head)->cmdline, NOOK_MAGIC, 12) == 0) { } else if (memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_MAGIC, 12) == 0
boot->flags |= NOOK_FLAG; || memcmp(((boot_img_hdr*) head)->cmdline, NOOKHD_NEW_MAGIC, 26) == 0) {
fprintf(stderr, "NOOK_GREEN_LOADER\n"); boot->flags |= NOOKHD_FLAG;
head += NOOK_PRE_HEADER_SZ - 1; 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; continue;
} else { } else {
boot->hdr = malloc(sizeof(boot_img_hdr)); boot->hdr = malloc(sizeof(boot_img_hdr));
@@ -162,7 +169,7 @@ int parse_img(const char *image, boot_img *boot) {
if (pos < boot->map_size) { if (pos < boot->map_size) {
boot->tail = head + pos; boot->tail = head + pos;
boot->tail_size = boot->map_size - pos; boot->tail_size = boot->map_size - (boot->tail - boot->map_addr);
} }
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE // Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
@@ -175,6 +182,29 @@ int parse_img(const char *image, boot_img *boot) {
// Search for dtb in kernel // Search for dtb in kernel
for (uint32_t i = 0; i < header(boot, kernel_size); ++i) { for (uint32_t i = 0; i < header(boot, kernel_size); ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) { 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->dtb = boot->kernel + i;
boot->dt_size = header(boot, kernel_size) - i; boot->dt_size = header(boot, kernel_size) - i;
lheader(boot, kernel_size, = i); lheader(boot, kernel_size, = i);
@@ -202,12 +232,12 @@ int parse_img(const char *image, boot_img *boot) {
fprintf(stderr, "MTK_RAMDISK_HDR\n"); fprintf(stderr, "MTK_RAMDISK_HDR\n");
boot->flags |= MTK_RAMDISK; boot->flags |= MTK_RAMDISK;
boot->r_hdr = malloc(sizeof(mtk_hdr)); boot->r_hdr = malloc(sizeof(mtk_hdr));
memcpy(boot->r_hdr, boot->kernel, sizeof(mtk_hdr)); memcpy(boot->r_hdr, boot->ramdisk, sizeof(mtk_hdr));
fprintf(stderr, "RAMDISK [%u]\n", boot->r_hdr->size); fprintf(stderr, "RAMDISK [%u]\n", boot->r_hdr->size);
fprintf(stderr, "NAME [%s]\n", boot->r_hdr->name); fprintf(stderr, "NAME [%s]\n", boot->r_hdr->name);
boot->ramdisk += 512; boot->ramdisk += 512;
lheader(boot, ramdisk_size, -= 512); lheader(boot, ramdisk_size, -= 512);
boot->k_fmt = check_fmt(boot->ramdisk, header(boot, ramdisk_size)); boot->r_fmt = check_fmt(boot->ramdisk, header(boot, ramdisk_size));
} }
char fmt[16]; char fmt[16];
@@ -288,8 +318,10 @@ void repack(const char* orig_image, const char* out_image) {
} else if (boot.flags & BLOB_FLAG) { } else if (boot.flags & BLOB_FLAG) {
// Skip blob header // Skip blob header
write_zero(fd, sizeof(blob_hdr)); write_zero(fd, sizeof(blob_hdr));
} else if (boot.flags & NOOK_FLAG) { } else if (boot.flags & NOOKHD_FLAG) {
restore_buf(fd, boot.map_addr, NOOK_PRE_HEADER_SZ); 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 // Skip a page for header
@@ -312,13 +344,14 @@ void repack(const char* orig_image, const char* out_image) {
} else { } else {
lheader(&boot, kernel_size, = restore(KERNEL_FILE, fd)); 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();
} }
// dtb
if (access(DTB_FILE, R_OK) == 0) {
lheader(&boot, kernel_size, += restore(DTB_FILE, fd));
}
file_align();
// ramdisk // ramdisk
ramdisk_off = lseek(fd, 0, SEEK_CUR); ramdisk_off = lseek(fd, 0, SEEK_CUR);
if (boot.flags & MTK_RAMDISK) { if (boot.flags & MTK_RAMDISK) {
@@ -333,7 +366,7 @@ void repack(const char* orig_image, const char* out_image) {
lheader(&boot, ramdisk_size, = comp(boot.r_fmt, fd, cpio, cpio_size)); lheader(&boot, ramdisk_size, = comp(boot.r_fmt, fd, cpio, cpio_size));
munmap(cpio, cpio_size); munmap(cpio, cpio_size);
} else { } else {
lheader(&boot, kernel_size, = restore(KERNEL_FILE, fd)); lheader(&boot, ramdisk_size, = restore(RAMDISK_FILE, fd));
} }
file_align(); file_align();
} }

View File

@@ -130,7 +130,8 @@ typedef struct blob_hdr {
#define LG_BUMP_FLAG 0x0040 #define LG_BUMP_FLAG 0x0040
#define SHA256_FLAG 0x0080 #define SHA256_FLAG 0x0080
#define BLOB_FLAG 0x0100 #define BLOB_FLAG 0x0100
#define NOOK_FLAG 0x0200 #define NOOKHD_FLAG 0x0200
#define ACCLAIM_FLAG 0x0400
typedef struct boot_img { typedef struct boot_img {
// Memory map of the whole image // Memory map of the whole image

View File

@@ -38,8 +38,11 @@ typedef enum {
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00" #define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
#define SEANDROID_MAGIC "SEANDROIDENFORCE" #define SEANDROID_MAGIC "SEANDROIDENFORCE"
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-" #define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
#define NOOK_MAGIC "Green Loader" #define NOOKHD_MAGIC "Green Loader"
#define NOOK_PRE_HEADER_SZ 1048575 #define NOOKHD_NEW_MAGIC "eMMC boot.img+secondloader"
#define NOOKHD_PRE_HEADER_SZ 1048576
#define ACCLAIM_MAGIC "BauwksBoot"
#define ACCLAIM_PRE_HEADER_SZ 262144
#define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL }) #define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
#define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL }) #define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })

View File

@@ -17,92 +17,92 @@ static void usage(char *arg0) {
"Usage: %s <action> [args...]\n" "Usage: %s <action> [args...]\n"
"\n" "\n"
"Supported actions:\n" "Supported actions:\n"
" --parse <bootimg>\n" " --parse <bootimg>\n"
" Parse <bootimg> only, do not unpack. Return values: \n" " Parse <bootimg> only, do not unpack. Return values: \n"
" 0:OK 1:error 2:insufficient boot partition size\n" " 0:OK 1:error 2:insufficient boot partition size\n"
" 3:chromeos 4:ELF32 5:ELF64\n" " 3:chromeos 4:ELF32 5:ELF64\n"
"\n" "\n"
" --unpack <bootimg>\n" " --unpack <bootimg>\n"
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into\n" " Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into\n"
" the current directory. Return value is the same as --parse\n" " the current directory. Return value is the same as --parse\n"
"\n" "\n"
" --repack <origbootimg> [outbootimg]\n" " --repack <origbootimg> [outbootimg]\n"
" Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n" " Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"
" to [outbootimg], or new-boot.img if not specified.\n" " to [outbootimg], or new-boot.img if not specified.\n"
" It will compress ramdisk.cpio with the same method used in <origbootimg>,\n" " It will compress ramdisk.cpio with the same method used in <origbootimg>,\n"
" or attempt to find ramdisk.cpio.[ext], and repack directly with the\n" " or attempt to find ramdisk.cpio.[ext], and repack directly with the\n"
" compressed ramdisk file\n" " compressed ramdisk file\n"
"\n" "\n"
" --hexpatch <file> <hexpattern1> <hexpattern2>\n" " --hexpatch <file> <hexpattern1> <hexpattern2>\n"
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n" " Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
"\n" "\n"
" --cpio <incpio> [commands...]\n" " --cpio <incpio> [commands...]\n"
" Do cpio commands to <incpio> (modifications are done directly)\n" " Do cpio commands to <incpio> (modifications are done directly)\n"
" Each command is a single argument, use quotes if necessary\n" " Each command is a single argument, use quotes if necessary\n"
" Supported commands:\n" " Supported commands:\n"
" rm [-r] ENTRY\n" " rm [-r] ENTRY\n"
" Remove ENTRY, specify [-r] to remove recursively\n" " Remove ENTRY, specify [-r] to remove recursively\n"
" mkdir MODE ENTRY\n" " mkdir MODE ENTRY\n"
" Create directory ENTRY in permissions MODE\n" " Create directory ENTRY in permissions MODE\n"
" ln TARGET ENTRY\n" " ln TARGET ENTRY\n"
" Create a symlink to TARGET with the name ENTRY\n" " Create a symlink to TARGET with the name ENTRY\n"
" mv SOURCE DEST\n" " mv SOURCE DEST\n"
" Move SOURCE to DEST\n" " Move SOURCE to DEST\n"
" add MODE ENTRY INFILE\n" " add MODE ENTRY INFILE\n"
" Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists\n" " Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists\n"
" extract [ENTRY OUT]\n" " extract [ENTRY OUT]\n"
" Extract ENTRY to OUT, or extract all entries to current directory\n" " Extract ENTRY to OUT, or extract all entries to current directory\n"
" test\n" " test\n"
" Test the current cpio's patch status\n" " Test the current cpio's patch status\n"
" Return value: 0/stock 1/Magisk 2/other (phh, SuperSU, Xposed)\n" " Return value: 0/stock 1/Magisk 2/other (phh, SuperSU, Xposed)\n"
" patch KEEPVERITY KEEPFORCEENCRYPT\n" " patch KEEPVERITY KEEPFORCEENCRYPT\n"
" Ramdisk patches. KEEP**** are boolean values\n" " Ramdisk patches. KEEP**** are boolean values\n"
" backup ORIG [SHA1]\n" " backup ORIG [SHA1]\n"
" Create ramdisk backups from ORIG\n" " Create ramdisk backups from ORIG\n"
" SHA1 of stock boot image is optional\n" " SHA1 of stock boot image is optional\n"
" restore\n" " restore\n"
" Restore ramdisk from ramdisk backup stored within incpio\n" " Restore ramdisk from ramdisk backup stored within incpio\n"
" magisk ORIG HIGHCOMP KEEPVERITY KEEPFORCEENCRYPT [SHA1]\n" " magisk ORIG HIGHCOMP KEEPVERITY KEEPFORCEENCRYPT [SHA1]\n"
" Do Magisk patches and backups all in one step\n" " Do Magisk patches and backups all in one step\n"
" Create ramdisk backups from ORIG\n" " Create ramdisk backups from ORIG\n"
" HIGHCOMP, KEEP**** are boolean values\n" " HIGHCOMP, KEEP**** are boolean values\n"
" SHA1 of stock boot image is optional\n" " SHA1 of stock boot image is optional\n"
" sha1\n" " sha1\n"
" Print stock boot SHA1 if previously stored\n" " Print stock boot SHA1 if previously stored\n"
"\n" "\n"
" --dtb-<cmd> <dtb>\n" " --dtb-<cmd> <dtb>\n"
" Do dtb related cmds to <dtb> (modifications are done directly)\n" " Do dtb related cmds to <dtb> (modifications are done directly)\n"
" Supported commands:\n" " Supported commands:\n"
" dump\n" " dump\n"
" Dump all contents from dtb for debugging\n" " Dump all contents from dtb for debugging\n"
" test\n" " test\n"
" Check if fstab has verity/avb flags\n" " Check if fstab has verity/avb flags\n"
" Return value: 0/no flags 1/flag exists\n" " Return value: 0/no flags 1/flag exists\n"
" patch\n" " patch\n"
" Search for fstab and remove verity/avb\n" " Search for fstab and remove verity/avb\n"
"\n" "\n"
" --compress[=method] <infile> [outfile]\n" " --compress[=method] <infile> [outfile]\n"
" Compress <infile> with [method] (default: gzip), optionally to [outfile]\n" " Compress <infile> with [method] (default: gzip), optionally to [outfile]\n"
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n" " <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
" Supported methods: " " Supported methods: "
, arg0); , arg0);
for (int i = 0; SUP_LIST[i]; ++i) for (int i = 0; SUP_LIST[i]; ++i)
fprintf(stderr, "%s ", SUP_LIST[i]); fprintf(stderr, "%s ", SUP_LIST[i]);
fprintf(stderr, fprintf(stderr,
"\n\n" "\n\n"
" --decompress <infile> [outfile]\n" " --decompress <infile> [outfile]\n"
" Detect method and decompress <infile>, optionally to [outfile]\n" " Detect method and decompress <infile>, optionally to [outfile]\n"
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n" " <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
" Supported methods: "); " Supported methods: ");
for (int i = 0; SUP_LIST[i]; ++i) for (int i = 0; SUP_LIST[i]; ++i)
fprintf(stderr, "%s ", SUP_LIST[i]); fprintf(stderr, "%s ", SUP_LIST[i]);
fprintf(stderr, fprintf(stderr,
"\n\n" "\n\n"
" --sha1 <file>\n" " --sha1 <file>\n"
" Print the SHA1 checksum for <file>\n" " Print the SHA1 checksum for <file>\n"
"\n" "\n"
" --cleanup\n" " --cleanup\n"
" Cleanup the current working directory\n" " Cleanup the current working directory\n"
"\n"); "\n");
exit(1); exit(1);

View File

@@ -34,18 +34,22 @@ static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
#define STOCK_BOOT 0x0 #define STOCK_BOOT 0x0
#define MAGISK_PATCH 0x1 #define MAGISK_PATCH 0x1
#define OTHER_PATCH 0x2 #define HIGH_COMPRESS 0x2
#define OTHER_PATCH 0x3
static int cpio_test(struct vector *v) { static int cpio_test(struct vector *v) {
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL }; const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL }; const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL };
for (int i = 0; OTHER_LIST[i]; ++i) for (int i = 0; OTHER_LIST[i]; ++i)
if (cpio_find(v, OTHER_LIST[i]) > 0) if (cpio_find(v, OTHER_LIST[i]) >= 0)
return OTHER_PATCH; return OTHER_PATCH;
if (cpio_find(v, "ramdisk.cpio.xz") >= 0)
return HIGH_COMPRESS;
for (int i = 0; MAGISK_LIST[i]; ++i) for (int i = 0; MAGISK_LIST[i]; ++i)
if (cpio_find(v, MAGISK_LIST[i]) > 0) if (cpio_find(v, MAGISK_LIST[i]) >= 0)
return MAGISK_PATCH; return MAGISK_PATCH;
return STOCK_BOOT; return STOCK_BOOT;

View File

@@ -17,27 +17,24 @@
#include "daemon.h" #include "daemon.h"
static char *prop_key[] = static char *prop_key[] =
{ "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit", { "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode",
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", NULL }; "ro.boot.warranty_bit", "ro.warranty_bit", "ro.debuggable", "ro.secure",
"ro.build.type", "ro.build.tags", "ro.build.selinux", NULL };
static char *prop_value[] = static char *prop_value[] =
{ "green", "1", "enforcing", "0", "0", "0", "1", "user", "release-keys", "0", NULL }; { "locked", "green", "1", "enforcing",
"0", "0", "0", "1",
static int mocked = 0; "user", "release-keys", "0", NULL };
void manage_selinux() { void manage_selinux() {
if (mocked) return; char val;
char val[1];
int fd = xopen(SELINUX_ENFORCE, O_RDONLY); int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
xxread(fd, val, 1); xxread(fd, &val, sizeof(val));
close(fd); close(fd);
// Permissive // Permissive
if (val[0] == '0') { if (val == '0') {
LOGI("hide_daemon: Permissive detected, hide the state\n");
chmod(SELINUX_ENFORCE, 0640); chmod(SELINUX_ENFORCE, 0640);
chmod(SELINUX_POLICY, 0440); chmod(SELINUX_POLICY, 0440);
mocked = 1;
} }
} }
@@ -56,15 +53,19 @@ void hide_sensitive_props() {
} }
} }
static void rm_magisk_prop(const char *name, const char *value) { static void rm_magisk_prop(const char *name, const char *value, void *v) {
if (strstr(name, "magisk")) { if (strstr(name, "magisk")) {
deleteprop2(name, 0); deleteprop2(name, 0);
} }
} }
static void kill_proc(int pid) {
kill(pid, SIGTERM);
}
void clean_magisk_props() { void clean_magisk_props() {
LOGD("hide_utils: Cleaning magisk props\n"); LOGD("hide_utils: Cleaning magisk props\n");
getprop_all(rm_magisk_prop); getprop_all(rm_magisk_prop, NULL);
} }
int add_list(char *proc) { int add_list(char *proc) {

View File

@@ -22,10 +22,6 @@ int hideEnabled = 0;
static pthread_t proc_monitor_thread; static pthread_t proc_monitor_thread;
pthread_mutex_t hide_lock, file_lock; pthread_mutex_t hide_lock, file_lock;
void kill_proc(int pid) {
kill(pid, SIGTERM);
}
static void usage(char *arg0) { static void usage(char *arg0) {
fprintf(stderr, fprintf(stderr,
"MagiskHide v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Hide Magisk!\n\n" "MagiskHide v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Hide Magisk!\n\n"
@@ -49,11 +45,14 @@ void launch_magiskhide(int client) {
return; return;
} }
if (!logd) { if (!loggable) {
if (client > 0) { if (client > 0) {
write_int(client, LOGD_DISABLED); write_int(client, LOGCAT_DISABLED);
close(client); close(client);
} }
setprop(MAGISKHIDE_PROP, "0");
// Remove without actually removing persist props
deleteprop2(MAGISKHIDE_PROP, 0);
return; return;
} }
@@ -144,14 +143,14 @@ int magiskhide_main(int argc, char *argv[]) {
case ROOT_REQUIRED: case ROOT_REQUIRED:
fprintf(stderr, "Root is required for this operation\n"); fprintf(stderr, "Root is required for this operation\n");
return code; return code;
case LOGD_DISABLED: case LOGCAT_DISABLED:
fprintf(stderr, "Logd is not running, cannot run logcat\n"); fprintf(stderr, "Logcat is disabled, cannot start MagiskHide\n");
return (code); return (code);
case HIDE_NOT_ENABLED: case HIDE_NOT_ENABLED:
fprintf(stderr, "Magisk hide is not enabled yet\n"); fprintf(stderr, "MagiskHide is not enabled yet\n");
return code; return code;
case HIDE_IS_ENABLED: case HIDE_IS_ENABLED:
fprintf(stderr, "Magisk hide is already enabled\n"); fprintf(stderr, "MagiskHide is already enabled\n");
return code; return code;
case HIDE_ITEM_EXIST: case HIDE_ITEM_EXIST:
fprintf(stderr, "Process [%s] already exists in hide list\n", argv[2]); fprintf(stderr, "Process [%s] already exists in hide list\n", argv[2]);

View File

@@ -4,10 +4,6 @@
#include <pthread.h> #include <pthread.h>
#define TERM_THREAD SIGUSR1 #define TERM_THREAD SIGUSR1
#define HIDE_DONE SIGUSR2
// Kill process
void kill_proc(int pid);
// Process monitor // Process monitor
void proc_monitor(); void proc_monitor();

View File

@@ -16,65 +16,53 @@
#include <sys/mount.h> #include <sys/mount.h>
#include "magisk.h" #include "magisk.h"
#include "daemon.h"
#include "utils.h" #include "utils.h"
#include "magiskhide.h" #include "magiskhide.h"
static char init_ns[32], zygote_ns[2][32], cache_block[256]; static int sockfd = -1;
static int hide_queue = 0, zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
// Workaround for the lack of pthread_cancel // Workaround for the lack of pthread_cancel
static void term_thread(int sig) { static void term_thread(int sig) {
LOGD("proc_monitor: running cleanup\n"); LOGD("proc_monitor: running cleanup\n");
destroy_list(); destroy_list();
hideEnabled = 0; hideEnabled = 0;
// Unregister listener close(sockfd);
log_events[HIDE_EVENT].fd = -1; sockfd = -1;
close(pipefd[0]);
close(pipefd[1]);
pipefd[0] = pipefd[1] = -1;
pthread_mutex_destroy(&hide_lock); pthread_mutex_destroy(&hide_lock);
pthread_mutex_destroy(&file_lock); pthread_mutex_destroy(&file_lock);
LOGD("proc_monitor: terminating...\n"); LOGD("proc_monitor: terminating...\n");
pthread_exit(NULL); pthread_exit(NULL);
} }
static void hide_done(int sig) {
--hide_queue;
if (hide_queue == 0) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xsymlink(DATABIN, "/data/magisk");
xsymlink(MAINIMG, "/data/magisk.img");
xsymlink(MOUNTPOINT, "/magisk");
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
}
static int read_namespace(const int pid, char* target, const size_t size) { static int read_namespace(const int pid, char* target, const size_t size) {
char path[32]; char path[32];
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid); sprintf(path, "/proc/%d/ns/mnt", pid);
if (access(path, R_OK) == -1) if (access(path, R_OK) == -1)
return 1; return 1;
xreadlink(path, target, size); xreadlink(path, target, size);
return 0; return 0;
} }
static void store_zygote_ns(int pid) {
if (zygote_num == 2) return;
do {
usleep(500);
read_namespace(pid, zygote_ns[zygote_num], 32);
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
++zygote_num;
}
static void lazy_unmount(const char* mountpoint) { static void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1) if (umount2(mountpoint, MNT_DETACH) != -1)
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint); LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
} }
static void hide_daemon(int pid, int ppid) { static int parse_ppid(int pid) {
char stat[512], path[32];
int fd, ppid;
sprintf(path, "/proc/%d/stat", pid);
fd = xopen(path, O_RDONLY);
xread(fd, stat, sizeof(stat));
close(fd);
/* PID COMM STATE PPID ..... */
sscanf(stat, "%*d %*s %*c %d", &ppid);
return ppid;
}
static void hide_daemon(int pid) {
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid); LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
strcpy(argv0, "hide_daemon");
char *line, buffer[PATH_MAX]; char *line, buffer[PATH_MAX];
struct vector mount_list; struct vector mount_list;
@@ -89,31 +77,9 @@ static void hide_daemon(int pid, int ppid) {
vec_init(&mount_list); vec_init(&mount_list);
file_to_vector(buffer, &mount_list); file_to_vector(buffer, &mount_list);
// Find the cache block name if not found yet // Unmount dummy skeletons and /sbin links
if (has_cache && cache_block[0] == '\0') {
vec_for_each(&mount_list, line) {
if (strstr(line, " /cache ")) {
sscanf(line, "%256s", cache_block);
break;
}
}
if (strlen(cache_block) == 0)
has_cache = 0;
}
// Unmout cache mounts
if (has_cache) {
vec_for_each(&mount_list, line) {
if (strstr(line, cache_block) && (strstr(line, " /system/") || strstr(line, " /vendor/"))) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
}
}
// Unmount dummy skeletons, /sbin links
vec_for_each(&mount_list, line) { vec_for_each(&mount_list, line) {
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) { if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) {
sscanf(line, "%*s %4096s", buffer); sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer); lazy_unmount(buffer);
} }
@@ -126,23 +92,19 @@ static void hide_daemon(int pid, int ppid) {
vec_init(&mount_list); vec_init(&mount_list);
file_to_vector(buffer, &mount_list); file_to_vector(buffer, &mount_list);
// Unmount any loop mounts // Unmount everything under /system, /vendor, and loop mounts
vec_for_each(&mount_list, line) { vec_for_each(&mount_list, line) {
if (strstr(line, "/dev/block/loop")) { if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) {
sscanf(line, "%*s %4096s", buffer); sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer); lazy_unmount(buffer);
} }
free(line); free(line);
} }
vec_destroy(&mount_list);
exit: exit:
// Send resume signal // Send resume signal
kill(pid, SIGCONT); kill(pid, SIGCONT);
// Free up memory
vec_destroy(&mount_list);
// Wait a while and link it back
sleep(10);
kill(ppid, HIDE_DONE);
_exit(0); _exit(0);
} }
@@ -151,7 +113,6 @@ void proc_monitor() {
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
sigaddset(&block_set, TERM_THREAD); sigaddset(&block_set, TERM_THREAD);
sigaddset(&block_set, HIDE_DONE);
pthread_sigmask(SIG_UNBLOCK, &block_set, NULL); pthread_sigmask(SIG_UNBLOCK, &block_set, NULL);
// Register the cancel signal // Register the cancel signal
@@ -159,107 +120,88 @@ void proc_monitor() {
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_handler = term_thread; act.sa_handler = term_thread;
sigaction(TERM_THREAD, &act, NULL); sigaction(TERM_THREAD, &act, NULL);
act.sa_handler = hide_done;
sigaction(HIDE_DONE, &act, NULL);
cache_block[0] = '\0'; if (access("/proc/1/ns/mnt", F_OK) != 0) {
// Get the mount namespace of init
if (read_namespace(1, init_ns, 32)) {
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n"); LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
term_thread(TERM_THREAD); term_thread(TERM_THREAD);
} }
LOGI("proc_monitor: init ns=%s\n", init_ns);
// Get the mount namespace of zygote while(1) {
zygote_num = 0; // Connect to the log daemon
while(!zygote_num) { connect_daemon2(LOG_DAEMON, &sockfd);
// Check zygote every 10 ms write_int(sockfd, HIDE_CONNECT);
usleep(10000);
ps_filter_proc_name("zygote", store_zygote_ns);
}
ps_filter_proc_name("zygote64", store_zygote_ns);
switch(zygote_num) { FILE *log_in = fdopen(sockfd, "r");
case 1: char buf[4096];
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]); while (fgets(buf, sizeof(buf), log_in)) {
break; char *ss = strchr(buf, '[');
case 2: int pid, ppid, num = 0;
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]); char *pos = ss, proc[256], ns[32], pns[32];
break;
}
// Register our listener to logcat monitor while(1) {
xpipe2(pipefd, O_CLOEXEC); pos = strchr(pos, ',');
log_events[HIDE_EVENT].fd = pipefd[1]; if(pos == NULL)
break;
for (char *log, *line;; free(log)) { pos[0] = ' ';
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) { ++num;
/* It might be interrupted */
log = NULL;
continue;
}
char *ss = strchr(log, '[');
int pid, ret, comma = 0;
char *pos = ss, processName[256], ns[32];
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++comma;
}
if (comma == 6)
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
else
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
if(ret != 2)
continue;
// Critical region
pthread_mutex_lock(&hide_lock);
vec_for_each(hide_list, line) {
if (strcmp(processName, line) == 0) {
while(1) {
ret = 1;
for (int i = 0; i < zygote_num; ++i) {
read_namespace(pid, ns, sizeof(ns));
if (strcmp(ns, zygote_ns[i]) == 0) {
usleep(50);
ret = 0;
break;
}
}
if (ret) break;
}
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1) continue;
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
unlink("/magisk");
unlink("/data/magisk");
unlink("/data/magisk.img");
unlink(MAGISKRC);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
++hide_queue;
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
int selfpid = getpid();
if (fork_dont_care() == 0)
hide_daemon(pid, selfpid);
break;
} }
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;
ppid = parse_ppid(pid);
read_namespace(ppid, pns, sizeof(pns));
do {
read_namespace(pid, ns, sizeof(ns));
if (strcmp(ns, pns) == 0)
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 = ':';
#ifdef MAGISK_DEBUG
LOGI("proc_monitor: %s (PID=[%d] ns=%s)(PPID=[%d] ns=%s)\n",
proc, pid, ns + 4, ppid, pns + 4);
#else
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);
} }
pthread_mutex_unlock(&hide_lock); // The other end EOF, restart the connection
} }
} }

View File

@@ -0,0 +1,31 @@
/* resetprop.h - Internal struct definitions
*/
#ifndef MAGISK_PROPS_H
#define MAGISK_PROPS_H
#include "system_properties.h"
#include "logging.h"
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];
};
struct read_cb_t {
void (*func)(const char *name, const char *value, void *cookie);
void *cookie;
};
char *persist_getprop(const char *name);
void persist_getprop_all(struct read_cb_t *read_cb);
bool persist_deleteprop(const char *name);
void collect_props(const char *name, const char *value, void *prop_list);
#endif //MAGISK_PROPS_H

View File

@@ -0,0 +1,252 @@
#include <stdbool.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include "_resetprop.h"
#include "utils.h"
#include "vector.h"
#define PERSISTENT_PROPERTY_DIR "/data/property"
/* ***********************************************************************
* Auto generated header and constant definitions compiled from
* android/platform/system/core/master/init/persistent_properties.proto
* using Nanopb's protoc
* Nanopb: https://github.com/nanopb/nanopb
* ***********************************************************************/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.1 at Sun Apr 22 14:36:22 2018. */
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Struct definitions */
typedef struct _PersistentProperties {
pb_callback_t properties;
/* @@protoc_insertion_point(struct:PersistentProperties) */
} PersistentProperties;
typedef struct _PersistentProperties_PersistentPropertyRecord {
pb_callback_t name;
bool has_value;
char value[92];
/* @@protoc_insertion_point(struct:PersistentProperties_PersistentPropertyRecord) */
} PersistentProperties_PersistentPropertyRecord;
/* Default values for struct fields */
/* Initializer values for message structs */
#define PersistentProperties_init_default {{{NULL}, NULL}}
#define PersistentProperties_PersistentPropertyRecord_init_default {{{NULL}, NULL}, false, ""}
#define PersistentProperties_init_zero {{{NULL}, NULL}}
#define PersistentProperties_PersistentPropertyRecord_init_zero {{{NULL}, NULL}, false, ""}
/* Field tags (for use in manual encoding/decoding) */
#define PersistentProperties_properties_tag 1
#define PersistentProperties_PersistentPropertyRecord_name_tag 1
#define PersistentProperties_PersistentPropertyRecord_value_tag 2
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.9.1 at Sun Apr 22 14:36:22 2018. */
/* Struct field encoding specification for nanopb */
const pb_field_t PersistentProperties_PersistentPropertyRecord_fields[3] = {
PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, PersistentProperties_PersistentPropertyRecord, name, name, 0),
PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, PersistentProperties_PersistentPropertyRecord, value, name, 0),
PB_LAST_FIELD
};
const pb_field_t PersistentProperties_fields[2] = {
PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, PersistentProperties, properties, properties, &PersistentProperties_PersistentPropertyRecord_fields),
PB_LAST_FIELD
};
/* Maximum encoded size of messages (where known) */
/* PersistentProperties_size depends on runtime parameters */
/* PersistentProperties_PersistentPropertyRecord_size depends on runtime parameters */
/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
#define PROPS_MESSAGES \
#endif
/* @@protoc_insertion_point(eof) */
/* ***************************
* End of auto generated code
* ***************************/
static bool name_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
uint8_t *name = xmalloc(stream->bytes_left + 1);
name[stream->bytes_left] = '\0';
if (!pb_read(stream, name, stream->bytes_left))
return false;
*arg = name;
return true;
}
static bool name_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
return pb_encode_tag_for_field(stream, field) && pb_encode_string(stream, *arg, strlen(*arg));
}
static bool prop_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
PersistentProperties_PersistentPropertyRecord prop = {};
prop.name.funcs.decode = name_decode;
if (!pb_decode(stream, PersistentProperties_PersistentPropertyRecord_fields, &prop))
return false;
struct read_cb_t *read_cb = *arg;
read_cb->func(prop.name.arg, prop.value, read_cb->cookie);
return true;
}
static bool prop_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
PersistentProperties_PersistentPropertyRecord prop = {};
prop.name.funcs.encode = name_encode;
prop.has_value = true;
struct vector *v = *arg;
struct prop_t *e;
vec_for_each(v, e) {
if (e == NULL)
continue;
if (!pb_encode_tag_for_field(stream, field))
return false;
prop.name.arg = e->name;
strcpy(prop.value, e->value);
if (!pb_encode_submessage(stream, PersistentProperties_PersistentPropertyRecord_fields, &prop))
return false;
free(e->name);
free(e);
}
return true;
}
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) {
int fd = (intptr_t)stream->state;
return xwrite(fd, buf, count) == count;
}
static pb_ostream_t create_ostream(const char *filename) {
int fd = creat(filename, 0644);
pb_ostream_t o = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
return o;
}
static void pb_getprop_cb(const char *name, const char *value, void *v) {
struct prop_t *prop = v;
if (prop->name && strcmp(name, prop->name) == 0) {
strcpy(prop->value, value);
prop->name = NULL;
}
}
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");
PersistentProperties props = PersistentProperties_init_zero;
props.properties.funcs.decode = prop_decode;
props.properties.arg = read_cb;
uint8_t *buf;
size_t size;
mmap_ro(PERSISTENT_PROPERTY_DIR "/persistent_properties", (void **) &buf, &size);
pb_istream_t stream = pb_istream_from_buffer(buf, size);
pb_decode(&stream, PersistentProperties_fields, &props);
munmap(buf, size);
} else {
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
struct dirent *entry;
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
continue;
char *value = persist_getprop(entry->d_name);
if (value) {
read_cb->func(strdup(entry->d_name), value, read_cb->cookie);
free(value);
}
}
}
}
char *persist_getprop(const char *name) {
struct prop_t prop;
prop.name = (char *) name;
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
struct read_cb_t read_cb = {
.func = pb_getprop_cb,
.cookie = &prop
};
persist_getprop_all(&read_cb);
if (prop.name)
return NULL;
} else {
// Try to read from file
char path[PATH_MAX];
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return NULL;
PRINT_D("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);
}
return strdup(prop.value);
}
bool persist_deleteprop(const char *name) {
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
struct vector v;
vec_init(&v);
struct read_cb_t read_cb = {
.func = collect_props,
.cookie = &v
};
persist_getprop_all(&read_cb);
struct prop_t *p;
bool reencode = false;
vec_for_each(&v, p) {
if (strcmp(p->name, name) == 0) {
// Remove the prop from the list
free(p->name);
free(p);
vec_cur(&v) = NULL;
reencode = true;
break;
}
}
if (reencode) {
// Dump the props back
PersistentProperties props = PersistentProperties_init_zero;
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");
if (!pb_encode(&ostream, PersistentProperties_fields, &props))
return false;
clone_attr(PERSISTENT_PROPERTY_DIR "/persistent_properties", PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
rename(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp", PERSISTENT_PROPERTY_DIR "/persistent_properties");
}
vec_destroy(&v);
return reencode;
} else {
char path[PATH_MAX];
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
if (unlink(path) == 0) {
PRINT_D("resetprop: unlink [%s]\n", path);
return true;
}
}
return false;
}

View File

@@ -2,52 +2,6 @@
* *
* Copyright 2016 nkk71 <nkk71x@gmail.com> * Copyright 2016 nkk71 <nkk71x@gmail.com>
* Copyright 2016 topjohnwu <topjohnwu@gmail.com> * Copyright 2016 topjohnwu <topjohnwu@gmail.com>
*
* Info:
*
* all changes are in
*
* bionic/libc/bionic/system_properties.cpp
*
* Functions that need to be patched/added in system_properties.cpp
*
* int __system_properties_init2()
* on android 7, first tear down the everything then let it initialize again:
* if (initialized) {
* //list_foreach(contexts, [](context_node* l) { l->reset_access(); });
* //return 0;
* free_and_unmap_contexts();
* initialized = false;
* }
*
*
* static prop_area* map_prop_area(const char* filename, bool is_legacy)
* we dont want this read only so change: 'O_RDONLY' to 'O_RDWR'
*
* static prop_area* map_fd_ro(const int fd)
* we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE'
*
*
* Copy the code of prop_info *prop_area::find_property, and modify to delete props
* const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
* {
* ...
* ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed
* ...
*
* if (prop_offset != 0) {
* atomic_store_explicit(&current->prop, 0, memory_order_release); // Add this line to nullify the prop entry
* return to_prop_info(&current->prop);
* } else {
*
* ....
* }
*
*
* by patching just those functions directly, all other functions should be ok
* as is.
*
*
*/ */
#include <stdio.h> #include <stdio.h>
@@ -65,15 +19,11 @@
#include "magisk.h" #include "magisk.h"
#include "resetprop.h" #include "resetprop.h"
extern "C" { #include "_resetprop.h"
#include "vector.h" #include "vector.h"
} #include "utils.h"
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) fprintf(stderr, __VA_ARGS__); } int prop_verbose = 0;
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
#define PERSISTENT_PROPERTY_DIR "/data/property"
static int verbose = 0;
static int check_legal_property_name(const char *name) { static int check_legal_property_name(const char *name) {
int namelen = strlen(name); int namelen = strlen(name);
@@ -129,6 +79,49 @@ static int usage(char* arg0) {
return 1; return 1;
} }
// The callback passes to __system_property_read_callback2, actually runs the callback in read_cb
static void callback_wrapper(void *read_cb, const char *name, const char *value, uint32_t serial) {
((struct read_cb_t *) read_cb)->func(name, value, ((struct read_cb_t *) read_cb)->cookie);
}
/* **********************************
* Callback functions for read_cb_t
* **********************************/
void collect_props(const char *name, const char *value, void *prop_list) {
struct prop_t *p = (struct prop_t *) xmalloc(sizeof(*p));
p->name = strdup(name);
strcpy(p->value, value);
vec_push_back(prop_list, p);
}
static void collect_unique_props(const char *name, const char *value, void *prop_list) {
struct vector *v = prop_list;
struct prop_t *p;
bool uniq = true;
vec_for_each(v, p) {
if (strcmp(name, p->name) == 0) {
uniq = 0;
break;
}
}
if (uniq)
collect_props(name, value, prop_list);
}
static void store_prop_value(const char *name, const char *value, void *dst) {
strcpy(dst, value);
}
static void prop_foreach_cb(const prop_info* pi, void* read_cb) {
__system_property_read_callback2(pi, callback_wrapper, read_cb);
}
// Comparision function used to sort prop vectors
static int prop_cmp(const void *p1, const void *p2) {
return strcmp(((struct prop_t *) p1)->name, ((struct prop_t *) p2)->name);
}
static int init_resetprop() { static int init_resetprop() {
if (__system_properties_init2()) { if (__system_properties_init2()) {
PRINT_E("resetprop: Initialize error\n"); PRINT_E("resetprop: Initialize error\n");
@@ -137,16 +130,36 @@ static int init_resetprop() {
return 0; return 0;
} }
static void print_props(int persist) {
struct prop_t *p;
struct vector prop_list;
vec_init(&prop_list);
getprop_all(collect_props, &prop_list);
if (persist) {
struct read_cb_t read_cb = {
.func = collect_unique_props,
.cookie = &prop_list
};
persist_getprop_all(&read_cb);
}
vec_sort(&prop_list, prop_cmp);
vec_for_each(&prop_list, p) {
printf("[%s]: [%s]\n", p->name, p->value);
free(p->name);
free(p);
}
vec_destroy(&prop_list);
}
/* **************************************************
* Implementations of functions in resetprop.h (APIs)
* **************************************************/
int prop_exist(const char *name) { int prop_exist(const char *name) {
if (init_resetprop()) return 0; if (init_resetprop()) return 0;
return __system_property_find2(name) != NULL; return __system_property_find2(name) != NULL;
} }
static void read_prop_info(void* cookie, const char *name, const char *value, uint32_t serial) {
strcpy((char *) cookie, value);
}
char *getprop(const char *name) { char *getprop(const char *name) {
return getprop2(name, 0); return getprop2(name, 0);
} }
@@ -155,103 +168,35 @@ char *getprop(const char *name) {
char *getprop2(const char *name, int persist) { char *getprop2(const char *name, int persist) {
if (check_legal_property_name(name)) if (check_legal_property_name(name))
return NULL; return NULL;
char value[PROP_VALUE_MAX];
if (init_resetprop()) return NULL; if (init_resetprop()) return NULL;
const prop_info *pi = __system_property_find2(name); const prop_info *pi = __system_property_find2(name);
if (pi == NULL) { if (pi == NULL) {
if (persist && strncmp(name, "persist.", 8) == 0) { if (persist && strncmp(name, "persist.", 8) == 0) {
// Try to read from file char *value = persist_getprop(name);
char path[PATH_MAX]; if (value)
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name); return value;
int fd = open(path, O_RDONLY | O_CLOEXEC); }
if (fd < 0) goto no_prop;
PRINT_D("resetprop: read prop from [%s]\n", path);
size_t len = read(fd, value, sizeof(value));
value[len] = '\0'; // Null terminate the read value
} else {
no_prop:
PRINT_D("resetprop: prop [%s] does not exist\n", name); PRINT_D("resetprop: prop [%s] does not exist\n", name);
return NULL; return NULL;
}
} else { } else {
__system_property_read_callback2(pi, read_prop_info, value); char value[PROP_VALUE_MAX];
struct read_cb_t read_cb = {
.func = store_prop_value,
.cookie = value
};
__system_property_read_callback2(pi, callback_wrapper, &read_cb);
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
return strdup(value);
} }
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
return strdup(value);
} }
struct wrapper { void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie) {
void (*func)(const char *, const char *);
};
static void cb_wrapper(void* cookie, const char *name, const char *value, uint32_t serial) {
((wrapper *) cookie)->func(name, value);
}
static void prop_foreach_cb(const prop_info* pi, void* cookie) {
__system_property_read_callback2(pi, cb_wrapper, cookie);
}
class property {
public:
property(const char *n, const char *v) {
name = strdup(n);
value = strdup(v);
}
~property() {
free((void *)name);
free((void *)value);
}
const char *name;
const char *value;
};
vector prop_list;
static int prop_cmp(const void *p1, const void *p2) {
return strcmp(((property *) p1)->name, ((property *) p2)->name);
}
static void print_all_props_cb(const char *name, const char *value) {
vec_push_back(&prop_list, new property(name, value));
}
static void print_all_props(int persist) {
void *p;
vec_init(&prop_list);
getprop_all(print_all_props_cb);
if (persist) {
// Check all persist props in data
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
struct dirent *entry;
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
continue;
int found = 0;
vec_for_each(&prop_list, p) {
if (strcmp(((property *) p)->name, entry->d_name) == 0) {
found = 1;
break;
}
}
if (!found)
vec_push_back(&prop_list, new property(entry->d_name, getprop2(entry->d_name, 1)));
}
}
vec_sort(&prop_list, prop_cmp);
vec_for_each(&prop_list, p) {
printf("[%s]: [%s]\n", ((property *) p)->name, ((property *) p)->value);
delete((property *) p);
}
vec_destroy(&prop_list);
}
void getprop_all(void (*callback)(const char*, const char*)) {
if (init_resetprop()) return; if (init_resetprop()) return;
struct wrapper wrap = { struct read_cb_t read_cb = {
.func = callback .func = callback,
.cookie = cookie
}; };
__system_property_foreach2(prop_foreach_cb, &wrap); __system_property_foreach2(prop_foreach_cb, &read_cb);
} }
int setprop(const char *name, const char *value) { int setprop(const char *name, const char *value) {
@@ -294,7 +239,7 @@ int deleteprop(const char *name) {
return deleteprop2(name, 1); return deleteprop2(name, 1);
} }
int deleteprop2(const char *name, const int persist) { int deleteprop2(const char *name, int persist) {
if (check_legal_property_name(name)) if (check_legal_property_name(name))
return 1; return 1;
if (init_resetprop()) return -1; if (init_resetprop()) return -1;
@@ -302,8 +247,8 @@ int deleteprop2(const char *name, const int persist) {
path[0] = '\0'; path[0] = '\0';
PRINT_D("resetprop: deleteprop [%s]\n", name); PRINT_D("resetprop: deleteprop [%s]\n", name);
if (persist && strncmp(name, "persist.", 8) == 0) if (persist && strncmp(name, "persist.", 8) == 0)
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name); persist = persist_deleteprop(name);
return __system_property_del(name) && unlink(path); return __system_property_del(name) && !(persist && strncmp(name, "persist.", 8) == 0);
} }
int read_prop_file(const char* filename, const int trigger) { int read_prop_file(const char* filename, const int trigger) {
@@ -336,7 +281,7 @@ int read_prop_file(const char* filename, const int trigger) {
} }
if (comment) continue; if (comment) continue;
pch = strchr(line, '='); pch = strchr(line, '=');
// Ignore ivalid formats // Ignore invalid formats
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue; if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
// Separate the string // Separate the string
*pch = '\0'; *pch = '\0';
@@ -367,7 +312,7 @@ int resetprop_main(int argc, char *argv[]) {
goto usage; goto usage;
} }
case 'v': case 'v':
verbose = 1; prop_verbose = 1;
continue; continue;
case 'p': case 'p':
persist = 1; persist = 1;
@@ -390,7 +335,7 @@ int resetprop_main(int argc, char *argv[]) {
switch (argc) { switch (argc) {
case 0: case 0:
print_all_props(persist); print_props(persist);
return 0; return 0;
case 1: case 1:
prop = getprop2(argv[0], persist); prop = getprop2(argv[0], persist);

View File

@@ -153,7 +153,7 @@ class prop_area {
} }
const prop_info* find(const char* name); const prop_info* find(const char* name);
bool del(const char *name); // resetprop add bool del(const char *name); /* resetprop add */
bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen);
bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie); bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
@@ -184,7 +184,7 @@ class prop_area {
const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen, const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen,
const char* value, uint32_t valuelen, bool alloc_if_needed); const char* value, uint32_t valuelen, bool alloc_if_needed);
bool find_property_and_del(prop_bt *const trie, const char *name); // resetprop add bool find_property_and_del(prop_bt *const trie, const char *name); /* resetprop add */
bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie), bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie),
void* cookie); void* cookie);
@@ -283,7 +283,8 @@ static prop_area* map_prop_area_rw(const char* filename, const char* context,
return pa; return pa;
} }
static prop_area* map_fd_ro(const int fd) { // resetprop: map the memory as rw
static prop_area* map_fd_rw(const int fd) {
struct stat fd_stat; struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) { if (fstat(fd, &fd_stat) < 0) {
return nullptr; return nullptr;
@@ -298,7 +299,7 @@ static prop_area* map_fd_ro(const int fd) {
pa_size = fd_stat.st_size; pa_size = fd_stat.st_size;
pa_data_size = pa_size - sizeof(prop_area); pa_data_size = pa_size - sizeof(prop_area);
void* const map_result = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // resetprop: add PROT_WRITE void* const map_result = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); /* resetprop: add PROT_WRITE */
if (map_result == MAP_FAILED) { if (map_result == MAP_FAILED) {
return nullptr; return nullptr;
} }
@@ -313,10 +314,10 @@ static prop_area* map_fd_ro(const int fd) {
} }
static prop_area* map_prop_area(const char* filename) { static prop_area* map_prop_area(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // resetprop: O_RDONLY -> O_RDWR int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); /* resetprop: O_RDONLY -> O_RDWR */
if (fd == -1) return nullptr; if (fd == -1) return nullptr;
prop_area* map_result = map_fd_ro(fd); prop_area* map_result = map_fd_rw(fd);
close(fd); close(fd);
return map_result; return map_result;
@@ -530,7 +531,7 @@ bool prop_area::find_property_and_del(prop_bt* const trie, const char* name) {
uint_least32_t prop_offset = atomic_load_explicit(&current->prop, memory_order_relaxed); uint_least32_t prop_offset = atomic_load_explicit(&current->prop, memory_order_relaxed);
if (prop_offset != 0) { if (prop_offset != 0) {
atomic_store_explicit(&current->prop, 0, memory_order_release); // resetprop: nullify the offset to delete the prop atomic_store_explicit(&current->prop, 0, memory_order_release);
return true; return true;
} else { } else {
return false; return false;
@@ -1116,16 +1117,16 @@ static bool initialize_properties() {
if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) { if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
return false; return false;
} }
if (!initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts")) { // Don't check for failure here, so we always have a sane list of properties.
return false; // E.g. In case of recovery, the vendor partition will not have mounted and we
} // still need the system / platform properties to function.
initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts") ||
initialize_properties_from_file("/vendor/etc/selinux/vendor_property_contexts");
} else { } else {
if (!initialize_properties_from_file("/plat_property_contexts")) { if (!initialize_properties_from_file("/plat_property_contexts")) {
return false; return false;
} }
if (!initialize_properties_from_file("/nonplat_property_contexts")) { initialize_properties_from_file("/nonplat_property_contexts");
return false;
}
} }
return true; return true;

View File

@@ -15,6 +15,7 @@
#include <selinux/selinux.h> #include <selinux/selinux.h>
#endif #endif
#include "magisk.h"
#include "utils.h" #include "utils.h"
char **excl_list = NULL; char **excl_list = NULL;
@@ -165,7 +166,6 @@ void cp_afc(const char *source, const char *destination) {
xmkdirs(destination, a.st.st_mode & 0777); xmkdirs(destination, a.st.st_mode & 0777);
src = xopen(source, O_RDONLY | O_CLOEXEC); src = xopen(source, O_RDONLY | O_CLOEXEC);
dest = xopen(destination, O_RDONLY | O_CLOEXEC); dest = xopen(destination, O_RDONLY | O_CLOEXEC);
fsetattr(dest, &a);
clone_dir(src, dest); clone_dir(src, dest);
close(src); close(src);
close(dest); close(dest);
@@ -175,16 +175,15 @@ void cp_afc(const char *source, const char *destination) {
src = xopen(source, O_RDONLY); src = xopen(source, O_RDONLY);
dest = xopen(destination, O_WRONLY | O_CREAT | O_TRUNC); dest = xopen(destination, O_WRONLY | O_CREAT | O_TRUNC);
xsendfile(dest, src, NULL, a.st.st_size); xsendfile(dest, src, NULL, a.st.st_size);
fsetattr(src, &a);
close(src); close(src);
close(dest); close(dest);
} else if (S_ISLNK(a.st.st_mode)) { } else if (S_ISLNK(a.st.st_mode)) {
char buf[PATH_MAX]; char buf[PATH_MAX];
xreadlink(source, buf, sizeof(buf)); xreadlink(source, buf, sizeof(buf));
xsymlink(buf, destination); xsymlink(buf, destination);
setattr(destination, &a);
} }
} }
setattr(destination, &a);
} }
void clone_dir(int src, int dest) { void clone_dir(int src, int dest) {
@@ -341,18 +340,23 @@ void fclone_attr(const int sourcefd, const int targetfd) {
#ifdef SELINUX #ifdef SELINUX
#include "magiskpolicy.h"
#define UNLABEL_CON "u:object_r:unlabeled:s0" #define UNLABEL_CON "u:object_r:unlabeled:s0"
#define SYSTEM_CON "u:object_r:system_file:s0" #define SYSTEM_CON "u:object_r:system_file:s0"
#define ADB_CON "u:object_r:adb_data_file:s0"
#define MAGISK_CON "u:object_r:"SEPOL_FILE_DOMAIN":s0"
void restorecon(int dirfd, int force) { static void restore_syscon(int dirfd) {
struct dirent *entry; struct dirent *entry;
DIR *dir; DIR *dir;
int fd;
char path[PATH_MAX], *con; char path[PATH_MAX], *con;
fd_getpath(dirfd, path, sizeof(path)); fd_getpath(dirfd, path, sizeof(path));
lgetfilecon(path, &con); size_t len = strlen(path);
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0) getfilecon(path, &con);
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON); lsetfilecon(path, SYSTEM_CON);
freecon(con); freecon(con);
@@ -361,25 +365,70 @@ void restorecon(int dirfd, int force) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue; continue;
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restorecon(fd, force); restore_syscon(fd);
close(fd);
} else { } else {
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC); path[len] = '/';
fd_getpath(fd, path, sizeof(path)); strcpy(path + len + 1, entry->d_name);
lgetfilecon(path, &con); lgetfilecon(path, &con);
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0) if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON); lsetfilecon(path, SYSTEM_CON);
freecon(con); freecon(con);
path[len] = '\0';
} }
close(fd);
} }
} }
static void restore_magiskcon(int dirfd) {
struct dirent *entry;
DIR *dir;
char path[PATH_MAX];
fd_getpath(dirfd, path, sizeof(path));
size_t len = strlen(path);
lsetfilecon(path, MAGISK_CON);
lchown(path, 0, 0);
dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (entry->d_type == DT_DIR) {
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restore_magiskcon(fd);
close(fd);
} else {
path[len] = '/';
strcpy(path + len + 1, entry->d_name);
lsetfilecon(path, MAGISK_CON);
lchown(path, 0, 0);
path[len] = '\0';
}
}
}
void restorecon() {
int fd;
fd = xopen(SELINUX_CONTEXT, O_WRONLY | O_CLOEXEC);
if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0) {
lsetfilecon(SECURE_DIR, ADB_CON);
}
close(fd);
fd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
restore_syscon(fd);
close(fd);
fd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
restore_magiskcon(fd);
close(fd);
}
#endif // SELINUX #endif // SELINUX
static int _mmap(int rw, const char *filename, void **buf, size_t *size) { static int _mmap(int rw, const char *filename, void **buf, size_t *size) {
struct stat st; struct stat st;
int fd = xopen(filename, rw ? O_RDWR : O_RDONLY); int fd = xopen(filename, (rw ? O_RDWR : O_RDONLY) | O_CLOEXEC);
fstat(fd, &st); fstat(fd, &st);
if (S_ISBLK(st.st_mode)) if (S_ISBLK(st.st_mode))
ioctl(fd, BLKGETSIZE64, size); ioctl(fd, BLKGETSIZE64, size);
@@ -407,7 +456,7 @@ void fd_full_read(int fd, void **buf, size_t *size) {
} }
void full_read(const char *filename, void **buf, size_t *size) { void full_read(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDONLY); int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
*buf = NULL; *buf = NULL;
*size = 0; *size = 0;
@@ -418,7 +467,7 @@ void full_read(const char *filename, void **buf, size_t *size) {
} }
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) { void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
int fd = xopenat(dirfd, filename, O_RDONLY); int fd = xopenat(dirfd, filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
*buf = NULL; *buf = NULL;
*size = 0; *size = 0;

View File

@@ -8,167 +8,196 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/statvfs.h>
#include <sys/sysmacros.h>
#include <linux/loop.h> #include <linux/loop.h>
#include "magisk.h" #include "magisk.h"
#include "utils.h" #include "utils.h"
static int e2fsck(const char *img) { #define round_size(a) ((((a) / 32) + 2) * 32)
// Check and repair ext4 image #define SOURCE_TMP "/dev/.img_src"
char buffer[128]; #define TARGET_TMP "/dev/.img_tgt"
int pid, fd = -1; #define MERGE_TMP "/dev/.img_mrg"
pid = exec_command(1, &fd, NULL, "e2fsck", "-yf", img, NULL);
if (pid < 0) struct fs_info {
return 1; unsigned size;
while (fdgets(buffer, sizeof(buffer), fd)) unsigned free;
LOGD("magisk_img: %s", buffer); unsigned used;
waitpid(pid, NULL, 0); };
close(fd);
return 0;
}
static char *loopsetup(const char *img) { static char *loopsetup(const char *img) {
char device[20]; char device[32];
struct loop_info64 info; struct loop_info64 info;
int i, lfd, ffd; int lfd = -1, ffd;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
// First get an empty loop device if (access(BLOCKDIR, F_OK) == 0) {
for (i = 0; i <= 7; ++i) { for (int i = 8; i < 100; ++i) {
sprintf(device, "/dev/block/loop%d", i); sprintf(device, BLOCKDIR "/loop%02d", i);
lfd = xopen(device, O_RDWR); if (access(device, F_OK) != 0)
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1) mknod(device, S_IFBLK | 0600, makedev(7, i * 8));
break; lfd = open(device, O_RDWR);
close(lfd); if (lfd < 0) /* Kernel does not support this */
break;
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
break;
close(lfd);
lfd = -1;
}
} }
if (i == 8) return NULL; // Fallback to existing loop in dev, but in reverse order
if (lfd < 0) {
for (int i = 7; i >= 0; --i) {
sprintf(device, "/dev/block/loop%d", i);
lfd = xopen(device, O_RDWR);
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
break;
close(lfd);
lfd = -1;
}
}
if (lfd < 0)
return NULL;
ffd = xopen(img, O_RDWR); ffd = xopen(img, O_RDWR);
if (ioctl(lfd, LOOP_SET_FD, ffd) == -1) if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
return NULL; return NULL;
strcpy((char *) info.lo_file_name, img); strncpy((char *) info.lo_file_name, img, sizeof(info.lo_file_name));
ioctl(lfd, LOOP_SET_STATUS64, &info); ioctl(lfd, LOOP_SET_STATUS64, &info);
close(lfd); close(lfd);
close(ffd); close(ffd);
return strdup(device); return strdup(device);
} }
static void check_filesystem(struct fs_info *info, const char *img, const char *mount) {
struct stat st;
struct statvfs vfs;
stat(img, &st);
statvfs(mount, &vfs);
info->size = st.st_size / 1048576;
info->free = vfs.f_bfree * vfs.f_frsize / 1048576;
info->used = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize / 1048576;
}
static void usage() {
fprintf(stderr,
"ImgTool v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - EXT4 Image Tools\n"
"\n"
"Usage: imgtool <action> [args...]\n"
"\n"
"Actions:\n"
" create IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
" resize IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
" mount IMG PATH mount IMG to PATH and prints the loop device\n"
" umount PATH LOOP unmount PATH and delete LOOP device\n"
" merge SRC TGT merge SRC to TGT\n"
" trim IMG trim IMG to save space\n"
);
exit(1);
}
int imgtool_main(int argc, char *argv[]) {
if (argc < 2)
usage();
if (strcmp(argv[1], "create") == 0) {
if (argc < 4)
usage();
return create_img(argv[2], atoi(argv[3]));
} else if (strcmp(argv[1], "resize") == 0) {
if (argc < 4)
usage();
return resize_img(argv[2], atoi(argv[3]));
} else if (strcmp(argv[1], "mount") == 0) {
if (argc < 4)
usage();
// Redirect 1 > /dev/null
int fd = open("/dev/null", O_WRONLY);
int out = dup(STDOUT_FILENO);
xdup2(fd, STDOUT_FILENO);
char *loop = mount_image(argv[2], argv[3]);
// Restore stdin
xdup2(out, STDOUT_FILENO);
close(fd);
close(out);
if (loop == NULL) {
fprintf(stderr, "Cannot mount image!\n");
return 1;
} else {
printf("%s\n", loop);
free(loop);
return 0;
}
} else if (strcmp(argv[1], "umount") == 0) {
if (argc < 4)
usage();
umount_image(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "merge") == 0) {
if (argc < 4)
usage();
return merge_img(argv[2], argv[3]);
} else if (strcmp(argv[1], "trim") == 0) {
if (argc < 3)
usage();
xmkdir(SOURCE_TMP, 0755);
char *loop = mount_image(argv[2], SOURCE_TMP);
int ret = trim_img(argv[2], SOURCE_TMP, loop);
umount_image(SOURCE_TMP, loop);
rmdir(SOURCE_TMP);
free(loop);
return ret;
}
usage();
return 1;
}
int create_img(const char *img, int size) { int create_img(const char *img, int size) {
if (size == 128) /* WTF...? */ if (size == 128) /* WTF...? */
size = 132; size = 132;
unlink(img); unlink(img);
LOGI("Create %s with size %dM\n", img, size); LOGI("Create %s with size %dM\n", img, size);
int ret; char size_str[16];
snprintf(size_str, sizeof(size_str), "%dM", size);
char buffer[16]; if (access("/system/bin/make_ext4fs", X_OK) == 0)
snprintf(buffer, sizeof(buffer), "%dM", size); return exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL);
ret = exec_command_sync("make_ext4fs", "-l", buffer, img, NULL); else if (access("/system/bin/mke2fs", X_OK) == 0)
if (ret < 0) // On Android P there is no make_ext4fs, use mke2fs
return exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
else
return 1; return 1;
return ret;
}
int get_img_size(const char *img, int *used, int *total) {
if (access(img, R_OK) == -1)
return 1;
char buffer[PATH_MAX];
int pid, fd = -1, status = 1;
pid = exec_command(1, &fd, NULL, "e2fsck", "-n", img, NULL);
if (pid < 0)
return 1;
while (fdgets(buffer, sizeof(buffer), fd)) {
if (strstr(buffer, img)) {
char *tok = strtok(buffer, ",");
while(tok != NULL) {
if (strstr(tok, "blocks")) {
status = 0;
break;
}
tok = strtok(NULL, ",");
}
if (status) continue;
sscanf(tok, "%d/%d", used, total);
*used = (*used + 255) / 256;
*total = (*total + 128) / 256;
break;
}
}
close(fd);
waitpid(pid, NULL, 0);
return 0;
} }
int resize_img(const char *img, int size) { int resize_img(const char *img, int size) {
LOGI("Resize %s to %dM\n", img, size); LOGI("Resize %s to %dM\n", img, size);
if (e2fsck(img)) exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
return 1; char ss[16];
char buffer[128]; snprintf(ss, sizeof(ss), "%dM", size);
int pid, fd = -1, used, total; return exec_command_sync("/system/bin/resize2fs", img, ss, NULL);
snprintf(buffer, sizeof(buffer), "%dM", size);
pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL);
if (pid < 0)
return 1;
while (fdgets(buffer, sizeof(buffer), fd))
LOGD("magisk_img: %s", buffer);
close(fd);
waitpid(pid, NULL, 0);
// Double check our image size
get_img_size(img, &used, &total);
if (total != size) {
// Sammy crap occurs or resize2fs failed, lets create a new image!
char *dir = dirname(img);
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
create_img(buffer, size);
char *s_loop, *t_loop;
s_loop = mount_image(img, SOURCE_TMP);
if (s_loop == NULL) return 1;
t_loop = mount_image(buffer, TARGET_TMP);
if (t_loop == NULL) return 1;
cp_afc(SOURCE_TMP, TARGET_TMP);
umount_image(SOURCE_TMP, s_loop);
umount_image(TARGET_TMP, t_loop);
rmdir(SOURCE_TMP);
rmdir(TARGET_TMP);
free(s_loop);
free(t_loop);
rename(buffer, img);
}
return 0;
} }
char *mount_image(const char *img, const char *target) { char *mount_image(const char *img, const char *target) {
if (access(img, F_OK) == -1) if (access(img, F_OK) == -1 || access(target, F_OK) == -1)
return NULL; return NULL;
if (access(target, F_OK) == -1) { exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
if (xmkdirs(target, 0755) == -1) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdirs(target, 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
}
if (e2fsck(img))
return NULL;
char *device = loopsetup(img); char *device = loopsetup(img);
if (device) if (device)
xmount(device, target, "ext4", 0, NULL); xmount(device, target, "ext4", 0, NULL);
return device; return device;
} }
void umount_image(const char *target, const char *device) { int umount_image(const char *target, const char *device) {
xumount(target); int ret = 0;
ret |= xumount(target);
int fd = xopen(device, O_RDWR); int fd = xopen(device, O_RDWR);
ioctl(fd, LOOP_CLR_FD); ret |= ioctl(fd, LOOP_CLR_FD);
close(fd); close(fd);
return ret;
} }
int merge_img(const char *source, const char *target) { int merge_img(const char *source, const char *target) {
if (access(source, F_OK) == -1) if (access(source, F_OK) == -1)
return 0; return 0;
LOGI("* Merging %s -> %s\n", source, target);
if (access(target, F_OK) == -1) { if (access(target, F_OK) == -1) {
LOGI("* Move %s -> %s\n", source, target);
if (rename(source, target) < 0) { if (rename(source, target) < 0) {
// Copy and remove // Copy and remove
int tgt = creat(target, 0644); int tgt = creat(target, 0644);
@@ -181,24 +210,22 @@ int merge_img(const char *source, const char *target) {
return 0; return 0;
} }
char buffer[PATH_MAX]; char buf[PATH_MAX];
// resize target to worst case
int s_used, s_total, t_used, t_total, n_total;
get_img_size(source, &s_used, &s_total);
get_img_size(target, &t_used, &t_total);
n_total = round_size(s_used + t_used);
if (n_total > t_total)
resize_img(target, n_total);
xmkdir(SOURCE_TMP, 0755); xmkdir(SOURCE_TMP, 0755);
xmkdir(TARGET_TMP, 0755); xmkdir(TARGET_TMP, 0755);
char *s_loop, *t_loop; char *s_loop, *t_loop, *m_loop;
s_loop = mount_image(source, SOURCE_TMP); s_loop = mount_image(source, SOURCE_TMP);
if (s_loop == NULL) return 1; if (s_loop == NULL)
return 1;
t_loop = mount_image(target, TARGET_TMP); t_loop = mount_image(target, TARGET_TMP);
if (t_loop == NULL) return 1; if (t_loop == NULL)
return 1;
snprintf(buf, sizeof(buf), "%s/%s", SOURCE_TMP, "lost+found");
rm_rf(buf);
snprintf(buf, sizeof(buf), "%s/%s", TARGET_TMP, "lost+found");
rm_rf(buf);
DIR *dir; DIR *dir;
struct dirent *entry; struct dirent *entry;
if (!(dir = xopendir(SOURCE_TMP))) if (!(dir = xopendir(SOURCE_TMP)))
@@ -207,37 +234,60 @@ int merge_img(const char *source, const char *target) {
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
if (strcmp(entry->d_name, ".") == 0 || if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, "..") == 0 ||
strcmp(entry->d_name, ".core") == 0 || strcmp(entry->d_name, ".core") == 0)
strcmp(entry->d_name, "lost+found") == 0)
continue; continue;
// Cleanup old module if exists // Cleanup old module if exists
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name); snprintf(buf, sizeof(buf), "%s/%s", TARGET_TMP, entry->d_name);
if (access(buffer, F_OK) == 0) { if (access(buf, F_OK) == 0)
LOGI("Upgrade module: %s\n", entry->d_name); rm_rf(buf);
rm_rf(buffer); LOGI("Upgrade/New module: %s\n", entry->d_name);
} else {
LOGI("New module: %s\n", entry->d_name);
}
} }
} }
closedir(dir); closedir(dir);
cp_afc(SOURCE_TMP, TARGET_TMP);
struct fs_info src, tgt;
check_filesystem(&src, source, SOURCE_TMP);
check_filesystem(&tgt, target, TARGET_TMP);
snprintf(buf, sizeof(buf), "%s/tmp.img", dirname(target));
create_img(buf, round_size(src.used + tgt.used));
xmkdir(MERGE_TMP, 0755);
m_loop = mount_image(buf, MERGE_TMP);
if (m_loop == NULL)
return 1;
LOGI("* Merging %s + %s -> %s", source, target, buf);
cp_afc(TARGET_TMP, MERGE_TMP);
cp_afc(SOURCE_TMP, MERGE_TMP);
// Unmount all loop devices // Unmount all loop devices
umount_image(SOURCE_TMP, s_loop); umount_image(SOURCE_TMP, s_loop);
umount_image(TARGET_TMP, t_loop); umount_image(TARGET_TMP, t_loop);
umount_image(MERGE_TMP, m_loop);
rmdir(SOURCE_TMP); rmdir(SOURCE_TMP);
rmdir(TARGET_TMP); rmdir(TARGET_TMP);
rmdir(MERGE_TMP);
free(s_loop); free(s_loop);
free(t_loop); free(t_loop);
free(m_loop);
// Cleanup
unlink(source); unlink(source);
LOGI("* Move %s -> %s", buf, target);
rename(buf, target);
return 0; return 0;
} }
void trim_img(const char *img) { int trim_img(const char *img, const char *mount, char *loop) {
int used, total, new_size; struct fs_info info;
get_img_size(img, &used, &total); check_filesystem(&info, img, mount);
new_size = round_size(used); int new_size = round_size(info.used);
if (new_size != total) if (info.size > new_size) {
umount_image(mount, loop);
resize_img(img, new_size); resize_img(img, new_size);
char *loop2 = mount_image(img, mount);
if (loop2 == NULL)
return 1;
strcpy(loop, loop2);
free(loop2);
}
return 0;
} }

View File

@@ -87,6 +87,8 @@ int check_data() {
/* All the string should be freed manually!! */ /* All the string should be freed manually!! */
int file_to_vector(const char* filename, struct vector *v) { int file_to_vector(const char* filename, struct vector *v) {
if (access(filename, R_OK) != 0)
return 1;
char *line = NULL; char *line = NULL;
size_t len = 0; size_t len = 0;
ssize_t read; ssize_t read;
@@ -168,22 +170,38 @@ void ps(void (*func)(int)) {
static void (*ps_filter_cb)(int); static void (*ps_filter_cb)(int);
static const char *ps_filter_pattern; static const char *ps_filter_pattern;
static void proc_name_filter(int pid) { static void proc_name_filter(int pid) {
char buf[64]; char buf[128];
int fd; FILE *f;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); sprintf(buf, "/proc/%d/comm", pid);
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1) if ((f = fopen(buf, "r"))) {
fgets(buf, sizeof(buf), f);
if (strcmp(buf, ps_filter_pattern) == 0)
goto run_cb;
} else {
// The PID is already killed
return; return;
if (fdgets(buf, sizeof(buf), fd) == 0) {
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
close(fd);
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return;
fdgets(buf, sizeof(buf), fd);
} }
if (strcmp(buf, ps_filter_pattern) == 0) { fclose(f);
ps_filter_cb(pid);
} sprintf(buf, "/proc/%d/cmdline", pid);
close(fd); f = fopen(buf, "r");
fgets(buf, sizeof(buf), f);
if (strcmp(basename(buf), ps_filter_pattern) == 0)
goto run_cb;
fclose(f);
sprintf(buf, "/proc/%d/exe", pid);
if (access(buf, F_OK) != 0)
return;
xreadlink(buf, buf, sizeof(buf));
if (strcmp(basename(buf), ps_filter_pattern) == 0)
goto run_cb;
return;
run_cb:
ps_filter_cb(pid);
fclose(f);
return;
} }
/* Call func with process name filtered with pattern */ /* Call func with process name filtered with pattern */
@@ -227,9 +245,9 @@ void setup_sighandlers(void (*handler)(int)) {
fd == NULL -> Ignore output fd == NULL -> Ignore output
*fd < 0 -> Open pipe and set *fd to the read end *fd < 0 -> Open pipe and set *fd to the read end
*fd >= 0 -> STDOUT (or STDERR) will be redirected to *fd *fd >= 0 -> STDOUT (or STDERR) will be redirected to *fd
*cb -> A callback function which runs after fork *setenv -> A callback function which sets up a vector of environment variables
*/ */
static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, va_list argv) { int exec_array(int err, int *fd, void (*setenv)(struct vector *), char *const *argv) {
int pipefd[2], writeEnd = -1; int pipefd[2], writeEnd = -1;
if (fd) { if (fd) {
@@ -242,20 +260,12 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
} }
} }
// Collect va_list into vector
struct vector args;
vec_init(&args);
vec_push_back(&args, strdup(argv0));
for (void *arg = va_arg(argv, void*); arg; arg = va_arg(argv, void*))
vec_push_back(&args, strdup(arg));
vec_push_back(&args, NULL);
// Setup environment // Setup environment
char *const *envp; char *const *envp;
struct vector env; struct vector env;
vec_init(&env); vec_init(&env);
if (setupenv) { if (setenv) {
setupenv(&env); setenv(&env);
envp = (char **) vec_entry(&env); envp = (char **) vec_entry(&env);
} else { } else {
extern char **environ; extern char **environ;
@@ -269,21 +279,34 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
*fd = pipefd[0]; *fd = pipefd[0];
close(pipefd[1]); close(pipefd[1]);
} }
vec_deep_destroy(&args);
vec_deep_destroy(&env); vec_deep_destroy(&env);
return pid; return pid;
} }
if (fd) { if (fd) {
xdup2(writeEnd, STDOUT_FILENO); xdup2(writeEnd, STDOUT_FILENO);
if (err) xdup2(writeEnd, STDERR_FILENO); if (err)
xdup2(writeEnd, STDERR_FILENO);
} }
execvpe(argv0, (char **) vec_entry(&args), envp); execvpe(argv[0], argv, envp);
PLOGE("execvpe"); PLOGE("execvpe %s", argv[0]);
return -1; return -1;
} }
static int v_exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, va_list argv) {
// Collect va_list into vector
struct vector args;
vec_init(&args);
vec_push_back(&args, strdup(argv0));
for (void *arg = va_arg(argv, void*); arg; arg = va_arg(argv, void*))
vec_push_back(&args, strdup(arg));
vec_push_back(&args, NULL);
int pid = exec_array(err, fd, setenv, (char **) vec_entry(&args));
vec_deep_destroy(&args);
return pid;
}
int exec_command_sync(char *const argv0, ...) { int exec_command_sync(char *const argv0, ...) {
va_list argv; va_list argv;
va_start(argv, argv0); va_start(argv, argv0);
@@ -296,10 +319,10 @@ int exec_command_sync(char *const argv0, ...) {
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...) { int exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, ...) {
va_list argv; va_list argv;
va_start(argv, argv0); va_start(argv, argv0);
int pid = v_exec_command(err, fd, setupenv, argv0, argv); int pid = v_exec_command(err, fd, setenv, argv0, argv);
va_end(argv); va_end(argv);
return pid; return pid;
} }

View File

@@ -59,15 +59,18 @@ void patch_init_rc(void **buf, size_t *size) {
} }
int patch_verity(void **buf, uint32_t *size, int patch) { int patch_verity(void **buf, uint32_t *size, int patch) {
int skip, src_size = *size; int skip, src_size = *size, found = 0;
char *src = *buf, *patched = patch ? xcalloc(src_size, 1) : NULL; char *src = *buf, *patched = patch ? xcalloc(src_size, 1) : NULL;
for (int read = 0, write = 0; read < src_size; ++read, ++write) { for (int read = 0, write = 0; read < src_size; ++read, ++write) {
if ((skip = check_verity_pattern(src + read)) > 0) { if ((skip = check_verity_pattern(src + read)) > 0) {
if (!patch) if (patch) {
return 1; fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read); *size -= skip;
} else {
fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read);
}
read += skip; read += skip;
*size -= skip; found = 1;
} }
if (patch) if (patch)
patched[write] = src[read]; patched[write] = src[read];
@@ -76,7 +79,7 @@ int patch_verity(void **buf, uint32_t *size, int patch) {
free(*buf); free(*buf);
*buf = patched; *buf = patched;
} }
return 0; return found;
} }
void patch_encryption(void **buf, uint32_t *size) { void patch_encryption(void **buf, uint32_t *size) {

View File

@@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include "vector.h" #include "vector.h"
@@ -23,6 +24,15 @@ void vec_push_back(struct vector *v, void *p) {
++vec_size(v); ++vec_size(v);
} }
void vec_push_back_all(struct vector *v, void *p, ...) {
va_list argv;
va_start(argv, p);
vec_push_back(v, p);
for (void *arg = va_arg(argv, char*); arg; arg = va_arg(argv, char*))
vec_push_back(v, arg);
va_end(argv);
}
void *vec_pop_back(struct vector *v) { void *vec_pop_back(struct vector *v) {
void *ret = vec_entry(v)[vec_size(v) - 1]; void *ret = vec_entry(v)[vec_size(v) - 1];
--vec_size(v); --vec_size(v);
@@ -73,11 +83,9 @@ void vec_deep_destroy(struct vector *v) {
vec_destroy(v); vec_destroy(v);
} }
struct vector *vec_dup(struct vector *v) { void vec_dup(struct vector *v, struct vector *vv) {
struct vector *ret = malloc(sizeof(*ret)); vec_size(vv) = vec_size(v);
vec_size(ret) = vec_size(v); vec_cap(vv) = vec_cap(v);
vec_cap(ret) = vec_cap(v); vec_entry(vv) = malloc(sizeof(void*) * vec_cap(v));
vec_entry(v) = malloc(sizeof(void*) * vec_cap(ret)); memcpy(vec_entry(vv), vec_entry(v), sizeof(void*) * vec_cap(v));
memcpy(vec_entry(ret), vec_entry(v), sizeof(void*) * vec_cap(ret));
return ret;
} }

View File

@@ -9,60 +9,99 @@
########################################################################################## ##########################################################################################
. /tmp/backuptool.functions . /tmp/backuptool.functions
[ -z $backuptool_ab ] && backuptool_ab=false
main() { initialize() {
# Magisk binaries # This path should work in any cases
MAGISKBIN=/data/adb/magisk TMPDIR=/dev/tmp
mount /data 2>/dev/null mount /data 2>/dev/null
MAGISKBIN=/data/adb/magisk
if [ ! -d $MAGISKBIN ]; then if [ ! -d $MAGISKBIN ]; then
echo "! Cannot find Magisk binaries!" echo "! Cannot find Magisk binaries!"
exit 1 exit 1
fi fi
# Wait for post addon.d processes to finish
sleep 5
# Load utility functions # Load utility functions
. $MAGISKBIN/util_functions.sh . $MAGISKBIN/util_functions.sh
APK=/data/adb/magisk.apk
[ -f $APK ] || APK=/data/.magisk/magisk.apk
[ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk
}
show_logo() {
ui_print "************************" ui_print "************************"
ui_print "* Magisk v$MAGISK_VER addon.d" ui_print "* Magisk v$MAGISK_VER addon.d"
ui_print "************************" ui_print "************************"
}
mount_partitions detection() {
find_boot_image
find_dtbo_image
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
ui_print "- Target image: $BOOTIMAGE"
[ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
get_flags
}
api_level_arch_detect installation() {
[ -f $APK ] && eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
recovery_actions $BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
remove_system_su
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
ui_print "- Found boot image: $BOOTIMAGE"
SOURCEDMODE=true SOURCEDMODE=true
cd $MAGISKBIN cd $MAGISKBIN
# Source the boot patcher # Source the boot patcher
. $MAGISKBIN/boot_patch.sh "$BOOTIMAGE" . ./boot_patch.sh "$BOOTIMAGE"
flash_boot_image new-boot.img "$BOOTIMAGE" flash_boot_image new-boot.img "$BOOTIMAGE"
rm -f new-boot.img rm -f new-boot.img
if [ -f stock_boot* ]; then if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null rm -f /data/stock_boot* 2>/dev/null
mv stock_boot* /data $DATA && mv stock_boot* /data
fi
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi fi
cd / cd /
recovery_cleanup }
finalize() {
ui_print "- Done" ui_print "- Done"
exit 0 exit 0
} }
main_v1() {
# Wait for post addon.d processes to finish
sleep 5
recovery_actions
show_logo
mount_partitions
detection
installation
recovery_cleanup
finalize
}
main_v2() {
boot_actions
show_logo
mount_partitions
# Swap the slot
if [ ! -z $SLOT ]; then [ $SLOT = _a ] && SLOT=_b || SLOT=_a; fi
detection
installation
finalize
}
case "$1" in case "$1" in
backup) backup)
# Stub # Stub
@@ -80,9 +119,15 @@ case "$1" in
# Stub # Stub
;; ;;
post-restore) post-restore)
# Get the FD for ui_print initialize
OUTFD=`ps | grep -v grep | grep -oE "update(.*)" | cut -d" " -f3` if $backuptool_ab; then
# Run the main function in a parallel subshell # addon.d-v2
(main) & main_v2
else
OUTFD=
get_outfd
# Run in background, hack for addon.d-v1
(main_v1) &
fi
;; ;;
esac esac

View File

@@ -4,18 +4,25 @@
# Magisk Boot Image Patcher # Magisk Boot Image Patcher
# by topjohnwu # by topjohnwu
# #
# Usage: sh boot_patch.sh <bootimage>
#
# The following additional flags can be set in environment variables:
# KEEPVERITY, KEEPFORCEENCRYPT, HIGHCOMP
#
# This script should be placed in a directory with the following files: # This script should be placed in a directory with the following files:
# #
# File name type Description # File name Type Description
# #
# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter. # boot_patch.sh script A script to patch boot. Expect path to boot image as parameter.
# (this file) The script will use binaries and files in its same directory # (this file) The script will use binaries and files in its same directory
# to complete the patching process # to complete the patching process
# monogisk binary The monolithic binary to replace /init # util_functions.sh script A script which hosts all functions requires for this script
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk # to work properly
# , and patch the ramdisk for Magisk support # magiskinit binary The binary to replace /init, which has the magisk binary embedded
# chromeos folder This folder should store all the utilities and keys to sign # magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk,
# (optional) a chromeos device, used in the tablet Pixel C # and patch the ramdisk for Magisk support
# chromeos folder This folder should store all the utilities and keys to sign
# (optional) a chromeos device. Used for Pixel C
# #
# If the script is not running as root, then the input boot image should be a stock image # If the script is not running as root, then the input boot image should be a stock image
# or have a backup included in ramdisk internally, since we cannot access the stock boot # or have a backup included in ramdisk internally, since we cannot access the stock boot
@@ -27,51 +34,32 @@
########################################################################################## ##########################################################################################
# Pure bash dirname implementation # Pure bash dirname implementation
dirname_wrap() { getdir() {
case "$1" in case "$1" in
*/*) */*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
dir=${1%/*} *) echo "." ;;
[ -z $dir ] && echo "/" || echo $dir
;;
*)
echo "."
;;
esac esac
} }
# Pure bash basename implementation
basename_wrap() {
echo ${1##*/}
}
########################################################################################## ##########################################################################################
# Initialization # Initialization
########################################################################################## ##########################################################################################
if [ -z $SOURCEDMODE ]; then if [ -z $SOURCEDMODE ]; then
# Switch to the location of the script file # Switch to the location of the script file
cd "`dirname_wrap "${BASH_SOURCE:-$0}"`" cd "`getdir "${BASH_SOURCE:-$0}"`"
# Load utility functions # Load utility functions
. ./util_functions.sh . ./util_functions.sh
fi fi
BOOTIMAGE="$1" BOOTIMAGE="$1"
[ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!" [ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!"
# Presets # Flags
[ -z $KEEPVERITY ] && KEEPVERITY=false [ -z $KEEPVERITY ] && KEEPVERITY=false
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
[ -z $HIGHCOMP ] && HIGHCOMP=false [ -z $HIGHCOMP ] && HIGHCOMP=false
if [ -z $KEEPFORCEENCRYPT ]; then
if [ "`getprop ro.crypto.state`" = "encrypted" ]; then
KEEPFORCEENCRYPT=true
ui_print "- Encrypted data detected, keep forceencrypt"
else
KEEPFORCEENCRYPT=false
fi
fi
chmod -R 755 . chmod -R 755 .
# Extract magisk if doesn't exist # Extract magisk if doesn't exist
@@ -91,9 +79,7 @@ case $? in
abort "! Unable to unpack boot image" abort "! Unable to unpack boot image"
;; ;;
2 ) 2 )
ui_print "! Insufficient boot partition size detected"
HIGHCOMP=true HIGHCOMP=true
ui_print "- Enable high compression mode"
;; ;;
3 ) 3 )
ui_print "- ChromeOS boot image detected" ui_print "- ChromeOS boot image detected"
@@ -114,6 +100,7 @@ esac
# Test patch status and do restore, after this section, ramdisk.cpio.orig is guaranteed to exist # Test patch status and do restore, after this section, ramdisk.cpio.orig is guaranteed to exist
ui_print "- Checking ramdisk status" ui_print "- Checking ramdisk status"
MAGISK_PATCHED=false
./magiskboot --cpio ramdisk.cpio test ./magiskboot --cpio ramdisk.cpio test
case $? in case $? in
0 ) # Stock boot 0 ) # Stock boot
@@ -125,18 +112,32 @@ case $? in
cp -af ramdisk.cpio ramdisk.cpio.orig cp -af ramdisk.cpio ramdisk.cpio.orig
;; ;;
1 ) # Magisk patched 1 ) # Magisk patched
ui_print "- Magisk patched image detected" MAGISK_PATCHED=true
# Find SHA1 of stock boot image HIGHCOMP=false
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
./magiskboot --cpio ramdisk.cpio restore
cp -af ramdisk.cpio ramdisk.cpio.orig
;; ;;
2 ) # Other patched 2 ) # High compression mode
MAGISK_PATCHED=true
HIGHCOMP=true
;;
3 ) # Other patched
ui_print "! Boot image patched by other programs" ui_print "! Boot image patched by other programs"
abort "! Please restore stock boot image" abort "! Please restore stock boot image"
;; ;;
esac esac
if $MAGISK_PATCHED; then
ui_print "- Magisk patched image detected"
# Find SHA1 of stock boot image
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
./magiskboot --cpio ramdisk.cpio restore
cp -af ramdisk.cpio ramdisk.cpio.orig
fi
if $HIGHCOMP; then
ui_print "! Insufficient boot partition size detected"
ui_print "- Enable high compression mode"
fi
########################################################################################## ##########################################################################################
# Ramdisk patches # Ramdisk patches
########################################################################################## ##########################################################################################
@@ -144,7 +145,7 @@ esac
ui_print "- Patching ramdisk" ui_print "- Patching ramdisk"
./magiskboot --cpio ramdisk.cpio \ ./magiskboot --cpio ramdisk.cpio \
'add 750 init magiskinit' \ "add 750 init magiskinit" \
"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1" "magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1"
rm -f ramdisk.cpio.orig rm -f ramdisk.cpio.orig
@@ -153,8 +154,9 @@ rm -f ramdisk.cpio.orig
# Binary patches # Binary patches
########################################################################################## ##########################################################################################
if ! $KEEPVERITY && [ -f dtb ]; then if ! $KEEPVERITY; then
./magiskboot --dtb-patch dtb && ui_print "- Patching fstab in dtb to remove dm-verity" [ -f dtb ] && ./magiskboot --dtb-patch dtb && ui_print "- Removing dm(avb)-verity in dtb"
[ -f extra ] && ./magiskboot --dtb-patch extra && ui_print "- Removing dm(avb)-verity in extra-dtb"
fi fi
if [ -f kernel ]; then if [ -f kernel ]; then

View File

@@ -48,28 +48,23 @@ ui_print "************************"
is_mounted /data || mount /data || is_mounted /cache || mount /cache || abort "! Unable to mount partitions" is_mounted /data || mount /data || is_mounted /cache || mount /cache || abort "! Unable to mount partitions"
mount_partitions mount_partitions
# read override variables find_boot_image
getvar KEEPVERITY find_dtbo_image
getvar KEEPFORCEENCRYPT
getvar BOOTIMAGE
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image" get_flags
ui_print "- Found boot/ramdisk image: $BOOTIMAGE"
if [ ! -z $DTBOIMAGE ]; then [ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
ui_print "- Found dtbo image: $DTBOIMAGE" ui_print "- Target image: $BOOTIMAGE"
# Disable dtbo patch by default [ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
[ -z $KEEPVERITY ] && KEEPVERITY=true
fi
# Detect version and architecture # Detect version and architecture
api_level_arch_detect api_level_arch_detect
[ $API -lt 21 ] && abort "! Magisk is only for Lollipop 5.0+ (SDK 21+)" [ $API -lt 21 ] && abort "! Magisk is only for Lollipop and above (5.0+) (SDK 21+)"
ui_print "- Device platform: $ARCH" ui_print "- Device platform: $ARCH"
BINDIR=$INSTALLER/$ARCH BINDIR=$INSTALLER/$ARCH32
chmod -R 755 $CHROMEDIR $BINDIR chmod -R 755 $CHROMEDIR $BINDIR
# Check if system root is installed and remove # Check if system root is installed and remove
@@ -81,15 +76,11 @@ remove_system_su
ui_print "- Constructing environment" ui_print "- Constructing environment"
# Check if we can actually access the data (DE storage) check_data
DATA=false
if grep ' /data ' /proc/mounts | grep -vq 'tmpfs'; then
[ ! -d /data/adb ] && mkdir /data/adb
touch /data/adb/.write_test && rm /data/adb/.write_test && DATA=true
fi
if $DATA; then if $DATA; then
MAGISKBIN=/data/adb/magisk MAGISKBIN=/data/.magisk
$DATA_DE && MAGISKBIN=/data/adb/magisk
run_migrations run_migrations
else else
MAGISKBIN=/cache/data_bin MAGISKBIN=/cache/data_bin
@@ -105,7 +96,9 @@ chmod -R 755 $MAGISKBIN
if [ -d /system/addon.d ]; then if [ -d /system/addon.d ]; then
ui_print "- Adding addon.d survival script" ui_print "- Adding addon.d survival script"
mount -o rw,remount /system mount -o rw,remount /system
cp -af $INSTALLER/addon.d/99-magisk.sh /system/addon.d/99-magisk.sh echo "#!/sbin/sh" > /system/addon.d/99-magisk.sh
echo "# ADDOND_VERSION=2" >> /system/addon.d/99-magisk.sh
echo ". /data/adb/magisk/addon.d.sh" >> /system/addon.d/99-magisk.sh
chmod 755 /system/addon.d/99-magisk.sh chmod 755 /system/addon.d/99-magisk.sh
fi fi
@@ -122,7 +115,7 @@ SOURCEDMODE=true
cd $MAGISKBIN cd $MAGISKBIN
# Source the boot patcher # Source the boot patcher
. $COMMONDIR/boot_patch.sh "$BOOTIMAGE" . ./boot_patch.sh "$BOOTIMAGE"
flash_boot_image new-boot.img "$BOOTIMAGE" flash_boot_image new-boot.img "$BOOTIMAGE"
rm -f new-boot.img rm -f new-boot.img

View File

@@ -1,52 +1,75 @@
#!/system/bin/sh #MAGISK
########################################################################################## ##########################################################################################
# #
# Magisk Uninstaller # Magisk Uninstaller (used in recovery)
# by topjohnwu # by topjohnwu
# #
# This script can be placed in /cache/magisk_uninstaller.sh # This script will load the real uninstaller in a flashable zip
# The Magisk main binary will pick up the script, and uninstall itself, following a reboot
# This script can also be used in flashable zip with the uninstaller_loader.sh
#
# This script will try to do restoration with the following:
# 1-1. Find and restore the original stock boot image dump (OTA proof)
# 1-2. If 1-1 fails, restore ramdisk from the internal backup
# (ramdisk fully restored, not OTA friendly)
# 1-3. If 1-2 fails, it will remove added files in ramdisk, however modified files
# are remained modified, because we have no backups. By doing so, Magisk will
# not be started at boot, but this isn't actually 100% cleaned up
# 2. Remove all Magisk related files
# (The list is LARGE, most likely due to bad decision in early versions
# the latest versions has much less bloat to cleanup)
# #
########################################################################################## ##########################################################################################
[ -z $BOOTMODE ] && BOOTMODE=false ##########################################################################################
# Preparation
##########################################################################################
[ -d /data/adb/magisk ] && MAGISKBIN=/data/adb/magisk || MAGISKBIN=/data/magisk # This path should work in any cases
CHROMEDIR=$MAGISKBIN/chromeos TMPDIR=/dev/tmp
if [ ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then INSTALLER=$TMPDIR/install
echo "! Cannot find $MAGISKBIN" CHROMEDIR=$INSTALLER/chromeos
# Default permissions
umask 022
OUTFD=$2
ZIP=$3
if [ ! -f $INSTALLER/util_functions.sh ]; then
echo "! Unable to extract zip file!"
exit 1 exit 1
fi fi
if $BOOTMODE; then # Load utility functions
# Load utility functions . $INSTALLER/util_functions.sh
. $MAGISKBIN/util_functions.sh
BOOTMODE=true get_outfd
boot_actions
mount_partitions ui_print "************************"
fi ui_print " Magisk Uninstaller "
ui_print "************************"
is_mounted /data || mount /data || abort "! Unable to mount partitions"
is_mounted /cache || mount /cache 2>/dev/null
mount_partitions
api_level_arch_detect
ui_print "- Device platform: $ARCH"
MAGISKBIN=$INSTALLER/$ARCH32
mv $CHROMEDIR $MAGISKBIN
chmod -R 755 $MAGISKBIN
check_data
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
$BOOTMODE || recovery_actions
##########################################################################################
# Uninstall
##########################################################################################
find_boot_image
find_dtbo_image
[ -e $BOOTIMAGE ] || abort "! Unable to detect boot image"
ui_print "- Found boot/ramdisk image: $BOOTIMAGE"
[ -z $DTBOIMAGE ] || ui_print "- Found dtbo image: $DTBOIMAGE"
cd $MAGISKBIN cd $MAGISKBIN
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image" CHROMEOS=false
ui_print "- Found Boot Image: $BOOTIMAGE"
ui_print "- Unpacking boot image" ui_print "- Unpacking boot image"
./magiskboot --unpack "$BOOTIMAGE" ./magiskboot --unpack "$BOOTIMAGE"
CHROMEOS=false
case $? in case $? in
1 ) 1 )
@@ -58,7 +81,7 @@ case $? in
;; ;;
4 ) 4 )
ui_print "! Sony ELF32 format detected" ui_print "! Sony ELF32 format detected"
abort "! Please use BootBridge from @AdrianDC to flash Magisk" abort "! Please use BootBridge from @AdrianDC"
;; ;;
5 ) 5 )
ui_print "! Sony ELF64 format detected" ui_print "! Sony ELF64 format detected"
@@ -71,36 +94,64 @@ ui_print "- Checking ramdisk status"
case $? in case $? in
0 ) # Stock boot 0 ) # Stock boot
ui_print "- Stock boot image detected" ui_print "- Stock boot image detected"
abort "! Magisk is not installed!"
;; ;;
1 ) # Magisk patched 1|2 ) # Magisk patched
ui_print "- Magisk patched image detected" ui_print "- Magisk patched image detected"
./magisk --unlock-blocks 2>/dev/null
# Find SHA1 of stock boot image # Find SHA1 of stock boot image
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null` [ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
OK=false STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true STOCKDTBO=/data/stock_dtbo.img.gz
if ! $OK; then if [ -f $STOCKBOOT ]; then
ui_print "- Restoring stock boot image"
gzip -d < $STOCKBOOT | cat - /dev/zero > $BOOTIMAGE 2>/dev/null
if [ -f $DTBOIMAGE -a -f $STOCKDTBO ]; then
ui_print "- Restoring stock dtbo image"
gzip -d < $STOCKDTBO > $DTBOIMAGE
fi
else
ui_print "! Boot image backup unavailable" ui_print "! Boot image backup unavailable"
ui_print "- Restoring ramdisk with internal backup" ui_print "- Restoring ramdisk with internal backup"
./magiskboot --cpio ramdisk.cpio restore ./magiskboot --cpio ramdisk.cpio restore
./magiskboot --repack $BOOTIMAGE ./magiskboot --repack $BOOTIMAGE
# Sign chromeos boot # Sign chromeos boot
$CHROMEOS && sign_chromeos $CHROMEOS && sign_chromeos
flash_boot_image new-boot.img "$BOOTIMAGE" flash_boot_image new-boot.img $BOOTIMAGE
fi fi
;; ;;
2 ) # Other patched 3 ) # Other patched
ui_print "! Boot image patched by other programs" ui_print "! Boot image patched by other programs"
abort "! Cannot uninstall" abort "! Cannot uninstall"
;; ;;
esac esac
cd /
ui_print "- Removing Magisk files" ui_print "- Removing Magisk files"
rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \ rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/app/com.topjohnwu.magisk* \ /data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* 2>/dev/null
/data/user*/*/magisk.db /data/user*/*/com.topjohnwu.magisk /data/user*/*/.tmp.magisk.config \
/data/adb/*magisk* 2>/dev/null
$BOOTMODE && /system/bin/reboot if [ -f /system/addon.d/99-magisk.sh ]; then
mount -o rw,remount /system
rm -f /system/addon.d/99-magisk.sh
fi
# Remove persist props (for Android P+ using protobuf)
for prop in `./magisk resetprop -p | grep -E 'persist.*magisk' | grep -oE '^\[[a-zA-Z0-9.@:_-]+\]' | tr -d '[]'`; do
./magisk resetprop -p --delete $prop
done
cd /
if $BOOTMODE; then
ui_print "**********************************************"
ui_print "* Magisk Manager will uninstall itself, and *"
ui_print "* the device will reboot after a few seconds *"
ui_print "**********************************************"
(sleep 8; /system/bin/reboot)&
else
rm -rf /data/user*/*/*magisk* /data/app/*magisk*
recovery_cleanup
ui_print "- Done"
fi
rm -rf $TMPDIR
exit 0

View File

@@ -1,80 +0,0 @@
#!/sbin/sh
##########################################################################################
#
# Magisk Uninstaller (used in recovery)
# by topjohnwu
#
# This script will load the real uninstaller in a flashable zip
#
##########################################################################################
##########################################################################################
# Preparation
##########################################################################################
BOOTMODE=false
# This path should work in any cases
TMPDIR=/dev/tmp
INSTALLER=$TMPDIR/install
# Default permissions
umask 022
OUTFD=$2
ZIP=$3
if [ ! -f $INSTALLER/util_functions.sh ]; then
echo "! Unable to extract zip file!"
exit 1
fi
# Load utility functions
. $INSTALLER/util_functions.sh
get_outfd
##########################################################################################
# Main
##########################################################################################
ui_print "************************"
ui_print " Magisk Uninstaller "
ui_print "************************"
is_mounted /data || mount /data || abort "! Unable to mount partitions"
is_mounted /cache || mount /cache 2>/dev/null
mount_partitions
api_level_arch_detect
ui_print "- Device platform: $ARCH"
CHROMEDIR=$INSTALLER/chromeos
BINDIR=$INSTALLER/$ARCH
##########################################################################################
# Detection all done, start installing
##########################################################################################
if is_mounted /data; then
recovery_actions
# Save our stock boot image dump before removing it
mv $MAGISKBIN/stock_boot* /data 2>/dev/null
# Copy the binaries to /data/magisk, in case they do not exist
rm -rf $MAGISKBIN 2>/dev/null
mkdir -p $MAGISKBIN
cp -af $BINDIR/. $CHROMEDIR $TMPDIR/bin/busybox $INSTALLER/util_functions.sh $MAGISKBIN
chmod -R 755 $MAGISKBIN
# Run the actual uninstallation
. $INSTALLER/magisk_uninstaller.sh
recovery_cleanup
else
ui_print "! Data unavailable"
ui_print "! Placing uninstall script to /cache"
ui_print "! The device might reboot multiple times"
cp -af $INSTALLER/magisk_uninstaller.sh /cache/magisk_uninstaller.sh
ui_print "- Rebooting....."
sleep 5
reboot
fi
ui_print "- Done"
exit 0

View File

@@ -1,31 +1,40 @@
# EX_ARM, EX_X86, BB_ARM, and BB_X86 should be generated in build.py # EX_ARM, EX_X86, BB_ARM, and BB_X86 should be generated in build.py
dirname_wrap() { getdir() {
case "$1" in case "$1" in
*/*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;; */*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
*) echo "." ;; *) echo "." ;;
esac esac
} }
[ "$1" = "indep" ] && INDEP=true || INDEP=false extract_bb() {
$INDEP && TMPDIR="`dirname_wrap "${BASH_SOURCE:-$0}"`" || TMPDIR=/dev/tmp EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox
INSTALLER=$TMPDIR/install; BBDIR=$TMPDIR/bin touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox echo -ne $EX_ARM > $EXBIN
$INDEP || rm -rf $TMPDIR 2>/dev/null; if $EXBIN --test 2>/dev/null; then
mkdir -p $BBDIR 2>/dev/null echo $BB_ARM | $EXBIN > $BBBIN
touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN else
echo -ne $EX_ARM > $EXBIN echo -ne $EX_X86 > $EXBIN
if $EXBIN --test 2>/dev/null; then echo $BB_X86 | $EXBIN > $BBBIN
echo $BB_ARM | $EXBIN > $BBBIN fi
else rm $EXBIN
echo -ne $EX_X86 > $EXBIN }
echo $BB_X86 | $EXBIN > $BBBIN setup_bb() {
fi BBDIR=$TMPDIR/bin; mkdir -p $BBDIR 2>/dev/null
$BBBIN --install -s $TMPDIR/bin extract_bb
export PATH=$BBDIR:$PATH $BBBIN --install -s $BBDIR
if $INDEP; then export PATH=$BBDIR:$PATH
shift }
exec sh "$@" case "$1" in
else "extract")
mkdir -p $INSTALLER [ -z "$2" ] && BBDIR=. || BBDIR="$2"
unzip -o "$3" -d $INSTALLER extract_bb
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@ ;;
fi "indep")
TMPDIR="`getdir "${BASH_SOURCE:-$0}"`"; setup_bb
shift; exec sh "$@"
;;
*)
TMPDIR=/dev/tmp; rm -rf $TMPDIR 2>/dev/null; setup_bb
INSTALLER=$TMPDIR/install; mkdir -p $INSTALLER; unzip -o "$3" -d $INSTALLER >&2
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@
;;
esac

View File

@@ -8,7 +8,6 @@
########################################################################################## ##########################################################################################
#MAGISK_VERSION_STUB #MAGISK_VERSION_STUB
SCRIPT_VERSION=$MAGISK_VER_CODE
# Detect whether in boot mode # Detect whether in boot mode
ps | grep zygote | grep -v grep >/dev/null && BOOTMODE=true || BOOTMODE=false ps | grep zygote | grep -v grep >/dev/null && BOOTMODE=true || BOOTMODE=false
@@ -24,15 +23,11 @@ BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.top
BOOTSIGNED=false BOOTSIGNED=false
get_outfd() { get_outfd() {
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
if [ "$?" -eq "0" ]; then # We will have to manually find out OUTFD
OUTFD=0
for FD in `ls /proc/$$/fd`; do for FD in `ls /proc/$$/fd`; do
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null if readlink /proc/$$/fd/$FD | grep -q pipe; then
if [ "$?" -eq "0" ]; then if ps | grep -v grep | grep -q " 3 $FD "; then
ps | grep " 3 $FD " | grep -v grep >/dev/null
if [ "$?" -eq "0" ]; then
OUTFD=$FD OUTFD=$FD
break break
fi fi
@@ -45,49 +40,102 @@ ui_print() {
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD $BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
} }
toupper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}
find_block() {
for BLOCK in "$@"; do
DEVICE=`find /dev/block -type l -iname $BLOCK | head -n 1` 2>/dev/null
if [ ! -z $DEVICE ]; then
readlink -f $DEVICE
return 0
fi
done
# Fallback by parsing sysfs uevents
for uevent in /sys/dev/block/*/uevent; do
local DEVNAME=`grep_prop DEVNAME $uevent`
local PARTNAME=`grep_prop PARTNAME $uevent`
for p in "$@"; do
if [ "`toupper $p`" = "`toupper $PARTNAME`" ]; then
echo /dev/block/$DEVNAME
return 0
fi
done
done
return 1
}
mount_partitions() { mount_partitions() {
# Check A/B slot # Check A/B slot
SLOT=`getprop ro.boot.slot_suffix` SLOT=`grep_cmdline androidboot.slot_suffix`
if [ -z $SLOT ]; then if [ -z $SLOT ]; then
SLOT=_`getprop ro.boot.slot` SLOT=_`grep_cmdline androidboot.slot`
[ $SLOT = "_" ] && SLOT= [ $SLOT = "_" ] && SLOT=
fi fi
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
# Check the boot image to make sure the slot actually make sense
find_boot_image
find_dtbo_image
[ -z $SLOT ] || ui_print "- A/B partition detected, current slot: $SLOT"
ui_print "- Mounting /system, /vendor" ui_print "- Mounting /system, /vendor"
is_mounted /system || [ -f /system/build.prop ] || mount -o ro /system 2>/dev/null [ -f /system/build.prop ] || is_mounted /system || mount -o ro /system 2>/dev/null
if ! is_mounted /system && ! [ -f /system/build.prop ]; then if ! is_mounted /system && ! [ -f /system/build.prop ]; then
SYSTEMBLOCK=`find /dev/block -iname system$SLOT | head -n 1` SYSTEMBLOCK=`find_block system$SLOT`
mount -t ext4 -o ro $SYSTEMBLOCK /system mount -t ext4 -o ro $SYSTEMBLOCK /system
fi fi
is_mounted /system || [ -f /system/build.prop ] || abort "! Cannot mount /system" [ -f /system/build.prop ] || is_mounted /system || abort "! Cannot mount /system"
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SKIP_INITRAMFS=true || SKIP_INITRAMFS=false cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SYSTEM_ROOT=true || SYSTEM_ROOT=false
if [ -f /system/init.rc ]; then if [ -f /system/init ]; then
SKIP_INITRAMFS=true SYSTEM_ROOT=true
mkdir /system_root 2>/dev/null mkdir /system_root 2>/dev/null
mount --move /system /system_root mount --move /system /system_root
mount -o bind /system_root/system /system mount -o bind /system_root/system /system
fi fi
$SKIP_INITRAMFS && ui_print "- Device skip_initramfs detected" $SYSTEM_ROOT && ui_print "- Device using system_root_image"
if [ -L /system/vendor ]; then if [ -L /system/vendor ]; then
# Seperate /vendor partition # Seperate /vendor partition
is_mounted /vendor || mount -o ro /vendor 2>/dev/null is_mounted /vendor || mount -o ro /vendor 2>/dev/null
if ! is_mounted /vendor; then if ! is_mounted /vendor; then
VENDORBLOCK=`find /dev/block -iname vendor$SLOT | head -n 1` VENDORBLOCK=`find_block vendor$SLOT`
mount -t ext4 -o ro $VENDORBLOCK /vendor mount -t ext4 -o ro $VENDORBLOCK /vendor
fi fi
is_mounted /vendor || abort "! Cannot mount /vendor" is_mounted /vendor || abort "! Cannot mount /vendor"
fi fi
} }
get_flags() {
# override variables
getvar KEEPVERITY
getvar KEEPFORCEENCRYPT
HIGHCOMP=false
if [ -z $KEEPVERITY ]; then
KEEPVERITY=false
hardware=`grep_cmdline androidboot.hardware`
for hw in taimen walleye; do
if [ "$hw" = "$hardware" ]; then
KEEPVERITY=true
ui_print "- Device on whitelist, keep avb-verity"
break
fi
done
fi
if [ -z $KEEPFORCEENCRYPT ]; then
if [ "`getprop ro.crypto.state`" = "encrypted" ]; then
KEEPFORCEENCRYPT=true
ui_print "- Encrypted data detected, keep forceencrypt"
else
KEEPFORCEENCRYPT=false
fi
fi
}
grep_cmdline() {
local REGEX="s/^$1=//p"
sed -E 's/ +/\n/g' /proc/cmdline | sed -n "$REGEX" 2>/dev/null
}
grep_prop() { grep_prop() {
REGEX="s/^$1=//p" local REGEX="s/^$1=//p"
shift shift
FILES=$@ local FILES=$@
[ -z "$FILES" ] && FILES='/system/build.prop' [ -z "$FILES" ] && FILES='/system/build.prop'
sed -n "$REGEX" $FILES 2>/dev/null | head -n 1 sed -n "$REGEX" $FILES 2>/dev/null | head -n 1
} }
@@ -95,36 +143,10 @@ grep_prop() {
getvar() { getvar() {
local VARNAME=$1 local VARNAME=$1
local VALUE= local VALUE=
for DIR in /.backup /dev /data /cache /system; do VALUE=`grep_prop $VARNAME /.backup/.magisk /data/.magisk /cache/.magisk /system/.magisk`
VALUE=`grep_prop $VARNAME $DIR/.magisk`
[ ! -z $VALUE ] && break;
done
[ ! -z $VALUE ] && eval $VARNAME=\$VALUE [ ! -z $VALUE ] && eval $VARNAME=\$VALUE
} }
find_boot_image() {
BOOTIMAGE=
if [ ! -z $SLOT ]; then
BOOTIMAGE=`find /dev/block -iname boot$SLOT | head -n 1` 2>/dev/null
fi
if [ -z $BOOTIMAGE ]; then
# The slot info is incorrect...
SLOT=
for BLOCK in ramdisk boot_a kern-a android_boot kernel boot lnx bootimg; do
BOOTIMAGE=`find /dev/block -iname $BLOCK | head -n 1` 2>/dev/null
[ ! -z $BOOTIMAGE ] && break
done
fi
# Recovery fallback
if [ -z $BOOTIMAGE ]; then
for FSTAB in /etc/*fstab*; do
BOOTIMAGE=`grep -v '#' $FSTAB | grep -E '/boot[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
[ ! -z $BOOTIMAGE ] && break
done
fi
[ ! -z $BOOTIMAGE ] && BOOTIMAGE=`readlink -f $BOOTIMAGE`
}
run_migrations() { run_migrations() {
# Update the broken boot backup # Update the broken boot backup
if [ -f /data/stock_boot_.img.gz ]; then if [ -f /data/stock_boot_.img.gz ]; then
@@ -140,11 +162,9 @@ run_migrations() {
fi fi
# Move the stock backups # Move the stock backups
if [ -f /data/magisk/stock_boot* ]; then if [ -f /data/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/magisk/stock_boot* /data 2>/dev/null mv /data/magisk/stock_boot* /data 2>/dev/null
fi fi
if [ -f /data/adb/magisk/stock_boot* ]; then if [ -f /data/adb/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/adb/magisk/stock_boot* /data 2>/dev/null mv /data/adb/magisk/stock_boot* /data 2>/dev/null
fi fi
# Remove old dbs # Remove old dbs
@@ -152,6 +172,19 @@ run_migrations() {
[ -L /data/magisk.img ] || mv /data/magisk.img /data/adb/magisk.img 2>/dev/null [ -L /data/magisk.img ] || mv /data/magisk.img /data/adb/magisk.img 2>/dev/null
} }
find_boot_image() {
BOOTIMAGE=
if [ ! -z $SLOT ]; then
BOOTIMAGE=`find_block boot$SLOT ramdisk$SLOT`
else
BOOTIMAGE=`find_block boot_a kern-a android_boot kernel boot lnx bootimg`
fi
if [ -z $BOOTIMAGE ]; then
# Lets see what fstabs tells me
BOOTIMAGE=`grep -v '#' /etc/*fstab* | grep -E '/boot[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*' | head -n 1`
fi
}
flash_boot_image() { flash_boot_image() {
# Make sure all blocks are writable # Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null $MAGISKBIN/magisk --unlock-blocks 2>/dev/null
@@ -178,16 +211,15 @@ flash_boot_image() {
} }
find_dtbo_image() { find_dtbo_image() {
DTBOIMAGE=`find /dev/block -iname dtbo$SLOT | head -n 1` 2>/dev/null DTBOIMAGE=`find_block dtbo$SLOT`
[ ! -z $DTBOIMAGE ] && DTBOIMAGE=`readlink -f $DTBOIMAGE`
} }
patch_dtbo_image() { patch_dtbo_image() {
if [ ! -z $DTBOIMAGE ]; then if [ ! -z $DTBOIMAGE ]; then
if $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then if $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
ui_print "- Backing up stock dtbo image" ui_print "- Backing up stock DTBO image"
$MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz $MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
ui_print "- Patching fstab in dtbo to remove avb-verity" ui_print "- Patching DTBO to remove avb-verity"
$MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE $MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE
return 0 return 0
fi fi
@@ -195,27 +227,6 @@ patch_dtbo_image() {
return 1 return 1
} }
restore_imgs() {
STOCKBOOT=/data/stock_boot_${1}.img.gz
STOCKDTBO=/data/stock_dtbo.img.gz
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
find_dtbo_image
if [ ! -z "$DTBOIMAGE" -a -f "$STOCKDTBO" ]; then
ui_print "- Restoring stock dtbo image"
gzip -d < $STOCKDTBO | dd of=$DTBOIMAGE
fi
BOOTSIGNED=false
find_boot_image
if [ ! -z "$BOOTIMAGE" -a -f "$STOCKBOOT" ]; then
ui_print "- Restoring stock boot image"
gzip -d < $STOCKBOOT | cat - /dev/zero 2>/dev/null | dd of="$BOOTIMAGE" bs=4096 2>/dev/null
return 0
fi
return 1
}
sign_chromeos() { sign_chromeos() {
ui_print "- Signing ChromeOS boot image" ui_print "- Signing ChromeOS boot image"
@@ -229,8 +240,7 @@ sign_chromeos() {
} }
is_mounted() { is_mounted() {
TARGET="`readlink -f $1`" cat /proc/mounts | grep -q " `readlink -f $1` " 2>/dev/null
cat /proc/mounts | grep " $TARGET " >/dev/null
return $? return $?
} }
@@ -266,11 +276,39 @@ api_level_arch_detect() {
ABILONG=`grep_prop ro.product.cpu.abi` ABILONG=`grep_prop ro.product.cpu.abi`
ARCH=arm ARCH=arm
ARCH32=arm
IS64BIT=false IS64BIT=false
if [ "$ABI" = "x86" ]; then ARCH=x86; fi; if [ "$ABI" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; if [ "$ABI2" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi; if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; ARCH32=arm; IS64BIT=true; fi;
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi; if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; ARCH32=x86; IS64BIT=true; fi;
}
check_data() {
DATA=false
DATA_DE=false
if grep ' /data ' /proc/mounts | grep -vq 'tmpfs'; then
# Test if data is writable
touch /data/.rw && rm /data/.rw && DATA=true
# Test if DE storage is writable
$DATA && [ -d /data/adb ] && touch /data/adb/.rw && rm /data/adb/.rw && DATA_DE=true
fi
}
setup_bb() {
if [ -x /sbin/.core/busybox/busybox ]; then
# Make sure this path is in the front
echo $PATH | grep -q '^/sbin/.core/busybox' || export PATH=/sbin/.core/busybox:$PATH
elif [ -x $TMPDIR/bin/busybox ]; then
# Make sure this path is in the front
echo $PATH | grep -q "^$TMPDIR/bin" || export PATH=$TMPDIR/bin:$PATH
else
# Construct the PATH
mkdir -p $TMPDIR/bin
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
$MAGISKBIN/busybox --install -s $TMPDIR/bin
export PATH=$TMPDIR/bin:$PATH
fi
} }
boot_actions() { boot_actions() {
@@ -279,6 +317,7 @@ boot_actions() {
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
fi fi
MAGISKBIN=/sbin/.core/mirror/bin MAGISKBIN=/sbin/.core/mirror/bin
setup_bb
} }
recovery_actions() { recovery_actions() {
@@ -286,24 +325,21 @@ recovery_actions() {
mount -o bind /dev/urandom /dev/random mount -o bind /dev/urandom /dev/random
# Preserve environment varibles # Preserve environment varibles
OLD_PATH=$PATH OLD_PATH=$PATH
OLD_LD_PATH=$LD_LIBRARY_PATH setup_bb
if [ ! -d $TMPDIR/bin ]; then
# Add busybox to PATH
mkdir -p $TMPDIR/bin
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
$MAGISKBIN/busybox --install -s $TMPDIR/bin
export PATH=$TMPDIR/bin:$PATH
fi
# Temporarily block out all custom recovery binaries/libs # Temporarily block out all custom recovery binaries/libs
mv /sbin /sbin_tmp mv /sbin /sbin_tmp
# Add all possible library paths # Unset library paths
$IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib OLD_LD_LIB=$LD_LIBRARY_PATH
OLD_LD_PRE=$LD_PRELOAD
unset LD_LIBRARY_PATH
unset LD_PRELOAD
} }
recovery_cleanup() { recovery_cleanup() {
mv /sbin_tmp /sbin 2>/dev/null mv /sbin_tmp /sbin 2>/dev/null
export LD_LIBRARY_PATH=$OLD_LD_PATH
[ -z $OLD_PATH ] || export PATH=$OLD_PATH [ -z $OLD_PATH ] || export PATH=$OLD_PATH
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
ui_print "- Unmounting partitions" ui_print "- Unmounting partitions"
umount -l /system_root 2>/dev/null umount -l /system_root 2>/dev/null
umount -l /system 2>/dev/null umount -l /system 2>/dev/null
@@ -339,51 +375,55 @@ mktouch() {
} }
request_size_check() { request_size_check() {
reqSizeM=`du -s $1 | cut -f1` reqSizeM=`du -ms $1 | cut -f1`
reqSizeM=$((reqSizeM / 1024 + 1))
} }
request_zip_size_check() { request_zip_size_check() {
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int($1 / 1048567 + 1) }'` reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
} }
image_size_check() { check_filesystem() {
SIZE="`$MAGISKBIN/magisk --imgsize $IMG`" curSizeM=`wc -c < $1`
curUsedM=`echo "$SIZE" | cut -d" " -f1` curSizeM=$((curSizeM / 1048576))
curSizeM=`echo "$SIZE" | cut -d" " -f2` local DF=`df -P $2 | grep $2`
curFreeM=$((curSizeM - curUsedM)) curUsedM=`echo $DF | awk '{ print int($3 / 1024) }'`
curFreeM=`echo $DF | awk '{ print int($4 / 1024) }'`
}
mount_snippet() {
MAGISKLOOP=`$MAGISKBIN/magisk imgtool mount $IMG $MOUNTPATH`
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
} }
mount_magisk_img() { mount_magisk_img() {
[ -z reqSizeM ] && reqSizeM=0 [ -z reqSizeM ] && reqSizeM=0
mkdir -p $MOUNTPATH 2>/dev/null
if [ -f "$IMG" ]; then if [ -f "$IMG" ]; then
ui_print "- Found $IMG" ui_print "- Found $IMG"
image_size_check $IMG mount_snippet
if [ "$reqSizeM" -gt "$curFreeM" ]; then check_filesystem $IMG $MOUNTPATH
newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64)) if [ $reqSizeM -gt $curFreeM ]; then
newSizeM=$(((curSizeM + reqSizeM - curFreeM) / 32 * 32 + 64))
ui_print "- Resizing $IMG to ${newSizeM}M" ui_print "- Resizing $IMG to ${newSizeM}M"
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM >&2 $MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
$MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
mount_snippet
fi fi
ui_print "- Mount $IMG to $MOUNTPATH"
else else
newSizeM=$((reqSizeM / 32 * 32 + 64)); newSizeM=$((reqSizeM / 32 * 32 + 64))
ui_print "- Creating $IMG with size ${newSizeM}M" ui_print "- Creating $IMG with size ${newSizeM}M"
$MAGISKBIN/magisk --createimg $IMG $newSizeM >&2 $MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
mount_snippet
fi fi
ui_print "- Mounting $IMG to $MOUNTPATH"
MAGISKLOOP=`$MAGISKBIN/magisk --mountimg $IMG $MOUNTPATH`
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
} }
unmount_magisk_img() { unmount_magisk_img() {
$MAGISKBIN/magisk --umountimg $MOUNTPATH $MAGISKLOOP check_filesystem $IMG $MOUNTPATH
# Shrink the image if possible
image_size_check $IMG
newSizeM=$((curUsedM / 32 * 32 + 64)) newSizeM=$((curUsedM / 32 * 32 + 64))
$MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
if [ $curSizeM -gt $newSizeM ]; then if [ $curSizeM -gt $newSizeM ]; then
ui_print "- Shrinking $IMG to ${newSizeM}M" ui_print "- Shrinking $IMG to ${newSizeM}M"
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM $MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
fi fi
} }

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 27 compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion "27.0.3" buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig { defaultConfig {
applicationId "com.topjohnwu.snet" applicationId "com.topjohnwu.snet"
minSdkVersion 21 minSdkVersion 14
targetSdkVersion 27 targetSdkVersion rootProject.ext.compileSdkVersion
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
} }
@@ -15,16 +15,13 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
} }
repositories {
google()
}
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) compileOnly fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.android.gms:play-services-safetynet:7.0.0' /* The oldest version */ implementation 'com.google.android.gms:play-services-safetynet:7.0.0' /* The oldest version */
} }

View File

@@ -1,5 +0,0 @@
package com.topjohnwu.snet;
public interface SafetyNetCallback {
void onResponse(int responseCode);
}

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.snet; package com.topjohnwu.snet;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -11,18 +9,19 @@ import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.SafetyNet; import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi; import com.google.android.gms.safetynet.SafetyNetApi;
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.lang.reflect.Field;
import java.security.SecureRandom; import java.security.SecureRandom;
public class SafetyNetHelper public class SafetyNetHelper implements ISafetyNetHelper, GoogleApiClient.ConnectionCallbacks,
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { GoogleApiClient.OnConnectionFailedListener, ResultCallback<SafetyNetApi.AttestationResult> {
public static final int CAUSE_SERVICE_DISCONNECTED = 0x01; public static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
public static final int CAUSE_NETWORK_LOST = 0x02; public static final int CAUSE_NETWORK_LOST = 0x02;
@@ -32,36 +31,24 @@ public class SafetyNetHelper
public static final int BASIC_PASS = 0x10; public static final int BASIC_PASS = 0x10;
public static final int CTS_PASS = 0x20; public static final int CTS_PASS = 0x20;
public static final int SNET_EXT_VER = 7; public static final int SNET_EXT_VER = 8;
private GoogleApiClient mGoogleApiClient; private GoogleApiClient mGoogleApiClient;
private Activity mActivity; private Activity mActivity;
private int responseCode; private Callback callback;
private SafetyNetCallback cb;
private String dexPath;
private boolean isDarkTheme;
public static int getVersion() { @Override
public int getVersion() {
return SNET_EXT_VER; return SNET_EXT_VER;
} }
public SafetyNetHelper(Activity activity, String dexPath, SafetyNetCallback cb) { public SafetyNetHelper(Activity activity, Callback cb) {
mActivity = activity; mActivity = activity;
this.cb = cb; callback = cb;
this.dexPath = dexPath;
responseCode = 0;
// Get theme
try {
Context context = activity.getApplicationContext();
Field theme = context.getClass().getField("isDarkTheme");
isDarkTheme = (boolean) theme.get(context);
} catch (Exception e) {
e.printStackTrace();
}
} }
// Entry point to start test // Entry point to start test
@Override
public void attest() { public void attest() {
// Connect Google Service // Connect Google Service
mGoogleApiClient = new GoogleApiClient.Builder(mActivity) mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
@@ -74,26 +61,15 @@ public class SafetyNetHelper
@Override @Override
public void onConnectionSuspended(int i) { public void onConnectionSuspended(int i) {
cb.onResponse(i); callback.onResponse(i);
} }
@Override @Override
public void onConnectionFailed(@NonNull ConnectionResult result) { public void onConnectionFailed(@NonNull ConnectionResult result) {
Class<? extends Activity> clazz = mActivity.getClass(); mActivity.swapResources(CheckSafetyNet.dexPath.getPath());
try { GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
// Use external resources mActivity.restoreResources();
clazz.getMethod("swapResources", String.class, int.class).invoke(mActivity, dexPath, callback.onResponse(CONNECTION_FAIL);
isDarkTheme ? android.R.style.Theme_Material : android.R.style.Theme_Material_Light);
try {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
} catch (Exception e) {
e.printStackTrace();
}
clazz.getMethod("restoreResources").invoke(mActivity);
} catch (Exception e) {
e.printStackTrace();
}
cb.onResponse(CONNECTION_FAIL);
} }
@Override @Override
@@ -103,28 +79,28 @@ public class SafetyNetHelper
new SecureRandom().nextBytes(nonce); new SecureRandom().nextBytes(nonce);
// Call SafetyNet // Call SafetyNet
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce).setResultCallback(this);
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() { }
@Override
public void onResult(@NonNull SafetyNetApi.AttestationResult result) {
Status status = result.getStatus();
try {
if (!status.isSuccess()) throw new JSONException("");
String json = new String(Base64.decode(
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
JSONObject decoded = new JSONObject(json);
responseCode |= decoded.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
responseCode |= decoded.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
} catch (JSONException e) {
responseCode = RESPONSE_ERR;
}
// Disconnect @Override
mGoogleApiClient.disconnect(); public void onResult(SafetyNetApi.AttestationResult result) {
int code = 0;
try {
if (!result.getStatus().isSuccess())
throw new JSONException("");
String jsonStr = new String(Base64.decode(
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
JSONObject json = new JSONObject(jsonStr);
code |= json.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
code |= json.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
} catch (JSONException e) {
code = RESPONSE_ERR;
}
// Return results // Disconnect
cb.onResponse(responseCode); mGoogleApiClient.disconnect();
}
}); // Return results
callback.onResponse(code);
} }
} }

View File

@@ -15,7 +15,7 @@ jar {
shadowJar { shadowJar {
baseName = 'zipsigner' baseName = 'zipsigner'
classifier = null classifier = null
version = 2.1 version = 2.2
} }
buildscript { buildscript {
@@ -23,7 +23,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2' classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
} }
} }
@@ -33,6 +33,6 @@ repositories {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.bouncycastle:bcprov-jdk15on:1.58' implementation 'org.bouncycastle:bcprov-jdk15on:1.59'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.58' implementation 'org.bouncycastle:bcpkix-jdk15on:1.59'
} }

View File

@@ -24,10 +24,11 @@ import java.util.zip.ZipFile;
* */ * */
public class JarMap implements Closeable, AutoCloseable { public class JarMap implements Closeable, AutoCloseable {
private JarFile jarFile; private JarFile jarFile;
private JarInputStream jis; private JarInputStream jis;
private boolean isInputStream = false;
private LinkedHashMap<String, JarEntry> bufMap; private LinkedHashMap<String, JarEntry> bufMap;
private Manifest manifest;
public JarMap(File file) throws IOException { public JarMap(File file) throws IOException {
this(file, true); this(file, true);
@@ -39,6 +40,7 @@ public class JarMap implements Closeable, AutoCloseable {
public JarMap(File file, boolean verify, int mode) throws IOException { public JarMap(File file, boolean verify, int mode) throws IOException {
jarFile = new JarFile(file, verify, mode); jarFile = new JarFile(file, verify, mode);
manifest = jarFile.getManifest();
} }
public JarMap(String name) throws IOException { public JarMap(String name) throws IOException {
@@ -54,48 +56,54 @@ public class JarMap implements Closeable, AutoCloseable {
} }
public JarMap(InputStream is, boolean verify) throws IOException { public JarMap(InputStream is, boolean verify) throws IOException {
isInputStream = true;
bufMap = new LinkedHashMap<>();
jis = new JarInputStream(is, verify); jis = new JarInputStream(is, verify);
bufMap = new LinkedHashMap<>();
JarEntry entry; JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) { while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis)); bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
} }
manifest = jis.getManifest();
} }
public File getFile() { public File getFile() {
return isInputStream ? null : new File(jarFile.getName()); return jarFile == null ? null : new File(jarFile.getName());
} }
public Manifest getManifest() throws IOException { public Manifest getManifest() {
return isInputStream ? jis.getManifest() : jarFile.getManifest(); return manifest;
} }
public InputStream getInputStream(ZipEntry ze) throws IOException { public InputStream getInputStream(ZipEntry ze) throws IOException {
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() : if (bufMap != null) {
jarFile.getInputStream(ze); JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
if (e != null)
return e.data.getInputStream();
}
return jarFile.getInputStream(ze);
} }
public OutputStream getOutputStream(ZipEntry ze) { public OutputStream getOutputStream(ZipEntry ze) {
if (!isInputStream) // Only support InputStream mode manifest = null; /* Invalidate the manifest */
return null; if (bufMap == null)
ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data; bufMap = new LinkedHashMap<>();
bs.reset(); JarMapEntry e = new JarMapEntry(ze.getName());
return bs; bufMap.put(ze.getName(), e);
return e.data;
} }
public byte[] getRawData(ZipEntry ze) throws IOException { public byte[] getRawData(ZipEntry ze) throws IOException {
if (isInputStream) { if (bufMap != null) {
return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray(); JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
} else { if (e != null)
ByteArrayStream bytes = new ByteArrayStream(); return e.data.toByteArray();
bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray();
} }
ByteArrayStream bytes = new ByteArrayStream();
bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray();
} }
public Enumeration<JarEntry> entries() { public Enumeration<JarEntry> entries() {
return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries(); return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries();
} }
public ZipEntry getEntry(String name) { public ZipEntry getEntry(String name) {
@@ -103,20 +111,29 @@ public class JarMap implements Closeable, AutoCloseable {
} }
public JarEntry getJarEntry(String name) { public JarEntry getJarEntry(String name) {
return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name); JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name);
if (e == null && bufMap != null)
return bufMap.get(name);
return e;
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
(isInputStream ? jis : jarFile).close(); (jarFile == null ? jis : jarFile).close();
} }
private static class JarMapEntry extends JarEntry { private static class JarMapEntry extends JarEntry {
ByteArrayStream data; ByteArrayStream data;
JarMapEntry(JarEntry je, InputStream is) { JarMapEntry(JarEntry je, InputStream is) {
super(je); super(je);
data = new ByteArrayStream(); data = new ByteArrayStream();
data.readFrom(is); data.readFrom(is);
} }
JarMapEntry(String s) {
super(s);
data = new ByteArrayStream();
}
} }
} }

View File

@@ -0,0 +1,28 @@
package com.topjohnwu.utils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ReusableInputStream extends BufferedInputStream {
public ReusableInputStream(InputStream in) {
super(in);
mark(Integer.MAX_VALUE);
}
public ReusableInputStream(InputStream in, int size) {
super(in, size);
mark(Integer.MAX_VALUE);
}
@Override
public void close() throws IOException {
/* Reset at close so we can reuse it */
reset();
}
public void destroy() throws IOException {
super.close();
}
}

View File

@@ -18,6 +18,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -73,19 +74,31 @@ public class SignAPK {
JarMap input, OutputStream output) throws Exception { JarMap input, OutputStream output) throws Exception {
File temp1 = File.createTempFile("signAPK", null); File temp1 = File.createTempFile("signAPK", null);
File temp2 = File.createTempFile("signAPK", null); File temp2 = File.createTempFile("signAPK", null);
if (cert == null) {
cert = SignAPK.class.getResourceAsStream("/keys/testkey.x509.pem");
}
if (key == null) {
key = SignAPK.class.getResourceAsStream("/keys/testkey.pk8");
}
ReusableInputStream c = new ReusableInputStream(cert);
ReusableInputStream k = new ReusableInputStream(key);
try { try {
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))) { try (OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))) {
signZip(cert, key, input, out, false); signZip(c, k, input, out, false);
} }
ZipAdjust.adjust(temp1, temp2); ZipAdjust.adjust(temp1, temp2);
try (JarMap map = new JarMap(temp2, false)) { try (JarMap map = new JarMap(temp2, false)) {
signZip(cert, key, map, output, true); signZip(c, k, map, output, true);
} }
} finally { } finally {
temp1.delete(); temp1.delete();
temp2.delete(); temp2.delete();
c.destroy();
k.destroy();
} }
} }
@@ -494,6 +507,7 @@ public class SignAPK {
outputStream.write(total_size & 0xff); outputStream.write(total_size & 0xff);
outputStream.write((total_size >> 8) & 0xff); outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream); temp.writeTo(outputStream);
outputStream.close();
} }
private static void signFile(Manifest manifest, JarMap inputJar, private static void signFile(Manifest manifest, JarMap inputJar,
X509Certificate publicKey, PrivateKey privateKey, X509Certificate publicKey, PrivateKey privateKey,

View File

@@ -28,8 +28,8 @@ public class ZipSigner {
InputStream key = null; InputStream key = null;
if (args.length - argStart == 4) { if (args.length - argStart == 4) {
cert = new BufferedInputStream(new FileInputStream(new File(args[argStart]))); cert = new FileInputStream(new File(args[argStart]));
key = new BufferedInputStream(new FileInputStream(new File(args[argStart + 1]))); key = new FileInputStream(new File(args[argStart + 1]));
argStart += 2; argStart += 2;
} }