Compare commits

..

155 Commits
v14.0 ... v15.2

Author SHA1 Message Date
topjohnwu
4fd61345af Happy New Year 2018-01-02 01:27:20 +08:00
topjohnwu
66cca24453 Samsung need more rules :) 2018-01-02 00:11:26 +08:00
topjohnwu
e733484fab Some devices don't like all log buffers 2018-01-01 23:58:13 +08:00
topjohnwu
e5c3183025 Update scripts 2018-01-01 16:46:28 +08:00
topjohnwu
930c82316a Slightly change logging style 2017-12-31 21:54:39 +08:00
Shaka Huang
3dc22db265 Support loading split sepolicy on non skip_initramfs devices
For certain device (e.g ZenFone 4 ZE554KL) there’s no sepolicy under rootfs and no a/b partition (implies no vendor partition) Magisk will failed to patch SELinux policy database and the system won’t boot up.

In order to cope with this configuration the status of loading policy db needs to be checked, once it failed we have to mount the system partition and do patch_sepolicy() again.

Signed-off-by: Shaka Huang <shakalaca@gmail.com>
2017-12-31 21:30:56 +08:00
topjohnwu
d8c51cb286 Update sepolicy handling 2017-12-31 19:32:04 +08:00
topjohnwu
2f79d0c3b3 Fix segfault while patching dtb 2017-12-31 19:30:56 +08:00
topjohnwu
d8bb3af06b Miscellaneous 2017-12-29 04:25:30 +08:00
topjohnwu
e139e8777b Fix faulty magiskboot ramdisk patch code 2017-12-29 04:25:03 +08:00
topjohnwu
d52d7cfbd9 Update magiskpolicy 2017-12-26 04:24:48 +08:00
topjohnwu
4f74a259e3 Update Magisk Manager 2017-12-26 04:00:01 +08:00
topjohnwu
74da6e1dc0 Support new 1500 template 2017-12-26 03:23:58 +08:00
topjohnwu
84ffdf0ed5 Eliminate a possbility to cause segfault 2017-12-25 05:21:40 +08:00
topjohnwu
022b18c8ce Properly detect /data status 2017-12-25 05:21:40 +08:00
topjohnwu
b92b1dcddb cil: Allow redeclare types[attributes] and disable neverallow 2017-12-25 05:21:40 +08:00
topjohnwu
1472dbb291 Add cache magisk image merging support 2017-12-22 16:10:38 +08:00
topjohnwu
d58a8dc868 Update Magisk Manager 2017-12-22 06:44:22 +08:00
topjohnwu
e94be0b70e Prevent creating 128M images
make_ext4fs will fail creating 128M ext4 images, LOL WTF? Change it to 132M to fix this issue
2017-12-22 02:39:44 +08:00
topjohnwu
f6ae7e1bf1 Allow su to work when manager uninstalled 2017-12-22 00:30:08 +08:00
topjohnwu
f7b4935677 Add recommend KEEPVERITY and KEEPFORCEENCRYPT flags 2017-12-21 15:42:03 +08:00
topjohnwu
a3c49de6a5 Refactor magiskboot 2017-12-21 03:36:18 +08:00
topjohnwu
e8dd1b292f Update snet 2017-12-19 15:51:01 +08:00
topjohnwu
d21264d01b Let magiskinit directly spawn new magisk_daemon process 2017-12-19 14:27:54 +08:00
topjohnwu
b0567eadfd Reduce effort and memory of log monitor 2017-12-18 18:17:37 +08:00
topjohnwu
5fc2058336 Show version code when report client 2017-12-18 16:00:44 +08:00
topjohnwu
d0567d29d2 Update su to match Linux's implementation 2017-12-18 15:46:18 +08:00
topjohnwu
4db0ad32f0 Fix install scripts 2017-12-18 03:08:53 +08:00
topjohnwu
d065040321 Fix magiskinit invincible mode and logcat monitor 2017-12-18 02:51:27 +08:00
topjohnwu
17f0fea3fc Fix unreleased resource in rootfs 2017-12-16 04:42:16 +08:00
topjohnwu
8ca1e43533 Move all /data files into /data/adb 2017-12-16 04:42:16 +08:00
topjohnwu
bd01c314dc Change database location 2017-12-12 03:04:55 +08:00
topjohnwu
e404476609 Fix logs on decrypted /data 2017-12-07 23:25:43 +08:00
topjohnwu
942c870981 Properly handle KEEPVERITY and HIGHCOMP 2017-12-07 04:20:27 +08:00
topjohnwu
baff9256c5 Dynamic patch verity and forceencrypt flag 2017-12-07 03:21:13 +08:00
topjohnwu
b4c0a255fc Separate pattern logic 2017-12-07 01:30:48 +08:00
topjohnwu
9f6a27c20d Add high compression ramdisk support 2017-12-06 12:51:16 +08:00
topjohnwu
742dc137ed More fail proof to magiskinit 2017-12-05 21:05:20 +08:00
topjohnwu
39a6bd33ce Fix critical bug 2017-12-05 05:32:15 +08:00
topjohnwu
4672a5fad6 Add cpio extract all feature 2017-12-05 03:32:37 +08:00
topjohnwu
e649b0a2df Update README.md 2017-12-04 22:59:29 +08:00
topjohnwu
fd8dbe3eff Greatly reduce Gradle sync time and frequency 2017-12-04 22:21:19 +08:00
topjohnwu
bb97cc594d Cleanup and small fixes 2017-12-04 18:05:07 +08:00
topjohnwu
70a322263e Fix Android Studio gradle sync 2017-12-04 15:32:18 +08:00
topjohnwu
c6f144d482 Update README.md 2017-12-04 15:21:06 +08:00
topjohnwu
3709489b3a Massive project restructure 2017-12-04 15:16:41 +08:00
topjohnwu
145ef32e28 Tidy up external 2017-12-04 03:37:00 +08:00
topjohnwu
2212800a23 Add symlink feature to cpio 2017-12-04 03:37:00 +08:00
topjohnwu
2e25431bb6 Generalize cpio functions 2017-12-04 03:37:00 +08:00
topjohnwu
32c8e7522f More precise logging configuration 2017-12-01 17:38:57 +08:00
topjohnwu
a5e4f3cc6b Separate cpio logic from MagiskBoot 2017-12-01 17:17:24 +08:00
topjohnwu
a30777bd9f Fix bug in compiling split cil 2017-12-01 03:33:25 +08:00
topjohnwu
e989195a68 Update magiskpolicy 2017-11-30 20:57:40 +08:00
topjohnwu
997d58932e Adjust daemon initialization 2017-11-28 04:43:46 +08:00
topjohnwu
b4015f877f New invincible implementation 2017-11-28 03:42:48 +08:00
topjohnwu
d15fff95b9 Use inotify to monitor files 2017-11-27 15:37:28 +08:00
topjohnwu
687e3b13ea Bump Magisk Manager version 2017-11-23 23:56:17 +08:00
topjohnwu
8c6bb383b7 Add support to build with NDK r10e 2017-11-23 23:55:33 +08:00
topjohnwu
bc592c1d13 Fix bootloops on some devices 2017-11-23 18:38:12 +08:00
topjohnwu
968bd8be67 Update Magisk Manager 2017-11-23 01:33:33 +08:00
topjohnwu
d8b8adb88c Fix is_mounted function 2017-11-22 22:22:29 +08:00
topjohnwu
f42d820891 Several small tweaks 2017-11-22 16:12:08 +08:00
topjohnwu
bc21a1fb71 Update internal paths 2017-11-22 16:04:24 +08:00
topjohnwu
3bc31374ac Fix issue of touch command on Android 5.0 2017-11-20 04:13:51 +08:00
topjohnwu
858e7bae2b More precise size for mmap 2017-11-20 03:40:37 +08:00
Patryk Szalanski
8c02d120a2 Fix missing includes for ndk r16 2017-11-17 02:54:16 +08:00
topjohnwu
07e353f4ff Fix typo when handling MTK 2017-11-16 22:26:28 +08:00
topjohnwu
bb33d9e600 Use real tmp files 2017-11-15 05:48:31 +08:00
topjohnwu
68eb0bdec9 Allow specific signal to specific threads 2017-11-15 05:48:31 +08:00
topjohnwu
32ee8e462c Properly and fully support dtbo patching 2017-11-15 05:48:31 +08:00
topjohnwu
e79aa54b70 Proper Windows building support 2017-11-12 04:17:56 +08:00
topjohnwu
9a95652034 Simplify compress code 2017-11-12 04:08:52 +08:00
topjohnwu
912c188b53 Add dtbo.img patch support 2017-11-11 01:33:50 +08:00
topjohnwu
e9d0f615ba Add dtb test command 2017-11-11 01:30:33 +08:00
topjohnwu
9136573596 Add stdin/stdout support 2017-11-10 20:25:41 +08:00
topjohnwu
2487ec94e6 Add support to remove avb verity 2017-11-10 03:45:06 +08:00
topjohnwu
811489f157 Small reorganization 2017-11-10 01:51:41 +08:00
topjohnwu
b438cc9335 Remove unnecessary monogisk 2017-11-10 00:54:54 +08:00
topjohnwu
1d3d30fa45 Get potential slot info 2017-11-09 03:05:25 +08:00
topjohnwu
72b5985398 Prevent race condition on /magisk symlink 2017-11-09 03:05:01 +08:00
topjohnwu
2db60e0a6b Add Pixel 2 XL support 2017-11-06 22:33:41 +08:00
topjohnwu
e710848345 Unify Magisk configuration
Introduce monogisk tool
2017-11-06 06:22:45 +08:00
topjohnwu
8d6f3c2450 Fix build error on release builds 2017-11-02 14:48:22 +08:00
topjohnwu
f863d127e7 Fix Xiaomi A1 support 2017-11-01 22:49:06 +08:00
topjohnwu
a831110816 Add boot signing to installation 2017-10-31 17:05:24 +08:00
topjohnwu
e97bdb53f4 Adjust java paths 2017-10-30 03:45:50 +08:00
topjohnwu
fe1439fbac Support changing su requester package name 2017-10-28 16:20:31 +08:00
topjohnwu
2bc30e5c22 Hide /magisk 2017-10-28 16:12:01 +08:00
topjohnwu
7244c02a0d Small adjustments 2017-10-28 16:11:01 +08:00
topjohnwu
6c229ffa68 Update external sources 2017-10-19 00:46:39 +08:00
topjohnwu
cdc5d983f3 Bump MagiskManager version 2017-10-15 03:21:33 +08:00
topjohnwu
96688e4dac Fix proper Lollipop selinux support 2017-10-14 22:37:02 +08:00
topjohnwu
28a945fee9 Fix SEGFAULT in magisk log dumper 2017-10-14 21:10:52 +08:00
topjohnwu
c7e777255a Reduce unnecessary stack memory allocation 2017-10-14 21:10:51 +08:00
topjohnwu
2dd4cf040e Prevent multiple process clashes to start daemon 2017-10-14 21:10:51 +08:00
topjohnwu
d1b9eca5eb Fix bug that cause boot delay 2017-10-14 00:19:13 +08:00
topjohnwu
594a67fe28 Cleanup and add more xwraps 2017-10-14 00:08:12 +08:00
topjohnwu
cddeaffada Remove err_handler 2017-10-13 22:26:42 +08:00
topjohnwu
2a8898e7c3 Fix lz4 legacy on LG zImages 2017-10-13 00:18:40 +08:00
topjohnwu
ce3f3b09b4 Brute force resizeimg for Crapsung device :) 2017-10-12 14:32:40 +08:00
topjohnwu
fe4b3df7e9 Fix selinux context on Magisk files 2017-10-12 14:32:40 +08:00
topjohnwu
25bdbcf526 Add new file operations 2017-10-12 14:32:40 +08:00
topjohnwu
df7eaa5598 Reduce update-binary size 2017-10-11 02:26:43 +08:00
topjohnwu
bb7099376b Improve daemon startup and log management 2017-10-11 02:26:28 +08:00
topjohnwu
0327fd9710 Restart MagiskHide if daemon restarted 2017-10-10 19:49:15 +08:00
topjohnwu
e645c6e465 Refactor resetprop 2017-10-10 02:04:50 +08:00
topjohnwu
78a3d36ccc Allow devices without separate vendor partition 2017-10-09 21:53:50 +08:00
topjohnwu
3942858ccd Introduce a single general purpose logcat monitor 2017-10-09 05:39:40 +08:00
topjohnwu
03c8d716cc Introduce Invincible Mode: Self recover service 2017-10-08 22:00:22 +08:00
topjohnwu
60181c4fcb MagiskManager -> java 2017-10-07 22:48:16 +08:00
topjohnwu
c215447405 Fix Pixel C installation 2017-10-07 22:08:10 +08:00
topjohnwu
89330b89d8 Clone attributes to new tmpfs mountpoint 2017-09-29 01:07:28 +08:00
topjohnwu
a8f3718ed0 Bump MagiskManager version 2017-09-28 05:50:18 +08:00
topjohnwu
a78ba44709 Always create core directories 2017-09-28 05:50:06 +08:00
topjohnwu
ff110e3513 Update docs 2017-09-28 05:39:10 +08:00
topjohnwu
cfae6c63b5 Remove sepolicy-inject alias 2017-09-28 01:01:46 +08:00
topjohnwu
dbfe49c56f Unlock blocks before flashing boot image 2017-09-28 00:54:01 +08:00
topjohnwu
98e21f9f5b Update uninstaller 2017-09-27 15:26:21 +08:00
topjohnwu
83af0497e4 Don't use dummy directory; directly use tmpfs 2017-09-27 04:36:01 +08:00
topjohnwu
6ce37b44db Improve logging 2017-09-27 04:36:01 +08:00
topjohnwu
9cb1cf756f Update scripts 2017-09-26 20:21:43 +08:00
topjohnwu
ffa005e4ab Don't patch FBE flags 2017-09-26 02:04:07 +08:00
topjohnwu
af102e47f1 Fix a small bug in deleteprop 2017-09-25 17:47:30 +08:00
topjohnwu
73064a816d Fix ramdisk patches 2017-09-25 13:44:00 +08:00
topjohnwu
9b4ae8fcc5 Adjust scripts 2017-09-16 03:48:58 +08:00
topjohnwu
a1a2c52409 Patch sepolicy at boot time 2017-09-16 01:32:09 +08:00
topjohnwu
9a0b26e0b0 Proper FBE support 2017-09-15 18:05:42 +08:00
topjohnwu
b805b96e16 Read SHA1 from overlay ramdisk 2017-09-15 18:03:56 +08:00
topjohnwu
590e7f7724 Proper support for Magisk Manager installation 2017-09-15 18:02:25 +08:00
Shaka Huang
4d61e5e319 Fix error generating binary for x86
fix typo

Signed-off-by: Shaka Huang <shakalaca@gmail.com>
2017-09-14 14:33:37 -05:00
topjohnwu
8c8a63ebfb Use external functions in init
Don't reinvent the wheel
2017-09-15 03:22:23 +08:00
topjohnwu
e5e34797a8 Patch fstab from dtb 2017-09-15 02:52:53 +08:00
topjohnwu
8516ebe6f5 Add libfdt 2017-09-15 02:52:53 +08:00
topjohnwu
9f6205f47f Refactor ramdisk pattern patches 2017-09-15 02:52:53 +08:00
topjohnwu
8b2ec23a89 Re-organize MagiskBoot 2017-09-15 02:52:53 +08:00
topjohnwu
1816ca6b02 Seperate logging to another header 2017-09-15 02:52:53 +08:00
topjohnwu
7394ff9346 Rename cpio_file -> cpio_entry 2017-09-15 02:52:53 +08:00
topjohnwu
bb5a6a1c28 Re-organize folders 2017-09-14 01:57:53 +08:00
topjohnwu
b614b06736 Search for dtb only if not existed 2017-09-14 01:57:44 +08:00
topjohnwu
7a376c9efc Adjust the daemon for Pixel support 2017-09-14 01:57:44 +08:00
topjohnwu
518f3d229f Slight adjustments to scripts 2017-09-13 15:51:45 +08:00
topjohnwu
46c91f923d Update scripts to support Pixel (XL) 2017-09-13 04:08:50 +08:00
topjohnwu
3a2262dfb3 Use backup init if booted as recovery 2017-09-13 04:08:50 +08:00
topjohnwu
ff7c38f8e9 Add runtime ramdisk patch support 2017-09-13 04:08:50 +08:00
topjohnwu
4229ba364f Update lz4 to v1.8.0 2017-09-13 04:08:50 +08:00
topjohnwu
ba8e7a211a Use default preferences from lz4 cli 2017-09-13 04:08:50 +08:00
topjohnwu
6b41653a32 Add kernel decompression and appended dtb support 2017-09-13 04:08:50 +08:00
topjohnwu
59c1125e72 Refactor MagiskBoot compression methods 2017-09-13 04:08:50 +08:00
topjohnwu
b536046720 MagiskBoot refactor 2017-09-13 04:08:50 +08:00
topjohnwu
619b805894 Add secilc 2017-09-13 04:08:50 +08:00
topjohnwu
8662537883 Add ramdisk overlay to magiskinit 2017-09-13 04:08:50 +08:00
topjohnwu
717890395b Introduce magiskinit for Pixel (XL) 2017-09-13 04:08:50 +08:00
topjohnwu
b7b4164f4f Add cpio-mv action 2017-09-13 04:08:44 +08:00
topjohnwu
7e65296470 Small improvement to cpio 2017-09-13 04:08:44 +08:00
topjohnwu
cd5f5d702f Fix stock image dump not stored correctly 2017-09-13 04:08:44 +08:00
147 changed files with 7303 additions and 6999 deletions

15
.gitignore vendored
View File

@@ -1,7 +1,16 @@
obj/
libs/
out
*.zip
*.jks
*.apk
# Copied binaries
# Built binaries
ziptools/zipadjust
# Android Studio / Gradle
*.iml
.gradle
/local.properties
/.idea
/build
/captures
.externalNativeBuild

25
.gitmodules vendored
View File

@@ -1,18 +1,27 @@
[submodule "jni/selinux"]
path = jni/selinux
path = core/jni/external/selinux
url = https://github.com/topjohnwu/selinux.git
[submodule "jni/su"]
path = jni/su
path = core/jni/su
url = https://github.com/topjohnwu/MagiskSU.git
[submodule "jni/ndk-compression"]
path = jni/ndk-compression
url = https://github.com/topjohnwu/ndk-compression.git
[submodule "jni/magiskpolicy"]
path = jni/magiskpolicy
path = core/jni/magiskpolicy
url = https://github.com/topjohnwu/magiskpolicy.git
[submodule "MagiskManager"]
path = MagiskManager
path = app
url = https://github.com/topjohnwu/MagiskManager.git
[submodule "jni/busybox"]
path = jni/busybox
path = core/jni/external/busybox
url = https://github.com/topjohnwu/ndk-busybox.git
[submodule "jni/external/dtc"]
path = core/jni/external/dtc
url = https://github.com/dgibson/dtc
[submodule "jni/external/lz4"]
path = core/jni/external/lz4
url = https://github.com/lz4/lz4.git
[submodule "jni/external/bzip2"]
path = core/jni/external/bzip2
url = https://github.com/nemequ/bzip2.git
[submodule "jni/external/xz"]
path = core/jni/external/xz
url = https://github.com/xz-mirror/xz.git

Submodule MagiskManager deleted from b362c0ef38

View File

@@ -2,34 +2,30 @@
## How to build Magisk
#### Building has been tested on 3 major platforms:
#### Building has been tested on 3 major platforms: macOS, Ubuntu, Windows 10
**macOS 10.12**
**Ubuntu 17.04 x64**
**Windows 10 x64**
#### Environment Requirements
### Environment Requirements
1. A 64-bit machine: `cmake` for Android is only available in 64-bit
2. Python 3.5+: to run the build script
3. Java Development Kit (JDK) 8: To compile Magisk Manager and sign zips
4. C compiler (Unix only): To build `zipadjust`. Windows users can use the pre-built `zipadjust.exe`
5. Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
6. Android NDK: Install NDK via `sdkmanager`, or via Android SDK Manager in Android Studio
2. Python 3.5+: run `build.py` script
3. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
4. Latest Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
5. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or specify custom path `ANDROID_NDK`
6. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
7. (Unix only) C compiler: Build `zipadjust`. Windows users can use the pre-built `zipadjust.exe`
#### Instructions and Notes
1. The easiest way to setup a working environment is to open Magisk Manager with Android Studio. The IDE will download required components and construct the environment for you. Don't forget to set `ANDROID_HOME` environment variable to the SDK path.
2. Windows users: while installing Python 3 on Windows, allow the installer to add Python to `PATH`, or you'll have to add it manually afterwards. By default, the Python executable is setup as `python`, not `python3` like most Unix environment. If you have both Python 2 and Python 3 installed, you'll have to deal with the executable name and `PATH` yourself. To double check the Python version, call `python --version`.
3. To run the script, on Windows call `python build.py [args...]`; on Unix call `python3 build.py [args...]`, or simply `./build.py [args...]`. To see the built-in help message, call the script with `-h` as an argument. The `-h` option also works for each supported actions to see the help message for the specific action.
4. By default, the script will build binaries and Magisk Manager in debug mode, which will enable verbose debugging messages. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place your Java Keystore file in `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
5. The python build script uses ANSI color codes to change the color of the terminal output. For Windows, this **only** works on Windows 10, as previous Windows console do not support them. If you use an older Windows version, a quick Google search should provide many workarounds.
### 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 this 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.
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).
## License
```
Magisk, including all subprojects (git submodule) is free software:
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
@@ -45,51 +41,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
## Credits
**MagiskManager** (`MagiskManager`)
**MagiskManager** (`app`)
* Copyright 2016-2017, John Wu (@topjohnwu)
* All contributors and translators
* Copyright 2016-2018, John Wu (@topjohnwu)
* All contributors and translators on Github
**MagiskSU** (`jni/su`)
**MagiskSU** (`core/jni/su`)
* Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2016-2018, John Wu (@topjohnwu)
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
* Copyright 2013, Koushik Dutta (@koush)
* Copyright 2010, Adam Shanks (@ChainsDD)
* Copyright 2008, Zinx Verituse (@zinxv)
**MagiskPolicy** (`jni/magiskpolicy`)
**MagiskPolicy** (`core/jni/magiskpolicy`)
* Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2016-2018, John Wu (@topjohnwu)
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
* Copyright 2015, Joshua Brindle (@joshua_brindle)
**MagiskHide** (`jni/magiskhide`)
**MagiskHide** (`core/jni/magiskhide`)
* Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me) (original hidesu)
* Copyright 2016-2018, John Wu (@topjohnwu)
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me)
**resetprop** (`jni/resetprop`)
**resetprop** (`core/jni/resetprop`)
* Copyright 2016-2017 John Wu (@topjohnwu)
* Copyright 2016-2018 John Wu (@topjohnwu)
* Copyright 2016 nkk71 (nkk71x@gmail.com)
**SELinux** (`jni/selinux`)
**External Dependencies** (`core/jni/external`)
* Makefile for NDK: Copyright 2016-2017, John Wu (@topjohnwu)
* Maintained by many developers in SELinux project
**ndk-compression** (`jni/ndk-compression`)
* Makefile for NDK: Copyright 2017, John Wu (@topjohnwu)
* Each library has its own copyright message in corresponding directories
**ndk-busybox** (`jni/busybox`)
* Makefile for NDK, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen): Copyright 2017, John Wu (@topjohnwu)
* Patches for NDK: Many contributors along the way, all placed in [osm0sis/android-busybox-ndk](https://github.com/osm0sis/android-busybox-ndk)
* The copyright message for busybox should be included in its own directory
* Makefile for busybox, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen)
* Each dependencies has its own license/copyright information in each subdirectory.
All of them are either GPL or GPL compatible.
**Others Not Mentioned**
* Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2016-2018, John Wu (@topjohnwu)

1
app Submodule

Submodule app added at 2e02af994e

27
build.gradle Normal file
View File

@@ -0,0 +1,27 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

312
build.py
View File

@@ -3,6 +3,10 @@ import sys
import os
import subprocess
if os.name == 'nt':
from colorama import init
init()
def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1)
@@ -37,14 +41,37 @@ import errno
import shutil
import lzma
import base64
import tempfile
def silentremove(file):
if 'ANDROID_NDK' in os.environ:
ndk_build = os.path.join(os.environ['ANDROID_NDK'], 'ndk-build')
else:
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
def mv(source, target):
print('mv: {} -> {}'.format(source, target))
shutil.move(source, target)
def cp(source, target):
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
def rm(file):
try:
os.remove(file)
os.remove(file)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def mkdir(path, mode=0o777):
try:
os.mkdir(path, mode)
except:
pass
def mkdir_p(path, mode=0o777):
os.makedirs(path, mode, exist_ok=True)
def zip_with_msg(zipfile, source, target):
if not os.path.exists(source):
error('{} does not exist! Try build \'binary\' and \'apk\' before zipping!'.format(source))
@@ -56,53 +83,65 @@ def build_all(args):
build_apk(args)
zip_main(args)
zip_uninstaller(args)
build_snet(args)
def build_binary(args):
header('* Building Magisk binaries')
# Force update Android.mk timestamp to trigger recompilation
os.utime(os.path.join('jni', 'Android.mk'))
# Force update logging.h timestamp to trigger recompilation
os.utime(os.path.join('core', 'jni', 'include', 'logging.h'))
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
proc = subprocess.run('{} APP_CFLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\" -j{}'.format(
ndk_build, args.versionString, args.versionCode, debug_flag, multiprocessing.cpu_count()), shell=True)
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
# Prebuild
proc = subprocess.run('{} -C core PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
if proc.returncode != 0:
error('Build Magisk binary 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('core', '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')
print('')
proc = subprocess.run('{} -C core {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), 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('core', 'libs', arch, binary), os.path.join('out', arch, binary))
except:
pass
def build_apk(args):
header('* Building Magisk Manager')
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
source = os.path.join('ziptools', key)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
target = os.path.join('app', 'src', 'main', 'assets', key)
cp(source, target)
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
source = os.path.join('scripts', script)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', script)
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
os.chdir('MagiskManager')
# Build unhide app and place in assets
proc = subprocess.run('{} unhide::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
source = os.path.join('unhide', 'build', 'outputs', 'apk', 'release', 'unhide-release-unsigned.apk')
target = os.path.join('app', 'src', 'main', 'assets', 'unhide.apk')
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
print('')
target = os.path.join('app', 'src', 'main', 'assets', script)
cp(source, target)
if args.release:
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
if not os.path.exists('release_signature.jks'):
error('Please generate a java keystore and place it in \'release_signature.jks\'')
proc = subprocess.run('{} app::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
@@ -113,8 +152,8 @@ def build_apk(args):
# Find the latest build tools
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
silentremove(aligned)
silentremove(release)
rm(aligned)
rm(release)
proc = subprocess.run([
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'),
@@ -132,87 +171,58 @@ def build_apk(args):
error('Cannot find apksigner.jar in Android SDK build tools')
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
apksigner,
os.path.join('..', 'release_signature.jks'),
release, aligned), shell=True)
apksigner, 'release_signature.jks', release, aligned), shell=True)
if proc.returncode != 0:
error('Release sign Magisk Manager failed!')
silentremove(unsigned)
silentremove(aligned)
rm(unsigned)
rm(aligned)
mkdir('out')
target = os.path.join('out', 'app-release.apk')
print('')
mv(release, target)
else:
proc = subprocess.run('{} app::assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
# Return to upper directory
os.chdir('..')
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
mkdir('out')
target = os.path.join('out', 'app-debug.apk')
print('')
mv(source, target)
def sign_adjust_zip(unsigned, output):
zipsigner = os.path.join('ziptools', 'zipsigner', 'build', 'libs', 'zipsigner.jar')
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
header('* Building zipadjust')
# Compile zipadjust
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
if proc.returncode != 0:
error('Build zipadjust failed!')
if not os.path.exists(zipsigner):
header('* Building zipsigner.jar')
os.chdir(os.path.join('ziptools', 'zipsigner'))
proc = subprocess.run('{} shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build zipsigner.jar failed!')
os.chdir(os.path.join('..', '..'))
header('* Signing / Adjusting Zip')
publicKey = os.path.join('ziptools', 'public.certificate.x509.pem')
privateKey = os.path.join('ziptools', 'private.key.pk8')
# Unsigned->signed
proc = subprocess.run(['java', '-jar', zipsigner,
publicKey, privateKey, unsigned, 'tmp_signed.zip'])
def build_snet(args):
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('First sign flashable zip failed!')
# Adjust zip
proc = subprocess.run([os.path.join('ziptools', 'zipadjust'), 'tmp_signed.zip', 'tmp_adjusted.zip'])
if proc.returncode != 0:
error('Adjust flashable zip failed!')
# Adjusted -> output
proc = subprocess.run(['java', '-jar', zipsigner,
"-m", publicKey, privateKey, 'tmp_adjusted.zip', output])
if proc.returncode != 0:
error('Second sign flashable zip failed!')
# Cleanup
silentremove(unsigned)
silentremove('tmp_signed.zip')
silentremove('tmp_adjusted.zip')
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)
def gen_update_binary():
update_bin = []
binary = os.path.join('libs', 'armeabi-v7a', 'b64xz')
binary = os.path.join('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('libs', 'x86', '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')
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('libs', 'armeabi-v7a', 'busybox')
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')
with open(binary, 'rb') as busybox:
update_bin.append('\nBB_ARM=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii'))
binary = os.path.join('libs', 'x86', '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')
with open(binary, 'rb') as busybox:
update_bin.append('\nBB_X86=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii'))
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
update_bin.append('\n')
with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script:
update_bin.append(script.read())
@@ -221,7 +231,9 @@ def gen_update_binary():
def zip_main(args):
header('* Packing Flashable Zip')
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -234,14 +246,13 @@ def zip_main(args):
# Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
for binary in ['magisk', 'magiskboot']:
source = os.path.join('libs', lib_dir, binary)
for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('out', lib_dir, binary)
target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target)
# APK
source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk',
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
source = os.path.join('out', 'app-release.apk' if args.release else 'app-debug.apk')
target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target)
@@ -255,7 +266,7 @@ def zip_main(args):
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))
'#MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode))
target = os.path.join('common', 'util_functions.sh')
print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func)
@@ -263,10 +274,6 @@ def zip_main(args):
source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('addon.d', '99-magisk.sh')
zip_with_msg(zipf, source, target)
# init.magisk.rc
source = os.path.join('scripts', 'init.magisk.rc')
target = os.path.join('common', 'init.magisk.rc')
zip_with_msg(zipf, source, target)
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
@@ -275,13 +282,15 @@ def zip_main(args):
# End of zipping
output = 'Magisk-v{}.zip'.format(args.versionString)
sign_adjust_zip('tmp_unsigned.zip', output)
output = os.path.join('out', 'Magisk-v{}.zip'.format(args.versionString))
sign_adjust_zip(unsigned, output)
def zip_uninstaller(args):
header('* Packing Uninstaller Zip')
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -294,7 +303,7 @@ def zip_uninstaller(args):
# Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
source = os.path.join('libs', lib_dir, 'magiskboot')
source = os.path.join('out', lib_dir, 'magiskboot')
target = os.path.join(zip_dir, 'magiskboot')
zip_with_msg(zipf, source, target)
@@ -307,11 +316,9 @@ def zip_uninstaller(args):
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
# Remove the stub
util_func = script.read().replace(
'MAGISK_VERSION_STUB', '')
target = os.path.join('util_functions.sh')
print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func)
zipf.writestr(target, script.read())
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
@@ -320,29 +327,79 @@ def zip_uninstaller(args):
# End of zipping
output = 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))
sign_adjust_zip('tmp_unsigned.zip', output)
output = os.path.join('out', 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')))
sign_adjust_zip(unsigned, output)
def sign_adjust_zip(unsigned, output):
signer_name = 'zipsigner-1.1.jar'
jarsigner = os.path.join('crypto', 'build', 'libs', signer_name)
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
header('* Building zipadjust')
# Compile zipadjust
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
if proc.returncode != 0:
error('Build zipadjust failed!')
if not os.path.exists(jarsigner):
header('* Building ' + signer_name)
proc = subprocess.run('{} crypto:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build {} failed!'.format(signer_name))
header('* Signing / Adjusting Zip')
publicKey = os.path.join('ziptools', 'public.certificate.x509.pem')
privateKey = os.path.join('ziptools', 'private.key.pk8')
signed = tempfile.mkstemp()[1]
# Unsigned->signed
proc = subprocess.run(['java', '-jar', jarsigner,
publicKey, privateKey, unsigned, signed])
if proc.returncode != 0:
error('First sign flashable zip failed!')
adjusted = tempfile.mkstemp()[1]
# Adjust zip
proc = subprocess.run([os.path.join('ziptools', 'zipadjust'), signed, adjusted])
if proc.returncode != 0:
error('Adjust flashable zip failed!')
# Adjusted -> output
proc = subprocess.run(['java', '-jar', jarsigner,
"-m", publicKey, privateKey, adjusted, output])
if proc.returncode != 0:
error('Second sign flashable zip failed!')
# Cleanup
rm(unsigned)
rm(signed)
rm(adjusted)
def cleanup(args):
if len(args.target) == 0:
args.target = ['binary', 'apk', 'zip']
args.target = ['binary', 'java', 'zip']
if 'binary' in args.target:
header('* Cleaning Magisk binaries')
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
header('* Cleaning binaries')
subprocess.run(ndk_build + ' -C core PRECOMPILE=true clean', shell=True)
subprocess.run(ndk_build + ' -C core clean', shell=True)
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
if 'apk' in args.target:
header('* Cleaning Magisk Manager')
os.chdir('MagiskManager')
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
os.chdir('..')
if 'java' in args.target:
header('* Cleaning java')
subprocess.run('{} app:clean snet:clean crypto: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 created zip files')
for f in os.listdir('.'):
header('* Cleaning zip files')
for f in os.listdir('out'):
if '.zip' in f:
print('rm {}'.format(f))
silentremove(f)
rm(os.path.join('out', f))
parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('--release', action='store_true', help='compile Magisk for release')
@@ -361,6 +418,9 @@ binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
apk_parser.set_defaults(func=build_apk)
snet_parser = subparsers.add_parser('snet', help='build snet extention for Magisk Manager')
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)
@@ -369,13 +429,13 @@ 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 apk zip')
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip')
clean_parser.add_argument('target', nargs='*')
clean_parser.set_defaults(func=cleanup)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
parser.print_help()
sys.exit(1)
args = parser.parse_args()

3
core/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/build
obj
libs

20
core/build.gradle Normal file
View File

@@ -0,0 +1,20 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
externalNativeBuild {
ndkBuild {
path 'jni/Android.mk'
}
}
defaultConfig {
externalNativeBuild {
ndkBuild {
// Passes an optional argument to ndk-build.
arguments "GRADLE=true"
}
}
}
}

138
core/jni/Android.mk Normal file
View File

@@ -0,0 +1,138 @@
LOCAL_PATH := $(call my-dir)
# Some handy paths
EXT_PATH := jni/external
SE_PATH := $(EXT_PATH)/selinux
LIBSELINUX := $(SE_PATH)/libselinux/include
LIBSEPOL := $(SE_PATH)/libsepol/include $(SE_PATH)/libsepol/cil/include
LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
LIBLZ4 := $(EXT_PATH)/lz4/lib
LIBBZ2 := $(EXT_PATH)/bzip2
LIBFDT := $(EXT_PATH)/dtc/libfdt
UTIL_SRC := utils/cpio.c \
utils/file.c \
utils/img.c \
utils/list.c \
utils/misc.c \
utils/pattern.c \
utils/vector.c \
utils/xwrap.c
########################
# Binaries
########################
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
# magisk main binary
include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_C_INCLUDES := \
jni/include \
jni/external/include \
$(LIBSELINUX)
LOCAL_SRC_FILES := \
core/magisk.c \
core/daemon.c \
core/log_monitor.c \
core/bootstages.c \
core/socket.c \
magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \
resetprop/resetprop.cpp \
resetprop/system_properties.cpp \
su/su.c \
su/activity.c \
su/db.c \
su/pts.c \
su/su_daemon.c \
su/su_socket.c \
$(UTIL_SRC)
LOCAL_CFLAGS := -DIS_DAEMON -DSELINUX
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
endif
ifndef PRECOMPILE
# magiskinit
include $(CLEAR_VARS)
LOCAL_MODULE := magiskinit
LOCAL_STATIC_LIBRARIES := libsepol liblzma
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
../out/$(TARGET_ARCH_ABI) \
$(LIBSEPOL) \
$(LIBLZMA)
LOCAL_SRC_FILES := \
core/magiskinit.c \
core/socket.c \
magiskpolicy/api.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
$(UTIL_SRC)
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
# magiskboot
include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := liblzma liblz4 libbz2 libfdt
LOCAL_C_INCLUDES := \
jni/include \
jni/external/include \
$(LIBLZMA) \
$(LIBLZ4) \
$(LIBBZ2) \
$(LIBFDT)
LOCAL_SRC_FILES := \
external/sha1/sha1.c \
magiskboot/main.c \
magiskboot/bootimg.c \
magiskboot/hexpatch.c \
magiskboot/compress.c \
magiskboot/types.c \
magiskboot/dtb.c \
magiskboot/ramdisk.c \
$(UTIL_SRC)
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)
# b64xz
include $(CLEAR_VARS)
LOCAL_MODULE := b64xz
LOCAL_STATIC_LIBRARIES := liblzma
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
endif
########################
# Externals
########################
include jni/external/Android.mk

View File

@@ -1,2 +1,4 @@
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
APP_PLATFORM := android-21
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99
APP_CPPFLAGS := -std=c++11

View File

@@ -11,8 +11,6 @@
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <selinux/selinux.h>
@@ -24,14 +22,9 @@
static char *buf, *buf2;
static struct vector module_list;
static int seperate_vendor = 0;
extern char **environ;
#ifdef MAGISK_DEBUG
static int debug_log_pid, debug_log_fd;
#endif
/******************
* Node structure *
******************/
@@ -101,7 +94,7 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
if (c->status > e->status) {
// Precedence is higher, replace with new node
destroy_subtree(e);
vec_entry(p->children)[_] = c;
vec_cur(p->children) = c;
return c;
} else {
// Free the new entry, return old
@@ -149,7 +142,7 @@ static void exec_common_script(const char* stage) {
struct dirent *entry;
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
if (!(dir = opendir(buf)))
if (!(dir = xopendir(buf)))
return;
while ((entry = xreaddir(dir))) {
@@ -194,7 +187,7 @@ static void construct_tree(const char *module, struct node_entry *parent) {
char *parent_path = get_full_path(parent);
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
if (!(dir = opendir(buf)))
if (!(dir = xopendir(buf)))
goto cleanup;
while ((entry = xreaddir(dir))) {
@@ -262,7 +255,7 @@ static void clone_skeleton(struct node_entry *node) {
// Clone the structure
char *full_path = get_full_path(node);
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
if (!(dir = opendir(buf)))
if (!(dir = xopendir(buf)))
goto cleanup;
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
@@ -276,26 +269,33 @@ static void clone_skeleton(struct node_entry *node) {
}
closedir(dir);
snprintf(buf, PATH_MAX, "%s%s", DUMMDIR, full_path);
mkdir_p(buf, 0755);
clone_attr(full_path, buf);
if (node->status & IS_SKEL)
bind_mount(buf, full_path);
if (node->status & IS_SKEL) {
struct stat s;
char *con;
xstat(full_path, &s);
getfilecon(full_path, &con);
LOGI("mnt_tmpfs : %s\n", full_path);
xmount("tmpfs", full_path, "tmpfs", 0, NULL);
chmod(full_path, s.st_mode & 0777);
chown(full_path, s.st_uid, s.st_gid);
setfilecon(full_path, con);
free(con);
}
vec_for_each(node->children, child) {
snprintf(buf, PATH_MAX, "%s%s/%s", DUMMDIR, full_path, child->name);
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
// Create the dummy file/directory
if (IS_DIR(child))
xmkdir(buf, 0755);
else if (IS_REG(child))
close(open_new(buf));
close(creat(buf, 0644));
// Links will be handled later
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
if (IS_LNK(child)) {
cp_afc(MIRRDIR "/system/vendor", "/system/vendor");
LOGI("cplink: %s -> %s\n", MIRRDIR "/system/vendor", "/system/vendor");
LOGI("creat_link: %s <- %s\n", "/system/vendor", MIRRDIR "/system/vendor");
}
// Skip
continue;
@@ -303,7 +303,7 @@ static void clone_skeleton(struct node_entry *node) {
// Mount from module file to dummy file
snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, full_path, child->name);
} else if (child->status & (IS_SKEL | IS_INTER)) {
// It's a intermediate folder, recursive clone
// It's an intermediate folder, recursive clone
clone_skeleton(child);
continue;
} else if (child->status & IS_DUMMY) {
@@ -314,7 +314,11 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_LNK(child)) {
// Copy symlinks directly
cp_afc(buf2, buf);
LOGI("cplink: %s -> %s\n", buf2, buf);
#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);
@@ -388,51 +392,21 @@ static void simple_mount(const char *path) {
* Miscellaneous *
*****************/
static void mount_mirrors() {
LOGI("* Mounting mirrors");
struct vector mounts;
vec_init(&mounts);
file_to_vector("/proc/mounts", &mounts);
char *line;
vec_for_each(&mounts, line) {
if (strstr(line, " /system ")) {
sscanf(line, "%s", buf);
xmkdir_p(MIRRDIR "/system", 0755);
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/system");
continue;
}
if (strstr(line, " /vendor ")) {
seperate_vendor = 1;
sscanf(line, "%s", buf);
xmkdir_p(MIRRDIR "/vendor", 0755);
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
continue;
}
}
vec_deep_destroy(&mounts);
if (!seperate_vendor) {
symlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
LOGI("link: %s -> %s\n", MIRRDIR "/system/vendor", MIRRDIR "/vendor");
}
mkdir_p(MIRRDIR "/bin", 0755);
bind_mount(DATABIN, MIRRDIR "/bin");
}
static void link_busybox() {
mkdir_p(BBPATH, 0755);
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
symlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
#define alt_img ((char *[]) \
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
static int prepare_img() {
int new_img = 0;
// Merge images
for (int i = 0; alt_img[i]; ++i) {
if (merge_img(alt_img[i], MAINIMG)) {
LOGE("Image merge %s -> " MAINIMG " failed!\n", alt_img[i]);
return 1;
}
}
if (access(MAINIMG, F_OK) == -1) {
if (create_img(MAINIMG, 64))
return 1;
new_img = 1;
}
LOGI("* Mounting " MAINIMG "\n");
@@ -441,71 +415,65 @@ static int prepare_img() {
if (magiskloop == NULL)
return 1;
vec_init(&module_list);
xmkdir(COREDIR, 0755);
xmkdir(COREDIR "/post-fs-data.d", 0755);
xmkdir(COREDIR "/service.d", 0755);
xmkdir(COREDIR "/props", 0755);
if (new_img) {
xmkdir(COREDIR, 0755);
xmkdir(COREDIR "/post-fs-data.d", 0755);
xmkdir(COREDIR "/service.d", 0755);
xmkdir(COREDIR "/props", 0755);
} else {
DIR *dir = xopendir(MOUNTPOINT);
struct dirent *entry;
while ((entry = xreaddir(dir))) {
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)
continue;
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
if (access(buf, F_OK) == 0) {
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
exec_command_sync(BBPATH "/rm", "-rf", buf, NULL);
continue;
}
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));
DIR *dir = xopendir(MOUNTPOINT);
struct dirent *entry;
while ((entry = xreaddir(dir))) {
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)
continue;
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
if (access(buf, F_OK) == 0) {
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
rm_rf(buf);
continue;
}
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);
free(magiskloop);
trim_img(MAINIMG);
// Remount them back :)
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
free(magiskloop);
}
closedir(dir);
// Trim image
umount_image(MOUNTPOINT, magiskloop);
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);
}
/****************
* Entry points *
****************/
static void *start_magisk_hide(void *args) {
launch_magiskhide(-1);
return NULL;
}
static void unblock_boot_process() {
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
pthread_exit(NULL);
}
void post_fs(int client) {
// Error handler
err_handler = unblock_boot_process;
// Start log monitor
monitor_logs();
LOGI("** post-fs mode running\n");
// ack
write_int(client, 0);
@@ -527,56 +495,25 @@ unblock:
}
void post_fs_data(int client) {
// Error handler
err_handler = unblock_boot_process;
// ack
write_int(client, 0);
close(client);
if (!check_data())
if (!is_daemon_init && !check_data())
goto unblock;
#ifdef MAGISK_DEBUG
// Start debug logs in new process
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", "brief", NULL);
close(debug_log_fd);
#endif
// Start the debug log
start_debug_full_log();
LOGI("** post-fs-data mode running\n");
// Allocate buffer
if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
vec_init(&module_list);
// 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";
if (bin_path) {
exec_command_sync("rm", "-rf", DATABIN, NULL);
exec_command_sync("cp", "-r", bin_path, DATABIN, NULL);
exec_command_sync("rm", "-rf", bin_path, NULL);
exec_command_sync("chmod", "-R", "755", bin_path, NULL);
// Lazy.... use shell blob to match files
exec_command_sync("sh", "-c", "mv /data/magisk/stock_boot* /data", NULL);
}
// Link busybox
mount_mirrors();
link_busybox();
// Merge images
if (merge_img("/cache/magisk.img", MAINIMG)) {
LOGE("Image merge %s -> %s failed!\n", "/cache/magisk.img", MAINIMG);
goto unblock;
}
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
goto unblock;
}
// Initialize
if (!is_daemon_init)
daemon_init();
// uninstaller
if (access(UNINSTALLER, F_OK) == 0) {
@@ -586,7 +523,7 @@ void post_fs_data(int client) {
return;
}
// Trim, mount magisk.img, which will also travel through the modules
// Merge, trim, mount magisk.img, which will also travel through the modules
// After this, it will create the module list
if (prepare_img())
goto core_only; // Mounting fails, we can only do core only stuffs
@@ -638,7 +575,7 @@ void post_fs_data(int client) {
if (access(buf, F_OK) == 0) {
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
unlink(buf2);
symlink(buf, buf2);
xsymlink(buf, buf2);
}
construct_tree(module, sys_root);
}
@@ -654,7 +591,7 @@ void post_fs_data(int client) {
child->name = strdup("vendor");
child->status = 0;
// Swap!
vec_entry(sys_root->children)[_] = child;
vec_cur(sys_root->children) = child;
ven_root->parent = NULL;
break;
}
@@ -676,14 +613,7 @@ core_only:
bind_mount(HOSTSFILE, "/system/etc/hosts");
}
// Enable magiskhide by default, only disable when set explicitly
char *hide_prop = getprop(MAGISKHIDE_PROP);
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
pthread_t thread;
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
pthread_detach(thread);
}
free(hide_prop);
auto_start_magiskhide();
unblock:
unblock_boot_process();
@@ -700,7 +630,8 @@ void late_start(int client) {
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done
pthread_join(sepol_patch, NULL);
wait_till_exists(PATCHDONE);
unlink(PATCHDONE);
// Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n");
@@ -741,9 +672,5 @@ core_only:
buf = buf2 = NULL;
vec_deep_destroy(&module_list);
#ifdef MAGISK_DEBUG
// Stop recording the boot logcat after every boot task is done
kill(debug_log_pid, SIGTERM);
waitpid(debug_log_pid, NULL, 0);
#endif
stop_debug_full_log();
}

313
core/jni/core/daemon.c Normal file
View File

@@ -0,0 +1,313 @@
/* daemon.c - Magisk Daemon
*
* Start the daemon and wait for requests
* Connect the daemon and send requests through sockets
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "resetprop.h"
int is_daemon_init = 0, seperate_vendor = 0;
static void *request_handler(void *args) {
int client = *((int *) args);
free(args);
client_request req = read_int(client);
struct ucred credential;
get_client_cred(client, &credential);
switch (req) {
case LAUNCH_MAGISKHIDE:
case STOP_MAGISKHIDE:
case ADD_HIDELIST:
case RM_HIDELIST:
case LS_HIDELIST:
case POST_FS:
case POST_FS_DATA:
case LATE_START:
if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
return NULL;
}
default:
break;
}
switch (req) {
case LAUNCH_MAGISKHIDE:
launch_magiskhide(client);
break;
case STOP_MAGISKHIDE:
stop_magiskhide(client);
break;
case ADD_HIDELIST:
add_hide_list(client);
break;
case RM_HIDELIST:
rm_hide_list(client);
break;
case LS_HIDELIST:
ls_hide_list(client);
break;
case SUPERUSER:
su_daemon_receiver(client, &credential);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VER_STR);
close(client);
break;
case CHECK_VERSION_CODE:
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;
default:
break;
}
return NULL;
}
static void *start_magisk_hide(void *args) {
launch_magiskhide(-1);
return NULL;
}
void auto_start_magiskhide() {
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
pthread_t thread;
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
pthread_detach(thread);
}
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];
// 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);
dir = xfdopendir(sbin);
while((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
linkat(sbin, entry->d_name, root, entry->d_name, 0);
if (strcmp(entry->d_name, "magisk") == 0)
unlinkat(sbin, entry->d_name, 0);
}
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);
}
for (int i = 0; applet[i]; ++i) {
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
xsymlink("/root/magisk", buf2);
}
for (int i = 0; init_applet[i]; ++i) {
snprintf(buf2, PATH_MAX, "/sbin/%s", init_applet[i]);
xsymlink("/root/magiskinit", 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 ")) {
xmkdir_p(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);
xmkdir_p(MIRRDIR "/system", 0755);
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/system");
#else
LOGI("mount: %s\n", MIRRDIR "/system");
#endif
} else if (strstr(line, " /vendor ")) {
seperate_vendor = 1;
sscanf(line, "%s", buf);
xmkdir_p(MIRRDIR "/vendor", 0755);
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
#ifdef MAGISK_DEBUG
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
#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 "/system/vendor", MIRRDIR "/vendor");
#else
LOGI("link: %s\n", MIRRDIR "/vendor");
#endif
}
xmkdir_p(MIRRDIR "/bin", 0755);
bind_mount(DATABIN, MIRRDIR "/bin");
LOGI("* Setting up internal busybox");
xmkdir_p(BBPATH, 0755);
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
void start_daemon() {
setsid();
setcon("u:r:su:s0");
umask(0);
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO);
close(fd);
// Block 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);
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();
}
// Start the log monitor
monitor_logs();
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
// Change process name
strcpy(argv0, "magisk_daemon");
// Unlock all blocks for rw
unlock_blocks();
// Loop forever to listen for requests
while(1) {
int *client = xmalloc(sizeof(int));
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
pthread_t thread;
xpthread_create(&thread, NULL, request_handler, client);
// Detach the thread, we will never join it
pthread_detach(thread);
}
}
/* Connect the daemon, and return a socketfd */
int connect_daemon() {
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
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "No daemon is currently running!\n");
exit(1);
}
if (xfork() == 0) {
LOGD("client: connect fail, try launching new daemon process\n");
close(fd);
start_daemon();
}
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
usleep(10000);
}
return fd;
}

178
core/jni/core/log_monitor.c Normal file
View File

@@ -0,0 +1,178 @@
/* 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"
extern int is_daemon_init;
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, debug_log_fd;
#endif
static void *logger_thread(void *args) {
int log_fd = -1, log_pid;
char line[4096];
LOGD("log_monitor: logger start");
while (1) {
// 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);
}
// 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;
// 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
// 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
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
pthread_t thread;
// Start debug thread
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
pthread_detach(thread);
#endif
}

View File

@@ -3,6 +3,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include "utils.h"
#include "magisk.h"
@@ -10,66 +12,64 @@
char *argv0;
char *applet[] =
{ "su", "resetprop", "magiskpolicy", "supolicy", "sepolicy-inject", "magiskhide", NULL };
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
// Global error hander function
// Should be changed each thread/process
__thread void (*err_handler)(void);
int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX];
if (bin == NULL) {
xreadlink("/proc/self/exe", self, sizeof(self));
bin = self;
}
int ret = 0;
for (int i = 0; applet[i]; ++i) {
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
unlink(linkpath);
ret |= symlink(bin, linkpath);
}
return ret;
}
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 --install [SOURCE] DIR\n"
" if SOURCE not provided, will link itself\n"
" or: %s --list\n"
" or: %s --createimg IMG SIZE\n"
" create ext4 image, SIZE is interpreted in MB\n"
" or: %s --imgsize IMG\n"
" or: %s --resizeimg IMG SIZE\n"
" SIZE is interpreted in MB\n"
" or: %s --mountimg IMG PATH\n"
" mount IMG to PATH and prints the loop device\n"
" or: %s --umountimg PATH LOOP\n"
" or: %s --[boot stage]\n"
" start boot stage service\n"
" or: %s [options]\n"
" or: applet [arguments]...\n"
"\n"
"Supported boot stages:\n"
" post-fs, post-fs-data, service\n"
" or: %s [options]...\n"
"\n"
"Options:\n"
" -c print client version\n"
" -v print daemon version\n"
" -V print daemon version code\n"
" -c print current binary version\n"
" -v print running daemon version\n"
" -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"
" --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"
"\n"
"Supported applets:\n"
, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
, argv0, argv0);
for (int i = 0; applet[i]; ++i) {
for (int i = 0; applet[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, "\n\n");
exit(1);
}
int main(int argc, char *argv[]) {
argv0 = argv[0];
// Exit the whole app if error occurs by default
err_handler = exit_proc;
char * arg = strrchr(argv[0], '/');
if (arg) ++arg;
else arg = argv[0];
if (strcmp(arg, "magisk") == 0) {
if (strcmp(basename(argv[0]), "magisk") == 0) {
if (argc < 2) usage();
if (strcmp(argv[1], "-c") == 0) {
printf("%s\n", MAGISK_VER_STR);
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon();
@@ -133,6 +133,20 @@ int main(int argc, char *argv[]) {
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);
@@ -149,16 +163,15 @@ int main(int argc, char *argv[]) {
// It's calling applets
--argc;
++argv;
arg = argv[0];
}
}
// Applets
for (int i = 0; applet[i]; ++i) {
if (strcmp(arg, applet[i]) == 0)
if (strcmp(basename(argv[0]), applet[i]) == 0)
return (*applet_main[i])(argc, argv);
}
fprintf(stderr, "%s: applet not found\n", arg);
fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
return 1;
}

547
core/jni/core/magiskinit.c Normal file
View File

@@ -0,0 +1,547 @@
/* magiskinit.c - Pre-init Magisk support
*
* This code has to be compiled statically to work properly.
*
* To unify Magisk support for both legacy "normal" devices and new skip_initramfs devices,
* magisk binary compilation is split into two parts - first part only compiles "magisk".
* The python build script will load the magisk main binary and compress with lzma2, dumping
* the results into "dump.h". The "magisk" binary is embedded into this binary, and will
* get extracted to the overlay folder along with init.magisk.rc.
*
* This tool does all pre-init operations to setup a Magisk environment, which pathces rootfs
* on the fly, providing fundamental support such as init, init.rc, and sepolicy patching.
*
* Magiskinit is also responsible for constructing a proper rootfs on skip_initramfs devices.
* On skip_initramfs devices, it will parse kernel cmdline, mount sysfs, parse through
* uevent files to make the system (or vendor if available) block device node, then copy
* rootfs files from system.
*
* This tool will be replaced with the real init to continue the boot process, but hardlinks are
* preserved as it also provides CLI for sepolicy patching (magiskpolicy)
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <sys/sysmacros.h>
#include <lzma.h>
#include <cil/cil.h>
#include "dump.h"
#include "magiskrc.h"
#include "utils.h"
#include "magiskpolicy.h"
#include "daemon.h"
#include "cpio.h"
#include "magisk.h"
#ifdef MAGISK_DEBUG
#define VLOG(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define VLOG(fmt, ...)
#endif
extern policydb_t *policydb;
int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL };
static int keepverity = 0, keepencrypt = 0;
struct cmdline {
int skip_initramfs;
char slot[3];
};
struct device {
dev_t major;
dev_t minor;
char devname[32];
char partname[32];
char path[64];
};
static void parse_cmdline(struct cmdline *cmd) {
// cleanup
cmd->skip_initramfs = 0;
cmd->slot[0] = '\0';
char cmdline[4096];
mkdir("/proc", 0555);
mount("proc", "/proc", "proc", 0, NULL);
int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
close(fd);
umount("/proc");
for (char *tok = strtok(cmdline, " "); tok; tok = strtok(NULL, " ")) {
if (strncmp(tok, "androidboot.slot_suffix", 23) == 0) {
sscanf(tok, "androidboot.slot_suffix=%s", cmd->slot);
} else if (strncmp(tok, "androidboot.slot", 16) == 0) {
cmd->slot[0] = '_';
sscanf(tok, "androidboot.slot=%s", cmd->slot + 1);
} else if (strcmp(tok, "skip_initramfs") == 0) {
cmd->skip_initramfs = 1;
}
}
}
static void parse_device(struct device *dev, char *uevent) {
dev->partname[0] = '\0';
char *tok;
tok = strtok(uevent, "\n");
while (tok != NULL) {
if (strncmp(tok, "MAJOR", 5) == 0) {
sscanf(tok, "MAJOR=%ld", (long*) &dev->major);
} else if (strncmp(tok, "MINOR", 5) == 0) {
sscanf(tok, "MINOR=%ld", (long*) &dev->minor);
} else if (strncmp(tok, "DEVNAME", 7) == 0) {
sscanf(tok, "DEVNAME=%s", dev->devname);
} else if (strncmp(tok, "PARTNAME", 8) == 0) {
sscanf(tok, "PARTNAME=%s", dev->partname);
}
tok = strtok(NULL, "\n");
}
VLOG("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor);
}
static int setup_block(struct device *dev, const char *partname) {
char buffer[1024], path[128];
struct dirent *entry;
DIR *dir = opendir("/sys/dev/block");
if (dir == NULL)
return 1;
int found = 0;
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(path, sizeof(path), "/sys/dev/block/%s/uevent", entry->d_name);
int fd = open(path, O_RDONLY | O_CLOEXEC);
ssize_t size = read(fd, buffer, sizeof(buffer));
buffer[size] = '\0';
close(fd);
parse_device(dev, buffer);
if (strcmp(dev->partname, partname) == 0) {
snprintf(dev->path, sizeof(dev->path), "/dev/block/%s", dev->devname);
found = 1;
break;
}
}
closedir(dir);
if (!found)
return 1;
mkdir("/dev", 0755);
mkdir("/dev/block", 0755);
mknod(dev->path, S_IFBLK | 0600, makedev(dev->major, dev->minor));
return 0;
}
static void fstab_patch_cb(int dirfd, struct dirent *entry) {
if (entry->d_type == DT_REG && strstr(entry->d_name, "fstab")) {
void *buf;
size_t _size;
uint32_t size;
full_read_at(dirfd, entry->d_name, &buf, &_size);
size = _size; /* Type conversion */
if (!keepverity)
patch_verity(&buf, &size, 1);
if (!keepencrypt)
patch_encryption(&buf, &size);
int fstab = xopenat(dirfd, entry->d_name, O_WRONLY | O_CLOEXEC);
write(fstab, buf, size);
close(fstab);
}
}
static void patch_ramdisk(int root) {
void *addr;
size_t size;
mmap_rw("/init", &addr, &size);
for (int i = 0; i < size; ++i) {
if (memcmp(addr + i, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL) - 1) == 0) {
memcpy(addr + i + sizeof(SPLIT_PLAT_CIL) - 4, "xxx", 3);
break;
}
}
munmap(addr, size);
full_read("/init.rc", &addr, &size);
patch_init_rc(&addr, &size);
int fd = creat("/init.rc", 0750);
write(fd, addr, size);
close(fd);
free(addr);
/* Disabled for now */
// char *key, *value;
// full_read("/.backup/.magisk", &addr, &size);
// for (char *tok = strtok(addr, "\n"); tok; tok = strtok(NULL, "\n")) {
// key = tok;
// value = strchr(tok, '=') + 1;
// value[-1] = '\0';
// if (strcmp(key, "KEEPVERITY") == 0)
// keepverity = strcmp(value, "true") == 0;
// else if (strcmp(key, "KEEPFORCEENCRYPT") == 0)
// keepencrypt = strcmp(value, "true") == 0;
// }
// excl_list = (char *[]) { "system_root", "system", "vendor", NULL };
// in_order_walk(root, fstab_patch_cb);
// if (!keepverity)
// unlink("/verity_key");
}
static int strend(const char *s1, const char *s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
return strcmp(s1 + l1 - l2, s2);
}
static int compile_cil() {
DIR *dir;
struct dirent *entry;
char path[128];
struct cil_db *db = NULL;
sepol_policydb_t *pdb = NULL;
void *addr;
size_t size;
cil_db_init(&db);
cil_set_mls(db, 1);
cil_set_multiple_decls(db, 1);
cil_set_disable_neverallow(db, 1);
cil_set_target_platform(db, SEPOL_TARGET_SELINUX);
cil_set_policy_version(db, POLICYDB_VERSION_XPERMS_IOCTL);
cil_set_attrs_expand_generated(db, 0);
// plat
mmap_ro(SPLIT_PLAT_CIL, &addr, &size);
VLOG("cil_add[%s]\n", SPLIT_PLAT_CIL);
cil_add_file(db, SPLIT_PLAT_CIL, addr, size);
munmap(addr, size);
// mapping
char plat[10];
int fd = open(SPLIT_NONPLAT_VER, O_RDONLY | O_CLOEXEC);
plat[read(fd, plat, sizeof(plat)) - 1] = '\0';
snprintf(path, sizeof(path), SPLIT_PLAT_MAPPING, plat);
mmap_ro(path, &addr, &size);
VLOG("cil_add[%s]\n", path);
cil_add_file(db, path, addr, size);
munmap(addr, size);
close(fd);
// nonplat
dir = opendir(NONPLAT_POLICY_DIR);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".cil") == 0) {
snprintf(path, sizeof(path), NONPLAT_POLICY_DIR "%s", entry->d_name);
mmap_ro(path, &addr, &size);
VLOG("cil_add[%s]\n", path);
cil_add_file(db, path, addr, size);
munmap(addr, size);
}
}
closedir(dir);
cil_compile(db);
cil_build_policydb(db, &pdb);
cil_db_destroy(&db);
policydb = &pdb->p;
return 0;
}
static int verify_precompiled() {
DIR *dir;
struct dirent *entry;
int fd;
char sys_sha[70], ven_sha[70];
// init the strings with different value
sys_sha[0] = 0;
ven_sha[0] = 1;
dir = opendir(NONPLAT_POLICY_DIR);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
ven_sha[read(fd, ven_sha, sizeof(ven_sha)) - 1] = '\0';
close(fd);
break;
}
}
closedir(dir);
dir = opendir(PLAT_POLICY_DIR);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
sys_sha[read(fd, sys_sha, sizeof(sys_sha)) - 1] = '\0';
close(fd);
break;
}
}
closedir(dir);
VLOG("sys_sha[%s]\nven_sha[%s]\n", sys_sha, ven_sha);
return strcmp(sys_sha, ven_sha) == 0;
}
static int patch_sepolicy() {
if (access("/sepolicy", R_OK) == 0)
load_policydb("/sepolicy");
else if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled())
load_policydb(SPLIT_PRECOMPILE);
else if (access(SPLIT_PLAT_CIL, R_OK) == 0)
compile_cil();
else
return 1;
sepol_magisk_rules();
dump_policydb("/sepolicy");
return 0;
}
#define BUFSIZE (1 << 20)
static int unxz(const void *buf, size_t size, int fd) {
lzma_stream strm = LZMA_STREAM_INIT;
if (lzma_auto_decoder(&strm, UINT64_MAX, 0) != LZMA_OK)
return 1;
lzma_ret ret = 0;
void *out = malloc(BUFSIZE);
strm.next_in = buf;
strm.avail_in = size;
do {
strm.next_out = out;
strm.avail_out = BUFSIZE;
ret = lzma_code(&strm, LZMA_RUN);
write(fd, out, BUFSIZE - strm.avail_out);
} while (strm.avail_out == 0 && ret == LZMA_OK);
free(out);
lzma_end(&strm);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
return 1;
return 0;
}
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);
close(fd);
return ret;
}
static int dump_magiskrc(const char *path, mode_t mode) {
int fd = creat(path, mode);
write(fd, magiskrc, sizeof(magiskrc));
close(fd);
return 0;
}
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);
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);
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);
for (int i = 0; init_applet[i]; ++i) {
if (strcmp(basename(argv[0]), init_applet[i]) == 0)
return (*init_applet_main[i])(argc, 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], "magiskrc") == 0)
return dump_magiskrc(argv[3], 0755);
}
// Prevent file descriptor confusion
mknod("/null", S_IFCHR | 0666, makedev(1, 3));
int null = open("/null", O_RDWR | O_CLOEXEC);
unlink("/null");
dup3(null, STDIN_FILENO, O_CLOEXEC);
dup3(null, STDOUT_FILENO, O_CLOEXEC);
dup3(null, STDERR_FILENO, O_CLOEXEC);
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);
mkdir("/overlay/root", 0755);
link("/init", "/overlay/root/magiskinit");
struct cmdline cmd;
parse_cmdline(&cmd);
VLOG("cmdline: skip_initramfs=[%d] slot_suffix=[%s]\n", cmd.skip_initramfs, cmd.slot);
int root = open("/", O_RDONLY | O_CLOEXEC);
if (cmd.skip_initramfs) {
// Exclude overlay folder
excl_list = (char *[]) { "overlay", ".backup", NULL };
// Clear rootfs
frm_rf(root);
mkdir("/sys", 0755);
mount("sysfs", "/sys", "sysfs", 0, NULL);
char partname[32];
snprintf(partname, sizeof(partname), "system%s", cmd.slot);
struct device dev;
setup_block(&dev, partname);
mkdir("/system_root", 0755);
mount(dev.path, "/system_root", "ext4", MS_RDONLY, NULL);
int system_root = open("/system_root", O_RDONLY | O_CLOEXEC);
// Exclude system folder
excl_list = (char *[]) { "system", NULL };
clone_dir(system_root, root);
mkdir("/system", 0755);
mount("/system_root/system", "/system", NULL, MS_BIND, NULL);
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot);
// We need to mount independent vendor partition
if (setup_block(&dev, partname) == 0)
mount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
close(system_root);
} else {
if (access("/ramdisk.cpio.xz", R_OK) == 0) {
// High compression mode
void *addr;
size_t size;
mmap_ro("/ramdisk.cpio.xz", &addr, &size);
int fd = creat("/ramdisk.cpio", 0);
unxz(addr, size, fd);
munmap(addr, size);
close(fd);
struct vector v;
vec_init(&v);
parse_cpio(&v, "/ramdisk.cpio");
excl_list = (char *[]) { "overlay", ".backup", NULL };
frm_rf(root);
chdir("/");
cpio_extract_all(&v);
cpio_vec_destroy(&v);
} else {
// Revert original init binary
unlink("/init");
link("/.backup/init", "/init");
}
}
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
// Only patch rootfs if not intended to run in recovery
if (access("/etc/recovery.fstab", F_OK) != 0) {
mv_dir(overlay, root);
patch_ramdisk(root);
if (patch_sepolicy()) {
/* Non skip_initramfs devices using separate sepolicy
* Mount /system and try to load again */
mount("sysfs", "/sys", "sysfs", 0, NULL);
struct device dev;
setup_block(&dev, "system");
mount(dev.path, "/system", "ext4", MS_RDONLY, NULL);
// We need to mount independent vendor partition
if (setup_block(&dev, "vendor") == 0)
mount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
patch_sepolicy();
umount("/system");
}
if (fork_dont_care() == 0) {
strcpy(argv[0], "magiskinit");
close(overlay);
close(root);
magisk_init_daemon();
}
}
// Clean up
close(overlay);
close(root);
umount("/vendor");
rmdir("/overlay");
// Finally, give control back!
execv("/init", argv);
}

View File

@@ -1,18 +1,22 @@
/* socket_trans.c - Functions to transfer data through socket
/* socket.c - All socket related operations
*/
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "logging.h"
#include "utils.h"
#include "magisk.h"
/* Setup the address and return socket fd */
int setup_socket(struct sockaddr_un *sun) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
return fd;
}
/*
* Receive a file descriptor from a Unix socket.

158
core/jni/external/Android.mk vendored Normal file
View File

@@ -0,0 +1,158 @@
LOCAL_PATH:= $(call my-dir)
# libsqlite.so (stub)
include $(CLEAR_VARS)
LOCAL_MODULE:= libsqlite
LOCAL_C_INCLUDES := jni/external/include
LOCAL_SRC_FILES := stubs/sqlite3_stub.c
include $(BUILD_SHARED_LIBRARY)
# libselinux.so (stub)
include $(CLEAR_VARS)
LOCAL_MODULE:= libselinux
LOCAL_C_INCLUDES := $(LIBSELINUX)
LOCAL_SRC_FILES := stubs/selinux_stub.c
include $(BUILD_SHARED_LIBRARY)
# libfdt.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt
LOCAL_C_INCLUDES := $(LIBFDT)
LOCAL_SRC_FILES := \
dtc/libfdt/fdt.c \
dtc/libfdt/fdt_addresses.c \
dtc/libfdt/fdt_empty_tree.c \
dtc/libfdt/fdt_overlay.c \
dtc/libfdt/fdt_ro.c \
dtc/libfdt/fdt_rw.c \
dtc/libfdt/fdt_strerror.c \
dtc/libfdt/fdt_sw.c \
dtc/libfdt/fdt_wip.c
include $(BUILD_STATIC_LIBRARY)
# liblz4.a
include $(CLEAR_VARS)
LOCAL_MODULE := liblz4
LOCAL_C_INCLUDES += $(LIBLZ4)
LOCAL_SRC_FILES := \
lz4/lib/lz4.c \
lz4/lib/lz4frame.c \
lz4/lib/lz4hc.c \
lz4/lib/xxhash.c
include $(BUILD_STATIC_LIBRARY)
# libbz2.a
include $(CLEAR_VARS)
LOCAL_MODULE := libbz2
LOCAL_C_INCLUDES += $(LIBBZ2)
LOCAL_SRC_FILES := \
bzip2/blocksort.c \
bzip2/huffman.c \
bzip2/crctable.c \
bzip2/randtable.c \
bzip2/compress.c \
bzip2/decompress.c \
bzip2/bzlib.c
include $(BUILD_STATIC_LIBRARY)
# liblzma.a
include $(CLEAR_VARS)
LOCAL_MODULE := liblzma
LOCAL_C_INCLUDES += \
$(EXT_PATH)/include/xz_config \
$(EXT_PATH)/xz/src/common \
$(EXT_PATH)/xz/src/liblzma/api \
$(EXT_PATH)/xz/src/liblzma/check \
$(EXT_PATH)/xz/src/liblzma/common \
$(EXT_PATH)/xz/src/liblzma/delta \
$(EXT_PATH)/xz/src/liblzma/lz \
$(EXT_PATH)/xz/src/liblzma/lzma \
$(EXT_PATH)/xz/src/liblzma/rangecoder \
$(EXT_PATH)/xz/src/liblzma/simple \
$(EXT_PATH)/xz/src/liblzma
LOCAL_SRC_FILES := \
xz/src/common/tuklib_cpucores.c \
xz/src/common/tuklib_exit.c \
xz/src/common/tuklib_mbstr_fw.c \
xz/src/common/tuklib_mbstr_width.c \
xz/src/common/tuklib_open_stdxxx.c \
xz/src/common/tuklib_physmem.c \
xz/src/common/tuklib_progname.c \
xz/src/liblzma/check/check.c \
xz/src/liblzma/check/crc32_fast.c \
xz/src/liblzma/check/crc32_table.c \
xz/src/liblzma/check/crc64_fast.c \
xz/src/liblzma/check/crc64_table.c \
xz/src/liblzma/check/sha256.c \
xz/src/liblzma/common/alone_decoder.c \
xz/src/liblzma/common/alone_encoder.c \
xz/src/liblzma/common/auto_decoder.c \
xz/src/liblzma/common/block_buffer_decoder.c \
xz/src/liblzma/common/block_buffer_encoder.c \
xz/src/liblzma/common/block_decoder.c \
xz/src/liblzma/common/block_encoder.c \
xz/src/liblzma/common/block_header_decoder.c \
xz/src/liblzma/common/block_header_encoder.c \
xz/src/liblzma/common/block_util.c \
xz/src/liblzma/common/common.c \
xz/src/liblzma/common/easy_buffer_encoder.c \
xz/src/liblzma/common/easy_decoder_memusage.c \
xz/src/liblzma/common/easy_encoder.c \
xz/src/liblzma/common/easy_encoder_memusage.c \
xz/src/liblzma/common/easy_preset.c \
xz/src/liblzma/common/filter_buffer_decoder.c \
xz/src/liblzma/common/filter_buffer_encoder.c \
xz/src/liblzma/common/filter_common.c \
xz/src/liblzma/common/filter_decoder.c \
xz/src/liblzma/common/filter_encoder.c \
xz/src/liblzma/common/filter_flags_decoder.c \
xz/src/liblzma/common/filter_flags_encoder.c \
xz/src/liblzma/common/hardware_cputhreads.c \
xz/src/liblzma/common/hardware_physmem.c \
xz/src/liblzma/common/index.c \
xz/src/liblzma/common/index_decoder.c \
xz/src/liblzma/common/index_encoder.c \
xz/src/liblzma/common/index_hash.c \
xz/src/liblzma/common/outqueue.c \
xz/src/liblzma/common/stream_buffer_decoder.c \
xz/src/liblzma/common/stream_buffer_encoder.c \
xz/src/liblzma/common/stream_decoder.c \
xz/src/liblzma/common/stream_encoder.c \
xz/src/liblzma/common/stream_encoder_mt.c \
xz/src/liblzma/common/stream_flags_common.c \
xz/src/liblzma/common/stream_flags_decoder.c \
xz/src/liblzma/common/stream_flags_encoder.c \
xz/src/liblzma/common/vli_decoder.c \
xz/src/liblzma/common/vli_encoder.c \
xz/src/liblzma/common/vli_size.c \
xz/src/liblzma/delta/delta_common.c \
xz/src/liblzma/delta/delta_decoder.c \
xz/src/liblzma/delta/delta_encoder.c \
xz/src/liblzma/lz/lz_decoder.c \
xz/src/liblzma/lz/lz_encoder.c \
xz/src/liblzma/lz/lz_encoder_mf.c \
xz/src/liblzma/lzma/fastpos_table.c \
xz/src/liblzma/lzma/fastpos_tablegen.c \
xz/src/liblzma/lzma/lzma2_decoder.c \
xz/src/liblzma/lzma/lzma2_encoder.c \
xz/src/liblzma/lzma/lzma_decoder.c \
xz/src/liblzma/lzma/lzma_encoder.c \
xz/src/liblzma/lzma/lzma_encoder_optimum_fast.c \
xz/src/liblzma/lzma/lzma_encoder_optimum_normal.c \
xz/src/liblzma/lzma/lzma_encoder_presets.c \
xz/src/liblzma/rangecoder/price_table.c \
xz/src/liblzma/rangecoder/price_tablegen.c \
xz/src/liblzma/simple/arm.c \
xz/src/liblzma/simple/armthumb.c \
xz/src/liblzma/simple/ia64.c \
xz/src/liblzma/simple/powerpc.c \
xz/src/liblzma/simple/simple_coder.c \
xz/src/liblzma/simple/simple_decoder.c \
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
include $(BUILD_STATIC_LIBRARY)
# libsepol.a
include $(SE_PATH)/libsepol/Android.mk

1
core/jni/external/busybox vendored Submodule

1
core/jni/external/bzip2 vendored Submodule

Submodule core/jni/external/bzip2 added at 67d818584d

1
core/jni/external/dtc vendored Submodule

Submodule core/jni/external/dtc added at 22a65c5331

View File

@@ -0,0 +1,498 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* How many MiB of RAM to assume if the real amount cannot be determined. */
#define ASSUME_RAM 128
/* Define to 1 if translation of program messages to the user's native
language is requested. */
/* #undef ENABLE_NLS */
/* Define to 1 if bswap_16 is available. */
#define HAVE_BSWAP_16 1
/* Define to 1 if bswap_32 is available. */
#define HAVE_BSWAP_32 1
/* Define to 1 if bswap_64 is available. */
#define HAVE_BSWAP_64 1
/* Define to 1 if you have the <byteswap.h> header file. */
#define HAVE_BYTESWAP_H 1
/* Define to 1 if Capsicum is available. */
/* #undef HAVE_CAPSICUM */
/* Define to 1 if the system has the type `CC_SHA256_CTX'. */
/* #undef HAVE_CC_SHA256_CTX */
/* Define to 1 if you have the `CC_SHA256_Init' function. */
/* #undef HAVE_CC_SHA256_INIT */
/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
CoreFoundation framework. */
/* #undef HAVE_CFLOCALECOPYCURRENT */
/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
the CoreFoundation framework. */
/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
/* Define to 1 if crc32 integrity check is enabled. */
#define HAVE_CHECK_CRC32 1
/* Define to 1 if crc64 integrity check is enabled. */
#define HAVE_CHECK_CRC64 1
/* Define to 1 if sha256 integrity check is enabled. */
#define HAVE_CHECK_SHA256 1
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
/* Define to 1 if you have the <CommonCrypto/CommonDigest.h> header file. */
/* #undef HAVE_COMMONCRYPTO_COMMONDIGEST_H */
/* Define if the GNU dcgettext() function is already present or preinstalled.
*/
/* #undef HAVE_DCGETTEXT */
/* Define to 1 if you have the declaration of `CLOCK_MONOTONIC', and to 0 if
you don't. */
#define HAVE_DECL_CLOCK_MONOTONIC 1
/* Define to 1 if you have the declaration of `program_invocation_name', and
to 0 if you don't. */
#define HAVE_DECL_PROGRAM_INVOCATION_NAME 0
/* Define to 1 if any of HAVE_DECODER_foo have been defined. */
#define HAVE_DECODERS 1
/* Define to 1 if arm decoder is enabled. */
#define HAVE_DECODER_ARM 1
/* Define to 1 if armthumb decoder is enabled. */
#define HAVE_DECODER_ARMTHUMB 1
/* Define to 1 if delta decoder is enabled. */
#define HAVE_DECODER_DELTA 1
/* Define to 1 if ia64 decoder is enabled. */
#define HAVE_DECODER_IA64 1
/* Define to 1 if lzma1 decoder is enabled. */
#define HAVE_DECODER_LZMA1 1
/* Define to 1 if lzma2 decoder is enabled. */
#define HAVE_DECODER_LZMA2 1
/* Define to 1 if powerpc decoder is enabled. */
#define HAVE_DECODER_POWERPC 1
/* Define to 1 if sparc decoder is enabled. */
#define HAVE_DECODER_SPARC 1
/* Define to 1 if x86 decoder is enabled. */
#define HAVE_DECODER_X86 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if any of HAVE_ENCODER_foo have been defined. */
#define HAVE_ENCODERS 1
/* Define to 1 if arm encoder is enabled. */
#define HAVE_ENCODER_ARM 1
/* Define to 1 if armthumb encoder is enabled. */
#define HAVE_ENCODER_ARMTHUMB 1
/* Define to 1 if delta encoder is enabled. */
#define HAVE_ENCODER_DELTA 1
/* Define to 1 if ia64 encoder is enabled. */
#define HAVE_ENCODER_IA64 1
/* Define to 1 if lzma1 encoder is enabled. */
#define HAVE_ENCODER_LZMA1 1
/* Define to 1 if lzma2 encoder is enabled. */
#define HAVE_ENCODER_LZMA2 1
/* Define to 1 if powerpc encoder is enabled. */
#define HAVE_ENCODER_POWERPC 1
/* Define to 1 if sparc encoder is enabled. */
#define HAVE_ENCODER_SPARC 1
/* Define to 1 if x86 encoder is enabled. */
#define HAVE_ENCODER_X86 1
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the `futimens' function. */
#define HAVE_FUTIMENS 1
/* Define to 1 if you have the `futimes' function. */
/* #undef HAVE_FUTIMES */
/* Define to 1 if you have the `futimesat' function. */
/* #undef HAVE_FUTIMESAT */
/* Define to 1 if you have the <getopt.h> header file. */
#define HAVE_GETOPT_H 1
/* Define to 1 if you have the `getopt_long' function. */
#define HAVE_GETOPT_LONG 1
/* Define if the GNU gettext() function is already present or preinstalled. */
/* #undef HAVE_GETTEXT */
/* Define if you have the iconv() function and it works. */
/* #undef HAVE_ICONV */
/* Define to 1 if you have the <immintrin.h> header file. */
/* #undef HAVE_IMMINTRIN_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
#define HAVE_MBRTOWC 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 to enable bt2 match finder. */
#define HAVE_MF_BT2 1
/* Define to 1 to enable bt3 match finder. */
#define HAVE_MF_BT3 1
/* Define to 1 to enable bt4 match finder. */
#define HAVE_MF_BT4 1
/* Define to 1 to enable hc3 match finder. */
#define HAVE_MF_HC3 1
/* Define to 1 to enable hc4 match finder. */
#define HAVE_MF_HC4 1
/* Define to 1 if you have the <minix/sha2.h> header file. */
/* #undef HAVE_MINIX_SHA2_H */
/* Define to 1 if getopt.h declares extern int optreset. */
#define HAVE_OPTRESET 1
/* Define to 1 if you have the `posix_fadvise' function. */
#define HAVE_POSIX_FADVISE 1
/* Define to 1 if you have the `pthread_condattr_setclock' function. */
#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
/* Have PTHREAD_PRIO_INHERIT. */
/* #undef HAVE_PTHREAD_PRIO_INHERIT */
/* Define to 1 if you have the `SHA256Init' function. */
/* #undef HAVE_SHA256INIT */
/* Define to 1 if the system has the type `SHA256_CTX'. */
/* #undef HAVE_SHA256_CTX */
/* Define to 1 if you have the <sha256.h> header file. */
/* #undef HAVE_SHA256_H */
/* Define to 1 if you have the `SHA256_Init' function. */
/* #undef HAVE_SHA256_INIT */
/* Define to 1 if the system has the type `SHA2_CTX'. */
/* #undef HAVE_SHA2_CTX */
/* Define to 1 if you have the <sha2.h> header file. */
/* #undef HAVE_SHA2_H */
/* Define to 1 if optimizing for size. */
/* #undef HAVE_SMALL */
/* Define to 1 if stdbool.h conforms to C99. */
#define HAVE_STDBOOL_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if `st_atimensec' is a member of `struct stat'. */
#define HAVE_STRUCT_STAT_ST_ATIMENSEC 1
/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC */
/* Define to 1 if `st_atim.st__tim.tv_nsec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC */
/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC */
/* Define to 1 if `st_uatime' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_UATIME */
/* Define to 1 if you have the <sys/byteorder.h> header file. */
/* #undef HAVE_SYS_BYTEORDER_H */
/* Define to 1 if you have the <sys/capsicum.h> header file. */
/* #undef HAVE_SYS_CAPSICUM_H */
/* Define to 1 if you have the <sys/endian.h> header file. */
/* #undef HAVE_SYS_ENDIAN_H */
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if the system has the type `uintptr_t'. */
#define HAVE_UINTPTR_T 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the `utime' function. */
/* #undef HAVE_UTIME */
/* Define to 1 if you have the `utimes' function. */
/* #undef HAVE_UTIMES */
/* Define to 1 or 0, depending whether the compiler supports simple visibility
declarations. */
#define HAVE_VISIBILITY 1
/* Define to 1 if you have the `wcwidth' function. */
#define HAVE_WCWIDTH 1
/* Define to 1 if the system has the type `_Bool'. */
#define HAVE__BOOL 1
/* Define to 1 if _mm_movemask_epi8 is available. */
/* #undef HAVE__MM_MOVEMASK_EPI8 */
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Define to 1 when using POSIX threads (pthreads). */
#define MYTHREAD_POSIX 1
/* Define to 1 when using Windows Vista compatible threads. This uses features
that are not available on Windows XP. */
/* #undef MYTHREAD_VISTA */
/* Define to 1 when using Windows 95 (and thus XP) compatible threads. This
avoids use of features that were added in Windows Vista. */
/* #undef MYTHREAD_WIN95 */
/* Define to 1 to disable debugging code. */
#define NDEBUG 1
/* Name of package */
#define PACKAGE "xz"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "lasse.collin@tukaani.org"
/* Define to the full name of this package. */
#define PACKAGE_NAME "XZ Utils"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "XZ Utils 5.3.0alpha"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "xz"
/* Define to the home page for this package. */
#define PACKAGE_URL "http://tukaani.org/xz/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "5.3.0alpha"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T 4
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if the number of available CPU cores can be detected with
cpuset(2). */
/* #undef TUKLIB_CPUCORES_CPUSET */
/* Define to 1 if the number of available CPU cores can be detected with
pstat_getdynamic(). */
/* #undef TUKLIB_CPUCORES_PSTAT_GETDYNAMIC */
/* Define to 1 if the number of available CPU cores can be detected with
sysconf(_SC_NPROCESSORS_ONLN) or sysconf(_SC_NPROC_ONLN). */
#define TUKLIB_CPUCORES_SYSCONF 1
/* Define to 1 if the number of available CPU cores can be detected with
sysctl(). */
/* #undef TUKLIB_CPUCORES_SYSCTL */
/* Define to 1 if the system supports fast unaligned access to 16-bit and
32-bit integers. */
/* #undef TUKLIB_FAST_UNALIGNED_ACCESS */
/* Define to 1 if the amount of physical memory can be detected with
_system_configuration.physmem. */
/* #undef TUKLIB_PHYSMEM_AIX */
/* Define to 1 if the amount of physical memory can be detected with
getinvent_r(). */
/* #undef TUKLIB_PHYSMEM_GETINVENT_R */
/* Define to 1 if the amount of physical memory can be detected with
getsysinfo(). */
/* #undef TUKLIB_PHYSMEM_GETSYSINFO */
/* Define to 1 if the amount of physical memory can be detected with
pstat_getstatic(). */
/* #undef TUKLIB_PHYSMEM_PSTAT_GETSTATIC */
/* Define to 1 if the amount of physical memory can be detected with
sysconf(_SC_PAGESIZE) and sysconf(_SC_PHYS_PAGES). */
#define TUKLIB_PHYSMEM_SYSCONF 1
/* Define to 1 if the amount of physical memory can be detected with sysctl().
*/
/* #undef TUKLIB_PHYSMEM_SYSCTL */
/* Define to 1 if the amount of physical memory can be detected with Linux
sysinfo(). */
/* #undef TUKLIB_PHYSMEM_SYSINFO */
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
/* Version number of package */
#define VERSION "5.3.0alpha"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT32_T */
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT64_T */
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT8_T */
/* Define to rpl_ if the getopt replacement functions and variables should be
used. */
/* #undef __GETOPT_PREFIX */
/* Define to the type of a signed integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
/* #undef int32_t */
/* Define to the type of a signed integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
/* #undef int64_t */
/* Define to the type of an unsigned integer type of width exactly 16 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint16_t */
/* Define to the type of an unsigned integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint32_t */
/* Define to the type of an unsigned integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint64_t */
/* Define to the type of an unsigned integer type of width exactly 8 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint8_t */
/* Define to the type of an unsigned integer type wide enough to hold a
pointer, if such a type exists, and if the system does not define it. */
/* #undef uintptr_t */

1
core/jni/external/lz4 vendored Submodule

Submodule core/jni/external/lz4 added at c10863b98e

1
core/jni/external/selinux vendored Submodule

View File

@@ -1,10 +1,11 @@
#include <stdbool.h>
#include "selinux/avc.h"
#include "selinux/context.h"
#include "selinux/get_context_list.h"
#include "selinux/get_default_type.h"
#include "selinux/restorecon.h"
#include "selinux/selinux.h"
#include <selinux/avc.h>
#include <selinux/context.h>
#include <selinux/get_context_list.h>
#include <selinux/get_default_type.h>
#include <selinux/label.h>
#include <selinux/restorecon.h>
#include <selinux/selinux.h>
int is_selinux_enabled(void) { return 0; }
int is_selinux_mls_enabled(void) { return 0; }
void freecon(char * con) { }
@@ -225,7 +226,7 @@ int selinux_raw_to_trans_context(const char * raw,
int selinux_raw_context_to_color(const char * raw,
char **color_str) { return 0; }
int getseuserbyname(const char *linuxuser, char **seuser, char **level) { return 0; }
int getseuser(const char *username, const char *service,
int getseuser(const char *username, const char *service,
char **r_seuser, char **r_level) { return 0; }
int selinux_file_context_cmp(const char * a,
const char * b) { return 0; }

1
core/jni/external/xz vendored Submodule

Submodule core/jni/external/xz added at 3d566cd519

60
core/jni/include/cpio.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef _CPIO_H_
#define _CPIO_H_
#include <stdint.h>
#include "vector.h"
typedef struct cpio_entry {
// uint32_t ino;
uint32_t mode;
uint32_t uid;
uint32_t gid;
// uint32_t nlink;
// uint32_t mtime;
uint32_t filesize;
// uint32_t devmajor;
// uint32_t devminor;
// uint32_t rdevmajor;
// uint32_t rdevminor;
// uint32_t namesize;
// uint32_t check;
char *filename;
void *data;
int remove;
} cpio_entry;
typedef struct cpio_newc_header {
char magic[6];
char ino[8];
char mode[8];
char uid[8];
char gid[8];
char nlink[8];
char mtime[8];
char filesize[8];
char devmajor[8];
char devminor[8];
char rdevmajor[8];
char rdevminor[8];
char namesize[8];
char check[8];
} cpio_newc_header;
// Basic cpio functions
void cpio_free(cpio_entry *e);
int cpio_find(struct vector *v, const char *entry);
int cpio_cmp(const void *a, const void *b);
void parse_cpio(struct vector *v, const char *filename);
void dump_cpio(struct vector *v, const char *filename);
void cpio_vec_insert(struct vector *v, cpio_entry *n);
void cpio_vec_destroy(struct vector *v);
void cpio_rm(struct vector *v, int recursive, const char *entry);
void cpio_mkdir(struct vector *v, mode_t mode, const char *entry);
void cpio_ln(struct vector *v, const char *target, const char *entry);
void cpio_add(struct vector *v, mode_t mode, const char *entry, const char *filename);
int cpio_mv(struct vector *v, const char *from, const char *to);
int cpio_extract(struct vector *v, const char *entry, const char *filename);
void cpio_extract_all(struct vector *v);
#endif

View File

@@ -5,8 +5,10 @@
#define _DAEMON_H_
#include <pthread.h>
#include <sys/un.h>
#include <sys/socket.h>
extern pthread_t sepol_patch;
extern int is_daemon_init, seperate_vendor;
// Commands require connecting to daemon
typedef enum {
@@ -38,11 +40,14 @@ typedef enum {
// daemon.c
void start_daemon(int client);
void start_daemon();
int connect_daemon();
void auto_start_magiskhide();
void daemon_init();
// socket_trans.c
// socket.c
int setup_socket(struct sockaddr_un *sun);
int recv_fd(int sockfd);
void send_fd(int sockfd, int fd);
int read_int(int fd);
@@ -50,10 +55,6 @@ void write_int(int fd, int val);
char* read_string(int fd);
void write_string(int fd, const char* val);
// log_monitor.c
void monitor_logs();
/***************
* Boot Stages *
***************/
@@ -61,6 +62,7 @@ void monitor_logs();
void post_fs(int client);
void post_fs_data(int client);
void late_start(int client);
void fix_filecon();
/**************
* MagiskHide *
@@ -76,6 +78,6 @@ void ls_hide_list(int client);
* Superuser *
*************/
void su_daemon_receiver(int client);
void su_daemon_receiver(int client, struct ucred *credential);
#endif

View File

@@ -0,0 +1,86 @@
/* logging.h - Error handling and logging
*/
#ifndef _LOGGING_H_
#define _LOGGING_H_
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#define str(a) #a
#define xstr(a) str(a)
/**************
* No logging *
**************/
#define LOGD(...)
#define LOGI(...)
#define LOGW(...)
#define LOGE(...)
#define PLOGE(...)
/******************
* Daemon logging *
******************/
#ifdef IS_DAEMON
#undef LOGI
#undef LOGW
#undef LOGE
#undef PLOGE
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "Magisk"
#ifdef MAGISK_DEBUG
#undef LOGD
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
enum {
HIDE_EVENT,
LOG_EVENT,
DEBUG_EVENT
};
struct log_listener {
int fd;
int (*filter) (const char*);
};
extern struct log_listener log_events[];
void monitor_logs();
void start_debug_full_log();
void stop_debug_full_log();
void start_debug_log();
#endif
/********************
* Tools Log & Exit *
********************/
#ifdef XWRAP_EXIT
#undef LOGE
#undef PLOGE
#include <stdio.h>
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
#endif
#endif // _LOGGING_H_

74
core/jni/include/magisk.h Normal file
View File

@@ -0,0 +1,74 @@
/* magisk.h - Top header
*/
#ifndef _MAGISK_H_
#define _MAGISK_H_
#include "logging.h"
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
#define REQUESTOR_DAEMON_PATH "\0MAGISK"
#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 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 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"
// split policy paths
#define PLAT_POLICY_DIR "/system/etc/selinux/"
#define NONPLAT_POLICY_DIR "/vendor/etc/selinux/"
#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil"
#define SPLIT_PLAT_MAPPING PLAT_POLICY_DIR "mapping/%s.cil"
#define SPLIT_PRECOMPILE NONPLAT_POLICY_DIR "precompiled_sepolicy"
#define SPLIT_NONPLAT_VER NONPLAT_POLICY_DIR "plat_sepolicy_vers.txt"
#define MAGISKHIDE_PROP "persist.magisk.hide"
extern char *argv0; /* For changing process name */
#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL })
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
int create_links(const char *bin, const char *path);
// Multi-call entrypoints
int 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
#endif

View File

@@ -0,0 +1,44 @@
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 /dev/.magisk.unblock\n"
"\n"
// Services
"service magisk_daemon /sbin/magisk --daemon\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
"\n"
"service magisk_pfs /sbin/magisk --post-fs\n"
" user root\n"
" seclabel u:r:su: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"
" class late_start\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
;

View File

@@ -12,9 +12,11 @@ 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);
char *getprop(const char *name);
int deleteprop(const char *name, const int trigger);
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 (*cbk)(const char *name));
void getprop_all(void (*callback)(const char*, const char*));
#ifdef __cplusplus
}

View File

@@ -7,7 +7,6 @@
#include <stdio.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -18,8 +17,6 @@
#define UID_SYSTEM (get_system_uid())
#define UID_RADIO (get_radio_uid())
extern int quit_signals[];
// xwrap.c
FILE *xfopen(const char *pathname, const char *mode);
@@ -28,12 +25,14 @@ FILE *xfdopen(int fd, const char *mode);
#define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
int xopen2(const char *pathname, int flags);
int xopen3(const char *pathname, int flags, mode_t mode);
int xopenat(int dirfd, const char *pathname, int flags);
ssize_t xwrite(int fd, const void *buf, size_t count);
ssize_t xread(int fd, void *buf, size_t count);
ssize_t xxread(int fd, void *buf, size_t count);
int xpipe2(int pipefd[2], int flags);
int xsetns(int fd, int nstype);
DIR *xopendir(const char *name);
DIR *xfdopendir(int fd);
struct dirent *xreaddir(DIR *dirp);
pid_t xsetsid();
int xsocket(int domain, int type, int protocol);
@@ -53,22 +52,26 @@ int xstat(const char *pathname, struct stat *buf);
int xlstat(const char *pathname, struct stat *buf);
int xdup2(int oldfd, int newfd);
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
int xsymlink(const char *target, const char *linkpath);
int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
int xumount(const char *target);
int xumount2(const char *target, int flags);
int xchmod(const char *pathname, mode_t mode);
int xrename(const char *oldpath, const char *newpath);
int xmkdir(const char *pathname, mode_t mode);
int xmkdir_p(const char *pathname, mode_t mode);
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
int xmkdir_p(const char *pathname, mode_t mode);
pid_t xfork();
// misc.c
#define quit_signals ((int []) { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 })
unsigned get_shell_uid();
unsigned get_system_uid();
unsigned get_radio_uid();
@@ -78,19 +81,52 @@ int vector_to_file(const char* filename, struct vector *v);
ssize_t fdgets(char *buf, size_t size, int fd);
void ps(void (*func)(int));
void ps_filter_proc_name(const char *filter, void (*func)(int));
int create_links(const char *bin, const char *path);
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_command_sync(char *const argv0, ...);
int mkdir_p(const char *pathname, mode_t mode);
int bind_mount(const char *from, const char *to);
int open_new(const char *filename);
int cp_afc(const char *source, const char *target);
void fclone_attr(const int sourcefd, const int targetfd);
void clone_attr(const char *source, const char *target);
void get_client_cred(int fd, struct ucred *cred);
int switch_mnt_ns(int pid);
int fork_dont_care();
void wait_till_exists(const char *target);
// file.c
extern char **excl_list;
struct file_attr {
struct stat st;
char con[128];
};
int fd_getpath(int fd, char *path, size_t size);
int mkdir_p(const char *pathname, mode_t mode);
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*));
void rm_rf(const char *path);
void frm_rf(int dirfd);
void mv_f(const char *source, const char *destination);
void mv_dir(int src, int dest);
void cp_afc(const char *source, const char *destination);
void clone_dir(int src, int dest);
int getattr(const char *path, struct file_attr *a);
int getattrat(int dirfd, const char *pathname, struct file_attr *a);
int fgetattr(int fd, struct file_attr *a);
int setattr(const char *path, struct file_attr *a);
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);
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);
void full_read(const char *filename, void **buf, size_t *size);
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size);
void stream_full_read(int fd, void **buf, size_t *size);
void write_zero(int fd, size_t size);
void mem_align(size_t *pos, size_t align);
void file_align(int fd, size_t align, int out);
// img.c
@@ -106,4 +142,10 @@ void umount_image(const char *target, const char *device);
int merge_img(const char *source, const char *target);
void trim_img(const char *img);
// pattern.c
void patch_init_rc(void **buf, size_t *size);
int patch_verity(void **buf, uint32_t *size, int patch);
void patch_encryption(void **buf, uint32_t *size);
#endif

View File

@@ -7,8 +7,8 @@
#include <sys/types.h>
struct vector {
size_t size;
size_t cap;
unsigned size;
unsigned cap;
void **data;
};
@@ -26,10 +26,12 @@ struct vector *vec_dup(struct vector *v);
/* Usage: vec_for_each(vector *v, void *e) */
#define vec_for_each(v, e) \
e = v ? (v)->data[0] : NULL; \
for (size_t _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
for (int _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
#define vec_for_each_r(v, e) \
e = v ? (v)->data[(v)->size - 1] : NULL; \
for (size_t _ = (v)->size; v && _ > 0; --_, e = (v)->data[_ - 1])
e = (v && (v)->size > 0) ? (v)->data[(v)->size - 1] : NULL; \
for (int _ = ((int) (v)->size) - 1; v && _ >= 0; --_, e = (v)->data[_])
#define vec_cur(v) vec_entry(v)[_]
#endif

View File

@@ -0,0 +1,306 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "bootimg.h"
#include "magiskboot.h"
#include "utils.h"
#include "logging.h"
#define INSUF_BLOCK_RET 2
#define CHROMEOS_RET 3
#define ELF32_RET 4
#define ELF64_RET 5
static void dump(void *buf, size_t size, const char *filename) {
int fd = creat(filename, 0644);
xwrite(fd, buf, size);
close(fd);
}
static size_t restore(const char *filename, int fd) {
int ifd = xopen(filename, O_RDONLY);
size_t size = lseek(ifd, 0, SEEK_END);
lseek(ifd, 0, SEEK_SET);
xsendfile(fd, ifd, NULL, size);
close(ifd);
return size;
}
static void restore_buf(int fd, const void *buf, size_t size) {
xwrite(fd, buf, size);
}
static void print_hdr(const boot_img_hdr *hdr) {
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr->kernel_size, hdr->kernel_addr);
fprintf(stderr, "RAMDISK [%d] @ 0x%08x\n", hdr->ramdisk_size, hdr->ramdisk_addr);
fprintf(stderr, "SECOND [%d] @ 0x%08x\n", hdr->second_size, hdr->second_addr);
fprintf(stderr, "EXTRA [%d] @ 0x%08x\n", hdr->extra_size, hdr->tags_addr);
fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size);
if (hdr->os_version != 0) {
int a,b,c,y,m = 0;
int os_version, os_patch_level;
os_version = hdr->os_version >> 11;
os_patch_level = hdr->os_version & 0x7ff;
a = (os_version >> 14) & 0x7f;
b = (os_version >> 7) & 0x7f;
c = os_version & 0x7f;
fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c);
y = (os_patch_level >> 4) + 2000;
m = os_patch_level & 0xf;
fprintf(stderr, "PATCH_LEVEL [%d-%02d]\n", y, m);
}
fprintf(stderr, "NAME [%s]\n", hdr->name);
fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline);
}
int parse_img(const char *image, boot_img *boot) {
memset(boot, 0, sizeof(*boot));
int is_blk = mmap_ro(image, &boot->map_addr, &boot->map_size);
// Parse image
fprintf(stderr, "Parsing boot image: [%s]\n", image);
for (size_t pos = 0; pos < boot->map_size; pos += 256) {
switch (check_type(boot->map_addr + pos)) {
case CHROMEOS:
// The caller should know it's chromeos, as it needs additional signing
boot->flags |= CHROMEOS_FLAG;
continue;
case ELF32:
exit(ELF32_RET);
case ELF64:
exit(ELF64_RET);
case AOSP:
// Read the header
memcpy(&boot->hdr, boot->map_addr + pos, sizeof(boot->hdr));
pos += boot->hdr.page_size;
print_hdr(&boot->hdr);
boot->kernel = boot->map_addr + pos;
pos += boot->hdr.kernel_size;
mem_align(&pos, boot->hdr.page_size);
boot->ramdisk = boot->map_addr + pos;
pos += boot->hdr.ramdisk_size;
mem_align(&pos, boot->hdr.page_size);
if (boot->hdr.second_size) {
boot->second = boot->map_addr + pos;
pos += boot->hdr.second_size;
mem_align(&pos, boot->hdr.page_size);
}
if (boot->hdr.extra_size) {
boot->extra = boot->map_addr + pos;
pos += boot->hdr.extra_size;
mem_align(&pos, boot->hdr.page_size);
}
if (pos < boot->map_size) {
boot->tail = boot->map_addr + pos;
boot->tail_size = boot->map_size - pos;
}
// Search for dtb in kernel
for (uint32_t i = 0; i < boot->hdr.kernel_size; ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
boot->dtb = boot->kernel + i;
boot->dt_size = boot->hdr.kernel_size - i;
boot->hdr.kernel_size = i;
fprintf(stderr, "DTB [%u]\n", boot->dt_size);
}
}
boot->ramdisk_type = check_type(boot->ramdisk);
boot->kernel_type = check_type(boot->kernel);
// Check MTK
if (boot->kernel_type == MTK) {
fprintf(stderr, "MTK_KERNEL_HDR [512]\n");
boot->flags |= MTK_KERNEL;
memcpy(&boot->mtk_kernel_hdr, boot->kernel, sizeof(mtk_hdr));
boot->kernel += 512;
boot->hdr.kernel_size -= 512;
boot->kernel_type = check_type(boot->kernel);
}
if (boot->ramdisk_type == MTK) {
fprintf(stderr, "MTK_RAMDISK_HDR [512]\n");
boot->flags |= MTK_RAMDISK;
memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr));
boot->ramdisk += 512;
boot->hdr.ramdisk_size -= 512;
boot->ramdisk_type = check_type(boot->ramdisk);
}
char fmt[16];
get_type_name(boot->kernel_type, fmt);
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
get_type_name(boot->ramdisk_type, fmt);
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt);
return boot->flags & CHROMEOS_FLAG ? CHROMEOS_RET :
((is_blk && boot->tail_size < 500 * 1024) ? INSUF_BLOCK_RET : 0);
default:
continue;
}
}
LOGE("No boot image magic found!\n");
}
void unpack(const char* image) {
boot_img boot;
int ret = parse_img(image, &boot);
int fd;
// Dump kernel
if (COMPRESSED(boot.kernel_type)) {
fd = creat(KERNEL_FILE, 0644);
decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size);
close(fd);
} else {
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE);
}
if (boot.dt_size) {
// Dump dtb
dump(boot.dtb, boot.dt_size, DTB_FILE);
}
// Dump ramdisk
if (COMPRESSED(boot.ramdisk_type)) {
fd = creat(RAMDISK_FILE, 0644);
decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size);
close(fd);
} else {
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw");
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
}
if (boot.hdr.second_size) {
// Dump second
dump(boot.second, boot.hdr.second_size, SECOND_FILE);
}
if (boot.hdr.extra_size) {
// Dump extra
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
}
munmap(boot.map_addr, boot.map_size);
exit(ret);
}
void repack(const char* orig_image, const char* out_image) {
boot_img boot;
// There are possible two MTK headers
size_t mtk_kernel_off, mtk_ramdisk_off;
// Parse original image
parse_img(orig_image, &boot);
fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
// Create new image
int fd = creat(out_image, 0644);
// Skip a page for header
write_zero(fd, boot.hdr.page_size);
if (boot.flags & MTK_KERNEL) {
// Record position and skip MTK header
mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, 512);
}
if (COMPRESSED(boot.kernel_type)) {
size_t raw_size;
void *kernel_raw;
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
boot.hdr.kernel_size = comp(boot.kernel_type, fd, kernel_raw, raw_size);
munmap(kernel_raw, raw_size);
} else {
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
}
// Restore dtb
if (boot.dt_size && access(DTB_FILE, R_OK) == 0) {
boot.hdr.kernel_size += restore(DTB_FILE, fd);
}
file_align(fd, boot.hdr.page_size, 1);
if (boot.flags & MTK_RAMDISK) {
// Record position and skip MTK header
mtk_ramdisk_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, 512);
}
if (access(RAMDISK_FILE, R_OK) == 0) {
// If we found raw cpio, compress to original format
size_t cpio_size;
void *cpio;
mmap_ro(RAMDISK_FILE, &cpio, &cpio_size);
boot.hdr.ramdisk_size = comp(boot.ramdisk_type, fd, cpio, cpio_size);
munmap(cpio, cpio_size);
} else {
// Find compressed ramdisk
char name[PATH_MAX];
int found = 0;
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
if (access(name, R_OK) == 0) {
found = 1;
break;
}
}
if (!found)
LOGE("No ramdisk exists!\n");
boot.hdr.ramdisk_size = restore(name, fd);
}
file_align(fd, boot.hdr.page_size, 1);
// Restore second
if (boot.hdr.second_size && access(SECOND_FILE, R_OK) == 0) {
boot.hdr.second_size = restore(SECOND_FILE, fd);
file_align(fd, boot.hdr.page_size, 1);
}
// Restore extra
if (boot.hdr.extra_size && access(EXTRA_FILE, R_OK) == 0) {
boot.hdr.extra_size = restore(EXTRA_FILE, fd);
file_align(fd, boot.hdr.page_size, 1);
}
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
if (boot.tail_size >= 16) {
if (memcmp(boot.tail, "SEANDROIDENFORCE", 16) == 0 ||
memcmp(boot.tail, LG_BUMP_MAGIC, 16) == 0 ) {
restore_buf(fd, boot.tail, 16);
}
}
// Write MTK headers back
if (boot.flags & MTK_KERNEL) {
lseek(fd, mtk_kernel_off, SEEK_SET);
boot.mtk_kernel_hdr.size = boot.hdr.kernel_size;
boot.hdr.kernel_size += 512;
restore_buf(fd, &boot.mtk_kernel_hdr, sizeof(mtk_hdr));
}
if (boot.flags & MTK_RAMDISK) {
lseek(fd, mtk_ramdisk_off, SEEK_SET);
boot.mtk_ramdisk_hdr.size = boot.hdr.ramdisk_size;
boot.hdr.ramdisk_size += 512;
restore_buf(fd, &boot.mtk_ramdisk_hdr, sizeof(mtk_hdr));
}
// Main header
lseek(fd, 0, SEEK_SET);
restore_buf(fd, &boot.hdr, sizeof(boot.hdr));
// Print new image info
print_hdr(&boot.hdr);
munmap(boot.map_addr, boot.map_size);
close(fd);
}

View File

@@ -2,20 +2,21 @@
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <stdint.h>
#include "types.h"
#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_
@@ -43,7 +44,7 @@ struct boot_img_hdr
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
uint32_t dt_size; /* device tree in bytes */
uint32_t extra_size; /* extra blob size in bytes */
/* operating system version and security patch level; for
* version "A.B.C" and patch level "Y-M-D":
@@ -64,22 +65,22 @@ struct boot_img_hdr
} __attribute__((packed));
/*
** +-----------------+
** +-----------------+
** | boot header | 1 page
** +-----------------+
** | kernel | n pages
** | kernel | n pages
** +-----------------+
** | ramdisk | m pages
** | ramdisk | m pages
** +-----------------+
** | second stage | o pages
** +-----------------+
** | device tree | p pages
** | extra blobs | p pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
** p = (dt_size + page_size - 1) / page_size
** p = (extra_size + page_size - 1) / page_size
**
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
@@ -99,4 +100,28 @@ typedef struct mtk_hdr {
uint8_t name[32]; /* The type of the header */
} mtk_hdr;
// Flags
#define MTK_KERNEL 0x1
#define MTK_RAMDISK 0x2
#define CHROMEOS_FLAG 0x4
typedef struct boot_img {
size_t map_size;
uint32_t dt_size;
size_t tail_size;
uint8_t flags;
file_t kernel_type, ramdisk_type;
boot_img_hdr hdr;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
void *map_addr;
void *kernel;
void *dtb;
void *ramdisk;
void *second;
void *extra;
void *tail;
} boot_img;
#endif

View File

@@ -0,0 +1,512 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <zlib.h>
#include <lzma.h>
#include <lz4.h>
#include <lz4frame.h>
#include <lz4hc.h>
#include <bzlib.h>
#include "magiskboot.h"
#include "logging.h"
#include "utils.h"
#define CHUNK 0x40000
// Mode: 0 = decode; 1 = encode
size_t gzip(int mode, int fd, const void *buf, size_t size) {
size_t ret = 0, have, total = 0;
z_stream strm;
unsigned char out[CHUNK];
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
switch(mode) {
case 0:
ret = inflateInit2(&strm, 15 | 16);
break;
case 1:
ret = deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
break;
}
if (ret != Z_OK)
LOGE("Unable to init zlib stream\n");
strm.next_in = (void *) buf;
strm.avail_in = size;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = inflate(&strm, Z_FINISH);
break;
case 1:
ret = deflate(&strm, Z_FINISH);
break;
}
if (ret == Z_STREAM_ERROR)
LOGE("Error when running gzip\n");
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
switch(mode) {
case 0:
inflateEnd(&strm);
break;
case 1:
deflateEnd(&strm);
break;
}
return total;
}
// Mode: 0 = decode xz/lzma; 1 = encode xz; 2 = encode lzma
size_t lzma(int mode, int fd, const void *buf, size_t size) {
size_t have, total = 0;
lzma_ret ret = 0;
lzma_stream strm = LZMA_STREAM_INIT;
lzma_options_lzma opt;
unsigned char out[CHUNK];
// Initialize preset
lzma_lzma_preset(&opt, 9);
lzma_filter filters[] = {
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
};
switch(mode) {
case 0:
ret = lzma_auto_decoder(&strm, UINT64_MAX, 0);
break;
case 1:
ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
break;
case 2:
ret = lzma_alone_encoder(&strm, &opt);
break;
}
if (ret != LZMA_OK)
LOGE("Unable to init lzma stream\n");
strm.next_in = buf;
strm.avail_in = size;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = lzma_code(&strm, LZMA_FINISH);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
LOGE("LZMA error %d!\n", ret);
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
lzma_end(&strm);
return total;
}
// Mode: 0 = decode; 1 = encode
size_t lz4(int mode, int fd, const void *buf, size_t size) {
LZ4F_decompressionContext_t dctx;
LZ4F_compressionContext_t cctx;
LZ4F_frameInfo_t info;
size_t blockSize, outCapacity, avail_in, ret = 0, pos = 0, total = 0;
size_t have, read;
void *out = NULL;
// Initialize context
switch(mode) {
case 0:
ret = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
break;
case 1:
ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
break;
}
if (LZ4F_isError(ret))
LOGE("Context creation error: %s\n", LZ4F_getErrorName(ret));
// Allocate out buffer
blockSize = 1 << 22;
switch(mode) {
case 0:
// Read header
read = blockSize;
ret = LZ4F_getFrameInfo(dctx, &info, buf, &read);
if (LZ4F_isError(ret))
LOGE("LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(ret));
switch (info.blockSizeID) {
case LZ4F_default:
case LZ4F_max64KB: outCapacity = 1 << 16; break;
case LZ4F_max256KB: outCapacity = 1 << 18; break;
case LZ4F_max1MB: outCapacity = 1 << 20; break;
case LZ4F_max4MB: outCapacity = 1 << 22; break;
default:
LOGE("Impossible unless more block sizes are allowed\n");
}
pos += read;
break;
case 1:
outCapacity = LZ4F_compressFrameBound(blockSize, NULL);
break;
}
out = xmalloc(outCapacity);
// Write header
if (mode == 1) {
LZ4F_preferences_t prefs;
memset(&prefs, 0, sizeof(prefs));
prefs.autoFlush = 1;
prefs.compressionLevel = 9;
prefs.frameInfo.blockMode = 1;
prefs.frameInfo.blockSizeID = 7;
prefs.frameInfo.contentChecksumFlag = 1;
have = ret = LZ4F_compressBegin(cctx, out, size, &prefs);
if (LZ4F_isError(ret))
LOGE("Failed to start compression: error %s\n", LZ4F_getErrorName(ret));
total += xwrite(fd, out, have);
}
do {
if (pos + blockSize >= size) {
avail_in = size - pos;
} else {
avail_in = blockSize;
}
do {
switch(mode) {
case 0:
have = outCapacity;
read = avail_in;
ret = LZ4F_decompress(dctx, out, &have, buf + pos, &read, NULL);
break;
case 1:
read = avail_in;
have = ret = LZ4F_compressUpdate(cctx, out, outCapacity, buf + pos, avail_in, NULL);
break;
}
if (LZ4F_isError(ret))
LOGE("LZ4 coding error: %s\n", LZ4F_getErrorName(ret));
total += xwrite(fd, out, have);
// Update status
pos += read;
avail_in -= read;
} while(avail_in != 0 && ret != 0);
} while(pos < size && ret != 0);
switch(mode) {
case 0:
LZ4F_freeDecompressionContext(dctx);
break;
case 1:
have = ret = LZ4F_compressEnd(cctx, out, outCapacity, NULL);
if (LZ4F_isError(ret))
LOGE("Failed to end compression: error %s\n", LZ4F_getErrorName(ret));
total += xwrite(fd, out, have);
LZ4F_freeCompressionContext(cctx);
break;
}
free(out);
return total;
}
// Mode: 0 = decode; 1 = encode
size_t bzip2(int mode, int fd, const void* buf, size_t size) {
size_t ret = 0, have, total = 0;
bz_stream strm;
char out[CHUNK];
strm.bzalloc = NULL;
strm.bzfree = NULL;
strm.opaque = NULL;
switch(mode) {
case 0:
ret = BZ2_bzDecompressInit(&strm, 0, 0);
break;
case 1:
ret = BZ2_bzCompressInit(&strm, 9, 0, 0);
break;
}
if (ret != BZ_OK)
LOGE("Unable to init bzlib stream\n");
strm.next_in = (void *) buf;
strm.avail_in = size;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = BZ2_bzDecompress(&strm);
break;
case 1:
ret = BZ2_bzCompress(&strm, BZ_FINISH);
break;
}
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
switch(mode) {
case 0:
BZ2_bzDecompressEnd(&strm);
break;
case 1:
BZ2_bzCompressEnd(&strm);
break;
}
return total;
}
#define LZ4_LEGACY_BLOCKSIZE 0x800000
// Mode: 0 = decode; 1 = encode
size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
size_t pos = 0;
int have;
char *out;
unsigned block_size, insize, total = 0;
switch(mode) {
case 0:
out = xmalloc(LZ4_LEGACY_BLOCKSIZE);
// Skip magic
pos += 4;
break;
case 1:
out = xmalloc(LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE));
// Write magic
total += xwrite(fd, "\x02\x21\x4c\x18", 4);
break;
}
do {
switch(mode) {
case 0:
// Read block size
block_size = *(unsigned *)(buf + pos);
pos += 4;
if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE))
goto done;
have = LZ4_decompress_safe(buf + pos, out, block_size, LZ4_LEGACY_BLOCKSIZE);
if (have < 0)
LOGE("Cannot decode lz4_legacy block\n");
pos += block_size;
break;
case 1:
if (pos + LZ4_LEGACY_BLOCKSIZE >= size)
insize = size - pos;
else
insize = LZ4_LEGACY_BLOCKSIZE;
have = LZ4_compress_HC(buf + pos, out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE), 9);
if (have == 0)
LOGE("lz4_legacy compression error\n");
pos += insize;
// Write block size
total += xwrite(fd, &have, sizeof(have));
break;
}
// Write main data
total += xwrite(fd, out, have);
} while(pos < size);
done:
if (mode == 1) {
// Append original size to output
unsigned uncomp = size;
xwrite(fd, &uncomp, sizeof(uncomp));
}
free(out);
return total;
}
long long decomp(file_t type, int to, const void *from, size_t size) {
switch (type) {
case GZIP:
return gzip(0, to, from, size);
case XZ:
return lzma(0, to, from, size);
case LZMA:
return lzma(0, to, from, size);
case BZIP2:
return bzip2(0, to, from, size);
case LZ4:
return lz4(0, to, from, size);
case LZ4_LEGACY:
return lz4_legacy(0, to, from, size);
default:
// Unsupported
return -1;
}
}
long long comp(file_t type, int to, const void *from, size_t size) {
switch (type) {
case GZIP:
return gzip(1, to, from, size);
case XZ:
return lzma(1, to, from, size);
case LZMA:
return lzma(2, to, from, size);
case BZIP2:
return bzip2(1, to, from, size);
case LZ4:
return lz4(1, to, from, size);
case LZ4_LEGACY:
return lz4_legacy(1, to, from, size);
default:
// Unsupported
return -1;
}
}
/*
* Below are utility functions for commandline
*/
void decomp_file(char *from, const char *to) {
int strip = 1;
void *file;
size_t size = 0;
if (strcmp(from, "-") == 0)
stream_full_read(STDIN_FILENO, &file, &size);
else
mmap_ro(from, &file, &size);
file_t type = check_type(file);
char *ext;
ext = strrchr(from, '.');
if (to == NULL)
to = from;
if (ext != NULL) {
// Strip out a matched file extension
switch (type) {
case GZIP:
if (strcmp(ext, ".gz") != 0)
strip = 0;
break;
case XZ:
if (strcmp(ext, ".xz") != 0)
strip = 0;
break;
case LZMA:
if (strcmp(ext, ".lzma") != 0)
strip = 0;
break;
case BZIP2:
if (strcmp(ext, ".bz2") != 0)
strip = 0;
break;
case LZ4_LEGACY:
case LZ4:
if (strcmp(ext, ".lz4") != 0)
strip = 0;
break;
default:
LOGE("Provided file \'%s\' is not a supported archive format\n", from);
}
if (strip)
*ext = '\0';
}
int fd;
if (strcmp(to, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = creat(to, 0644);
fprintf(stderr, "Decompressing to [%s]\n", to);
}
decomp(type, fd, file, size);
close(fd);
if (to == from && ext != NULL) {
*ext = '.';
unlink(from);
}
if (strcmp(from, "-") == 0)
free(file);
else
munmap(file, size);
}
void comp_file(const char *method, const char *from, const char *to) {
file_t type;
char *ext, dest[PATH_MAX];
if (strcmp(method, "gzip") == 0) {
type = GZIP;
ext = "gz";
} else if (strcmp(method, "xz") == 0) {
type = XZ;
ext = "xz";
} else if (strcmp(method, "lzma") == 0) {
type = LZMA;
ext = "lzma";
} else if (strcmp(method, "lz4") == 0) {
type = LZ4;
ext = "lz4";
} else if (strcmp(method, "lz4_legacy") == 0) {
type = LZ4_LEGACY;
ext = "lz4";
} else if (strcmp(method, "bzip2") == 0) {
type = BZIP2;
ext = "bz2";
} else {
fprintf(stderr, "Only support following methods: ");
for (int i = 0; SUP_LIST[i]; ++i)
fprintf(stderr, "%s ", SUP_LIST[i]);
fprintf(stderr, "\n");
exit(1);
}
void *file;
size_t size;
if (strcmp(from, "-") == 0)
stream_full_read(STDIN_FILENO, &file, &size);
else
mmap_ro(from, &file, &size);
if (to == NULL) {
if (strcmp(from, "-") == 0)
strcpy(dest, "-");
else
snprintf(dest, sizeof(dest), "%s.%s", from, ext);
} else
strcpy(dest, to);
int fd;
if (strcmp(dest, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = creat(dest, 0644);
fprintf(stderr, "Compressing to [%s]\n", dest);
}
comp(type, fd, file, size);
close(fd);
if (strcmp(from, "-") == 0)
free(file);
else
munmap(file, size);
if (to == NULL)
unlink(from);
}

111
core/jni/magiskboot/dtb.c Normal file
View File

@@ -0,0 +1,111 @@
#include <libfdt.h>
#include <unistd.h>
#include <sys/mman.h>
#include "magiskboot.h"
#include "utils.h"
static void print_props(const void *fdt, int node, int depth) {
int prop;
fdt_for_each_property_offset(prop, fdt, node) {
for (int i = 0; i < depth; ++i) printf(" ");
printf(" ");
int size;
const char *name;
const char *value = fdt_getprop_by_offset(fdt, prop, &name, &size);
printf("[%s]: [%s]\n", name, value);
}
}
static void print_subnode(const void *fdt, int parent, int depth) {
int node;
fdt_for_each_subnode(node, fdt, parent) {
for (int i = 0; i < depth; ++i) printf(" ");
printf("#%d: %s\n", node, fdt_get_name(fdt, node, NULL));
print_props(fdt, node, depth);
print_subnode(fdt, node, depth + 1);
}
}
static int find_fstab(const void *fdt, int parent) {
int node, fstab;
fdt_for_each_subnode(node, fdt, parent) {
if (strcmp(fdt_get_name(fdt, node, NULL), "fstab") == 0)
return node;
fstab = find_fstab(fdt, node);
if (fstab != -1)
return fstab;
}
return -1;
}
static void dtb_dump(const char *file) {
size_t size ;
void *dtb, *fdt;
fprintf(stderr, "Loading dtbs from [%s]\n", file);
mmap_ro(file, &dtb, &size);
// Loop through all the dtbs
int dtb_num = 0;
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
fdt = dtb + i;
fprintf(stderr, "Dumping dtb.%04d\n", dtb_num++);
print_subnode(fdt, 0, 0);
}
}
fprintf(stderr, "\n");
munmap(dtb, size);
exit(0);
}
static void dtb_patch(const char *file, int patch) {
size_t size ;
void *dtb, *fdt;
fprintf(stderr, "Loading dtbs from [%s]\n", file);
if (patch)
mmap_rw(file, &dtb, &size);
else
mmap_ro(file, &dtb, &size);
// Loop through all the dtbs
int dtb_num = 0, found = 0;
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
fdt = dtb + i;
int fstab = find_fstab(fdt, 0);
if (fstab > 0) {
fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num++);
int block;
fdt_for_each_subnode(block, fdt, fstab) {
fprintf(stderr, "Found block [%s] in fstab\n", fdt_get_name(fdt, block, NULL));
uint32_t value_size;
void *value = (void *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
if (patch) {
void *dup = xmalloc(value_size);
memcpy(dup, value, value_size);
memset(value, 0, value_size);
found |= patch_verity(&dup, &value_size, 1);
memcpy(value, dup, value_size);
free(dup);
} else {
found |= patch_verity(&value, &value_size, 0);
}
}
}
}
}
munmap(dtb, size);
exit(!found);
}
int dtb_commands(const char *cmd, int argc, char *argv[]) {
if (argc == 0) return 1;
if (strcmp(cmd, "dump") == 0)
dtb_dump(argv[0]);
else if (strcmp(cmd, "patch") == 0)
dtb_patch(argv[0], 1);
else if (strcmp(cmd, "test") == 0)
dtb_patch(argv[0], 0);
return 0;
}

View File

@@ -1,4 +1,10 @@
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/mman.h>
#include "magiskboot.h"
#include "utils.h"
static void hex2byte(const char *hex, unsigned char *str) {
char high, low;
@@ -12,15 +18,15 @@ static void hex2byte(const char *hex, unsigned char *str) {
void hexpatch(const char *image, const char *from, const char *to) {
int patternsize = strlen(from) / 2, patchsize = strlen(to) / 2;
size_t filesize;
unsigned char *file, *pattern, *patch;
void *file, *pattern, *patch;
mmap_rw(image, &file, &filesize);
pattern = xmalloc(patternsize);
patch = xmalloc(patchsize);
hex2byte(from, pattern);
hex2byte(to, patch);
for (size_t i = 0; i < filesize - patternsize; ++i) {
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
if (memcmp(file + i, pattern, patternsize) == 0) {
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to);
memset(file + i, 0, patternsize);
memcpy(file + i, patch, patchsize);
i += patternsize - 1;

View File

@@ -0,0 +1,35 @@
#ifndef _MAGISKBOOT_H_
#define _MAGISKBOOT_H_
#include <sys/types.h>
#include "logging.h"
#include "bootimg.h"
#define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio"
#define SECOND_FILE "second"
#define EXTRA_FILE "extra"
#define DTB_FILE "dtb"
#define NEW_BOOT "new-boot.img"
// Main entries
void unpack(const char *image);
void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to);
int parse_img(const char *image, boot_img *boot);
int cpio_commands(int argc, char *argv[]);
void comp_file(const char *method, const char *from, const char *to);
void decomp_file(char *from, const char *to);
int dtb_commands(const char *cmd, int argc, char *argv[]);
// Compressions
size_t gzip(int mode, int fd, const void *buf, size_t size);
size_t lzma(int mode, int fd, const void *buf, size_t size);
size_t lz4(int mode, int fd, const void *buf, size_t size);
size_t bzip2(int mode, int fd, const void *buf, size_t size);
size_t lz4_legacy(int mode, int fd, const void *buf, size_t size);
long long comp(file_t type, int to, const void *from, size_t size);
long long decomp(file_t type, int to, const void *from, size_t size);
#endif

166
core/jni/magiskboot/main.c Normal file
View File

@@ -0,0 +1,166 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include "magiskboot.h"
#include "utils.h"
#include "sha1.h"
/********************
Patch Boot Image
*********************/
static void usage(char *arg0) {
fprintf(stderr,
"Usage: %s <action> [args...]\n"
"\n"
"Supported actions:\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"
"\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"
"\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"
"\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: "
, 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: ");
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"
"\n"
" --cleanup\n"
" Cleanup the current working directory\n"
"\n");
exit(1);
}
int main(int argc, char *argv[]) {
fprintf(stderr, "MagiskBoot v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Boot Image Modification Tool\n");
umask(0);
if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) {
fprintf(stderr, "Cleaning up...\n");
char name[PATH_MAX];
unlink(KERNEL_FILE);
unlink(RAMDISK_FILE);
unlink(RAMDISK_FILE ".raw");
unlink(SECOND_FILE);
unlink(DTB_FILE);
unlink(EXTRA_FILE);
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
unlink(name);
}
} else if (argc > 2 && strcmp(argv[1], "--sha1") == 0) {
char sha1[21], *buf;
size_t size;
mmap_ro(argv[2], (void **) &buf, &size);
SHA1(sha1, buf, size);
for (int i = 0; i < 20; ++i)
printf("%02x", sha1[i]);
printf("\n");
munmap(buf, size);
} else if (argc > 2 && strcmp(argv[1], "--parse") == 0) {
boot_img boot;
exit(parse_img(argv[2], &boot));
} else if (argc > 2 && strcmp(argv[1], "--unpack") == 0) {
unpack(argv[2]);
} else if (argc > 2 && strcmp(argv[1], "--repack") == 0) {
repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT);
} else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) {
decomp_file(argv[2], argc > 3 ? argv[3] : NULL);
} else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) {
char *method;
method = strchr(argv[1], '=');
if (method == NULL) method = "gzip";
else method++;
comp_file(method, argv[2], argc > 3 ? argv[3] : NULL);
} else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) {
hexpatch(argv[2], argv[3], argv[4]);
} else if (argc > 2 && strcmp(argv[1], "--cpio") == 0) {
if (cpio_commands(argc - 2, argv + 2)) usage(argv[0]);
} else if (argc > 2 && strncmp(argv[1], "--dtb", 5) == 0) {
char *cmd = argv[1] + 5;
if (*cmd == '\0') usage(argv[0]);
else ++cmd;
if (dtb_commands(cmd, argc - 2, argv + 2)) usage(argv[0]);
} else {
usage(argv[0]);
}
return 0;
}

View File

@@ -0,0 +1,334 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <utils.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "magiskboot.h"
#include "cpio.h"
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n",
keepverity ? "true" : "false", keepforceencrypt ? "true" : "false");
cpio_entry *e;
vec_for_each(v, e) {
if (!e) continue;
if (!keepverity) {
if (strncmp(e->filename, ".backup", 7) && strstr(e->filename, "fstab") && S_ISREG(e->mode)) {
patch_verity(&e->data, &e->filesize, 1);
} else if (strcmp(e->filename, "verity_key") == 0) {
fprintf(stderr, "Remove [verity_key]\n");
cpio_free(e);
vec_cur(v) = NULL;
}
}
if (!keepforceencrypt) {
if (strstr(e->filename, "fstab") != NULL && S_ISREG(e->mode)) {
patch_encryption(&e->data, &e->filesize);
}
}
}
}
#define STOCK_BOOT 0x0
#define MAGISK_PATCH 0x1
#define OTHER_PATCH 0x2
static int cpio_test(struct vector *v) {
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL };
for (int i = 0; OTHER_LIST[i]; ++i)
if (cpio_find(v, OTHER_LIST[i]) > 0)
return OTHER_PATCH;
for (int i = 0; MAGISK_LIST[i]; ++i)
if (cpio_find(v, MAGISK_LIST[i]) > 0)
return MAGISK_PATCH;
return STOCK_BOOT;
}
static char *cpio_sha1(struct vector *v) {
cpio_entry *e;
char sha1[41];
vec_for_each(v, e) {
if (!e) continue;
if (strcmp(e->filename, "init.magisk.rc") == 0
|| strcmp(e->filename, "overlay/init.magisk.rc") == 0) {
for (void *pos = e->data; pos < e->data + e->filesize; pos = strchr(pos + 1, '\n') + 1) {
if (memcmp(pos, "# STOCKSHA1=", 12) == 0) {
pos += 12;
memcpy(sha1, pos, 40);
sha1[40] = '\0';
return strdup(sha1);
}
}
} else if (strcmp(e->filename, ".backup/.sha1") == 0) {
return e->data;
}
}
return NULL;
}
static void cpio_backup(struct vector *v, struct vector *bak, const char *orig, const char *sha1) {
struct vector o_body, *o = &o_body;
cpio_entry *m, *n, *rem, *cksm;
char buf[PATH_MAX];
int res, backup;
m = xcalloc(sizeof(*m), 1);
m->filename = strdup(".backup");
m->mode = S_IFDIR;
vec_push_back(bak, m);
rem = xcalloc(sizeof(*rem), 1);
rem->filename = strdup(".backup/.rmlist");
rem->mode = S_IFREG;
if (sha1) {
fprintf(stderr, "Save SHA1: [%s] -> [.backup/.sha1]\n", sha1);
cksm = xcalloc(sizeof(*cksm), 1);
vec_push_back(bak, cksm);
cksm->filename = strdup(".backup/.sha1");
cksm->mode = S_IFREG;
cksm->data = strdup(sha1);
cksm->filesize = strlen(sha1) + 1;
}
vec_init(o);
parse_cpio(o, orig);
// Remove possible backups in original ramdisk
cpio_rm(o, 1, ".backup");
cpio_rm(v, 1, ".backup");
// Sort both vectors before comparing
vec_sort(o, cpio_cmp);
vec_sort(v, cpio_cmp);
// Start comparing
size_t i = 0, j = 0;
while(i != vec_size(o) || j != vec_size(v)) {
backup = 0;
if (i != vec_size(o) && j != vec_size(v)) {
m = vec_entry(o)[i];
n = vec_entry(v)[j];
res = strcmp(m->filename, n->filename);
} else if (i == vec_size(o)) {
n = vec_entry(v)[j];
res = 1;
} else if (j == vec_size(v)) {
m = vec_entry(o)[i];
res = -1;
}
if (res < 0) {
// Something is missing in new ramdisk, backup!
++i;
backup = 1;
fprintf(stderr, "Backup missing entry: ");
} else if (res == 0) {
++i; ++j;
if (m->filesize == n->filesize && memcmp(m->data, n->data, m->filesize) == 0)
continue;
// Not the same!
backup = 1;
fprintf(stderr, "Backup mismatch entry: ");
} else {
// Something new in ramdisk, record in rem
++j;
rem->data = xrealloc(rem->data, rem->filesize + strlen(n->filename) + 1);
memcpy(rem->data + rem->filesize, n->filename, strlen(n->filename) + 1);
rem->filesize += strlen(n->filename) + 1;
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", n->filename);
}
if (backup) {
sprintf(buf, ".backup/%s", m->filename);
fprintf(stderr, "[%s] -> [%s]\n", m->filename, buf);
free(m->filename);
m->filename = strdup(buf);
vec_push_back(bak, m);
// NULL the original entry, so it won't be freed
vec_entry(o)[i - 1] = NULL;
}
}
if (rem->filesize)
vec_push_back(bak, rem);
else
cpio_free(rem);
// Cleanup
cpio_vec_destroy(o);
}
static void cpio_restore(struct vector *v) {
cpio_entry *e;
vec_for_each(v, e) {
if (!e) continue;
if (strncmp(e->filename, ".backup", 7) == 0) {
if (e->filename[7] == '\0') continue;
if (e->filename[8] == '.') {
if (strcmp(e->filename, ".backup/.rmlist") == 0) {
for (int pos = 0; pos < e->filesize; pos += strlen(e->data + pos) + 1)
cpio_rm(v, 0, e->data + pos);
}
continue;
} else {
fprintf(stderr, "Restore [%s] -> [%s]\n", e->filename, e->filename + 8);
vec_cur(v) = NULL;
char *new_name = strdup(e->filename + 8);
free(e->filename);
e->filename = new_name;
cpio_vec_insert(v, e);
}
}
}
// Some known stuff we can remove
cpio_rm(v, 1, ".backup");
cpio_rm(v, 1, "overlay");
cpio_rm(v, 0, "sbin/magic_mask.sh");
cpio_rm(v, 0, "init.magisk.rc");
cpio_rm(v, 0, "magisk");
cpio_rm(v, 0, "ramdisk-recovery.xz");
}
static void restore_high_compress(struct vector *v, const char *incpio) {
// Check if the ramdisk is in high compression mode
if (cpio_extract(v, "ramdisk.cpio.xz", incpio) == 0) {
void *xz;
size_t size;
full_read(incpio, &xz, &size);
int fd = creat(incpio, 0644);
lzma(0, fd, xz, size);
close(fd);
free(xz);
cpio_rm(v, 0, "ramdisk.cpio.xz");
cpio_rm(v, 0, "init");
struct vector vv;
vec_init(&vv);
parse_cpio(&vv, incpio);
cpio_entry *e;
vec_for_each(&vv, e)
vec_push_back(v, e);
vec_destroy(&vv);
}
}
static void enable_high_compress(struct vector *v, struct vector *b, const char *incpio) {
cpio_entry *init, *magiskinit;
// Swap magiskinit with original init
int i = cpio_find(b, ".backup/init"), j = cpio_find(v, "init");
init = vec_entry(b)[i];
magiskinit = vec_entry(v)[j];
free(init->filename);
init->filename = strdup("init");
vec_entry(v)[j] = init;
vec_entry(b)[i] = NULL;
dump_cpio(v, incpio);
cpio_vec_destroy(v);
void *cpio;
size_t size;
full_read(incpio, &cpio, &size);
int fd = creat(incpio, 0644);
lzma(1, fd, cpio, size);
close(fd);
free(cpio);
vec_init(v);
vec_push_back(v, magiskinit);
cpio_add(v, 0, "ramdisk.cpio.xz", incpio);
}
int cpio_commands(int argc, char *argv[]) {
char *incpio = argv[0];
++argv;
--argc;
struct vector v;
vec_init(&v);
parse_cpio(&v, incpio);
int cmdc;
char *cmdv[6];
while (argc) {
cmdc = 0;
for (char *tok = strtok(argv[0], " "); tok; tok = strtok(NULL, " "))
cmdv[cmdc++] = tok;
if (strcmp(cmdv[0], "test") == 0) {
exit(cpio_test(&v));
} else if (strcmp(cmdv[0], "restore") == 0) {
restore_high_compress(&v, incpio);
cpio_restore(&v);
} else if (strcmp(cmdv[0], "sha1") == 0) {
char *sha1 = cpio_sha1(&v);
if (sha1)
printf("%s\n", sha1);
return 0;
} else if (cmdc >= 2 && strcmp(cmdv[0], "backup") == 0) {
struct vector back;
vec_init(&back);
cpio_backup(&v, &back, cmdv[1], cmdc > 2 ? cmdv[2] : NULL);
cpio_entry *e;
vec_for_each(&back, e)
if (e) vec_push_back(&v, e);
vec_destroy(&back);
} else if (cmdc >= 5 && strcmp(cmdv[0], "magisk") == 0) {
cpio_patch(&v, strcmp(cmdv[3], "true") == 0, strcmp(cmdv[4], "true") == 0);
struct vector back;
vec_init(&back);
cpio_backup(&v, &back, cmdv[1], cmdc > 5 ? cmdv[5] : NULL);
cpio_entry *e;
e = xcalloc(sizeof(*e), 1);
e->filename = strdup(".backup/.magisk");
e->mode = S_IFREG;
e->data = xmalloc(50);
snprintf(e->data, 50, "KEEPVERITY=%s\nKEEPFORCEENCRYPT=%s\n", cmdv[3], cmdv[4]);
e->filesize = strlen(e->data) + 1;
vec_push_back(&back, e);
// Enable high compression mode
if (strcmp(cmdv[2], "true") == 0)
enable_high_compress(&v, &back, incpio);
vec_for_each(&back, e)
if (e) vec_push_back(&v, e);
vec_destroy(&back);
} else if (cmdc >= 2 && strcmp(cmdv[0], "rm") == 0) {
int recur = cmdc > 2 && strcmp(cmdv[1], "-r") == 0;
cpio_rm(&v, recur, cmdv[1 + recur]);
} else if (cmdc == 3 && strcmp(cmdv[0], "mv") == 0) {
cpio_mv(&v, cmdv[1], cmdv[2]);
} else if (cmdc == 3 && strcmp(cmdv[0], "patch") == 0) {
cpio_patch(&v, strcmp(cmdv[1], "true") == 0, strcmp(cmdv[2], "true") == 0);
} else if (strcmp(cmdv[0], "extract") == 0) {
if (cmdc == 3) {
return cpio_extract(&v, cmdv[1], cmdv[2]);
} else {
cpio_extract_all(&v);
return 0;
}
} else if (cmdc == 3 && strcmp(cmdv[0], "mkdir") == 0) {
cpio_mkdir(&v, strtoul(cmdv[1], NULL, 8), cmdv[2]);
} else if (cmdc == 3 && strcmp(cmdv[0], "ln") == 0) {
cpio_ln(&v, cmdv[1], cmdv[2]);
} else if (cmdc == 4 && strcmp(cmdv[0], "add") == 0) {
cpio_add(&v, strtoul(cmdv[1], NULL, 8), cmdv[2], cmdv[3]);
} else {
return 1;
}
--argc;
++argv;
}
dump_cpio(&v, incpio);
cpio_vec_destroy(&v);
return 0;
}

View File

@@ -0,0 +1,79 @@
#include <string.h>
#include "bootimg.h"
#include "types.h"
file_t check_type(const void *buf) {
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
return CHROMEOS;
} else if (memcmp(buf, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) {
return AOSP;
} else if (memcmp(buf, ELF32_MAGIC, 5) == 0) {
return ELF32;
} else if (memcmp(buf, ELF64_MAGIC, 5) == 0) {
return ELF64;
} else if (memcmp(buf, GZIP_MAGIC, 4) == 0) {
return GZIP;
} else if (memcmp(buf, LZOP_MAGIC, 9) == 0) {
return LZOP;
} else if (memcmp(buf, XZ_MAGIC, 6) == 0) {
return XZ;
} else if (memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return LZMA;
} else if (memcmp(buf, BZIP_MAGIC, 3) == 0) {
return BZIP2;
} else if (memcmp(buf, LZ4_MAGIC, 4) == 0) {
return LZ4;
} else if (memcmp(buf, LZ4_LEG_MAGIC, 4) == 0) {
return LZ4_LEGACY;
} else if (memcmp(buf, MTK_MAGIC, 4) == 0) {
return MTK;
} else if (memcmp(buf, DTB_MAGIC, 4) == 0) {
return DTB;
} else {
return UNKNOWN;
}
}
void get_type_name(file_t type, char *name) {
char *s;
switch (type) {
case CHROMEOS:
s = "chromeos";
break;
case AOSP:
s = "aosp";
break;
case GZIP:
s = "gzip";
break;
case LZOP:
s = "lzop";
break;
case XZ:
s = "xz";
break;
case LZMA:
s = "lzma";
break;
case BZIP2:
s = "bzip2";
break;
case LZ4:
s = "lz4";
break;
case LZ4_LEGACY:
s = "lz4_legacy";
break;
case MTK:
s = "mtk";
break;
case DTB:
s = "dtb";
break;
default:
s = "raw";
}
strcpy(name, s);
}

View File

@@ -0,0 +1,42 @@
#ifndef _TYPES_H_
#define _TYPES_H_
typedef enum {
UNKNOWN,
CHROMEOS,
AOSP,
ELF32,
ELF64,
GZIP,
LZOP,
XZ,
LZMA,
BZIP2,
LZ4,
LZ4_LEGACY,
MTK,
DTB
} file_t;
#define COMPRESSED(type) (type >= GZIP && type <= LZ4_LEGACY)
#define CHROMEOS_MAGIC "CHROMEOS"
#define ELF32_MAGIC "\x7f""ELF\x01"
#define ELF64_MAGIC "\x7f""ELF\x02"
#define GZIP_MAGIC "\x1f\x8b\x08\x00"
#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
#define XZ_MAGIC "\xfd""7zXZ\x00"
#define BZIP_MAGIC "BZh"
#define LZ4_MAGIC "\x04\x22\x4d\x18"
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
#define MTK_MAGIC "\x88\x16\x88\x58"
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
#define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
#define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })
file_t check_type(const void *buf);
void get_type_name(file_t type, char *name);
#endif

View File

@@ -4,10 +4,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <selinux/selinux.h>
@@ -58,9 +56,9 @@ void hide_sensitive_props() {
}
}
static void rm_magisk_prop(const char *name) {
static void rm_magisk_prop(const char *name, const char *value) {
if (strstr(name, "magisk")) {
deleteprop(name, 0);
deleteprop2(name, 0);
}
}
@@ -69,42 +67,6 @@ void clean_magisk_props() {
getprop_all(rm_magisk_prop);
}
void relink_sbin() {
struct stat st;
if (stat("/sbin_orig", &st) == -1 && errno == ENOENT) {
// Re-link all binaries and bind mount
DIR *dir;
struct dirent *entry;
char from[PATH_MAX], to[PATH_MAX];
LOGI("hide_utils: Re-linking /sbin\n");
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xrename("/sbin", "/sbin_orig");
xmkdir("/sbin", 0755);
xchmod("/sbin", 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
xmkdir("/dev/sbin_bind", 0755);
xchmod("/dev/sbin_bind", 0755);
dir = xopendir("/sbin_orig");
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, "..") == 0)
continue;
snprintf(from, sizeof(from), "/sbin_orig/%s", entry->d_name);
if (entry->d_type == DT_LNK)
xreadlink(from, from, sizeof(from));
snprintf(to, sizeof(to), "/dev/sbin_bind/%s", entry->d_name);
symlink(from, to);
lsetfilecon(to, "u:object_r:rootfs:s0");
}
closedir(dir);
xmount("/dev/sbin_bind", "/sbin", NULL, MS_BIND, NULL);
}
}
int add_list(char *proc) {
if (!hideEnabled) {
free(proc);
@@ -223,7 +185,6 @@ int destroy_list() {
}
void add_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client);
// ack
write_int(client, add_list(proc));
@@ -231,7 +192,6 @@ void add_hide_list(int client) {
}
void rm_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client);
// ack
write_int(client, rm_list(proc));
@@ -239,7 +199,6 @@ void rm_hide_list(int client) {
}
void ls_hide_list(int client) {
err_handler = do_nothing;
if (!hideEnabled) {
write_int(client, HIDE_NOT_ENABLED);
return;

View File

@@ -41,9 +41,6 @@ static void usage(char *arg0) {
}
void launch_magiskhide(int client) {
// We manually handle crashes
err_handler = do_nothing;
if (hideEnabled) {
if (client > 0) {
write_int(client, HIDE_IS_ENABLED);
@@ -55,7 +52,7 @@ void launch_magiskhide(int client) {
hideEnabled = 1;
LOGI("* Starting MagiskHide\n");
deleteprop(MAGISKHIDE_PROP, 1);
deleteprop2(MAGISKHIDE_PROP, 1);
hide_sensitive_props();
@@ -102,8 +99,8 @@ void stop_magiskhide(int client) {
hideEnabled = 0;
setprop(MAGISKHIDE_PROP, "0");
// Remove without actually removing persist props
deleteprop(MAGISKHIDE_PROP, 0);
pthread_kill(proc_monitor_thread, SIGUSR1);
deleteprop2(MAGISKHIDE_PROP, 0);
pthread_kill(proc_monitor_thread, TERM_THREAD);
write_int(client, DAEMON_SUCCESS);
close(client);
@@ -124,6 +121,8 @@ int magiskhide_main(int argc, char *argv[]) {
req = RM_HIDELIST;
} else if (strcmp(argv[1], "--ls") == 0) {
req = LS_HIDELIST;
} else {
usage(argv[0]);
}
int fd = connect_daemon();
write_int(fd, req);

View File

@@ -3,6 +3,9 @@
#include <pthread.h>
#define TERM_THREAD SIGUSR1
#define HIDE_DONE SIGUSR2
// Kill process
void kill_proc(int pid);
@@ -12,7 +15,6 @@ void proc_monitor();
// Utility functions
void manage_selinux();
void hide_sensitive_props();
void relink_sbin();
void clean_magisk_props();
// List managements

View File

@@ -0,0 +1,267 @@
/* proc_monitor.c - Monitor am_proc_start events and unmount
*
* We monitor the logcat am_proc_start events. When a target starts up,
* we pause it ASAP, and fork a new process to join its mount namespace
* and do all the unmounting/mocking
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include "magisk.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 };
// 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;
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);
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);
else
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
}
static void hide_daemon(int pid, int ppid) {
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
strcpy(argv0, "hide_daemon");
char *line, buffer[PATH_MAX];
struct vector mount_list;
manage_selinux();
clean_magisk_props();
if (switch_mnt_ns(pid))
goto exit;
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
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
vec_for_each(&mount_list, line) {
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
vec_destroy(&mount_list);
// Re-read mount infos
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
vec_init(&mount_list);
file_to_vector(buffer, &mount_list);
// Unmount any loop mounts
vec_for_each(&mount_list, line) {
if (strstr(line, "/dev/block/loop")) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
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);
}
void proc_monitor() {
// Unblock user signals
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
struct sigaction act;
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)) {
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);
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;
}
// 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;
}
}
pthread_mutex_unlock(&hide_lock);
}
}

1
core/jni/magiskpolicy Submodule

Submodule core/jni/magiskpolicy added at 2f4062a923

View File

@@ -0,0 +1,407 @@
/* resetprop.cpp - Manipulate any system props
*
* 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>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include "_system_properties.h"
#include "system_properties.h"
#include "magisk.h"
#include "resetprop.h"
extern "C" {
#include "vector.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;
static int check_legal_property_name(const char *name) {
int namelen = strlen(name);
if (namelen < 1) goto illegal;
if (name[0] == '.') goto illegal;
if (name[namelen - 1] == '.') goto illegal;
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
// i=0 is guaranteed to never have a dot. See above.
if (name[i-1] == '.') goto illegal;
continue;
}
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
goto illegal;
}
return 0;
illegal:
PRINT_E("Illegal property name: [%s]\n", name);
return 1;
}
static int usage(char* arg0) {
fprintf(stderr,
"resetprop v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu & nkk71) - System Props Modification Tool\n\n"
"Usage: %s [flags] [options...]\n"
"\n"
"Options:\n"
" -h, --help show this message\n"
" (no arguments) print all properties\n"
" NAME get property\n"
" NAME VALUE set property entry NAME with VALUE\n"
" --file FILE load props from FILE\n"
" --delete NAME delete property\n"
"\n"
"Flags:\n"
" -v print verbose output to stderr\n"
" -n set properties without init triggers\n"
" only affects setprop\n"
" -p access actual persist storage\n"
" only affects getprop and deleteprop\n"
"\n"
, arg0);
return 1;
}
static int init_resetprop() {
if (__system_properties_init2()) {
PRINT_E("resetprop: Initialize error\n");
return -1;
}
return 0;
}
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);
}
// Get prop by name, return string (should free manually!)
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:
PRINT_D("resetprop: prop [%s] does not exist\n", name);
return NULL;
}
} else {
__system_property_read_callback2(pi, read_prop_info, 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*)) {
if (init_resetprop()) return;
struct wrapper wrap = {
.func = callback
};
__system_property_foreach2(prop_foreach_cb, &wrap);
}
int setprop(const char *name, const char *value) {
return setprop2(name, value, 1);
}
int setprop2(const char *name, const char *value, const int trigger) {
if (check_legal_property_name(name))
return 1;
if (init_resetprop()) return -1;
int ret;
prop_info *pi = (prop_info*) __system_property_find2(name);
if (pi != NULL) {
if (trigger) {
if (strncmp(name, "ro.", 3) == 0) deleteprop(name);
ret = __system_property_set2(name, value);
} else {
ret = __system_property_update2(pi, value, strlen(value));
}
} else {
PRINT_D("resetprop: New prop [%s]\n", name);
if (trigger) {
ret = __system_property_set2(name, value);
} else {
ret = __system_property_add2(name, strlen(name), value, strlen(value));
}
}
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
trigger ? "property_service" : "modifing prop data structure");
if (ret)
PRINT_E("resetprop: setprop error\n");
return ret;
}
int deleteprop(const char *name) {
return deleteprop2(name, 1);
}
int deleteprop2(const char *name, const int persist) {
if (check_legal_property_name(name))
return 1;
if (init_resetprop()) return -1;
char path[PATH_MAX];
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);
}
int read_prop_file(const char* filename, const int trigger) {
if (init_resetprop()) return -1;
PRINT_D("resetprop: Load prop file [%s]\n", filename);
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
PRINT_E("Cannot open [%s]\n", filename);
return 1;
}
char *line = NULL, *pch;
size_t len;
ssize_t read;
int comment = 0, i;
while ((read = getline(&line, &len, fp)) != -1) {
// Remove the trailing newline
if (line[read - 1] == '\n') {
line[read - 1] = '\0';
--read;
}
comment = 0;
for (i = 0; i < read; ++i) {
// Ignore starting spaces
if (line[i] == ' ') continue;
else {
// A line starting with # is ignored
if (line[i] == '#') comment = 1;
break;
}
}
if (comment) continue;
pch = strchr(line, '=');
// Ignore ivalid formats
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
// Separate the string
*pch = '\0';
setprop2(line + i, pch + 1, trigger);
}
free(line);
fclose(fp);
return 0;
}
int resetprop_main(int argc, char *argv[]) {
int trigger = 1, persist = 0;
char *argv0 = argv[0], *prop;
--argc;
++argv;
// Parse flags and -- options
while (argc && argv[0][0] == '-') {
for (int idx = 1; 1; ++idx) {
switch (argv[0][idx]) {
case '-':
if (strcmp(argv[0], "--file") == 0 && argc == 2) {
return read_prop_file(argv[1], trigger);
} else if (strcmp(argv[0], "--delete") == 0 && argc == 2) {
return deleteprop2(argv[1], persist);
} else if (strcmp(argv[0], "--help") == 0) {
goto usage;
}
case 'v':
verbose = 1;
continue;
case 'p':
persist = 1;
continue;
case 'n':
trigger = 0;
continue;
case '\0':
break;
case 'h':
default:
usage:
return usage(argv0);
}
break;
}
--argc;
++argv;
}
switch (argc) {
case 0:
print_all_props(persist);
return 0;
case 1:
prop = getprop2(argv[0], persist);
if (prop == NULL) return 1;
printf("%s\n", prop);
free(prop);
return 0;
case 2:
return setprop2(argv[0], argv[1], trigger);
default:
usage(argv0);
return 1;
}
}

View File

@@ -30,11 +30,13 @@
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <new>
@@ -48,6 +50,9 @@
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#undef XATTR_CREATE
#undef XATTR_REPLACE
#include <sys/xattr.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
@@ -1319,7 +1324,7 @@ int __system_property_get2(const char* name, char* value) {
static constexpr uint32_t kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2; // current
static atomic_uint_least32_t g_propservice_protocol_version = 0;
static uint32_t g_propservice_protocol_version = 0;
static void detect_protocol_version() {
char value[PROP_VALUE_MAX];
@@ -1486,7 +1491,7 @@ int __system_property_add2(const char* name, unsigned int namelen, const char* v
uint32_t __system_property_serial2(const prop_info* pi) {
uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
while (SERIAL_DIRTY(serial)) {
__futex_wait(const_cast<_Atomic(uint_least32_t)*>(&pi->serial), serial, nullptr);
__futex_wait(const_cast<atomic_uint_least32_t*>(&pi->serial), serial, nullptr);
serial = load_const_atomic(&pi->serial, memory_order_acquire);
}
return serial;

View File

@@ -43,7 +43,7 @@ typedef struct prop_info prop_info;
/*
* Sets system property `key` to `value`, creating the system property if it doesn't already exist.
*/
int __system_property_set2(const char* key, const char* value) __INTRODUCED_IN(12);
int __system_property_set2(const char* key, const char* value);
/*
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
@@ -58,7 +58,7 @@ const prop_info* __system_property_find2(const char* name);
*/
void __system_property_read_callback2(const prop_info *pi,
void (*callback)(void* cookie, const char *name, const char *value, uint32_t serial),
void* cookie) __INTRODUCED_IN(26);
void* cookie);
/*
* Passes a `prop_info` for each system property to the provided
@@ -66,8 +66,7 @@ void __system_property_read_callback2(const prop_info *pi,
*
* This method is for inspecting and debugging the property system, and not generally useful.
*/
int __system_property_foreach2(void (*propfn)(const prop_info* pi, void* cookie), void* cookie)
__INTRODUCED_IN(19);
int __system_property_foreach2(void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
/*
* Waits for the specific system property identified by `pi` to be updated
@@ -85,8 +84,7 @@ struct timespec;
bool __system_property_wait2(const prop_info* pi,
uint32_t old_serial,
uint32_t* new_serial_ptr,
const struct timespec* relative_timeout)
__INTRODUCED_IN(26);
const struct timespec* relative_timeout);
/* Deprecated. In Android O and above, there's no limit on property name length. */
#define PROP_NAME_MAX 32

1
core/jni/su Submodule

Submodule core/jni/su added at ed5dd827e9

256
core/jni/utils/cpio.c Normal file
View File

@@ -0,0 +1,256 @@
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "cpio.h"
#include "logging.h"
#include "utils.h"
static uint32_t x8u(char *hex) {
uint32_t val, inpos = 8, outpos;
char pattern[6];
while (*hex == '0') {
hex++;
if (!--inpos) return 0;
}
// Because scanf gratuitously treats %*X differently than printf does.
sprintf(pattern, "%%%dx%%n", inpos);
sscanf(hex, pattern, &val, &outpos);
if (inpos != outpos) LOGE("bad cpio header\n");
return val;
}
void cpio_free(cpio_entry *e) {
if (e) {
free(e->filename);
free(e->data);
free(e);
}
}
int cpio_find(struct vector *v, const char *entry) {
cpio_entry *e;
vec_for_each(v, e) {
if (!e) continue;
if (strcmp(e->filename, entry) == 0)
return _;
}
return -1;
}
int cpio_cmp(const void *a, const void *b) {
return strcmp(((cpio_entry *) a)->filename, ((cpio_entry *) b)->filename);
}
void cpio_vec_insert(struct vector *v, cpio_entry *n) {
int i = cpio_find(v, n->filename);
if (i > 0) {
// Replace, then all is done
cpio_free(vec_entry(v)[i]);
vec_entry(v)[i] = n;
return;
}
vec_push_back(v, n);
}
// Parse cpio file to a vector of cpio_entry
void parse_cpio(struct vector *v, const char *filename) {
int fd = open(filename, O_RDONLY);
if (fd < 0) return;
fprintf(stderr, "Loading cpio: [%s]\n", filename);
cpio_newc_header header;
cpio_entry *f;
while(xxread(fd, &header, 110) != -1) {
f = xcalloc(sizeof(*f), 1);
// f->ino = x8u(header.ino);
f->mode = x8u(header.mode);
f->uid = x8u(header.uid);
f->gid = x8u(header.gid);
// f->nlink = x8u(header.nlink);
// f->mtime = x8u(header.mtime);
f->filesize = x8u(header.filesize);
// f->devmajor = x8u(header.devmajor);
// f->devminor = x8u(header.devminor);
// f->rdevmajor = x8u(header.rdevmajor);
// f->rdevminor = x8u(header.rdevminor);
uint32_t namesize = x8u(header.namesize);
// f->check = x8u(header.check);
f->filename = xmalloc(namesize);
xxread(fd, f->filename, namesize);
file_align(fd, 4, 0);
if (strcmp(f->filename, ".") == 0 || strcmp(f->filename, "..") == 0) {
cpio_free(f);
continue;
}
if (strcmp(f->filename, "TRAILER!!!") == 0) {
cpio_free(f);
break;
}
if (f->filesize) {
f->data = xmalloc(f->filesize);
xxread(fd, f->data, f->filesize);
file_align(fd, 4, 0);
}
vec_push_back(v, f);
}
close(fd);
}
void dump_cpio(struct vector *v, const char *filename) {
fprintf(stderr, "Dump cpio: [%s]\n", filename);
unsigned inode = 300000;
char header[111];
// Sort by name
vec_sort(v, cpio_cmp);
cpio_entry *e;
int fd = creat(filename, 0644);
vec_for_each(v, e) {
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
inode++, // e->ino
e->mode,
e->uid,
e->gid,
1, // e->nlink
0, // e->mtime
e->filesize,
0, // e->devmajor
0, // e->devminor
0, // e->rdevmajor
0, // e->rdevminor
(uint32_t) strlen(e->filename) + 1,
0 // e->check
);
xwrite(fd, header, 110);
xwrite(fd, e->filename, strlen(e->filename) + 1);
file_align(fd, 4, 1);
if (e->filesize) {
xwrite(fd, e->data, e->filesize);
file_align(fd, 4, 1);
}
}
// Write trailer
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", inode++, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
xwrite(fd, header, 110);
xwrite(fd, "TRAILER!!!\0", 11);
file_align(fd, 4, 1);
close(fd);
}
void cpio_vec_destroy(struct vector *v) {
// Free each cpio_entry
cpio_entry *e;
vec_for_each(v, e)
cpio_free(e);
vec_destroy(v);
}
void cpio_rm(struct vector *v, int recursive, const char *entry) {
cpio_entry *e;
size_t len = strlen(entry);
vec_for_each(v, e) {
if (!e) continue;
if (strncmp(e->filename, entry, len) == 0) {
if ((recursive && e->filename[len] == '/') || e->filename[len] == '\0') {
fprintf(stderr, "Remove [%s]\n", e->filename);
cpio_free(e);
vec_cur(v) = NULL;
if (!recursive) return;
}
}
}
}
void cpio_mkdir(struct vector *v, mode_t mode, const char *entry) {
cpio_entry *e = xcalloc(sizeof(*e), 1);
e->mode = S_IFDIR | mode;
e->filename = strdup(entry);
cpio_vec_insert(v, e);
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
}
void cpio_ln(struct vector *v, const char *target, const char *entry) {
cpio_entry *e = xcalloc(sizeof(*e), 1);
e->mode = S_IFLNK;
e->filename = strdup(entry);
e->filesize = strlen(target);
e->data = strdup(target);
cpio_vec_insert(v, e);
fprintf(stderr, "Create symlink [%s] -> [%s]\n", entry, target);
}
void cpio_add(struct vector *v, mode_t mode, const char *entry, const char *filename) {
int fd = xopen(filename, O_RDONLY);
cpio_entry *e = xcalloc(sizeof(*e), 1);
e->mode = S_IFREG | mode;
e->filename = strdup(entry);
e->filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
e->data = xmalloc(e->filesize);
xxread(fd, e->data, e->filesize);
close(fd);
cpio_vec_insert(v, e);
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
}
int cpio_mv(struct vector *v, const char *from, const char *to) {
struct cpio_entry *e;
int f = cpio_find(v, from), t = cpio_find(v, to);
if (f > 0) {
if (t > 0) {
cpio_free(vec_entry(v)[t]);
vec_entry(v)[t] = NULL;
}
e = vec_entry(v)[f];
free(e->filename);
e->filename = strdup(to);
return 0;
}
fprintf(stderr, "Cannot find entry %s\n", from);
return 1;
}
int cpio_extract(struct vector *v, const char *entry, const char *filename) {
int i = cpio_find(v, entry);
if (i > 0) {
cpio_entry *e = vec_entry(v)[i];
fprintf(stderr, "Extracting [%s] to [%s]\n", entry, filename);
if (S_ISREG(e->mode)) {
int fd = creat(filename, e->mode & 0777);
xwrite(fd, e->data, e->filesize);
fchown(fd, e->uid, e->gid);
close(fd);
} else if (S_ISLNK(e->mode)) {
char *target = xcalloc(e->filesize + 1, 1);
memcpy(target, e->data, e->filesize);
unlink(filename);
symlink(target, filename);
}
return 0;
}
fprintf(stderr, "Cannot find the file entry [%s]\n", entry);
return 1;
}
void cpio_extract_all(struct vector *v) {
cpio_entry *e;
vec_for_each(v, e) {
if (!e) continue;
fprintf(stderr, "Extracting [%s]\n", e->filename);
unlink(e->filename);
rmdir(e->filename);
if (S_ISDIR(e->mode)) {
mkdir(e->filename, e->mode & 0777);
} else if (S_ISREG(e->mode)) {
int fd = creat(e->filename, e->mode & 0777);
xwrite(fd, e->data, e->filesize);
fchown(fd, e->uid, e->gid);
close(fd);
} else if (S_ISLNK(e->mode)) {
char *target = xcalloc(e->filesize + 1, 1);
memcpy(target, e->data, e->filesize);
symlink(target, e->filename);
}
}
}

447
core/jni/utils/file.c Normal file
View File

@@ -0,0 +1,447 @@
/* file.c - Contains all files related utilities
*/
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <sys/sendfile.h>
#include <sys/mman.h>
#include <linux/fs.h>
#ifdef SELINUX
#include <selinux/selinux.h>
#endif
#include "utils.h"
char **excl_list = NULL;
static int is_excl(const char *name) {
if (excl_list)
for (int i = 0; excl_list[i]; ++i)
if (strcmp(name, excl_list[i]) == 0)
return 1;
return 0;
}
int fd_getpath(int fd, char *path, size_t size) {
snprintf(path, size, "/proc/self/fd/%d", fd);
if (xreadlink(path, path, size) == -1)
return -1;
return 0;
}
int mkdir_p(const char *pathname, mode_t mode) {
char *path = strdup(pathname), *p;
errno = 0;
for (p = path + 1; *p; ++p) {
if (*p == '/') {
*p = '\0';
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
*p = '/';
}
}
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
free(path);
return 0;
}
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*)) {
struct dirent *entry;
int newfd;
DIR *dir = fdopendir(dirfd);
if (dir == NULL) return;
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (is_excl(entry->d_name))
continue;
if (entry->d_type == DT_DIR) {
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
in_order_walk(newfd, callback);
close(newfd);
}
callback(dirfd, entry);
}
}
static void rm_cb(int dirfd, struct dirent *entry) {
switch (entry->d_type) {
case DT_DIR:
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
break;
default:
unlinkat(dirfd, entry->d_name, 0);
break;
}
}
void rm_rf(const char *path) {
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (fd >= 0) {
frm_rf(fd);
close(fd);
}
remove(path);
}
void frm_rf(int dirfd) {
in_order_walk(dirfd, rm_cb);
}
/* This will only on the same file system */
void mv_f(const char *source, const char *destination) {
struct stat st;
xlstat(source, &st);
int src, dest;
struct file_attr a;
if (S_ISDIR(st.st_mode)) {
xmkdir_p(destination, st.st_mode & 0777);
src = xopen(source, O_RDONLY | O_CLOEXEC);
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
fclone_attr(src, dest);
mv_dir(src, dest);
close(src);
close(dest);
} else{
getattr(source, &a);
xrename(source, destination);
setattr(destination, &a);
}
rmdir(source);
}
/* This will only on the same file system */
void mv_dir(int src, int dest) {
struct dirent *entry;
DIR *dir;
int newsrc, newdest;
struct file_attr a;
dir = xfdopendir(src);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (is_excl(entry->d_name))
continue;
getattrat(src, entry->d_name, &a);
switch (entry->d_type) {
case DT_DIR:
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
fsetattr(newdest, &a);
mv_dir(newsrc, newdest);
close(newsrc);
close(newdest);
unlinkat(src, entry->d_name, AT_REMOVEDIR);
break;
case DT_LNK:
case DT_REG:
renameat(src, entry->d_name, dest, entry->d_name);
setattrat(dest, entry->d_name, &a);
break;
}
}
}
void cp_afc(const char *source, const char *destination) {
int src, dest;
struct file_attr a;
getattr(source, &a);
if (S_ISDIR(a.st.st_mode)) {
xmkdir_p(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);
} else{
unlink(destination);
if (S_ISREG(a.st.st_mode)) {
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);
}
}
}
void clone_dir(int src, int dest) {
struct dirent *entry;
DIR *dir;
int srcfd, destfd, newsrc, newdest;
char buf[PATH_MAX];
struct file_attr a;
dir = xfdopendir(src);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (is_excl(entry->d_name))
continue;
getattrat(src, entry->d_name, &a);
switch (entry->d_type) {
case DT_DIR:
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
setattrat(dest, entry->d_name, &a);
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
clone_dir(newsrc, newdest);
close(newsrc);
close(newdest);
break;
case DT_REG:
destfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
srcfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
xsendfile(destfd, srcfd, 0, a.st.st_size);
fsetattr(destfd, &a);
close(destfd);
close(srcfd);
break;
case DT_LNK:
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
symlinkat(buf, dest, entry->d_name);
setattrat(dest, entry->d_name, &a);
break;
}
}
}
int getattr(const char *path, struct file_attr *a) {
if (xlstat(path, &a->st) == -1)
return -1;
#ifdef SELINUX
char *con = "";
if (lgetfilecon(path, &con) == -1)
return -1;
strcpy(a->con, con);
freecon(con);
#else
a->con[0] = '\0';
#endif
return 0;
}
int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0)
return -1;
int ret = fgetattr(fd, a);
close(fd);
return ret;
}
int fgetattr(int fd, struct file_attr *a) {
#ifdef SELINUX
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return getattr(path, a);
#else
if (fstat(fd, &a->st) == -1)
return -1;
a->con[0] = '\0';
return 0;
#endif
}
int setattr(const char *path, struct file_attr *a) {
if (chmod(path, a->st.st_mode & 0777) < 0)
return -1;
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
return -1;
#ifdef SELINUX
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
return -1;
#endif
return 0;
}
int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0)
return -1;
int ret = fsetattr(fd, a);
close(fd);
return ret;
}
int fsetattr(int fd, struct file_attr *a) {
#ifdef SELINUX
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return setattr(path, a);
#else
if (fchmod(fd, a->st.st_mode & 0777) < 0)
return -1;
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
return -1;
return 0;
#endif
}
void clone_attr(const char *source, const char *target) {
struct file_attr a;
getattr(source, &a);
setattr(target, &a);
}
void fclone_attr(const int sourcefd, const int targetfd) {
struct file_attr a;
fgetattr(sourcefd, &a);
fsetattr(targetfd, &a);
}
#ifdef SELINUX
#define UNLABEL_CON "u:object_r:unlabeled:s0"
#define SYSTEM_CON "u:object_r:system_file:s0"
void restorecon(int dirfd, int force) {
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)
lsetfilecon(path, SYSTEM_CON);
freecon(con);
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) {
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restorecon(fd, force);
} else {
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
fd_getpath(fd, path, sizeof(path));
lgetfilecon(path, &con);
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON);
freecon(con);
}
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);
fstat(fd, &st);
if (S_ISBLK(st.st_mode))
ioctl(fd, BLKGETSIZE64, size);
else
*size = st.st_size;
*buf = *size > 0 ? xmmap(NULL, *size, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, fd, 0) : NULL;
close(fd);
return S_ISBLK(st.st_mode);
}
int mmap_ro(const char *filename, void **buf, size_t *size) {
return _mmap(0, filename, buf, size);
}
int mmap_rw(const char *filename, void **buf, size_t *size) {
return _mmap(1, filename, buf, size);
}
void fd_full_read(int fd, void **buf, size_t *size) {
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
*buf = xmalloc(*size);
xxread(fd, *buf, *size);
}
void full_read(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDONLY);
if (fd < 0) {
*buf = NULL;
*size = 0;
return;
}
fd_full_read(fd, buf, size);
close(fd);
}
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
int fd = xopenat(dirfd, filename, O_RDONLY);
if (fd < 0) {
*buf = NULL;
*size = 0;
return;
}
fd_full_read(fd, buf, size);
close(fd);
}
void stream_full_read(int fd, void **buf, size_t *size) {
size_t cap = 1 << 20;
uint8_t tmp[1 << 20];
*buf = xmalloc(cap);
ssize_t read;
*size = 0;
while (1) {
read = xread(fd, tmp, sizeof(tmp));
if (read <= 0)
break;
if (*size + read > cap) {
cap *= 2;
*buf = realloc(*buf, cap);
}
memcpy(*buf + *size, tmp, read);
*size += read;
}
}
void write_zero(int fd, size_t size) {
size_t pos = lseek(fd, 0, SEEK_CUR);
ftruncate(fd, pos + size);
lseek(fd, pos + size, SEEK_SET);
}
void mem_align(size_t *pos, size_t align) {
size_t mask = align - 1;
if (*pos & mask) {
*pos += align - (*pos & mask);
}
}
void file_align(int fd, size_t align, int out) {
size_t pos = lseek(fd, 0, SEEK_CUR);
size_t mask = align - 1;
size_t off;
if (pos & mask) {
off = align - (pos & mask);
if (out) {
write_zero(fd, off);
} else {
lseek(fd, pos + off, SEEK_SET);
}
}
}

View File

@@ -2,6 +2,7 @@
*/
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
@@ -49,22 +50,17 @@ static char *loopsetup(const char *img) {
}
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);
// Create a temp file with the file contexts
char file_contexts[] = "/magisk(/.*)? u:object_r:system_file:s0\n";
// If not root, attempt to create in current diretory
char *filename = getuid() == UID_ROOT ? "/dev/file_contexts_image" : "file_contexts_image";
int ret, fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
xwrite(fd, file_contexts, sizeof(file_contexts));
close(fd);
int ret;
char buffer[16];
snprintf(buffer, sizeof(buffer), "%dM", size);
ret = exec_command_sync("make_ext4fs", "-l", buffer, "-a", "/magisk", "-S", filename, img, NULL);
ret = exec_command_sync("make_ext4fs", "-l", buffer, img, NULL);
if (ret < 0)
return 1;
unlink(filename);
return ret;
}
@@ -103,7 +99,7 @@ int resize_img(const char *img, int size) {
if (e2fsck(img))
return 1;
char buffer[128];
int pid, status, fd = -1;
int pid, fd = -1, used, total;
snprintf(buffer, sizeof(buffer), "%dM", size);
pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL);
if (pid < 0)
@@ -111,17 +107,41 @@ int resize_img(const char *img, int size) {
while (fdgets(buffer, sizeof(buffer), fd))
LOGD("magisk_img: %s", buffer);
close(fd);
waitpid(pid, &status, 0);
return WEXITSTATUS(status);
waitpid(pid, NULL, 0);
// Double check our image size
get_img_size(img, &used, &total);
if (total != size) {
// Sammy crap occurs or resize2fs failed, lets create a new image!
char *dir = dirname(img);
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
create_img(buffer, size);
char *s_loop, *t_loop;
s_loop = mount_image(img, SOURCE_TMP);
if (s_loop == NULL) return 1;
t_loop = mount_image(buffer, TARGET_TMP);
if (t_loop == NULL) return 1;
cp_afc(SOURCE_TMP, TARGET_TMP);
umount_image(SOURCE_TMP, s_loop);
umount_image(TARGET_TMP, t_loop);
rmdir(SOURCE_TMP);
rmdir(TARGET_TMP);
free(s_loop);
free(t_loop);
rename(buffer, img);
}
return 0;
}
char *mount_image(const char *img, const char *target) {
if (access(img, F_OK) == -1)
return NULL;
if (access(target, F_OK) == -1) {
if (xmkdir(target, 0755) == -1) {
if (xmkdir_p(target, 0755) == -1) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdir(target, 0755);
xmkdir_p(target, 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
}
@@ -145,8 +165,9 @@ void umount_image(const char *target, const char *device) {
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) {
rename(source, target);
xrename(source, target);
return 0;
}
@@ -157,7 +178,7 @@ int merge_img(const char *source, const char *target) {
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)
if (n_total > t_total)
resize_img(target, n_total);
xmkdir(SOURCE_TMP, 0755);
@@ -170,7 +191,7 @@ int merge_img(const char *source, const char *target) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(SOURCE_TMP)))
if (!(dir = xopendir(SOURCE_TMP)))
return 1;
while ((entry = xreaddir(dir))) {
if (entry->d_type == DT_DIR) {
@@ -183,7 +204,7 @@ int merge_img(const char *source, const char *target) {
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);
exec_command_sync(BBPATH "/rm", "-rf", buffer, NULL);
rm_rf(buffer);
} else {
LOGI("New module: %s\n", entry->d_name);
}

View File

@@ -1,9 +1,6 @@
/* list.h - Double link list implementation
*/
#include <stdlib.h>
#include <string.h>
#include "list.h"
void init_list_head(struct list_head *head) {

View File

@@ -6,24 +6,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <errno.h>
#include <sched.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <selinux/selinux.h>
#include <sys/inotify.h>
#include "magisk.h"
#include "logging.h"
#include "utils.h"
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
#include "resetprop.h"
unsigned get_shell_uid() {
struct passwd* ppwd = getpwnam("shell");
@@ -50,18 +47,22 @@ unsigned get_radio_uid() {
}
int check_data() {
int ret = 0;
char buffer[4096];
FILE *fp = xfopen("/proc/mounts", "r");
while (fgets(buffer, sizeof(buffer), fp)) {
if (strstr(buffer, " /data ")) {
if (strstr(buffer, "tmpfs") == NULL)
ret = 1;
struct vector v;
vec_init(&v);
file_to_vector("/proc/mounts", &v);
char *line, *crypto;
int mnt = 0;
vec_for_each(&v, line) {
if (strstr(line, " /data ")) {
if (strstr(line, "tmpfs") == NULL)
mnt = 1;
break;
}
}
fclose(fp);
return ret;
vec_deep_destroy(&v);
// /data is mounted and not tmpfs and data is unencrypted or vold is started
return mnt && (((crypto = getprop("ro.crypto.state")) && strcmp(crypto, "unencrypted") == 0)
|| getprop("init.svc.vold"));
}
/* All the string should be freed manually!! */
@@ -145,12 +146,12 @@ static void proc_name_filter(int pid) {
char buf[64];
int fd;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
if ((fd = open(buf, O_RDONLY)) == -1)
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return;
if (fdgets(buf, sizeof(buf), fd) == 0) {
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
close(fd);
if ((fd = open(buf, O_RDONLY)) == -1)
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return;
fdgets(buf, sizeof(buf), fd);
}
@@ -167,48 +168,25 @@ void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
ps(proc_name_filter);
}
int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX];
if (bin == NULL) {
xreadlink("/proc/self/exe", self, sizeof(self));
bin = self;
}
int ret = 0;
for (int i = 0; applet[i]; ++i) {
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
unlink(linkpath);
ret |= symlink(bin, linkpath);
}
return ret;
}
#define DEV_BLOCK "/dev/block"
void unlock_blocks() {
char path[PATH_MAX];
DIR *dir;
struct dirent *entry;
int fd, OFF = 0;
int fd, dev, OFF = 0;
if (!(dir = xopendir(DEV_BLOCK)))
if ((dev = xopen("/dev/block", O_RDONLY | O_CLOEXEC)) < 0)
return;
dir = xfdopendir(dev);
while((entry = readdir(dir))) {
if (entry->d_type == DT_BLK &&
strstr(entry->d_name, "ram") == NULL &&
strstr(entry->d_name, "loop") == NULL &&
strstr(entry->d_name, "dm-0") == NULL) {
snprintf(path, sizeof(path), "%s/%s", DEV_BLOCK, entry->d_name);
if ((fd = xopen(path, O_RDONLY)) < 0)
if (entry->d_type == DT_BLK) {
if ((fd = openat(dev, entry->d_name, O_RDONLY)) < 0)
continue;
if (ioctl(fd, BLKROSET, &OFF) == -1)
PLOGE("unlock %s", path);
PLOGE("unlock %s", entry->d_name);
close(fd);
}
}
closedir(dir);
close(dev);
}
void setup_sighandlers(void (*handler)(int)) {
@@ -259,7 +237,7 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
envp = environ;
}
int pid = fork();
int pid = xfork();
if (pid != 0) {
if (fd && *fd < 0) {
// Give the read end and close write end
@@ -271,9 +249,6 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
return pid;
}
// Don't return to the daemon if anything goes wrong
err_handler = exit_proc;
if (fd) {
xdup2(writeEnd, STDOUT_FILENO);
if (err) xdup2(writeEnd, STDERR_FILENO);
@@ -304,114 +279,16 @@ int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char
return pid;
}
int mkdir_p(const char *pathname, mode_t mode) {
char *path = strdup(pathname), *p;
errno = 0;
for (p = path + 1; *p; ++p) {
if (*p == '/') {
*p = '\0';
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
*p = '/';
}
}
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
free(path);
return 0;
}
int bind_mount(const char *from, const char *to) {
int ret = xmount(from, to, NULL, MS_BIND, NULL);
#ifdef MAGISK_DEBUG
LOGD("bind_mount: %s -> %s\n", from, to);
LOGI("bind_mount: %s <- %s\n", to, from);
#else
LOGI("bind_mount: %s\n", to);
#endif
return ret;
}
int open_new(const char *filename) {
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
int cp_afc(const char *source, const char *target) {
struct stat buf;
xlstat(source, &buf);
if (S_ISDIR(buf.st_mode)) {
DIR *dir;
struct dirent *entry;
char *s_path, *t_path;
if (!(dir = xopendir(source)))
return 1;
s_path = xmalloc(PATH_MAX);
t_path = xmalloc(PATH_MAX);
mkdir_p(target, 0755);
clone_attr(source, target);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name);
snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name);
cp_afc(s_path, t_path);
}
free(s_path);
free(t_path);
closedir(dir);
} else{
unlink(target);
if (S_ISREG(buf.st_mode)) {
int sfd, tfd;
sfd = xopen(source, O_RDONLY);
tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC);
xsendfile(tfd, sfd, NULL, buf.st_size);
fclone_attr(sfd, tfd);
close(sfd);
close(tfd);
} else if (S_ISLNK(buf.st_mode)) {
char buffer[PATH_MAX];
xreadlink(source, buffer, sizeof(buffer));
xsymlink(buffer, target);
clone_attr(source, target);
} else {
return 1;
}
}
return 0;
}
void clone_attr(const char *source, const char *target) {
struct stat buf;
lstat(target, &buf);
chmod(target, buf.st_mode & 0777);
chown(target, buf.st_uid, buf.st_gid);
char *con;
lgetfilecon(source, &con);
lsetfilecon(target, con);
free(con);
}
void fclone_attr(const int sourcefd, const int targetfd) {
struct stat buf;
fstat(sourcefd, &buf);
fchmod(targetfd, buf.st_mode & 0777);
fchown(targetfd, buf.st_uid, buf.st_gid);
char *con;
fgetfilecon(sourcefd, &con);
fsetfilecon(targetfd, con);
free(con);
}
void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred);
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
@@ -431,3 +308,31 @@ int switch_mnt_ns(int pid) {
close(fd);
return ret;
}
int fork_dont_care() {
int pid = xfork();
if (pid) {
waitpid(pid, NULL, 0);
return pid;
} else if ((pid = xfork())) {
exit(0);
}
return 0;
}
void wait_till_exists(const char *target) {
if (access(target, F_OK) == 0)
return;
int fd = inotify_init();
char *dir = dirname(target);
char crap[PATH_MAX];
inotify_add_watch(fd, dir, IN_CREATE);
while (1) {
struct inotify_event event;
read(fd, &event, sizeof(event));
read(fd, crap, event.len);
if (access(target, F_OK) == 0)
break;
}
close(fd);
}

99
core/jni/utils/pattern.c Normal file
View File

@@ -0,0 +1,99 @@
#include <malloc.h>
#include <string.h>
#include "utils.h"
static int check_verity_pattern(const char *s) {
int skip = 0;
if (s[0] == ',') ++skip;
if (strncmp(s + skip, "verify", 6) == 0)
skip += 6;
else if (strncmp(s + skip, "avb", 3) == 0)
skip += 3;
else
return -1;
if (s[skip] == '=') {
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') ++skip;
}
return skip;
}
static int check_encryption_pattern(const char *s) {
const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe", NULL };
for (int i = 0 ; encrypt_list[i]; ++i) {
int len = strlen(encrypt_list[i]);
if (strncmp(s, encrypt_list[i], len) == 0)
return len;
}
return -1;
}
void patch_init_rc(void **buf, size_t *size) {
int injected = 0;
char *new_data = malloc(*size + 23);
char *old_data = *buf;
size_t pos = 0;
for (char *tok = strsep(&old_data, "\n"); tok; tok = strsep(&old_data, "\n")) {
if (!injected && strncmp(tok, "import", 6) == 0) {
if (strstr(tok, "init.magisk.rc")) {
injected = 1;
} else {
strcpy(new_data + pos, "import /init.magisk.rc\n");
pos += 23;
injected = 1;
}
} else if (strstr(tok, "selinux.reload_policy")) {
continue;
}
// Copy the line
strcpy(new_data + pos, tok);
pos += strlen(tok);
new_data[pos++] = '\n';
}
free(*buf);
*size = pos;
*buf = new_data;
}
int patch_verity(void **buf, uint32_t *size, int patch) {
int skip, src_size = *size;
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);
read += skip;
*size -= skip;
}
if (patch)
patched[write] = src[read];
}
if (patch) {
free(*buf);
*buf = patched;
}
return 0;
}
void patch_encryption(void **buf, uint32_t *size) {
int skip, src_size = *size;
char *src = *buf, *patched = xcalloc(src_size, 1);
for (int read = 0, write = 0; read < src_size; ++read, ++write) {
if ((skip = check_encryption_pattern(src + read)) > 0) {
fprintf(stderr, "Replace pattern [%.*s] with [encryptable]\n", skip, src + read);
memcpy(patched + read, "encryptable", 11);
read += skip;
write += 11;
*size -= (skip - 11);
}
patched[write] = src[read];
}
free(*buf);
*buf = patched;
}

View File

@@ -29,9 +29,25 @@ void *vec_pop_back(struct vector *v) {
return ret;
}
static int (*cmp)(const void *, const void *);
static int vec_comp(const void *a, const void *b) {
void *aa = *((void **)a), *bb = *((void **)b);
if (aa == NULL && bb == NULL) return 0;
else if (aa == NULL) return 1;
else if (bb == NULL) return -1;
else return cmp ? cmp(aa, bb) : 0;
}
void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) {
if (v == NULL) return;
qsort(vec_entry(v), vec_size(v), sizeof(void*), compar);
cmp = compar;
qsort(vec_entry(v), vec_size(v), sizeof(void*), vec_comp);
void *e;
vec_for_each_r(v, e) {
if (e) break;
--vec_size(v);
}
}
/* Will cleanup only the vector itself

View File

@@ -1,8 +1,8 @@
/* xwrap.c - wrappers around existing library functions.
*
* Functions with the x prefix are wrappers that either succeed or kill the
* program with an error message, but never return failure. They usually have
* the same arguments and return value as the function they wrap.
* Functions with the x prefix are wrappers that either succeed or log the
* error message. They usually have the same arguments and return value
* as the function they wrap.
*
*/
@@ -14,6 +14,7 @@
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -21,7 +22,7 @@
#include <sys/mman.h>
#include <sys/sendfile.h>
#include "magisk.h"
#include "logging.h"
#include "utils.h"
FILE *xfopen(const char *pathname, const char *mode) {
@@ -56,6 +57,14 @@ int xopen3(const char *pathname, int flags, mode_t mode) {
return fd;
}
int xopenat(int dirfd, const char *pathname, int flags) {
int fd = openat(dirfd, pathname, flags);
if (fd < 0) {
PLOGE("openat: %s", pathname);
}
return fd;
}
ssize_t xwrite(int fd, const void *buf, size_t count) {
int ret = write(fd, buf, count);
if (count != ret) {
@@ -106,6 +115,14 @@ DIR *xopendir(const char *name) {
return d;
}
DIR *xfdopendir(int fd) {
DIR *d = fdopendir(fd);
if (d == NULL) {
PLOGE("fdopendir");
}
return d;
}
struct dirent *xreaddir(DIR *dirp) {
errno = 0;
struct dirent *e = readdir(dirp);
@@ -142,7 +159,7 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int ret = connect(sockfd, addr, addrlen);
if (ret == -1) {
PLOGE("bind");
PLOGE("connect");
}
return ret;
}
@@ -250,7 +267,16 @@ ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
PLOGE("readlink %s", pathname);
} else {
buf[ret] = '\0';
++ret;
}
return ret;
}
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
if (ret == -1) {
PLOGE("readlinkat %s", pathname);
} else {
buf[ret] = '\0';
}
return ret;
}
@@ -289,14 +315,6 @@ int xumount2(const char *target, int flags) {
return ret;
}
int xchmod(const char *pathname, mode_t mode) {
int ret = chmod(pathname, mode);
if (ret == -1) {
PLOGE("chmod %s %u", pathname, mode);
}
return ret;
}
int xrename(const char *oldpath, const char *newpath) {
int ret = rename(oldpath, newpath);
if (ret == -1) {
@@ -313,6 +331,22 @@ int xmkdir(const char *pathname, mode_t mode) {
return ret;
}
int xmkdir_p(const char *pathname, mode_t mode) {
int ret = mkdir_p(pathname, mode);
if (ret == -1) {
PLOGE("mkdir_p %s", pathname);
}
return ret;
}
int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
int ret = mkdirat(dirfd, pathname, mode);
if (ret == -1 && errno != EEXIST) {
PLOGE("mkdirat %s %u", pathname, mode);
}
return ret;
}
void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset) {
void *ret = mmap(addr, length, prot, flags, fd, offset);
@@ -330,10 +364,10 @@ ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
return ret;
}
int xmkdir_p(const char *pathname, mode_t mode) {
int ret = mkdir_p(pathname, mode);
pid_t xfork() {
int ret = fork();
if (ret == -1) {
PLOGE("mkdir_p %s", pathname);
PLOGE("fork");
}
return ret;
}

View File

@@ -0,0 +1 @@
<manifest package="com.topjohnwu.core.magisk" />

1
crypto/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

38
crypto/build.gradle Normal file
View File

@@ -0,0 +1,38 @@
apply plugin: 'java-library'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'java'
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
jar {
manifest {
attributes 'Main-Class': 'com.topjohnwu.crypto.ZipSigner'
}
}
shadowJar {
baseName = 'zipsigner'
classifier = null
version = 1.1
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
}
}
repositories {
jcenter()
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.bouncycastle:bcprov-jdk15on:1.58'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.58'
}

View File

@@ -0,0 +1,34 @@
package com.topjohnwu.crypto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ByteArrayStream extends ByteArrayOutputStream {
public byte[] getBuf() {
return buf;
}
public synchronized void readFrom(InputStream is) {
readFrom(is, Integer.MAX_VALUE);
}
public synchronized void readFrom(InputStream is, int len) {
int read;
byte buffer[] = new byte[4096];
try {
while ((read = is.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
write(buffer, 0, read);
len -= read;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void writeTo(OutputStream out, int off, int len) throws IOException {
out.write(buf, off, len);
}
public ByteArrayInputStream getInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
}

View File

@@ -0,0 +1,136 @@
package com.topjohnwu.crypto;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class CryptoUtils {
private static final Map<String, String> ID_TO_ALG;
private static final Map<String, String> ALG_TO_ID;
static {
ID_TO_ALG = new HashMap<>();
ALG_TO_ID = new HashMap<>();
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA");
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA");
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA");
ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId());
ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId());
ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId());
ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId());
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
}
private static String getSignatureAlgorithm(Key key) throws Exception {
if ("EC".equals(key.getAlgorithm())) {
int curveSize;
KeyFactory factory = KeyFactory.getInstance("EC");
if (key instanceof PublicKey) {
ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class);
curveSize = spec.getParams().getCurve().getField().getFieldSize();
} else if (key instanceof PrivateKey) {
ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class);
curveSize = spec.getParams().getCurve().getField().getFieldSize();
} else {
throw new InvalidKeySpecException();
}
if (curveSize <= 256) {
return "SHA256withECDSA";
} else if (curveSize <= 384) {
return "SHA384withECDSA";
} else {
return "SHA512withECDSA";
}
} else if ("RSA".equals(key.getAlgorithm())) {
return "SHA256withRSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
}
}
static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception {
String id = ALG_TO_ID.get(getSignatureAlgorithm(key));
if (id == null) {
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
}
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
}
static boolean verify(PublicKey key, byte[] input, byte[] signature,
AlgorithmIdentifier algId) throws Exception {
String algName = ID_TO_ALG.get(algId.getAlgorithm().getId());
if (algName == null) {
throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm());
}
Signature verifier = Signature.getInstance(algName);
verifier.initVerify(key);
verifier.update(input);
return verifier.verify(signature);
}
static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey));
signer.initSign(privateKey);
signer.update(input);
return signer.sign();
}
static X509Certificate readPublicKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(input);
} finally {
input.close();
}
}
/** Read a PKCS#8 format private key. */
static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
byte[] buffer = new byte[4096];
int size = input.read(buffer);
byte[] bytes = Arrays.copyOf(buffer, size);
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
/*
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
* OID and use that to construct a KeyFactory.
*/
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
return KeyFactory.getInstance(algOid).generatePrivate(spec);
} finally {
input.close();
}
}
}

View File

@@ -0,0 +1,122 @@
package com.topjohnwu.crypto;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/*
* A universal random access interface for both JarFile and JarInputStream
*
* In the case when JarInputStream is provided to constructor, the whole stream
* will be loaded into memory for random access purposes.
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
* */
public class JarMap implements Closeable, AutoCloseable {
private JarFile jarFile;
private JarInputStream jis;
private boolean isInputStream = false;
private LinkedHashMap<String, JarEntry> bufMap;
public JarMap(File file) throws IOException {
this(file, true);
}
public JarMap(File file, boolean verify) throws IOException {
this(file, verify, ZipFile.OPEN_READ);
}
public JarMap(File file, boolean verify, int mode) throws IOException {
jarFile = new JarFile(file, verify, mode);
}
public JarMap(String name) throws IOException {
this(new File(name));
}
public JarMap(String name, boolean verify) throws IOException {
this(new File(name), verify);
}
public JarMap(InputStream is) throws IOException {
this(is, true);
}
public JarMap(InputStream is, boolean verify) throws IOException {
isInputStream = true;
bufMap = new LinkedHashMap<>();
jis = new JarInputStream(is, verify);
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
}
}
public File getFile() {
return isInputStream ? null : new File(jarFile.getName());
}
public Manifest getManifest() throws IOException {
return isInputStream ? jis.getManifest() : jarFile.getManifest();
}
public InputStream getInputStream(ZipEntry ze) throws IOException {
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() :
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;
}
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();
}
}
public Enumeration<JarEntry> entries() {
return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries();
}
public ZipEntry getEntry(String name) {
return getJarEntry(name);
}
public JarEntry getJarEntry(String name) {
return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name);
}
@Override
public void close() throws IOException {
(isInputStream ? jis : jarFile).close();
}
private static class JarMapEntry extends JarEntry {
ByteArrayStream data;
JarMapEntry(JarEntry je, InputStream is) {
super(je);
data = new ByteArrayStream();
data.readFrom(is);
}
}
}

View File

@@ -1,10 +1,9 @@
package com.topjohnwu;
package com.topjohnwu.crypto;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
@@ -19,29 +18,26 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import java.io.ByteArrayInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
@@ -58,16 +54,56 @@ import java.util.regex.Pattern;
* Modified from from AOSP(Marshmallow) SignAPK.java
* */
public class ZipSigner {
public class SignAPK {
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
private static Provider sBouncyCastleProvider;
public static Provider sBouncyCastleProvider;
// bitmasks for which hash algorithms we need the manifest to include.
private static final int USE_SHA1 = 1;
private static final int USE_SHA256 = 2;
static {
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
}
public static void signZip(InputStream publicIn, InputStream privateIn,
JarMap input, File output, boolean minSign) throws Exception {
int alignment = 4;
BufferedOutputStream outputFile;
int hashes = 0;
X509Certificate publicKey = CryptoUtils.readPublicKey(publicIn);
hashes |= getDigestAlgorithm(publicKey);
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
PrivateKey privateKey = CryptoUtils.readPrivateKey(privateIn);
outputFile = new BufferedOutputStream(new FileOutputStream(output));
if (minSign) {
signWholeFile(input.getFile(), publicKey, privateKey, outputFile);
} else {
JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(input, hashes);
copyFiles(manifest, input, outputJar, timestamp, alignment);
signFile(manifest, input, publicKey, privateKey, outputJar);
outputJar.close();
}
input.close();
outputFile.close();
}
/**
* Return one of USE_SHA1 or USE_SHA256 according to the signature
* algorithm specified in the cert.
@@ -104,42 +140,12 @@ public class ZipSigner {
private static Pattern stripPattern =
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
private static X509Certificate readPublicKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(input);
} finally {
input.close();
}
}
/** Read a PKCS#8 format private key. */
private static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
byte[] buffer = new byte[4096];
int size = input.read(buffer);
byte[] bytes = Arrays.copyOf(buffer, size);
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
/*
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
* OID and use that to construct a KeyFactory.
*/
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
return KeyFactory.getInstance(algOid).generatePrivate(spec);
} finally {
input.close();
}
}
/**
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private static Manifest addDigestsToManifest(JarFile jar, int hashes)
private static Manifest addDigestsToManifest(JarMap jar, int hashes)
throws IOException, GeneralSecurityException {
Manifest input = jar.getManifest();
Manifest output = new Manifest();
@@ -294,12 +300,12 @@ public class ZipSigner {
* reduce variation in the output file and make incremental OTAs
* more efficient.
*/
private static void copyFiles(Manifest manifest, JarFile in, JarOutputStream out,
private static void copyFiles(Manifest manifest, JarMap in, JarOutputStream out,
long timestamp, int alignment) throws IOException {
byte[] buffer = new byte[4096];
int num;
Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<String>(entries.keySet());
ArrayList<String> names = new ArrayList<>(entries.keySet());
Collections.sort(names);
boolean firstEntry = true;
long offset = 0L;
@@ -368,15 +374,12 @@ public class ZipSigner {
// Used for signWholeFile
private static class CMSProcessableFile implements CMSTypedData {
private File file;
private ASN1ObjectIdentifier type;
private byte[] buffer;
int bufferSize = 0;
private RandomAccessFile file;
CMSProcessableFile(File file) {
this.file = file;
CMSProcessableFile(File file) throws FileNotFoundException {
this.file = new RandomAccessFile(file, "r");
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
buffer = new byte[4096];
}
@Override
@@ -386,16 +389,13 @@ public class ZipSigner {
@Override
public void write(OutputStream out) throws IOException, CMSException {
FileInputStream input = new FileInputStream(file);
long len = file.length() - 2;
while ((bufferSize = input.read(buffer)) > 0) {
if (len <= bufferSize) {
out.write(buffer, 0, (int) len);
break;
} else {
out.write(buffer, 0, bufferSize);
}
len -= bufferSize;
file.seek(0);
int read;
byte buffer[] = new byte[4096];
int len = (int) file.length() - 2;
while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
out.write(buffer, 0, read);
len -= read;
}
}
@@ -404,8 +404,11 @@ public class ZipSigner {
return file;
}
byte[] getTail() {
return Arrays.copyOfRange(buffer, 0, bufferSize);
byte[] getTail() throws IOException {
byte tail[] = new byte[22];
file.seek(file.length() - 22);
file.readFully(tail);
return tail;
}
}
@@ -470,7 +473,7 @@ public class ZipSigner {
outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream);
}
private static void signFile(Manifest manifest, JarFile inputJar,
private static void signFile(Manifest manifest, JarMap inputJar,
X509Certificate publicKey, PrivateKey privateKey,
JarOutputStream outputJar)
throws Exception {
@@ -496,72 +499,4 @@ public class ZipSigner {
writeSignatureBlock(new CMSProcessableByteArray(signedData),
publicKey, privateKey, outputJar);
}
public static void main(String[] args) {
boolean minSign = false;
int argStart = 0;
if (args.length < 4) {
System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar");
System.exit(2);
}
if (args[0].equals("-m")) {
minSign = true;
argStart = 1;
}
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
File pubKey = new File(args[argStart]);
File privKey = new File(args[argStart + 1]);
File input = new File(args[argStart + 2]);
File output = new File(args[argStart + 3]);
int alignment = 4;
JarFile inputJar = null;
FileOutputStream outputFile = null;
int hashes = 0;
try {
X509Certificate publicKey = readPublicKey(new FileInputStream(pubKey));
hashes |= getDigestAlgorithm(publicKey);
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
PrivateKey privateKey = readPrivateKey(new FileInputStream(privKey));
outputFile = new FileOutputStream(output);
if (minSign) {
ZipSigner.signWholeFile(input, publicKey, privateKey, outputFile);
} else {
inputJar = new JarFile(input, false); // Don't verify.
JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(inputJar, hashes);
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
outputJar.close();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
try {
if (inputJar != null) inputJar.close();
if (outputFile != null) outputFile.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
}
}

View File

@@ -0,0 +1,231 @@
package com.topjohnwu.crypto;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
public class SignBoot {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
InputStream keyIn, InputStream certIn) {
try {
ByteArrayStream bas = new ByteArrayStream();
bas.readFrom(imgIn);
byte[] image = bas.toByteArray();
bas.close();
int signableSize = getSignableImageSize(image);
if (signableSize < image.length) {
System.err.println("NOTE: truncating input from " +
image.length + " to " + signableSize + " bytes");
image = Arrays.copyOf(image, signableSize);
} else if (signableSize > image.length) {
throw new IllegalArgumentException("Invalid image: too short, expected " +
signableSize + " bytes");
}
BootSignature bootsig = new BootSignature(target, image.length);
X509Certificate cert = CryptoUtils.readPublicKey(certIn);
bootsig.setCertificate(cert);
PrivateKey key = CryptoUtils.readPrivateKey(keyIn);
bootsig.setSignature(bootsig.sign(image, key),
CryptoUtils.getSignatureAlgorithmIdentifier(key));
byte[] encoded_bootsig = bootsig.getEncoded();
imgOut.write(image);
imgOut.write(encoded_bootsig);
imgOut.flush();
return true;
} catch (Exception e) {
e.printStackTrace(System.err);
return false;
}
}
public static boolean verifySignature(InputStream imgIn, InputStream certPath) {
try {
ByteArrayStream bas = new ByteArrayStream();
bas.readFrom(imgIn);
byte[] image = bas.toByteArray();
bas.close();
int signableSize = getSignableImageSize(image);
if (signableSize >= image.length) {
System.err.println("Invalid image: not signed");
return false;
}
byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
BootSignature bootsig = new BootSignature(signature);
if (certPath != null) {
bootsig.setCertificate(CryptoUtils.readPublicKey(certPath));
}
if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
System.err.println("Signature is VALID");
return true;
} else {
System.err.println("Signature is INVALID");
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.println("Invalid image: not signed");
}
return false;
}
public static int getSignableImageSize(byte[] data) throws Exception {
if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
"ANDROID!".getBytes("US-ASCII"))) {
throw new IllegalArgumentException("Invalid image header: missing magic");
}
ByteBuffer image = ByteBuffer.wrap(data);
image.order(ByteOrder.LITTLE_ENDIAN);
image.getLong(); // magic
int kernelSize = image.getInt();
image.getInt(); // kernel_addr
int ramdskSize = image.getInt();
image.getInt(); // ramdisk_addr
int secondSize = image.getInt();
image.getLong(); // second_addr + tags_addr
int pageSize = image.getInt();
int length = pageSize // include the page aligned image header
+ ((kernelSize + pageSize - 1) / pageSize) * pageSize
+ ((ramdskSize + pageSize - 1) / pageSize) * pageSize
+ ((secondSize + pageSize - 1) / pageSize) * pageSize;
length = ((length + pageSize - 1) / pageSize) * pageSize;
if (length <= 0) {
throw new IllegalArgumentException("Invalid image header: invalid length");
}
return length;
}
static class BootSignature extends ASN1Object {
private ASN1Integer formatVersion;
private ASN1Encodable certificate;
private AlgorithmIdentifier algorithmIdentifier;
private DERPrintableString target;
private ASN1Integer length;
private DEROctetString signature;
private PublicKey publicKey;
private static final int FORMAT_VERSION = 1;
/**
* Initializes the object for signing an image file
* @param target Target name, included in the signed data
* @param length Length of the image, included in the signed data
*/
public BootSignature(String target, int length) {
this.formatVersion = new ASN1Integer(FORMAT_VERSION);
this.target = new DERPrintableString(target);
this.length = new ASN1Integer(length);
}
/**
* Initializes the object for verifying a signed image file
* @param signature Signature footer
*/
public BootSignature(byte[] signature)
throws Exception {
ASN1InputStream stream = new ASN1InputStream(signature);
ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
formatVersion = (ASN1Integer) sequence.getObjectAt(0);
if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
throw new IllegalArgumentException("Unsupported format version");
}
certificate = sequence.getObjectAt(1);
byte[] encoded = ((ASN1Object) certificate).getEncoded();
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
publicKey = c.getPublicKey();
ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
algorithmIdentifier = new AlgorithmIdentifier(
(ASN1ObjectIdentifier) algId.getObjectAt(0));
ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
target = (DERPrintableString) attrs.getObjectAt(0);
length = (ASN1Integer) attrs.getObjectAt(1);
this.signature = (DEROctetString) sequence.getObjectAt(4);
}
public ASN1Object getAuthenticatedAttributes() {
ASN1EncodableVector attrs = new ASN1EncodableVector();
attrs.add(target);
attrs.add(length);
return new DERSequence(attrs);
}
public byte[] getEncodedAuthenticatedAttributes() throws IOException {
return getAuthenticatedAttributes().getEncoded();
}
public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
algorithmIdentifier = algId;
signature = new DEROctetString(sig);
}
public void setCertificate(X509Certificate cert)
throws Exception, IOException, CertificateEncodingException {
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
certificate = s.readObject();
publicKey = cert.getPublicKey();
}
public byte[] generateSignableImage(byte[] image) throws IOException {
byte[] attrs = getEncodedAuthenticatedAttributes();
byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
for (int i=0; i < attrs.length; i++) {
signable[i+image.length] = attrs[i];
}
return signable;
}
public byte[] sign(byte[] image, PrivateKey key) throws Exception {
byte[] signable = generateSignableImage(image);
return CryptoUtils.sign(key, signable);
}
public boolean verify(byte[] image) throws Exception {
if (length.getValue().intValue() != image.length) {
throw new IllegalArgumentException("Invalid image length");
}
byte[] signable = generateSignableImage(image);
return CryptoUtils.verify(publicKey, signable, signature.getOctets(),
algorithmIdentifier);
}
@Override
public ASN1Primitive toASN1Primitive() {
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(formatVersion);
v.add(certificate);
v.add(algorithmIdentifier);
v.add(getAuthenticatedAttributes());
v.add(signature);
return new DERSequence(v);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.topjohnwu.crypto;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.Security;
public class ZipSigner {
public static void main(String[] args) {
boolean minSign = false;
int argStart = 0;
if (args.length < 4) {
System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar");
System.exit(2);
}
if (args[0].equals("-m")) {
minSign = true;
argStart = 1;
}
SignAPK.sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(SignAPK.sBouncyCastleProvider, 1);
File pubKey = new File(args[argStart]);
File privKey = new File(args[argStart + 1]);
File input = new File(args[argStart + 2]);
File output = new File(args[argStart + 3]);
try (InputStream pub = new FileInputStream(pubKey);
InputStream priv = new FileInputStream(privKey);
JarMap jar = new JarMap(input, false)) {
SignAPK.signZip(pub, priv, jar, output, minSign);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}

View File

@@ -1,7 +1,7 @@
# Magisk Documentations
(Updated on 2017.8.16) ([Changelog](changelog.md))
(Updated on 2017.9.28) ([Changelog](changelog.md))
## Table of Content
## Table of Contents
- [Introduction](#introduction)
- [Procedure Diagram](https://cdn.rawgit.com/topjohnwu/Magisk/cc1809688299f1f8b5db494a234850852712c0c9/docs/procedures.html)
@@ -19,6 +19,7 @@
- [Modules and Templates](module_repo.md#magisk-module-format)
- [Submit Modules to Repo](module_repo.md#submit-your-module-to-magisk-modules-repo)
- [Tips and Tricks](tips.md)
- [OTA Installation Tips](tips.md#ota-installation-tips)
- [Remove Files](tips.md#remove-files)
- [Remove Folders](tips.md#remove-folders)

View File

@@ -8,32 +8,27 @@ Command help message:
```
Usage: magisk [applet [arguments]...]
or: magisk --install [SOURCE] DIR
if SOURCE not provided, will link itself
or: magisk --list
or: magisk --createimg IMG SIZE
create ext4 image, SIZE is interpreted in MB
or: magisk --imgsize IMG
or: magisk --resizeimg IMG SIZE
SIZE is interpreted in MB
or: magisk --mountimg IMG PATH
mount IMG to PATH and prints the loop device
or: magisk --umountimg PATH LOOP
or: magisk --[boot stage]
start boot stage service
or: magisk [options]
or: applet [arguments]...
or: magisk [options]...
Options:
-c print current binary version
-v print running daemon version
-V print running daemon version code
--list list all availible applets
--install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional
--createimg IMG SIZE create ext4 image. SIZE is interpreted in MB
--imgsize IMG report ext4 image used/total size
--resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB
--mountimg IMG PATH mount IMG to PATH and prints the loop device
--umountimg PATH LOOP unmount PATH and delete LOOP device
--[boot stage] start boot stage service
--unlock-blocks set BLKROSET flag to OFF for all block devices
Supported boot stages:
post-fs, post-fs-data, service
Options:
-c print client version
-v print daemon version
-V print daemon version code
Supported applets:
su, resetprop, magiskpolicy, supolicy, sepolicy-inject, magiskhide
su, resetprop, magiskpolicy, supolicy, magiskhide
```
### su
@@ -86,7 +81,7 @@ resetprop --delete NAME remove prop entry NAME
```
### magiskpolicy
(This tool is aliased to `supolicy` and `sepolicy-injection` for legacy reasons)
(This tool is aliased to `supolicy` for compatibility)
A tool to patch `sepolicy`. **magiskpolicy** also comes with built-in rules to unleash restrictions to make Magisk work properly. `sepolicy` is a compiled binary containing SELinux rules; we directly patch rules in the binary format since we don't have access to the SELinux policy source (`*.te`) files.

View File

@@ -1,3 +1,6 @@
# Changelog
- 2017.8.16
- Initial version for Magisk v13.5+
- Initial version for Magisk v13.5
- 2017.9.28
- Update applets info to Magisk v14.1
- Add OTA tips

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
docs/images/flashfire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

BIN
docs/images/ota_step2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

View File

@@ -61,7 +61,7 @@ If you want to share your module with others, you can submit your modules to [Ma
#### Once your module is live on the Modules Repo, the description of your repo should be the ID of your module. Please do NOT change the description, repeat, do NOT change the description.
![repo_description.png](repo_description.png)
![repo_description.png](images/repo_description.png)
## Notes
- The Module Template depends on external scripts, be aware of the minimal required Magisk version of the template.

View File

@@ -1,6 +1,53 @@
# Tips and Tricks
## OTA Installation Tips
Magisk do modifications systemless-ly, which means applying official OTAs is much simpler. Here I provide a few tutorials for several different kind of devices to apply OTAs and preserve Magisk after the installation if possible.
**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!!**
#### Prerequisites
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
<img src="images/disable_auto_ota.png" width="250">
1. When an OTA is available, please go to Magisk Manager → Uninstall → Restore Stock Boot. **Do not reboot immediately or you will have Magisk uninstalled.** This will restore your boot back to 100% untouched stock boot image in order to pass boot verification. **This step is required before doing any of the following steps written below!**
<img src="images/restore_boot.png" width="250">
#### 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.
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.
<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.
#### Devices with FlashFire Support
(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).
1. After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.**
1. Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
1. Please use the options shown in the screenshot below. The key point is to disable EverRoot (or it will install SuperSU), and add a new action to flash Magisk zip **after** the OTA update.zip (the update.zip is auto generated in the previous step).
<img src="images/flashfire.png" width="250">
1. Press the big **Flash** button, after a few minutes it should reboot updated with Magisk installed.
#### Other Devices - General Case
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 my daily driver - 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 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. Apply and reboot your device. This will use the official stock OTA installation mechanism of your device to upgrade your system.
1. Once it's done you will be left with an upgraded, 100% stock, un-rooted device. You will have to manually flash Magisk back. Consider using the methods stated in step 1. to flash Magisk without touching the recovery partition if you want to receive stock OTAs frequently.
## Remove Files
How to efficiently remove a file systemless-ly? To actually make the file **disappear** within the folder 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, then you're basically done!
How to efficiently 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 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`.

22
gradle.properties Normal file
View File

@@ -0,0 +1,22 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2560m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
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

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Thu Aug 24 10:35:40 CST 2017
#Mon Dec 04 11:24:34 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
#!/usr/bin/env bash
##############################################################################
##
@@ -6,30 +6,12 @@
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# 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"`
# 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"
@@ -48,7 +30,6 @@ die ( ) {
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -59,11 +40,26 @@ case "`uname`" in
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -89,7 +85,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -154,19 +150,11 @@ if $cygwin ; then
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
APP_ARGS=$(save "$@")
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# 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" "$@"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

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,9 +46,10 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
@rem Get command-line arguments, handling Windowz 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.
@@ -59,6 +60,11 @@ 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,107 +0,0 @@
LOCAL_PATH := $(call my-dir)
########################
# Binaries
########################
# magisk main binary
include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_STATIC_LIBRARIES := libsepol
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/external \
$(LOCAL_PATH)/selinux/libsepol/include
LOCAL_SRC_FILES := \
daemon/magisk.c \
daemon/daemon.c \
daemon/socket_trans.c \
daemon/log_monitor.c \
daemon/bootstages.c \
utils/misc.c \
utils/vector.c \
utils/xwrap.c \
utils/list.c \
utils/img.c \
magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
magiskpolicy/api.c \
resetprop/resetprop.cpp \
resetprop/system_properties.cpp \
su/su.c \
su/activity.c \
su/db.c \
su/misc.c \
su/pts.c \
su/su_daemon.c \
su/su_socket.c
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch
LOCAL_CPPFLAGS := -std=c++11
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
# magiskboot
include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := libz liblzma liblz4 libbz2
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/magiskboot \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/ndk-compression/zlib \
$(LOCAL_PATH)/ndk-compression/xz/src/liblzma/api \
$(LOCAL_PATH)/ndk-compression/lz4/lib \
$(LOCAL_PATH)/ndk-compression/bzip2
LOCAL_SRC_FILES := \
magiskboot/main.c \
magiskboot/bootimg.c \
magiskboot/hexpatch.c \
magiskboot/compress.c \
magiskboot/boot_utils.c \
magiskboot/cpio.c \
magiskboot/sha1.c \
utils/xwrap.c \
utils/vector.c \
utils/list.c
LOCAL_CFLAGS := -DZLIB_CONST
include $(BUILD_EXECUTABLE)
# 32-bit static binaries
ifneq ($(TARGET_ARCH_ABI), x86_64)
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
# b64xz
include $(CLEAR_VARS)
LOCAL_MODULE := b64xz
LOCAL_STATIC_LIBRARIES := liblzma
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ndk-compression/xz/src/liblzma/api
LOCAL_SRC_FILES := b64xz.c
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
# Busybox
include jni/busybox/Android.mk
endif
endif
########################
# Libraries
########################
# External shared libraries, include stub libselinux and libsqlite
include jni/external/Android.mk
# libsepol, static library
include jni/selinux/libsepol/Android.mk
# Compression libraries for magiskboot
include jni/ndk-compression/zlib/Android.mk
include jni/ndk-compression/xz/src/liblzma/Android.mk
include jni/ndk-compression/lz4/lib/Android.mk
include jni/ndk-compression/bzip2/Android.mk

Submodule jni/busybox deleted from 90c9c4fd96

View File

@@ -1,204 +0,0 @@
/* daemon.c - Magisk Daemon
*
* Start the daemon and wait for requests
* Connect the daemon and send requests through sockets
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "magiskpolicy.h"
pthread_t sepol_patch;
static void *request_handler(void *args) {
// Setup the default error handler for threads
err_handler = exit_thread;
int client = *((int *) args);
free(args);
client_request req = read_int(client);
struct ucred credentials;
get_client_cred(client, &credentials);
switch (req) {
case LAUNCH_MAGISKHIDE:
case STOP_MAGISKHIDE:
case ADD_HIDELIST:
case RM_HIDELIST:
case LS_HIDELIST:
case POST_FS:
case POST_FS_DATA:
case LATE_START:
if (credentials.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
return NULL;
}
default:
break;
}
switch (req) {
case LAUNCH_MAGISKHIDE:
launch_magiskhide(client);
break;
case STOP_MAGISKHIDE:
stop_magiskhide(client);
break;
case ADD_HIDELIST:
add_hide_list(client);
break;
case RM_HIDELIST:
rm_hide_list(client);
break;
case LS_HIDELIST:
ls_hide_list(client);
break;
case SUPERUSER:
su_daemon_receiver(client);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VER_STR);
close(client);
break;
case CHECK_VERSION_CODE:
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;
default:
break;
}
return NULL;
}
/* Setup the address and return socket fd */
static int setup_socket(struct sockaddr_un *sun) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
return fd;
}
static void *large_sepol_patch(void *args) {
LOGD("sepol: Starting large patch thread\n");
// Patch su to everything
sepol_allow("su", ALL, ALL, ALL);
dump_policydb(SELINUX_LOAD);
LOGD("sepol: Large patch done\n");
destroy_policydb();
return NULL;
}
void start_daemon(int client) {
// Launch the daemon, create new session, set proper context
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "Starting daemon requires root: %s\n", strerror(errno));
PLOGE("start daemon");
}
switch (fork()) {
case -1:
PLOGE("fork");
case 0:
break;
default:
return;
}
// First close the client, it's useless for us
close(client);
xsetsid();
setcon("u:r:su:s0");
umask(022);
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO);
close(fd);
// Patch selinux with medium patch before we do anything
load_policydb(SELINUX_POLICY);
sepol_med_rules();
dump_policydb(SELINUX_LOAD);
// Continue the larger patch in another thread, we will join later
pthread_create(&sepol_patch, NULL, large_sepol_patch, NULL);
struct sockaddr_un sun;
fd = setup_socket(&sun);
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
xlisten(fd, 10);
// Change process name
strcpy(argv0, "magisk_daemon");
// The root daemon should not do anything if an error occurs
// It should stay intact under any circumstances
err_handler = do_nothing;
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
// Unlock all blocks for rw
unlock_blocks();
// Setup links under /sbin
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
create_links(NULL, "/sbin");
xchmod("/sbin", 0755);
xmkdir("/magisk", 0755);
xchmod("/magisk", 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
// Loop forever to listen for requests
while(1) {
int *client = xmalloc(sizeof(int));
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
pthread_t thread;
xpthread_create(&thread, NULL, request_handler, client);
// Detach the thread, we will never join it
pthread_detach(thread);
}
}
/* Connect the daemon, and return a socketfd */
int connect_daemon() {
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 the daemon
* since there is no clear entry point when the daemon should be started
*/
LOGD("client: connect fail, try launching new daemon process\n");
start_daemon(fd);
do {
// Wait for 10ms
usleep(10);
} while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)));
}
return fd;
}

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