Compare commits

...

82 Commits
v14.3 ... 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
116 changed files with 5324 additions and 2437 deletions

12
.gitignore vendored
View File

@@ -1,8 +1,16 @@
obj/
libs/
out
*.zip
*.jks
*.apk
# Built binaries
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"]
path = jni/external/selinux
path = core/jni/external/selinux
url = https://github.com/topjohnwu/selinux.git
[submodule "jni/su"]
path = jni/su
path = core/jni/su
url = https://github.com/topjohnwu/MagiskSU.git
[submodule "jni/ndk-compression"]
path = jni/external/ndk-compression
url = https://github.com/topjohnwu/ndk-compression.git
[submodule "jni/magiskpolicy"]
path = jni/magiskpolicy
path = core/jni/magiskpolicy
url = https://github.com/topjohnwu/magiskpolicy.git
[submodule "MagiskManager"]
path = java
path = app
url = https://github.com/topjohnwu/MagiskManager.git
[submodule "jni/busybox"]
path = jni/external/busybox
path = core/jni/external/busybox
url = https://github.com/topjohnwu/ndk-busybox.git
[submodule "jni/external/dtc"]
path = jni/external/dtc
path = core/jni/external/dtc
url = https://github.com/dgibson/dtc
[submodule "jni/external/lz4"]
path = core/jni/external/lz4
url = https://github.com/lz4/lz4.git
[submodule "jni/external/bzip2"]
path = core/jni/external/bzip2
url = https://github.com/nemequ/bzip2.git
[submodule "jni/external/xz"]
path = core/jni/external/xz
url = https://github.com/xz-mirror/xz.git

View File

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

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
}

284
build.py
View File

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

3
core/.gitignore vendored Normal file
View File

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

20
core/build.gradle Normal file
View File

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

View File

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

View File

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

View File

@@ -11,8 +11,6 @@
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <selinux/selinux.h>
@@ -24,7 +22,6 @@
static char *buf, *buf2;
static struct vector module_list;
static int seperate_vendor = 0;
extern char **environ;
@@ -292,7 +289,7 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_DIR(child))
xmkdir(buf, 0755);
else if (IS_REG(child))
close(open_new(buf));
close(creat(buf, 0644));
// Links will be handled later
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
@@ -395,105 +392,16 @@ static void simple_mount(const char *path) {
* Miscellaneous *
*****************/
// A one time setup
static void daemon_init() {
LOGI("* Creating /sbin overlay");
DIR *dir;
struct dirent *entry;
int root, sbin;
// 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);
mount("tmpfs", "/sbin", "tmpfs", 0, NULL);
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
fchmod(sbin, 0755);
fsetfilecon(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);
}
xmkdir("/magisk", 0755);
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");
}
#define alt_img ((char *[]) \
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
static int prepare_img() {
// First merge images
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
LOGE("Image merge /data/magisk_merge.img -> " MAINIMG " failed!\n");
return 1;
// 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) {
@@ -507,8 +415,6 @@ static int prepare_img() {
if (magiskloop == NULL)
return 1;
vec_init(&module_list);
xmkdir(COREDIR, 0755);
xmkdir(COREDIR "/post-fs-data.d", 0755);
xmkdir(COREDIR "/service.d", 0755);
@@ -556,9 +462,6 @@ void fix_filecon() {
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
restorecon(dirfd, 0);
close(dirfd);
dirfd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
restorecon(dirfd, 1);
close(dirfd);
}
/****************
@@ -566,7 +469,7 @@ void fix_filecon() {
****************/
static void unblock_boot_process() {
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
pthread_exit(NULL);
}
@@ -595,7 +498,7 @@ void post_fs_data(int client) {
// ack
write_int(client, 0);
close(client);
if (!check_data())
if (!is_daemon_init && !check_data())
goto unblock;
// Start the debug log
@@ -606,25 +509,11 @@ void post_fs_data(int client) {
// Allocate buffer
if (buf == NULL) buf = xmalloc(PATH_MAX);
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// 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);
// Lazy.... use shell blob to match files
exec_command_sync("sh", "-c", "mv /data/magisk/stock_boot* /data", NULL);
}
vec_init(&module_list);
// Initialize
daemon_init();
if (!is_daemon_init)
daemon_init();
// uninstaller
if (access(UNINSTALLER, F_OK) == 0) {
@@ -741,7 +630,8 @@ void late_start(int client) {
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done
pthread_join(sepol_patch, NULL);
wait_till_exists(PATCHDONE);
unlink(PATCHDONE);
// Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n");

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

View File

@@ -6,7 +6,6 @@
*/
#include <stdio.h>
#include <limits.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -14,8 +13,35 @@
#include "magisk.h"
#include "utils.h"
int logcat_events[] = { -1, -1, -1 };
extern int is_restart;
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;
@@ -31,16 +57,23 @@ static void *logger_thread(void *args) {
// 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(logcat_events) / sizeof(int)); ++i) {
if (logcat_events[i] > 0) {
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(logcat_events[i], &s, sizeof(s));
xwrite(log_events[i].fd, &s, sizeof(s));
}
}
if (kill(log_pid, 0))
break;
}
// Clear buffer if restart required
// 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);
}
@@ -49,44 +82,36 @@ static void *logger_thread(void *args) {
}
static void *magisk_log_thread(void *args) {
int have_data = 0;
// Temp buffer for logs before we have data access
// Buffer logs before we have data access
struct vector logs;
vec_init(&logs);
FILE *log;
int pipefd[2];
if (xpipe2(pipefd, O_CLOEXEC) == -1)
return NULL;
// Register our listener
logcat_events[LOG_EVENT] = pipefd[1];
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)) {
char *ss;
if ((ss = strstr(line, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V')) {
if (!have_data) {
if ((have_data = check_data())) {
// Dump buffered logs to file
if (!is_restart)
rename(LOGFILE, LASTLOG);
log = xfopen(LOGFILE, "a");
setbuf(log, NULL);
char *tmp;
vec_for_each(&logs, tmp) {
fprintf(log, "%s", tmp);
free(tmp);
}
vec_destroy(&logs);
} else {
vec_push_back(&logs, strdup(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);
}
if (have_data)
fprintf(log, "%s", line);
fprintf(log, "%s", line);
}
}
return NULL;
@@ -102,13 +127,11 @@ static void *debug_magisk_log_thread(void *args) {
LOGD("log_monitor: debug log dumper start");
// Register our listener
logcat_events[DEBUG_EVENT] = pipefd[1];
log_events[DEBUG_EVENT].fd = pipefd[1];
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
fprintf(log, "%s", line);
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
char *ss;
if ((ss = strstr(line, "Magisk")))
fprintf(log, "%s", line);
}
return NULL;
}
@@ -140,10 +163,7 @@ void 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);
pthread_t thread;
// Start debug thread
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
pthread_detach(thread);
start_debug_log();
#endif
}

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include "utils.h"
#include "magisk.h"
@@ -11,11 +12,7 @@
char *argv0;
char *applet[] =
{ "su", "resetprop", "magiskpolicy", "supolicy", "magiskhide", NULL };
int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX];
@@ -43,8 +40,8 @@ static void usage() {
" -c print current binary version\n"
" -v print running daemon version\n"
" -V print running daemon version code\n"
" --list list all availible applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
" --list list all available applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
" --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
" --imgsize IMG report ext4 image used/total size\n"
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
@@ -53,9 +50,10 @@ static void usage() {
" --[init service] start init service\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
" --restorecon fix selinux context on Magisk files and folders\n"
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
"\n"
"Supported init services:\n"
" daemon post-fs, post-fs-data, service\n"
" daemon, post-fs, post-fs-data, service\n"
"\n"
"Supported applets:\n"
, argv0, argv0);
@@ -68,13 +66,10 @@ static void usage() {
int main(int argc, char *argv[]) {
argv0 = argv[0];
char * arg = strrchr(argv[0], '/');
if (arg) ++arg;
else arg = argv[0];
if (strcmp(arg, "magisk") == 0) {
if (strcmp(basename(argv[0]), "magisk") == 0) {
if (argc < 2) usage();
if (strcmp(argv[1], "-c") == 0) {
printf("%s\n", MAGISK_VER_STR);
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
int fd = connect_daemon();
@@ -144,9 +139,14 @@ int main(int argc, char *argv[]) {
} 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) {
// Start daemon, this process won't return
start_daemon();
if (xfork() == 0)
start_daemon();
return 0;
} else if (strcmp(argv[1], "--post-fs") == 0) {
int fd = connect_daemon();
write_int(fd, POST_FS);
@@ -163,16 +163,15 @@ int main(int argc, char *argv[]) {
// It's calling applets
--argc;
++argv;
arg = argv[0];
}
}
// Applets
for (int i = 0; applet[i]; ++i) {
if (strcmp(arg, applet[i]) == 0)
if (strcmp(basename(argv[0]), applet[i]) == 0)
return (*applet_main[i])(argc, argv);
}
fprintf(stderr, "%s: applet not found\n", arg);
fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
return 1;
}

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 <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "logging.h"
#include "utils.h"
#include "magisk.h"
/* Setup the address and return socket fd */
int setup_socket(struct sockaddr_un *sun) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
return fd;
}
/*
* Receive a file descriptor from a Unix socket.

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

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

1
core/jni/external/busybox vendored Submodule

1
core/jni/external/bzip2 vendored Submodule

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

1
core/jni/external/dtc vendored Submodule

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

View File

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

1
core/jni/external/lz4 vendored Submodule

Submodule core/jni/external/lz4 added at c10863b98e

1
core/jni/external/selinux vendored Submodule

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,9 +5,10 @@
#define _DAEMON_H_
#include <pthread.h>
#include <sys/un.h>
#include <sys/socket.h>
extern pthread_t sepol_patch;
extern int is_restart;
extern int is_daemon_init, seperate_vendor;
// Commands require connecting to daemon
typedef enum {
@@ -42,9 +43,11 @@ typedef enum {
void start_daemon();
int connect_daemon();
void auto_start_magiskhide();
void daemon_init();
// socket_trans.c
// socket.c
int setup_socket(struct sockaddr_un *sun);
int recv_fd(int sockfd);
void send_fd(int sockfd, int fd);
int read_int(int fd);
@@ -75,6 +78,6 @@ void ls_hide_list(int client);
* Superuser *
*************/
void su_daemon_receiver(int client);
void su_daemon_receiver(int client, struct ucred *credential);
#endif

View File

@@ -8,22 +8,42 @@
#include <errno.h>
#include <stdlib.h>
#define str(a) #a
#define xstr(a) str(a)
/**************
* No logging *
**************/
#define LOGD(...)
#define LOGI(...)
#define LOGW(...)
#define LOGE(...)
#define PLOGE(...)
/******************
* Daemon logging *
******************/
#ifdef IS_DAEMON
#undef LOGI
#undef LOGW
#undef LOGE
#undef PLOGE
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "Magisk"
#ifdef MAGISK_DEBUG
#undef LOGD
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#else
#define LOGD(...) {}
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
enum {
@@ -31,20 +51,36 @@ enum {
LOG_EVENT,
DEBUG_EVENT
};
extern int logcat_events[];
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();
#else // IS_DAEMON
#endif
/********************
* Tools Log & Exit *
********************/
#ifdef XWRAP_EXIT
#undef LOGE
#undef PLOGE
#include <stdio.h>
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
#endif // IS_DAEMON
#endif
#endif // _LOGGING_H_

View File

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

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

@@ -6,6 +6,7 @@
#include <stdio.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -69,7 +70,7 @@ pid_t xfork();
// misc.c
extern int quit_signals[];
#define quit_signals ((int []) { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 })
unsigned get_shell_uid();
unsigned get_system_uid();
@@ -85,10 +86,10 @@ void setup_sighandlers(void (*handler)(int));
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...);
int exec_command_sync(char *const argv0, ...);
int bind_mount(const char *from, const char *to);
int open_new(const char *filename);
void get_client_cred(int fd, struct ucred *cred);
int switch_mnt_ns(int pid);
int fork_dont_care();
void wait_till_exists(const char *target);
// file.c
@@ -101,6 +102,7 @@ struct file_attr {
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);
@@ -116,6 +118,15 @@ 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
@@ -131,4 +142,10 @@ void umount_image(const char *target, const char *device);
int merge_img(const char *source, const char *target);
void trim_img(const char *img);
// pattern.c
void patch_init_rc(void **buf, size_t *size);
int patch_verity(void **buf, uint32_t *size, int patch);
void patch_encryption(void **buf, uint32_t *size);
#endif

View File

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

View File

@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "bootimg.h"
@@ -7,8 +8,13 @@
#include "utils.h"
#include "logging.h"
#define INSUF_BLOCK_RET 2
#define CHROMEOS_RET 3
#define ELF32_RET 4
#define ELF64_RET 5
static void dump(void *buf, size_t size, const char *filename) {
int fd = open_new(filename);
int fd = creat(filename, 0644);
xwrite(fd, buf, size);
close(fd);
}
@@ -49,63 +55,63 @@ static void print_hdr(const boot_img_hdr *hdr) {
}
fprintf(stderr, "NAME [%s]\n", hdr->name);
fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline);
fprintf(stderr, "\n");
}
int parse_img(void *orig, size_t size, boot_img *boot) {
void *base, *end;
size_t pos = 0;
int ret = 0;
int parse_img(const char *image, boot_img *boot) {
memset(boot, 0, sizeof(*boot));
for(base = orig, end = orig + size; base < end; base += 256, size -= 256) {
switch (check_type(base)) {
int is_blk = mmap_ro(image, &boot->map_addr, &boot->map_size);
// Parse image
fprintf(stderr, "Parsing boot image: [%s]\n", image);
for (size_t pos = 0; pos < boot->map_size; pos += 256) {
switch (check_type(boot->map_addr + pos)) {
case CHROMEOS:
// The caller should know it's chromeos, as it needs additional signing
ret = 2;
boot->flags |= CHROMEOS_FLAG;
continue;
case ELF32:
exit(3);
exit(ELF32_RET);
case ELF64:
exit(4);
exit(ELF64_RET);
case AOSP:
// 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;
print_hdr(&boot->hdr);
boot->kernel = base + pos;
boot->kernel = boot->map_addr + pos;
pos += boot->hdr.kernel_size;
mem_align(&pos, boot->hdr.page_size);
boot->ramdisk = base + pos;
boot->ramdisk = boot->map_addr + pos;
pos += boot->hdr.ramdisk_size;
mem_align(&pos, boot->hdr.page_size);
if (boot->hdr.second_size) {
boot->second = base + pos;
boot->second = boot->map_addr + pos;
pos += boot->hdr.second_size;
mem_align(&pos, boot->hdr.page_size);
}
if (boot->hdr.extra_size) {
boot->extra = base + pos;
boot->extra = boot->map_addr + pos;
pos += boot->hdr.extra_size;
mem_align(&pos, boot->hdr.page_size);
}
if (pos < size) {
boot->tail = base + pos;
boot->tail_size = end - base - pos;
if (pos < boot->map_size) {
boot->tail = boot->map_addr + pos;
boot->tail_size = boot->map_size - pos;
}
// Search for dtb in kernel
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
for (uint32_t i = 0; i < boot->hdr.kernel_size; ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
boot->dtb = boot->kernel + i;
boot->dt_size = boot->hdr.kernel_size - i;
boot->hdr.kernel_size = i;
fprintf(stderr, "DTB [%d]\n", boot->dt_size);
fprintf(stderr, "DTB [%u]\n", boot->dt_size);
}
}
@@ -127,7 +133,7 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr));
boot->ramdisk += 512;
boot->hdr.ramdisk_size -= 512;
boot->ramdisk_type = check_type(boot->ramdisk + 512);
boot->ramdisk_type = check_type(boot->ramdisk);
}
char fmt[16];
@@ -136,31 +142,24 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
get_type_name(boot->ramdisk_type, 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:
continue;
}
}
LOGE("No boot image magic found!\n");
return 1;
}
void unpack(const char* image) {
size_t size;
void *orig;
mmap_ro(image, &orig, &size);
int fd;
boot_img boot;
// Parse image
fprintf(stderr, "Parsing boot image: [%s]\n\n", image);
int ret = parse_img(orig, size, &boot);
int ret = parse_img(image, &boot);
int fd;
// Dump kernel
if (COMPRESSED(boot.kernel_type)) {
fd = open_new(KERNEL_FILE);
fd = creat(KERNEL_FILE, 0644);
decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size);
close(fd);
} else {
@@ -174,7 +173,7 @@ void unpack(const char* image) {
// Dump ramdisk
if (COMPRESSED(boot.ramdisk_type)) {
fd = open_new(RAMDISK_FILE);
fd = creat(RAMDISK_FILE, 0644);
decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size);
close(fd);
} else {
@@ -192,29 +191,23 @@ void unpack(const char* image) {
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
}
munmap(orig, size);
munmap(boot.map_addr, boot.map_size);
exit(ret);
}
void repack(const char* orig_image, const char* out_image) {
size_t size;
void *orig;
boot_img boot;
// There are possible two MTK headers
size_t mtk_kernel_off, mtk_ramdisk_off;
// Load original image
mmap_ro(orig_image, &orig, &size);
// Parse original image
fprintf(stderr, "Parsing boot image: [%s]\n\n", orig_image);
parse_img(orig, size, &boot);
parse_img(orig_image, &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
int fd = open_new(out_image);
int fd = creat(out_image, 0644);
// Skip a page for header
write_zero(fd, boot.hdr.page_size);
@@ -308,7 +301,6 @@ void repack(const char* orig_image, const char* out_image) {
// Print new image info
print_hdr(&boot.hdr);
munmap(orig, size);
munmap(boot.map_addr, boot.map_size);
close(fd);
}

View File

@@ -103,20 +103,25 @@ typedef struct mtk_hdr {
// Flags
#define MTK_KERNEL 0x1
#define MTK_RAMDISK 0x2
#define CHROMEOS_FLAG 0x4
typedef struct boot_img {
size_t map_size;
uint32_t dt_size;
size_t tail_size;
uint8_t flags;
file_t kernel_type, ramdisk_type;
boot_img_hdr hdr;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
void *map_addr;
void *kernel;
void *dtb;
uint32_t dt_size;
void *ramdisk;
void *second;
void *extra;
void *tail;
uint32_t tail_size;
uint32_t flags;
file_t kernel_type, ramdisk_type;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
} boot_img;
#endif

View File

@@ -1,4 +1,5 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <zlib.h>
@@ -12,18 +13,11 @@
#include "logging.h"
#include "utils.h"
#define windowBits 15
#define ZLIB_GZIP 16
#define memLevel 8
#define CHUNK 0x40000
#define LZ4_HEADER_SIZE 19
#define LZ4_FOOTER_SIZE 4
#define LZ4_LEGACY_BLOCKSIZE 0x800000
// Mode: 0 = decode; 1 = encode
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;
unsigned char out[CHUNK];
@@ -33,47 +27,35 @@ size_t gzip(int mode, int fd, const void *buf, size_t size) {
switch(mode) {
case 0:
ret = inflateInit2(&strm, windowBits | ZLIB_GZIP);
ret = inflateInit2(&strm, 15 | 16);
break;
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;
}
if (ret != Z_OK)
LOGE("Unable to init zlib stream\n");
strm.next_in = (void *) buf;
strm.avail_in = size;
do {
strm.next_in = buf + pos;
if (pos + CHUNK >= size) {
strm.avail_in = size - pos;
flush = Z_FINISH;
} else {
strm.avail_in = CHUNK;
flush = Z_NO_FLUSH;
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = inflate(&strm, Z_FINISH);
break;
case 1:
ret = deflate(&strm, Z_FINISH);
break;
}
pos += strm.avail_in;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = inflate(&strm, flush);
break;
case 1:
ret = deflate(&strm, flush);
break;
}
if (ret == Z_STREAM_ERROR)
LOGE("Error when running gzip\n");
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
} while(pos < size);
if (ret == Z_STREAM_ERROR)
LOGE("Error when running gzip\n");
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
switch(mode) {
case 0:
@@ -86,15 +68,13 @@ size_t gzip(int mode, int fd, const void *buf, size_t size) {
return total;
}
// Mode: 0 = decode xz/lzma; 1 = encode xz; 2 = encode lzma
size_t lzma(int mode, int fd, const void *buf, size_t size) {
size_t have, pos = 0, total = 0;
size_t have, total = 0;
lzma_ret ret = 0;
lzma_stream strm = LZMA_STREAM_INIT;
lzma_options_lzma opt;
lzma_action action;
unsigned char out[BUFSIZ];
unsigned char out[CHUNK];
// Initialize preset
lzma_lzma_preset(&opt, 9);
@@ -119,29 +99,18 @@ size_t lzma(int mode, int fd, const void *buf, size_t size) {
if (ret != LZMA_OK)
LOGE("Unable to init lzma stream\n");
strm.next_in = buf;
strm.avail_in = size;
do {
strm.next_in = buf + pos;
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 {
strm.avail_out = BUFSIZ;
strm.next_out = out;
ret = lzma_code(&strm, action);
have = BUFSIZ - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0 && ret == LZMA_OK);
strm.avail_out = CHUNK;
strm.next_out = out;
ret = lzma_code(&strm, LZMA_FINISH);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
LOGE("LZMA error %d!\n", ret);
} while (pos < size);
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
lzma_end(&strm);
return total;
@@ -263,7 +232,7 @@ size_t lz4(int mode, int fd, const void *buf, size_t size) {
// Mode: 0 = decode; 1 = encode
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;
char out[CHUNK];
@@ -283,35 +252,23 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
if (ret != BZ_OK)
LOGE("Unable to init bzlib stream\n");
strm.next_in = (void *) buf;
strm.avail_in = size;
do {
strm.next_in = (char *) buf + pos;
if (pos + CHUNK >= size) {
strm.avail_in = size - pos;
action = BZ_FINISH;
} else {
strm.avail_in = CHUNK;
action = BZ_RUN;
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = BZ2_bzDecompress(&strm);
break;
case 1:
ret = BZ2_bzCompress(&strm, BZ_FINISH);
break;
}
pos += strm.avail_in;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
switch(mode) {
case 0:
ret = BZ2_bzDecompress(&strm);
break;
case 1:
ret = BZ2_bzCompress(&strm, action);
break;
}
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
} while(pos < size);
have = CHUNK - strm.avail_out;
total += xwrite(fd, out, have);
} while (strm.avail_out == 0);
switch(mode) {
case 0:
@@ -324,6 +281,8 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
return total;
}
#define LZ4_LEGACY_BLOCKSIZE 0x800000
// Mode: 0 = decode; 1 = encode
size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
size_t pos = 0;
@@ -404,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) {
switch (type) {
case GZIP:
@@ -430,60 +388,68 @@ long long comp(file_t type, int to, const void *from, size_t size) {
*/
void decomp_file(char *from, const char *to) {
int ok = 1;
int strip = 1;
void *file;
size_t size;
mmap_ro(from, &file, &size);
size_t size = 0;
if (strcmp(from, "-") == 0)
stream_full_read(STDIN_FILENO, &file, &size);
else
mmap_ro(from, &file, &size);
file_t type = check_type(file);
char *ext;
ext = strrchr(from, '.');
if (ext == NULL)
LOGE("Bad filename extention\n");
// File type and extension should match
switch (type) {
if (to == NULL)
to = from;
if (ext != NULL) {
// Strip out a matched file extension
switch (type) {
case GZIP:
if (strcmp(ext, ".gz") != 0)
ok = 0;
strip = 0;
break;
case XZ:
if (strcmp(ext, ".xz") != 0)
ok = 0;
strip = 0;
break;
case LZMA:
if (strcmp(ext, ".lzma") != 0)
ok = 0;
strip = 0;
break;
case BZIP2:
if (strcmp(ext, ".bz2") != 0)
ok = 0;
strip = 0;
break;
case LZ4_LEGACY:
case LZ4:
if (strcmp(ext, ".lz4") != 0)
ok = 0;
strip = 0;
break;
default:
LOGE("Provided file \'%s\' is not a supported archive format\n", from);
}
if (ok) {
// If all match, strip out the suffix
if (!to) {
}
if (strip)
*ext = '\0';
to = from;
}
int fd = open_new(to);
fprintf(stderr, "Decompressing to [%s]\n\n", to);
decomp(type, fd, file, size);
close(fd);
if (to == from) {
*ext = '.';
unlink(from);
}
} else {
LOGE("Bad filename extention \'%s\'\n", ext);
}
munmap(file, size);
int fd;
if (strcmp(to, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = creat(to, 0644);
fprintf(stderr, "Decompressing to [%s]\n", to);
}
decomp(type, fd, file, size);
close(fd);
if (to == from && ext != NULL) {
*ext = '.';
unlink(from);
}
if (strcmp(from, "-") == 0)
free(file);
else
munmap(file, size);
}
void comp_file(const char *method, const char *from, const char *to) {
@@ -516,17 +482,31 @@ void comp_file(const char *method, const char *from, const char *to) {
}
void *file;
size_t size;
mmap_ro(from, &file, &size);
if (!to)
snprintf(dest, sizeof(dest), "%s.%s", from, ext);
if (strcmp(from, "-") == 0)
stream_full_read(STDIN_FILENO, &file, &size);
else
mmap_ro(from, &file, &size);
if (to == NULL) {
if (strcmp(from, "-") == 0)
strcpy(dest, "-");
else
snprintf(dest, sizeof(dest), "%s.%s", from, ext);
} else
strcpy(dest, to);
fprintf(stderr, "Compressing to [%s]\n\n", dest);
int fd = open_new(dest);
int fd;
if (strcmp(dest, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = creat(dest, 0644);
fprintf(stderr, "Compressing to [%s]\n", dest);
}
comp(type, fd, file, size);
close(fd);
munmap(file, size);
if (!to)
if (strcmp(from, "-") == 0)
free(file);
else
munmap(file, size);
if (to == NULL)
unlink(from);
}

View File

@@ -5,11 +5,24 @@
#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));
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);
}
}
@@ -26,7 +39,7 @@ static int find_fstab(const void *fdt, int parent) {
return -1;
}
void dtb_print(const char *file) {
static void dtb_dump(const char *file) {
size_t size ;
void *dtb, *fdt;
fprintf(stderr, "Loading dtbs from [%s]\n", file);
@@ -36,7 +49,7 @@ void dtb_print(const char *file) {
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
fdt = dtb + i;
fprintf(stderr, "\nPrinting dtb.%04d\n\n", dtb_num++);
fprintf(stderr, "Dumping dtb.%04d\n", dtb_num++);
print_subnode(fdt, 0, 0);
}
}
@@ -45,13 +58,16 @@ void dtb_print(const char *file) {
exit(0);
}
void dtb_patch(const char *file) {
static void dtb_patch(const char *file, int patch) {
size_t size ;
void *dtb, *fdt;
fprintf(stderr, "Loading dtbs from [%s]\n\n", file);
mmap_rw(file, &dtb, &size);
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, patched = 0;
int dtb_num = 0, found = 0;
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
fdt = dtb + i;
@@ -61,23 +77,26 @@ void dtb_patch(const char *file) {
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;
}
}
uint32_t value_size;
void *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
found |= patch_verity(&value, &value_size, patch);
}
}
}
}
fprintf(stderr, "\n");
munmap(dtb, size);
exit(!patched);
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

@@ -26,7 +26,7 @@ void hexpatch(const char *image, const char *from, const char *to) {
hex2byte(to, patch);
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
if (memcmp(file + i, pattern, patternsize) == 0) {
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to);
memset(file + i, 0, patternsize);
memcpy(file + i, patch, patchsize);
i += patternsize - 1;

View File

@@ -3,6 +3,7 @@
#include <sys/types.h>
#include "logging.h"
#include "bootimg.h"
#define KERNEL_FILE "kernel"
@@ -12,19 +13,15 @@
#define DTB_FILE "dtb"
#define NEW_BOOT "new-boot.img"
#define str(a) #a
#define xstr(a) str(a)
// Main entries
void unpack(const char *image);
void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to);
int parse_img(void *orig, size_t size, boot_img *boot);
int cpio_commands(const char *command, int argc, char *argv[]);
int parse_img(const char *image, boot_img *boot);
int cpio_commands(int argc, char *argv[]);
void comp_file(const char *method, const char *from, const char *to);
void decomp_file(char *from, const char *to);
void dtb_print(const char *file);
void dtb_patch(const char *file);
int dtb_commands(const char *cmd, int argc, char *argv[]);
// Compressions
size_t gzip(int mode, int fd, const void *buf, size_t size);
@@ -35,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 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

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 "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) {
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
return CHROMEOS;

View File

@@ -33,9 +33,8 @@ typedef enum {
#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"
extern char *SUP_LIST[];
extern char *SUP_EXT_LIST[];
extern file_t SUP_TYPE_LIST[];
#define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
#define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })
file_t check_type(const void *buf);
void get_type_name(file_t type, char *name);

View File

@@ -4,10 +4,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <selinux/selinux.h>

View File

@@ -100,7 +100,7 @@ void stop_magiskhide(int client) {
setprop(MAGISKHIDE_PROP, "0");
// Remove without actually removing persist props
deleteprop2(MAGISKHIDE_PROP, 0);
pthread_kill(proc_monitor_thread, SIGUSR1);
pthread_kill(proc_monitor_thread, TERM_THREAD);
write_int(client, DAEMON_SUCCESS);
close(client);

View File

@@ -3,6 +3,9 @@
#include <pthread.h>
#define TERM_THREAD SIGUSR1
#define HIDE_DONE SIGUSR2
// Kill process
void kill_proc(int pid);

View File

@@ -20,15 +20,15 @@
#include "magiskhide.h"
static char init_ns[32], zygote_ns[2][32], cache_block[256];
static int zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
static int hide_queue = 0, zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
// Workaround for the lack of pthread_cancel
static void quit_pthread(int sig) {
static void term_thread(int sig) {
LOGD("proc_monitor: running cleanup\n");
destroy_list();
hideEnabled = 0;
// Unregister listener
logcat_events[HIDE_EVENT] = -1;
log_events[HIDE_EVENT].fd = -1;
close(pipefd[0]);
close(pipefd[1]);
pipefd[0] = pipefd[1] = -1;
@@ -38,6 +38,17 @@ static void quit_pthread(int sig) {
pthread_exit(NULL);
}
static void hide_done(int sig) {
--hide_queue;
if (hide_queue == 0) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xsymlink(DATABIN, "/data/magisk");
xsymlink(MAINIMG, "/data/magisk.img");
xsymlink(MOUNTPOINT, "/magisk");
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
}
static int read_namespace(const int pid, char* target, const size_t size) {
char path[32];
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
@@ -63,8 +74,9 @@ static void lazy_unmount(const char* mountpoint) {
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
}
static void hide_daemon(int pid) {
static void hide_daemon(int pid, int ppid) {
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
strcpy(argv0, "hide_daemon");
char *line, buffer[PATH_MAX];
struct vector mount_list;
@@ -101,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) {
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);
lazy_unmount(buffer);
}
@@ -130,22 +142,34 @@ exit:
kill(pid, SIGCONT);
// Free up memory
vec_destroy(&mount_list);
// Wait a while and link it back
sleep(10);
kill(ppid, HIDE_DONE);
_exit(0);
}
void proc_monitor() {
// Unblock user signals
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, TERM_THREAD);
sigaddset(&block_set, HIDE_DONE);
pthread_sigmask(SIG_UNBLOCK, &block_set, NULL);
// Register the cancel signal
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = quit_pthread;
sigaction(SIGUSR1, &act, NULL);
act.sa_handler = term_thread;
sigaction(TERM_THREAD, &act, NULL);
act.sa_handler = hide_done;
sigaction(HIDE_DONE, &act, NULL);
cache_block[0] = '\0';
// Get the mount namespace of init
if (read_namespace(1, init_ns, 32)) {
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
quit_pthread(SIGUSR1);
term_thread(TERM_THREAD);
}
LOGI("proc_monitor: init ns=%s\n", init_ns);
@@ -169,65 +193,75 @@ void proc_monitor() {
// Register our listener to logcat monitor
xpipe2(pipefd, O_CLOEXEC);
logcat_events[HIDE_EVENT] = pipefd[1];
log_events[HIDE_EVENT].fd = pipefd[1];
for (char *log, *line; xxread(pipefd[0], &log, sizeof(log)) > 0; free(log)) {
char *ss;
if ((ss = strstr(log, "am_proc_start")) && (ss = strchr(ss, '['))) {
int pid, ret, comma = 0;
char *pos = ss, processName[256], ns[32];
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++comma;
}
if (comma == 6)
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
else
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
if(ret != 2)
continue;
ret = 0;
// Critical region
pthread_mutex_lock(&hide_lock);
vec_for_each(hide_list, line) {
if (strcmp(processName, line) == 0) {
while(1) {
ret = 1;
for (int i = 0; i < zygote_num; ++i) {
read_namespace(pid, ns, sizeof(ns));
if (strcmp(ns, zygote_ns[i]) == 0) {
usleep(50);
ret = 0;
break;
}
}
if (ret) break;
}
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1) continue;
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
if (fork_dont_care() == 0)
hide_daemon(pid);
break;
}
}
pthread_mutex_unlock(&hide_lock);
for (char *log, *line;; free(log)) {
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
/* It might be interrupted */
log = NULL;
continue;
}
char *ss = strchr(log, '[');
int pid, ret, comma = 0;
char *pos = ss, processName[256], ns[32];
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++comma;
}
if (comma == 6)
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
else
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
if(ret != 2)
continue;
// Critical region
pthread_mutex_lock(&hide_lock);
vec_for_each(hide_list, line) {
if (strcmp(processName, line) == 0) {
while(1) {
ret = 1;
for (int i = 0; i < zygote_num; ++i) {
read_namespace(pid, ns, sizeof(ns));
if (strcmp(ns, zygote_ns[i]) == 0) {
usleep(50);
ret = 0;
break;
}
}
if (ret) break;
}
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1) continue;
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
unlink("/magisk");
unlink("/data/magisk");
unlink("/data/magisk.img");
unlink(MAGISKRC);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
++hide_queue;
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
int selfpid = getpid();
if (fork_dont_care() == 0)
hide_daemon(pid, selfpid);
break;
}
}
pthread_mutex_unlock(&hide_lock);
}
}

1
core/jni/magiskpolicy Submodule

Submodule core/jni/magiskpolicy added at edab891427

View File

@@ -209,7 +209,7 @@ public:
vector prop_list;
static int prop_cmp(const void *p1, const void *p2) {
return strcmp((*((property **) p1))->name, (*((property **) p2))->name);
return strcmp(((property *) p1)->name, ((property *) p2)->name);
}
static void print_all_props_cb(const char *name, const char *value) {

View File

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

View File

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

1
core/jni/su Submodule

Submodule core/jni/su added at ed5dd827e9

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

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

View File

@@ -4,21 +4,26 @@
#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>
#ifndef NO_SELINUX
#ifdef SELINUX
#include <selinux/selinux.h>
#endif
#include "utils.h"
char **excl_list = (char *[]) { NULL };
char **excl_list = NULL;
static int is_excl(const char *name) {
for (int i = 0; excl_list[i]; ++i) {
if (strcmp(name, excl_list[i]) == 0)
return 1;
}
if (excl_list)
for (int i = 0; excl_list[i]; ++i)
if (strcmp(name, excl_list[i]) == 0)
return 1;
return 0;
}
@@ -50,37 +55,50 @@ int mkdir_p(const char *pathname, mode_t mode) {
return 0;
}
void rm_rf(const char *path) {
int fd = xopen(path, O_RDONLY | O_CLOEXEC);
frm_rf(fd);
close(fd);
rmdir(path);
}
void frm_rf(int dirfd) {
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*)) {
struct dirent *entry;
int newfd;
DIR *dir = xfdopendir(dirfd);
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;
switch (entry->d_type) {
case DT_DIR:
if (entry->d_type == DT_DIR) {
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
frm_rf(newfd);
in_order_walk(newfd, callback);
close(newfd);
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
break;
default:
unlinkat(dirfd, entry->d_name, 0);
break;
}
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;
@@ -213,8 +231,8 @@ void clone_dir(int src, int dest) {
int getattr(const char *path, struct file_attr *a) {
if (xlstat(path, &a->st) == -1)
return -1;
#ifdef SELINUX
char *con = "";
#ifndef NO_SELINUX
if (lgetfilecon(path, &con) == -1)
return -1;
strcpy(a->con, con);
@@ -235,7 +253,7 @@ int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
}
int fgetattr(int fd, struct file_attr *a) {
#ifndef NO_SELINUX
#ifdef SELINUX
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return getattr(path, a);
@@ -252,7 +270,7 @@ int setattr(const char *path, struct file_attr *a) {
return -1;
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
return -1;
#ifndef NO_SELINUX
#ifdef SELINUX
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
return -1;
#endif
@@ -269,7 +287,7 @@ int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
}
int fsetattr(int fd, struct file_attr *a) {
#ifndef NO_SELINUX
#ifdef SELINUX
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return setattr(path, a);
@@ -294,7 +312,7 @@ void fclone_attr(const int sourcefd, const int targetfd) {
fsetattr(targetfd, &a);
}
#ifndef NO_SELINUX
#ifdef SELINUX
#define UNLABEL_CON "u:object_r:unlabeled:s0"
#define SYSTEM_CON "u:object_r:system_file:s0"
@@ -330,4 +348,100 @@ void restorecon(int dirfd, int force) {
}
}
#endif // NO_SELINUX
#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

@@ -50,6 +50,8 @@ static char *loopsetup(const char *img) {
}
int create_img(const char *img, int size) {
if (size == 128) /* WTF...? */
size = 132;
unlink(img);
LOGI("Create %s with size %dM\n", img, size);
int ret;
@@ -137,9 +139,9 @@ char *mount_image(const char *img, const char *target) {
if (access(img, F_OK) == -1)
return NULL;
if (access(target, F_OK) == -1) {
if (xmkdir(target, 0755) == -1) {
if (xmkdir_p(target, 0755) == -1) {
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdir(target, 0755);
xmkdir_p(target, 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
}
}

View File

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

View File

@@ -10,15 +10,17 @@
#include <pwd.h>
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include "logging.h"
#include "utils.h"
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
#include "resetprop.h"
unsigned get_shell_uid() {
struct passwd* ppwd = getpwnam("shell");
@@ -45,18 +47,22 @@ unsigned get_radio_uid() {
}
int check_data() {
int ret = 0;
char buffer[4096];
FILE *fp = xfopen("/proc/mounts", "r");
while (fgets(buffer, sizeof(buffer), fp)) {
if (strstr(buffer, " /data ")) {
if (strstr(buffer, "tmpfs") == NULL)
ret = 1;
struct vector v;
vec_init(&v);
file_to_vector("/proc/mounts", &v);
char *line, *crypto;
int mnt = 0;
vec_for_each(&v, line) {
if (strstr(line, " /data ")) {
if (strstr(line, "tmpfs") == NULL)
mnt = 1;
break;
}
}
fclose(fp);
return ret;
vec_deep_destroy(&v);
// /data is mounted and not tmpfs and data is unencrypted or vold is started
return mnt && (((crypto = getprop("ro.crypto.state")) && strcmp(crypto, "unencrypted") == 0)
|| getprop("init.svc.vold"));
}
/* All the string should be freed manually!! */
@@ -283,10 +289,6 @@ int bind_mount(const char *from, const char *to) {
return ret;
}
int open_new(const char *filename) {
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred);
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
@@ -317,3 +319,20 @@ int fork_dont_care() {
}
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;
}
static int (*cmp)(const void *, const void *);
static int vec_comp(const void *a, const void *b) {
void *aa = *((void **)a), *bb = *((void **)b);
if (aa == NULL && bb == NULL) return 0;
else if (aa == NULL) return 1;
else if (bb == NULL) return -1;
else return cmp ? cmp(aa, bb) : 0;
}
void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) {
if (v == NULL) return;
qsort(vec_entry(v), vec_size(v), sizeof(void*), compar);
cmp = compar;
qsort(vec_entry(v), vec_size(v), sizeof(void*), vec_comp);
void *e;
vec_for_each_r(v, e) {
if (e) break;
--vec_size(v);
}
}
/* Will cleanup only the vector itself

View File

@@ -159,7 +159,7 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int ret = connect(sockfd, addr, addrlen);
if (ret == -1) {
PLOGE("bind");
PLOGE("connect");
}
return ret;
}

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

@@ -0,0 +1,502 @@
package com.topjohnwu.crypto;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
/*
* Modified from from AOSP(Marshmallow) SignAPK.java
* */
public class SignAPK {
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
public static Provider sBouncyCastleProvider;
// bitmasks for which hash algorithms we need the manifest to include.
private static final int USE_SHA1 = 1;
private static final int USE_SHA256 = 2;
static {
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
}
public static void signZip(InputStream publicIn, InputStream privateIn,
JarMap input, File output, boolean minSign) throws Exception {
int alignment = 4;
BufferedOutputStream outputFile;
int hashes = 0;
X509Certificate publicKey = CryptoUtils.readPublicKey(publicIn);
hashes |= getDigestAlgorithm(publicKey);
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
PrivateKey privateKey = CryptoUtils.readPrivateKey(privateIn);
outputFile = new BufferedOutputStream(new FileOutputStream(output));
if (minSign) {
signWholeFile(input.getFile(), publicKey, privateKey, outputFile);
} else {
JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(input, hashes);
copyFiles(manifest, input, outputJar, timestamp, alignment);
signFile(manifest, input, publicKey, privateKey, outputJar);
outputJar.close();
}
input.close();
outputFile.close();
}
/**
* Return one of USE_SHA1 or USE_SHA256 according to the signature
* algorithm specified in the cert.
*/
private static int getDigestAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
if ("SHA1WITHRSA".equals(sigAlg) ||
"MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
return USE_SHA1;
} else if (sigAlg.startsWith("SHA256WITH")) {
return USE_SHA256;
} else {
throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
"\" in cert [" + cert.getSubjectDN());
}
}
/** Returns the expected signature algorithm for this key type. */
private static String getSignatureAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
if ("RSA".equalsIgnoreCase(keyType)) {
if (getDigestAlgorithm(cert) == USE_SHA256) {
return "SHA256withRSA";
} else {
return "SHA1withRSA";
}
} else if ("EC".equalsIgnoreCase(keyType)) {
return "SHA256withECDSA";
} else {
throw new IllegalArgumentException("unsupported key type: " + keyType);
}
}
// Files matching this pattern are not copied to the output.
private static Pattern stripPattern =
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
/**
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private static Manifest addDigestsToManifest(JarMap jar, int hashes)
throws IOException, GeneralSecurityException {
Manifest input = jar.getManifest();
Manifest output = new Manifest();
Attributes main = output.getMainAttributes();
if (input != null) {
main.putAll(input.getMainAttributes());
} else {
main.putValue("Manifest-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
}
MessageDigest md_sha1 = null;
MessageDigest md_sha256 = null;
if ((hashes & USE_SHA1) != 0) {
md_sha1 = MessageDigest.getInstance("SHA1");
}
if ((hashes & USE_SHA256) != 0) {
md_sha256 = MessageDigest.getInstance("SHA256");
}
byte[] buffer = new byte[4096];
int num;
// We sort the input entries by name, and add them to the
// output manifest in sorted order. We expect that the output
// map will be deterministic.
TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
JarEntry entry = e.nextElement();
byName.put(entry.getName(), entry);
}
for (JarEntry entry: byName.values()) {
String name = entry.getName();
if (!entry.isDirectory() &&
(stripPattern == null || !stripPattern.matcher(name).matches())) {
InputStream data = jar.getInputStream(entry);
while ((num = data.read(buffer)) > 0) {
if (md_sha1 != null) md_sha1.update(buffer, 0, num);
if (md_sha256 != null) md_sha256.update(buffer, 0, num);
}
Attributes attr = null;
if (input != null) attr = input.getAttributes(name);
attr = attr != null ? new Attributes(attr) : new Attributes();
if (md_sha1 != null) {
attr.putValue("SHA1-Digest",
new String(Base64.encode(md_sha1.digest()), "ASCII"));
}
if (md_sha256 != null) {
attr.putValue("SHA-256-Digest",
new String(Base64.encode(md_sha256.digest()), "ASCII"));
}
output.getEntries().put(name, attr);
}
}
return output;
}
/** Write to another stream and track how many bytes have been
* written.
*/
private static class CountOutputStream extends FilterOutputStream {
private int mCount;
public CountOutputStream(OutputStream out) {
super(out);
mCount = 0;
}
@Override
public void write(int b) throws IOException {
super.write(b);
mCount++;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
mCount += len;
}
public int size() {
return mCount;
}
}
/** Write a .SF file with a digest of the specified manifest. */
private static void writeSignatureFile(Manifest manifest, OutputStream out,
int hash)
throws IOException, GeneralSecurityException {
Manifest sf = new Manifest();
Attributes main = sf.getMainAttributes();
main.putValue("Signature-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
MessageDigest md = MessageDigest.getInstance(
hash == USE_SHA256 ? "SHA256" : "SHA1");
PrintStream print = new PrintStream(
new DigestOutputStream(new ByteArrayOutputStream(), md),
true, "UTF-8");
// Digest of the entire manifest
manifest.write(print);
print.flush();
main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
Map<String, Attributes> entries = manifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
// Digest of the manifest stanza for this entry.
print.print("Name: " + entry.getKey() + "\r\n");
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
}
print.print("\r\n");
print.flush();
Attributes sfAttr = new Attributes();
sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
sf.getEntries().put(entry.getKey(), sfAttr);
}
CountOutputStream cout = new CountOutputStream(out);
sf.write(cout);
// A bug in the java.util.jar implementation of Android platforms
// up to version 1.6 will cause a spurious IOException to be thrown
// if the length of the signature file is a multiple of 1024 bytes.
// As a workaround, add an extra CRLF in this case.
if ((cout.size() % 1024) == 0) {
cout.write('\r');
cout.write('\n');
}
}
/** Sign data and write the digital signature to 'out'. */
private static void writeSignatureBlock(
CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
OutputStream out)
throws IOException,
CertificateEncodingException,
OperatorCreationException,
CMSException {
ArrayList<X509Certificate> certList = new ArrayList<>(1);
certList.add(publicKey);
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
.setProvider(sBouncyCastleProvider)
.build(privateKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder()
.setProvider(sBouncyCastleProvider)
.build())
.setDirectSignature(true)
.build(signer, publicKey));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(data, false);
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
}
/**
* Copy all the files in a manifest from input to output. We set
* the modification times in the output to a fixed time, so as to
* reduce variation in the output file and make incremental OTAs
* more efficient.
*/
private static void copyFiles(Manifest manifest, JarMap in, JarOutputStream out,
long timestamp, int alignment) throws IOException {
byte[] buffer = new byte[4096];
int num;
Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<>(entries.keySet());
Collections.sort(names);
boolean firstEntry = true;
long offset = 0L;
// We do the copy in two passes -- first copying all the
// entries that are STORED, then copying all the entries that
// have any other compression flag (which in practice means
// DEFLATED). This groups all the stored entries together at
// the start of the file and makes it easier to do alignment
// on them (since only stored entries are aligned).
for (String name : names) {
JarEntry inEntry = in.getJarEntry(name);
JarEntry outEntry = null;
if (inEntry.getMethod() != JarEntry.STORED) continue;
// Preserve the STORED method of the input entry.
outEntry = new JarEntry(inEntry);
outEntry.setTime(timestamp);
// 'offset' is the offset into the file at which we expect
// the file data to begin. This is the value we need to
// make a multiple of 'alignement'.
offset += JarFile.LOCHDR + outEntry.getName().length();
if (firstEntry) {
// The first entry in a jar file has an extra field of
// four bytes that you can't get rid of; any extra
// data you specify in the JarEntry is appended to
// these forced four bytes. This is JAR_MAGIC in
// JarOutputStream; the bytes are 0xfeca0000.
offset += 4;
firstEntry = false;
}
if (alignment > 0 && (offset % alignment != 0)) {
// Set the "extra data" of the entry to between 1 and
// alignment-1 bytes, to make the file data begin at
// an aligned offset.
int needed = alignment - (int)(offset % alignment);
outEntry.setExtra(new byte[needed]);
offset += needed;
}
out.putNextEntry(outEntry);
InputStream data = in.getInputStream(inEntry);
while ((num = data.read(buffer)) > 0) {
out.write(buffer, 0, num);
offset += num;
}
out.flush();
}
// Copy all the non-STORED entries. We don't attempt to
// maintain the 'offset' variable past this point; we don't do
// alignment on these entries.
for (String name : names) {
JarEntry inEntry = in.getJarEntry(name);
JarEntry outEntry = null;
if (inEntry.getMethod() == JarEntry.STORED) continue;
// Create a new entry so that the compressed len is recomputed.
outEntry = new JarEntry(name);
outEntry.setTime(timestamp);
out.putNextEntry(outEntry);
InputStream data = in.getInputStream(inEntry);
while ((num = data.read(buffer)) > 0) {
out.write(buffer, 0, num);
}
out.flush();
}
}
// This class is to provide a file's content, but trimming out the last two bytes
// Used for signWholeFile
private static class CMSProcessableFile implements CMSTypedData {
private ASN1ObjectIdentifier type;
private RandomAccessFile file;
CMSProcessableFile(File file) throws FileNotFoundException {
this.file = new RandomAccessFile(file, "r");
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
}
@Override
public ASN1ObjectIdentifier getContentType() {
return type;
}
@Override
public void write(OutputStream out) throws IOException, CMSException {
file.seek(0);
int read;
byte buffer[] = new byte[4096];
int len = (int) file.length() - 2;
while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
out.write(buffer, 0, read);
len -= read;
}
}
@Override
public Object getContent() {
return file;
}
byte[] getTail() throws IOException {
byte tail[] = new byte[22];
file.seek(file.length() - 22);
file.readFully(tail);
return tail;
}
}
private static void signWholeFile(File input, X509Certificate publicKey,
PrivateKey privateKey, OutputStream outputStream)
throws Exception {
ByteArrayOutputStream temp = new ByteArrayOutputStream();
// put a readable message and a null char at the start of the
// archive comment, so that tools that display the comment
// (hopefully) show something sensible.
// TODO: anything more useful we can put in this message?
byte[] message = "signed by SignApk".getBytes("UTF-8");
temp.write(message);
temp.write(0);
CMSProcessableFile cmsFile = new CMSProcessableFile(input);
writeSignatureBlock(cmsFile, publicKey, privateKey, temp);
// For a zip with no archive comment, the
// end-of-central-directory record will be 22 bytes long, so
// we expect to find the EOCD marker 22 bytes from the end.
byte[] zipData = cmsFile.getTail();
if (zipData[zipData.length-22] != 0x50 ||
zipData[zipData.length-21] != 0x4b ||
zipData[zipData.length-20] != 0x05 ||
zipData[zipData.length-19] != 0x06) {
throw new IllegalArgumentException("zip data already has an archive comment");
}
int total_size = temp.size() + 6;
if (total_size > 0xffff) {
throw new IllegalArgumentException("signature is too big for ZIP file comment");
}
// signature starts this many bytes from the end of the file
int signature_start = total_size - message.length - 1;
temp.write(signature_start & 0xff);
temp.write((signature_start >> 8) & 0xff);
// Why the 0xff bytes? In a zip file with no archive comment,
// bytes [-6:-2] of the file are the little-endian offset from
// the start of the file to the central directory. So for the
// two high bytes to be 0xff 0xff, the archive would have to
// be nearly 4GB in size. So it's unlikely that a real
// commentless archive would have 0xffs here, and lets us tell
// an old signed archive from a new one.
temp.write(0xff);
temp.write(0xff);
temp.write(total_size & 0xff);
temp.write((total_size >> 8) & 0xff);
temp.flush();
// Signature verification checks that the EOCD header is the
// last such sequence in the file (to avoid minzip finding a
// fake EOCD appended after the signature in its scan). The
// odds of producing this sequence by chance are very low, but
// let's catch it here if it does.
byte[] b = temp.toByteArray();
for (int i = 0; i < b.length-3; ++i) {
if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
throw new IllegalArgumentException("found spurious EOCD header at " + i);
}
}
cmsFile.write(outputStream);
outputStream.write(total_size & 0xff);
outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream);
}
private static void signFile(Manifest manifest, JarMap inputJar,
X509Certificate publicKey, PrivateKey privateKey,
JarOutputStream outputJar)
throws Exception {
// Assume the certificate is valid for at least an hour.
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
// MANIFEST.MF
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
manifest.write(outputJar);
je = new JarEntry(CERT_SF_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey));
byte[] signedData = baos.toByteArray();
outputJar.write(signedData);
// CERT.{EC,RSA} / CERT#.{EC,RSA}
final String keyType = publicKey.getPublicKey().getAlgorithm();
je = new JarEntry(String.format(CERT_SIG_NAME, keyType));
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureBlock(new CMSProcessableByteArray(signedData),
publicKey, privateKey, outputJar);
}
}

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

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

160
gradlew vendored Executable file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
java

Submodule java deleted from 13bf1b27b4

View File

@@ -1,212 +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"
#include "resetprop.h"
pthread_t sepol_patch;
int is_restart = 0;
static void *request_handler(void *args) {
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;
}
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 start_daemon() {
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);
struct sockaddr_un sun;
fd = setup_socket(&sun);
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)) == -1)
exit(1);
xlisten(fd, 10);
if ((is_restart = access(UNBLOCKFILE, F_OK) == 0)) {
// Restart stuffs if the daemon is restarted
exec_command_sync("logcat", "-b", "all", "-c", NULL);
auto_start_magiskhide();
start_debug_log();
}
// Start the log monitor
monitor_logs();
// Continue the larger patch in another thread, we will join later
xpthread_create(&sepol_patch, NULL, large_sepol_patch, NULL);
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();
// Notifiy init the daemon is started
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
// 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 (xconnect(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);
xsetsid();
start_daemon();
}
do {
// Wait for 10ms
usleep(10);
} while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)));
}
return fd;
}

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 = *size > 0 ? xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0) : NULL;
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 = *size > 0 ? xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) : NULL;
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,143 +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-print <dtb>\n"
" Print all nodes in <dtb>, for debugging\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);
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], "--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-print") == 0) {
dtb_print(argv[2]);
} 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,312 +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 "utils.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 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];
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;
if ((dir = opendir("/vendor/etc/selinux")) == NULL)
goto check_done;
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);
if ((dir = opendir("/system/etc/selinux")) == NULL)
goto check_done;
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);
}
check_done:
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);
// Exclude overlay folder
excl_list = (char *[]) { "overlay", NULL };
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);
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
if (overlay > 0)
mv_dir(overlay, root);
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);
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;
}

Submodule jni/magiskpolicy deleted from 3c6a170138

View File

@@ -1,8 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := resetprop
LOCAL_SRC_FILES := resetprop.cpp system_properties.cpp libc_logging.cpp
LOCAL_LDLIBS += -latomic
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch -DINDEP_BINARY
include $(BUILD_EXECUTABLE)

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