Compare commits

...

103 Commits
v14.2 ... v15.1

Author SHA1 Message Date
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
123 changed files with 5866 additions and 3447 deletions

15
.gitignore vendored
View File

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

24
.gitmodules vendored
View File

@@ -1,21 +1,27 @@
[submodule "jni/selinux"] [submodule "jni/selinux"]
path = jni/external/selinux path = core/jni/external/selinux
url = https://github.com/topjohnwu/selinux.git url = https://github.com/topjohnwu/selinux.git
[submodule "jni/su"] [submodule "jni/su"]
path = jni/su path = core/jni/su
url = https://github.com/topjohnwu/MagiskSU.git url = https://github.com/topjohnwu/MagiskSU.git
[submodule "jni/ndk-compression"]
path = jni/external/ndk-compression
url = https://github.com/topjohnwu/ndk-compression.git
[submodule "jni/magiskpolicy"] [submodule "jni/magiskpolicy"]
path = jni/magiskpolicy path = core/jni/magiskpolicy
url = https://github.com/topjohnwu/magiskpolicy.git url = https://github.com/topjohnwu/magiskpolicy.git
[submodule "MagiskManager"] [submodule "MagiskManager"]
path = MagiskManager path = app
url = https://github.com/topjohnwu/MagiskManager.git url = https://github.com/topjohnwu/MagiskManager.git
[submodule "jni/busybox"] [submodule "jni/busybox"]
path = jni/external/busybox path = core/jni/external/busybox
url = https://github.com/topjohnwu/ndk-busybox.git url = https://github.com/topjohnwu/ndk-busybox.git
[submodule "jni/external/dtc"] [submodule "jni/external/dtc"]
path = jni/external/dtc path = core/jni/external/dtc
url = https://github.com/dgibson/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 773c24b7fc

View File

@@ -2,34 +2,30 @@
## How to build Magisk ## 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** ### Environment Requirements
**Ubuntu 17.04 x64**
**Windows 10 x64**
#### Environment Requirements
1. A 64-bit machine: `cmake` for Android is only available in 64-bit 1. A 64-bit machine: `cmake` for Android is only available in 64-bit
2. Python 3.5+: to run the build script 2. Python 3.5+: run `build.py` script
3. Java Development Kit (JDK) 8: To compile Magisk Manager and sign zips 3. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
4. C compiler (Unix only): To build `zipadjust`. Windows users can use the pre-built `zipadjust.exe` 4. Latest Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
5. 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. Android NDK: Install NDK via `sdkmanager`, or via Android SDK Manager in Android Studio 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 ### 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.
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. 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.
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. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
3. 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. Build everything with `build.py`, don't directly call `gradlew` or `ndk-build`, since most requires special setup / dependencies.
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. By default, `build.py` will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place a Java Keystore file at `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
5. The 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.
## License ## 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 you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation, GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version. either version 3 of the License, or (at your option) any later version.
@@ -45,12 +41,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
## Credits ## Credits
**MagiskManager** (`MagiskManager`) **MagiskManager** (`app`)
* Copyright 2016-2017, John Wu (@topjohnwu) * Copyright 2016-2017, John Wu (@topjohnwu)
* All contributors and translators * All contributors and translators on Github
**MagiskSU** (`jni/su`) **MagiskSU** (`core/jni/su`)
* Copyright 2016-2017, John Wu (@topjohnwu) * Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me) * Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
@@ -58,37 +54,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2010, Adam Shanks (@ChainsDD) * Copyright 2010, Adam Shanks (@ChainsDD)
* Copyright 2008, Zinx Verituse (@zinxv) * Copyright 2008, Zinx Verituse (@zinxv)
**MagiskPolicy** (`jni/magiskpolicy`) **MagiskPolicy** (`core/jni/magiskpolicy`)
* Copyright 2016-2017, John Wu (@topjohnwu) * Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me) * Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
* Copyright 2015, Joshua Brindle (@joshua_brindle) * Copyright 2015, Joshua Brindle (@joshua_brindle)
**MagiskHide** (`jni/magiskhide`) **MagiskHide** (`core/jni/magiskhide`)
* Copyright 2016-2017, John Wu (@topjohnwu) * Copyright 2016-2017, John Wu (@topjohnwu)
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me) (original hidesu) * Copyright 2016, Pierre-Hugues Husson (phh@phh.me)
**resetprop** (`jni/resetprop`) **resetprop** (`core/jni/resetprop`)
* Copyright 2016-2017 John Wu (@topjohnwu) * Copyright 2016-2017 John Wu (@topjohnwu)
* Copyright 2016 nkk71 (nkk71x@gmail.com) * Copyright 2016 nkk71 (nkk71x@gmail.com)
**SELinux** (`jni/selinux`) **External Dependencies** (`core/jni/external`)
* Makefile for NDK: Copyright 2016-2017, John Wu (@topjohnwu) * Makefile for busybox, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen)
* Maintained by many developers in SELinux project * Each dependencies has its own license/copyright information in each subdirectory.
All of them are either GPL or GPL compatible.
**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
**Others Not Mentioned** **Others Not Mentioned**

1
app Submodule

Submodule app added at 1adf331268

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
}

309
build.py
View File

@@ -3,6 +3,10 @@ import sys
import os import os
import subprocess import subprocess
if os.name == 'nt':
from colorama import init
init()
def error(str): def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n') print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1) sys.exit(1)
@@ -37,14 +41,37 @@ import errno
import shutil import shutil
import lzma import lzma
import base64 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: try:
os.remove(file) os.remove(file)
except OSError as e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise 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): def zip_with_msg(zipfile, source, target):
if not os.path.exists(source): if not os.path.exists(source):
error('{} does not exist! Try build \'binary\' and \'apk\' before zipping!'.format(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) build_apk(args)
zip_main(args) zip_main(args)
zip_uninstaller(args) zip_uninstaller(args)
build_snet(args)
def build_binary(args): def build_binary(args):
header('* Building Magisk binaries') header('* Building Magisk binaries')
# Force update Android.mk timestamp to trigger recompilation # Force update logging.h timestamp to trigger recompilation
os.utime(os.path.join('jni', 'Android.mk')) 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' debug_flag = '' if args.release else '-DMAGISK_DEBUG'
proc = subprocess.run('{} APP_CFLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\" -j{}'.format( cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
ndk_build, args.versionString, args.versionCode, debug_flag, multiprocessing.cpu_count()), shell=True)
# Prebuild
proc = subprocess.run('{} -C core PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk binary failed!') 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): def build_apk(args):
header('* Building Magisk Manager') header('* Building Magisk Manager')
for key in ['public.certificate.x509.pem', 'private.key.pk8']: for key in ['public.certificate.x509.pem', 'private.key.pk8']:
source = os.path.join('ziptools', key) source = os.path.join('ziptools', key)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key) target = os.path.join('app', 'src', 'main', 'assets', key)
print('cp: {} -> {}'.format(source, target)) cp(source, target)
shutil.copyfile(source, target)
for script in ['magisk_uninstaller.sh', 'util_functions.sh']: for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
source = os.path.join('scripts', script) source = os.path.join('scripts', script)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', script) target = os.path.join('app', 'src', 'main', 'assets', script)
print('cp: {} -> {}'.format(source, target)) cp(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('')
if args.release: 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\'') 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: if proc.returncode != 0:
error('Build Magisk Manager failed!') error('Build Magisk Manager failed!')
@@ -113,8 +152,8 @@ def build_apk(args):
# Find the latest build tools # Find the latest build tools
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1] build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
silentremove(aligned) rm(aligned)
silentremove(release) rm(release)
proc = subprocess.run([ proc = subprocess.run([
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'), 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') error('Cannot find apksigner.jar in Android SDK build tools')
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format( proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
apksigner, apksigner, 'release_signature.jks', release, aligned), shell=True)
os.path.join('..', 'release_signature.jks'),
release, aligned), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Release sign Magisk Manager failed!') error('Release sign Magisk Manager failed!')
silentremove(unsigned) rm(unsigned)
silentremove(aligned) rm(aligned)
mkdir('out')
target = os.path.join('out', 'app-release.apk')
print('')
mv(release, target)
else: else:
proc = subprocess.run('{} app::assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True) proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk Manager failed!') error('Build Magisk Manager failed!')
# Return to upper directory source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
os.chdir('..') mkdir('out')
target = os.path.join('out', 'app-debug.apk')
print('')
mv(source, target)
def sign_adjust_zip(unsigned, output): def build_snet(args):
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
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: if proc.returncode != 0:
error('Build zipadjust failed!') error('Build snet extention failed!')
if not os.path.exists(zipsigner): source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
header('* Building zipsigner.jar') mkdir('out')
os.chdir(os.path.join('ziptools', 'zipsigner')) target = os.path.join('out', 'snet.apk')
proc = subprocess.run('{} shadowJar'.format(os.path.join('.', 'gradlew')), shell=True) print('')
if proc.returncode != 0: mv(source, target)
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'])
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')
def gen_update_binary(): def gen_update_binary():
update_bin = [] update_bin = []
binary = os.path.join('libs', 'armeabi-v7a', 'b64xz') binary = os.path.join('out', 'armeabi-v7a', 'b64xz')
if not os.path.exists(binary): if not os.path.exists(binary):
error('Please build \'binary\' before zipping!') error('Please build \'binary\' before zipping!')
with open(binary, 'rb') as b64xz: with open(binary, 'rb') as b64xz:
update_bin.append('#! /sbin/sh\nEX_ARM=') update_bin.append('#! /sbin/sh\nEX_ARM=\'')
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read())) update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('libs', 'x86', 'b64xz') binary = os.path.join('out', 'x86', 'b64xz')
with open(binary, 'rb') as b64xz: with open(binary, 'rb') as b64xz:
update_bin.append('\nEX_X86=') update_bin.append('\'\nEX_X86=\'')
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read())) update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('libs', 'armeabi-v7a', 'busybox') binary = os.path.join('out', 'armeabi-v7a', 'busybox')
with open(binary, 'rb') as busybox: with open(binary, 'rb') as busybox:
update_bin.append('\nBB_ARM=') update_bin.append('\'\nBB_ARM=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii')) update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
binary = os.path.join('libs', 'x86', 'busybox') binary = os.path.join('out', 'x86', 'busybox')
with open(binary, 'rb') as busybox: with open(binary, 'rb') as busybox:
update_bin.append('\nBB_X86=') update_bin.append('\nBB_X86=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii')) update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
update_bin.append('\n') update_bin.append('\n')
with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script: with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script:
update_bin.append(script.read()) update_bin.append(script.read())
@@ -221,7 +231,9 @@ def gen_update_binary():
def zip_main(args): def zip_main(args):
header('* Packing Flashable Zip') 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 # META-INF
# update-binary # update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary') target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -234,17 +246,13 @@ def zip_main(args):
# Binaries # Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]: for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
for binary in ['magisk', 'magiskboot']: for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('libs', lib_dir, binary) source = os.path.join('out', lib_dir, binary)
target = os.path.join(zip_dir, binary) target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
source = os.path.join('libs', 'arm64-v8a', 'magiskinit')
target = os.path.join('arm64', 'magiskinit')
zip_with_msg(zipf, source, target)
# APK # APK
source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk', source = os.path.join('out', 'app-release.apk' if args.release else 'app-debug.apk')
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
target = os.path.join('common', 'magisk.apk') target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
@@ -258,7 +266,7 @@ def zip_main(args):
with open(source, 'r') as script: with open(source, 'r') as script:
# Add version info util_functions.sh # Add version info util_functions.sh
util_func = script.read().replace( util_func = script.read().replace(
'MAGISK_VERSION_STUB', 'MAGISK_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') target = os.path.join('common', 'util_functions.sh')
print('zip: ' + source + ' -> ' + target) print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func) zipf.writestr(target, util_func)
@@ -266,10 +274,6 @@ def zip_main(args):
source = os.path.join('scripts', 'addon.d.sh') source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('addon.d', '99-magisk.sh') target = os.path.join('addon.d', '99-magisk.sh')
zip_with_msg(zipf, source, target) 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 # Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']: for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
@@ -278,13 +282,15 @@ def zip_main(args):
# End of zipping # End of zipping
output = 'Magisk-v{}.zip'.format(args.versionString) output = os.path.join('out', 'Magisk-v{}.zip'.format(args.versionString))
sign_adjust_zip('tmp_unsigned.zip', output) sign_adjust_zip(unsigned, output)
def zip_uninstaller(args): def zip_uninstaller(args):
header('* Packing Uninstaller Zip') 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 # META-INF
# update-binary # update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary') target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -297,7 +303,7 @@ def zip_uninstaller(args):
# Binaries # Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]: for lib_dir, zip_dir in [('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') target = os.path.join(zip_dir, 'magiskboot')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
@@ -310,11 +316,9 @@ def zip_uninstaller(args):
source = os.path.join('scripts', 'util_functions.sh') source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script: with open(source, 'r') as script:
# Remove the stub # Remove the stub
util_func = script.read().replace(
'MAGISK_VERSION_STUB', '')
target = os.path.join('util_functions.sh') target = os.path.join('util_functions.sh')
print('zip: ' + source + ' -> ' + target) print('zip: ' + source + ' -> ' + target)
zipf.writestr(target, util_func) zipf.writestr(target, script.read())
# Prebuilts # Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']: for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
@@ -323,29 +327,79 @@ def zip_uninstaller(args):
# End of zipping # End of zipping
output = 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')) output = os.path.join('out', 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')))
sign_adjust_zip('tmp_unsigned.zip', output) 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): def cleanup(args):
if len(args.target) == 0: if len(args.target) == 0:
args.target = ['binary', 'apk', 'zip'] args.target = ['binary', 'java', 'zip']
if 'binary' in args.target: if 'binary' in args.target:
header('* Cleaning Magisk binaries') header('* Cleaning binaries')
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True) 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: if 'java' in args.target:
header('* Cleaning Magisk Manager') header('* Cleaning java')
os.chdir('MagiskManager') subprocess.run('{} app:clean snet:clean crypto:clean'.format(os.path.join('.', 'gradlew')), shell=True)
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True) for f in os.listdir('out'):
os.chdir('..') if '.apk' in f:
rm(os.path.join('out', f))
if 'zip' in args.target: if 'zip' in args.target:
header('* Cleaning created zip files') header('* Cleaning zip files')
for f in os.listdir('.'): for f in os.listdir('out'):
if '.zip' in f: if '.zip' in f:
print('rm {}'.format(f)) rm(os.path.join('out', f))
silentremove(f)
parser = argparse.ArgumentParser(description='Magisk build script') parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('--release', action='store_true', help='compile Magisk for release') parser.add_argument('--release', action='store_true', help='compile Magisk for release')
@@ -364,6 +418,9 @@ binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK') apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
apk_parser.set_defaults(func=build_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 = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
zip_parser.add_argument('versionString') zip_parser.add_argument('versionString')
zip_parser.add_argument('versionCode', type=int) zip_parser.add_argument('versionCode', type=int)
@@ -372,7 +429,7 @@ zip_parser.set_defaults(func=zip_main)
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller') uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
uninstaller_parser.set_defaults(func=zip_uninstaller) uninstaller_parser.set_defaults(func=zip_uninstaller)
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary apk zip') clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip')
clean_parser.add_argument('target', nargs='*') clean_parser.add_argument('target', nargs='*')
clean_parser.set_defaults(func=cleanup) clean_parser.set_defaults(func=cleanup)

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"
}
}
}
}

View File

@@ -1,52 +1,48 @@
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
# Some handy paths # Some handy paths
JNI_ROOT := jni EXT_PATH := jni/external
SELINUX_PATH := jni/external/selinux SE_PATH := $(EXT_PATH)/selinux
COMPRESS_LIB := jni/external/ndk-compression LIBSELINUX := $(SE_PATH)/libselinux/include
DTC_PATH := jni/external/dtc LIBSEPOL := $(SE_PATH)/libsepol/include $(SE_PATH)/libsepol/cil/include
LIBSELINUX := $(SELINUX_PATH)/libselinux/include LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
LIBSEPOL := $(SELINUX_PATH)/libsepol/include $(SELINUX_PATH)/libsepol/cil/include LIBLZ4 := $(EXT_PATH)/lz4/lib
LIBZ := $(COMPRESS_LIB)/zlib LIBBZ2 := $(EXT_PATH)/bzip2
LIBLZMA := $(COMPRESS_LIB)/xz/src/liblzma/api LIBFDT := $(EXT_PATH)/dtc/libfdt
LIBLZ4 := $(COMPRESS_LIB)/lz4/lib UTIL_SRC := utils/cpio.c \
LIBBZ2 := $(COMPRESS_LIB)/bzip2 utils/file.c \
LIBFDT := $(DTC_PATH)/libfdt utils/img.c \
utils/list.c \
utils/misc.c \
utils/pattern.c \
utils/vector.c \
utils/xwrap.c
######################## ########################
# Binaries # Binaries
######################## ########################
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
# magisk main binary # magisk main binary
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := magisk LOCAL_MODULE := magisk
LOCAL_STATIC_LIBRARIES := libsepol
LOCAL_SHARED_LIBRARIES := libsqlite libselinux LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
jni/include \ jni/include \
jni/external \ jni/external/include \
$(LIBSELINUX) \ $(LIBSELINUX)
$(LIBSEPOL)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
daemon/magisk.c \ core/magisk.c \
daemon/daemon.c \ core/daemon.c \
daemon/socket_trans.c \ core/log_monitor.c \
daemon/log_monitor.c \ core/bootstages.c \
daemon/bootstages.c \ core/socket.c \
utils/misc.c \
utils/vector.c \
utils/xwrap.c \
utils/list.c \
utils/img.c \
magiskhide/magiskhide.c \ magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \ magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \ magiskhide/hide_utils.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
magiskpolicy/api.c \
resetprop/resetprop.cpp \ resetprop/resetprop.cpp \
resetprop/system_properties.cpp \ resetprop/system_properties.cpp \
su/su.c \ su/su.c \
@@ -54,58 +50,69 @@ LOCAL_SRC_FILES := \
su/db.c \ su/db.c \
su/pts.c \ su/pts.c \
su/su_daemon.c \ su/su_daemon.c \
su/su_socket.c su/su_socket.c \
$(UTIL_SRC)
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch -DIS_DAEMON LOCAL_CFLAGS := -DIS_DAEMON -DSELINUX
LOCAL_CPPFLAGS := -std=c++11
LOCAL_LDLIBS := -llog LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE) 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 # magiskboot
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := libz liblzma liblz4 libbz2 libfdt LOCAL_STATIC_LIBRARIES := liblzma liblz4 libbz2 libfdt
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
jni/include \ jni/include \
$(LIBZ) \ jni/external/include \
$(LIBLZMA) \ $(LIBLZMA) \
$(LIBLZ4) \ $(LIBLZ4) \
$(LIBBZ2) \ $(LIBBZ2) \
$(LIBFDT) $(LIBFDT)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
external/sha1/sha1.c \
magiskboot/main.c \ magiskboot/main.c \
magiskboot/bootimg.c \ magiskboot/bootimg.c \
magiskboot/hexpatch.c \ magiskboot/hexpatch.c \
magiskboot/compress.c \ magiskboot/compress.c \
magiskboot/boot_utils.c \
magiskboot/cpio.c \
magiskboot/sha1.c \
magiskboot/types.c \ magiskboot/types.c \
magiskboot/dtb.c \ magiskboot/dtb.c \
utils/xwrap.c \ magiskboot/ramdisk.c \
utils/vector.c $(UTIL_SRC)
LOCAL_CFLAGS := -DZLIB_CONST
include $(BUILD_EXECUTABLE)
# magiskinit LOCAL_CFLAGS := -DXWRAP_EXIT
ifeq ($(TARGET_ARCH_ABI), arm64-v8a) LOCAL_LDLIBS := -lz
include $(CLEAR_VARS)
LOCAL_MODULE := magiskinit
LOCAL_STATIC_LIBRARIES := libsepol
LOCAL_C_INCLUDES := jni/include $(LIBSEPOL)
LOCAL_SRC_FILES := \
magiskinit.c \
magiskboot/boot_utils.c \
utils/xwrap.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
magiskpolicy/api.c
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
endif
# 32-bit static binaries # 32-bit static binaries
ifndef GRADLE # Do not run gradle sync on these binaries
ifneq ($(TARGET_ARCH_ABI), x86_64) ifneq ($(TARGET_ARCH_ABI), x86_64)
ifneq ($(TARGET_ARCH_ABI), arm64-v8a) ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
# b64xz # b64xz
@@ -120,6 +127,10 @@ include $(BUILD_EXECUTABLE)
include jni/external/busybox/Android.mk include jni/external/busybox/Android.mk
endif endif
endif endif
endif
# Precompile
endif
######################## ########################
# Externals # Externals

View File

@@ -1,2 +1,4 @@
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
APP_PLATFORM := android-21 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 <fcntl.h>
#include <string.h> #include <string.h>
#include <dirent.h> #include <dirent.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <selinux/selinux.h> #include <selinux/selinux.h>
@@ -24,14 +22,9 @@
static char *buf, *buf2; static char *buf, *buf2;
static struct vector module_list; static struct vector module_list;
static int seperate_vendor = 0;
extern char **environ; extern char **environ;
#ifdef MAGISK_DEBUG
static int debug_log_pid, debug_log_fd;
#endif
/****************** /******************
* Node structure * * Node structure *
******************/ ******************/
@@ -149,7 +142,7 @@ static void exec_common_script(const char* stage) {
struct dirent *entry; struct dirent *entry;
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage); snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
if (!(dir = opendir(buf))) if (!(dir = xopendir(buf)))
return; return;
while ((entry = xreaddir(dir))) { 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); char *parent_path = get_full_path(parent);
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path); snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
if (!(dir = opendir(buf))) if (!(dir = xopendir(buf)))
goto cleanup; goto cleanup;
while ((entry = xreaddir(dir))) { while ((entry = xreaddir(dir))) {
@@ -262,7 +255,7 @@ static void clone_skeleton(struct node_entry *node) {
// Clone the structure // Clone the structure
char *full_path = get_full_path(node); char *full_path = get_full_path(node);
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path); snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
if (!(dir = opendir(buf))) if (!(dir = xopendir(buf)))
goto cleanup; goto cleanup;
while ((entry = xreaddir(dir))) { while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
@@ -282,7 +275,7 @@ static void clone_skeleton(struct node_entry *node) {
xstat(full_path, &s); xstat(full_path, &s);
getfilecon(full_path, &con); getfilecon(full_path, &con);
LOGI("tmpfs: %s\n", full_path); LOGI("tmpfs: %s\n", full_path);
mount("tmpfs", full_path, "tmpfs", 0, NULL); xmount("tmpfs", full_path, "tmpfs", 0, NULL);
chmod(full_path, s.st_mode & 0777); chmod(full_path, s.st_mode & 0777);
chown(full_path, s.st_uid, s.st_gid); chown(full_path, s.st_uid, s.st_gid);
setfilecon(full_path, con); setfilecon(full_path, con);
@@ -296,7 +289,7 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_DIR(child)) if (IS_DIR(child))
xmkdir(buf, 0755); xmkdir(buf, 0755);
else if (IS_REG(child)) else if (IS_REG(child))
close(open_new(buf)); close(creat(buf, 0644));
// Links will be handled later // Links will be handled later
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) { if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
@@ -399,67 +392,18 @@ static void simple_mount(const char *path) {
* Miscellaneous * * Miscellaneous *
*****************/ *****************/
static void mount_mirrors() { #define alt_img ((char *[]) \
LOGI("* Mounting mirrors"); { "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
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
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);
#ifdef MAGISK_DEBUG
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
#else
LOGI("mount: %s\n", MIRRDIR "/vendor");
#endif
continue;
}
}
vec_deep_destroy(&mounts);
if (!seperate_vendor) {
symlink(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
}
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");
}
static int prepare_img() { static int prepare_img() {
// 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 (access(MAINIMG, F_OK) == -1) {
if (create_img(MAINIMG, 64)) if (create_img(MAINIMG, 64))
return 1; return 1;
@@ -471,8 +415,6 @@ static int prepare_img() {
if (magiskloop == NULL) if (magiskloop == NULL)
return 1; return 1;
vec_init(&module_list);
xmkdir(COREDIR, 0755); xmkdir(COREDIR, 0755);
xmkdir(COREDIR "/post-fs-data.d", 0755); xmkdir(COREDIR "/post-fs-data.d", 0755);
xmkdir(COREDIR "/service.d", 0755); xmkdir(COREDIR "/service.d", 0755);
@@ -490,7 +432,7 @@ static int prepare_img() {
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name); snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
if (access(buf, F_OK) == 0) { if (access(buf, F_OK) == 0) {
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name); snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
exec_command_sync(BBPATH "/rm", "-rf", buf, NULL); rm_rf(buf);
continue; continue;
} }
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name); snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
@@ -511,27 +453,27 @@ static int prepare_img() {
magiskloop = mount_image(MAINIMG, MOUNTPOINT); magiskloop = mount_image(MAINIMG, MOUNTPOINT);
free(magiskloop); free(magiskloop);
// Fix file selinux contexts
fix_filecon();
return 0; return 0;
} }
void fix_filecon() {
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
restorecon(dirfd, 0);
close(dirfd);
}
/**************** /****************
* Entry points * * Entry points *
****************/ ****************/
static void *start_magisk_hide(void *args) {
launch_magiskhide(-1);
return NULL;
}
static void unblock_boot_process() { static void unblock_boot_process() {
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT)); close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
pthread_exit(NULL); pthread_exit(NULL);
} }
void post_fs(int client) { void post_fs(int client) {
// Error handler
err_handler = unblock_boot_process;
LOGI("** post-fs mode running\n"); LOGI("** post-fs mode running\n");
// ack // ack
write_int(client, 0); write_int(client, 0);
@@ -553,57 +495,25 @@ unblock:
} }
void post_fs_data(int client) { void post_fs_data(int client) {
// Error handler
err_handler = unblock_boot_process;
// ack // ack
write_int(client, 0); write_int(client, 0);
close(client); close(client);
if (!check_data()) if (!is_daemon_init && !check_data())
goto unblock; goto unblock;
// Start log monitor // Start the debug log
monitor_logs(); 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);
xwrite(debug_log_fd, "Boot logs:\n", 11);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", NULL);
#endif
LOGI("** post-fs-data mode running\n"); LOGI("** post-fs-data mode running\n");
// Allocate buffer // Allocate buffer
if (buf == NULL) buf = xmalloc(PATH_MAX); if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX); if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
vec_init(&module_list);
// Magisk binaries // Initialize
char *bin_path = NULL; if (!is_daemon_init)
if (access("/cache/data_bin", F_OK) == 0) daemon_init();
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) {
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("/data/magisk_merge.img", MAINIMG)) {
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
goto unblock;
}
// uninstaller // uninstaller
if (access(UNINSTALLER, F_OK) == 0) { if (access(UNINSTALLER, F_OK) == 0) {
@@ -613,7 +523,7 @@ void post_fs_data(int client) {
return; 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 // After this, it will create the module list
if (prepare_img()) if (prepare_img())
goto core_only; // Mounting fails, we can only do core only stuffs goto core_only; // Mounting fails, we can only do core only stuffs
@@ -665,7 +575,7 @@ void post_fs_data(int client) {
if (access(buf, F_OK) == 0) { if (access(buf, F_OK) == 0) {
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module); snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
unlink(buf2); unlink(buf2);
symlink(buf, buf2); xsymlink(buf, buf2);
} }
construct_tree(module, sys_root); construct_tree(module, sys_root);
} }
@@ -703,14 +613,7 @@ core_only:
bind_mount(HOSTSFILE, "/system/etc/hosts"); bind_mount(HOSTSFILE, "/system/etc/hosts");
} }
// Enable magiskhide by default, only disable when set explicitly auto_start_magiskhide();
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);
unblock: unblock:
unblock_boot_process(); unblock_boot_process();
@@ -727,7 +630,8 @@ void late_start(int client) {
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX); if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done // Wait till the full patch is done
pthread_join(sepol_patch, NULL); wait_till_exists(PATCHDONE);
unlink(PATCHDONE);
// Run scripts after full patch, most reliable way to run scripts // Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n"); LOGI("* Running service.d scripts\n");
@@ -768,12 +672,5 @@ core_only:
buf = buf2 = NULL; buf = buf2 = NULL;
vec_deep_destroy(&module_list); vec_deep_destroy(&module_list);
#ifdef MAGISK_DEBUG stop_debug_full_log();
// Stop recording the boot logcat after every boot task is done
kill(debug_log_pid, SIGTERM);
waitpid(debug_log_pid, NULL, 0);
// Then start to log Magisk verbosely
xwrite(debug_log_fd, "\nVerbose logs:\n", 15);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", "-s", "Magisk", NULL);
#endif
} }

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

@@ -0,0 +1,318 @@
/* 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);
// Use shell glob to match files
exec_command_sync("sh", "-c",
"mv -f /data/adb/magisk/stock_*.img.gz /data;"
"rm -f /data/user*/*/magisk.db;", NULL);
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", "all" , "-v", "threadtime", "-s", "am_proc_start", "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", "all", "-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

@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h>
#include "utils.h" #include "utils.h"
#include "magisk.h" #include "magisk.h"
@@ -11,11 +12,7 @@
char *argv0; char *argv0;
char *applet[] = int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
{ "su", "resetprop", "magiskpolicy", "supolicy", "magiskhide", NULL };
int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
int create_links(const char *bin, const char *path) { int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX]; char self[PATH_MAX], linkpath[PATH_MAX];
@@ -32,10 +29,6 @@ int create_links(const char *bin, const char *path) {
return ret; return ret;
} }
// Global error hander function
// Should be changed each thread/process
__thread void (*err_handler)(void);
static void usage() { static void usage() {
fprintf(stderr, fprintf(stderr,
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n" "Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
@@ -47,18 +40,20 @@ static void usage() {
" -c print current binary version\n" " -c print current binary version\n"
" -v print running daemon version\n" " -v print running daemon version\n"
" -V print running daemon version code\n" " -V print running daemon version code\n"
" --list list all availible applets\n" " --list list all available applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n" " --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
" --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n" " --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
" --imgsize IMG report ext4 image used/total size\n" " --imgsize IMG report ext4 image used/total size\n"
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\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" " --mountimg IMG PATH mount IMG to PATH and prints the loop device\n"
" --umountimg PATH LOOP unmount PATH and delete LOOP device\n" " --umountimg PATH LOOP unmount PATH and delete LOOP device\n"
" --[boot stage] start boot stage service\n" " --[init service] start init service\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n" " --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
" --restorecon fix selinux context on Magisk files and folders\n"
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
"\n" "\n"
"Supported boot stages:\n" "Supported init services:\n"
" post-fs, post-fs-data, service\n" " daemon, post-fs, post-fs-data, service\n"
"\n" "\n"
"Supported applets:\n" "Supported applets:\n"
, argv0, argv0); , argv0, argv0);
@@ -71,15 +66,10 @@ static void usage() {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
argv0 = argv[0]; argv0 = argv[0];
// Exit the whole app if error occurs by default if (strcmp(basename(argv[0]), "magisk") == 0) {
err_handler = exit_proc;
char * arg = strrchr(argv[0], '/');
if (arg) ++arg;
else arg = argv[0];
if (strcmp(arg, "magisk") == 0) {
if (argc < 2) usage(); if (argc < 2) usage();
if (strcmp(argv[1], "-c") == 0) { if (strcmp(argv[1], "-c") == 0) {
printf("%s\n", MAGISK_VER_STR); printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
return 0; return 0;
} else if (strcmp(argv[1], "-v") == 0) { } else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon(); int fd = connect_daemon();
@@ -146,6 +136,17 @@ int main(int argc, char *argv[]) {
} else if (strcmp(argv[1], "--unlock-blocks") == 0) { } else if (strcmp(argv[1], "--unlock-blocks") == 0) {
unlock_blocks(); unlock_blocks();
return 0; 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) { } else if (strcmp(argv[1], "--post-fs") == 0) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, POST_FS); write_int(fd, POST_FS);
@@ -162,16 +163,15 @@ int main(int argc, char *argv[]) {
// It's calling applets // It's calling applets
--argc; --argc;
++argv; ++argv;
arg = argv[0];
} }
} }
// Applets // Applets
for (int i = 0; applet[i]; ++i) { 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); 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; return 1;
} }

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

@@ -0,0 +1,528 @@
/* 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 void 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();
sepol_magisk_rules();
dump_policydb("/sepolicy");
}
#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
wait_till_exists("/dev/.coldboot_done");
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);
patch_sepolicy();
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 <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.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. * 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

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_ #define _DAEMON_H_
#include <pthread.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 // Commands require connecting to daemon
typedef enum { typedef enum {
@@ -38,11 +40,14 @@ typedef enum {
// daemon.c // daemon.c
void start_daemon(int client); void start_daemon();
int connect_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); int recv_fd(int sockfd);
void send_fd(int sockfd, int fd); void send_fd(int sockfd, int fd);
int read_int(int fd); int read_int(int fd);
@@ -50,10 +55,6 @@ void write_int(int fd, int val);
char* read_string(int fd); char* read_string(int fd);
void write_string(int fd, const char* val); void write_string(int fd, const char* val);
// log_monitor.c
void monitor_logs();
/*************** /***************
* Boot Stages * * Boot Stages *
***************/ ***************/
@@ -61,6 +62,7 @@ void monitor_logs();
void post_fs(int client); void post_fs(int client);
void post_fs_data(int client); void post_fs_data(int client);
void late_start(int client); void late_start(int client);
void fix_filecon();
/************** /**************
* MagiskHide * * MagiskHide *
@@ -76,6 +78,6 @@ void ls_hide_list(int client);
* Superuser * * Superuser *
*************/ *************/
void su_daemon_receiver(int client); void su_daemon_receiver(int client, struct ucred *credential);
#endif #endif

View File

@@ -8,39 +8,79 @@
#include <errno.h> #include <errno.h>
#include <stdlib.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 #ifdef IS_DAEMON
#undef LOGI
#undef LOGW
#undef LOGE
#undef PLOGE
#include <pthread.h> #include <pthread.h>
#include <android/log.h> #include <android/log.h>
#define LOG_TAG "Magisk" #define LOG_TAG "Magisk"
// Global handler for PLOGE
extern __thread void (*err_handler)(void);
// Common error handlers
static inline void exit_proc() { exit(1); }
static inline void exit_thread() { pthread_exit(NULL); }
static inline void do_nothing() {}
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
#undef LOGD
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#else
#define LOGD(...) {}
#endif #endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #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 LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#define PLOGE(fmt, args...) { LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler(); } enum {
HIDE_EVENT,
LOG_EVENT,
DEBUG_EVENT
};
#else // IS_DAEMON 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> #include <stdio.h>
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); } #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); } #define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
#endif // IS_DAEMON #endif
#endif // _LOGGING_H_ #endif // _LOGGING_H_

View File

@@ -6,9 +6,6 @@
#include "logging.h" #include "logging.h"
#define str(a) #a
#define xstr(a) str(a)
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK" #define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
#define REQUESTOR_DAEMON_PATH "\0MAGISK" #define REQUESTOR_DAEMON_PATH "\0MAGISK"
@@ -17,34 +14,47 @@
#endif #endif
#define LOGFILE "/cache/magisk.log" #define LOGFILE "/cache/magisk.log"
#define LASTLOG "/cache/last_magisk.log" #define DEBUG_LOG "/data/adb/magisk_debug.log"
#define DEBUG_LOG "/data/magisk_debug.log"
#define UNBLOCKFILE "/dev/.magisk.unblock" #define UNBLOCKFILE "/dev/.magisk.unblock"
#define PATCHDONE "/dev/.magisk.patch.done"
#define DISABLEFILE "/cache/.disable_magisk" #define DISABLEFILE "/cache/.disable_magisk"
#define UNINSTALLER "/cache/magisk_uninstaller.sh" #define UNINSTALLER "/cache/magisk_uninstaller.sh"
#define MOUNTPOINT "/magisk" #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 COREDIR MOUNTPOINT "/.core"
#define HOSTSFILE COREDIR "/hosts" #define HOSTSFILE COREDIR "/hosts"
#define HIDELIST COREDIR "/hidelist" #define HIDELIST COREDIR "/hidelist"
#define MAINIMG "/data/magisk.img" #define MAINIMG "/data/adb/magisk.img"
#define DATABIN "/data/magisk" #define DATABIN "/data/adb/magisk"
#define MANAGERAPK DATABIN "/magisk.apk" #define MANAGERAPK DATABIN "/magisk.apk"
#define MAGISKTMP "/dev/magisk" #define MAGISKRC "/init.magisk.rc"
#define MIRRDIR MAGISKTMP "/mirror"
#define BBPATH MAGISKTMP "/bin"
#define CACHEMOUNT "/cache/magisk_mount"
// selinuxfs paths
#define SELINUX_PATH "/sys/fs/selinux/" #define SELINUX_PATH "/sys/fs/selinux/"
#define SELINUX_ENFORCE SELINUX_PATH "enforce" #define SELINUX_ENFORCE SELINUX_PATH "enforce"
#define SELINUX_POLICY SELINUX_PATH "policy" #define SELINUX_POLICY SELINUX_PATH "policy"
#define SELINUX_LOAD SELINUX_PATH "load" #define SELINUX_LOAD SELINUX_PATH "load"
// 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" #define MAGISKHIDE_PROP "persist.magisk.hide"
extern char *argv0; /* For changing process name */ extern char *argv0; /* For changing process name */
extern char *applet[]; #define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL })
extern int (*applet_main[]) (int, char *[]); #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); int create_links(const char *bin, const char *path);

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 setprop(const char *name, const char *value);
int setprop2(const char *name, const char *value, const int trigger); int setprop2(const char *name, const char *value, const int trigger);
char *getprop(const char *name); 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); 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 #ifdef __cplusplus
} }

View File

@@ -7,7 +7,6 @@
#include <stdio.h> #include <stdio.h>
#include <dirent.h> #include <dirent.h>
#include <pthread.h> #include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -18,8 +17,6 @@
#define UID_SYSTEM (get_system_uid()) #define UID_SYSTEM (get_system_uid())
#define UID_RADIO (get_radio_uid()) #define UID_RADIO (get_radio_uid())
extern int quit_signals[];
// xwrap.c // xwrap.c
FILE *xfopen(const char *pathname, const char *mode); 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__) #define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
int xopen2(const char *pathname, int flags); int xopen2(const char *pathname, int flags);
int xopen3(const char *pathname, int flags, mode_t mode); 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 xwrite(int fd, const void *buf, size_t count);
ssize_t xread(int fd, 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); ssize_t xxread(int fd, void *buf, size_t count);
int xpipe2(int pipefd[2], int flags); int xpipe2(int pipefd[2], int flags);
int xsetns(int fd, int nstype); int xsetns(int fd, int nstype);
DIR *xopendir(const char *name); DIR *xopendir(const char *name);
DIR *xfdopendir(int fd);
struct dirent *xreaddir(DIR *dirp); struct dirent *xreaddir(DIR *dirp);
pid_t xsetsid(); pid_t xsetsid();
int xsocket(int domain, int type, int protocol); 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 xlstat(const char *pathname, struct stat *buf);
int xdup2(int oldfd, int newfd); int xdup2(int oldfd, int newfd);
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz); 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 xsymlink(const char *target, const char *linkpath);
int xmount(const char *source, const char *target, int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags, const char *filesystemtype, unsigned long mountflags,
const void *data); const void *data);
int xumount(const char *target); int xumount(const char *target);
int xumount2(const char *target, int flags); int xumount2(const char *target, int flags);
int xchmod(const char *pathname, mode_t mode);
int xrename(const char *oldpath, const char *newpath); int xrename(const char *oldpath, const char *newpath);
int xmkdir(const char *pathname, mode_t mode); 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, void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset); int fd, off_t offset);
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count); 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 // misc.c
#define quit_signals ((int []) { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 })
unsigned get_shell_uid(); unsigned get_shell_uid();
unsigned get_system_uid(); unsigned get_system_uid();
unsigned get_radio_uid(); unsigned get_radio_uid();
@@ -82,14 +85,48 @@ void unlock_blocks();
void setup_sighandlers(void (*handler)(int)); void setup_sighandlers(void (*handler)(int));
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...); int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...);
int exec_command_sync(char *const 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 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); void get_client_cred(int fd, struct ucred *cred);
int switch_mnt_ns(int pid); 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 // img.c
@@ -105,4 +142,10 @@ void umount_image(const char *target, const char *device);
int merge_img(const char *source, const char *target); int merge_img(const char *source, const char *target);
void trim_img(const char *img); 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 #endif

View File

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

View File

@@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "bootimg.h" #include "bootimg.h"
@@ -7,8 +8,13 @@
#include "utils.h" #include "utils.h"
#include "logging.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) { static void dump(void *buf, size_t size, const char *filename) {
int fd = open_new(filename); int fd = creat(filename, 0644);
xwrite(fd, buf, size); xwrite(fd, buf, size);
close(fd); close(fd);
} }
@@ -30,7 +36,7 @@ static void print_hdr(const boot_img_hdr *hdr) {
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr->kernel_size, hdr->kernel_addr); 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, "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, "SECOND [%d] @ 0x%08x\n", hdr->second_size, hdr->second_addr);
fprintf(stderr, "DTB [%d] @ 0x%08x\n", hdr->dt_size, hdr->tags_addr); fprintf(stderr, "EXTRA [%d] @ 0x%08x\n", hdr->extra_size, hdr->tags_addr);
fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size); fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size);
if (hdr->os_version != 0) { if (hdr->os_version != 0) {
int a,b,c,y,m = 0; int a,b,c,y,m = 0;
@@ -49,65 +55,63 @@ static void print_hdr(const boot_img_hdr *hdr) {
} }
fprintf(stderr, "NAME [%s]\n", hdr->name); fprintf(stderr, "NAME [%s]\n", hdr->name);
fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline); fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline);
fprintf(stderr, "\n");
} }
int parse_img(void *orig, size_t size, boot_img *boot) { int parse_img(const char *image, boot_img *boot) {
void *base, *end;
size_t pos = 0;
int ret = 0;
memset(boot, 0, sizeof(*boot)); memset(boot, 0, sizeof(*boot));
for(base = orig, end = orig + size; base < end; base += 256, size -= 256) { int is_blk = mmap_ro(image, &boot->map_addr, &boot->map_size);
switch (check_type(base)) {
// 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: case CHROMEOS:
// The caller should know it's chromeos, as it needs additional signing // The caller should know it's chromeos, as it needs additional signing
ret = 2; boot->flags |= CHROMEOS_FLAG;
continue; continue;
case ELF32: case ELF32:
exit(3); exit(ELF32_RET);
case ELF64: case ELF64:
exit(4); exit(ELF64_RET);
case AOSP: case AOSP:
// Read the header // Read the header
memcpy(&boot->hdr, base, sizeof(boot->hdr)); memcpy(&boot->hdr, boot->map_addr + pos, sizeof(boot->hdr));
pos += boot->hdr.page_size; pos += boot->hdr.page_size;
print_hdr(&boot->hdr); print_hdr(&boot->hdr);
boot->kernel = base + pos; boot->kernel = boot->map_addr + pos;
pos += boot->hdr.kernel_size; pos += boot->hdr.kernel_size;
mem_align(&pos, boot->hdr.page_size); mem_align(&pos, boot->hdr.page_size);
boot->ramdisk = base + pos; boot->ramdisk = boot->map_addr + pos;
pos += boot->hdr.ramdisk_size; pos += boot->hdr.ramdisk_size;
mem_align(&pos, boot->hdr.page_size); mem_align(&pos, boot->hdr.page_size);
if (boot->hdr.second_size) { if (boot->hdr.second_size) {
boot->second = base + pos; boot->second = boot->map_addr + pos;
pos += boot->hdr.second_size; pos += boot->hdr.second_size;
mem_align(&pos, boot->hdr.page_size); mem_align(&pos, boot->hdr.page_size);
} }
if (boot->hdr.dt_size) { if (boot->hdr.extra_size) {
boot->dtb = base + pos; boot->extra = boot->map_addr + pos;
pos += boot->hdr.dt_size; pos += boot->hdr.extra_size;
mem_align(&pos, boot->hdr.page_size); mem_align(&pos, boot->hdr.page_size);
} }
if (pos < size) { if (pos < boot->map_size) {
boot->extra = base + pos; boot->tail = boot->map_addr + pos;
boot->tail_size = boot->map_size - pos;
} }
// Search for dtb in kernel if not found // Search for dtb in kernel
if (boot->hdr.dt_size == 0) { for (uint32_t i = 0; i < boot->hdr.kernel_size; ++i) {
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) { if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
boot->flags |= APPEND_DTB;
boot->dtb = boot->kernel + i; boot->dtb = boot->kernel + i;
boot->hdr.dt_size = boot->hdr.kernel_size - i; boot->dt_size = boot->hdr.kernel_size - i;
boot->hdr.kernel_size = i; boot->hdr.kernel_size = i;
fprintf(stderr, "APPEND_DTB [%d]\n", boot->hdr.dt_size); fprintf(stderr, "DTB [%u]\n", boot->dt_size);
}
} }
} }
@@ -129,7 +133,7 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr)); memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr));
boot->ramdisk += 512; boot->ramdisk += 512;
boot->hdr.ramdisk_size -= 512; boot->hdr.ramdisk_size -= 512;
boot->ramdisk_type = check_type(boot->ramdisk + 512); boot->ramdisk_type = check_type(boot->ramdisk);
} }
char fmt[16]; char fmt[16];
@@ -138,9 +142,9 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt); fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
get_type_name(boot->ramdisk_type, fmt); get_type_name(boot->ramdisk_type, fmt);
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt); fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt);
fprintf(stderr, "\n");
return ret; return boot->flags & CHROMEOS_FLAG ? CHROMEOS_RET :
((is_blk && boot->tail_size < 500 * 1024) ? INSUF_BLOCK_RET : 0);
default: default:
continue; continue;
} }
@@ -149,33 +153,32 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
} }
void unpack(const char* image) { void unpack(const char* image) {
size_t size;
void *orig;
mmap_ro(image, &orig, &size);
int fd;
boot_img boot; boot_img boot;
int ret = parse_img(image, &boot);
// Parse image int fd;
fprintf(stderr, "Parsing boot image: [%s]\n\n", image);
int ret = parse_img(orig, size, &boot);
// Dump kernel // Dump kernel
if (boot.kernel_type == UNKNOWN) { if (COMPRESSED(boot.kernel_type)) {
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE); fd = creat(KERNEL_FILE, 0644);
} else {
fd = open_new(KERNEL_FILE);
decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size); decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size);
close(fd); 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 // Dump ramdisk
if (boot.ramdisk_type == UNKNOWN) { if (COMPRESSED(boot.ramdisk_type)) {
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw"); fd = creat(RAMDISK_FILE, 0644);
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
} else {
fd = open_new(RAMDISK_FILE);
decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size); decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size);
close(fd); 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) { if (boot.hdr.second_size) {
@@ -183,34 +186,28 @@ void unpack(const char* image) {
dump(boot.second, boot.hdr.second_size, SECOND_FILE); dump(boot.second, boot.hdr.second_size, SECOND_FILE);
} }
if (boot.hdr.dt_size) { if (boot.hdr.extra_size) {
// Dump dtb // Dump extra
dump(boot.dtb, boot.hdr.dt_size, DTB_FILE); dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
} }
munmap(orig, size); munmap(boot.map_addr, boot.map_size);
exit(ret); exit(ret);
} }
void repack(const char* orig_image, const char* out_image) { void repack(const char* orig_image, const char* out_image) {
size_t size;
void *orig;
boot_img boot; boot_img boot;
// There are possible two MTK headers // There are possible two MTK headers
size_t mtk_kernel_off, mtk_ramdisk_off; size_t mtk_kernel_off, mtk_ramdisk_off;
// Load original image
mmap_ro(orig_image, &orig, &size);
// Parse original image // Parse original image
fprintf(stderr, "Parsing boot image: [%s]\n\n", orig_image); parse_img(orig_image, &boot);
parse_img(orig, size, &boot);
fprintf(stderr, "Repack to boot image: [%s]\n\n", out_image); fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
// Create new image // Create new image
int fd = open_new(out_image); int fd = creat(out_image, 0644);
// Skip a page for header // Skip a page for header
write_zero(fd, boot.hdr.page_size); write_zero(fd, boot.hdr.page_size);
@@ -220,18 +217,18 @@ void repack(const char* orig_image, const char* out_image) {
mtk_kernel_off = lseek(fd, 0, SEEK_CUR); mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, 512); write_zero(fd, 512);
} }
if (boot.kernel_type == UNKNOWN) { if (COMPRESSED(boot.kernel_type)) {
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
} else {
size_t raw_size; size_t raw_size;
void *kernel_raw; void *kernel_raw;
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size); mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
boot.hdr.kernel_size = comp(boot.kernel_type, fd, kernel_raw, raw_size); boot.hdr.kernel_size = comp(boot.kernel_type, fd, kernel_raw, raw_size);
munmap(kernel_raw, raw_size); munmap(kernel_raw, raw_size);
} else {
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
} }
if (boot.flags & APPEND_DTB) { // Restore dtb
if (boot.dt_size && access(DTB_FILE, R_OK) == 0) {
boot.hdr.kernel_size += restore(DTB_FILE, fd); boot.hdr.kernel_size += restore(DTB_FILE, fd);
boot.hdr.dt_size = 0;
} }
file_align(fd, boot.hdr.page_size, 1); file_align(fd, boot.hdr.page_size, 1);
@@ -270,18 +267,17 @@ void repack(const char* orig_image, const char* out_image) {
file_align(fd, boot.hdr.page_size, 1); file_align(fd, boot.hdr.page_size, 1);
} }
// Restore dtb // Restore extra
if (boot.hdr.dt_size && access(DTB_FILE, R_OK) == 0) { if (boot.hdr.extra_size && access(EXTRA_FILE, R_OK) == 0) {
printf("Here\n"); boot.hdr.extra_size = restore(EXTRA_FILE, fd);
boot.hdr.dt_size = restore(DTB_FILE, fd);
file_align(fd, boot.hdr.page_size, 1); file_align(fd, boot.hdr.page_size, 1);
} }
// Check extra info, currently only for LG Bump and Samsung SEANDROIDENFORCE // Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
if (boot.extra) { if (boot.tail_size >= 16) {
if (memcmp(boot.extra, "SEANDROIDENFORCE", 16) == 0 || if (memcmp(boot.tail, "SEANDROIDENFORCE", 16) == 0 ||
memcmp(boot.extra, LG_BUMP_MAGIC, 16) == 0 ) { memcmp(boot.tail, LG_BUMP_MAGIC, 16) == 0 ) {
restore_buf(fd, boot.extra, 16); restore_buf(fd, boot.tail, 16);
} }
} }
@@ -305,7 +301,6 @@ void repack(const char* orig_image, const char* out_image) {
// Print new image info // Print new image info
print_hdr(&boot.hdr); print_hdr(&boot.hdr);
munmap(orig, size); munmap(boot.map_addr, boot.map_size);
close(fd); close(fd);
} }

View File

@@ -44,7 +44,7 @@ struct boot_img_hdr
uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */ 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 /* operating system version and security patch level; for
* version "A.B.C" and patch level "Y-M-D": * version "A.B.C" and patch level "Y-M-D":
@@ -74,13 +74,13 @@ struct boot_img_hdr
** +-----------------+ ** +-----------------+
** | second stage | o pages ** | second stage | o pages
** +-----------------+ ** +-----------------+
** | device tree | p pages ** | extra blobs | p pages
** +-----------------+ ** +-----------------+
** **
** n = (kernel_size + page_size - 1) / page_size ** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size ** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_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 ** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0) ** 1. kernel and ramdisk are required (size != 0)
@@ -103,18 +103,25 @@ typedef struct mtk_hdr {
// Flags // Flags
#define MTK_KERNEL 0x1 #define MTK_KERNEL 0x1
#define MTK_RAMDISK 0x2 #define MTK_RAMDISK 0x2
#define APPEND_DTB 0x4 #define CHROMEOS_FLAG 0x4
typedef struct boot_img { 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; boot_img_hdr hdr;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
void *map_addr;
void *kernel; void *kernel;
void *dtb;
void *ramdisk; void *ramdisk;
void *second; void *second;
void *dtb;
void *extra; void *extra;
int flags; void *tail;
file_t kernel_type, ramdisk_type;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
} boot_img; } boot_img;
#endif #endif

View File

@@ -1,28 +1,23 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <zlib.h> #include <zlib.h>
#include <lzma.h> #include <lzma.h>
#include <lz4.h> #include <lz4.h>
#include <lz4frame.h> #include <lz4frame.h>
#include <lz4hc.h>
#include <bzlib.h> #include <bzlib.h>
#include "magiskboot.h" #include "magiskboot.h"
#include "logging.h" #include "logging.h"
#include "utils.h" #include "utils.h"
#define windowBits 15
#define ZLIB_GZIP 16
#define memLevel 8
#define CHUNK 0x40000 #define CHUNK 0x40000
#define LZ4_HEADER_SIZE 19
#define LZ4_FOOTER_SIZE 4
#define LZ4_LEGACY_BLOCKSIZE 0x800000
// Mode: 0 = decode; 1 = encode // Mode: 0 = decode; 1 = encode
size_t gzip(int mode, int fd, const void *buf, size_t size) { size_t gzip(int mode, int fd, const void *buf, size_t size) {
size_t ret = 0, flush, have, pos = 0, total = 0; size_t ret = 0, have, total = 0;
z_stream strm; z_stream strm;
unsigned char out[CHUNK]; unsigned char out[CHUNK];
@@ -32,50 +27,36 @@ size_t gzip(int mode, int fd, const void *buf, size_t size) {
switch(mode) { switch(mode) {
case 0: case 0:
ret = inflateInit2(&strm, windowBits | ZLIB_GZIP); ret = inflateInit2(&strm, 15 | 16);
break; break;
case 1: case 1:
ret = deflateInit2(&strm, 9, Z_DEFLATED, windowBits | ZLIB_GZIP, memLevel, Z_DEFAULT_STRATEGY); ret = deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
break; break;
default:
LOGE("Unsupported gzip mode!\n");
} }
if (ret != Z_OK) if (ret != Z_OK)
LOGE("Unable to init zlib stream\n"); LOGE("Unable to init zlib stream\n");
do { strm.next_in = (void *) buf;
strm.next_in = buf + pos; strm.avail_in = size;
if (pos + CHUNK >= size) {
strm.avail_in = size - pos;
flush = Z_FINISH;
} else {
strm.avail_in = CHUNK;
flush = Z_NO_FLUSH;
}
pos += strm.avail_in;
do { do {
strm.avail_out = CHUNK; strm.avail_out = CHUNK;
strm.next_out = out; strm.next_out = out;
switch(mode) { switch(mode) {
case 0: case 0:
ret = inflate(&strm, flush); ret = inflate(&strm, Z_FINISH);
break; break;
case 1: case 1:
ret = deflate(&strm, flush); ret = deflate(&strm, Z_FINISH);
break; break;
} }
if (ret == Z_STREAM_ERROR) if (ret == Z_STREAM_ERROR)
LOGE("Error when running gzip\n"); LOGE("Error when running gzip\n");
have = CHUNK - strm.avail_out; have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have); total += xwrite(fd, out, have);
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
} while(pos < size);
switch(mode) { switch(mode) {
case 0: case 0:
inflateEnd(&strm); inflateEnd(&strm);
@@ -87,18 +68,16 @@ size_t gzip(int mode, int fd, const void *buf, size_t size) {
return total; return total;
} }
// Mode: 0 = decode xz/lzma; 1 = encode xz; 2 = encode lzma // 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 lzma(int mode, int fd, const void *buf, size_t size) {
size_t have, pos = 0, total = 0; size_t have, total = 0;
lzma_ret ret = 0; lzma_ret ret = 0;
lzma_stream strm = LZMA_STREAM_INIT; lzma_stream strm = LZMA_STREAM_INIT;
lzma_options_lzma opt; lzma_options_lzma opt;
lzma_action action; unsigned char out[CHUNK];
unsigned char out[BUFSIZ];
// Initialize preset // Initialize preset
lzma_lzma_preset(&opt, LZMA_PRESET_DEFAULT); lzma_lzma_preset(&opt, 9);
lzma_filter filters[] = { lzma_filter filters[] = {
{ .id = LZMA_FILTER_LZMA2, .options = &opt }, { .id = LZMA_FILTER_LZMA2, .options = &opt },
{ .id = LZMA_VLI_UNKNOWN, .options = NULL }, { .id = LZMA_VLI_UNKNOWN, .options = NULL },
@@ -114,37 +93,24 @@ size_t lzma(int mode, int fd, const void *buf, size_t size) {
case 2: case 2:
ret = lzma_alone_encoder(&strm, &opt); ret = lzma_alone_encoder(&strm, &opt);
break; break;
default:
LOGE("Unsupported lzma mode!\n");
} }
if (ret != LZMA_OK) if (ret != LZMA_OK)
LOGE("Unable to init lzma stream\n"); LOGE("Unable to init lzma stream\n");
do { strm.next_in = buf;
strm.next_in = buf + pos; strm.avail_in = size;
if (pos + BUFSIZ >= size) {
strm.avail_in = size - pos;
action = LZMA_FINISH;
} else {
strm.avail_in = BUFSIZ;
action = LZMA_RUN;
}
pos += strm.avail_in;
do { do {
strm.avail_out = BUFSIZ; strm.avail_out = CHUNK;
strm.next_out = out; strm.next_out = out;
ret = lzma_code(&strm, action); ret = lzma_code(&strm, LZMA_FINISH);
have = BUFSIZ - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0 && ret == LZMA_OK);
if (ret != LZMA_OK && ret != LZMA_STREAM_END) if (ret != LZMA_OK && ret != LZMA_STREAM_END)
LOGE("LZMA error %d!\n", ret); LOGE("LZMA error %d!\n", ret);
have = CHUNK - strm.avail_out;
} while (pos < size); total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
lzma_end(&strm); lzma_end(&strm);
return total; return total;
@@ -168,8 +134,6 @@ size_t lz4(int mode, int fd, const void *buf, size_t size) {
case 1: case 1:
ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION); ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
break; break;
default:
LOGE("Unsupported lz4 mode!\n");
} }
if (LZ4F_isError(ret)) if (LZ4F_isError(ret))
@@ -268,7 +232,7 @@ size_t lz4(int mode, int fd, const void *buf, size_t size) {
// Mode: 0 = decode; 1 = encode // Mode: 0 = decode; 1 = encode
size_t bzip2(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 ret = 0, action, have, pos = 0, total = 0; size_t ret = 0, have, total = 0;
bz_stream strm; bz_stream strm;
char out[CHUNK]; char out[CHUNK];
@@ -283,23 +247,13 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
case 1: case 1:
ret = BZ2_bzCompressInit(&strm, 9, 0, 0); ret = BZ2_bzCompressInit(&strm, 9, 0, 0);
break; break;
default:
LOGE("Unsupported bzip2 mode!\n");
} }
if (ret != BZ_OK) if (ret != BZ_OK)
LOGE("Unable to init bzlib stream\n"); LOGE("Unable to init bzlib stream\n");
do { strm.next_in = (void *) buf;
strm.next_in = (char *) buf + pos; strm.avail_in = size;
if (pos + CHUNK >= size) {
strm.avail_in = size - pos;
action = BZ_FINISH;
} else {
strm.avail_in = CHUNK;
action = BZ_RUN;
}
pos += strm.avail_in;
do { do {
strm.avail_out = CHUNK; strm.avail_out = CHUNK;
@@ -309,17 +263,13 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
ret = BZ2_bzDecompress(&strm); ret = BZ2_bzDecompress(&strm);
break; break;
case 1: case 1:
ret = BZ2_bzCompress(&strm, action); ret = BZ2_bzCompress(&strm, BZ_FINISH);
break; break;
} }
have = CHUNK - strm.avail_out; have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have); total += xwrite(fd, out, have);
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
} while(pos < size);
switch(mode) { switch(mode) {
case 0: case 0:
BZ2_bzDecompressEnd(&strm); BZ2_bzDecompressEnd(&strm);
@@ -331,13 +281,14 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
return total; return total;
} }
#define LZ4_LEGACY_BLOCKSIZE 0x800000
// Mode: 0 = decode; 1 = encode // Mode: 0 = decode; 1 = encode
size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) { size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
size_t pos = 0, total = 0; size_t pos = 0;
int have; int have;
char *out; char *out;
unsigned block_size, insize; unsigned block_size, insize, total = 0;
unsigned char block_size_le[4];
switch(mode) { switch(mode) {
case 0: case 0:
@@ -350,22 +301,17 @@ size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
// Write magic // Write magic
total += xwrite(fd, "\x02\x21\x4c\x18", 4); total += xwrite(fd, "\x02\x21\x4c\x18", 4);
break; break;
default:
LOGE("Unsupported lz4_legacy mode!\n");
} }
do { do {
const char *buff = buf;
switch(mode) { switch(mode) {
case 0: case 0:
block_size = buff[pos]; // Read block size
block_size += (buff[pos + 1]<<8); block_size = *(unsigned *)(buf + pos);
block_size += (buff[pos + 2]<<16);
block_size += ((unsigned)buff[pos + 3])<<24;
pos += 4; pos += 4;
if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE)) if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE))
LOGE("lz4_legacy block size too large!\n"); goto done;
have = LZ4_decompress_safe((const char*) (buf + pos), out, block_size, LZ4_LEGACY_BLOCKSIZE); have = LZ4_decompress_safe(buf + pos, out, block_size, LZ4_LEGACY_BLOCKSIZE);
if (have < 0) if (have < 0)
LOGE("Cannot decode lz4_legacy block\n"); LOGE("Cannot decode lz4_legacy block\n");
pos += block_size; pos += block_size;
@@ -375,21 +321,24 @@ size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
insize = size - pos; insize = size - pos;
else else
insize = LZ4_LEGACY_BLOCKSIZE; insize = LZ4_LEGACY_BLOCKSIZE;
have = LZ4_compress_default((const char*) (buf + pos), out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE)); have = LZ4_compress_HC(buf + pos, out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE), 9);
if (have == 0) if (have == 0)
LOGE("lz4_legacy compression error\n"); LOGE("lz4_legacy compression error\n");
pos += insize; pos += insize;
block_size_le[0] = have & 0xff; // Write block size
block_size_le[1] = (have >> 8) & 0xff; total += xwrite(fd, &have, sizeof(have));
block_size_le[2] = (have >> 16) & 0xff;
block_size_le[3] = (have >> 24) & 0xff;
total += xwrite(fd, block_size_le, 4);
break; break;
} }
// Write main data // Write main data
total += xwrite(fd, out, have); total += xwrite(fd, out, have);
} while(pos < size); } while(pos < size);
done:
if (mode == 1) {
// Append original size to output
unsigned uncomp = size;
xwrite(fd, &uncomp, sizeof(uncomp));
}
free(out); free(out);
return total; return total;
} }
@@ -414,7 +363,6 @@ long long decomp(file_t type, int to, const void *from, size_t size) {
} }
} }
// Output will be to.ext
long long comp(file_t type, int to, const void *from, size_t size) { long long comp(file_t type, int to, const void *from, size_t size) {
switch (type) { switch (type) {
case GZIP: case GZIP:
@@ -440,59 +388,67 @@ long long comp(file_t type, int to, const void *from, size_t size) {
*/ */
void decomp_file(char *from, const char *to) { void decomp_file(char *from, const char *to) {
int ok = 1; int strip = 1;
void *file; void *file;
size_t size; size_t size = 0;
if (strcmp(from, "-") == 0)
stream_full_read(STDIN_FILENO, &file, &size);
else
mmap_ro(from, &file, &size); mmap_ro(from, &file, &size);
file_t type = check_type(file); file_t type = check_type(file);
char *ext; char *ext;
ext = strrchr(from, '.'); ext = strrchr(from, '.');
if (ext == NULL) if (to == NULL)
LOGE("Bad filename extention\n"); to = from;
if (ext != NULL) {
// File type and extension should match // Strip out a matched file extension
switch (type) { switch (type) {
case GZIP: case GZIP:
if (strcmp(ext, ".gz") != 0) if (strcmp(ext, ".gz") != 0)
ok = 0; strip = 0;
break; break;
case XZ: case XZ:
if (strcmp(ext, ".xz") != 0) if (strcmp(ext, ".xz") != 0)
ok = 0; strip = 0;
break; break;
case LZMA: case LZMA:
if (strcmp(ext, ".lzma") != 0) if (strcmp(ext, ".lzma") != 0)
ok = 0; strip = 0;
break; break;
case BZIP2: case BZIP2:
if (strcmp(ext, ".bz2") != 0) if (strcmp(ext, ".bz2") != 0)
ok = 0; strip = 0;
break; break;
case LZ4_LEGACY: case LZ4_LEGACY:
case LZ4: case LZ4:
if (strcmp(ext, ".lz4") != 0) if (strcmp(ext, ".lz4") != 0)
ok = 0; strip = 0;
break; break;
default: default:
LOGE("Provided file \'%s\' is not a supported archive format\n", from); LOGE("Provided file \'%s\' is not a supported archive format\n", from);
} }
if (ok) { if (strip)
// If all match, strip out the suffix
if (!to) {
*ext = '\0'; *ext = '\0';
to = from;
} }
int fd = open_new(to);
fprintf(stderr, "Decompressing to [%s]\n\n", to); 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); decomp(type, fd, file, size);
close(fd); close(fd);
if (to == from) { if (to == from && ext != NULL) {
*ext = '.'; *ext = '.';
unlink(from); unlink(from);
} }
} else { if (strcmp(from, "-") == 0)
LOGE("Bad filename extention \'%s\'\n", ext); free(file);
} else
munmap(file, size); munmap(file, size);
} }
@@ -526,17 +482,31 @@ void comp_file(const char *method, const char *from, const char *to) {
} }
void *file; void *file;
size_t size; size_t size;
mmap_ro(from, &file, &size); if (strcmp(from, "-") == 0)
if (!to) stream_full_read(STDIN_FILENO, &file, &size);
snprintf(dest, sizeof(dest), "%s.%s", from, ext);
else 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); strcpy(dest, to);
fprintf(stderr, "Compressing to [%s]\n\n", dest); int fd;
int fd = open_new(dest); if (strcmp(dest, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = creat(dest, 0644);
fprintf(stderr, "Compressing to [%s]\n", dest);
}
comp(type, fd, file, size); comp(type, fd, file, size);
close(fd); close(fd);
if (strcmp(from, "-") == 0)
free(file);
else
munmap(file, size); munmap(file, size);
if (!to) if (to == NULL)
unlink(from); unlink(from);
} }

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

@@ -0,0 +1,102 @@
#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 = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
found |= patch_verity(&value, &value_size, patch);
}
}
}
}
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

@@ -24,9 +24,9 @@ void hexpatch(const char *image, const char *from, const char *to) {
patch = xmalloc(patchsize); patch = xmalloc(patchsize);
hex2byte(from, pattern); hex2byte(from, pattern);
hex2byte(to, patch); 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) { 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); memset(file + i, 0, patternsize);
memcpy(file + i, patch, patchsize); memcpy(file + i, patch, patchsize);
i += patternsize - 1; i += patternsize - 1;

View File

@@ -3,26 +3,25 @@
#include <sys/types.h> #include <sys/types.h>
#include "logging.h"
#include "bootimg.h" #include "bootimg.h"
#define KERNEL_FILE "kernel" #define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio" #define RAMDISK_FILE "ramdisk.cpio"
#define SECOND_FILE "second" #define SECOND_FILE "second"
#define EXTRA_FILE "extra"
#define DTB_FILE "dtb" #define DTB_FILE "dtb"
#define NEW_BOOT "new-boot.img" #define NEW_BOOT "new-boot.img"
#define str(a) #a
#define xstr(a) str(a)
// Main entries // Main entries
void unpack(const char *image); void unpack(const char *image);
void repack(const char* orig_image, const char* out_image); void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to); void hexpatch(const char *image, const char *from, const char *to);
int parse_img(void *orig, size_t size, boot_img *boot); int parse_img(const char *image, boot_img *boot);
int cpio_commands(const char *command, int argc, char *argv[]); int cpio_commands(int argc, char *argv[]);
void comp_file(const char *method, const char *from, const char *to); void comp_file(const char *method, const char *from, const char *to);
void decomp_file(char *from, const char *to); void decomp_file(char *from, const char *to);
void dtb_patch(const char *file); int dtb_commands(const char *cmd, int argc, char *argv[]);
// Compressions // Compressions
size_t gzip(int mode, int fd, const void *buf, size_t size); size_t gzip(int mode, int fd, const void *buf, size_t size);
@@ -33,15 +32,4 @@ 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 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); long long decomp(file_t type, int to, const void *from, size_t size);
// Utils
extern void mmap_ro(const char *filename, void **buf, size_t *size);
extern void mmap_rw(const char *filename, void **buf, size_t *size);
extern void write_zero(int fd, size_t size);
extern void mem_align(size_t *pos, size_t align);
extern void file_align(int fd, size_t align, int out);
extern int open_new(const char *filename);
extern void *patch_init_rc(char *data, uint32_t *size);
extern int check_verity_pattern(const char *s);
extern int check_encryption_pattern(const char *s);
#endif #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

@@ -3,10 +3,6 @@
#include "bootimg.h" #include "bootimg.h"
#include "types.h" #include "types.h"
char *SUP_LIST[] = { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL };
char *SUP_EXT_LIST[] = { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL };
file_t SUP_TYPE_LIST[] = { GZIP, XZ, LZMA, BZIP2, LZ4, LZ4_LEGACY, 0 };
file_t check_type(const void *buf) { file_t check_type(const void *buf) {
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) { if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
return CHROMEOS; return CHROMEOS;

View File

@@ -18,6 +18,8 @@ typedef enum {
DTB DTB
} file_t; } file_t;
#define COMPRESSED(type) (type >= GZIP && type <= LZ4_LEGACY)
#define CHROMEOS_MAGIC "CHROMEOS" #define CHROMEOS_MAGIC "CHROMEOS"
#define ELF32_MAGIC "\x7f""ELF\x01" #define ELF32_MAGIC "\x7f""ELF\x01"
#define ELF64_MAGIC "\x7f""ELF\x02" #define ELF64_MAGIC "\x7f""ELF\x02"
@@ -31,9 +33,8 @@ typedef enum {
#define DTB_MAGIC "\xd0\x0d\xfe\xed" #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 LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
extern char *SUP_LIST[]; #define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
extern char *SUP_EXT_LIST[]; #define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })
extern file_t SUP_TYPE_LIST[];
file_t check_type(const void *buf); file_t check_type(const void *buf);
void get_type_name(file_t type, char *name); void get_type_name(file_t type, char *name);

View File

@@ -4,10 +4,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <sys/mount.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <selinux/selinux.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")) { if (strstr(name, "magisk")) {
deleteprop(name, 0); deleteprop2(name, 0);
} }
} }
@@ -69,42 +67,6 @@ void clean_magisk_props() {
getprop_all(rm_magisk_prop); 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) { int add_list(char *proc) {
if (!hideEnabled) { if (!hideEnabled) {
free(proc); free(proc);
@@ -223,7 +185,6 @@ int destroy_list() {
} }
void add_hide_list(int client) { void add_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client); char *proc = read_string(client);
// ack // ack
write_int(client, add_list(proc)); write_int(client, add_list(proc));
@@ -231,7 +192,6 @@ void add_hide_list(int client) {
} }
void rm_hide_list(int client) { void rm_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client); char *proc = read_string(client);
// ack // ack
write_int(client, rm_list(proc)); write_int(client, rm_list(proc));
@@ -239,7 +199,6 @@ void rm_hide_list(int client) {
} }
void ls_hide_list(int client) { void ls_hide_list(int client) {
err_handler = do_nothing;
if (!hideEnabled) { if (!hideEnabled) {
write_int(client, HIDE_NOT_ENABLED); write_int(client, HIDE_NOT_ENABLED);
return; return;

View File

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

View File

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

View File

@@ -19,36 +19,34 @@
#include "utils.h" #include "utils.h"
#include "magiskhide.h" #include "magiskhide.h"
static int zygote_num;
static char init_ns[32], zygote_ns[2][32], cache_block[256]; static char init_ns[32], zygote_ns[2][32], cache_block[256];
static int log_pid, log_fd, target_pid, has_cache = 1; static int hide_queue = 0, zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
static char *buffer;
// Workaround for the lack of pthread_cancel // Workaround for the lack of pthread_cancel
static void quit_pthread(int sig) { static void term_thread(int sig) {
err_handler = do_nothing;
LOGD("proc_monitor: running cleanup\n"); LOGD("proc_monitor: running cleanup\n");
destroy_list(); destroy_list();
free(buffer);
hideEnabled = 0; hideEnabled = 0;
// Kill the logging if needed // Unregister listener
if (log_pid > 0) { log_events[HIDE_EVENT].fd = -1;
kill(log_pid, SIGTERM); close(pipefd[0]);
waitpid(log_pid, NULL, 0); close(pipefd[1]);
close(log_fd); pipefd[0] = pipefd[1] = -1;
}
// Resume process if possible
if (target_pid > 0)
kill(target_pid, SIGCONT);
pthread_mutex_destroy(&hide_lock); pthread_mutex_destroy(&hide_lock);
pthread_mutex_destroy(&file_lock); pthread_mutex_destroy(&file_lock);
LOGD("proc_monitor: terminating...\n"); LOGD("proc_monitor: terminating...\n");
pthread_exit(NULL); pthread_exit(NULL);
} }
static void proc_monitor_err() { static void hide_done(int sig) {
LOGD("proc_monitor: error occured, stopping magiskhide services\n"); --hide_queue;
quit_pthread(SIGUSR1); if (hide_queue == 0) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xsymlink(DATABIN, "/data/magisk");
xsymlink(MAINIMG, "/data/magisk.img");
xsymlink(MOUNTPOINT, "/magisk");
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
} }
static int read_namespace(const int pid, char* target, const size_t size) { static int read_namespace(const int pid, char* target, const size_t size) {
@@ -76,27 +74,20 @@ static void lazy_unmount(const char* mountpoint) {
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint); LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
} }
static void hide_daemon_err() { static void hide_daemon(int pid, int ppid) {
LOGD("hide_daemon: error occured, stopping magiskhide services\n");
_exit(-1);
}
static void hide_daemon(int pid) {
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid); LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
// When an error occurs, report its failure to main process strcpy(argv0, "hide_daemon");
err_handler = hide_daemon_err;
char *line; char *line, buffer[PATH_MAX];
struct vector mount_list; struct vector mount_list;
manage_selinux(); manage_selinux();
relink_sbin();
clean_magisk_props(); clean_magisk_props();
if (switch_mnt_ns(pid)) if (switch_mnt_ns(pid))
return; goto exit;
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid); snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
vec_init(&mount_list); vec_init(&mount_list);
file_to_vector(buffer, &mount_list); file_to_vector(buffer, &mount_list);
@@ -122,9 +113,9 @@ static void hide_daemon(int pid) {
} }
} }
// Unmount dummy skeletons, /sbin links, and mirrors // Unmount dummy skeletons, /sbin links
vec_for_each(&mount_list, line) { vec_for_each(&mount_list, line) {
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin") || strstr(line, MIRRDIR)) { if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) {
sscanf(line, "%*s %4096s", buffer); sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer); lazy_unmount(buffer);
} }
@@ -133,7 +124,7 @@ static void hide_daemon(int pid) {
vec_destroy(&mount_list); vec_destroy(&mount_list);
// Re-read mount infos // Re-read mount infos
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid); snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
vec_init(&mount_list); vec_init(&mount_list);
file_to_vector(buffer, &mount_list); file_to_vector(buffer, &mount_list);
@@ -146,28 +137,39 @@ static void hide_daemon(int pid) {
free(line); free(line);
} }
// Free uo memory exit:
// Send resume signal
kill(pid, SIGCONT);
// Free up memory
vec_destroy(&mount_list); vec_destroy(&mount_list);
// Wait a while and link it back
sleep(10);
kill(ppid, HIDE_DONE);
_exit(0);
} }
void proc_monitor() { 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 // Register the cancel signal
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_handler = quit_pthread; act.sa_handler = term_thread;
sigaction(SIGUSR1, &act, NULL); sigaction(TERM_THREAD, &act, NULL);
act.sa_handler = hide_done;
sigaction(HIDE_DONE, &act, NULL);
// The error handler should stop magiskhide services
err_handler = proc_monitor_err;
log_pid = target_pid = -1;
buffer = xmalloc(PATH_MAX);
cache_block[0] = '\0'; cache_block[0] = '\0';
// Get the mount namespace of init // Get the mount namespace of init
if (read_namespace(1, init_ns, 32)) { if (read_namespace(1, init_ns, 32)) {
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n"); LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
proc_monitor_err(); term_thread(TERM_THREAD);
} }
LOGI("proc_monitor: init ns=%s\n", init_ns); LOGI("proc_monitor: init ns=%s\n", init_ns);
@@ -189,20 +191,19 @@ void proc_monitor() {
break; break;
} }
while (1) { // Register our listener to logcat monitor
// Clear previous logcat buffer xpipe2(pipefd, O_CLOEXEC);
exec_command_sync("logcat", "-b", "events", "-c", NULL); log_events[HIDE_EVENT].fd = pipefd[1];
// Monitor am_proc_start for (char *log, *line;; free(log)) {
log_fd = -1; if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-v", "raw", "-s", "am_proc_start", NULL); /* It might be interrupted */
log = NULL;
if (log_pid < 0) continue; continue;
if (kill(log_pid, 0)) continue; }
char *ss = strchr(log, '[');
while(fdgets(buffer, PATH_MAX, log_fd)) {
int pid, ret, comma = 0; int pid, ret, comma = 0;
char *pos = buffer, *line, processName[256]; char *pos = ss, processName[256], ns[32];
while(1) { while(1) {
pos = strchr(pos, ','); pos = strchr(pos, ',');
@@ -213,25 +214,22 @@ void proc_monitor() {
} }
if (comma == 6) if (comma == 6)
ret = sscanf(buffer, "[%*d %d %*d %*d %256s", &pid, processName); ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
else else
ret = sscanf(buffer, "[%*d %d %*d %256s", &pid, processName); ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
if(ret != 2) if(ret != 2)
continue; continue;
ret = 0;
// Critical region // Critical region
pthread_mutex_lock(&hide_lock); pthread_mutex_lock(&hide_lock);
vec_for_each(hide_list, line) { vec_for_each(hide_list, line) {
if (strcmp(processName, line) == 0) { if (strcmp(processName, line) == 0) {
target_pid = pid;
while(1) { while(1) {
ret = 1; ret = 1;
for (int i = 0; i < zygote_num; ++i) { for (int i = 0; i < zygote_num; ++i) {
read_namespace(target_pid, buffer, 32); read_namespace(pid, ns, sizeof(ns));
if (strcmp(buffer, zygote_ns[i]) == 0) { if (strcmp(ns, zygote_ns[i]) == 0) {
usleep(50); usleep(50);
ret = 0; ret = 0;
break; break;
@@ -241,43 +239,29 @@ void proc_monitor() {
} }
// Send pause signal ASAP // Send pause signal ASAP
if (kill(target_pid, SIGSTOP) == -1) continue; if (kill(pid, SIGSTOP) == -1) continue;
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, target_pid, buffer); 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 * The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts * We have to fork a new process, setns, then do the unmounts
*/ */
int hide_pid = fork(); int selfpid = getpid();
switch(hide_pid) { if (fork_dont_care() == 0)
case -1: hide_daemon(pid, selfpid);
PLOGE("fork");
return;
case 0:
hide_daemon(target_pid);
_exit(0);
default:
break;
}
// Wait till the unmount process is done
waitpid(hide_pid, &ret, 0);
if (WEXITSTATUS(ret))
quit_pthread(SIGUSR1);
// All done, send resume signal
kill(target_pid, SIGCONT);
target_pid = -1;
break; break;
} }
} }
pthread_mutex_unlock(&hide_lock); pthread_mutex_unlock(&hide_lock);
} }
// For some reason it went here, restart logging
kill(log_pid, SIGTERM);
waitpid(log_pid, NULL, 0);
close(log_fd);
}
} }

1
core/jni/magiskpolicy Submodule

Submodule core/jni/magiskpolicy added at edab891427

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 <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#include <stdio.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <new> #include <new>
@@ -48,6 +50,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
#undef XATTR_CREATE
#undef XATTR_REPLACE
#include <sys/xattr.h> #include <sys/xattr.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_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 kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2; // current 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() { static void detect_protocol_version() {
char value[PROP_VALUE_MAX]; 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 __system_property_serial2(const prop_info* pi) {
uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire); uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
while (SERIAL_DIRTY(serial)) { 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); serial = load_const_atomic(&pi->serial, memory_order_acquire);
} }
return serial; 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. * 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. * 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 __system_property_read_callback2(const prop_info *pi,
void (*callback)(void* cookie, const char *name, const char *value, uint32_t serial), 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 * 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. * 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) int __system_property_foreach2(void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
__INTRODUCED_IN(19);
/* /*
* Waits for the specific system property identified by `pi` to be updated * 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, bool __system_property_wait2(const prop_info* pi,
uint32_t old_serial, uint32_t old_serial,
uint32_t* new_serial_ptr, uint32_t* new_serial_ptr,
const struct timespec* relative_timeout) const struct timespec* relative_timeout);
__INTRODUCED_IN(26);
/* Deprecated. In Android O and above, there's no limit on property name length. */ /* Deprecated. In Android O and above, there's no limit on property name length. */
#define PROP_NAME_MAX 32 #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 <unistd.h>
#include <libgen.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/mount.h> #include <sys/mount.h>
@@ -49,22 +50,17 @@ static char *loopsetup(const char *img) {
} }
int create_img(const char *img, int size) { int create_img(const char *img, int size) {
if (size == 128) /* WTF...? */
size = 132;
unlink(img); unlink(img);
LOGI("Create %s with size %dM\n", img, size); LOGI("Create %s with size %dM\n", img, size);
// Create a temp file with the file contexts int ret;
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);
char buffer[16]; char buffer[16];
snprintf(buffer, sizeof(buffer), "%dM", size); 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) if (ret < 0)
return 1; return 1;
unlink(filename);
return ret; return ret;
} }
@@ -103,7 +99,7 @@ int resize_img(const char *img, int size) {
if (e2fsck(img)) if (e2fsck(img))
return 1; return 1;
char buffer[128]; char buffer[128];
int pid, status, fd = -1; int pid, fd = -1, used, total;
snprintf(buffer, sizeof(buffer), "%dM", size); snprintf(buffer, sizeof(buffer), "%dM", size);
pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL); pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL);
if (pid < 0) if (pid < 0)
@@ -111,17 +107,41 @@ int resize_img(const char *img, int size) {
while (fdgets(buffer, sizeof(buffer), fd)) while (fdgets(buffer, sizeof(buffer), fd))
LOGD("magisk_img: %s", buffer); LOGD("magisk_img: %s", buffer);
close(fd); close(fd);
waitpid(pid, &status, 0); waitpid(pid, NULL, 0);
return WEXITSTATUS(status);
// Double check our image size
get_img_size(img, &used, &total);
if (total != size) {
// Sammy crap occurs or resize2fs failed, lets create a new image!
char *dir = dirname(img);
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
create_img(buffer, size);
char *s_loop, *t_loop;
s_loop = mount_image(img, SOURCE_TMP);
if (s_loop == NULL) return 1;
t_loop = mount_image(buffer, TARGET_TMP);
if (t_loop == NULL) return 1;
cp_afc(SOURCE_TMP, TARGET_TMP);
umount_image(SOURCE_TMP, s_loop);
umount_image(TARGET_TMP, t_loop);
rmdir(SOURCE_TMP);
rmdir(TARGET_TMP);
free(s_loop);
free(t_loop);
rename(buffer, img);
}
return 0;
} }
char *mount_image(const char *img, const char *target) { char *mount_image(const char *img, const char *target) {
if (access(img, F_OK) == -1) if (access(img, F_OK) == -1)
return NULL; return NULL;
if (access(target, F_OK) == -1) { if (access(target, F_OK) == -1) {
if (xmkdir(target, 0755) == -1) { if (xmkdir_p(target, 0755) == -1) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL); xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdir(target, 0755); xmkdir_p(target, 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL); 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) { int merge_img(const char *source, const char *target) {
if (access(source, F_OK) == -1) if (access(source, F_OK) == -1)
return 0; return 0;
LOGI("* Merging %s -> %s\n", source, target);
if (access(target, F_OK) == -1) { if (access(target, F_OK) == -1) {
rename(source, target); xrename(source, target);
return 0; 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(source, &s_used, &s_total);
get_img_size(target, &t_used, &t_total); get_img_size(target, &t_used, &t_total);
n_total = round_size(s_used + t_used); n_total = round_size(s_used + t_used);
if (n_total != t_total) if (n_total > t_total)
resize_img(target, n_total); resize_img(target, n_total);
xmkdir(SOURCE_TMP, 0755); xmkdir(SOURCE_TMP, 0755);
@@ -170,7 +191,7 @@ int merge_img(const char *source, const char *target) {
DIR *dir; DIR *dir;
struct dirent *entry; struct dirent *entry;
if (!(dir = opendir(SOURCE_TMP))) if (!(dir = xopendir(SOURCE_TMP)))
return 1; return 1;
while ((entry = xreaddir(dir))) { while ((entry = xreaddir(dir))) {
if (entry->d_type == DT_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); snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
if (access(buffer, F_OK) == 0) { if (access(buffer, F_OK) == 0) {
LOGI("Upgrade module: %s\n", entry->d_name); LOGI("Upgrade module: %s\n", entry->d_name);
exec_command_sync(BBPATH "/rm", "-rf", buffer, NULL); rm_rf(buffer);
} else { } else {
LOGI("New module: %s\n", entry->d_name); LOGI("New module: %s\n", entry->d_name);
} }

View File

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

View File

@@ -6,24 +6,21 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h> #include <signal.h>
#include <errno.h>
#include <sched.h> #include <sched.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <selinux/selinux.h> #include <sys/inotify.h>
#include "logging.h" #include "logging.h"
#include "utils.h" #include "utils.h"
#include "resetprop.h"
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
unsigned get_shell_uid() { unsigned get_shell_uid() {
struct passwd* ppwd = getpwnam("shell"); struct passwd* ppwd = getpwnam("shell");
@@ -50,18 +47,22 @@ unsigned get_radio_uid() {
} }
int check_data() { int check_data() {
int ret = 0; struct vector v;
char buffer[4096]; vec_init(&v);
FILE *fp = xfopen("/proc/mounts", "r"); file_to_vector("/proc/mounts", &v);
while (fgets(buffer, sizeof(buffer), fp)) { char *line, *crypto;
if (strstr(buffer, " /data ")) { int mnt = 0;
if (strstr(buffer, "tmpfs") == NULL) vec_for_each(&v, line) {
ret = 1; if (strstr(line, " /data ")) {
if (strstr(line, "tmpfs") == NULL)
mnt = 1;
break; break;
} }
} }
fclose(fp); vec_deep_destroy(&v);
return ret; // /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!! */ /* All the string should be freed manually!! */
@@ -145,12 +146,12 @@ static void proc_name_filter(int pid) {
char buf[64]; char buf[64];
int fd; int fd;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); 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; return;
if (fdgets(buf, sizeof(buf), fd) == 0) { if (fdgets(buf, sizeof(buf), fd) == 0) {
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid); snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
close(fd); close(fd);
if ((fd = open(buf, O_RDONLY)) == -1) if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return; return;
fdgets(buf, sizeof(buf), fd); fdgets(buf, sizeof(buf), fd);
} }
@@ -167,32 +168,25 @@ void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
ps(proc_name_filter); ps(proc_name_filter);
} }
#define DEV_BLOCK "/dev/block"
void unlock_blocks() { void unlock_blocks() {
char path[PATH_MAX];
DIR *dir; DIR *dir;
struct dirent *entry; 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; return;
dir = xfdopendir(dev);
while((entry = readdir(dir))) { while((entry = readdir(dir))) {
if (entry->d_type == DT_BLK && if (entry->d_type == DT_BLK) {
strstr(entry->d_name, "ram") == NULL && if ((fd = openat(dev, entry->d_name, O_RDONLY)) < 0)
strstr(entry->d_name, "loop") == NULL) {
snprintf(path, sizeof(path), "%s/%s", DEV_BLOCK, entry->d_name);
if ((fd = xopen(path, O_RDONLY)) < 0)
continue; continue;
if (ioctl(fd, BLKROSET, &OFF) == -1) if (ioctl(fd, BLKROSET, &OFF) == -1)
PLOGE("unlock %s", path); PLOGE("unlock %s", entry->d_name);
close(fd); close(fd);
} }
} }
close(dev);
closedir(dir);
} }
void setup_sighandlers(void (*handler)(int)) { void setup_sighandlers(void (*handler)(int)) {
@@ -243,7 +237,7 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
envp = environ; envp = environ;
} }
int pid = fork(); int pid = xfork();
if (pid != 0) { if (pid != 0) {
if (fd && *fd < 0) { if (fd && *fd < 0) {
// Give the read end and close write end // Give the read end and close write end
@@ -255,9 +249,6 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
return pid; return pid;
} }
// Don't return to the daemon if anything goes wrong
err_handler = exit_proc;
if (fd) { if (fd) {
xdup2(writeEnd, STDOUT_FILENO); xdup2(writeEnd, STDOUT_FILENO);
if (err) xdup2(writeEnd, STDERR_FILENO); if (err) xdup2(writeEnd, STDERR_FILENO);
@@ -288,27 +279,6 @@ int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char
return pid; 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 bind_mount(const char *from, const char *to) {
int ret = xmount(from, to, NULL, MS_BIND, NULL); int ret = xmount(from, to, NULL, MS_BIND, NULL);
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
@@ -319,83 +289,6 @@ int bind_mount(const char *from, const char *to) {
return ret; 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) { void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred); socklen_t ucred_length = sizeof(*cred);
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length)) if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
@@ -415,3 +308,31 @@ int switch_mnt_ns(int pid) {
close(fd); close(fd);
return ret; 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);
}

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

@@ -0,0 +1,97 @@
#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];
}
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; 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 *)) { void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) {
if (v == NULL) return; 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 /* Will cleanup only the vector itself

View File

@@ -1,8 +1,8 @@
/* xwrap.c - wrappers around existing library functions. /* xwrap.c - wrappers around existing library functions.
* *
* Functions with the x prefix are wrappers that either succeed or kill the * Functions with the x prefix are wrappers that either succeed or log the
* program with an error message, but never return failure. They usually have * error message. They usually have the same arguments and return value
* the same arguments and return value as the function they wrap. * as the function they wrap.
* *
*/ */
@@ -14,6 +14,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <pthread.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -56,6 +57,14 @@ int xopen3(const char *pathname, int flags, mode_t mode) {
return fd; 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) { ssize_t xwrite(int fd, const void *buf, size_t count) {
int ret = write(fd, buf, count); int ret = write(fd, buf, count);
if (count != ret) { if (count != ret) {
@@ -106,6 +115,14 @@ DIR *xopendir(const char *name) {
return d; return d;
} }
DIR *xfdopendir(int fd) {
DIR *d = fdopendir(fd);
if (d == NULL) {
PLOGE("fdopendir");
}
return d;
}
struct dirent *xreaddir(DIR *dirp) { struct dirent *xreaddir(DIR *dirp) {
errno = 0; errno = 0;
struct dirent *e = readdir(dirp); 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 xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int ret = connect(sockfd, addr, addrlen); int ret = connect(sockfd, addr, addrlen);
if (ret == -1) { if (ret == -1) {
PLOGE("bind"); PLOGE("connect");
} }
return ret; return ret;
} }
@@ -250,7 +267,16 @@ ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
PLOGE("readlink %s", pathname); PLOGE("readlink %s", pathname);
} else { } else {
buf[ret] = '\0'; 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; return ret;
} }
@@ -289,14 +315,6 @@ int xumount2(const char *target, int flags) {
return ret; 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 xrename(const char *oldpath, const char *newpath) {
int ret = rename(oldpath, newpath); int ret = rename(oldpath, newpath);
if (ret == -1) { if (ret == -1) {
@@ -313,6 +331,22 @@ int xmkdir(const char *pathname, mode_t mode) {
return ret; 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, void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset) { int fd, off_t offset) {
void *ret = mmap(addr, length, prot, flags, fd, 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; return ret;
} }
int xmkdir_p(const char *pathname, mode_t mode) { pid_t xfork() {
int ret = mkdir_p(pathname, mode); int ret = fork();
if (ret == -1) { if (ret == -1) {
PLOGE("mkdir_p %s", pathname); PLOGE("fork");
} }
return ret; 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.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSProcessableByteArray;
@@ -19,29 +18,26 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import java.io.ByteArrayInputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Provider; import java.security.Provider;
import java.security.Security; import java.security.Security;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Locale; import java.util.Locale;
@@ -58,16 +54,56 @@ import java.util.regex.Pattern;
* Modified from from AOSP(Marshmallow) SignAPK.java * 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_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s"; 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. // bitmasks for which hash algorithms we need the manifest to include.
private static final int USE_SHA1 = 1; private static final int USE_SHA1 = 1;
private static final int USE_SHA256 = 2; 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 * Return one of USE_SHA1 or USE_SHA256 according to the signature
* algorithm specified in the cert. * algorithm specified in the cert.
@@ -104,42 +140,12 @@ public class ZipSigner {
private static Pattern stripPattern = private static Pattern stripPattern =
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" + Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
Pattern.quote(JarFile.MANIFEST_NAME) + ")$"); 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 * Add the hash(es) of every file to the manifest, creating it if
* necessary. * necessary.
*/ */
private static Manifest addDigestsToManifest(JarFile jar, int hashes) private static Manifest addDigestsToManifest(JarMap jar, int hashes)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
Manifest input = jar.getManifest(); Manifest input = jar.getManifest();
Manifest output = new Manifest(); Manifest output = new Manifest();
@@ -294,12 +300,12 @@ public class ZipSigner {
* reduce variation in the output file and make incremental OTAs * reduce variation in the output file and make incremental OTAs
* more efficient. * 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 { long timestamp, int alignment) throws IOException {
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int num; int num;
Map<String, Attributes> entries = manifest.getEntries(); Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<String>(entries.keySet()); ArrayList<String> names = new ArrayList<>(entries.keySet());
Collections.sort(names); Collections.sort(names);
boolean firstEntry = true; boolean firstEntry = true;
long offset = 0L; long offset = 0L;
@@ -368,15 +374,12 @@ public class ZipSigner {
// Used for signWholeFile // Used for signWholeFile
private static class CMSProcessableFile implements CMSTypedData { private static class CMSProcessableFile implements CMSTypedData {
private File file;
private ASN1ObjectIdentifier type; private ASN1ObjectIdentifier type;
private byte[] buffer; private RandomAccessFile file;
int bufferSize = 0;
CMSProcessableFile(File file) { CMSProcessableFile(File file) throws FileNotFoundException {
this.file = file; this.file = new RandomAccessFile(file, "r");
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()); type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
buffer = new byte[4096];
} }
@Override @Override
@@ -386,16 +389,13 @@ public class ZipSigner {
@Override @Override
public void write(OutputStream out) throws IOException, CMSException { public void write(OutputStream out) throws IOException, CMSException {
FileInputStream input = new FileInputStream(file); file.seek(0);
long len = file.length() - 2; int read;
while ((bufferSize = input.read(buffer)) > 0) { byte buffer[] = new byte[4096];
if (len <= bufferSize) { int len = (int) file.length() - 2;
out.write(buffer, 0, (int) len); while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
break; out.write(buffer, 0, read);
} else { len -= read;
out.write(buffer, 0, bufferSize);
}
len -= bufferSize;
} }
} }
@@ -404,8 +404,11 @@ public class ZipSigner {
return file; return file;
} }
byte[] getTail() { byte[] getTail() throws IOException {
return Arrays.copyOfRange(buffer, 0, bufferSize); 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); outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream); temp.writeTo(outputStream);
} }
private static void signFile(Manifest manifest, JarFile inputJar, private static void signFile(Manifest manifest, JarMap inputJar,
X509Certificate publicKey, PrivateKey privateKey, X509Certificate publicKey, PrivateKey privateKey,
JarOutputStream outputJar) JarOutputStream outputJar)
throws Exception { throws Exception {
@@ -496,72 +499,4 @@ public class ZipSigner {
writeSignatureBlock(new CMSProcessableByteArray(signedData), writeSignatureBlock(new CMSProcessableByteArray(signedData),
publicKey, privateKey, outputJar); 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);
}
}
}

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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# Resolve links: $0 may be a link DEFAULT_JVM_OPTS=""
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
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` 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. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@@ -48,7 +30,6 @@ die ( ) {
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false
case "`uname`" in case "`uname`" in
CYGWIN* ) CYGWIN* )
cygwin=true cygwin=true
@@ -59,11 +40,26 @@ case "`uname`" in
MINGW* ) MINGW* )
msys=true msys=true
;; ;;
NONSTOP* )
nonstop=true
;;
esac 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 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -89,7 +85,7 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n` MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -154,19 +150,11 @@ if $cygwin ; then
esac esac
fi fi
# Escape application args # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
save ( ) { function splitJvmOpts() {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done JVM_OPTS=("$@")
echo " "
} }
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 exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
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" "$@"

View File

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

View File

@@ -1,203 +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(0);
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);
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;
}

View File

@@ -1,45 +0,0 @@
/* log_monitor.c - New thread to monitor logcat
*
* Open a new thread to call logcat and get logs with tag "Magisk"
* Also, write the logs to a log file for debugging purpose
*
*/
#include <stdio.h>
#include <limits.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
static void *logger_thread(void *args) {
// Setup error handler
err_handler = exit_thread;
rename(LOGFILE, LASTLOG);
int log_fd, log_pid;
log_fd = xopen(LOGFILE, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
while (1) {
// Start logcat
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-v", "thread", "Magisk:I", "*:S", NULL);
if (log_pid > 0)
waitpid(log_pid, NULL, 0);
// For some reason it went here, clear buffer and restart
exec_command_sync("logcat", "-c", NULL);
}
// Should never be here, but well...
return NULL;
}
/* Start a new thread to monitor logcat and dump to logfile */
void monitor_logs() {
pthread_t thread;
xpthread_create(&thread, NULL, logger_thread, NULL);
pthread_detach(thread);
}

View File

@@ -1,40 +0,0 @@
LOCAL_PATH:= $(call my-dir)
EXTERNAL := $(LOCAL_PATH)
# libsqlite.so (stub)
include $(CLEAR_VARS)
LOCAL_MODULE:= libsqlite
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
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)
# libsepol, static library
include $(SELINUX_PATH)/libsepol/Android.mk
# Compression libraries for magiskboot
include $(COMPRESS_LIB)/zlib/Android.mk
include $(COMPRESS_LIB)/xz/src/liblzma/Android.mk
include $(COMPRESS_LIB)/lz4/lib/Android.mk
include $(COMPRESS_LIB)/bzip2/Android.mk

Submodule jni/external/busybox deleted from 90c9c4fd96

1
jni/external/dtc vendored

Submodule jni/external/dtc deleted from fe50bd1ecc

Submodule jni/external/selinux deleted from 2fefdfc40f

View File

@@ -1,36 +0,0 @@
/* magiskpolicy.h - Public API for policy patching
*/
#ifndef _MAGISKPOLICY_H
#define _MAGISKPOLICY_H
#include <stdlib.h>
#define ALL NULL
// policydb functions
int load_policydb(const char *filename);
int dump_policydb(const char *filename);
void destroy_policydb();
// Handy functions
int sepol_allow(char *s, char *t, char *c, char *p);
int sepol_deny(char *s, char *t, char *c, char *p);
int sepol_auditallow(char *s, char *t, char *c, char *p);
int sepol_auditdeny(char *s, char *t, char *c, char *p);
int sepol_typetrans(char *s, char *t, char *c, char *d, char *o);
int sepol_allowxperm(char *s, char *t, char *c, char *range);
int sepol_auditallowxperm(char *s, char *t, char *c, char *range);
int sepol_dontauditxperm(char *s, char *t, char *c, char *range);
int sepol_create(char *s);
int sepol_permissive(char *s);
int sepol_enforce(char *s);
int sepol_attradd(char *s, char *a);
int sepol_exists(char *source);
// Built in rules
void sepol_min_rules();
void sepol_med_rules();
void sepol_full_rules();
#endif

View File

@@ -1,102 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include "utils.h"
void mmap_ro(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDONLY);
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
*buf = xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
}
void mmap_rw(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDWR);
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
*buf = xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
}
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);
}
}
}
int open_new(const char *filename) {
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
void *patch_init_rc(char *data, uint32_t *size) {
int injected = 0;
char *new_data = xmalloc(*size + 23);
char *old_data = data;
uint32_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 {
fprintf(stderr, "Inject [import /init.magisk.rc] to [init.rc]\n");
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';
}
*size = pos;
return new_data;
}
int check_verity_pattern(const char *s) {
int pos = 0;
if (s[0] == ',') ++pos;
if (strncmp(s + pos, "verify", 6) != 0) return -1;
pos += 6;
if (s[pos] == '=') {
while (s[pos] != '\0' && s[pos] != ' ' && s[pos] != '\n' && s[pos] != ',') ++pos;
}
return pos;
}
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;
}

View File

@@ -1,507 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include "magiskboot.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;
}
static void cpio_free(cpio_entry *f) {
if (f) {
free(f->filename);
free(f->data);
free(f);
}
}
static void cpio_vec_insert(struct vector *v, cpio_entry *n) {
cpio_entry *f;
vec_for_each(v, f) {
if (strcmp(f->filename, n->filename) == 0) {
// Replace, then all is done
cpio_free(f);
vec_cur(v) = n;
return;
}
}
vec_push_back(v, n);
}
static int cpio_cmp(const void *a, const void *b) {
return strcmp((*(cpio_entry **) a)->filename, (*(cpio_entry **) b)->filename);
}
// Parse cpio file to a vector of cpio_entry
static void parse_cpio(const char *filename, struct vector *v) {
fprintf(stderr, "Loading cpio: [%s]\n\n", filename);
int fd = xopen(filename, O_RDONLY);
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);
f->namesize = x8u(header.namesize);
// f->check = x8u(header.check);
f->filename = xmalloc(f->namesize);
xxread(fd, f->filename, f->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 = malloc(f->filesize);
xxread(fd, f->data, f->filesize);
file_align(fd, 4, 0);
}
vec_push_back(v, f);
}
close(fd);
}
static void dump_cpio(const char *filename, struct vector *v) {
fprintf(stderr, "\nDump cpio: [%s]\n\n", filename);
int fd = open_new(filename);
unsigned inode = 300000;
char header[111];
// Sort by name
vec_sort(v, cpio_cmp);
cpio_entry *f;
vec_for_each(v, f) {
if (f->remove) continue;
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
inode++, // f->ino
f->mode,
f->uid,
f->gid,
1, // f->nlink
0, // f->mtime
f->filesize,
0, // f->devmajor
0, // f->devminor
0, // f->rdevmajor
0, // f->rdevminor
f->namesize,
0 // f->check
);
xwrite(fd, header, 110);
xwrite(fd, f->filename, f->namesize);
file_align(fd, 4, 1);
if (f->filesize) {
xwrite(fd, f->data, f->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);
}
static void cpio_vec_destroy(struct vector *v) {
// Free each cpio_entry
cpio_entry *f;
vec_for_each(v, f) {
cpio_free(f);
}
vec_destroy(v);
}
static void cpio_rm(int recursive, const char *entry, struct vector *v) {
cpio_entry *f;
vec_for_each(v, f) {
if (strncmp(f->filename, entry, strlen(entry)) == 0) {
char next = f->filename[strlen(entry)];
if ((recursive && next == '/') || next == '\0') {
if (!f->remove) {
fprintf(stderr, "Remove [%s]\n", f->filename);
f->remove = 1;
}
if (!recursive) return;
}
}
}
}
static void cpio_mkdir(mode_t mode, const char *entry, struct vector *v) {
cpio_entry *f = xcalloc(sizeof(*f), 1);
f->mode = S_IFDIR | mode;
f->namesize = strlen(entry) + 1;
f->filename = strdup(entry);
cpio_vec_insert(v, f);
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
}
static void cpio_add(mode_t mode, const char *entry, const char *filename, struct vector *v) {
int fd = xopen(filename, O_RDONLY);
cpio_entry *f = xcalloc(sizeof(*f), 1);
f->mode = S_IFREG | mode;
f->namesize = strlen(entry) + 1;
f->filename = strdup(entry);
f->filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
f->data = xmalloc(f->filesize);
xxread(fd, f->data, f->filesize);
close(fd);
cpio_vec_insert(v, f);
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
}
static void cpio_test(struct vector *v) {
#define STOCK_BOOT 0x0
#define MAGISK_PATCH 0x1
#define OTHER_PATCH 0x2
int ret = STOCK_BOOT;
cpio_entry *f;
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
const char *MAGISK_LIST[] = { "init.magisk.rc", "overlay/init.magisk.rc", NULL };
vec_for_each(v, f) {
for (int i = 0; OTHER_LIST[i]; ++i) {
if (strcmp(f->filename, OTHER_LIST[i]) == 0) {
ret |= OTHER_PATCH;
// Already find other files, abort
exit(OTHER_PATCH);
}
}
for (int i = 0; MAGISK_LIST[i]; ++i) {
if (strcmp(f->filename, MAGISK_LIST[i]) == 0)
ret = MAGISK_PATCH;
}
}
cpio_vec_destroy(v);
exit(ret);
}
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
cpio_entry *f;
int skip, write;
vec_for_each(v, f) {
if (strcmp(f->filename, "init.rc") == 0) {
void *new_data = patch_init_rc(f->data, &f->filesize);
free(f->data);
f->data = new_data;
} else {
if (!keepverity) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_verity_pattern(f->data + read)) > 0) {
fprintf(stderr, "Remove pattern [%.*s] in [%s]\n", skip, f->data + read, f->filename);
read += skip;
}
f->data[write] = f->data[read];
}
f->filesize = write;
} else if (strcmp(f->filename, "verity_key") == 0) {
fprintf(stderr, "Remove [verity_key]\n");
f->remove = 1;
}
}
if (!keepforceencrypt) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_encryption_pattern(f->data + read)) > 0) {
// assert(skip > 11)!
fprintf(stderr, "Replace pattern [%.*s] with [encryptable] in [%s]\n", skip, f->data + read, f->filename);
memcpy(f->data + write, "encryptable", 11);
write += 11;
read += skip;
}
f->data[write] = f->data[read];
}
f->filesize = write;
}
}
}
}
}
static void cpio_extract(const char *entry, const char *filename, struct vector *v) {
cpio_entry *f;
vec_for_each(v, f) {
if (strcmp(f->filename, entry) == 0 && S_ISREG(f->mode)) {
fprintf(stderr, "Extracting [%s] to [%s]\n\n", entry, filename);
int fd = open_new(filename);
xwrite(fd, f->data, f->filesize);
fchmod(fd, f->mode);
fchown(fd, f->uid, f->gid);
close(fd);
exit(0);
}
}
LOGE("Cannot find the file entry [%s]\n", entry);
}
static void cpio_backup(const char *orig, struct vector *v) {
struct vector o_body, *o = &o_body, bak;
cpio_entry *m, *n, *dir, *rem;
char buf[PATH_MAX];
int res, doBak;
dir = xcalloc(sizeof(*dir), 1);
rem = xcalloc(sizeof(*rem), 1);
vec_init(o);
vec_init(&bak);
// First push back the directory and the rmlist
vec_push_back(&bak, dir);
vec_push_back(&bak, rem);
parse_cpio(orig, o);
// Remove possible backups in original ramdisk
cpio_rm(1, ".backup", o);
cpio_rm(1, ".backup", v);
// Sort both vectors before comparing
vec_sort(v, cpio_cmp);
vec_sort(o, cpio_cmp);
// Init the directory and rmlist
dir->filename = strdup(".backup");
dir->namesize = strlen(dir->filename) + 1;
dir->mode = S_IFDIR;
rem->filename = strdup(".backup/.rmlist");
rem->namesize = strlen(rem->filename) + 1;
rem->mode = S_IFREG;
// Start comparing
size_t i = 0, j = 0;
while(i != vec_size(o) || j != vec_size(v)) {
doBak = 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;
doBak = 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!
doBak = 1;
fprintf(stderr, "Backup mismatch entry: ");
} else {
// Someting new in ramdisk, record in rem
++j;
if (n->remove) continue;
rem->data = xrealloc(rem->data, rem->filesize + n->namesize);
memcpy(rem->data + rem->filesize, n->filename, n->namesize);
rem->filesize += n->namesize;
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", n->filename);
}
if (doBak) {
m->namesize += 8;
m->filename = realloc(m->filename, m->namesize);
strcpy(buf, m->filename);
sprintf(m->filename, ".backup/%s", buf);
fprintf(stderr, "[%s] -> [%s]\n", buf, m->filename);
vec_push_back(&bak, m);
// NULL the original entry, so it won't be freed
vec_entry(o)[i - 1] = NULL;
}
}
// Add the backup files to the original ramdisk
vec_for_each(&bak, m) {
vec_push_back(v, m);
}
// Don't include if empty
if (rem->filesize == 0) {
rem->remove = 1;
if (bak.size == 2)
dir->remove = 1;
}
// Cleanup
cpio_vec_destroy(o);
}
static int cpio_restore(struct vector *v) {
cpio_entry *f, *n;
int ret = 1;
vec_for_each(v, f) {
if (strstr(f->filename, ".backup") != NULL) {
ret = 0;
f->remove = 1;
if (strcmp(f->filename, ".backup") == 0) continue;
if (strcmp(f->filename, ".backup/.rmlist") == 0) {
for (int pos = 0; pos < f->filesize; pos += strlen(f->data + pos) + 1)
cpio_rm(0, f->data + pos, v);
continue;
}
n = xcalloc(sizeof(*n), 1);
memcpy(n, f, sizeof(*f));
n->namesize -= 8;
n->filename = strdup(f->filename + 8);
n->data = f->data;
f->data = NULL;
n->remove = 0;
fprintf(stderr, "Restoring [%s] -> [%s]\n", f->filename, n->filename);
cpio_vec_insert(v, n);
}
}
// Some known stuff we can remove
cpio_rm(0, "sbin/magic_mask.sh", v);
cpio_rm(0, "init.magisk.rc", v);
cpio_rm(0, "magisk", v);
return ret;
}
static void cpio_stocksha1(struct vector *v) {
cpio_entry *f;
char sha1[41];
vec_for_each(v, f) {
if (strcmp(f->filename, "init.magisk.rc") == 0
|| strcmp(f->filename, "overlay/init.magisk.rc") == 0) {
for (char *pos = f->data; pos < f->data + f->filesize; pos = strchr(pos + 1, '\n') + 1) {
if (memcmp(pos, "# STOCKSHA1=", 12) == 0) {
pos += 12;
memcpy(sha1, pos, 40);
sha1[40] = '\0';
printf("%s\n", sha1);
return;
}
}
}
}
}
static void cpio_mv(struct vector *v, const char *from, const char *to) {
struct cpio_entry *f, *t;
vec_for_each(v, f) {
if (strcmp(f->filename, from) == 0) {
fprintf(stderr, "Move [%s] -> [%s]\n", from, to);
vec_for_each(v, t) {
if (strcmp(t->filename, to) == 0) {
t->remove = 1;
break;
}
}
free(f->filename);
f->namesize = strlen(to) + 1;
f->filename = strdup(to);
return;
}
}
fprintf(stderr, "Cannot find entry %s\n", from);
exit(1);
}
int cpio_commands(const char *command, int argc, char *argv[]) {
int recursive = 0, ret = 0;
command_t cmd;
char *incpio = argv[0];
++argv;
--argc;
if (strcmp(command, "test") == 0) {
cmd = TEST;
} else if (strcmp(command, "restore") == 0) {
cmd = RESTORE;
} else if (strcmp(command, "stocksha1") == 0) {
cmd = STOCKSHA1;
} else if (argc == 1 && strcmp(command, "backup") == 0) {
cmd = BACKUP;
} else if (argc > 0 && strcmp(command, "rm") == 0) {
cmd = RM;
if (argc == 2 && strcmp(argv[0], "-r") == 0) {
recursive = 1;
++argv;
--argc;
}
} else if (argc == 2 && strcmp(command, "mv") == 0) {
cmd = MV;
} else if (argc == 2 && strcmp(command, "patch") == 0) {
cmd = PATCH;
} else if (argc == 2 && strcmp(command, "extract") == 0) {
cmd = EXTRACT;
} else if (argc == 2 && strcmp(command, "mkdir") == 0) {
cmd = MKDIR;
} else if (argc == 3 && strcmp(command, "add") == 0) {
cmd = ADD;
} else {
cmd = NONE;
}
struct vector v;
vec_init(&v);
parse_cpio(incpio, &v);
switch(cmd) {
case TEST:
cpio_test(&v);
break;
case RESTORE:
ret = cpio_restore(&v);
break;
case STOCKSHA1:
cpio_stocksha1(&v);
return 0;
case BACKUP:
cpio_backup(argv[0], &v);
case RM:
cpio_rm(recursive, argv[0], &v);
break;
case PATCH:
cpio_patch(&v, strcmp(argv[0], "true") == 0, strcmp(argv[1], "true") == 0);
break;
case EXTRACT:
cpio_extract(argv[0], argv[1], &v);
break;
case MKDIR:
cpio_mkdir(strtoul(argv[0], NULL, 8), argv[1], &v);
break;
case ADD:
cpio_add(strtoul(argv[0], NULL, 8), argv[1], argv[2], &v);
break;
case MV:
cpio_mv(&v, argv[0], argv[1]);
break;
case NONE:
return 1;
}
dump_cpio(incpio, &v);
cpio_vec_destroy(&v);
exit(ret);
}

View File

@@ -1,56 +0,0 @@
#ifndef _CPIO_H_
#define _CPIO_H_
#include <stdint.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;
char *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;
typedef enum {
NONE,
RM,
MKDIR,
ADD,
MV,
EXTRACT,
TEST,
PATCH,
BACKUP,
RESTORE,
STOCKSHA1
} command_t;
#endif

View File

@@ -1,66 +0,0 @@
#include <libfdt.h>
#include <unistd.h>
#include <sys/mman.h>
#include "magiskboot.h"
#include "utils.h"
/* Left here for debugging */
// 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_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;
}
void dtb_patch(const char *file) {
size_t size ;
void *dtb, *fdt;
fprintf(stderr, "Loading dtbs from [%s]\n\n", file);
mmap_rw(file, &dtb, &size);
// Loop through all the dtbs
int dtb_num = 0, patched = 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));
int skip, value_size;
char *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
for (int i = 0; i < value_size; ++i) {
if ((skip = check_verity_pattern(value + i)) > 0) {
fprintf(stderr, "Remove pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
memcpy(value + i, value + i + skip, value_size - i - skip);
memset(value + value_size - skip, '\0', skip);
patched = 1;
}
}
}
}
}
}
fprintf(stderr, "\n");
munmap(dtb, size);
exit(!patched);
}

View File

@@ -1,137 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include "magiskboot.h"
#include "sha1.h"
/********************
Patch Boot Image
*********************/
static void usage(char *arg0) {
fprintf(stderr,
"Usage: %s <action> [args...]\n"
"\n"
"Supported actions:\n"
" --unpack <bootimg>\n"
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb) into the\n"
" current directory\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"
" if exists, or attempt to find ramdisk.cpio.[ext], and repack\n"
" directly with the compressed ramdisk file\n"
"\n"
" --hexpatch <file> <hexpattern1> <hexpattern2>\n"
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
"\n"
" --cpio-<cmd> <incpio> [flags...] [args...]\n"
" Do cpio related cmds to <incpio> (modifications are done directly)\n"
" Supported commands:\n"
" -rm [-r] <entry>\n"
" Remove entry from <incpio>, flag -r to remove recursively\n"
" -mkdir <mode> <entry>\n"
" Create directory as an <entry>\n"
" -add <mode> <entry> <infile>\n"
" Add <infile> as an <entry>; replaces <entry> if already exists\n"
" -mv <from-entry> <to-entry>\n"
" Move <from-entry> to <to-entry>\n"
" -extract <entry> <outfile>\n"
" Extract <entry> to <outfile>\n"
" -test \n"
" Return value: 0/stock 1/Magisk 2/other (e.g. phh, SuperSU)\n"
" -patch <KEEPVERITY> <KEEPFORCEENCRYPT>\n"
" Patch cpio for Magisk. KEEP**** are true/false values\n"
" -backup <origcpio>\n"
" Create ramdisk backups into <incpio> from <origcpio>\n"
" -restore\n"
" Restore ramdisk from ramdisk backup within <incpio>\n"
" -stocksha1\n"
" Get stock boot SHA1 recorded within <incpio>\n"
"\n"
" --dtb-patch <dtb>\n"
" Search for fstab in <dtb> and remove verity checks\n"
"\n"
" --compress[=method] <infile> [outfile]\n"
" Compress <infile> with [method] (default: gzip), optionally to [outfile]\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 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\n");
if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) {
fprintf(stderr, "Cleaning up...\n\n");
char name[PATH_MAX];
unlink(KERNEL_FILE);
unlink(RAMDISK_FILE);
unlink(RAMDISK_FILE ".raw");
unlink(SECOND_FILE);
unlink(DTB_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], "--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 && strcmp(argv[1], "--dtb-patch") == 0) {
dtb_patch(argv[2]);
} 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 && strncmp(argv[1], "--cpio", 6) == 0) {
char *command;
command = strchr(argv[1] + 2, '-');
if (command == NULL) usage(argv[0]);
else ++command;
if (cpio_commands(command, argc - 2, argv + 2)) usage(argv[0]);
} else {
usage(argv[0]);
}
return 0;
}

View File

@@ -1,405 +0,0 @@
/* magiskinit.c - Workaround for skip_initramfs devices
*
* This code has to be compiled statically to work properly.
*
* Magiskinit will mount sysfs, parse through uevent files to make the system block device,
* then it'll mount the system partition and clone rootfs except files under /system.
* Folders placed in "overlay" will then be overlayed to the root.
* Lastly, before giving control back to the real init, it'll patch the root files,
* extract (or compile if needed) sepolicy and patch it to load Magisk.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.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 <cil/cil.h>
#include "magiskpolicy.h"
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];
};
extern policydb_t *policydb;
extern void mmap_ro(const char *filename, void **buf, size_t *size);
extern void mmap_rw(const char *filename, void **buf, size_t *size);
extern void *patch_init_rc(char *data, uint32_t *size);
static void clone_dir(int src, int dest) {
struct dirent *entry;
DIR *dir;
int srcfd, destfd, newsrc, newdest;
struct stat st;
char buf[PATH_MAX];
ssize_t size;
dir = fdopendir(src);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
switch (entry->d_type) {
case DT_DIR:
mkdirat(dest, entry->d_name, st.st_mode & 0777);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
// Don't clone recursive if it's /system
if (strcmp(entry->d_name, "system") == 0)
continue;
newsrc = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
newdest = openat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
clone_dir(newsrc, newdest);
close(newsrc);
close(newdest);
break;
case DT_REG:
destfd = openat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, st.st_mode & 0777);
srcfd = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
sendfile(destfd, srcfd, 0, st.st_size);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
close(destfd);
close(srcfd);
break;
case DT_LNK:
size = readlinkat(src, entry->d_name, buf, sizeof(buf));
buf[size] = '\0';
symlinkat(buf, dest, entry->d_name);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW);
break;
}
}
}
static void mv_dir(int src, int dest) {
struct dirent *entry;
DIR *dir;
int newsrc, newdest;
struct stat st;
char buf[PATH_MAX];
ssize_t size;
dir = fdopendir(src);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
switch (entry->d_type) {
case DT_DIR:
mkdirat(dest, entry->d_name, st.st_mode & 0777);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
newsrc = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
newdest = openat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
mv_dir(newsrc, newdest);
close(newsrc);
close(newdest);
break;
case DT_REG:
renameat(src, entry->d_name, dest, entry->d_name);
fchmodat(dest, entry->d_name, st.st_mode & 0777, 0);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
break;
case DT_LNK:
size = readlinkat(src, entry->d_name, buf, sizeof(buf));
buf[size] = '\0';
symlinkat(buf, dest, entry->d_name);
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW);
break;
}
unlinkat(src, entry->d_name, AT_REMOVEDIR);
}
}
static void rm_rf(int path) {
struct dirent *entry;
int newfd;
DIR *dir = fdopendir(path);
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
switch (entry->d_type) {
case DT_DIR:
// Preserve overlay
if (strcmp(entry->d_name, "overlay") == 0)
continue;
newfd = openat(path, entry->d_name, O_RDONLY | O_CLOEXEC);
rm_rf(newfd);
close(newfd);
unlinkat(path, entry->d_name, AT_REMOVEDIR);
break;
default:
unlinkat(path, entry->d_name, 0);
break;
}
}
}
static void parse_cmdline(struct cmdline *cmd) {
char *tok;
char buffer[4096];
mkdir("/proc", 0555);
mount("proc", "/proc", "proc", 0, NULL);
int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
ssize_t size = read(fd, buffer, sizeof(buffer));
buffer[size] = '\0';
close(fd);
umount("/proc");
tok = strtok(buffer, " ");
cmd->skip_initramfs = 0;
cmd->slot[0] = '\0';
while (tok != NULL) {
if (strncmp(tok, "androidboot.slot_suffix", 23) == 0) {
sscanf(tok, "androidboot.slot_suffix=%s", cmd->slot);
} else if (strcmp(tok, "skip_initramfs") == 0) {
cmd->skip_initramfs = 1;
break;
}
tok = strtok(NULL, " ");
}
}
static void parse_device(struct device *dev, char *uevent) {
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");
}
}
static int setup_block(struct device *dev, const char *partname) {
char buffer[1024], path[128];
mkdir("/sys", 0755);
mount("sysfs", "/sys", "sysfs", 0, NULL);
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 patch_ramdisk() {
void *addr;
size_t size;
mmap_rw("/init", &addr, &size);
for (int i = 0; i < size; ++i) {
if (memcmp(addr + i, "/system/etc/selinux/plat_sepolicy.cil", 37) == 0) {
memcpy(addr + i, "/system/etc/selinux/plat_sepolicy.xxx", 37);
break;
}
}
munmap(addr, size);
mmap_rw("/init.rc", &addr, &size);
uint32_t new_size = size;
void *init_rc = patch_init_rc(addr, &new_size);
munmap(addr, size);
int fd = open("/init.rc", O_WRONLY | O_TRUNC | O_CLOEXEC);
write(fd, init_rc, new_size);
close(fd);
free(init_rc);
}
static int strend(const char *s1, const char *s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
return strcmp(s1 + l1 - l2, s2);
}
static void patch_sepolicy() {
DIR *dir;
struct dirent *entry;
char *sepolicy = NULL, path[128];
if (access("/system_root/sepolicy", R_OK) == 0)
sepolicy = "/system_root/sepolicy";
if (sepolicy == NULL && access("/vendor/etc/selinux/precompiled_sepolicy", R_OK) == 0) {
void *sys_sha = NULL, *ven_sha = NULL;
size_t sys_size = 0, ven_size = 0;
dir = opendir("/vendor/etc/selinux");
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
snprintf(path, sizeof(path), "/vendor/etc/selinux/%s", entry->d_name);
mmap_ro(path, &ven_sha, &ven_size);
break;
}
}
closedir(dir);
dir = opendir("/system/etc/selinux");
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
snprintf(path, sizeof(path), "/system/etc/selinux/%s", entry->d_name);
mmap_ro(path, &sys_sha, &sys_size);
break;
}
}
closedir(dir);
if (sys_size == ven_size && memcmp(sys_sha, ven_sha, sys_size) == 0)
sepolicy = "/vendor/etc/selinux/precompiled_sepolicy";
munmap(sys_sha, sys_size);
munmap(ven_sha, ven_size);
}
if (sepolicy) {
load_policydb(sepolicy);
} else {
// Compile cil
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_target_platform(db, SEPOL_TARGET_SELINUX);
cil_set_policy_version(db, POLICYDB_VERSION_XPERMS_IOCTL);
cil_set_attrs_expand_generated(db, 0);
mmap_ro("/system/etc/selinux/plat_sepolicy.cil", &addr, &size);
cil_add_file(db, "/system/etc/selinux/plat_sepolicy.cil", addr, size);
munmap(addr, size);
dir = opendir("/system/etc/selinux/mapping");
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), "/system/etc/selinux/mapping/%s", entry->d_name);
mmap_ro(path, &addr, &size);
cil_add_file(db, path, addr, size);
munmap(addr, size);
}
}
closedir(dir);
dir = opendir("/vendor/etc/selinux");
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), "/vendor/etc/selinux/%s", entry->d_name);
mmap_ro(path, &addr, &size);
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;
}
// Magisk patches
sepol_min_rules();
dump_policydb("/sepolicy");
destroy_policydb();
}
int main(int argc, char *argv[]) {
umask(0);
struct cmdline cmd;
parse_cmdline(&cmd);
if (cmd.skip_initramfs) {
// Normal boot mode
// Clear rootfs
int root = open("/", O_RDONLY | O_CLOEXEC);
rm_rf(root);
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);
clone_dir(system_root, root);
mount("/system_root/system", "/system", NULL, MS_BIND, NULL);
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
if (overlay > 0)
mv_dir(overlay, root);
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot);
setup_block(&dev, partname);
mount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
patch_ramdisk();
patch_sepolicy();
close(root);
close(system_root);
close(overlay);
rmdir("/overlay");
umount("/vendor");
} else {
// Recovery mode
// Revert original init binary
unlink("/init");
rename("/.backup/init", "/init");
}
execv("/init", argv);
return 0;
}

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