Compare commits

...

109 Commits
v16.2 ... 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
68 changed files with 2818 additions and 2094 deletions

6
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@@ -25,3 +25,6 @@
[submodule "xz"]
path = native/jni/external/xz
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
## How to build Magisk
#### Building has been tested on 3 major platforms: macOS, Ubuntu, Windows 10
### Environment Requirements
## Building Environment Requirements
1. Python 3.5+: run `build.py` script
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
4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or specify custom path `ANDROID_NDK`
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 optionally specify a custom path `ANDROID_NDK`
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
### Instructions and Notes
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.
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.
## Building Notes and Instructions
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. 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`
4. Build everything with `build.py`, don't directly call `gradlew` or `ndk-build`, since most requires special setup / dependencies.
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).
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. 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

2
app

Submodule app updated: 0c782edf21...b885ccbd63

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
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
@@ -19,9 +19,16 @@ allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.0"
supportLibVersion = "27.1.1"
}
task clean(type: Delete) {
delete rootProject.buildDir
}

349
build.py
View File

@@ -4,8 +4,8 @@ import os
import subprocess
if os.name == 'nt':
from colorama import init
init()
import colorama
colorama.init()
def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
@@ -16,7 +16,7 @@ def header(str):
# Environment checks
if not sys.version_info >= (3, 5):
error('Requires Python >= 3.5')
error('Requires Python 3.5+')
if 'ANDROID_HOME' not in os.environ:
error('Please add Android SDK path to ANDROID_HOME environment variable!')
@@ -24,7 +24,7 @@ if 'ANDROID_HOME' not in os.environ:
try:
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
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 multiprocessing
@@ -41,13 +41,21 @@ if 'ANDROID_NDK' in os.environ:
else:
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):
print('mv: {} -> {}'.format(source, target))
shutil.move(source, target)
try:
shutil.move(source, target)
except:
pass
def cp(source, target):
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
try:
shutil.copyfile(source, target)
print('cp: {} -> {}'.format(source, target))
except:
pass
def rm(file):
try:
@@ -72,143 +80,199 @@ def zip_with_msg(zipfile, source, target):
zipfile.write(source, target)
def build_all(args):
build_binary(args)
build_apk(args)
build_binary(args)
zip_main(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):
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'))
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
# Basic flags
base_flags = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={} MAGISK_DEBUG={}'.format(config['version'], config['versionCode'],
'' if args.release else '-DMAGISK_DEBUG')
# Prebuild
proc = subprocess.run('{} -C native PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
if 'magisk' in targets:
# 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:
error('Build Magisk binary failed!')
error('Zipalign Magisk Manager failed!')
print('')
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
mkdir_p(os.path.join('out', arch))
with open(os.path.join('out', arch, 'dump.h'), 'w') as dump:
dump.write('#include "stdlib.h"\n')
mv(os.path.join('native', 'libs', arch, 'magisk'), os.path.join('out', arch, 'magisk'))
with open(os.path.join('out', arch, 'magisk'), 'rb') as bin:
dump.write('const uint8_t magisk_dump[] = "')
dump.write(''.join("\\x{:02X}".format(c) for c in lzma.compress(bin.read(), preset=9)))
dump.write('";\n')
# Find apksigner.jar
apksigner = ''
for root, dirs, files in os.walk(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')
print('')
proc = subprocess.run('{} -C native {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
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)
if proc.returncode != 0:
error('Build Magisk binary 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
error('Release sign Magisk Manager failed!')
def build_apk(args):
header('* Building Magisk Manager')
mkdir(os.path.join('app', 'src', 'main', 'assets'))
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
source = os.path.join('scripts', script)
target = os.path.join('app', 'src', 'main', 'assets', script)
cp(source, target)
source = os.path.join('scripts', 'util_functions.sh')
target = os.path.join('app', 'src', 'full', 'res', 'raw', 'util_functions.sh')
cp(source, target)
if args.release:
if not os.path.exists('release_signature.jks'):
error('Please generate a java keystore and place it in \'release_signature.jks\'')
if not os.path.exists('release-key.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)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-unsigned.apk')
aligned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-aligned.apk')
release = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release.apk')
# 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!')
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'release', 'app-full-release-unsigned.apk')
release = os.path.join(config['outdir'], 'app-release.apk')
sign_apk(unsigned, release)
header('Output: ' + release)
rm(unsigned)
rm(aligned)
mkdir('out')
target = os.path.join('out', 'app-release.apk')
print('')
mv(release, target)
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'stub', 'release', 'app-stub-release-unsigned.apk')
release = os.path.join(config['outdir'], 'stub-release.apk')
sign_apk(unsigned, release)
header('Output: ' + release)
rm(unsigned)
else:
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
mkdir('out')
target = os.path.join('out', 'app-debug.apk')
print('')
source = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'debug', 'app-full-debug.apk')
target = os.path.join(config['outdir'], 'app-debug.apk')
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):
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
mkdir('out')
target = os.path.join('out', 'snet.apk')
print('')
mv(source, target)
target = os.path.join(config['outdir'], 'snet.apk')
# Re-compress the whole APK for smaller size
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
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():
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):
error('Please build \'binary\' before zipping!')
with open(binary, 'rb') as b64xz:
update_bin.append('#! /sbin/sh\nEX_ARM=\'')
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:
update_bin.append('\'\nEX_X86=\'')
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:
update_bin.append('\'\nBB_ARM=')
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:
update_bin.append('\nBB_X86=')
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)
# 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']:
source = os.path.join('out', lib_dir, binary)
source = os.path.join('native', 'out', lib_dir, binary)
target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target)
# 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')
zip_with_msg(zipf, source, target)
@@ -254,14 +318,14 @@ def zip_main(args):
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
# Add version info util_functions.sh
util_func = script.read().replace(
'#MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode))
util_func = script.read().replace('#MAGISK_VERSION_STUB',
'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(config['version'], config['versionCode']))
target = os.path.join('common', 'util_functions.sh')
print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func)
# 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)
# Prebuilts
@@ -271,8 +335,10 @@ def zip_main(args):
# 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)
header('Output: ' + output)
def zip_uninstaller(args):
header('* Packing Uninstaller Zip')
@@ -286,19 +352,16 @@ def zip_uninstaller(args):
print('zip: ' + target)
zipf.writestr(target, gen_update_binary())
# 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')
zip_with_msg(zipf, source, target)
# Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
source = os.path.join('out', lib_dir, 'magiskboot')
target = os.path.join(zip_dir, 'magiskboot')
zip_with_msg(zipf, source, target)
source = os.path.join('scripts', 'magisk_uninstaller.sh')
target = 'magisk_uninstaller.sh'
zip_with_msg(zipf, source, target)
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for bin in ['magisk', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, bin)
target = os.path.join(zip_dir, bin)
zip_with_msg(zipf, source, target)
# Scripts
# util_functions.sh
@@ -316,11 +379,13 @@ def zip_uninstaller(args):
# 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)
header('Output: ' + 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)
if not os.path.exists(jarsigner):
@@ -339,40 +404,56 @@ def sign_adjust_zip(unsigned, output):
def cleanup(args):
if len(args.target) == 0:
args.target = ['binary', 'java', 'zip']
args.target = ['native', 'java']
if 'binary' in args.target:
header('* Cleaning binaries')
subprocess.run(ndk_build + ' -C native PRECOMPILE=true clean', shell=True)
subprocess.run(ndk_build + ' -C native clean', shell=True)
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
if 'native' in args.target:
header('* Cleaning native')
subprocess.run(ndk_build + ' -C native B_MAGISK=1 B_INIT=1 B_BOOT=1 B_BXZ=1 B_BB=1 clean', shell=True)
shutil.rmtree(os.path.join('native', 'out'), ignore_errors=True)
if 'java' in args.target:
header('* Cleaning java')
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:
header('* Cleaning zip files')
for f in os.listdir('out'):
if '.zip' in f:
rm(os.path.join('out', f))
def parse_config():
c = {}
with open('config.prop', 'r') as f:
for line in [l.strip(' \t\r\n') for l in 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.add_argument('--release', action='store_true', help='compile Magisk for release')
subparsers = parser.add_subparsers(title='actions')
all_parser = subparsers.add_parser('all', help='build everything and create flashable zip with uninstaller')
all_parser.add_argument('versionString')
all_parser.add_argument('versionCode', type=int)
all_parser = subparsers.add_parser('all', help='build everything (binaries/apks/zips)')
all_parser.set_defaults(func=build_all)
binary_parser = subparsers.add_parser('binary', help='build Magisk binaries')
binary_parser.add_argument('versionString')
binary_parser.add_argument('versionCode', type=int)
binary_parser = subparsers.add_parser('binary', help='build binaries. target: magisk magiskinit magiskboot busybox b64xz')
binary_parser.add_argument('target', nargs='*')
binary_parser.set_defaults(func=build_binary)
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)
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)
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable 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.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

@@ -5,7 +5,7 @@ Magisk does modifications systemless-ly, which means applying official OTAs is m
**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
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
@@ -16,10 +16,10 @@ Magisk does modifications systemless-ly, which means applying official OTAs is m
#### Devices with A/B Partitions
(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. 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. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates).
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">
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)
(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. 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.
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 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. 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.
## 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.
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
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
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME
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
# Resolve links: $0 may be a link
PRG="$0"
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
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
# Determine the Java command to use to start the JVM.
@@ -85,7 +89,7 @@ location of your Java installation."
fi
# 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`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
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
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
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
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
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
: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 "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@@ -1,7 +1,8 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
externalNativeBuild {
ndkBuild {
@@ -12,8 +13,9 @@ android {
defaultConfig {
externalNativeBuild {
ndkBuild {
// Passes an optional argument to ndk-build.
arguments "GRADLE=true"
// Pass arguments to ndk-build.
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
LIBBZ2 := $(EXT_PATH)/bzip2
LIBFDT := $(EXT_PATH)/dtc/libfdt
LIBNANOPB := $(EXT_PATH)/nanopb
UTIL_SRC := utils/cpio.c \
utils/file.c \
utils/img.c \
@@ -22,32 +23,35 @@ UTIL_SRC := utils/cpio.c \
# Binaries
########################
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
ifdef B_MAGISK
# magisk main binary
include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_STATIC_LIBRARIES := libnanopb
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
$(EXT_PATH)/include \
$(LIBSELINUX)
$(LIBSELINUX) \
$(LIBNANOPB)
LOCAL_SRC_FILES := \
core/magisk.c \
core/daemon.c \
core/log_monitor.c \
core/log_daemon.c \
core/bootstages.c \
core/socket.c \
core/db.c \
magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \
resetprop/resetprop.cpp \
resetprop/persist_props.c \
resetprop/resetprop.c \
resetprop/system_properties.cpp \
su/su.c \
su/activity.c \
su/db.c \
su/pts.c \
su/su_daemon.c \
su/su_socket.c \
@@ -59,7 +63,7 @@ include $(BUILD_EXECUTABLE)
endif
ifndef PRECOMPILE
ifdef B_INIT
# magiskinit
include $(CLEAR_VARS)
@@ -68,13 +72,13 @@ LOCAL_STATIC_LIBRARIES := libsepol liblzma
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
../out/$(TARGET_ARCH_ABI) \
out \
out/$(TARGET_ARCH_ABI) \
$(LIBSEPOL) \
$(LIBLZMA)
LOCAL_SRC_FILES := \
core/magiskinit.c \
core/socket.c \
magiskpolicy/api.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
@@ -84,6 +88,10 @@ LOCAL_SRC_FILES := \
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
endif
ifdef B_BOOT
# magiskboot
include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot
@@ -110,10 +118,10 @@ LOCAL_CFLAGS := -DXWRAP_EXIT
LOCAL_LDLIBS := -lz
include $(BUILD_EXECUTABLE)
# 32-bit static binaries
ifndef GRADLE # Do not run gradle sync on these binaries
ifneq ($(TARGET_ARCH_ABI), x86_64)
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
endif
ifdef B_BXZ
# b64xz
include $(CLEAR_VARS)
LOCAL_MODULE := b64xz
@@ -122,13 +130,14 @@ LOCAL_C_INCLUDES := $(LIBLZMA)
LOCAL_SRC_FILES := b64xz.c
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
# Busybox
include jni/external/busybox/Android.mk
endif
endif
endif
# Precompile
ifdef B_BB
# Busybox
include jni/external/busybox/Android.mk
endif
########################

View File

@@ -1,5 +1,10 @@
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
APP_PLATFORM := android-21
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99
APP_ABI := x86 armeabi-v7a
APP_CFLAGS := -std=gnu99 ${MAGISK_DEBUG} \
-DMAGISK_VERSION="${MAGISK_VERSION}" -DMAGISK_VER_CODE=${MAGISK_VER_CODE}
APP_CPPFLAGS := -std=c++11
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 "magisk.h"
#include "db.h"
#include "utils.h"
#include "daemon.h"
#include "resetprop.h"
#include "magiskpolicy.h"
static char *buf, *buf2;
static struct vector module_list;
@@ -112,11 +114,11 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
* setenvs *
***********/
static void bb_setenv(struct vector *v) {
static void set_path(struct vector *v) {
for (int i = 0; environ[i]; ++i) {
if (strncmp(environ[i], "PATH=", 5) == 0) {
snprintf(buf, PATH_MAX, "PATH=%s:%s", BBPATH, strchr(environ[i], '=') + 1);
vec_push_back(v, strdup(buf));
vec_push_back(v, strdup("PATH=" BBPATH ":/sbin:" MIRRDIR "/system/bin:"
MIRRDIR "/system/xbin:" MIRRDIR "/vendor/bin"));
} else {
vec_push_back(v, strdup(environ[i]));
}
@@ -124,15 +126,6 @@ static void bb_setenv(struct vector *v) {
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 *
***********/
@@ -151,7 +144,7 @@ static void exec_common_script(const char* stage) {
if (access(buf2, X_OK) == -1)
continue;
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)
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)
continue;
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)
waitpid(pid, NULL, 0);
}
@@ -314,11 +307,11 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_LNK(child)) {
// Copy symlinks directly
cp_afc(buf2, buf);
#ifdef MAGISK_DEBUG
LOGI("creat_link: %s <- %s\n",buf, buf2);
#else
LOGI("creat_link: %s\n", buf);
#endif
#ifdef MAGISK_DEBUG
LOGI("creat_link: %s <- %s\n",buf, buf2);
#else
LOGI("creat_link: %s\n", buf);
#endif
} else {
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
bind_mount(buf2, buf);
@@ -359,7 +352,7 @@ static void simple_mount(const char *path) {
DIR *dir;
struct dirent *entry;
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, path);
snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, path);
if (!(dir = opendir(buf)))
return;
@@ -377,7 +370,7 @@ static void simple_mount(const char *path) {
free(new_path);
} else if (entry->d_type == DT_REG) {
// Actual file path
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, buf2);
snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, buf2);
// Clone all attributes
clone_attr(buf2, buf);
// Finally, mount the file
@@ -435,33 +428,44 @@ static int prepare_img() {
rm_rf(buf);
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);
if (access(buf, F_OK) == 0)
continue;
vec_push_back(&module_list, strdup(entry->d_name));
}
}
closedir(dir);
// Trim image
umount_image(MOUNTPOINT, magiskloop);
if (trim_img(MAINIMG, MOUNTPOINT, magiskloop))
return 1;
free(magiskloop);
trim_img(MAINIMG);
// Remount them back :)
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
free(magiskloop);
// Fix file selinux contexts
fix_filecon();
return 0;
}
void fix_filecon() {
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
restorecon(dirfd, 0);
close(dirfd);
void install_apk(const char *apk) {
setfilecon(apk, "u:object_r:"SEPOL_FILE_DOMAIN":s0");
while (1) {
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);
}
void post_fs(int client) {
LOGI("** post-fs mode running\n");
// ack
write_int(client, 0);
close(client);
static const char wrapper[] =
"#!/system/bin/sh\n"
"unset LD_LIBRARY_PATH\n"
"unset LD_PRELOAD\n"
"exec /sbin/magisk.bin \"${0##*/}\" \"$@\"\n";
// Uninstall or core only mode
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
goto unblock;
void startup() {
if (!check_data())
unblock_boot_process();
// Allocate buffer
buf = xmalloc(PATH_MAX);
buf2 = xmalloc(PATH_MAX);
if (access(SECURE_DIR, F_OK) != 0) {
/* If the folder is not automatically created by the system,
* 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");
simple_mount("/vendor");
// No uninstaller or core-only mode
if (access(DISABLEFILE, F_OK) != 0) {
// Allocate buffer
buf = xmalloc(PATH_MAX);
buf2 = xmalloc(PATH_MAX);
unblock:
unblock_boot_process();
simple_mount("/system");
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) {
// ack
write_int(client, 0);
close(client);
if (!is_daemon_init && !check_data())
goto unblock;
// Start the debug log
start_debug_full_log();
// If post-fs-data mode is started, it means startup succeeded
setup_done = 1;
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
if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
buf = xmalloc(PATH_MAX);
buf2 = xmalloc(PATH_MAX);
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
// After this, it will create the module list
if (prepare_img())
goto core_only; // Mounting fails, we can only do core only stuffs
restorecon();
chmod(SECURE_DIR, 0700);
// Run common scripts
LOGI("* Running post-fs-data.d scripts\n");
exec_common_script("post-fs-data");
@@ -614,8 +779,6 @@ core_only:
}
auto_start_magiskhide();
unblock:
unblock_boot_process();
}
@@ -625,13 +788,24 @@ void late_start(int client) {
write_int(client, 0);
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
if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done
wait_till_exists(PATCHDONE);
unlink(PATCHDONE);
if (full_patch_pid > 0)
waitpid(full_patch_pid, NULL, 0);
// Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n");
@@ -645,32 +819,21 @@ void late_start(int client) {
exec_module_script("service");
core_only:
// Install Magisk Manager if exists
if (access(MANAGERAPK, F_OK) == 0) {
// Install Magisk Manager if exists
rename(MANAGERAPK, "/data/magisk.apk");
setfilecon("/data/magisk.apk", "u:object_r:su_file:s0");
while (1) {
sleep(5);
int apk_res = -1, pid;
pid = exec_command(1, &apk_res, pm_setenv,
"app_process",
"/system/bin", "com.android.commands.pm.Pm",
"install", "-r", "/data/magisk.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;
}
install_apk("/data/magisk.apk");
} else {
// Check whether we have a valid manager installed
sqlite3 *db = get_magiskdb();
struct db_strings str;
INIT_DB_STRINGS(&str);
get_db_strings(db, SU_MANAGER, &str);
if (validate_manager(str.s[SU_MANAGER], 0, NULL)) {
// There is no manager installed, install the stub
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk", NULL);
install_apk("/data/magisk.apk");
}
unlink("/data/magisk.apk");
}
// All boot stage done, cleanup everything
@@ -678,6 +841,4 @@ core_only:
free(buf2);
buf = buf2 = NULL;
vec_deep_destroy(&module_list);
stop_debug_full_log();
}

View File

@@ -19,8 +19,11 @@
#include "utils.h"
#include "daemon.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) {
int client = *((int *) args);
@@ -36,7 +39,6 @@ static void *request_handler(void *args) {
case ADD_HIDELIST:
case RM_HIDELIST:
case LS_HIDELIST:
case POST_FS:
case POST_FS_DATA:
case LATE_START:
if (credential.uid != 0) {
@@ -75,16 +77,17 @@ static void *request_handler(void *args) {
write_int(client, MAGISK_VER_CODE);
close(client);
break;
case POST_FS:
post_fs(client);
break;
case POST_FS_DATA:
post_fs_data(client);
break;
case LATE_START:
late_start(client);
break;
case HANDSHAKE:
/* Do NOT close the client, make it hold */
break;
default:
close(client);
break;
}
return NULL;
@@ -106,184 +109,48 @@ void auto_start_magiskhide() {
free(hide_prop);
}
void daemon_init() {
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() {
void main_daemon() {
setsid();
setcon("u:r:su:s0");
umask(0);
setcon("u:r:"SEPOL_PROC_DOMAIN":s0");
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO);
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;
sigemptyset(&block_set);
sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2);
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
struct sockaddr_un sun;
fd = setup_socket(&sun);
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
exit(1);
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();
// Ignore SIGPIPE
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL);
// Loop forever to listen for requests
while(1) {
@@ -296,26 +163,38 @@ void start_daemon() {
}
}
/* Connect the daemon, and return a socketfd */
int connect_daemon() {
/* Connect the daemon, set sockfd, and return if new daemon is spawned */
int connect_daemon2(daemon_t d, int *sockfd) {
struct sockaddr_un sun;
int fd = setup_socket(&sun);
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
// If we cannot access the daemon, we start a daemon in the child process if possible
*sockfd = setup_socket(&sun, d);
if (connect(*sockfd, (struct sockaddr*) &sun, sizeof(sun))) {
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "No daemon is currently running!\n");
exit(1);
}
if (xfork() == 0) {
if (fork_dont_care() == 0) {
LOGD("client: connect fail, try launching new daemon process\n");
close(fd);
start_daemon();
close(*sockfd);
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);
return 1;
}
return 0;
}
int connect_daemon() {
int fd;
connect_daemon2(MAIN_DAEMON, &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;
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) {
char self[PATH_MAX], linkpath[PATH_MAX];
@@ -33,8 +34,8 @@ static void usage() {
fprintf(stderr,
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
"\n"
"Usage: %s [applet [arguments]...]\n"
" or: %s [options]...\n"
"Usage: magisk [applet [arguments]...]\n"
" or: magisk [options]...\n"
"\n"
"Options:\n"
" -c print current binary version\n"
@@ -42,21 +43,16 @@ static void usage() {
" -V print running daemon version code\n"
" --list list all available applets\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"
" --imgsize IMG report ext4 image used/total size\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"
" --daemon manually start magisk daemon\n"
" --[init trigger] start service for init trigger\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
" --restorecon fix selinux context on Magisk files and folders\n"
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
"\n"
"Supported init services:\n"
" daemon, post-fs, post-fs-data, service\n"
"Supported init triggers:\n"
" startup, post-fs-data, service\n"
"\n"
"Supported applets:\n"
, argv0, argv0);
"Supported applets:\n");
for (int i = 0; applet[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
@@ -64,102 +60,78 @@ static void usage() {
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[]) {
umask(0);
argv0 = argv[0];
if (strcmp(basename(argv[0]), "magisk") == 0) {
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], "--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 {
if (strcmp(basename(argv0), "magisk.bin") == 0) {
if (argc >= 2) {
// It's calling applets
--argc;
++argv;
@@ -168,10 +140,12 @@ int main(int argc, char *argv[]) {
// Applets
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);
}
}
fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
return 1;
// Not an applet
return magisk_main(argc, argv);
}

View File

@@ -39,7 +39,9 @@
#include <lzma.h>
#include <cil/cil.h>
#include "dump.h"
#include "binaries_xz.h"
#include "binaries_arch_xz.h"
#include "magiskrc.h"
#include "utils.h"
#include "magiskpolicy.h"
@@ -55,8 +57,6 @@
extern policydb_t *policydb;
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 {
char skip_initramfs;
@@ -73,7 +73,7 @@ struct device {
static void parse_cmdline(struct cmdline *cmd) {
// cleanup
memset(cmd, 0, sizeof(&cmd));
memset(cmd, 0, sizeof(*cmd));
char cmdline[4096];
mkdir("/proc", 0555);
@@ -131,7 +131,7 @@ static int setup_block(struct device *dev, const char *partname) {
buffer[size] = '\0';
close(fd);
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);
found = 1;
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) {
unlink(path);
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);
return ret;
}
@@ -335,70 +343,24 @@ static int dump_magiskrc(const char *path, mode_t mode) {
static void patch_socket_name(const char *path) {
void *buf;
char name[sizeof(MAIN_SOCKET)];
size_t size;
mmap_rw(path, &buf, &size);
if (SOCKET_OFF < 0) {
for (int i = 0; i < size; ++i) {
if (memcmp(buf + i, socket_name, sizeof(SOCKET_NAME)) == 0) {
SOCKET_OFF = i;
break;
}
for (int i = 0; i < size; ++i) {
if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
gen_rand_str(name, sizeof(name));
memcpy(buf + i, name, sizeof(name));
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);
}
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[]) {
umask(0);
@@ -410,6 +372,8 @@ int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "-x") == 0) {
if (strcmp(argv[2], "magisk") == 0)
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)
return dump_magiskrc(argv[3], 0755);
}
@@ -424,14 +388,8 @@ int main(int argc, char *argv[]) {
if (null > STDERR_FILENO)
close(null);
// Extract and link files
mkdir("/overlay", 0000);
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");
// Backup self
link("/init", "/init.bak");
struct cmdline cmd;
parse_cmdline(&cmd);
@@ -444,7 +402,7 @@ int main(int argc, char *argv[]) {
if (cmd.skip_initramfs) {
// Clear rootfs
excl_list = (char *[]) { "overlay", ".backup", NULL };
excl_list = (char *[]) { "overlay", ".backup", "init.bak", NULL };
frm_rf(root);
} else if (access("/ramdisk.cpio.xz", R_OK) == 0) {
// High compression mode
@@ -473,6 +431,9 @@ int main(int argc, char *argv[]) {
* Early Mount
* ************/
int mounted_system = 0;
int mounted_vendor = 0;
// If skip_initramfs or using split policies, we need early mount
if (cmd.skip_initramfs || access("/sepolicy", R_OK) != 0) {
char partname[32];
@@ -499,12 +460,15 @@ int main(int argc, char *argv[]) {
xmount("/system_root/system", "/system", NULL, MS_BIND, NULL);
} else {
xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL);
mounted_system = 1;
}
// Mount vendor
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);
mounted_vendor = 1;
}
}
/* *************
@@ -513,30 +477,32 @@ int main(int argc, char *argv[]) {
// Only patch rootfs if not intended to run in recovery
if (access("/etc/recovery.fstab", F_OK) != 0) {
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
mv_dir(overlay, root);
close(overlay);
rmdir("/overlay");
int fd;
// Handle ramdisk overlays
fd = open("/overlay", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
mv_dir(fd, root);
close(fd);
rmdir("/overlay");
}
patch_ramdisk();
patch_sepolicy();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
if (fork_dont_care() == 0) {
strcpy(argv[0], "magiskinit");
close(root);
magisk_init_daemon();
}
// Dump binaries
dump_magiskrc("/init.magisk.rc", 0750);
dump_magisk("/sbin/magisk", 0755);
patch_socket_name("/sbin/magisk");
rename("/init.bak", "/sbin/magiskinit");
}
// Clean up
close(root);
if (!cmd.skip_initramfs)
if (mounted_system)
umount("/system");
umount("/vendor");
if (mounted_vendor)
umount("/vendor");
// Finally, give control back!
execv("/init", argv);

View File

@@ -8,19 +8,25 @@
#include "utils.h"
#include "magisk.h"
char socket_name[] = SOCKET_NAME;
/* 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);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
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;
}
/*
* Receive a file descriptor from a Unix socket.
* Contributed by @mkasick

View File

@@ -1,4 +1,4 @@
LOCAL_PATH:= $(call my-dir)
LOCAL_PATH := $(call my-dir)
# libsqlite.so (stub)
include $(CLEAR_VARS)
@@ -28,6 +28,16 @@ LOCAL_SRC_FILES := \
mincrypt/sha256.c
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
include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt
@@ -165,7 +175,7 @@ LOCAL_SRC_FILES := \
xz/src/liblzma/simple/simple_encoder.c \
xz/src/liblzma/simple/sparc.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)
# libsepol.a

1
native/jni/external/nanopb vendored Submodule

View File

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

View File

@@ -7,37 +7,40 @@
#include "logging.h"
#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
#define ARG_MAX 4096
#endif
#define LOGFILE "/cache/magisk.log"
#define DEBUG_LOG "/data/adb/magisk_debug.log"
#define UNBLOCKFILE "/dev/.magisk.unblock"
#define PATCHDONE "/dev/.magisk.patch.done"
#define DISABLEFILE "/cache/.disable_magisk"
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
#define CACHEMOUNT "/cache/magisk_mount"
#define MAGISKTMP "/sbin/.core"
#define BLOCKDIR MAGISKTMP "/block"
#define MIRRDIR MAGISKTMP "/mirror"
#define BBPATH MAGISKTMP "/busybox"
#define MOUNTPOINT MAGISKTMP "/img"
#define COREDIR MOUNTPOINT "/.core"
#define HOSTSFILE COREDIR "/hosts"
#define HIDELIST COREDIR "/hidelist"
#define MAINIMG "/data/adb/magisk.img"
#define DATABIN "/data/adb/magisk"
#define SECURE_DIR "/data/adb"
#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 MAGISKRC "/init.magisk.rc"
// selinuxfs paths
#define SELINUX_PATH "/sys/fs/selinux/"
#define SELINUX_ENFORCE SELINUX_PATH "enforce"
#define SELINUX_POLICY SELINUX_PATH "policy"
#define SELINUX_LOAD SELINUX_PATH "load"
#define SELINUX_PATH "/sys/fs/selinux"
#define SELINUX_ENFORCE SELINUX_PATH "/enforce"
#define SELINUX_POLICY SELINUX_PATH "/policy"
#define SELINUX_LOAD SELINUX_PATH "/load"
#define SELINUX_CONTEXT SELINUX_PATH "/context"
// split policy paths
#define PLAT_POLICY_DIR "/system/etc/selinux/"
@@ -51,11 +54,10 @@
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 })
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);
@@ -63,13 +65,7 @@ int create_links(const char *bin, const char *path);
int magiskhide_main(int argc, char *argv[]);
int magiskpolicy_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[]);
#ifdef __cplusplus
}
#endif
int imgtool_main(int argc, char *argv[]);
#endif

View File

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

View File

@@ -4,10 +4,6 @@
#ifndef _RESETPROP_H_
#define _RESETPROP_H_
#ifdef __cplusplus
extern "C" {
#endif
int prop_exist(const char *name);
int setprop(const char *name, const char *value);
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 deleteprop2(const char *name, const int persist);
int read_prop_file(const char* filename, const int trigger);
void getprop_all(void (*callback)(const char*, const char*));
#ifdef __cplusplus
}
#endif
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie);
#endif

View File

@@ -83,7 +83,8 @@ void ps(void (*func)(int));
void ps_filter_proc_name(const char *filter, void (*func)(int));
void unlock_blocks();
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 bind_mount(const char *from, const char *to);
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);
void fclone_attr(const int sourcefd, const int targetfd);
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_rw(const char *filename, 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
#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 get_img_size(const char *img, int *used, int *total);
int resize_img(const char *img, int size);
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);
void trim_img(const char *img);
int trim_img(const char *img, const char *mount, char *loop);
// pattern.c

View File

@@ -14,11 +14,12 @@ struct vector {
void vec_init(struct vector *v);
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_sort(struct vector *v, int (*compar)(const void *, const void *));
void vec_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_cap(v) (v)->cap

View File

@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libfdt.h>
#include <sys/mman.h>
#include "bootimg.h"
@@ -168,7 +169,7 @@ int parse_img(const char *image, boot_img *boot) {
if (pos < boot->map_size) {
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
@@ -181,6 +182,29 @@ int parse_img(const char *image, boot_img *boot) {
// Search for dtb in kernel
for (uint32_t i = 0; i < header(boot, kernel_size); ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
// Check that fdt_header.totalsize does not overflow kernel image size
uint32_t dt_size = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 4));
if (dt_size > header(boot, kernel_size) - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: size (%u) > remaining (%u)\n",
i, dt_size, header(boot, kernel_size) - i);
continue;
}
// Check that fdt_header.off_dt_struct does not overflow kernel image size
uint32_t dt_struct_offset = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 8));
if (dt_struct_offset > header(boot, kernel_size) - i) {
fprintf(stderr, "Invalid DTB detection at 0x%x: struct offset (%u) > remaining (%u)\n",
i, dt_struct_offset, header(boot, kernel_size) - i);
continue;
}
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
uint32_t dt_begin_node = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + dt_struct_offset));
if (dt_begin_node != FDT_BEGIN_NODE) {
fprintf(stderr, "Invalid DTB detection at 0x%x: header tag of first node != FDT_BEGIN_NODE\n", i);
continue;
}
boot->dtb = boot->kernel + i;
boot->dt_size = header(boot, kernel_size) - i;
lheader(boot, kernel_size, = i);

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,65 +16,53 @@
#include <sys/mount.h>
#include "magisk.h"
#include "daemon.h"
#include "utils.h"
#include "magiskhide.h"
static char init_ns[32], zygote_ns[2][32], cache_block[256];
static int hide_queue = 0, zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
static int sockfd = -1;
// Workaround for the lack of pthread_cancel
static void term_thread(int sig) {
LOGD("proc_monitor: running cleanup\n");
destroy_list();
hideEnabled = 0;
// Unregister listener
log_events[HIDE_EVENT].fd = -1;
close(pipefd[0]);
close(pipefd[1]);
pipefd[0] = pipefd[1] = -1;
close(sockfd);
sockfd = -1;
pthread_mutex_destroy(&hide_lock);
pthread_mutex_destroy(&file_lock);
LOGD("proc_monitor: terminating...\n");
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) {
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)
return 1;
xreadlink(path, target, size);
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) {
if (umount2(mountpoint, MNT_DETACH) != -1)
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);
strcpy(argv0, "hide_daemon");
char *line, buffer[PATH_MAX];
struct vector mount_list;
@@ -89,31 +77,9 @@ static void hide_daemon(int pid, int ppid) {
vec_init(&mount_list);
file_to_vector(buffer, &mount_list);
// Find the cache block name if not found yet
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
// Unmount dummy skeletons and /sbin links
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);
lazy_unmount(buffer);
}
@@ -126,23 +92,19 @@ static void hide_daemon(int pid, int ppid) {
vec_init(&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) {
if (strstr(line, "/dev/block/loop")) {
if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
vec_destroy(&mount_list);
exit:
// Send resume signal
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);
}
@@ -151,7 +113,6 @@ void proc_monitor() {
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, TERM_THREAD);
sigaddset(&block_set, HIDE_DONE);
pthread_sigmask(SIG_UNBLOCK, &block_set, NULL);
// Register the cancel signal
@@ -159,107 +120,88 @@ void proc_monitor() {
memset(&act, 0, sizeof(act));
act.sa_handler = term_thread;
sigaction(TERM_THREAD, &act, NULL);
act.sa_handler = hide_done;
sigaction(HIDE_DONE, &act, NULL);
cache_block[0] = '\0';
// Get the mount namespace of init
if (read_namespace(1, init_ns, 32)) {
if (access("/proc/1/ns/mnt", F_OK) != 0) {
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
term_thread(TERM_THREAD);
}
LOGI("proc_monitor: init ns=%s\n", init_ns);
// Get the mount namespace of zygote
zygote_num = 0;
while(!zygote_num) {
// Check zygote every 10 ms
usleep(10000);
ps_filter_proc_name("zygote", store_zygote_ns);
}
ps_filter_proc_name("zygote64", store_zygote_ns);
while(1) {
// Connect to the log daemon
connect_daemon2(LOG_DAEMON, &sockfd);
write_int(sockfd, HIDE_CONNECT);
switch(zygote_num) {
case 1:
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]);
break;
case 2:
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]);
break;
}
FILE *log_in = fdopen(sockfd, "r");
char buf[4096];
while (fgets(buf, sizeof(buf), log_in)) {
char *ss = strchr(buf, '[');
int pid, ppid, num = 0;
char *pos = ss, proc[256], ns[32], pns[32];
// Register our listener to logcat monitor
xpipe2(pipefd, O_CLOEXEC);
log_events[HIDE_EVENT].fd = pipefd[1];
for (char *log, *line;; free(log)) {
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
/* 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;
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++num;
}
if(sscanf(ss, num == 6 ? "[%*d %d %*d %*d %256s" : "[%*d %d %*d %256s", &pid, proc) != 2)
continue;
// Make sure our target is alive
if (kill(pid, 0))
continue;
// Allow hiding sub-services of applications
char *colon = strchr(proc, ':');
if (colon)
*colon = '\0';
int hide = 0;
pthread_mutex_lock(&hide_lock);
char *line;
vec_for_each(hide_list, line) {
if (strcmp(proc, line) == 0) {
hide = 1;
break;
}
}
pthread_mutex_unlock(&hide_lock);
if (!hide)
continue;
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 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>
@@ -65,15 +19,11 @@
#include "magisk.h"
#include "resetprop.h"
extern "C" {
#include "_resetprop.h"
#include "vector.h"
}
#include "utils.h"
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) fprintf(stderr, __VA_ARGS__); }
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
#define PERSISTENT_PROPERTY_DIR "/data/property"
static int verbose = 0;
int prop_verbose = 0;
static int check_legal_property_name(const char *name) {
int namelen = strlen(name);
@@ -129,6 +79,49 @@ static int usage(char* arg0) {
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() {
if (__system_properties_init2()) {
PRINT_E("resetprop: Initialize error\n");
@@ -137,16 +130,36 @@ static int init_resetprop() {
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) {
if (init_resetprop()) return 0;
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) {
return getprop2(name, 0);
}
@@ -155,103 +168,35 @@ char *getprop(const char *name) {
char *getprop2(const char *name, int persist) {
if (check_legal_property_name(name))
return NULL;
char value[PROP_VALUE_MAX];
if (init_resetprop()) return NULL;
const prop_info *pi = __system_property_find2(name);
if (pi == NULL) {
if (persist && strncmp(name, "persist.", 8) == 0) {
// 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) 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:
char *value = persist_getprop(name);
if (value)
return value;
}
PRINT_D("resetprop: prop [%s] does not exist\n", name);
return NULL;
}
} 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 (*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*)) {
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie) {
if (init_resetprop()) return;
struct wrapper wrap = {
.func = callback
struct read_cb_t read_cb = {
.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) {
@@ -294,7 +239,7 @@ int deleteprop(const char *name) {
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))
return 1;
if (init_resetprop()) return -1;
@@ -302,8 +247,8 @@ int deleteprop2(const char *name, const int persist) {
path[0] = '\0';
PRINT_D("resetprop: deleteprop [%s]\n", name);
if (persist && strncmp(name, "persist.", 8) == 0)
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
return __system_property_del(name) && unlink(path);
persist = persist_deleteprop(name);
return __system_property_del(name) && !(persist && strncmp(name, "persist.", 8) == 0);
}
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;
pch = strchr(line, '=');
// Ignore ivalid formats
// Ignore invalid formats
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
// Separate the string
*pch = '\0';
@@ -367,7 +312,7 @@ int resetprop_main(int argc, char *argv[]) {
goto usage;
}
case 'v':
verbose = 1;
prop_verbose = 1;
continue;
case 'p':
persist = 1;
@@ -390,7 +335,7 @@ int resetprop_main(int argc, char *argv[]) {
switch (argc) {
case 0:
print_all_props(persist);
print_props(persist);
return 0;
case 1:
prop = getprop2(argv[0], persist);

View File

@@ -153,7 +153,7 @@ class prop_area {
}
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 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 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),
void* cookie);
@@ -283,7 +283,8 @@ static prop_area* map_prop_area_rw(const char* filename, const char* context,
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;
if (fstat(fd, &fd_stat) < 0) {
return nullptr;
@@ -298,7 +299,7 @@ static prop_area* map_fd_ro(const int fd) {
pa_size = fd_stat.st_size;
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) {
return nullptr;
}
@@ -313,10 +314,10 @@ static prop_area* map_fd_ro(const int fd) {
}
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;
prop_area* map_result = map_fd_ro(fd);
prop_area* map_result = map_fd_rw(fd);
close(fd);
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);
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;
} else {
return false;
@@ -1116,17 +1117,16 @@ static bool initialize_properties() {
if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
return false;
}
if (!initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts")
&& !initialize_properties_from_file("/vendor/etc/selinux/vendor_property_contexts")) {
return false;
}
// Don't check for failure here, so we always have a sane list of properties.
// 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 {
if (!initialize_properties_from_file("/plat_property_contexts")) {
return false;
}
if (!initialize_properties_from_file("/nonplat_property_contexts")) {
return false;
}
initialize_properties_from_file("/nonplat_property_contexts");
}
return true;

View File

@@ -15,6 +15,7 @@
#include <selinux/selinux.h>
#endif
#include "magisk.h"
#include "utils.h"
char **excl_list = NULL;
@@ -165,7 +166,6 @@ void cp_afc(const char *source, const char *destination) {
xmkdirs(destination, a.st.st_mode & 0777);
src = xopen(source, O_RDONLY | O_CLOEXEC);
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
fsetattr(dest, &a);
clone_dir(src, dest);
close(src);
close(dest);
@@ -175,16 +175,15 @@ void cp_afc(const char *source, const char *destination) {
src = xopen(source, O_RDONLY);
dest = xopen(destination, O_WRONLY | O_CREAT | O_TRUNC);
xsendfile(dest, src, NULL, a.st.st_size);
fsetattr(src, &a);
close(src);
close(dest);
} else if (S_ISLNK(a.st.st_mode)) {
char buf[PATH_MAX];
xreadlink(source, buf, sizeof(buf));
xsymlink(buf, destination);
setattr(destination, &a);
}
}
setattr(destination, &a);
}
void clone_dir(int src, int dest) {
@@ -341,18 +340,23 @@ void fclone_attr(const int sourcefd, const int targetfd) {
#ifdef SELINUX
#include "magiskpolicy.h"
#define UNLABEL_CON "u:object_r:unlabeled: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;
DIR *dir;
int fd;
char path[PATH_MAX], *con;
fd_getpath(dirfd, path, sizeof(path));
lgetfilecon(path, &con);
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
size_t len = strlen(path);
getfilecon(path, &con);
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON);
freecon(con);
@@ -361,25 +365,70 @@ void restorecon(int dirfd, int force) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (entry->d_type == DT_DIR) {
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restorecon(fd, force);
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restore_syscon(fd);
close(fd);
} else {
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
fd_getpath(fd, path, sizeof(path));
path[len] = '/';
strcpy(path + len + 1, entry->d_name);
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);
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
static int _mmap(int rw, const char *filename, void **buf, size_t *size) {
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);
if (S_ISBLK(st.st_mode))
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) {
int fd = xopen(filename, O_RDONLY);
int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
*buf = NULL;
*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) {
int fd = xopenat(dirfd, filename, O_RDONLY);
int fd = xopenat(dirfd, filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
*buf = NULL;
*size = 0;

View File

@@ -8,169 +8,196 @@
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/sendfile.h>
#include <sys/statvfs.h>
#include <sys/sysmacros.h>
#include <linux/loop.h>
#include "magisk.h"
#include "utils.h"
static int e2fsck(const char *img) {
// Check and repair ext4 image
char buffer[128];
int pid, fd = -1;
pid = exec_command(1, &fd, NULL, "/system/bin/e2fsck", "-yf", img, NULL);
if (pid < 0)
return 1;
while (fdgets(buffer, sizeof(buffer), fd))
LOGD("magisk_img: %s", buffer);
waitpid(pid, NULL, 0);
close(fd);
return 0;
}
#define round_size(a) ((((a) / 32) + 2) * 32)
#define SOURCE_TMP "/dev/.img_src"
#define TARGET_TMP "/dev/.img_tgt"
#define MERGE_TMP "/dev/.img_mrg"
struct fs_info {
unsigned size;
unsigned free;
unsigned used;
};
static char *loopsetup(const char *img) {
char device[20];
char device[32];
struct loop_info64 info;
int i, lfd, ffd;
int lfd = -1, ffd;
memset(&info, 0, sizeof(info));
// First get an empty loop device
for (i = 0; i <= 7; ++i) {
sprintf(device, "/dev/block/loop%d", i);
lfd = xopen(device, O_RDWR);
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
break;
close(lfd);
if (access(BLOCKDIR, F_OK) == 0) {
for (int i = 8; i < 100; ++i) {
sprintf(device, BLOCKDIR "/loop%02d", i);
if (access(device, F_OK) != 0)
mknod(device, S_IFBLK | 0600, makedev(7, i * 8));
lfd = open(device, O_RDWR);
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);
if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
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);
close(lfd);
close(ffd);
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) {
if (size == 128) /* WTF...? */
size = 132;
unlink(img);
LOGI("Create %s with size %dM\n", img, size);
int ret;
char size_str[16];
snprintf(size_str, sizeof(size_str), "%dM", size);
ret = exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL);
if (ret < 0) {
if (access("/system/bin/make_ext4fs", X_OK) == 0)
return exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL);
else if (access("/system/bin/mke2fs", X_OK) == 0)
// On Android P there is no make_ext4fs, use mke2fs
ret = exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
}
return ret;
}
int get_img_size(const char *img, int *used, int *total) {
if (access(img, R_OK) == -1)
return exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
else
return 1;
char buffer[PATH_MAX];
int pid, fd = -1, status = 1;
pid = exec_command(1, &fd, NULL, "/system/bin/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) {
LOGI("Resize %s to %dM\n", img, size);
if (e2fsck(img))
return 1;
char buffer[128];
int pid, fd = -1, used, total;
snprintf(buffer, sizeof(buffer), "%dM", size);
pid = exec_command(1, &fd, NULL, "/system/bin/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;
exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
char ss[16];
snprintf(ss, sizeof(ss), "%dM", size);
return exec_command_sync("/system/bin/resize2fs", img, ss, NULL);
}
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;
if (access(target, F_OK) == -1) {
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;
exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
char *device = loopsetup(img);
if (device)
xmount(device, target, "ext4", 0, NULL);
return device;
}
void umount_image(const char *target, const char *device) {
xumount(target);
int umount_image(const char *target, const char *device) {
int ret = 0;
ret |= xumount(target);
int fd = xopen(device, O_RDWR);
ioctl(fd, LOOP_CLR_FD);
ret |= ioctl(fd, LOOP_CLR_FD);
close(fd);
return ret;
}
int merge_img(const char *source, const char *target) {
if (access(source, F_OK) == -1)
return 0;
LOGI("* Merging %s -> %s\n", source, target);
if (access(target, F_OK) == -1) {
LOGI("* Move %s -> %s\n", source, target);
if (rename(source, target) < 0) {
// Copy and remove
int tgt = creat(target, 0644);
@@ -183,24 +210,22 @@ int merge_img(const char *source, const char *target) {
return 0;
}
char buffer[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);
char buf[PATH_MAX];
xmkdir(SOURCE_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);
if (s_loop == NULL) return 1;
if (s_loop == NULL)
return 1;
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;
struct dirent *entry;
if (!(dir = xopendir(SOURCE_TMP)))
@@ -209,37 +234,60 @@ int merge_img(const char *source, const char *target) {
if (entry->d_type == DT_DIR) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0 ||
strcmp(entry->d_name, ".core") == 0 ||
strcmp(entry->d_name, "lost+found") == 0)
strcmp(entry->d_name, ".core") == 0)
continue;
// Cleanup old module if exists
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
if (access(buffer, F_OK) == 0) {
LOGI("Upgrade module: %s\n", entry->d_name);
rm_rf(buffer);
} else {
LOGI("New module: %s\n", entry->d_name);
}
snprintf(buf, sizeof(buf), "%s/%s", TARGET_TMP, entry->d_name);
if (access(buf, F_OK) == 0)
rm_rf(buf);
LOGI("Upgrade/New module: %s\n", entry->d_name);
}
}
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
umount_image(SOURCE_TMP, s_loop);
umount_image(TARGET_TMP, t_loop);
umount_image(MERGE_TMP, m_loop);
rmdir(SOURCE_TMP);
rmdir(TARGET_TMP);
rmdir(MERGE_TMP);
free(s_loop);
free(t_loop);
free(m_loop);
// Cleanup
unlink(source);
LOGI("* Move %s -> %s", buf, target);
rename(buf, target);
return 0;
}
void trim_img(const char *img) {
int used, total, new_size;
get_img_size(img, &used, &total);
new_size = round_size(used);
if (new_size != total)
int trim_img(const char *img, const char *mount, char *loop) {
struct fs_info info;
check_filesystem(&info, img, mount);
int new_size = round_size(info.used);
if (info.size > new_size) {
umount_image(mount, loop);
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!! */
int file_to_vector(const char* filename, struct vector *v) {
if (access(filename, R_OK) != 0)
return 1;
char *line = NULL;
size_t len = 0;
ssize_t read;
@@ -168,22 +170,38 @@ void ps(void (*func)(int)) {
static void (*ps_filter_cb)(int);
static const char *ps_filter_pattern;
static void proc_name_filter(int pid) {
char buf[64];
int fd;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
char buf[128];
FILE *f;
sprintf(buf, "/proc/%d/comm", pid);
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;
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) {
ps_filter_cb(pid);
}
close(fd);
fclose(f);
sprintf(buf, "/proc/%d/cmdline", pid);
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 */
@@ -227,9 +245,9 @@ void setup_sighandlers(void (*handler)(int)) {
fd == NULL -> Ignore output
*fd < 0 -> Open pipe and set *fd to the read end
*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;
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
char *const *envp;
struct vector env;
vec_init(&env);
if (setupenv) {
setupenv(&env);
if (setenv) {
setenv(&env);
envp = (char **) vec_entry(&env);
} else {
extern char **environ;
@@ -269,21 +279,34 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
*fd = pipefd[0];
close(pipefd[1]);
}
vec_deep_destroy(&args);
vec_deep_destroy(&env);
return pid;
}
if (fd) {
xdup2(writeEnd, STDOUT_FILENO);
if (err) xdup2(writeEnd, STDERR_FILENO);
if (err)
xdup2(writeEnd, STDERR_FILENO);
}
execvpe(argv0, (char **) vec_entry(&args), envp);
PLOGE("execvpe %s", argv0);
execvpe(argv[0], argv, envp);
PLOGE("execvpe %s", argv[0]);
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, ...) {
va_list argv;
va_start(argv, argv0);
@@ -296,10 +319,10 @@ int exec_command_sync(char *const argv0, ...) {
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_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);
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 skip, src_size = *size;
int skip, src_size = *size, found = 0;
char *src = *buf, *patched = patch ? xcalloc(src_size, 1) : NULL;
for (int read = 0, write = 0; read < src_size; ++read, ++write) {
if ((skip = check_verity_pattern(src + read)) > 0) {
if (!patch)
return 1;
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
if (patch) {
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
*size -= skip;
} else {
fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read);
}
read += skip;
*size -= skip;
found = 1;
}
if (patch)
patched[write] = src[read];
@@ -76,7 +79,7 @@ int patch_verity(void **buf, uint32_t *size, int patch) {
free(*buf);
*buf = patched;
}
return 0;
return found;
}
void patch_encryption(void **buf, uint32_t *size) {

View File

@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "vector.h"
@@ -23,6 +24,15 @@ void vec_push_back(struct vector *v, void *p) {
++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 *ret = vec_entry(v)[vec_size(v) - 1];
--vec_size(v);
@@ -73,11 +83,9 @@ void vec_deep_destroy(struct vector *v) {
vec_destroy(v);
}
struct vector *vec_dup(struct vector *v) {
struct vector *ret = malloc(sizeof(*ret));
vec_size(ret) = vec_size(v);
vec_cap(ret) = vec_cap(v);
vec_entry(v) = malloc(sizeof(void*) * vec_cap(ret));
memcpy(vec_entry(ret), vec_entry(v), sizeof(void*) * vec_cap(ret));
return ret;
void vec_dup(struct vector *v, struct vector *vv) {
vec_size(vv) = vec_size(v);
vec_cap(vv) = vec_cap(v);
vec_entry(vv) = malloc(sizeof(void*) * vec_cap(v));
memcpy(vec_entry(vv), vec_entry(v), sizeof(void*) * vec_cap(v));
}

View File

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

View File

@@ -4,18 +4,25 @@
# Magisk Boot Image Patcher
# 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:
#
# File name type Description
# File name Type Description
#
# 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
# to complete the patching process
# monogisk binary The monolithic binary to replace /init
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk
# , 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 in the tablet Pixel C
# 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
# to complete the patching process
# util_functions.sh script A script which hosts all functions requires for this script
# to work properly
# magiskinit binary The binary to replace /init, which has the magisk binary embedded
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk,
# 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
# or have a backup included in ramdisk internally, since we cannot access the stock boot
@@ -27,51 +34,32 @@
##########################################################################################
# Pure bash dirname implementation
dirname_wrap() {
getdir() {
case "$1" in
*/*)
dir=${1%/*}
[ -z $dir ] && echo "/" || echo $dir
;;
*)
echo "."
;;
*/*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
*) echo "." ;;
esac
}
# Pure bash basename implementation
basename_wrap() {
echo ${1##*/}
}
##########################################################################################
# Initialization
##########################################################################################
if [ -z $SOURCEDMODE ]; then
# Switch to the location of the script file
cd "`dirname_wrap "${BASH_SOURCE:-$0}"`"
cd "`getdir "${BASH_SOURCE:-$0}"`"
# Load utility functions
. ./util_functions.sh
fi
BOOTIMAGE="$1"
[ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!"
# Presets
# Flags
[ -z $KEEPVERITY ] && KEEPVERITY=false
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=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 .
# Extract magisk if doesn't exist
@@ -157,7 +145,7 @@ fi
ui_print "- Patching ramdisk"
./magiskboot --cpio ramdisk.cpio \
'add 750 init magiskinit' \
"add 750 init magiskinit" \
"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1"
rm -f ramdisk.cpio.orig
@@ -166,8 +154,9 @@ rm -f ramdisk.cpio.orig
# Binary patches
##########################################################################################
if ! $KEEPVERITY && [ -f dtb ]; then
./magiskboot --dtb-patch dtb && ui_print "- Patching fstab in dtb to remove dm-verity"
if ! $KEEPVERITY; then
[ -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
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"
mount_partitions
# read override variables
getvar KEEPVERITY
getvar KEEPFORCEENCRYPT
getvar BOOTIMAGE
find_boot_image
find_dtbo_image
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
ui_print "- Found boot/ramdisk image: $BOOTIMAGE"
get_flags
if [ ! -z $DTBOIMAGE ]; then
ui_print "- Found dtbo image: $DTBOIMAGE"
# Disable dtbo patch by default
[ -z $KEEPVERITY ] && KEEPVERITY=true
fi
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
ui_print "- Target image: $BOOTIMAGE"
[ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
# Detect version and architecture
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"
BINDIR=$INSTALLER/$ARCH
BINDIR=$INSTALLER/$ARCH32
chmod -R 755 $CHROMEDIR $BINDIR
# Check if system root is installed and remove
@@ -81,15 +76,11 @@ remove_system_su
ui_print "- Constructing environment"
# Check if we can actually access the data (DE storage)
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
check_data
if $DATA; then
MAGISKBIN=/data/adb/magisk
MAGISKBIN=/data/.magisk
$DATA_DE && MAGISKBIN=/data/adb/magisk
run_migrations
else
MAGISKBIN=/cache/data_bin
@@ -105,7 +96,9 @@ chmod -R 755 $MAGISKBIN
if [ -d /system/addon.d ]; then
ui_print "- Adding addon.d survival script"
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
fi
@@ -122,7 +115,7 @@ SOURCEDMODE=true
cd $MAGISKBIN
# Source the boot patcher
. $COMMONDIR/boot_patch.sh "$BOOTIMAGE"
. ./boot_patch.sh "$BOOTIMAGE"
flash_boot_image new-boot.img "$BOOTIMAGE"
rm -f new-boot.img

View File

@@ -1,52 +1,75 @@
#!/system/bin/sh
#MAGISK
##########################################################################################
#
# Magisk Uninstaller
# Magisk Uninstaller (used in recovery)
# by topjohnwu
#
# This script can be placed in /cache/magisk_uninstaller.sh
# 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)
# This script will load the real uninstaller in a flashable zip
#
##########################################################################################
[ -z $BOOTMODE ] && BOOTMODE=false
##########################################################################################
# Preparation
##########################################################################################
[ -d /data/adb/magisk ] && MAGISKBIN=/data/adb/magisk || MAGISKBIN=/data/magisk
CHROMEDIR=$MAGISKBIN/chromeos
# This path should work in any cases
TMPDIR=/dev/tmp
if [ ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
echo "! Cannot find $MAGISKBIN"
INSTALLER=$TMPDIR/install
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
fi
if $BOOTMODE; then
# Load utility functions
. $MAGISKBIN/util_functions.sh
BOOTMODE=true
boot_actions
mount_partitions
fi
# Load utility functions
. $INSTALLER/util_functions.sh
get_outfd
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"
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
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
ui_print "- Found Boot Image: $BOOTIMAGE"
CHROMEOS=false
ui_print "- Unpacking boot image"
./magiskboot --unpack "$BOOTIMAGE"
CHROMEOS=false
case $? in
1 )
@@ -58,7 +81,7 @@ case $? in
;;
4 )
ui_print "! Sony ELF32 format detected"
abort "! Please use BootBridge from @AdrianDC to flash Magisk"
abort "! Please use BootBridge from @AdrianDC"
;;
5 )
ui_print "! Sony ELF64 format detected"
@@ -71,22 +94,29 @@ ui_print "- Checking ramdisk status"
case $? in
0 ) # Stock boot
ui_print "- Stock boot image detected"
abort "! Magisk is not installed!"
;;
1|2 ) # Magisk patched
ui_print "- Magisk patched image detected"
./magisk --unlock-blocks 2>/dev/null
# Find SHA1 of stock boot image
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
OK=false
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true
if ! $OK; then
STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
STOCKDTBO=/data/stock_dtbo.img.gz
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 "- Restoring ramdisk with internal backup"
./magiskboot --cpio ramdisk.cpio restore
./magiskboot --repack $BOOTIMAGE
# Sign chromeos boot
$CHROMEOS && sign_chromeos
flash_boot_image new-boot.img "$BOOTIMAGE"
flash_boot_image new-boot.img $BOOTIMAGE
fi
;;
3 ) # Other patched
@@ -95,12 +125,33 @@ case $? in
;;
esac
cd /
ui_print "- Removing Magisk files"
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/user*/*/magisk.db /data/user*/*/com.topjohnwu.magisk /data/user*/*/.tmp.magisk.config \
/data/adb/*magisk* 2>/dev/null
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /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
dirname_wrap() {
getdir() {
case "$1" in
*/*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
*) echo "." ;;
esac
}
[ "$1" = "indep" ] && INDEP=true || INDEP=false
$INDEP && TMPDIR="`dirname_wrap "${BASH_SOURCE:-$0}"`" || TMPDIR=/dev/tmp
INSTALLER=$TMPDIR/install; BBDIR=$TMPDIR/bin
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox
$INDEP || rm -rf $TMPDIR 2>/dev/null;
mkdir -p $BBDIR 2>/dev/null
touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN
echo -ne $EX_ARM > $EXBIN
if $EXBIN --test 2>/dev/null; then
echo $BB_ARM | $EXBIN > $BBBIN
else
echo -ne $EX_X86 > $EXBIN
echo $BB_X86 | $EXBIN > $BBBIN
fi
$BBBIN --install -s $TMPDIR/bin
export PATH=$BBDIR:$PATH
if $INDEP; then
shift
exec sh "$@"
else
mkdir -p $INSTALLER
unzip -o "$3" -d $INSTALLER
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@
fi
extract_bb() {
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox
touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN
echo -ne $EX_ARM > $EXBIN
if $EXBIN --test 2>/dev/null; then
echo $BB_ARM | $EXBIN > $BBBIN
else
echo -ne $EX_X86 > $EXBIN
echo $BB_X86 | $EXBIN > $BBBIN
fi
rm $EXBIN
}
setup_bb() {
BBDIR=$TMPDIR/bin; mkdir -p $BBDIR 2>/dev/null
extract_bb
$BBBIN --install -s $BBDIR
export PATH=$BBDIR:$PATH
}
case "$1" in
"extract")
[ -z "$2" ] && BBDIR=. || BBDIR="$2"
extract_bb
;;
"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
SCRIPT_VERSION=$MAGISK_VER_CODE
# Detect whether in boot mode
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
get_outfd() {
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
if [ "$?" -eq "0" ]; then
OUTFD=0
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
# We will have to manually find out OUTFD
for FD in `ls /proc/$$/fd`; do
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
if [ "$?" -eq "0" ]; then
ps | grep " 3 $FD " | grep -v grep >/dev/null
if [ "$?" -eq "0" ]; then
if readlink /proc/$$/fd/$FD | grep -q pipe; then
if ps | grep -v grep | grep -q " 3 $FD "; then
OUTFD=$FD
break
fi
@@ -45,49 +40,102 @@ ui_print() {
$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() {
# Check A/B slot
SLOT=`getprop ro.boot.slot_suffix`
SLOT=`grep_cmdline androidboot.slot_suffix`
if [ -z $SLOT ]; then
SLOT=_`getprop ro.boot.slot`
SLOT=_`grep_cmdline androidboot.slot`
[ $SLOT = "_" ] && SLOT=
fi
# 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"
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
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
SYSTEMBLOCK=`find /dev/block -iname system$SLOT | head -n 1`
SYSTEMBLOCK=`find_block system$SLOT`
mount -t ext4 -o ro $SYSTEMBLOCK /system
fi
is_mounted /system || [ -f /system/build.prop ] || abort "! Cannot mount /system"
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SKIP_INITRAMFS=true || SKIP_INITRAMFS=false
if [ -f /system/init.rc ]; then
SKIP_INITRAMFS=true
[ -f /system/build.prop ] || is_mounted /system || abort "! Cannot mount /system"
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SYSTEM_ROOT=true || SYSTEM_ROOT=false
if [ -f /system/init ]; then
SYSTEM_ROOT=true
mkdir /system_root 2>/dev/null
mount --move /system /system_root
mount -o bind /system_root/system /system
fi
$SKIP_INITRAMFS && ui_print "- Device skip_initramfs detected"
$SYSTEM_ROOT && ui_print "- Device using system_root_image"
if [ -L /system/vendor ]; then
# Seperate /vendor partition
is_mounted /vendor || mount -o ro /vendor 2>/dev/null
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
fi
is_mounted /vendor || abort "! Cannot mount /vendor"
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() {
REGEX="s/^$1=//p"
local REGEX="s/^$1=//p"
shift
FILES=$@
local FILES=$@
[ -z "$FILES" ] && FILES='/system/build.prop'
sed -n "$REGEX" $FILES 2>/dev/null | head -n 1
}
@@ -95,36 +143,10 @@ grep_prop() {
getvar() {
local VARNAME=$1
local VALUE=
for DIR in /.backup /dev /data /cache /system; do
VALUE=`grep_prop $VARNAME $DIR/.magisk`
[ ! -z $VALUE ] && break;
done
VALUE=`grep_prop $VARNAME /.backup/.magisk /data/.magisk /cache/.magisk /system/.magisk`
[ ! -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() {
# Update the broken boot backup
if [ -f /data/stock_boot_.img.gz ]; then
@@ -140,11 +162,9 @@ run_migrations() {
fi
# Move the stock backups
if [ -f /data/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/magisk/stock_boot* /data 2>/dev/null
fi
if [ -f /data/adb/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/adb/magisk/stock_boot* /data 2>/dev/null
fi
# Remove old dbs
@@ -152,6 +172,19 @@ run_migrations() {
[ -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() {
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
@@ -178,16 +211,15 @@ flash_boot_image() {
}
find_dtbo_image() {
DTBOIMAGE=`find /dev/block -iname dtbo$SLOT | head -n 1` 2>/dev/null
[ ! -z $DTBOIMAGE ] && DTBOIMAGE=`readlink -f $DTBOIMAGE`
DTBOIMAGE=`find_block dtbo$SLOT`
}
patch_dtbo_image() {
if [ ! -z $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
ui_print "- Patching fstab in dtbo to remove avb-verity"
ui_print "- Patching DTBO to remove avb-verity"
$MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE
return 0
fi
@@ -195,27 +227,6 @@ patch_dtbo_image() {
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() {
ui_print "- Signing ChromeOS boot image"
@@ -229,8 +240,7 @@ sign_chromeos() {
}
is_mounted() {
TARGET="`readlink -f $1`"
cat /proc/mounts | grep " $TARGET " >/dev/null
cat /proc/mounts | grep -q " `readlink -f $1` " 2>/dev/null
return $?
}
@@ -266,11 +276,39 @@ api_level_arch_detect() {
ABILONG=`grep_prop ro.product.cpu.abi`
ARCH=arm
ARCH32=arm
IS64BIT=false
if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
if [ "$ABI" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABI2" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; ARCH32=arm; 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() {
@@ -279,6 +317,7 @@ boot_actions() {
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
fi
MAGISKBIN=/sbin/.core/mirror/bin
setup_bb
}
recovery_actions() {
@@ -286,24 +325,21 @@ recovery_actions() {
mount -o bind /dev/urandom /dev/random
# Preserve environment varibles
OLD_PATH=$PATH
OLD_LD_PATH=$LD_LIBRARY_PATH
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
setup_bb
# Temporarily block out all custom recovery binaries/libs
mv /sbin /sbin_tmp
# Add all possible library paths
$IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib
# Unset library paths
OLD_LD_LIB=$LD_LIBRARY_PATH
OLD_LD_PRE=$LD_PRELOAD
unset LD_LIBRARY_PATH
unset LD_PRELOAD
}
recovery_cleanup() {
mv /sbin_tmp /sbin 2>/dev/null
export LD_LIBRARY_PATH=$OLD_LD_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"
umount -l /system_root 2>/dev/null
umount -l /system 2>/dev/null
@@ -339,51 +375,55 @@ mktouch() {
}
request_size_check() {
reqSizeM=`du -s $1 | cut -f1`
reqSizeM=$((reqSizeM / 1024 + 1))
reqSizeM=`du -ms $1 | cut -f1`
}
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() {
SIZE="`$MAGISKBIN/magisk --imgsize $IMG`"
curUsedM=`echo "$SIZE" | cut -d" " -f1`
curSizeM=`echo "$SIZE" | cut -d" " -f2`
curFreeM=$((curSizeM - curUsedM))
check_filesystem() {
curSizeM=`wc -c < $1`
curSizeM=$((curSizeM / 1048576))
local DF=`df -P $2 | grep $2`
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() {
[ -z reqSizeM ] && reqSizeM=0
mkdir -p $MOUNTPATH 2>/dev/null
if [ -f "$IMG" ]; then
ui_print "- Found $IMG"
image_size_check $IMG
if [ "$reqSizeM" -gt "$curFreeM" ]; then
newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64))
mount_snippet
check_filesystem $IMG $MOUNTPATH
if [ $reqSizeM -gt $curFreeM ]; then
newSizeM=$(((curSizeM + reqSizeM - curFreeM) / 32 * 32 + 64))
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
ui_print "- Mount $IMG to $MOUNTPATH"
else
newSizeM=$((reqSizeM / 32 * 32 + 64));
newSizeM=$((reqSizeM / 32 * 32 + 64))
ui_print "- Creating $IMG with size ${newSizeM}M"
$MAGISKBIN/magisk --createimg $IMG $newSizeM >&2
$MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
mount_snippet
fi
ui_print "- Mounting $IMG to $MOUNTPATH"
MAGISKLOOP=`$MAGISKBIN/magisk --mountimg $IMG $MOUNTPATH`
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
}
unmount_magisk_img() {
$MAGISKBIN/magisk --umountimg $MOUNTPATH $MAGISKLOOP
# Shrink the image if possible
image_size_check $IMG
check_filesystem $IMG $MOUNTPATH
newSizeM=$((curUsedM / 32 * 32 + 64))
$MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
if [ $curSizeM -gt $newSizeM ]; then
ui_print "- Shrinking $IMG to ${newSizeM}M"
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM
$MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
fi
}

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.topjohnwu.snet"
minSdkVersion 21
targetSdkVersion 27
minSdkVersion 14
targetSdkVersion rootProject.ext.compileSdkVersion
versionCode 1
versionName "1.0"
}
@@ -15,16 +15,13 @@ android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
repositories {
google()
}
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 */
}

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;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
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.api.GoogleApiClient;
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.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.JSONObject;
import java.lang.reflect.Field;
import java.security.SecureRandom;
public class SafetyNetHelper
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public class SafetyNetHelper implements ISafetyNetHelper, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, ResultCallback<SafetyNetApi.AttestationResult> {
public static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
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 CTS_PASS = 0x20;
public static final int SNET_EXT_VER = 7;
public static final int SNET_EXT_VER = 8;
private GoogleApiClient mGoogleApiClient;
private Activity mActivity;
private int responseCode;
private SafetyNetCallback cb;
private String dexPath;
private boolean isDarkTheme;
private Callback callback;
public static int getVersion() {
@Override
public int getVersion() {
return SNET_EXT_VER;
}
public SafetyNetHelper(Activity activity, String dexPath, SafetyNetCallback cb) {
public SafetyNetHelper(Activity activity, Callback cb) {
mActivity = activity;
this.cb = 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();
}
callback = cb;
}
// Entry point to start test
@Override
public void attest() {
// Connect Google Service
mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
@@ -74,26 +61,15 @@ public class SafetyNetHelper
@Override
public void onConnectionSuspended(int i) {
cb.onResponse(i);
callback.onResponse(i);
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult result) {
Class<? extends Activity> clazz = mActivity.getClass();
try {
// Use external resources
clazz.getMethod("swapResources", String.class, int.class).invoke(mActivity, dexPath,
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);
mActivity.swapResources(CheckSafetyNet.dexPath.getPath());
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
mActivity.restoreResources();
callback.onResponse(CONNECTION_FAIL);
}
@Override
@@ -103,28 +79,28 @@ public class SafetyNetHelper
new SecureRandom().nextBytes(nonce);
// Call SafetyNet
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
.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;
}
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce).setResultCallback(this);
}
// Disconnect
mGoogleApiClient.disconnect();
@Override
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
cb.onResponse(responseCode);
}
});
// Disconnect
mGoogleApiClient.disconnect();
// Return results
callback.onResponse(code);
}
}

View File

@@ -15,7 +15,7 @@ jar {
shadowJar {
baseName = 'zipsigner'
classifier = null
version = 2.1
version = 2.2
}
buildscript {
@@ -23,7 +23,7 @@ buildscript {
jcenter()
}
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 {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.bouncycastle:bcprov-jdk15on:1.58'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.58'
implementation 'org.bouncycastle:bcprov-jdk15on:1.59'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.59'
}

View File

@@ -24,10 +24,11 @@ import java.util.zip.ZipFile;
* */
public class JarMap implements Closeable, AutoCloseable {
private JarFile jarFile;
private JarInputStream jis;
private boolean isInputStream = false;
private LinkedHashMap<String, JarEntry> bufMap;
private Manifest manifest;
public JarMap(File file) throws IOException {
this(file, true);
@@ -39,6 +40,7 @@ public class JarMap implements Closeable, AutoCloseable {
public JarMap(File file, boolean verify, int mode) throws IOException {
jarFile = new JarFile(file, verify, mode);
manifest = jarFile.getManifest();
}
public JarMap(String name) throws IOException {
@@ -54,48 +56,54 @@ public class JarMap implements Closeable, AutoCloseable {
}
public JarMap(InputStream is, boolean verify) throws IOException {
isInputStream = true;
bufMap = new LinkedHashMap<>();
jis = new JarInputStream(is, verify);
bufMap = new LinkedHashMap<>();
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
}
manifest = jis.getManifest();
}
public File getFile() {
return isInputStream ? null : new File(jarFile.getName());
return jarFile == null ? null : new File(jarFile.getName());
}
public Manifest getManifest() throws IOException {
return isInputStream ? jis.getManifest() : jarFile.getManifest();
public Manifest getManifest() {
return manifest;
}
public InputStream getInputStream(ZipEntry ze) throws IOException {
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() :
jarFile.getInputStream(ze);
if (bufMap != null) {
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
if (e != null)
return e.data.getInputStream();
}
return jarFile.getInputStream(ze);
}
public OutputStream getOutputStream(ZipEntry ze) {
if (!isInputStream) // Only support InputStream mode
return null;
ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data;
bs.reset();
return bs;
manifest = null; /* Invalidate the manifest */
if (bufMap == null)
bufMap = new LinkedHashMap<>();
JarMapEntry e = new JarMapEntry(ze.getName());
bufMap.put(ze.getName(), e);
return e.data;
}
public byte[] getRawData(ZipEntry ze) throws IOException {
if (isInputStream) {
return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray();
} else {
ByteArrayStream bytes = new ByteArrayStream();
bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray();
if (bufMap != null) {
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
if (e != null)
return e.data.toByteArray();
}
ByteArrayStream bytes = new ByteArrayStream();
bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray();
}
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) {
@@ -103,20 +111,29 @@ public class JarMap implements Closeable, AutoCloseable {
}
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
public void close() throws IOException {
(isInputStream ? jis : jarFile).close();
(jarFile == null ? jis : jarFile).close();
}
private static class JarMapEntry extends JarEntry {
ByteArrayStream data;
JarMapEntry(JarEntry je, InputStream is) {
super(je);
data = new ByteArrayStream();
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.util.encoders.Base64;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -73,19 +74,31 @@ public class SignAPK {
JarMap input, OutputStream output) throws Exception {
File temp1 = 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 (OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))) {
signZip(cert, key, input, out, false);
signZip(c, k, input, out, false);
}
ZipAdjust.adjust(temp1, temp2);
try (JarMap map = new JarMap(temp2, false)) {
signZip(cert, key, map, output, true);
signZip(c, k, map, output, true);
}
} finally {
temp1.delete();
temp2.delete();
c.destroy();
k.destroy();
}
}

View File

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