mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 05:17:25 +00:00
Compare commits
207 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d52d7cfbd9 | ||
![]() |
4f74a259e3 | ||
![]() |
74da6e1dc0 | ||
![]() |
84ffdf0ed5 | ||
![]() |
022b18c8ce | ||
![]() |
b92b1dcddb | ||
![]() |
1472dbb291 | ||
![]() |
d58a8dc868 | ||
![]() |
e94be0b70e | ||
![]() |
f6ae7e1bf1 | ||
![]() |
f7b4935677 | ||
![]() |
a3c49de6a5 | ||
![]() |
e8dd1b292f | ||
![]() |
d21264d01b | ||
![]() |
b0567eadfd | ||
![]() |
5fc2058336 | ||
![]() |
d0567d29d2 | ||
![]() |
4db0ad32f0 | ||
![]() |
d065040321 | ||
![]() |
17f0fea3fc | ||
![]() |
8ca1e43533 | ||
![]() |
bd01c314dc | ||
![]() |
e404476609 | ||
![]() |
942c870981 | ||
![]() |
baff9256c5 | ||
![]() |
b4c0a255fc | ||
![]() |
9f6a27c20d | ||
![]() |
742dc137ed | ||
![]() |
39a6bd33ce | ||
![]() |
4672a5fad6 | ||
![]() |
e649b0a2df | ||
![]() |
fd8dbe3eff | ||
![]() |
bb97cc594d | ||
![]() |
70a322263e | ||
![]() |
c6f144d482 | ||
![]() |
3709489b3a | ||
![]() |
145ef32e28 | ||
![]() |
2212800a23 | ||
![]() |
2e25431bb6 | ||
![]() |
32c8e7522f | ||
![]() |
a5e4f3cc6b | ||
![]() |
a30777bd9f | ||
![]() |
e989195a68 | ||
![]() |
997d58932e | ||
![]() |
b4015f877f | ||
![]() |
d15fff95b9 | ||
![]() |
687e3b13ea | ||
![]() |
8c6bb383b7 | ||
![]() |
bc592c1d13 | ||
![]() |
968bd8be67 | ||
![]() |
d8b8adb88c | ||
![]() |
f42d820891 | ||
![]() |
bc21a1fb71 | ||
![]() |
3bc31374ac | ||
![]() |
858e7bae2b | ||
![]() |
8c02d120a2 | ||
![]() |
07e353f4ff | ||
![]() |
bb33d9e600 | ||
![]() |
68eb0bdec9 | ||
![]() |
32ee8e462c | ||
![]() |
e79aa54b70 | ||
![]() |
9a95652034 | ||
![]() |
912c188b53 | ||
![]() |
e9d0f615ba | ||
![]() |
9136573596 | ||
![]() |
2487ec94e6 | ||
![]() |
811489f157 | ||
![]() |
b438cc9335 | ||
![]() |
1d3d30fa45 | ||
![]() |
72b5985398 | ||
![]() |
2db60e0a6b | ||
![]() |
e710848345 | ||
![]() |
8d6f3c2450 | ||
![]() |
f863d127e7 | ||
![]() |
a831110816 | ||
![]() |
e97bdb53f4 | ||
![]() |
fe1439fbac | ||
![]() |
2bc30e5c22 | ||
![]() |
7244c02a0d | ||
![]() |
6c229ffa68 | ||
![]() |
cdc5d983f3 | ||
![]() |
96688e4dac | ||
![]() |
28a945fee9 | ||
![]() |
c7e777255a | ||
![]() |
2dd4cf040e | ||
![]() |
d1b9eca5eb | ||
![]() |
594a67fe28 | ||
![]() |
cddeaffada | ||
![]() |
2a8898e7c3 | ||
![]() |
ce3f3b09b4 | ||
![]() |
fe4b3df7e9 | ||
![]() |
25bdbcf526 | ||
![]() |
df7eaa5598 | ||
![]() |
bb7099376b | ||
![]() |
0327fd9710 | ||
![]() |
e645c6e465 | ||
![]() |
78a3d36ccc | ||
![]() |
3942858ccd | ||
![]() |
03c8d716cc | ||
![]() |
60181c4fcb | ||
![]() |
c215447405 | ||
![]() |
89330b89d8 | ||
![]() |
a8f3718ed0 | ||
![]() |
a78ba44709 | ||
![]() |
ff110e3513 | ||
![]() |
cfae6c63b5 | ||
![]() |
dbfe49c56f | ||
![]() |
98e21f9f5b | ||
![]() |
83af0497e4 | ||
![]() |
6ce37b44db | ||
![]() |
9cb1cf756f | ||
![]() |
ffa005e4ab | ||
![]() |
af102e47f1 | ||
![]() |
73064a816d | ||
![]() |
9b4ae8fcc5 | ||
![]() |
a1a2c52409 | ||
![]() |
9a0b26e0b0 | ||
![]() |
b805b96e16 | ||
![]() |
590e7f7724 | ||
![]() |
4d61e5e319 | ||
![]() |
8c8a63ebfb | ||
![]() |
e5e34797a8 | ||
![]() |
8516ebe6f5 | ||
![]() |
9f6205f47f | ||
![]() |
8b2ec23a89 | ||
![]() |
1816ca6b02 | ||
![]() |
7394ff9346 | ||
![]() |
bb5a6a1c28 | ||
![]() |
b614b06736 | ||
![]() |
7a376c9efc | ||
![]() |
518f3d229f | ||
![]() |
46c91f923d | ||
![]() |
3a2262dfb3 | ||
![]() |
ff7c38f8e9 | ||
![]() |
4229ba364f | ||
![]() |
ba8e7a211a | ||
![]() |
6b41653a32 | ||
![]() |
59c1125e72 | ||
![]() |
b536046720 | ||
![]() |
619b805894 | ||
![]() |
8662537883 | ||
![]() |
717890395b | ||
![]() |
b7b4164f4f | ||
![]() |
7e65296470 | ||
![]() |
cd5f5d702f | ||
![]() |
44b93e7cc4 | ||
![]() |
0eb79e5acd | ||
![]() |
eceba26894 | ||
![]() |
0bf404f75e | ||
![]() |
cd8dd65a65 | ||
![]() |
50c56f8b50 | ||
![]() |
9e9f8ca8f3 | ||
![]() |
f63af0601c | ||
![]() |
189c671ce2 | ||
![]() |
bb39a01361 | ||
![]() |
764999704a | ||
![]() |
ecfa4aafc1 | ||
![]() |
a1e33c4d2f | ||
![]() |
7f8ba74dac | ||
![]() |
e3df62d812 | ||
![]() |
1913125881 | ||
![]() |
e8e58f3fed | ||
![]() |
1ca9ec384b | ||
![]() |
9522255e3a | ||
![]() |
2a22fa694e | ||
![]() |
1591f5a0ca | ||
![]() |
3bc4e9a724 | ||
![]() |
f7a6bb0723 | ||
![]() |
e9c17a3ef7 | ||
![]() |
29bb5840b5 | ||
![]() |
c9d8d860f6 | ||
![]() |
cc18096882 | ||
![]() |
15f2a664d1 | ||
![]() |
70b4f62ddc | ||
![]() |
e1023fdfaf | ||
![]() |
5e9648387a | ||
![]() |
2ba8b4df67 | ||
![]() |
3a084c5d7b | ||
![]() |
f7200e39c3 | ||
![]() |
a7dfc20967 | ||
![]() |
6eb7c0b5d6 | ||
![]() |
0b3c078aeb | ||
![]() |
750872cc37 | ||
![]() |
29895ff474 | ||
![]() |
44adccc147 | ||
![]() |
2a7e2c70b5 | ||
![]() |
8d431b6762 | ||
![]() |
273849c0c8 | ||
![]() |
5cc14405c7 | ||
![]() |
f0cfd60e62 | ||
![]() |
d6547f0701 | ||
![]() |
3b68905037 | ||
![]() |
eae611c54d | ||
![]() |
b37bad35c2 | ||
![]() |
5fab15fee5 | ||
![]() |
10c8ea17aa | ||
![]() |
7058c8ff5a | ||
![]() |
64e85da59f | ||
![]() |
f79fad64aa | ||
![]() |
cb70eebb08 | ||
![]() |
edaf8787d1 | ||
![]() |
24164c8580 | ||
![]() |
9fca7011aa | ||
![]() |
b13eb3fd40 | ||
![]() |
b7986a351c | ||
![]() |
ce87591c62 | ||
![]() |
25c289ad3e |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -8,9 +8,11 @@
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.cmd text eol=crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
chromeos/** binary
|
||||
*.jar binary
|
||||
*.exe binary
|
||||
*.apk binary
|
||||
*.png binary
|
||||
|
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,7 +1,16 @@
|
||||
obj/
|
||||
libs/
|
||||
out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
|
||||
# Copied binaries
|
||||
# Built binaries
|
||||
ziptools/zipadjust
|
||||
|
||||
# Android Studio / Gradle
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
26
.gitmodules
vendored
26
.gitmodules
vendored
@@ -1,15 +1,27 @@
|
||||
[submodule "jni/selinux"]
|
||||
path = jni/selinux
|
||||
path = core/jni/external/selinux
|
||||
url = https://github.com/topjohnwu/selinux.git
|
||||
[submodule "jni/su"]
|
||||
path = jni/su
|
||||
path = core/jni/su
|
||||
url = https://github.com/topjohnwu/MagiskSU.git
|
||||
[submodule "jni/ndk-compression"]
|
||||
path = jni/ndk-compression
|
||||
url = https://github.com/topjohnwu/ndk-compression.git
|
||||
[submodule "jni/magiskpolicy"]
|
||||
path = jni/magiskpolicy
|
||||
path = core/jni/magiskpolicy
|
||||
url = https://github.com/topjohnwu/magiskpolicy.git
|
||||
[submodule "MagiskManager"]
|
||||
path = MagiskManager
|
||||
path = app
|
||||
url = https://github.com/topjohnwu/MagiskManager.git
|
||||
[submodule "jni/busybox"]
|
||||
path = core/jni/external/busybox
|
||||
url = https://github.com/topjohnwu/ndk-busybox.git
|
||||
[submodule "jni/external/dtc"]
|
||||
path = core/jni/external/dtc
|
||||
url = https://github.com/dgibson/dtc
|
||||
[submodule "jni/external/lz4"]
|
||||
path = core/jni/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "jni/external/bzip2"]
|
||||
path = core/jni/external/bzip2
|
||||
url = https://github.com/nemequ/bzip2.git
|
||||
[submodule "jni/external/xz"]
|
||||
path = core/jni/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
|
Submodule MagiskManager deleted from 3550d1e61c
79
README.MD
79
README.MD
@@ -2,35 +2,33 @@
|
||||
|
||||
## 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.5***
|
||||
***Ubuntu 17.04 x64***
|
||||
***Windows 10 Creators Update x64***
|
||||
### Environment Requirements
|
||||
|
||||
#### Environment Requirements
|
||||
1. A 64-bit machine: `cmake` for Android is only available in 64-bit
|
||||
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`
|
||||
|
||||
1. Python 3 **(>= 3.5)**: `python3` (or in some cases `python`) should be accessible
|
||||
2. Java runtime: `java` should be accessible
|
||||
3. (Unix only) C compiler: `gcc` should be accessible
|
||||
4. Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
|
||||
5. NDK: Install NDK using `sdkmanager`, or through Android SDK Manager
|
||||
6. Android build-tools: Should have build-tools version matching `MagiskManager/app/build.gradle` installed
|
||||
### 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).
|
||||
|
||||
#### Instructions and Notes
|
||||
|
||||
1. 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 insist to use an older Windows version, a quick Google search should provide many workarounds
|
||||
2. After installing the latest Python 3 on Windows (allow the installer to add Python to PATH, or you'll have to manually set the environment), instead of calling `python3` like most Unix environment, you should call `python` in shell (cmd or Powershell both OK). You can double check the version by `python --version`
|
||||
3. The build script will do several checks, it will refuse to run if the environment doesn't meet the requirements
|
||||
4. For further instructions, please check the built in help message by `python3 build.py -h`
|
||||
(Unix users can simply `./build.py -h`, Windows users, as mentioned, call `python` instead)
|
||||
5. Each action has its own help message, access them by commands like `python3 build.py all -h`
|
||||
6. To build Magisk for release (enabled through the `--release` flag, the script builds in debug mode by default), you will need to provide a Java keystore file, and place it in `release_signature.jks` to sign Magisk Manager APK for release builds. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually)
|
||||
7. To properly setup the Android SDK environment, the easiest way is to use Android Studio and open Magisk Manager. If gradle sync passed, your build-tools etc. should be set properly. You can also access SDK Manager GUI within Android Studio to download NDK. Don't forget to add Android Studio's SDK path into environment variable ANDROID_HOME.
|
||||
|
||||
## License
|
||||
|
||||
Magisk, including all subprojects (git submodule) is 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.
|
||||
```
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -39,40 +37,45 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
**MagiskManager** (`MagiskManager`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* All contributors and translators
|
||||
**MagiskManager** (`app`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* All contributors and translators on Github
|
||||
|
||||
**MagiskSU** (`core/jni/su`)
|
||||
|
||||
**MagiskSU** (`jni/su`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
* Copyright 2013, Koushik Dutta (@koush)
|
||||
* Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
* Copyright 2008, Zinx Verituse (@zinxv)
|
||||
|
||||
**MagiskPolicy** (`jni/magiskpolicy`)
|
||||
**MagiskPolicy** (`core/jni/magiskpolicy`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
* Copyright 2015, Joshua Brindle (@joshua_brindle)
|
||||
|
||||
**MagiskHide** (`jni/magiskhide`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me) (original hidesu)
|
||||
**MagiskHide** (`core/jni/magiskhide`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me)
|
||||
|
||||
**resetprop** (`core/jni/resetprop`)
|
||||
|
||||
**resetprop** (`jni/resetprop`)
|
||||
* Copyright 2016-2017 John Wu (@topjohnwu)
|
||||
* Copyright 2016 nkk71 (nkk71x@gmail.com)
|
||||
|
||||
**SELinux** (`jni/selinux`)
|
||||
* Makefile for NDK: Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* It is maintained by many developers in SELinux project, copyright belongs to them
|
||||
|
||||
**ndk-compression** (`jni/ndk-compression`)
|
||||
* Makefile for NDK: Copyright 2017, John Wu (@topjohnwu)
|
||||
* Each library has its own copyright message in each directories
|
||||
**External Dependencies** (`core/jni/external`)
|
||||
|
||||
* Makefile for busybox, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen)
|
||||
* Each dependencies has its own license/copyright information in each subdirectory.
|
||||
All of them are either GPL or GPL compatible.
|
||||
|
||||
**Others Not Mentioned**
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
|
1
app
Submodule
1
app
Submodule
Submodule app added at de2285d5e9
27
build.gradle
Normal file
27
build.gradle
Normal 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
|
||||
}
|
368
build.py
368
build.py
@@ -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)
|
||||
@@ -34,18 +38,44 @@ import multiprocessing
|
||||
import zipfile
|
||||
import datetime
|
||||
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: ' + source + ' -> ' + target)
|
||||
print('zip: {} -> {}'.format(source, target))
|
||||
zipfile.write(source, target)
|
||||
|
||||
def build_all(args):
|
||||
@@ -53,26 +83,65 @@ def build_all(args):
|
||||
build_apk(args)
|
||||
zip_main(args)
|
||||
zip_uninstaller(args)
|
||||
build_snet(args)
|
||||
|
||||
def build_binary(args):
|
||||
header('* Building Magisk binaries')
|
||||
|
||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||
debug_flag = '' if args.release else '-DDEBUG'
|
||||
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)
|
||||
# Force update logging.h timestamp to trigger recompilation
|
||||
os.utime(os.path.join('core', 'jni', 'include', 'logging.h'))
|
||||
|
||||
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
|
||||
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
|
||||
|
||||
# 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')
|
||||
|
||||
os.chdir('MagiskManager')
|
||||
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
|
||||
source = os.path.join('ziptools', 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('app', 'src', 'main', 'assets', script)
|
||||
cp(source, target)
|
||||
|
||||
if args.release:
|
||||
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
|
||||
if not os.path.exists('release_signature.jks'):
|
||||
error('Please generate a java keystore and place it in \'release_signature.jks\'')
|
||||
|
||||
proc = subprocess.run('{} assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
@@ -83,109 +152,111 @@ 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'),
|
||||
'-v', '-p', '4', unsigned, aligned])
|
||||
'-v', '-p', '4', unsigned, aligned], stdout=subprocess.DEVNULL)
|
||||
if proc.returncode != 0:
|
||||
error('Zipalign Magisk Manager failed!')
|
||||
|
||||
proc = subprocess.run('{} sign --ks {} --out {} {}'.format(
|
||||
'java -jar {}'.format(os.path.join('../ziptools/apksigner.jar')),
|
||||
os.path.join('..', 'release_signature.jks'),
|
||||
release, aligned), shell=True)
|
||||
# Find apksigner.jar
|
||||
apksigner = ''
|
||||
for root, dirs, files in os.walk(os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool)):
|
||||
if 'apksigner.jar' in files:
|
||||
apksigner = os.path.join(root, 'apksigner.jar')
|
||||
break
|
||||
if not apksigner:
|
||||
error('Cannot find apksigner.jar in Android SDK build tools')
|
||||
|
||||
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
|
||||
apksigner, 'release_signature.jks', release, aligned), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Release sign Magisk Manager failed!')
|
||||
|
||||
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('{} assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
# Return to upper directory
|
||||
os.chdir('..')
|
||||
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'app-debug.apk')
|
||||
print('')
|
||||
mv(source, target)
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
header('* Signing / Adjusting Zip')
|
||||
|
||||
# Unsigned->signed
|
||||
proc = subprocess.run(['java', '-jar', os.path.join('ziptools', 'signapk.jar'),
|
||||
os.path.join('ziptools', 'test.certificate.x509.pem'),
|
||||
os.path.join('ziptools', 'test.key.pk8'), unsigned, 'tmp_signed.zip'])
|
||||
def build_snet(args):
|
||||
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('First sign flashable zip failed!')
|
||||
error('Build snet extention failed!')
|
||||
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'snet.apk')
|
||||
print('')
|
||||
mv(source, target)
|
||||
|
||||
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
|
||||
# Compile zipadjust
|
||||
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/src/*.c -lz', shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build zipadjust 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', os.path.join('ziptools', 'minsignapk.jar'),
|
||||
os.path.join('ziptools', 'test.certificate.x509.pem'),
|
||||
os.path.join('ziptools', 'test.key.pk8'), 'tmp_adjusted.zip', output])
|
||||
if proc.returncode != 0:
|
||||
error('Second sign flashable zip failed!')
|
||||
|
||||
# Cleanup
|
||||
silentremove(unsigned)
|
||||
silentremove('tmp_signed.zip')
|
||||
silentremove('tmp_adjusted.zip')
|
||||
def gen_update_binary():
|
||||
update_bin = []
|
||||
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('out', 'x86', 'b64xz')
|
||||
with open(binary, 'rb') as b64xz:
|
||||
update_bin.append('\'\nEX_X86=\'')
|
||||
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
binary = os.path.join('out', 'armeabi-v7a', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
update_bin.append('\'\nBB_ARM=')
|
||||
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
|
||||
binary = os.path.join('out', 'x86', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
update_bin.append('\nBB_X86=')
|
||||
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())
|
||||
return ''.join(update_bin)
|
||||
|
||||
def zip_main(args):
|
||||
header('* Packing Flashable Zip')
|
||||
|
||||
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
# Compiled Binaries
|
||||
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')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, gen_update_binary())
|
||||
# updater-script
|
||||
source = os.path.join('scripts', 'flash_script.sh')
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Binaries
|
||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
for binary in ['magisk', 'magiskboot']:
|
||||
source = os.path.join('libs', lib_dir, binary)
|
||||
for binary in ['magiskinit', 'magiskboot']:
|
||||
source = os.path.join('out', lib_dir, binary)
|
||||
target = os.path.join(zip_dir, binary)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# APK
|
||||
source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk',
|
||||
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
source = os.path.join('out', 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
target = os.path.join('common', 'magisk.apk')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Scripts
|
||||
# flash_script.sh
|
||||
source = os.path.join('scripts', 'flash_script.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info into flash script
|
||||
update_binary = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'Magisk v{} Installer'.format(args.versionString))
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, update_binary)
|
||||
# addon.d.sh
|
||||
source = os.path.join('scripts', 'addon.d.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info addon.d.sh
|
||||
addond = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'Magisk v{} addon.d'.format(args.versionString))
|
||||
target = os.path.join('addon.d', '99-magisk.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, addond)
|
||||
# updater-script
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, '#MAGISK\n')
|
||||
# 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)
|
||||
# boot_patch.sh
|
||||
source = os.path.join('scripts', 'boot_patch.sh')
|
||||
target = os.path.join('common', 'boot_patch.sh')
|
||||
@@ -195,10 +266,15 @@ def zip_main(args):
|
||||
with open(source, 'r') as script:
|
||||
# Add version info util_functions.sh
|
||||
util_func = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'SCRIPT_VERSION={}'.format(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)
|
||||
# addon.d.sh
|
||||
source = os.path.join('scripts', 'addon.d.sh')
|
||||
target = os.path.join('addon.d', '99-magisk.sh')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Prebuilts
|
||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
source = os.path.join('chromeos', chromeos)
|
||||
@@ -206,16 +282,28 @@ 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) as zipf:
|
||||
# Compiled Binaries
|
||||
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')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, gen_update_binary())
|
||||
# updater-script
|
||||
source = os.path.join('scripts', 'uninstaller_loader.sh')
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Binaries
|
||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
source = os.path.join('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)
|
||||
|
||||
@@ -223,23 +311,14 @@ def zip_uninstaller(args):
|
||||
target = 'magisk_uninstaller.sh'
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Scripts
|
||||
# util_functions.sh
|
||||
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)
|
||||
|
||||
source = os.path.join('scripts', 'uninstaller_loader.sh')
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, '#MAGISK\n')
|
||||
zipf.writestr(target, script.read())
|
||||
|
||||
# Prebuilts
|
||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
@@ -248,29 +327,79 @@ def zip_uninstaller(args):
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))
|
||||
sign_adjust_zip('tmp_unsigned.zip', output)
|
||||
output = os.path.join('out', 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')))
|
||||
sign_adjust_zip(unsigned, output)
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
signer_name = 'zipsigner-1.1.jar'
|
||||
jarsigner = os.path.join('crypto', 'build', 'libs', signer_name)
|
||||
|
||||
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
|
||||
header('* Building zipadjust')
|
||||
# Compile zipadjust
|
||||
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build zipadjust failed!')
|
||||
if not os.path.exists(jarsigner):
|
||||
header('* Building ' + signer_name)
|
||||
proc = subprocess.run('{} crypto:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build {} failed!'.format(signer_name))
|
||||
|
||||
header('* Signing / Adjusting Zip')
|
||||
|
||||
publicKey = os.path.join('ziptools', 'public.certificate.x509.pem')
|
||||
privateKey = os.path.join('ziptools', 'private.key.pk8')
|
||||
|
||||
signed = tempfile.mkstemp()[1]
|
||||
|
||||
# Unsigned->signed
|
||||
proc = subprocess.run(['java', '-jar', jarsigner,
|
||||
publicKey, privateKey, unsigned, signed])
|
||||
if proc.returncode != 0:
|
||||
error('First sign flashable zip failed!')
|
||||
|
||||
adjusted = tempfile.mkstemp()[1]
|
||||
|
||||
# Adjust zip
|
||||
proc = subprocess.run([os.path.join('ziptools', 'zipadjust'), signed, adjusted])
|
||||
if proc.returncode != 0:
|
||||
error('Adjust flashable zip failed!')
|
||||
|
||||
# Adjusted -> output
|
||||
proc = subprocess.run(['java', '-jar', jarsigner,
|
||||
"-m", publicKey, privateKey, adjusted, output])
|
||||
if proc.returncode != 0:
|
||||
error('Second sign flashable zip failed!')
|
||||
|
||||
# Cleanup
|
||||
rm(unsigned)
|
||||
rm(signed)
|
||||
rm(adjusted)
|
||||
|
||||
def cleanup(args):
|
||||
if len(args.target) == 0:
|
||||
args.target = ['binary', 'apk', 'zip']
|
||||
args.target = ['binary', 'java', 'zip']
|
||||
|
||||
if 'binary' in args.target:
|
||||
header('* Cleaning Magisk binaries')
|
||||
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
|
||||
header('* Cleaning binaries')
|
||||
subprocess.run(ndk_build + ' -C core PRECOMPILE=true clean', shell=True)
|
||||
subprocess.run(ndk_build + ' -C core clean', shell=True)
|
||||
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
|
||||
|
||||
if 'apk' in args.target:
|
||||
header('* Cleaning Magisk Manager')
|
||||
os.chdir('MagiskManager')
|
||||
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
os.chdir('..')
|
||||
if 'java' in args.target:
|
||||
header('* Cleaning java')
|
||||
subprocess.run('{} app:clean snet:clean crypto:clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
for f in os.listdir('out'):
|
||||
if '.apk' in f:
|
||||
rm(os.path.join('out', f))
|
||||
|
||||
if 'zip' in args.target:
|
||||
header('* Cleaning created zip files')
|
||||
for f in os.listdir('.'):
|
||||
header('* Cleaning zip files')
|
||||
for f in os.listdir('out'):
|
||||
if '.zip' in f:
|
||||
print('rm {}'.format(f))
|
||||
silentremove(f)
|
||||
rm(os.path.join('out', f))
|
||||
|
||||
parser = argparse.ArgumentParser(description='Magisk build script')
|
||||
parser.add_argument('--release', action='store_true', help='compile Magisk for release')
|
||||
@@ -289,6 +418,9 @@ binary_parser.set_defaults(func=build_binary)
|
||||
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
|
||||
apk_parser.set_defaults(func=build_apk)
|
||||
|
||||
snet_parser = subparsers.add_parser('snet', help='build snet extention for Magisk Manager')
|
||||
snet_parser.set_defaults(func=build_snet)
|
||||
|
||||
zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
|
||||
zip_parser.add_argument('versionString')
|
||||
zip_parser.add_argument('versionCode', type=int)
|
||||
@@ -297,13 +429,13 @@ zip_parser.set_defaults(func=zip_main)
|
||||
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
|
||||
uninstaller_parser.set_defaults(func=zip_uninstaller)
|
||||
|
||||
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary apk zip')
|
||||
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip')
|
||||
clean_parser.add_argument('target', nargs='*')
|
||||
clean_parser.set_defaults(func=cleanup)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
3
core/.gitignore
vendored
Normal file
3
core/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/build
|
||||
obj
|
||||
libs
|
20
core/build.gradle
Normal file
20
core/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path 'jni/Android.mk'
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
// Passes an optional argument to ndk-build.
|
||||
arguments "GRADLE=true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
138
core/jni/Android.mk
Normal file
138
core/jni/Android.mk
Normal file
@@ -0,0 +1,138 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
# Some handy paths
|
||||
EXT_PATH := jni/external
|
||||
SE_PATH := $(EXT_PATH)/selinux
|
||||
LIBSELINUX := $(SE_PATH)/libselinux/include
|
||||
LIBSEPOL := $(SE_PATH)/libsepol/include $(SE_PATH)/libsepol/cil/include
|
||||
LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
|
||||
LIBLZ4 := $(EXT_PATH)/lz4/lib
|
||||
LIBBZ2 := $(EXT_PATH)/bzip2
|
||||
LIBFDT := $(EXT_PATH)/dtc/libfdt
|
||||
UTIL_SRC := utils/cpio.c \
|
||||
utils/file.c \
|
||||
utils/img.c \
|
||||
utils/list.c \
|
||||
utils/misc.c \
|
||||
utils/pattern.c \
|
||||
utils/vector.c \
|
||||
utils/xwrap.c
|
||||
|
||||
########################
|
||||
# Binaries
|
||||
########################
|
||||
|
||||
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
|
||||
|
||||
# magisk main binary
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magisk
|
||||
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/external/include \
|
||||
$(LIBSELINUX)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/magisk.c \
|
||||
core/daemon.c \
|
||||
core/log_monitor.c \
|
||||
core/bootstages.c \
|
||||
core/socket.c \
|
||||
magiskhide/magiskhide.c \
|
||||
magiskhide/proc_monitor.c \
|
||||
magiskhide/hide_utils.c \
|
||||
resetprop/resetprop.cpp \
|
||||
resetprop/system_properties.cpp \
|
||||
su/su.c \
|
||||
su/activity.c \
|
||||
su/db.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c \
|
||||
su/su_socket.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_CFLAGS := -DIS_DAEMON -DSELINUX
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
ifndef PRECOMPILE
|
||||
|
||||
# magiskinit
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskinit
|
||||
LOCAL_STATIC_LIBRARIES := libsepol liblzma
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/magiskpolicy \
|
||||
../out/$(TARGET_ARCH_ABI) \
|
||||
$(LIBSEPOL) \
|
||||
$(LIBLZMA)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/magiskinit.c \
|
||||
core/socket.c \
|
||||
magiskpolicy/api.c \
|
||||
magiskpolicy/magiskpolicy.c \
|
||||
magiskpolicy/rules.c \
|
||||
magiskpolicy/sepolicy.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# magiskboot
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
LOCAL_STATIC_LIBRARIES := liblzma liblz4 libbz2 libfdt
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/external/include \
|
||||
$(LIBLZMA) \
|
||||
$(LIBLZ4) \
|
||||
$(LIBBZ2) \
|
||||
$(LIBFDT)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
external/sha1/sha1.c \
|
||||
magiskboot/main.c \
|
||||
magiskboot/bootimg.c \
|
||||
magiskboot/hexpatch.c \
|
||||
magiskboot/compress.c \
|
||||
magiskboot/types.c \
|
||||
magiskboot/dtb.c \
|
||||
magiskboot/ramdisk.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_CFLAGS := -DXWRAP_EXIT
|
||||
LOCAL_LDLIBS := -lz
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# 32-bit static binaries
|
||||
ifndef GRADLE # Do not run gradle sync on these binaries
|
||||
ifneq ($(TARGET_ARCH_ABI), x86_64)
|
||||
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
|
||||
# b64xz
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := b64xz
|
||||
LOCAL_STATIC_LIBRARIES := liblzma
|
||||
LOCAL_C_INCLUDES := $(LIBLZMA)
|
||||
LOCAL_SRC_FILES := b64xz.c
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
# Busybox
|
||||
include jni/external/busybox/Android.mk
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Precompile
|
||||
endif
|
||||
|
||||
########################
|
||||
# Externals
|
||||
########################
|
||||
include jni/external/Android.mk
|
@@ -1,4 +1,4 @@
|
||||
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
|
||||
APP_PLATFORM := android-21
|
||||
APP_UNIFIED_HEADERS := true
|
||||
APP_CPPFLAGS += -std=c++11
|
||||
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99
|
||||
APP_CPPFLAGS := -std=c++11
|
83
core/jni/b64xz.c
Normal file
83
core/jni/b64xz.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/* b64xz.c - Base64-XZ Extractor
|
||||
*
|
||||
* This program expects data from stdin. The data should be compressed with xz and
|
||||
* then encoded into base64 format. What b64xz does is basically the reverse of the
|
||||
* mentioned process: decode base64 to uint8_ts, decompress xz, then dump to stdout
|
||||
*
|
||||
* The compiled binary will be hex-dumped into update-binary
|
||||
* Busybox will be xz-compressed, base64 encoded and dumped into update-binary
|
||||
* This program is to recover busybox for Magisk installation environment
|
||||
*
|
||||
* I intentionally removed stdio. This will result in a smaller binary size because
|
||||
* all I/O are handled by system calls (read/write) instead of libc wrappers
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <lzma.h>
|
||||
|
||||
#define BUFSIZE 8192
|
||||
|
||||
static const char trans_tbl[] =
|
||||
"|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
|
||||
|
||||
static void decodeblock(uint8_t* in, uint8_t* out) {
|
||||
out[0] = (uint8_t)(in[0] << 2 | in[1] >> 4);
|
||||
out[1] = (uint8_t)(in[1] << 4 | in[2] >> 2);
|
||||
out[2] = (uint8_t)(((in[2] << 6) & 0xc0) | in[3]);
|
||||
}
|
||||
|
||||
static int unxz(lzma_stream *strm, void *buf, size_t size) {
|
||||
lzma_ret ret = 0;
|
||||
uint8_t out[BUFSIZE];
|
||||
strm->next_in = buf;
|
||||
strm->avail_in = size;
|
||||
do {
|
||||
strm->next_out = out;
|
||||
strm->avail_out = sizeof(out);
|
||||
ret = lzma_code(strm, LZMA_RUN);
|
||||
write(STDOUT_FILENO, out, sizeof(out) - strm->avail_out);
|
||||
} while (strm->avail_out == 0 && ret == LZMA_OK);
|
||||
|
||||
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
|
||||
write(STDERR_FILENO, "LZMA error!\n", 13);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char const* argv[]) {
|
||||
if (argc > 1)
|
||||
return 0;
|
||||
|
||||
uint8_t in[4], buf[BUFSIZE];
|
||||
int len = 0, pos = 0;
|
||||
char c;
|
||||
|
||||
// Setup lzma stream
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
if (lzma_auto_decoder(&strm, UINT64_MAX, 0) != LZMA_OK) {
|
||||
write(STDERR_FILENO, "Unable to init lzma stream\n", 28);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (read(STDIN_FILENO, &c, sizeof(c)) == 1) {
|
||||
c = ((c < 43 || c > 122) ? -1 : (trans_tbl[c - 43] == '$' ? -1 : trans_tbl[c - 43] - 62));
|
||||
if (c >= 0)
|
||||
in[len++] = c;
|
||||
if (len < 4)
|
||||
continue;
|
||||
len = 0;
|
||||
decodeblock(in, buf + pos);
|
||||
pos += 3;
|
||||
if (pos > sizeof(buf) - 3) {
|
||||
// Buffer is full, unxz
|
||||
if (unxz(&strm, buf, pos))
|
||||
return 1;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
if (pos) {
|
||||
if (unxz(&strm, buf, pos))
|
||||
return 1;
|
||||
}
|
||||
lzma_end(&strm);
|
||||
return 0;
|
||||
}
|
@@ -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>
|
||||
@@ -25,9 +23,7 @@
|
||||
static char *buf, *buf2;
|
||||
static struct vector module_list;
|
||||
|
||||
#ifdef DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
#endif
|
||||
extern char **environ;
|
||||
|
||||
/******************
|
||||
* Node structure *
|
||||
@@ -39,7 +35,9 @@ static int debug_log_pid, debug_log_fd;
|
||||
#define IS_SKEL 0x04 /* mount from skeleton */
|
||||
#define IS_MODULE 0x08 /* mount from module */
|
||||
|
||||
#define IS_VENDOR 0x10 /* special vendor placeholder */
|
||||
#define IS_DIR(n) (n->type == DT_DIR)
|
||||
#define IS_LNK(n) (n->type == DT_LNK)
|
||||
#define IS_REG(n) (n->type == DT_REG)
|
||||
|
||||
struct node_entry {
|
||||
const char *module; /* Only used when status & IS_MODULE */
|
||||
@@ -50,143 +48,18 @@ struct node_entry {
|
||||
struct vector *children;
|
||||
};
|
||||
|
||||
#define IS_DIR(n) (n->type == DT_DIR)
|
||||
#define IS_LNK(n) (n->type == DT_LNK)
|
||||
#define IS_REG(n) (n->type == DT_REG)
|
||||
|
||||
/******************
|
||||
* Image handling *
|
||||
******************/
|
||||
|
||||
#define round_size(a) ((((a) / 32) + 2) * 32)
|
||||
#define SOURCE_TMP "/dev/source"
|
||||
#define TARGET_TMP "/dev/target"
|
||||
|
||||
static int merge_img(const char *source, const char *target) {
|
||||
if (access(source, F_OK) == -1)
|
||||
return 0;
|
||||
if (access(target, F_OK) == -1) {
|
||||
rename(source, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// resize target to worst case
|
||||
int s_used, s_total, t_used, t_total, n_total;
|
||||
get_img_size(source, &s_used, &s_total);
|
||||
get_img_size(target, &t_used, &t_total);
|
||||
n_total = round_size(s_used + t_used);
|
||||
if (n_total != t_total)
|
||||
resize_img(target, n_total);
|
||||
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
xmkdir(TARGET_TMP, 0755);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(source, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(target, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if (!(dir = opendir(SOURCE_TMP)))
|
||||
return 1;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
// Cleanup old module if exists
|
||||
snprintf(buf, PATH_MAX, "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("Upgrade module: %s\n", entry->d_name);
|
||||
rm_rf(buf);
|
||||
} else {
|
||||
LOGI("New module: %s\n", entry->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
clone_dir(SOURCE_TMP, TARGET_TMP);
|
||||
|
||||
// Unmount all loop devices
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
unlink(source);
|
||||
return 0;
|
||||
static void concat_path(struct node_entry *node) {
|
||||
if (node->parent)
|
||||
concat_path(node->parent);
|
||||
int len = strlen(buf);
|
||||
buf[len] = '/';
|
||||
strcpy(buf + len + 1, node->name);
|
||||
}
|
||||
|
||||
static void trim_img(const char *img) {
|
||||
int used, total, new_size;
|
||||
get_img_size(img, &used, &total);
|
||||
new_size = round_size(used);
|
||||
if (new_size != total)
|
||||
resize_img(img, new_size);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Scripts *
|
||||
***********/
|
||||
|
||||
void exec_common_script(const char* stage) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s", buf, entry->d_name);
|
||||
if (access(buf2, X_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
char *const command[] = { "sh", buf2, NULL };
|
||||
int pid = run_command(0, NULL, "/system/bin/sh", command);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void exec_module_script(const char* stage) {
|
||||
char *module;
|
||||
vec_for_each(&module_list, module) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/%s.sh", MOUNTPOINT, module, stage);
|
||||
if (access(buf, F_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
char *const command[] = { "sh", buf, NULL };
|
||||
int pid = run_command(0, NULL, "/system/bin/sh", command);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************
|
||||
* Magic Mount *
|
||||
***************/
|
||||
|
||||
static char *get_full_path(struct node_entry *node) {
|
||||
char buffer[PATH_MAX], temp[PATH_MAX];
|
||||
// Concat the paths
|
||||
struct node_entry *cur = node;
|
||||
strcpy(buffer, node->name);
|
||||
while (cur->parent) {
|
||||
strcpy(temp, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", cur->parent->name, temp);
|
||||
cur = cur->parent;
|
||||
}
|
||||
return strdup(buffer);
|
||||
buf[0] = '\0';
|
||||
concat_path(node);
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
// Free the node
|
||||
@@ -221,7 +94,7 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
|
||||
if (c->status > e->status) {
|
||||
// Precedence is higher, replace with new node
|
||||
destroy_subtree(e);
|
||||
vec_entry(p->children)[_] = c;
|
||||
vec_cur(p->children) = c;
|
||||
return c;
|
||||
} else {
|
||||
// Free the new entry, return old
|
||||
@@ -235,6 +108,77 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
|
||||
return c;
|
||||
}
|
||||
|
||||
/***********
|
||||
* setenvs *
|
||||
***********/
|
||||
|
||||
static void bb_setenv(struct vector *v) {
|
||||
for (int i = 0; environ[i]; ++i) {
|
||||
if (strncmp(environ[i], "PATH=", 5) == 0) {
|
||||
snprintf(buf, PATH_MAX, "PATH=%s:%s", BBPATH, strchr(environ[i], '=') + 1);
|
||||
vec_push_back(v, strdup(buf));
|
||||
} else {
|
||||
vec_push_back(v, strdup(environ[i]));
|
||||
}
|
||||
}
|
||||
vec_push_back(v, NULL);
|
||||
}
|
||||
|
||||
static void pm_setenv(struct vector *v) {
|
||||
for (int i = 0; environ[i]; ++i) {
|
||||
if (strncmp(environ[i], "CLASSPATH=", 10) != 0)
|
||||
vec_push_back(v, strdup(environ[i]));
|
||||
}
|
||||
vec_push_back(v, strdup("CLASSPATH=/system/framework/pm.jar"));
|
||||
vec_push_back(v, NULL);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Scripts *
|
||||
***********/
|
||||
|
||||
static void exec_common_script(const char* stage) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
|
||||
|
||||
if (!(dir = xopendir(buf)))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s", buf, entry->d_name);
|
||||
if (access(buf2, X_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static void exec_module_script(const char* stage) {
|
||||
char *module;
|
||||
vec_for_each(&module_list, module) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/%s.sh", MOUNTPOINT, module, stage);
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, module);
|
||||
if (access(buf2, F_OK) == -1 || access(buf, F_OK) == 0)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************
|
||||
* Magic Mount *
|
||||
***************/
|
||||
|
||||
static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
@@ -243,7 +187,7 @@ static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
char *parent_path = get_full_path(parent);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
@@ -261,11 +205,11 @@ static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
* 1. File in module is a symlink
|
||||
* 2. Target file do not exist
|
||||
* 3. Target file is a symlink, but not /system/vendor
|
||||
*/
|
||||
*/
|
||||
int clone = 0;
|
||||
if (IS_LNK(node) || access(buf, F_OK) == -1) {
|
||||
clone = 1;
|
||||
} else if (strcmp(parent->name, "/system") != 0 || strcmp(node->name, "vendor") != 0) {
|
||||
} else if (parent->parent != NULL || strcmp(node->name, "vendor") != 0) {
|
||||
struct stat s;
|
||||
xstat(buf, &s);
|
||||
if (S_ISLNK(s.st_mode))
|
||||
@@ -296,7 +240,7 @@ static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
construct_tree(module, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closedir(dir);
|
||||
|
||||
cleanup:
|
||||
@@ -311,7 +255,7 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
// Clone the structure
|
||||
char *full_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
|
||||
if (!(dir = opendir(buf)))
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
@@ -325,23 +269,30 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
snprintf(buf, PATH_MAX, "%s%s", DUMMDIR, full_path);
|
||||
mkdir_p(buf, 0755);
|
||||
clone_attr(full_path, buf);
|
||||
if (node->status & IS_SKEL)
|
||||
bind_mount(buf, full_path);
|
||||
if (node->status & IS_SKEL) {
|
||||
struct stat s;
|
||||
char *con;
|
||||
xstat(full_path, &s);
|
||||
getfilecon(full_path, &con);
|
||||
LOGI("tmpfs: %s\n", full_path);
|
||||
xmount("tmpfs", full_path, "tmpfs", 0, NULL);
|
||||
chmod(full_path, s.st_mode & 0777);
|
||||
chown(full_path, s.st_uid, s.st_gid);
|
||||
setfilecon(full_path, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
vec_for_each(node->children, child) {
|
||||
snprintf(buf, PATH_MAX, "%s%s/%s", DUMMDIR, full_path, child->name);
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
|
||||
// Create the dummy file/directory
|
||||
if (IS_DIR(child))
|
||||
xmkdir(buf, 0755);
|
||||
else if (IS_REG(child))
|
||||
close(open_new(buf));
|
||||
close(creat(buf, 0644));
|
||||
// Links will be handled later
|
||||
|
||||
if (child->status & IS_VENDOR) {
|
||||
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
|
||||
if (IS_LNK(child)) {
|
||||
cp_afc(MIRRDIR "/system/vendor", "/system/vendor");
|
||||
LOGI("cplink: %s -> %s\n", MIRRDIR "/system/vendor", "/system/vendor");
|
||||
@@ -352,7 +303,7 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
// Mount from module file to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, full_path, child->name);
|
||||
} else if (child->status & (IS_SKEL | IS_INTER)) {
|
||||
// It's a intermediate folder, recursive clone
|
||||
// It's an intermediate folder, recursive clone
|
||||
clone_skeleton(child);
|
||||
continue;
|
||||
} else if (child->status & IS_DUMMY) {
|
||||
@@ -363,7 +314,11 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
if (IS_LNK(child)) {
|
||||
// Copy symlinks directly
|
||||
cp_afc(buf2, buf);
|
||||
LOGI("cplink: %s -> %s\n", buf2, buf);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("cplink: %s -> %s\n",buf2, buf);
|
||||
#else
|
||||
LOGI("cplink: %s\n", buf);
|
||||
#endif
|
||||
} else {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
bind_mount(buf2, buf);
|
||||
@@ -433,27 +388,92 @@ static void simple_mount(const char *path) {
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/*****************
|
||||
* Miscellaneous *
|
||||
*****************/
|
||||
|
||||
#define alt_img ((char *[]) \
|
||||
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
|
||||
|
||||
static int prepare_img() {
|
||||
// Merge images
|
||||
for (int i = 0; alt_img[i]; ++i) {
|
||||
if (merge_img(alt_img[i], MAINIMG)) {
|
||||
LOGE("Image merge %s -> " MAINIMG " failed!\n", alt_img[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (access(MAINIMG, F_OK) == -1) {
|
||||
if (create_img(MAINIMG, 64))
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOGI("* Mounting " MAINIMG "\n");
|
||||
// Mounting magisk image
|
||||
char *magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
if (magiskloop == NULL)
|
||||
return 1;
|
||||
|
||||
xmkdir(COREDIR, 0755);
|
||||
xmkdir(COREDIR "/post-fs-data.d", 0755);
|
||||
xmkdir(COREDIR "/service.d", 0755);
|
||||
xmkdir(COREDIR "/props", 0755);
|
||||
|
||||
DIR *dir = xopendir(MOUNTPOINT);
|
||||
struct dirent *entry;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
|
||||
rm_rf(buf);
|
||||
continue;
|
||||
}
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
|
||||
if (access(buf, F_OK) == 0)
|
||||
continue;
|
||||
vec_push_back(&module_list, strdup(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
// Trim image
|
||||
umount_image(MOUNTPOINT, magiskloop);
|
||||
free(magiskloop);
|
||||
trim_img(MAINIMG);
|
||||
|
||||
// Remount them back :)
|
||||
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
free(magiskloop);
|
||||
|
||||
// Fix file selinux contexts
|
||||
fix_filecon();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fix_filecon() {
|
||||
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(dirfd, 0);
|
||||
close(dirfd);
|
||||
}
|
||||
|
||||
/****************
|
||||
* Entry points *
|
||||
****************/
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unblock_boot_process() {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void post_fs(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
// Start log monitor
|
||||
monitor_logs();
|
||||
|
||||
LOGI("** post-fs mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
@@ -475,81 +495,38 @@ unblock:
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (!check_data())
|
||||
if (!is_daemon_init && !check_data())
|
||||
goto unblock;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Start debug logs in new process
|
||||
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
char *const command[] = { "logcat", "-v", "brief", NULL };
|
||||
debug_log_pid = run_command(0, &debug_log_fd, "/system/bin/logcat", command);
|
||||
close(debug_log_fd);
|
||||
#endif
|
||||
// Start the debug log
|
||||
start_debug_full_log();
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
system("(BOOTMODE=true sh " UNINSTALLER ") &");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
vec_init(&module_list);
|
||||
|
||||
// Cache support
|
||||
if (access("/cache/data_bin", F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
rename("/cache/data_bin", DATABIN);
|
||||
// Initialize
|
||||
if (!is_daemon_init)
|
||||
daemon_init();
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
setenv("BOOTMODE", "true", 1);
|
||||
exec_command(0, NULL, bb_setenv, "sh", UNINSTALLER, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Magisk Manual Injector support
|
||||
if (access("/data/local/tmp/magisk_inject", F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
rename("/data/local/tmp/magisk_inject", DATABIN);
|
||||
}
|
||||
|
||||
// Lazy.... use shell blob
|
||||
system("mv /data/magisk/stock_boot* /data;");
|
||||
|
||||
// Merge images
|
||||
if (merge_img("/cache/magisk.img", MAINIMG)) {
|
||||
LOGE("Image merge %s -> %s failed!\n", "/cache/magisk.img", MAINIMG);
|
||||
goto unblock;
|
||||
}
|
||||
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
|
||||
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
|
||||
goto unblock;
|
||||
}
|
||||
|
||||
int new_img = 0;
|
||||
|
||||
if (access(MAINIMG, F_OK) == -1) {
|
||||
if (create_img(MAINIMG, 64))
|
||||
goto unblock;
|
||||
new_img = 1;
|
||||
}
|
||||
|
||||
LOGI("* Mounting " MAINIMG "\n");
|
||||
// Mounting magisk image
|
||||
char *magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
if (magiskloop == NULL)
|
||||
goto unblock;
|
||||
|
||||
if (new_img) {
|
||||
xmkdir(COREDIR, 0755);
|
||||
xmkdir(COREDIR "/post-fs-data.d", 0755);
|
||||
xmkdir(COREDIR "/service.d", 0755);
|
||||
xmkdir(COREDIR "/props", 0755);
|
||||
}
|
||||
// Merge, trim, mount magisk.img, which will also travel through the modules
|
||||
// After this, it will create the module list
|
||||
if (prepare_img())
|
||||
goto core_only; // Mounting fails, we can only do core only stuffs
|
||||
|
||||
// Run common scripts
|
||||
LOGI("* Running post-fs-data.d scripts\n");
|
||||
@@ -559,119 +536,51 @@ void post_fs_data(int client) {
|
||||
if (access(DISABLEFILE, F_OK) == 0)
|
||||
goto core_only;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
// Execute module scripts
|
||||
LOGI("* Running module post-fs-data scripts\n");
|
||||
exec_module_script("post-fs-data");
|
||||
|
||||
char *module;
|
||||
struct node_entry *sys_root, *ven_root = NULL, *child;
|
||||
|
||||
dir = xopendir(MOUNTPOINT);
|
||||
|
||||
// Create the system root entry
|
||||
sys_root = xcalloc(sizeof(*sys_root), 1);
|
||||
sys_root->name = strdup("/system");
|
||||
sys_root->name = strdup("system");
|
||||
sys_root->status = IS_INTER;
|
||||
|
||||
int has_modules = 0;
|
||||
|
||||
// Travel through each modules
|
||||
vec_init(&module_list);
|
||||
LOGI("* Loading modules\n");
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
|
||||
// Check whether remove
|
||||
snprintf(buf2, PATH_MAX, "%s/remove", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
rm_rf(buf);
|
||||
continue;
|
||||
}
|
||||
// Check whether disable
|
||||
snprintf(buf2, PATH_MAX, "%s/disable", buf);
|
||||
if (access(buf2, F_OK) == 0)
|
||||
continue;
|
||||
// Add the module to list
|
||||
module = strdup(entry->d_name);
|
||||
vec_push_back(&module_list, module);
|
||||
// Read props
|
||||
snprintf(buf2, PATH_MAX, "%s/system.prop", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
read_prop_file(buf2, 0);
|
||||
}
|
||||
// Check whether enable auto_mount
|
||||
snprintf(buf2, PATH_MAX, "%s/auto_mount", buf);
|
||||
if (access(buf2, F_OK) == -1)
|
||||
continue;
|
||||
// Double check whether the system folder exists
|
||||
snprintf(buf2, PATH_MAX, "%s/system", buf);
|
||||
if (access(buf2, F_OK) == -1)
|
||||
continue;
|
||||
|
||||
// Construct structure
|
||||
has_modules = 1;
|
||||
LOGI("%s: constructing magic mount structure\n", module);
|
||||
// If /system/vendor exists in module, create a link outside
|
||||
snprintf(buf2, PATH_MAX, "%s/system/vendor", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
|
||||
unlink(buf);
|
||||
symlink(buf2, buf);
|
||||
}
|
||||
construct_tree(module, sys_root);
|
||||
vec_for_each(&module_list, module) {
|
||||
// Read props
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system.prop", MOUNTPOINT, module);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
read_prop_file(buf, 0);
|
||||
}
|
||||
// Check whether enable auto_mount
|
||||
snprintf(buf, PATH_MAX, "%s/%s/auto_mount", MOUNTPOINT, module);
|
||||
if (access(buf, F_OK) == -1)
|
||||
continue;
|
||||
// Double check whether the system folder exists
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system", MOUNTPOINT, module);
|
||||
if (access(buf, F_OK) == -1)
|
||||
continue;
|
||||
|
||||
// Construct structure
|
||||
has_modules = 1;
|
||||
LOGI("%s: constructing magic mount structure\n", module);
|
||||
// If /system/vendor exists in module, create a link outside
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MOUNTPOINT, module);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
|
||||
unlink(buf2);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
construct_tree(module, sys_root);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
// Trim image
|
||||
umount_image(MOUNTPOINT, magiskloop);
|
||||
free(magiskloop);
|
||||
trim_img(MAINIMG);
|
||||
|
||||
// Remount them back :)
|
||||
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
free(magiskloop);
|
||||
|
||||
if (has_modules) {
|
||||
// Mount mirrors
|
||||
LOGI("* Mounting system/vendor mirrors");
|
||||
int seperate_vendor = 0;
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
snprintf(buf2, PATH_MAX, "%s/system", MIRRDIR);
|
||||
xmkdir_p(buf2, 0755);
|
||||
xmount(buf, buf2, "ext4", MS_RDONLY, NULL);
|
||||
LOGI("mount: %s -> %s\n", buf, buf2);
|
||||
continue;
|
||||
}
|
||||
if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR);
|
||||
xmkdir_p(buf2, 0755);
|
||||
xmount(buf, buf2, "ext4", MS_RDONLY, NULL);
|
||||
LOGI("mount: %s -> %s\n", buf, buf2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
vec_deep_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
snprintf(buf, PATH_MAX, "%s/system/vendor", MIRRDIR);
|
||||
snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR);
|
||||
symlink(buf, buf2);
|
||||
LOGI("link: %s -> %s\n", buf, buf2);
|
||||
}
|
||||
|
||||
// Extract the vendor node out of system tree and swap with placeholder
|
||||
vec_for_each(sys_root->children, child) {
|
||||
if (strcmp(child->name, "vendor") == 0) {
|
||||
@@ -679,10 +588,10 @@ void post_fs_data(int client) {
|
||||
child = xcalloc(sizeof(*child), 1);
|
||||
child->type = seperate_vendor ? DT_LNK : DT_DIR;
|
||||
child->parent = ven_root->parent;
|
||||
child->name = ven_root->name;
|
||||
child->status = IS_VENDOR;
|
||||
vec_entry(sys_root->children)[_] = child;
|
||||
ven_root->name = strdup("/vendor");
|
||||
child->name = strdup("vendor");
|
||||
child->status = 0;
|
||||
// Swap!
|
||||
vec_cur(sys_root->children) = child;
|
||||
ven_root->parent = NULL;
|
||||
break;
|
||||
}
|
||||
@@ -697,10 +606,6 @@ void post_fs_data(int client) {
|
||||
destroy_subtree(sys_root);
|
||||
if (ven_root) destroy_subtree(ven_root);
|
||||
|
||||
// Execute module scripts
|
||||
LOGI("* Running module post-fs-data scripts\n");
|
||||
exec_module_script("post-fs-data");
|
||||
|
||||
core_only:
|
||||
// Systemless hosts
|
||||
if (access(HOSTSFILE, F_OK) == 0) {
|
||||
@@ -708,14 +613,7 @@ core_only:
|
||||
bind_mount(HOSTSFILE, "/system/etc/hosts");
|
||||
}
|
||||
|
||||
// Enable magiskhide by default, only disable when set explicitly
|
||||
char *hide_prop = getprop(MAGISKHIDE_PROP);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
auto_start_magiskhide();
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
@@ -732,37 +630,38 @@ 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");
|
||||
exec_common_script("service");
|
||||
|
||||
// Core only mode
|
||||
if (access(DISABLEFILE, F_OK) == 0) {
|
||||
setprop("ro.magisk.disable", "1");
|
||||
return;
|
||||
}
|
||||
if (access(DISABLEFILE, F_OK) == 0)
|
||||
goto core_only;
|
||||
|
||||
LOGI("* Running module service scripts\n");
|
||||
exec_module_script("service");
|
||||
|
||||
core_only:
|
||||
// Install Magisk Manager if exists
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
while (1) {
|
||||
sleep(5);
|
||||
char *const command[] = { "sh", "-c",
|
||||
"CLASSPATH=/system/framework/pm.jar "
|
||||
"/system/bin/app_process /system/bin "
|
||||
"com.android.commands.pm.Pm install -r " MANAGERAPK, NULL };
|
||||
int apk_res = -1, pid;
|
||||
pid = run_command(1, &apk_res, "/system/bin/sh", command);
|
||||
waitpid(pid, NULL, 0);
|
||||
fdgets(buf, PATH_MAX, apk_res);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (strstr(buf, "Error:") == NULL)
|
||||
break;
|
||||
pid = exec_command(1, &apk_res, pm_setenv,
|
||||
"app_process",
|
||||
"/system/bin", "com.android.commands.pm.Pm",
|
||||
"install", "-r", MANAGERAPK, NULL);
|
||||
if (pid != -1) {
|
||||
waitpid(pid, NULL, 0);
|
||||
fdgets(buf, PATH_MAX, apk_res);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (strstr(buf, "Error:") == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlink(MANAGERAPK);
|
||||
}
|
||||
@@ -773,9 +672,5 @@ void late_start(int client) {
|
||||
buf = buf2 = NULL;
|
||||
vec_deep_destroy(&module_list);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Stop recording the boot logcat after every boot task is done
|
||||
kill(debug_log_pid, SIGTERM);
|
||||
waitpid(debug_log_pid, NULL, 0);
|
||||
#endif
|
||||
stop_debug_full_log();
|
||||
}
|
318
core/jni/core/daemon.c
Normal file
318
core/jni/core/daemon.c
Normal file
@@ -0,0 +1,318 @@
|
||||
/* daemon.c - Magisk Daemon
|
||||
*
|
||||
* Start the daemon and wait for requests
|
||||
* Connect the daemon and send requests through sockets
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
int is_daemon_init = 0, seperate_vendor = 0;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
client_request req = read_int(client);
|
||||
|
||||
struct ucred credential;
|
||||
get_client_cred(client, &credential);
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
case STOP_MAGISKHIDE:
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case LS_HIDELIST:
|
||||
case POST_FS:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
if (credential.uid != 0) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
close(client);
|
||||
return NULL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
launch_magiskhide(client);
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
stop_magiskhide(client);
|
||||
break;
|
||||
case ADD_HIDELIST:
|
||||
add_hide_list(client);
|
||||
break;
|
||||
case RM_HIDELIST:
|
||||
rm_hide_list(client);
|
||||
break;
|
||||
case LS_HIDELIST:
|
||||
ls_hide_list(client);
|
||||
break;
|
||||
case SUPERUSER:
|
||||
su_daemon_receiver(client, &credential);
|
||||
break;
|
||||
case CHECK_VERSION:
|
||||
write_string(client, MAGISK_VER_STR);
|
||||
close(client);
|
||||
break;
|
||||
case CHECK_VERSION_CODE:
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
close(client);
|
||||
break;
|
||||
case POST_FS:
|
||||
post_fs(client);
|
||||
break;
|
||||
case POST_FS_DATA:
|
||||
post_fs_data(client);
|
||||
break;
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void auto_start_magiskhide() {
|
||||
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
}
|
||||
|
||||
void daemon_init() {
|
||||
is_daemon_init = 1;
|
||||
|
||||
// Magisk binaries
|
||||
char *bin_path = NULL;
|
||||
if (access("/cache/data_bin", F_OK) == 0)
|
||||
bin_path = "/cache/data_bin";
|
||||
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/data/com.topjohnwu.magisk/install";
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Migration
|
||||
rm_rf("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink("/data/magisk_debug.log");
|
||||
chmod("/data/adb", 0700);
|
||||
|
||||
// Use shell glob to match files
|
||||
exec_command_sync("sh", "-c",
|
||||
"mv -f /data/adb/magisk/stock_*.img.gz /data;"
|
||||
"rm -f /data/user*/*/magisk.db;", NULL);
|
||||
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin;
|
||||
char buf[PATH_MAX], buf2[PATH_MAX];
|
||||
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir("/root", 0755);
|
||||
chmod("/root", 0755);
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
dir = xfdopendir(sbin);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
linkat(sbin, entry->d_name, root, entry->d_name, 0);
|
||||
if (strcmp(entry->d_name, "magisk") == 0)
|
||||
unlinkat(sbin, entry->d_name, 0);
|
||||
}
|
||||
close(sbin);
|
||||
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
chmod("/sbin", 0755);
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
dir = xfdopendir(root);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
|
||||
xsymlink("/root/magisk", buf2);
|
||||
}
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", init_applet[i]);
|
||||
xsymlink("/root/magiskinit", buf2);
|
||||
}
|
||||
close(root);
|
||||
|
||||
// Backward compatibility
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vec_for_each(&mounts, line) {
|
||||
if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/system");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s -> %s\n", MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdir_p(MIRRDIR "/bin", 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
|
||||
LOGI("* Setting up internal busybox");
|
||||
xmkdir_p(BBPATH, 0755);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
|
||||
void start_daemon() {
|
||||
setsid();
|
||||
setcon("u:r:su:s0");
|
||||
umask(0);
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
// Block user signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
exit(1);
|
||||
xlisten(fd, 10);
|
||||
|
||||
if ((is_daemon_init = (access(MAGISKTMP, F_OK) == 0))) {
|
||||
// Restart stuffs if the daemon is restarted
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
auto_start_magiskhide();
|
||||
start_debug_log();
|
||||
} else if (check_data()) {
|
||||
daemon_init();
|
||||
}
|
||||
|
||||
// Start the log monitor
|
||||
monitor_logs();
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
int *client = xmalloc(sizeof(int));
|
||||
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, request_handler, client);
|
||||
// Detach the thread, we will never join it
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon() {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
// If we cannot access the daemon, we start a daemon in the child process if possible
|
||||
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "No daemon is currently running!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (xfork() == 0) {
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
close(fd);
|
||||
start_daemon();
|
||||
}
|
||||
|
||||
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000);
|
||||
}
|
||||
return fd;
|
||||
}
|
178
core/jni/core/log_monitor.c
Normal file
178
core/jni/core/log_monitor.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/* log_monitor.c - New thread to monitor logcat
|
||||
*
|
||||
* A universal logcat monitor for many usages. Add listeners to the list,
|
||||
* and the pointer of the new log line will be sent through pipes to trigger
|
||||
* asynchronous events without polling
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern int is_daemon_init;
|
||||
|
||||
static int am_proc_start_filter(const char *log) {
|
||||
return strstr(log, "am_proc_start") != NULL;
|
||||
}
|
||||
|
||||
static int magisk_log_filter(const char *log) {
|
||||
char *ss;
|
||||
return (ss = strstr(log, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V');
|
||||
}
|
||||
|
||||
static int magisk_debug_log_filter(const char *log) {
|
||||
return strstr(log, "Magisk") != NULL;
|
||||
}
|
||||
|
||||
struct log_listener log_events[] = {
|
||||
{ /* HIDE_EVENT */
|
||||
.fd = -1,
|
||||
.filter = am_proc_start_filter
|
||||
},
|
||||
{ /* LOG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_log_filter
|
||||
},
|
||||
{ /* DEBUG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_debug_log_filter
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
#endif
|
||||
|
||||
static void *logger_thread(void *args) {
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
|
||||
LOGD("log_monitor: logger start");
|
||||
|
||||
while (1) {
|
||||
// Start logcat
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "all" , "-v", "threadtime", "-s", "am_proc_start", "Magisk", NULL);
|
||||
while (fdgets(line, sizeof(line), log_fd)) {
|
||||
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
|
||||
if (log_events[i].fd > 0 && log_events[i].filter(line)) {
|
||||
char *s = strdup(line);
|
||||
xwrite(log_events[i].fd, &s, sizeof(s));
|
||||
}
|
||||
}
|
||||
if (kill(log_pid, 0))
|
||||
break;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
close(log_fd);
|
||||
log_fd = -1;
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
|
||||
// Clear buffer before restart
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
}
|
||||
|
||||
// Should never be here, but well...
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *magisk_log_thread(void *args) {
|
||||
// Buffer logs before we have data access
|
||||
struct vector logs;
|
||||
vec_init(&logs);
|
||||
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
// Register our listener
|
||||
log_events[LOG_EVENT].fd = pipefd[1];
|
||||
|
||||
LOGD("log_monitor: magisk log dumper start");
|
||||
|
||||
FILE *log = NULL;
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
if (!is_daemon_init) {
|
||||
vec_push_back(&logs, strdup(line));
|
||||
} else {
|
||||
if (log == NULL) {
|
||||
// Dump buffered logs to file
|
||||
log = xfopen(LOGFILE, "w");
|
||||
setbuf(log, NULL);
|
||||
char *tmp;
|
||||
vec_for_each(&logs, tmp) {
|
||||
fprintf(log, "%s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
vec_destroy(&logs);
|
||||
}
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *debug_magisk_log_thread(void *args) {
|
||||
FILE *log = xfopen(DEBUG_LOG, "a");
|
||||
setbuf(log, NULL);
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
LOGD("log_monitor: debug log dumper start");
|
||||
|
||||
// Register our listener
|
||||
log_events[DEBUG_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
|
||||
fprintf(log, "%s", line);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start new threads to monitor logcat and dump to logfile */
|
||||
void monitor_logs() {
|
||||
pthread_t thread;
|
||||
|
||||
// Start log file dumper before monitor
|
||||
xpthread_create(&thread, NULL, magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
|
||||
// Start logcat monitor
|
||||
xpthread_create(&thread, NULL, logger_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
|
||||
}
|
||||
|
||||
void start_debug_full_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Log everything initially
|
||||
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "threadtime", NULL);
|
||||
close(debug_log_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void stop_debug_full_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Stop recording the boot logcat after every boot task is done
|
||||
kill(debug_log_pid, SIGTERM);
|
||||
waitpid(debug_log_pid, NULL, 0);
|
||||
// Start debug thread
|
||||
start_debug_log();
|
||||
#endif
|
||||
}
|
||||
|
||||
void start_debug_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
pthread_t thread;
|
||||
// Start debug thread
|
||||
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
#endif
|
||||
}
|
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "magisk.h"
|
||||
@@ -10,65 +12,64 @@
|
||||
|
||||
char *argv0;
|
||||
|
||||
char *applet[] =
|
||||
{ "su", "resetprop", "magiskpolicy", "supolicy", "sepolicy-inject", "magiskhide", NULL };
|
||||
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
|
||||
|
||||
int (*applet_main[]) (int, char *[]) =
|
||||
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
|
||||
|
||||
// Global error hander function
|
||||
// Should be changed each thread/process
|
||||
__thread void (*err_handler)(void);
|
||||
int create_links(const char *bin, const char *path) {
|
||||
char self[PATH_MAX], linkpath[PATH_MAX];
|
||||
if (bin == NULL) {
|
||||
xreadlink("/proc/self/exe", self, sizeof(self));
|
||||
bin = self;
|
||||
}
|
||||
int ret = 0;
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
|
||||
unlink(linkpath);
|
||||
ret |= symlink(bin, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr,
|
||||
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
|
||||
"\n"
|
||||
"Usage: %s [applet [arguments]...]\n"
|
||||
" or: %s --install [SOURCE] <DIR> \n"
|
||||
" or: %s --list\n"
|
||||
" or: %s --createimg <PATH> <SIZE>\n"
|
||||
" create ext4 image, SIZE is interpreted in MB\n"
|
||||
" or: %s --imgsize <PATH>\n"
|
||||
" or: %s --resizeimg <PATH> <SIZE>\n"
|
||||
" SIZE is interpreted in MB\n"
|
||||
" or: %s --mountimg <IMG> <PATH>\n"
|
||||
" Prints out the loop device\n"
|
||||
" or: %s --umountimg <PATH> <LOOP>\n"
|
||||
" or: %s --[boot stage]\n"
|
||||
" start boot stage service\n"
|
||||
" or: %s [options]\n"
|
||||
" or: applet [arguments]...\n"
|
||||
"\n"
|
||||
"Supported boot stages:\n"
|
||||
" post-fs, post-fs-data, service\n"
|
||||
" or: %s [options]...\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c print client version\n"
|
||||
" -v print daemon version\n"
|
||||
" -V print daemon version code\n"
|
||||
" -c print current binary version\n"
|
||||
" -v print running daemon version\n"
|
||||
" -V print running daemon version code\n"
|
||||
" --list list all available applets\n"
|
||||
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
|
||||
" --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
|
||||
" --imgsize IMG report ext4 image used/total size\n"
|
||||
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
|
||||
" --mountimg IMG PATH mount IMG to PATH and prints the loop device\n"
|
||||
" --umountimg PATH LOOP unmount PATH and delete LOOP device\n"
|
||||
" --[init service] start init service\n"
|
||||
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
|
||||
" --restorecon fix selinux context on Magisk files and folders\n"
|
||||
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
|
||||
"\n"
|
||||
"Supported init services:\n"
|
||||
" daemon, post-fs, post-fs-data, service\n"
|
||||
"\n"
|
||||
"Supported applets:\n"
|
||||
, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
|
||||
, argv0, argv0);
|
||||
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
for (int i = 0; applet[i]; ++i)
|
||||
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
|
||||
fprintf(stderr, "\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
argv0 = argv[0];
|
||||
// Exit the whole app if error occurs by default
|
||||
err_handler = exit_proc;
|
||||
char * arg = strrchr(argv[0], '/');
|
||||
if (arg) ++arg;
|
||||
else arg = argv[0];
|
||||
if (strcmp(arg, "magisk") == 0) {
|
||||
if (strcmp(basename(argv[0]), "magisk") == 0) {
|
||||
if (argc < 2) usage();
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
printf("%s\n", MAGISK_VER_STR);
|
||||
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-v") == 0) {
|
||||
int fd = connect_daemon();
|
||||
@@ -132,6 +133,20 @@ int main(int argc, char *argv[]) {
|
||||
if (argc < 4) usage();
|
||||
umount_image(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
|
||||
unlock_blocks();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--restorecon") == 0) {
|
||||
fix_filecon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--clone-attr") == 0) {
|
||||
if (argc < 4) usage();
|
||||
clone_attr(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--daemon") == 0) {
|
||||
if (xfork() == 0)
|
||||
start_daemon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS);
|
||||
@@ -148,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
528
core/jni/core/magiskinit.c
Normal 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);
|
||||
}
|
@@ -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
158
core/jni/external/Android.mk
vendored
Normal 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/busybox
vendored
Submodule
Submodule core/jni/external/busybox added at e3a1a4d91f
1
core/jni/external/bzip2
vendored
Submodule
1
core/jni/external/bzip2
vendored
Submodule
Submodule core/jni/external/bzip2 added at 67d818584d
1
core/jni/external/dtc
vendored
Submodule
1
core/jni/external/dtc
vendored
Submodule
Submodule core/jni/external/dtc added at 22a65c5331
498
core/jni/external/include/xz_config/config.h
vendored
Normal file
498
core/jni/external/include/xz_config/config.h
vendored
Normal 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
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/selinux
vendored
Submodule
Submodule core/jni/external/selinux added at 8e849a5639
@@ -1,10 +1,11 @@
|
||||
#include <stdbool.h>
|
||||
#include "selinux/avc.h"
|
||||
#include "selinux/context.h"
|
||||
#include "selinux/get_context_list.h"
|
||||
#include "selinux/get_default_type.h"
|
||||
#include "selinux/restorecon.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include <selinux/avc.h>
|
||||
#include <selinux/context.h>
|
||||
#include <selinux/get_context_list.h>
|
||||
#include <selinux/get_default_type.h>
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/restorecon.h>
|
||||
#include <selinux/selinux.h>
|
||||
int is_selinux_enabled(void) { return 0; }
|
||||
int is_selinux_mls_enabled(void) { return 0; }
|
||||
void freecon(char * con) { }
|
||||
@@ -225,7 +226,7 @@ int selinux_raw_to_trans_context(const char * raw,
|
||||
int selinux_raw_context_to_color(const char * raw,
|
||||
char **color_str) { return 0; }
|
||||
int getseuserbyname(const char *linuxuser, char **seuser, char **level) { return 0; }
|
||||
int getseuser(const char *username, const char *service,
|
||||
int getseuser(const char *username, const char *service,
|
||||
char **r_seuser, char **r_level) { return 0; }
|
||||
int selinux_file_context_cmp(const char * a,
|
||||
const char * b) { return 0; }
|
1
core/jni/external/xz
vendored
Submodule
1
core/jni/external/xz
vendored
Submodule
Submodule core/jni/external/xz added at 3d566cd519
60
core/jni/include/cpio.h
Normal file
60
core/jni/include/cpio.h
Normal 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
|
@@ -5,8 +5,10 @@
|
||||
#define _DAEMON_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
extern pthread_t sepol_patch;
|
||||
extern int is_daemon_init, seperate_vendor;
|
||||
|
||||
// Commands require connecting to daemon
|
||||
typedef enum {
|
||||
@@ -15,6 +17,7 @@ typedef enum {
|
||||
STOP_MAGISKHIDE,
|
||||
ADD_HIDELIST,
|
||||
RM_HIDELIST,
|
||||
LS_HIDELIST,
|
||||
SUPERUSER,
|
||||
CHECK_VERSION,
|
||||
CHECK_VERSION_CODE,
|
||||
@@ -37,11 +40,14 @@ typedef enum {
|
||||
|
||||
// daemon.c
|
||||
|
||||
void start_daemon(int client);
|
||||
void start_daemon();
|
||||
int connect_daemon();
|
||||
void auto_start_magiskhide();
|
||||
void daemon_init();
|
||||
|
||||
// socket_trans.c
|
||||
// socket.c
|
||||
|
||||
int setup_socket(struct sockaddr_un *sun);
|
||||
int recv_fd(int sockfd);
|
||||
void send_fd(int sockfd, int fd);
|
||||
int read_int(int fd);
|
||||
@@ -49,10 +55,6 @@ void write_int(int fd, int val);
|
||||
char* read_string(int fd);
|
||||
void write_string(int fd, const char* val);
|
||||
|
||||
// log_monitor.c
|
||||
|
||||
void monitor_logs();
|
||||
|
||||
/***************
|
||||
* Boot Stages *
|
||||
***************/
|
||||
@@ -60,6 +62,7 @@ void monitor_logs();
|
||||
void post_fs(int client);
|
||||
void post_fs_data(int client);
|
||||
void late_start(int client);
|
||||
void fix_filecon();
|
||||
|
||||
/**************
|
||||
* MagiskHide *
|
||||
@@ -69,11 +72,12 @@ void launch_magiskhide(int client);
|
||||
void stop_magiskhide(int client);
|
||||
void add_hide_list(int client);
|
||||
void rm_hide_list(int client);
|
||||
void ls_hide_list(int client);
|
||||
|
||||
/*************
|
||||
* Superuser *
|
||||
*************/
|
||||
|
||||
void su_daemon_receiver(int client);
|
||||
void su_daemon_receiver(int client, struct ucred *credential);
|
||||
|
||||
#endif
|
86
core/jni/include/logging.h
Normal file
86
core/jni/include/logging.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* logging.h - Error handling and logging
|
||||
*/
|
||||
|
||||
#ifndef _LOGGING_H_
|
||||
#define _LOGGING_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define str(a) #a
|
||||
#define xstr(a) str(a)
|
||||
|
||||
/**************
|
||||
* No logging *
|
||||
**************/
|
||||
|
||||
#define LOGD(...)
|
||||
#define LOGI(...)
|
||||
#define LOGW(...)
|
||||
#define LOGE(...)
|
||||
#define PLOGE(...)
|
||||
|
||||
/******************
|
||||
* Daemon logging *
|
||||
******************/
|
||||
|
||||
#ifdef IS_DAEMON
|
||||
|
||||
#undef LOGI
|
||||
#undef LOGW
|
||||
#undef LOGE
|
||||
#undef PLOGE
|
||||
|
||||
#include <pthread.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "Magisk"
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
#undef LOGD
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
|
||||
enum {
|
||||
HIDE_EVENT,
|
||||
LOG_EVENT,
|
||||
DEBUG_EVENT
|
||||
};
|
||||
|
||||
struct log_listener {
|
||||
int fd;
|
||||
int (*filter) (const char*);
|
||||
};
|
||||
|
||||
extern struct log_listener log_events[];
|
||||
|
||||
void monitor_logs();
|
||||
void start_debug_full_log();
|
||||
void stop_debug_full_log();
|
||||
void start_debug_log();
|
||||
|
||||
#endif
|
||||
|
||||
/********************
|
||||
* Tools Log & Exit *
|
||||
********************/
|
||||
|
||||
#ifdef XWRAP_EXIT
|
||||
|
||||
#undef LOGE
|
||||
#undef PLOGE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
|
||||
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _LOGGING_H_
|
74
core/jni/include/magisk.h
Normal file
74
core/jni/include/magisk.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* magisk.h - Top header
|
||||
*/
|
||||
|
||||
#ifndef _MAGISK_H_
|
||||
#define _MAGISK_H_
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
|
||||
#define REQUESTOR_DAEMON_PATH "\0MAGISK"
|
||||
|
||||
#ifndef ARG_MAX
|
||||
#define ARG_MAX 4096
|
||||
#endif
|
||||
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define DEBUG_LOG "/data/adb/magisk_debug.log"
|
||||
#define UNBLOCKFILE "/dev/.magisk.unblock"
|
||||
#define PATCHDONE "/dev/.magisk.patch.done"
|
||||
#define DISABLEFILE "/cache/.disable_magisk"
|
||||
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
|
||||
#define CACHEMOUNT "/cache/magisk_mount"
|
||||
#define MAGISKTMP "/sbin/.core"
|
||||
#define MIRRDIR MAGISKTMP "/mirror"
|
||||
#define BBPATH MAGISKTMP "/busybox"
|
||||
#define MOUNTPOINT MAGISKTMP "/img"
|
||||
#define COREDIR MOUNTPOINT "/.core"
|
||||
#define HOSTSFILE COREDIR "/hosts"
|
||||
#define HIDELIST COREDIR "/hidelist"
|
||||
#define MAINIMG "/data/adb/magisk.img"
|
||||
#define DATABIN "/data/adb/magisk"
|
||||
#define MANAGERAPK DATABIN "/magisk.apk"
|
||||
#define MAGISKRC "/init.magisk.rc"
|
||||
|
||||
|
||||
// selinuxfs paths
|
||||
#define SELINUX_PATH "/sys/fs/selinux/"
|
||||
#define SELINUX_ENFORCE SELINUX_PATH "enforce"
|
||||
#define SELINUX_POLICY SELINUX_PATH "policy"
|
||||
#define SELINUX_LOAD SELINUX_PATH "load"
|
||||
|
||||
// split policy paths
|
||||
#define PLAT_POLICY_DIR "/system/etc/selinux/"
|
||||
#define NONPLAT_POLICY_DIR "/vendor/etc/selinux/"
|
||||
#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil"
|
||||
#define SPLIT_PLAT_MAPPING PLAT_POLICY_DIR "mapping/%s.cil"
|
||||
#define SPLIT_PRECOMPILE NONPLAT_POLICY_DIR "precompiled_sepolicy"
|
||||
#define SPLIT_NONPLAT_VER NONPLAT_POLICY_DIR "plat_sepolicy_vers.txt"
|
||||
|
||||
#define MAGISKHIDE_PROP "persist.magisk.hide"
|
||||
|
||||
extern char *argv0; /* For changing process name */
|
||||
|
||||
#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL })
|
||||
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
|
||||
|
||||
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
|
||||
|
||||
int create_links(const char *bin, const char *path);
|
||||
|
||||
// Multi-call entrypoints
|
||||
int magiskhide_main(int argc, char *argv[]);
|
||||
int magiskpolicy_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
44
core/jni/include/magiskrc.h
Normal file
44
core/jni/include/magiskrc.h
Normal 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"
|
||||
;
|
@@ -12,9 +12,11 @@ int prop_exist(const char *name);
|
||||
int setprop(const char *name, const char *value);
|
||||
int setprop2(const char *name, const char *value, const int trigger);
|
||||
char *getprop(const char *name);
|
||||
int deleteprop(const char *name, const int trigger);
|
||||
char *getprop2(const char *name, int persist);
|
||||
int deleteprop(const char *name);
|
||||
int deleteprop2(const char *name, const int persist);
|
||||
int read_prop_file(const char* filename, const int trigger);
|
||||
void getprop_all(void (*cbk)(const char *name));
|
||||
void getprop_all(void (*callback)(const char*, const char*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
@@ -7,7 +7,6 @@
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -18,8 +17,6 @@
|
||||
#define UID_SYSTEM (get_system_uid())
|
||||
#define UID_RADIO (get_radio_uid())
|
||||
|
||||
extern int quit_signals[];
|
||||
|
||||
// xwrap.c
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
@@ -28,12 +25,14 @@ FILE *xfdopen(int fd, const char *mode);
|
||||
#define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
|
||||
int xopen2(const char *pathname, int flags);
|
||||
int xopen3(const char *pathname, int flags, mode_t mode);
|
||||
int xopenat(int dirfd, const char *pathname, int flags);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
int xpipe2(int pipefd[2], int flags);
|
||||
int xsetns(int fd, int nstype);
|
||||
DIR *xopendir(const char *name);
|
||||
DIR *xfdopendir(int fd);
|
||||
struct dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xsocket(int domain, int type, int protocol);
|
||||
@@ -53,52 +52,100 @@ int xstat(const char *pathname, struct stat *buf);
|
||||
int xlstat(const char *pathname, struct stat *buf);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount(const char *target);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xchmod(const char *pathname, mode_t mode);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdir_p(const char *pathname, mode_t mode);
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
int xmkdir_p(const char *pathname, mode_t mode);
|
||||
pid_t xfork();
|
||||
|
||||
// misc.c
|
||||
|
||||
#define quit_signals ((int []) { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 })
|
||||
|
||||
unsigned get_shell_uid();
|
||||
unsigned get_system_uid();
|
||||
unsigned get_radio_uid();
|
||||
int check_data();
|
||||
int file_to_vector(const char* filename, struct vector *v);
|
||||
int vector_to_file(const char* filename, struct vector *v);
|
||||
int isNum(const char *s);
|
||||
ssize_t fdgets(char *buf, size_t size, int fd);
|
||||
void ps(void (*func)(int));
|
||||
void ps_filter_proc_name(const char *filter, void (*func)(int));
|
||||
int create_links(const char *bin, const char *path);
|
||||
void unlock_blocks();
|
||||
void setup_sighandlers(void (*handler)(int));
|
||||
int run_command(int err, int *fd, const char *path, char *const argv[]);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
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);
|
||||
int cp_afc(const char *source, const char *target);
|
||||
int clone_dir(const char *source, const char *target);
|
||||
int rm_rf(const char *target);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void get_client_cred(int fd, struct ucred *cred);
|
||||
int switch_mnt_ns(int pid);
|
||||
int fork_dont_care();
|
||||
void wait_till_exists(const char *target);
|
||||
|
||||
// file.c
|
||||
|
||||
extern char **excl_list;
|
||||
|
||||
struct file_attr {
|
||||
struct stat st;
|
||||
char con[128];
|
||||
};
|
||||
|
||||
int fd_getpath(int fd, char *path, size_t size);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*));
|
||||
void rm_rf(const char *path);
|
||||
void frm_rf(int dirfd);
|
||||
void mv_f(const char *source, const char *destination);
|
||||
void mv_dir(int src, int dest);
|
||||
void cp_afc(const char *source, const char *destination);
|
||||
void clone_dir(int src, int dest);
|
||||
int getattr(const char *path, struct file_attr *a);
|
||||
int getattrat(int dirfd, const char *pathname, struct file_attr *a);
|
||||
int fgetattr(int fd, struct file_attr *a);
|
||||
int setattr(const char *path, struct file_attr *a);
|
||||
int setattrat(int dirfd, const char *pathname, struct file_attr *a);
|
||||
int fsetattr(int fd, struct file_attr *a);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void restorecon(int dirfd, int force);
|
||||
int mmap_ro(const char *filename, void **buf, size_t *size);
|
||||
int mmap_rw(const char *filename, void **buf, size_t *size);
|
||||
void fd_full_read(int fd, void **buf, size_t *size);
|
||||
void full_read(const char *filename, void **buf, size_t *size);
|
||||
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size);
|
||||
void stream_full_read(int fd, void **buf, size_t *size);
|
||||
void write_zero(int fd, size_t size);
|
||||
void mem_align(size_t *pos, size_t align);
|
||||
void file_align(int fd, size_t align, int out);
|
||||
|
||||
// img.c
|
||||
|
||||
#define round_size(a) ((((a) / 32) + 2) * 32)
|
||||
#define SOURCE_TMP "/dev/source"
|
||||
#define TARGET_TMP "/dev/target"
|
||||
|
||||
int create_img(const char *img, int size);
|
||||
int get_img_size(const char *img, int *used, int *total);
|
||||
int resize_img(const char *img, int size);
|
||||
char *mount_image(const char *img, const char *target);
|
||||
void umount_image(const char *target, const char *device);
|
||||
int 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
|
@@ -7,8 +7,8 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
struct vector {
|
||||
size_t size;
|
||||
size_t cap;
|
||||
unsigned size;
|
||||
unsigned cap;
|
||||
void **data;
|
||||
};
|
||||
|
||||
@@ -26,10 +26,12 @@ struct vector *vec_dup(struct vector *v);
|
||||
/* Usage: vec_for_each(vector *v, void *e) */
|
||||
#define vec_for_each(v, e) \
|
||||
e = v ? (v)->data[0] : NULL; \
|
||||
for (size_t _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
|
||||
for (int _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
|
||||
|
||||
#define vec_for_each_r(v, e) \
|
||||
e = v ? (v)->data[(v)->size - 1] : NULL; \
|
||||
for (size_t _ = (v)->size; v && _ > 0; --_, e = (v)->data[_ - 1])
|
||||
e = (v && (v)->size > 0) ? (v)->data[(v)->size - 1] : NULL; \
|
||||
for (int _ = ((int) (v)->size) - 1; v && _ >= 0; --_, e = (v)->data[_])
|
||||
|
||||
#endif
|
||||
#define vec_cur(v) vec_entry(v)[_]
|
||||
|
||||
#endif
|
306
core/jni/magiskboot/bootimg.c
Normal file
306
core/jni/magiskboot/bootimg.c
Normal file
@@ -0,0 +1,306 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
#include "magiskboot.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define INSUF_BLOCK_RET 2
|
||||
#define CHROMEOS_RET 3
|
||||
#define ELF32_RET 4
|
||||
#define ELF64_RET 5
|
||||
|
||||
static void dump(void *buf, size_t size, const char *filename) {
|
||||
int fd = creat(filename, 0644);
|
||||
xwrite(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static size_t restore(const char *filename, int fd) {
|
||||
int ifd = xopen(filename, O_RDONLY);
|
||||
size_t size = lseek(ifd, 0, SEEK_END);
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
xsendfile(fd, ifd, NULL, size);
|
||||
close(ifd);
|
||||
return size;
|
||||
}
|
||||
|
||||
static void restore_buf(int fd, const void *buf, size_t size) {
|
||||
xwrite(fd, buf, size);
|
||||
}
|
||||
|
||||
static void print_hdr(const boot_img_hdr *hdr) {
|
||||
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr->kernel_size, hdr->kernel_addr);
|
||||
fprintf(stderr, "RAMDISK [%d] @ 0x%08x\n", hdr->ramdisk_size, hdr->ramdisk_addr);
|
||||
fprintf(stderr, "SECOND [%d] @ 0x%08x\n", hdr->second_size, hdr->second_addr);
|
||||
fprintf(stderr, "EXTRA [%d] @ 0x%08x\n", hdr->extra_size, hdr->tags_addr);
|
||||
fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size);
|
||||
if (hdr->os_version != 0) {
|
||||
int a,b,c,y,m = 0;
|
||||
int os_version, os_patch_level;
|
||||
os_version = hdr->os_version >> 11;
|
||||
os_patch_level = hdr->os_version & 0x7ff;
|
||||
|
||||
a = (os_version >> 14) & 0x7f;
|
||||
b = (os_version >> 7) & 0x7f;
|
||||
c = os_version & 0x7f;
|
||||
fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c);
|
||||
|
||||
y = (os_patch_level >> 4) + 2000;
|
||||
m = os_patch_level & 0xf;
|
||||
fprintf(stderr, "PATCH_LEVEL [%d-%02d]\n", y, m);
|
||||
}
|
||||
fprintf(stderr, "NAME [%s]\n", hdr->name);
|
||||
fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline);
|
||||
}
|
||||
|
||||
int parse_img(const char *image, boot_img *boot) {
|
||||
memset(boot, 0, sizeof(*boot));
|
||||
int is_blk = mmap_ro(image, &boot->map_addr, &boot->map_size);
|
||||
|
||||
// Parse image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
||||
for (size_t pos = 0; pos < boot->map_size; pos += 256) {
|
||||
switch (check_type(boot->map_addr + pos)) {
|
||||
case CHROMEOS:
|
||||
// The caller should know it's chromeos, as it needs additional signing
|
||||
boot->flags |= CHROMEOS_FLAG;
|
||||
continue;
|
||||
case ELF32:
|
||||
exit(ELF32_RET);
|
||||
case ELF64:
|
||||
exit(ELF64_RET);
|
||||
case AOSP:
|
||||
// Read the header
|
||||
memcpy(&boot->hdr, boot->map_addr + pos, sizeof(boot->hdr));
|
||||
pos += boot->hdr.page_size;
|
||||
|
||||
print_hdr(&boot->hdr);
|
||||
|
||||
boot->kernel = boot->map_addr + pos;
|
||||
pos += boot->hdr.kernel_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
|
||||
boot->ramdisk = boot->map_addr + pos;
|
||||
pos += boot->hdr.ramdisk_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
|
||||
if (boot->hdr.second_size) {
|
||||
boot->second = boot->map_addr + pos;
|
||||
pos += boot->hdr.second_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (boot->hdr.extra_size) {
|
||||
boot->extra = boot->map_addr + pos;
|
||||
pos += boot->hdr.extra_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (pos < boot->map_size) {
|
||||
boot->tail = boot->map_addr + pos;
|
||||
boot->tail_size = boot->map_size - pos;
|
||||
}
|
||||
|
||||
// Search for dtb in kernel
|
||||
for (uint32_t i = 0; i < boot->hdr.kernel_size; ++i) {
|
||||
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
|
||||
boot->dtb = boot->kernel + i;
|
||||
boot->dt_size = boot->hdr.kernel_size - i;
|
||||
boot->hdr.kernel_size = i;
|
||||
fprintf(stderr, "DTB [%u]\n", boot->dt_size);
|
||||
}
|
||||
}
|
||||
|
||||
boot->ramdisk_type = check_type(boot->ramdisk);
|
||||
boot->kernel_type = check_type(boot->kernel);
|
||||
|
||||
// Check MTK
|
||||
if (boot->kernel_type == MTK) {
|
||||
fprintf(stderr, "MTK_KERNEL_HDR [512]\n");
|
||||
boot->flags |= MTK_KERNEL;
|
||||
memcpy(&boot->mtk_kernel_hdr, boot->kernel, sizeof(mtk_hdr));
|
||||
boot->kernel += 512;
|
||||
boot->hdr.kernel_size -= 512;
|
||||
boot->kernel_type = check_type(boot->kernel);
|
||||
}
|
||||
if (boot->ramdisk_type == MTK) {
|
||||
fprintf(stderr, "MTK_RAMDISK_HDR [512]\n");
|
||||
boot->flags |= MTK_RAMDISK;
|
||||
memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr));
|
||||
boot->ramdisk += 512;
|
||||
boot->hdr.ramdisk_size -= 512;
|
||||
boot->ramdisk_type = check_type(boot->ramdisk);
|
||||
}
|
||||
|
||||
char fmt[16];
|
||||
|
||||
get_type_name(boot->kernel_type, fmt);
|
||||
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
|
||||
get_type_name(boot->ramdisk_type, fmt);
|
||||
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt);
|
||||
|
||||
return boot->flags & CHROMEOS_FLAG ? CHROMEOS_RET :
|
||||
((is_blk && boot->tail_size < 500 * 1024) ? INSUF_BLOCK_RET : 0);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOGE("No boot image magic found!\n");
|
||||
}
|
||||
|
||||
void unpack(const char* image) {
|
||||
boot_img boot;
|
||||
int ret = parse_img(image, &boot);
|
||||
int fd;
|
||||
|
||||
// Dump kernel
|
||||
if (COMPRESSED(boot.kernel_type)) {
|
||||
fd = creat(KERNEL_FILE, 0644);
|
||||
decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE);
|
||||
}
|
||||
|
||||
if (boot.dt_size) {
|
||||
// Dump dtb
|
||||
dump(boot.dtb, boot.dt_size, DTB_FILE);
|
||||
}
|
||||
|
||||
// Dump ramdisk
|
||||
if (COMPRESSED(boot.ramdisk_type)) {
|
||||
fd = creat(RAMDISK_FILE, 0644);
|
||||
decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw");
|
||||
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
|
||||
}
|
||||
|
||||
if (boot.hdr.second_size) {
|
||||
// Dump second
|
||||
dump(boot.second, boot.hdr.second_size, SECOND_FILE);
|
||||
}
|
||||
|
||||
if (boot.hdr.extra_size) {
|
||||
// Dump extra
|
||||
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
|
||||
}
|
||||
|
||||
munmap(boot.map_addr, boot.map_size);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void repack(const char* orig_image, const char* out_image) {
|
||||
boot_img boot;
|
||||
|
||||
// There are possible two MTK headers
|
||||
size_t mtk_kernel_off, mtk_ramdisk_off;
|
||||
|
||||
// Parse original image
|
||||
parse_img(orig_image, &boot);
|
||||
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
|
||||
|
||||
// Create new image
|
||||
int fd = creat(out_image, 0644);
|
||||
|
||||
// Skip a page for header
|
||||
write_zero(fd, boot.hdr.page_size);
|
||||
|
||||
if (boot.flags & MTK_KERNEL) {
|
||||
// Record position and skip MTK header
|
||||
mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
|
||||
write_zero(fd, 512);
|
||||
}
|
||||
if (COMPRESSED(boot.kernel_type)) {
|
||||
size_t raw_size;
|
||||
void *kernel_raw;
|
||||
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
|
||||
boot.hdr.kernel_size = comp(boot.kernel_type, fd, kernel_raw, raw_size);
|
||||
munmap(kernel_raw, raw_size);
|
||||
} else {
|
||||
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
|
||||
}
|
||||
// Restore dtb
|
||||
if (boot.dt_size && access(DTB_FILE, R_OK) == 0) {
|
||||
boot.hdr.kernel_size += restore(DTB_FILE, fd);
|
||||
}
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
|
||||
if (boot.flags & MTK_RAMDISK) {
|
||||
// Record position and skip MTK header
|
||||
mtk_ramdisk_off = lseek(fd, 0, SEEK_CUR);
|
||||
write_zero(fd, 512);
|
||||
}
|
||||
if (access(RAMDISK_FILE, R_OK) == 0) {
|
||||
// If we found raw cpio, compress to original format
|
||||
size_t cpio_size;
|
||||
void *cpio;
|
||||
mmap_ro(RAMDISK_FILE, &cpio, &cpio_size);
|
||||
boot.hdr.ramdisk_size = comp(boot.ramdisk_type, fd, cpio, cpio_size);
|
||||
munmap(cpio, cpio_size);
|
||||
} else {
|
||||
// Find compressed ramdisk
|
||||
char name[PATH_MAX];
|
||||
int found = 0;
|
||||
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
|
||||
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
|
||||
if (access(name, R_OK) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
LOGE("No ramdisk exists!\n");
|
||||
boot.hdr.ramdisk_size = restore(name, fd);
|
||||
}
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
|
||||
// Restore second
|
||||
if (boot.hdr.second_size && access(SECOND_FILE, R_OK) == 0) {
|
||||
boot.hdr.second_size = restore(SECOND_FILE, fd);
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Restore extra
|
||||
if (boot.hdr.extra_size && access(EXTRA_FILE, R_OK) == 0) {
|
||||
boot.hdr.extra_size = restore(EXTRA_FILE, fd);
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
|
||||
if (boot.tail_size >= 16) {
|
||||
if (memcmp(boot.tail, "SEANDROIDENFORCE", 16) == 0 ||
|
||||
memcmp(boot.tail, LG_BUMP_MAGIC, 16) == 0 ) {
|
||||
restore_buf(fd, boot.tail, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// Write MTK headers back
|
||||
if (boot.flags & MTK_KERNEL) {
|
||||
lseek(fd, mtk_kernel_off, SEEK_SET);
|
||||
boot.mtk_kernel_hdr.size = boot.hdr.kernel_size;
|
||||
boot.hdr.kernel_size += 512;
|
||||
restore_buf(fd, &boot.mtk_kernel_hdr, sizeof(mtk_hdr));
|
||||
}
|
||||
if (boot.flags & MTK_RAMDISK) {
|
||||
lseek(fd, mtk_ramdisk_off, SEEK_SET);
|
||||
boot.mtk_ramdisk_hdr.size = boot.hdr.ramdisk_size;
|
||||
boot.hdr.ramdisk_size += 512;
|
||||
restore_buf(fd, &boot.mtk_ramdisk_hdr, sizeof(mtk_hdr));
|
||||
}
|
||||
// Main header
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
restore_buf(fd, &boot.hdr, sizeof(boot.hdr));
|
||||
|
||||
// Print new image info
|
||||
print_hdr(&boot.hdr);
|
||||
|
||||
munmap(boot.map_addr, boot.map_size);
|
||||
close(fd);
|
||||
}
|
@@ -2,20 +2,21 @@
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
#ifndef _BOOT_IMAGE_H_
|
||||
#define _BOOT_IMAGE_H_
|
||||
@@ -43,7 +44,7 @@ struct boot_img_hdr
|
||||
|
||||
uint32_t tags_addr; /* physical addr for kernel tags */
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t dt_size; /* device tree in bytes */
|
||||
uint32_t extra_size; /* extra blob size in bytes */
|
||||
|
||||
/* operating system version and security patch level; for
|
||||
* version "A.B.C" and patch level "Y-M-D":
|
||||
@@ -64,22 +65,22 @@ struct boot_img_hdr
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
** +-----------------+
|
||||
** +-----------------+
|
||||
** | boot header | 1 page
|
||||
** +-----------------+
|
||||
** | kernel | n pages
|
||||
** | kernel | n pages
|
||||
** +-----------------+
|
||||
** | ramdisk | m pages
|
||||
** | ramdisk | m pages
|
||||
** +-----------------+
|
||||
** | second stage | o pages
|
||||
** +-----------------+
|
||||
** | device tree | p pages
|
||||
** | extra blobs | p pages
|
||||
** +-----------------+
|
||||
**
|
||||
** n = (kernel_size + page_size - 1) / page_size
|
||||
** m = (ramdisk_size + page_size - 1) / page_size
|
||||
** o = (second_size + page_size - 1) / page_size
|
||||
** p = (dt_size + page_size - 1) / page_size
|
||||
** p = (extra_size + page_size - 1) / page_size
|
||||
**
|
||||
** 0. all entities are page_size aligned in flash
|
||||
** 1. kernel and ramdisk are required (size != 0)
|
||||
@@ -99,4 +100,28 @@ typedef struct mtk_hdr {
|
||||
uint8_t name[32]; /* The type of the header */
|
||||
} mtk_hdr;
|
||||
|
||||
// Flags
|
||||
#define MTK_KERNEL 0x1
|
||||
#define MTK_RAMDISK 0x2
|
||||
#define CHROMEOS_FLAG 0x4
|
||||
|
||||
typedef struct boot_img {
|
||||
size_t map_size;
|
||||
uint32_t dt_size;
|
||||
size_t tail_size;
|
||||
uint8_t flags;
|
||||
file_t kernel_type, ramdisk_type;
|
||||
|
||||
boot_img_hdr hdr;
|
||||
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
|
||||
|
||||
void *map_addr;
|
||||
void *kernel;
|
||||
void *dtb;
|
||||
void *ramdisk;
|
||||
void *second;
|
||||
void *extra;
|
||||
void *tail;
|
||||
} boot_img;
|
||||
|
||||
#endif
|
512
core/jni/magiskboot/compress.c
Normal file
512
core/jni/magiskboot/compress.c
Normal file
@@ -0,0 +1,512 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
#include <lz4hc.h>
|
||||
#include <bzlib.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define CHUNK 0x40000
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
size_t gzip(int mode, int fd, const void *buf, size_t size) {
|
||||
size_t ret = 0, have, total = 0;
|
||||
z_stream strm;
|
||||
unsigned char out[CHUNK];
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = inflateInit2(&strm, 15 | 16);
|
||||
break;
|
||||
case 1:
|
||||
ret = deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != Z_OK)
|
||||
LOGE("Unable to init zlib stream\n");
|
||||
|
||||
strm.next_in = (void *) buf;
|
||||
strm.avail_in = size;
|
||||
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = inflate(&strm, Z_FINISH);
|
||||
break;
|
||||
case 1:
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
break;
|
||||
}
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
LOGE("Error when running gzip\n");
|
||||
have = CHUNK - strm.avail_out;
|
||||
total += xwrite(fd, out, have);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
inflateEnd(&strm);
|
||||
break;
|
||||
case 1:
|
||||
deflateEnd(&strm);
|
||||
break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Mode: 0 = decode xz/lzma; 1 = encode xz; 2 = encode lzma
|
||||
size_t lzma(int mode, int fd, const void *buf, size_t size) {
|
||||
size_t have, total = 0;
|
||||
lzma_ret ret = 0;
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
lzma_options_lzma opt;
|
||||
unsigned char out[CHUNK];
|
||||
|
||||
// Initialize preset
|
||||
lzma_lzma_preset(&opt, 9);
|
||||
lzma_filter filters[] = {
|
||||
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
|
||||
{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
|
||||
};
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = lzma_auto_decoder(&strm, UINT64_MAX, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
|
||||
break;
|
||||
case 2:
|
||||
ret = lzma_alone_encoder(&strm, &opt);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (ret != LZMA_OK)
|
||||
LOGE("Unable to init lzma stream\n");
|
||||
|
||||
strm.next_in = buf;
|
||||
strm.avail_in = size;
|
||||
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
ret = lzma_code(&strm, LZMA_FINISH);
|
||||
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
|
||||
LOGE("LZMA error %d!\n", ret);
|
||||
have = CHUNK - strm.avail_out;
|
||||
total += xwrite(fd, out, have);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
lzma_end(&strm);
|
||||
return total;
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
size_t lz4(int mode, int fd, const void *buf, size_t size) {
|
||||
LZ4F_decompressionContext_t dctx;
|
||||
LZ4F_compressionContext_t cctx;
|
||||
LZ4F_frameInfo_t info;
|
||||
|
||||
size_t blockSize, outCapacity, avail_in, ret = 0, pos = 0, total = 0;
|
||||
size_t have, read;
|
||||
void *out = NULL;
|
||||
|
||||
// Initialize context
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
|
||||
break;
|
||||
case 1:
|
||||
ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE("Context creation error: %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
// Allocate out buffer
|
||||
blockSize = 1 << 22;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
// Read header
|
||||
read = blockSize;
|
||||
ret = LZ4F_getFrameInfo(dctx, &info, buf, &read);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE("LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(ret));
|
||||
switch (info.blockSizeID) {
|
||||
case LZ4F_default:
|
||||
case LZ4F_max64KB: outCapacity = 1 << 16; break;
|
||||
case LZ4F_max256KB: outCapacity = 1 << 18; break;
|
||||
case LZ4F_max1MB: outCapacity = 1 << 20; break;
|
||||
case LZ4F_max4MB: outCapacity = 1 << 22; break;
|
||||
default:
|
||||
LOGE("Impossible unless more block sizes are allowed\n");
|
||||
}
|
||||
pos += read;
|
||||
break;
|
||||
case 1:
|
||||
outCapacity = LZ4F_compressFrameBound(blockSize, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
out = xmalloc(outCapacity);
|
||||
|
||||
// Write header
|
||||
if (mode == 1) {
|
||||
LZ4F_preferences_t prefs;
|
||||
memset(&prefs, 0, sizeof(prefs));
|
||||
prefs.autoFlush = 1;
|
||||
prefs.compressionLevel = 9;
|
||||
prefs.frameInfo.blockMode = 1;
|
||||
prefs.frameInfo.blockSizeID = 7;
|
||||
prefs.frameInfo.contentChecksumFlag = 1;
|
||||
have = ret = LZ4F_compressBegin(cctx, out, size, &prefs);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE("Failed to start compression: error %s\n", LZ4F_getErrorName(ret));
|
||||
total += xwrite(fd, out, have);
|
||||
}
|
||||
|
||||
do {
|
||||
if (pos + blockSize >= size) {
|
||||
avail_in = size - pos;
|
||||
} else {
|
||||
avail_in = blockSize;
|
||||
}
|
||||
|
||||
do {
|
||||
switch(mode) {
|
||||
case 0:
|
||||
have = outCapacity;
|
||||
read = avail_in;
|
||||
ret = LZ4F_decompress(dctx, out, &have, buf + pos, &read, NULL);
|
||||
break;
|
||||
case 1:
|
||||
read = avail_in;
|
||||
have = ret = LZ4F_compressUpdate(cctx, out, outCapacity, buf + pos, avail_in, NULL);
|
||||
break;
|
||||
}
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE("LZ4 coding error: %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
total += xwrite(fd, out, have);
|
||||
// Update status
|
||||
pos += read;
|
||||
avail_in -= read;
|
||||
} while(avail_in != 0 && ret != 0);
|
||||
|
||||
} while(pos < size && ret != 0);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
LZ4F_freeDecompressionContext(dctx);
|
||||
break;
|
||||
case 1:
|
||||
have = ret = LZ4F_compressEnd(cctx, out, outCapacity, NULL);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE("Failed to end compression: error %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
total += xwrite(fd, out, have);
|
||||
|
||||
LZ4F_freeCompressionContext(cctx);
|
||||
break;
|
||||
}
|
||||
|
||||
free(out);
|
||||
return total;
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
size_t bzip2(int mode, int fd, const void* buf, size_t size) {
|
||||
size_t ret = 0, have, total = 0;
|
||||
bz_stream strm;
|
||||
char out[CHUNK];
|
||||
|
||||
strm.bzalloc = NULL;
|
||||
strm.bzfree = NULL;
|
||||
strm.opaque = NULL;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = BZ2_bzDecompressInit(&strm, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = BZ2_bzCompressInit(&strm, 9, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != BZ_OK)
|
||||
LOGE("Unable to init bzlib stream\n");
|
||||
|
||||
strm.next_in = (void *) buf;
|
||||
strm.avail_in = size;
|
||||
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = BZ2_bzDecompress(&strm);
|
||||
break;
|
||||
case 1:
|
||||
ret = BZ2_bzCompress(&strm, BZ_FINISH);
|
||||
break;
|
||||
}
|
||||
have = CHUNK - strm.avail_out;
|
||||
total += xwrite(fd, out, have);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
BZ2_bzDecompressEnd(&strm);
|
||||
break;
|
||||
case 1:
|
||||
BZ2_bzCompressEnd(&strm);
|
||||
break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
#define LZ4_LEGACY_BLOCKSIZE 0x800000
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
|
||||
size_t pos = 0;
|
||||
int have;
|
||||
char *out;
|
||||
unsigned block_size, insize, total = 0;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
out = xmalloc(LZ4_LEGACY_BLOCKSIZE);
|
||||
// Skip magic
|
||||
pos += 4;
|
||||
break;
|
||||
case 1:
|
||||
out = xmalloc(LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE));
|
||||
// Write magic
|
||||
total += xwrite(fd, "\x02\x21\x4c\x18", 4);
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
switch(mode) {
|
||||
case 0:
|
||||
// Read block size
|
||||
block_size = *(unsigned *)(buf + pos);
|
||||
pos += 4;
|
||||
if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE))
|
||||
goto done;
|
||||
have = LZ4_decompress_safe(buf + pos, out, block_size, LZ4_LEGACY_BLOCKSIZE);
|
||||
if (have < 0)
|
||||
LOGE("Cannot decode lz4_legacy block\n");
|
||||
pos += block_size;
|
||||
break;
|
||||
case 1:
|
||||
if (pos + LZ4_LEGACY_BLOCKSIZE >= size)
|
||||
insize = size - pos;
|
||||
else
|
||||
insize = LZ4_LEGACY_BLOCKSIZE;
|
||||
have = LZ4_compress_HC(buf + pos, out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE), 9);
|
||||
if (have == 0)
|
||||
LOGE("lz4_legacy compression error\n");
|
||||
pos += insize;
|
||||
// Write block size
|
||||
total += xwrite(fd, &have, sizeof(have));
|
||||
break;
|
||||
}
|
||||
// Write main data
|
||||
total += xwrite(fd, out, have);
|
||||
} while(pos < size);
|
||||
|
||||
done:
|
||||
if (mode == 1) {
|
||||
// Append original size to output
|
||||
unsigned uncomp = size;
|
||||
xwrite(fd, &uncomp, sizeof(uncomp));
|
||||
}
|
||||
free(out);
|
||||
return total;
|
||||
}
|
||||
|
||||
long long decomp(file_t type, int to, const void *from, size_t size) {
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
return gzip(0, to, from, size);
|
||||
case XZ:
|
||||
return lzma(0, to, from, size);
|
||||
case LZMA:
|
||||
return lzma(0, to, from, size);
|
||||
case BZIP2:
|
||||
return bzip2(0, to, from, size);
|
||||
case LZ4:
|
||||
return lz4(0, to, from, size);
|
||||
case LZ4_LEGACY:
|
||||
return lz4_legacy(0, to, from, size);
|
||||
default:
|
||||
// Unsupported
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long long comp(file_t type, int to, const void *from, size_t size) {
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
return gzip(1, to, from, size);
|
||||
case XZ:
|
||||
return lzma(1, to, from, size);
|
||||
case LZMA:
|
||||
return lzma(2, to, from, size);
|
||||
case BZIP2:
|
||||
return bzip2(1, to, from, size);
|
||||
case LZ4:
|
||||
return lz4(1, to, from, size);
|
||||
case LZ4_LEGACY:
|
||||
return lz4_legacy(1, to, from, size);
|
||||
default:
|
||||
// Unsupported
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Below are utility functions for commandline
|
||||
*/
|
||||
|
||||
void decomp_file(char *from, const char *to) {
|
||||
int strip = 1;
|
||||
void *file;
|
||||
size_t size = 0;
|
||||
if (strcmp(from, "-") == 0)
|
||||
stream_full_read(STDIN_FILENO, &file, &size);
|
||||
else
|
||||
mmap_ro(from, &file, &size);
|
||||
file_t type = check_type(file);
|
||||
char *ext;
|
||||
ext = strrchr(from, '.');
|
||||
if (to == NULL)
|
||||
to = from;
|
||||
if (ext != NULL) {
|
||||
// Strip out a matched file extension
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
if (strcmp(ext, ".gz") != 0)
|
||||
strip = 0;
|
||||
break;
|
||||
case XZ:
|
||||
if (strcmp(ext, ".xz") != 0)
|
||||
strip = 0;
|
||||
break;
|
||||
case LZMA:
|
||||
if (strcmp(ext, ".lzma") != 0)
|
||||
strip = 0;
|
||||
break;
|
||||
case BZIP2:
|
||||
if (strcmp(ext, ".bz2") != 0)
|
||||
strip = 0;
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
case LZ4:
|
||||
if (strcmp(ext, ".lz4") != 0)
|
||||
strip = 0;
|
||||
break;
|
||||
default:
|
||||
LOGE("Provided file \'%s\' is not a supported archive format\n", from);
|
||||
}
|
||||
if (strip)
|
||||
*ext = '\0';
|
||||
}
|
||||
|
||||
int fd;
|
||||
|
||||
if (strcmp(to, "-") == 0) {
|
||||
fd = STDOUT_FILENO;
|
||||
} else {
|
||||
fd = creat(to, 0644);
|
||||
fprintf(stderr, "Decompressing to [%s]\n", to);
|
||||
}
|
||||
|
||||
decomp(type, fd, file, size);
|
||||
close(fd);
|
||||
if (to == from && ext != NULL) {
|
||||
*ext = '.';
|
||||
unlink(from);
|
||||
}
|
||||
if (strcmp(from, "-") == 0)
|
||||
free(file);
|
||||
else
|
||||
munmap(file, size);
|
||||
}
|
||||
|
||||
void comp_file(const char *method, const char *from, const char *to) {
|
||||
file_t type;
|
||||
char *ext, dest[PATH_MAX];
|
||||
if (strcmp(method, "gzip") == 0) {
|
||||
type = GZIP;
|
||||
ext = "gz";
|
||||
} else if (strcmp(method, "xz") == 0) {
|
||||
type = XZ;
|
||||
ext = "xz";
|
||||
} else if (strcmp(method, "lzma") == 0) {
|
||||
type = LZMA;
|
||||
ext = "lzma";
|
||||
} else if (strcmp(method, "lz4") == 0) {
|
||||
type = LZ4;
|
||||
ext = "lz4";
|
||||
} else if (strcmp(method, "lz4_legacy") == 0) {
|
||||
type = LZ4_LEGACY;
|
||||
ext = "lz4";
|
||||
} else if (strcmp(method, "bzip2") == 0) {
|
||||
type = BZIP2;
|
||||
ext = "bz2";
|
||||
} else {
|
||||
fprintf(stderr, "Only support following methods: ");
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
void *file;
|
||||
size_t size;
|
||||
if (strcmp(from, "-") == 0)
|
||||
stream_full_read(STDIN_FILENO, &file, &size);
|
||||
else
|
||||
mmap_ro(from, &file, &size);
|
||||
if (to == NULL) {
|
||||
if (strcmp(from, "-") == 0)
|
||||
strcpy(dest, "-");
|
||||
else
|
||||
snprintf(dest, sizeof(dest), "%s.%s", from, ext);
|
||||
} else
|
||||
strcpy(dest, to);
|
||||
int fd;
|
||||
if (strcmp(dest, "-") == 0) {
|
||||
fd = STDOUT_FILENO;
|
||||
} else {
|
||||
fd = creat(dest, 0644);
|
||||
fprintf(stderr, "Compressing to [%s]\n", dest);
|
||||
}
|
||||
comp(type, fd, file, size);
|
||||
close(fd);
|
||||
if (strcmp(from, "-") == 0)
|
||||
free(file);
|
||||
else
|
||||
munmap(file, size);
|
||||
if (to == NULL)
|
||||
unlink(from);
|
||||
}
|
||||
|
102
core/jni/magiskboot/dtb.c
Normal file
102
core/jni/magiskboot/dtb.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <libfdt.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "utils.h"
|
||||
|
||||
static void print_props(const void *fdt, int node, int depth) {
|
||||
int prop;
|
||||
fdt_for_each_property_offset(prop, fdt, node) {
|
||||
for (int i = 0; i < depth; ++i) printf(" ");
|
||||
printf(" ");
|
||||
int size;
|
||||
const char *name;
|
||||
const char *value = fdt_getprop_by_offset(fdt, prop, &name, &size);
|
||||
printf("[%s]: [%s]\n", name, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_subnode(const void *fdt, int parent, int depth) {
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, parent) {
|
||||
for (int i = 0; i < depth; ++i) printf(" ");
|
||||
printf("#%d: %s\n", node, fdt_get_name(fdt, node, NULL));
|
||||
print_props(fdt, node, depth);
|
||||
print_subnode(fdt, node, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_fstab(const void *fdt, int parent) {
|
||||
int node, fstab;
|
||||
fdt_for_each_subnode(node, fdt, parent) {
|
||||
if (strcmp(fdt_get_name(fdt, node, NULL), "fstab") == 0)
|
||||
return node;
|
||||
fstab = find_fstab(fdt, node);
|
||||
if (fstab != -1)
|
||||
return fstab;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dtb_dump(const char *file) {
|
||||
size_t size ;
|
||||
void *dtb, *fdt;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
mmap_ro(file, &dtb, &size);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
fdt = dtb + i;
|
||||
fprintf(stderr, "Dumping dtb.%04d\n", dtb_num++);
|
||||
print_subnode(fdt, 0, 0);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
munmap(dtb, size);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void dtb_patch(const char *file, int patch) {
|
||||
size_t size ;
|
||||
void *dtb, *fdt;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
if (patch)
|
||||
mmap_rw(file, &dtb, &size);
|
||||
else
|
||||
mmap_ro(file, &dtb, &size);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0, found = 0;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
fdt = dtb + i;
|
||||
int fstab = find_fstab(fdt, 0);
|
||||
if (fstab > 0) {
|
||||
fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num++);
|
||||
int block;
|
||||
fdt_for_each_subnode(block, fdt, fstab) {
|
||||
fprintf(stderr, "Found block [%s] in fstab\n", fdt_get_name(fdt, block, NULL));
|
||||
uint32_t value_size;
|
||||
void *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
|
||||
found |= patch_verity(&value, &value_size, patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
munmap(dtb, size);
|
||||
exit(!found);
|
||||
}
|
||||
|
||||
int dtb_commands(const char *cmd, int argc, char *argv[]) {
|
||||
if (argc == 0) return 1;
|
||||
if (strcmp(cmd, "dump") == 0)
|
||||
dtb_dump(argv[0]);
|
||||
else if (strcmp(cmd, "patch") == 0)
|
||||
dtb_patch(argv[0], 1);
|
||||
else if (strcmp(cmd, "test") == 0)
|
||||
dtb_patch(argv[0], 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "utils.h"
|
||||
|
||||
static void hex2byte(const char *hex, unsigned char *str) {
|
||||
char high, low;
|
||||
@@ -12,15 +18,15 @@ static void hex2byte(const char *hex, unsigned char *str) {
|
||||
void hexpatch(const char *image, const char *from, const char *to) {
|
||||
int patternsize = strlen(from) / 2, patchsize = strlen(to) / 2;
|
||||
size_t filesize;
|
||||
unsigned char *file, *pattern, *patch;
|
||||
void *file, *pattern, *patch;
|
||||
mmap_rw(image, &file, &filesize);
|
||||
pattern = xmalloc(patternsize);
|
||||
patch = xmalloc(patchsize);
|
||||
hex2byte(from, pattern);
|
||||
hex2byte(to, patch);
|
||||
for (size_t i = 0; i < filesize - patternsize; ++i) {
|
||||
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
|
||||
if (memcmp(file + i, pattern, patternsize) == 0) {
|
||||
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
|
||||
fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to);
|
||||
memset(file + i, 0, patternsize);
|
||||
memcpy(file + i, patch, patchsize);
|
||||
i += patternsize - 1;
|
35
core/jni/magiskboot/magiskboot.h
Normal file
35
core/jni/magiskboot/magiskboot.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _MAGISKBOOT_H_
|
||||
#define _MAGISKBOOT_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "bootimg.h"
|
||||
|
||||
#define KERNEL_FILE "kernel"
|
||||
#define RAMDISK_FILE "ramdisk.cpio"
|
||||
#define SECOND_FILE "second"
|
||||
#define EXTRA_FILE "extra"
|
||||
#define DTB_FILE "dtb"
|
||||
#define NEW_BOOT "new-boot.img"
|
||||
|
||||
// Main entries
|
||||
void unpack(const char *image);
|
||||
void repack(const char* orig_image, const char* out_image);
|
||||
void hexpatch(const char *image, const char *from, const char *to);
|
||||
int parse_img(const char *image, boot_img *boot);
|
||||
int cpio_commands(int argc, char *argv[]);
|
||||
void comp_file(const char *method, const char *from, const char *to);
|
||||
void decomp_file(char *from, const char *to);
|
||||
int dtb_commands(const char *cmd, int argc, char *argv[]);
|
||||
|
||||
// Compressions
|
||||
size_t gzip(int mode, int fd, const void *buf, size_t size);
|
||||
size_t lzma(int mode, int fd, const void *buf, size_t size);
|
||||
size_t lz4(int mode, int fd, const void *buf, size_t size);
|
||||
size_t bzip2(int mode, int fd, const void *buf, size_t size);
|
||||
size_t lz4_legacy(int mode, int fd, const void *buf, size_t size);
|
||||
long long comp(file_t type, int to, const void *from, size_t size);
|
||||
long long decomp(file_t type, int to, const void *from, size_t size);
|
||||
|
||||
#endif
|
166
core/jni/magiskboot/main.c
Normal file
166
core/jni/magiskboot/main.c
Normal 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"
|
||||
" -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;
|
||||
}
|
332
core/jni/magiskboot/ramdisk.c
Normal file
332
core/jni/magiskboot/ramdisk.c
Normal file
@@ -0,0 +1,332 @@
|
||||
#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) {
|
||||
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 {
|
||||
// Someting 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;
|
||||
}
|
79
core/jni/magiskboot/types.c
Normal file
79
core/jni/magiskboot/types.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
#include "types.h"
|
||||
|
||||
file_t check_type(const void *buf) {
|
||||
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
|
||||
return CHROMEOS;
|
||||
} else if (memcmp(buf, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) {
|
||||
return AOSP;
|
||||
} else if (memcmp(buf, ELF32_MAGIC, 5) == 0) {
|
||||
return ELF32;
|
||||
} else if (memcmp(buf, ELF64_MAGIC, 5) == 0) {
|
||||
return ELF64;
|
||||
} else if (memcmp(buf, GZIP_MAGIC, 4) == 0) {
|
||||
return GZIP;
|
||||
} else if (memcmp(buf, LZOP_MAGIC, 9) == 0) {
|
||||
return LZOP;
|
||||
} else if (memcmp(buf, XZ_MAGIC, 6) == 0) {
|
||||
return XZ;
|
||||
} else if (memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
return LZMA;
|
||||
} else if (memcmp(buf, BZIP_MAGIC, 3) == 0) {
|
||||
return BZIP2;
|
||||
} else if (memcmp(buf, LZ4_MAGIC, 4) == 0) {
|
||||
return LZ4;
|
||||
} else if (memcmp(buf, LZ4_LEG_MAGIC, 4) == 0) {
|
||||
return LZ4_LEGACY;
|
||||
} else if (memcmp(buf, MTK_MAGIC, 4) == 0) {
|
||||
return MTK;
|
||||
} else if (memcmp(buf, DTB_MAGIC, 4) == 0) {
|
||||
return DTB;
|
||||
} else {
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void get_type_name(file_t type, char *name) {
|
||||
char *s;
|
||||
switch (type) {
|
||||
case CHROMEOS:
|
||||
s = "chromeos";
|
||||
break;
|
||||
case AOSP:
|
||||
s = "aosp";
|
||||
break;
|
||||
case GZIP:
|
||||
s = "gzip";
|
||||
break;
|
||||
case LZOP:
|
||||
s = "lzop";
|
||||
break;
|
||||
case XZ:
|
||||
s = "xz";
|
||||
break;
|
||||
case LZMA:
|
||||
s = "lzma";
|
||||
break;
|
||||
case BZIP2:
|
||||
s = "bzip2";
|
||||
break;
|
||||
case LZ4:
|
||||
s = "lz4";
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
s = "lz4_legacy";
|
||||
break;
|
||||
case MTK:
|
||||
s = "mtk";
|
||||
break;
|
||||
case DTB:
|
||||
s = "dtb";
|
||||
break;
|
||||
default:
|
||||
s = "raw";
|
||||
}
|
||||
strcpy(name, s);
|
||||
}
|
42
core/jni/magiskboot/types.h
Normal file
42
core/jni/magiskboot/types.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
CHROMEOS,
|
||||
AOSP,
|
||||
ELF32,
|
||||
ELF64,
|
||||
GZIP,
|
||||
LZOP,
|
||||
XZ,
|
||||
LZMA,
|
||||
BZIP2,
|
||||
LZ4,
|
||||
LZ4_LEGACY,
|
||||
MTK,
|
||||
DTB
|
||||
} file_t;
|
||||
|
||||
#define COMPRESSED(type) (type >= GZIP && type <= LZ4_LEGACY)
|
||||
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define ELF32_MAGIC "\x7f""ELF\x01"
|
||||
#define ELF64_MAGIC "\x7f""ELF\x02"
|
||||
#define GZIP_MAGIC "\x1f\x8b\x08\x00"
|
||||
#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
|
||||
#define XZ_MAGIC "\xfd""7zXZ\x00"
|
||||
#define BZIP_MAGIC "BZh"
|
||||
#define LZ4_MAGIC "\x04\x22\x4d\x18"
|
||||
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
|
||||
#define MTK_MAGIC "\x88\x16\x88\x58"
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
|
||||
|
||||
#define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
|
||||
#define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })
|
||||
|
||||
file_t check_type(const void *buf);
|
||||
void get_type_name(file_t type, char *name);
|
||||
|
||||
#endif
|
@@ -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>
|
||||
@@ -19,7 +17,7 @@
|
||||
#include "daemon.h"
|
||||
|
||||
static char *prop_key[] =
|
||||
{ "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
|
||||
{ "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
|
||||
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", NULL };
|
||||
|
||||
static char *prop_value[] =
|
||||
@@ -58,9 +56,9 @@ void hide_sensitive_props() {
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_magisk_prop(const char *name) {
|
||||
static void rm_magisk_prop(const char *name, const char *value) {
|
||||
if (strstr(name, "magisk")) {
|
||||
deleteprop(name, 0);
|
||||
deleteprop2(name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,42 +67,6 @@ void clean_magisk_props() {
|
||||
getprop_all(rm_magisk_prop);
|
||||
}
|
||||
|
||||
void relink_sbin() {
|
||||
struct stat st;
|
||||
if (stat("/sbin_orig", &st) == -1 && errno == ENOENT) {
|
||||
// Re-link all binaries and bind mount
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char from[PATH_MAX], to[PATH_MAX];
|
||||
|
||||
LOGI("hide_utils: Re-linking /sbin\n");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xrename("/sbin", "/sbin_orig");
|
||||
xmkdir("/sbin", 0755);
|
||||
xchmod("/sbin", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
xmkdir("/dev/sbin_bind", 0755);
|
||||
xchmod("/dev/sbin_bind", 0755);
|
||||
dir = xopendir("/sbin_orig");
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(from, sizeof(from), "/sbin_orig/%s", entry->d_name);
|
||||
if (entry->d_type == DT_LNK)
|
||||
xreadlink(from, from, sizeof(from));
|
||||
snprintf(to, sizeof(to), "/dev/sbin_bind/%s", entry->d_name);
|
||||
symlink(from, to);
|
||||
lsetfilecon(to, "u:object_r:rootfs:s0");
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
xmount("/dev/sbin_bind", "/sbin", NULL, MS_BIND, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int add_list(char *proc) {
|
||||
if (!hideEnabled) {
|
||||
free(proc);
|
||||
@@ -140,10 +102,7 @@ int add_list(char *proc) {
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
|
||||
pthread_mutex_lock(&file_lock);
|
||||
if (vector_to_file(HIDELIST, hide_list)) {
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
return DAEMON_ERROR;
|
||||
}
|
||||
vector_to_file(HIDELIST, hide_list); // Do not complain if file not found
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
@@ -184,8 +143,7 @@ int rm_list(char *proc) {
|
||||
|
||||
ret = DAEMON_SUCCESS;
|
||||
pthread_mutex_lock(&file_lock);
|
||||
if (vector_to_file(HIDELIST, hide_list))
|
||||
ret = DAEMON_ERROR;
|
||||
vector_to_file(HIDELIST, hide_list); // Do not complain if file not found
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
} else {
|
||||
ret = HIDE_ITEM_NOT_EXIST;
|
||||
@@ -227,7 +185,6 @@ int destroy_list() {
|
||||
}
|
||||
|
||||
void add_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, add_list(proc));
|
||||
@@ -235,9 +192,22 @@ void add_hide_list(int client) {
|
||||
}
|
||||
|
||||
void rm_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, rm_list(proc));
|
||||
close(client);
|
||||
}
|
||||
|
||||
void ls_hide_list(int client) {
|
||||
if (!hideEnabled) {
|
||||
write_int(client, HIDE_NOT_ENABLED);
|
||||
return;
|
||||
}
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
write_int(client, vec_size(hide_list));
|
||||
char *s;
|
||||
vec_for_each(hide_list, s) {
|
||||
write_string(client, s);
|
||||
}
|
||||
close(client);
|
||||
}
|
@@ -29,21 +29,18 @@ void kill_proc(int pid) {
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
"MagiskHide v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Hide Magisk!\n\n"
|
||||
"%s [--options [arguments...] ]\n\n"
|
||||
"Usage: %s [--options [arguments...] ]\n\n"
|
||||
"Options:\n"
|
||||
" --enable: Start the magiskhide daemon\n"
|
||||
" --disable: Stop the magiskhide daemon\n"
|
||||
" --add <process name>: Add <process name> to the list\n"
|
||||
" --rm <process name>: Remove <process name> from the list\n"
|
||||
" --ls: Print out the current hide list\n"
|
||||
" --enable Start magiskhide\n"
|
||||
" --disable Stop magiskhide\n"
|
||||
" --add PROCESS Add PROCESS to the hide list\n"
|
||||
" --rm PROCESS Remove PROCESS from the hide list\n"
|
||||
" --ls Print out the current hide list\n"
|
||||
, arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void launch_magiskhide(int client) {
|
||||
// We manually handle crashes
|
||||
err_handler = do_nothing;
|
||||
|
||||
if (hideEnabled) {
|
||||
if (client > 0) {
|
||||
write_int(client, HIDE_IS_ENABLED);
|
||||
@@ -55,7 +52,7 @@ void launch_magiskhide(int client) {
|
||||
hideEnabled = 1;
|
||||
LOGI("* Starting MagiskHide\n");
|
||||
|
||||
deleteprop(MAGISKHIDE_PROP, 1);
|
||||
deleteprop2(MAGISKHIDE_PROP, 1);
|
||||
|
||||
hide_sensitive_props();
|
||||
|
||||
@@ -102,8 +99,8 @@ void stop_magiskhide(int client) {
|
||||
hideEnabled = 0;
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
// Remove without actually removing persist props
|
||||
deleteprop(MAGISKHIDE_PROP, 0);
|
||||
pthread_kill(proc_monitor_thread, SIGUSR1);
|
||||
deleteprop2(MAGISKHIDE_PROP, 0);
|
||||
pthread_kill(proc_monitor_thread, TERM_THREAD);
|
||||
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
close(client);
|
||||
@@ -123,15 +120,9 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
} else if (strcmp(argv[1], "--rm") == 0 && argc > 2) {
|
||||
req = RM_HIDELIST;
|
||||
} else if (strcmp(argv[1], "--ls") == 0) {
|
||||
FILE *fp = fopen(HIDELIST, "r");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
char buffer[512];
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
printf("%s", buffer);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
req = LS_HIDELIST;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, req);
|
||||
@@ -139,28 +130,38 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
write_string(fd, argv[2]);
|
||||
}
|
||||
daemon_response code = read_int(fd);
|
||||
close(fd);
|
||||
switch (code) {
|
||||
case DAEMON_ERROR:
|
||||
fprintf(stderr, "Error occured in daemon...\n");
|
||||
break;
|
||||
return code;
|
||||
case DAEMON_SUCCESS:
|
||||
break;
|
||||
case ROOT_REQUIRED:
|
||||
fprintf(stderr, "Root is required for this operation\n");
|
||||
break;
|
||||
return code;
|
||||
case HIDE_NOT_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is not enabled yet\n");
|
||||
break;
|
||||
return code;
|
||||
case HIDE_IS_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is already enabled\n");
|
||||
break;
|
||||
return code;
|
||||
case HIDE_ITEM_EXIST:
|
||||
fprintf(stderr, "Process [%s] already exists in hide list\n", argv[2]);
|
||||
break;
|
||||
return code;
|
||||
case HIDE_ITEM_NOT_EXIST:
|
||||
fprintf(stderr, "Process [%s] does not exist in hide list\n", argv[2]);
|
||||
break;
|
||||
return code;
|
||||
}
|
||||
return code;
|
||||
|
||||
if (req == LS_HIDELIST) {
|
||||
int argc = read_int(fd);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
char *s = read_string(fd);
|
||||
printf("%s\n", s);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -3,6 +3,9 @@
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define TERM_THREAD SIGUSR1
|
||||
#define HIDE_DONE SIGUSR2
|
||||
|
||||
// Kill process
|
||||
void kill_proc(int pid);
|
||||
|
||||
@@ -12,7 +15,6 @@ void proc_monitor();
|
||||
// Utility functions
|
||||
void manage_selinux();
|
||||
void hide_sensitive_props();
|
||||
void relink_sbin();
|
||||
void clean_magisk_props();
|
||||
|
||||
// List managements
|
267
core/jni/magiskhide/proc_monitor.c
Normal file
267
core/jni/magiskhide/proc_monitor.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/* proc_monitor.c - Monitor am_proc_start events and unmount
|
||||
*
|
||||
* We monitor the logcat am_proc_start events. When a target starts up,
|
||||
* we pause it ASAP, and fork a new process to join its mount namespace
|
||||
* and do all the unmounting/mocking
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "magiskhide.h"
|
||||
|
||||
static char init_ns[32], zygote_ns[2][32], cache_block[256];
|
||||
static int hide_queue = 0, zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
|
||||
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void term_thread(int sig) {
|
||||
LOGD("proc_monitor: running cleanup\n");
|
||||
destroy_list();
|
||||
hideEnabled = 0;
|
||||
// Unregister listener
|
||||
log_events[HIDE_EVENT].fd = -1;
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
pipefd[0] = pipefd[1] = -1;
|
||||
pthread_mutex_destroy(&hide_lock);
|
||||
pthread_mutex_destroy(&file_lock);
|
||||
LOGD("proc_monitor: terminating...\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void hide_done(int sig) {
|
||||
--hide_queue;
|
||||
if (hide_queue == 0) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
if (access(path, R_OK) == -1)
|
||||
return 1;
|
||||
xreadlink(path, target, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void store_zygote_ns(int pid) {
|
||||
if (zygote_num == 2) return;
|
||||
do {
|
||||
usleep(500);
|
||||
read_namespace(pid, zygote_ns[zygote_num], 32);
|
||||
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
|
||||
++zygote_num;
|
||||
}
|
||||
|
||||
static void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
||||
else
|
||||
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
static void hide_daemon(int pid, int ppid) {
|
||||
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
|
||||
strcpy(argv0, "hide_daemon");
|
||||
|
||||
char *line, buffer[PATH_MAX];
|
||||
struct vector mount_list;
|
||||
|
||||
manage_selinux();
|
||||
clean_magisk_props();
|
||||
|
||||
if (switch_mnt_ns(pid))
|
||||
goto exit;
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Find the cache block name if not found yet
|
||||
if (has_cache && cache_block[0] == '\0') {
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, " /cache ")) {
|
||||
sscanf(line, "%256s", cache_block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strlen(cache_block) == 0)
|
||||
has_cache = 0;
|
||||
}
|
||||
|
||||
// Unmout cache mounts
|
||||
if (has_cache) {
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, cache_block) && (strstr(line, " /system/") || strstr(line, " /vendor/"))) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unmount dummy skeletons, /sbin links
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mount_list);
|
||||
|
||||
// Re-read mount infos
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Unmount any loop mounts
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "/dev/block/loop")) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
|
||||
exit:
|
||||
// Send resume signal
|
||||
kill(pid, SIGCONT);
|
||||
// Free up memory
|
||||
vec_destroy(&mount_list);
|
||||
// Wait a while and link it back
|
||||
sleep(10);
|
||||
kill(ppid, HIDE_DONE);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
void proc_monitor() {
|
||||
// Unblock user signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, TERM_THREAD);
|
||||
sigaddset(&block_set, HIDE_DONE);
|
||||
pthread_sigmask(SIG_UNBLOCK, &block_set, NULL);
|
||||
|
||||
// Register the cancel signal
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = term_thread;
|
||||
sigaction(TERM_THREAD, &act, NULL);
|
||||
act.sa_handler = hide_done;
|
||||
sigaction(HIDE_DONE, &act, NULL);
|
||||
|
||||
cache_block[0] = '\0';
|
||||
|
||||
// Get the mount namespace of init
|
||||
if (read_namespace(1, init_ns, 32)) {
|
||||
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
|
||||
term_thread(TERM_THREAD);
|
||||
}
|
||||
LOGI("proc_monitor: init ns=%s\n", init_ns);
|
||||
|
||||
// Get the mount namespace of zygote
|
||||
zygote_num = 0;
|
||||
while(!zygote_num) {
|
||||
// Check zygote every 2 secs
|
||||
sleep(2);
|
||||
ps_filter_proc_name("zygote", store_zygote_ns);
|
||||
}
|
||||
ps_filter_proc_name("zygote64", store_zygote_ns);
|
||||
|
||||
switch(zygote_num) {
|
||||
case 1:
|
||||
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]);
|
||||
break;
|
||||
case 2:
|
||||
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Register our listener to logcat monitor
|
||||
xpipe2(pipefd, O_CLOEXEC);
|
||||
log_events[HIDE_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *log, *line;; free(log)) {
|
||||
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
|
||||
/* It might be interrupted */
|
||||
log = NULL;
|
||||
continue;
|
||||
}
|
||||
char *ss = strchr(log, '[');
|
||||
int pid, ret, comma = 0;
|
||||
char *pos = ss, processName[256], ns[32];
|
||||
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
++comma;
|
||||
}
|
||||
|
||||
if (comma == 6)
|
||||
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
else
|
||||
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
unlink("/magisk");
|
||||
unlink("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink(MAGISKRC);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
++hide_queue;
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
int selfpid = getpid();
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid, selfpid);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
}
|
||||
}
|
1
core/jni/magiskpolicy
Submodule
1
core/jni/magiskpolicy
Submodule
Submodule core/jni/magiskpolicy added at edab891427
407
core/jni/resetprop/resetprop.cpp
Normal file
407
core/jni/resetprop/resetprop.cpp
Normal file
@@ -0,0 +1,407 @@
|
||||
/* resetprop.cpp - Manipulate any system props
|
||||
*
|
||||
* Copyright 2016 nkk71 <nkk71x@gmail.com>
|
||||
* Copyright 2016 topjohnwu <topjohnwu@gmail.com>
|
||||
*
|
||||
* Info:
|
||||
*
|
||||
* all changes are in
|
||||
*
|
||||
* bionic/libc/bionic/system_properties.cpp
|
||||
*
|
||||
* Functions that need to be patched/added in system_properties.cpp
|
||||
*
|
||||
* int __system_properties_init2()
|
||||
* on android 7, first tear down the everything then let it initialize again:
|
||||
* if (initialized) {
|
||||
* //list_foreach(contexts, [](context_node* l) { l->reset_access(); });
|
||||
* //return 0;
|
||||
* free_and_unmap_contexts();
|
||||
* initialized = false;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* static prop_area* map_prop_area(const char* filename, bool is_legacy)
|
||||
* we dont want this read only so change: 'O_RDONLY' to 'O_RDWR'
|
||||
*
|
||||
* static prop_area* map_fd_ro(const int fd)
|
||||
* we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE'
|
||||
*
|
||||
*
|
||||
* Copy the code of prop_info *prop_area::find_property, and modify to delete props
|
||||
* const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
|
||||
* {
|
||||
* ...
|
||||
* ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed
|
||||
* ...
|
||||
*
|
||||
* if (prop_offset != 0) {
|
||||
* atomic_store_explicit(¤t->prop, 0, memory_order_release); // Add this line to nullify the prop entry
|
||||
* return to_prop_info(¤t->prop);
|
||||
* } else {
|
||||
*
|
||||
* ....
|
||||
* }
|
||||
*
|
||||
*
|
||||
* by patching just those functions directly, all other functions should be ok
|
||||
* as is.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include "_system_properties.h"
|
||||
#include "system_properties.h"
|
||||
|
||||
#include "magisk.h"
|
||||
#include "resetprop.h"
|
||||
extern "C" {
|
||||
#include "vector.h"
|
||||
}
|
||||
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) fprintf(stderr, __VA_ARGS__); }
|
||||
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
|
||||
#define PERSISTENT_PROPERTY_DIR "/data/property"
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
static int check_legal_property_name(const char *name) {
|
||||
int namelen = strlen(name);
|
||||
|
||||
if (namelen < 1) goto illegal;
|
||||
if (name[0] == '.') goto illegal;
|
||||
if (name[namelen - 1] == '.') goto illegal;
|
||||
|
||||
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
|
||||
/* Don't allow ".." to appear in a property name */
|
||||
for (size_t i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') goto illegal;
|
||||
continue;
|
||||
}
|
||||
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
goto illegal;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
illegal:
|
||||
PRINT_E("Illegal property name: [%s]\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int usage(char* arg0) {
|
||||
fprintf(stderr,
|
||||
"resetprop v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu & nkk71) - System Props Modification Tool\n\n"
|
||||
"Usage: %s [flags] [options...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -h, --help show this message\n"
|
||||
" (no arguments) print all properties\n"
|
||||
" NAME get property\n"
|
||||
" NAME VALUE set property entry NAME with VALUE\n"
|
||||
" --file FILE load props from FILE\n"
|
||||
" --delete NAME delete property\n"
|
||||
"\n"
|
||||
"Flags:\n"
|
||||
" -v print verbose output to stderr\n"
|
||||
" -n set properties without init triggers\n"
|
||||
" only affects setprop\n"
|
||||
" -p access actual persist storage\n"
|
||||
" only affects getprop and deleteprop\n"
|
||||
"\n"
|
||||
|
||||
, arg0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int init_resetprop() {
|
||||
if (__system_properties_init2()) {
|
||||
PRINT_E("resetprop: Initialize error\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prop_exist(const char *name) {
|
||||
if (init_resetprop()) return 0;
|
||||
return __system_property_find2(name) != NULL;
|
||||
}
|
||||
|
||||
static void read_prop_info(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
strcpy((char *) cookie, value);
|
||||
}
|
||||
|
||||
|
||||
char *getprop(const char *name) {
|
||||
return getprop2(name, 0);
|
||||
}
|
||||
|
||||
// Get prop by name, return string (should free manually!)
|
||||
char *getprop2(const char *name, int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return NULL;
|
||||
char value[PROP_VALUE_MAX];
|
||||
if (init_resetprop()) return NULL;
|
||||
const prop_info *pi = __system_property_find2(name);
|
||||
if (pi == NULL) {
|
||||
if (persist && strncmp(name, "persist.", 8) == 0) {
|
||||
// Try to read from file
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) goto no_prop;
|
||||
PRINT_D("resetprop: read prop from [%s]\n", path);
|
||||
size_t len = read(fd, value, sizeof(value));
|
||||
value[len] = '\0'; // Null terminate the read value
|
||||
} else {
|
||||
no_prop:
|
||||
PRINT_D("resetprop: prop [%s] does not exist\n", name);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
__system_property_read_callback2(pi, read_prop_info, value);
|
||||
}
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
struct wrapper {
|
||||
void (*func)(const char *, const char *);
|
||||
};
|
||||
|
||||
static void cb_wrapper(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
((wrapper *) cookie)->func(name, value);
|
||||
}
|
||||
|
||||
static void prop_foreach_cb(const prop_info* pi, void* cookie) {
|
||||
__system_property_read_callback2(pi, cb_wrapper, cookie);
|
||||
}
|
||||
|
||||
class property {
|
||||
public:
|
||||
property(const char *n, const char *v) {
|
||||
name = strdup(n);
|
||||
value = strdup(v);
|
||||
}
|
||||
~property() {
|
||||
free((void *)name);
|
||||
free((void *)value);
|
||||
}
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
vector prop_list;
|
||||
|
||||
static int prop_cmp(const void *p1, const void *p2) {
|
||||
return strcmp(((property *) p1)->name, ((property *) p2)->name);
|
||||
}
|
||||
|
||||
static void print_all_props_cb(const char *name, const char *value) {
|
||||
vec_push_back(&prop_list, new property(name, value));
|
||||
}
|
||||
|
||||
static void print_all_props(int persist) {
|
||||
void *p;
|
||||
vec_init(&prop_list);
|
||||
getprop_all(print_all_props_cb);
|
||||
if (persist) {
|
||||
// Check all persist props in data
|
||||
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
|
||||
continue;
|
||||
int found = 0;
|
||||
vec_for_each(&prop_list, p) {
|
||||
if (strcmp(((property *) p)->name, entry->d_name) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
vec_push_back(&prop_list, new property(entry->d_name, getprop2(entry->d_name, 1)));
|
||||
}
|
||||
}
|
||||
vec_sort(&prop_list, prop_cmp);
|
||||
vec_for_each(&prop_list, p) {
|
||||
printf("[%s]: [%s]\n", ((property *) p)->name, ((property *) p)->value);
|
||||
delete((property *) p);
|
||||
}
|
||||
vec_destroy(&prop_list);
|
||||
}
|
||||
|
||||
void getprop_all(void (*callback)(const char*, const char*)) {
|
||||
if (init_resetprop()) return;
|
||||
struct wrapper wrap = {
|
||||
.func = callback
|
||||
};
|
||||
__system_property_foreach2(prop_foreach_cb, &wrap);
|
||||
}
|
||||
|
||||
int setprop(const char *name, const char *value) {
|
||||
return setprop2(name, value, 1);
|
||||
}
|
||||
|
||||
int setprop2(const char *name, const char *value, const int trigger) {
|
||||
if (check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
int ret;
|
||||
|
||||
prop_info *pi = (prop_info*) __system_property_find2(name);
|
||||
if (pi != NULL) {
|
||||
if (trigger) {
|
||||
if (strncmp(name, "ro.", 3) == 0) deleteprop(name);
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_update2(pi, value, strlen(value));
|
||||
}
|
||||
} else {
|
||||
PRINT_D("resetprop: New prop [%s]\n", name);
|
||||
if (trigger) {
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_add2(name, strlen(name), value, strlen(value));
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
|
||||
trigger ? "property_service" : "modifing prop data structure");
|
||||
|
||||
if (ret)
|
||||
PRINT_E("resetprop: setprop error\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int deleteprop(const char *name) {
|
||||
return deleteprop2(name, 1);
|
||||
}
|
||||
|
||||
int deleteprop2(const char *name, const int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
char path[PATH_MAX];
|
||||
path[0] = '\0';
|
||||
PRINT_D("resetprop: deleteprop [%s]\n", name);
|
||||
if (persist && strncmp(name, "persist.", 8) == 0)
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
return __system_property_del(name) && unlink(path);
|
||||
}
|
||||
|
||||
int read_prop_file(const char* filename, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: Load prop file [%s]\n", filename);
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
PRINT_E("Cannot open [%s]\n", filename);
|
||||
return 1;
|
||||
}
|
||||
char *line = NULL, *pch;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
int comment = 0, i;
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove the trailing newline
|
||||
if (line[read - 1] == '\n') {
|
||||
line[read - 1] = '\0';
|
||||
--read;
|
||||
}
|
||||
comment = 0;
|
||||
for (i = 0; i < read; ++i) {
|
||||
// Ignore starting spaces
|
||||
if (line[i] == ' ') continue;
|
||||
else {
|
||||
// A line starting with # is ignored
|
||||
if (line[i] == '#') comment = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comment) continue;
|
||||
pch = strchr(line, '=');
|
||||
// Ignore ivalid formats
|
||||
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
setprop2(line + i, pch + 1, trigger);
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
int trigger = 1, persist = 0;
|
||||
char *argv0 = argv[0], *prop;
|
||||
|
||||
--argc;
|
||||
++argv;
|
||||
|
||||
// Parse flags and -- options
|
||||
while (argc && argv[0][0] == '-') {
|
||||
for (int idx = 1; 1; ++idx) {
|
||||
switch (argv[0][idx]) {
|
||||
case '-':
|
||||
if (strcmp(argv[0], "--file") == 0 && argc == 2) {
|
||||
return read_prop_file(argv[1], trigger);
|
||||
} else if (strcmp(argv[0], "--delete") == 0 && argc == 2) {
|
||||
return deleteprop2(argv[1], persist);
|
||||
} else if (strcmp(argv[0], "--help") == 0) {
|
||||
goto usage;
|
||||
}
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
continue;
|
||||
case 'p':
|
||||
persist = 1;
|
||||
continue;
|
||||
case 'n':
|
||||
trigger = 0;
|
||||
continue;
|
||||
case '\0':
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage:
|
||||
return usage(argv0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
print_all_props(persist);
|
||||
return 0;
|
||||
case 1:
|
||||
prop = getprop2(argv[0], persist);
|
||||
if (prop == NULL) return 1;
|
||||
printf("%s\n", prop);
|
||||
free(prop);
|
||||
return 0;
|
||||
case 2:
|
||||
return setprop2(argv[0], argv[1], trigger);
|
||||
default:
|
||||
usage(argv0);
|
||||
return 1;
|
||||
}
|
||||
}
|
@@ -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;
|
@@ -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
1
core/jni/su
Submodule
Submodule core/jni/su added at ed5dd827e9
256
core/jni/utils/cpio.c
Normal file
256
core/jni/utils/cpio.c
Normal file
@@ -0,0 +1,256 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "cpio.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
static uint32_t x8u(char *hex) {
|
||||
uint32_t val, inpos = 8, outpos;
|
||||
char pattern[6];
|
||||
|
||||
while (*hex == '0') {
|
||||
hex++;
|
||||
if (!--inpos) return 0;
|
||||
}
|
||||
// Because scanf gratuitously treats %*X differently than printf does.
|
||||
sprintf(pattern, "%%%dx%%n", inpos);
|
||||
sscanf(hex, pattern, &val, &outpos);
|
||||
if (inpos != outpos) LOGE("bad cpio header\n");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void cpio_free(cpio_entry *e) {
|
||||
if (e) {
|
||||
free(e->filename);
|
||||
free(e->data);
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
int cpio_find(struct vector *v, const char *entry) {
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strcmp(e->filename, entry) == 0)
|
||||
return _;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cpio_cmp(const void *a, const void *b) {
|
||||
return strcmp(((cpio_entry *) a)->filename, ((cpio_entry *) b)->filename);
|
||||
}
|
||||
|
||||
void cpio_vec_insert(struct vector *v, cpio_entry *n) {
|
||||
int i = cpio_find(v, n->filename);
|
||||
if (i > 0) {
|
||||
// Replace, then all is done
|
||||
cpio_free(vec_entry(v)[i]);
|
||||
vec_entry(v)[i] = n;
|
||||
return;
|
||||
}
|
||||
vec_push_back(v, n);
|
||||
}
|
||||
|
||||
// Parse cpio file to a vector of cpio_entry
|
||||
void parse_cpio(struct vector *v, const char *filename) {
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) return;
|
||||
fprintf(stderr, "Loading cpio: [%s]\n", filename);
|
||||
cpio_newc_header header;
|
||||
cpio_entry *f;
|
||||
while(xxread(fd, &header, 110) != -1) {
|
||||
f = xcalloc(sizeof(*f), 1);
|
||||
// f->ino = x8u(header.ino);
|
||||
f->mode = x8u(header.mode);
|
||||
f->uid = x8u(header.uid);
|
||||
f->gid = x8u(header.gid);
|
||||
// f->nlink = x8u(header.nlink);
|
||||
// f->mtime = x8u(header.mtime);
|
||||
f->filesize = x8u(header.filesize);
|
||||
// f->devmajor = x8u(header.devmajor);
|
||||
// f->devminor = x8u(header.devminor);
|
||||
// f->rdevmajor = x8u(header.rdevmajor);
|
||||
// f->rdevminor = x8u(header.rdevminor);
|
||||
uint32_t namesize = x8u(header.namesize);
|
||||
// f->check = x8u(header.check);
|
||||
f->filename = xmalloc(namesize);
|
||||
xxread(fd, f->filename, namesize);
|
||||
file_align(fd, 4, 0);
|
||||
if (strcmp(f->filename, ".") == 0 || strcmp(f->filename, "..") == 0) {
|
||||
cpio_free(f);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(f->filename, "TRAILER!!!") == 0) {
|
||||
cpio_free(f);
|
||||
break;
|
||||
}
|
||||
if (f->filesize) {
|
||||
f->data = xmalloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 0);
|
||||
}
|
||||
vec_push_back(v, f);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void dump_cpio(struct vector *v, const char *filename) {
|
||||
fprintf(stderr, "Dump cpio: [%s]\n", filename);
|
||||
unsigned inode = 300000;
|
||||
char header[111];
|
||||
// Sort by name
|
||||
vec_sort(v, cpio_cmp);
|
||||
cpio_entry *e;
|
||||
int fd = creat(filename, 0644);
|
||||
vec_for_each(v, e) {
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, // e->ino
|
||||
e->mode,
|
||||
e->uid,
|
||||
e->gid,
|
||||
1, // e->nlink
|
||||
0, // e->mtime
|
||||
e->filesize,
|
||||
0, // e->devmajor
|
||||
0, // e->devminor
|
||||
0, // e->rdevmajor
|
||||
0, // e->rdevminor
|
||||
(uint32_t) strlen(e->filename) + 1,
|
||||
0 // e->check
|
||||
);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, e->filename, strlen(e->filename) + 1);
|
||||
file_align(fd, 4, 1);
|
||||
if (e->filesize) {
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
file_align(fd, 4, 1);
|
||||
}
|
||||
}
|
||||
// Write trailer
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", inode++, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, "TRAILER!!!\0", 11);
|
||||
file_align(fd, 4, 1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void cpio_vec_destroy(struct vector *v) {
|
||||
// Free each cpio_entry
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e)
|
||||
cpio_free(e);
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
void cpio_rm(struct vector *v, int recursive, const char *entry) {
|
||||
cpio_entry *e;
|
||||
size_t len = strlen(entry);
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strncmp(e->filename, entry, len) == 0) {
|
||||
if ((recursive && e->filename[len] == '/') || e->filename[len] == '\0') {
|
||||
fprintf(stderr, "Remove [%s]\n", e->filename);
|
||||
cpio_free(e);
|
||||
vec_cur(v) = NULL;
|
||||
if (!recursive) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpio_mkdir(struct vector *v, mode_t mode, const char *entry) {
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFDIR | mode;
|
||||
e->filename = strdup(entry);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
|
||||
}
|
||||
|
||||
void cpio_ln(struct vector *v, const char *target, const char *entry) {
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFLNK;
|
||||
e->filename = strdup(entry);
|
||||
e->filesize = strlen(target);
|
||||
e->data = strdup(target);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Create symlink [%s] -> [%s]\n", entry, target);
|
||||
}
|
||||
|
||||
void cpio_add(struct vector *v, mode_t mode, const char *entry, const char *filename) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFREG | mode;
|
||||
e->filename = strdup(entry);
|
||||
e->filesize = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
e->data = xmalloc(e->filesize);
|
||||
xxread(fd, e->data, e->filesize);
|
||||
close(fd);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
|
||||
}
|
||||
|
||||
int cpio_mv(struct vector *v, const char *from, const char *to) {
|
||||
struct cpio_entry *e;
|
||||
int f = cpio_find(v, from), t = cpio_find(v, to);
|
||||
if (f > 0) {
|
||||
if (t > 0) {
|
||||
cpio_free(vec_entry(v)[t]);
|
||||
vec_entry(v)[t] = NULL;
|
||||
}
|
||||
e = vec_entry(v)[f];
|
||||
free(e->filename);
|
||||
e->filename = strdup(to);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Cannot find entry %s\n", from);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cpio_extract(struct vector *v, const char *entry, const char *filename) {
|
||||
int i = cpio_find(v, entry);
|
||||
if (i > 0) {
|
||||
cpio_entry *e = vec_entry(v)[i];
|
||||
fprintf(stderr, "Extracting [%s] to [%s]\n", entry, filename);
|
||||
if (S_ISREG(e->mode)) {
|
||||
int fd = creat(filename, e->mode & 0777);
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
fchown(fd, e->uid, e->gid);
|
||||
close(fd);
|
||||
} else if (S_ISLNK(e->mode)) {
|
||||
char *target = xcalloc(e->filesize + 1, 1);
|
||||
memcpy(target, e->data, e->filesize);
|
||||
unlink(filename);
|
||||
symlink(target, filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Cannot find the file entry [%s]\n", entry);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cpio_extract_all(struct vector *v) {
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
fprintf(stderr, "Extracting [%s]\n", e->filename);
|
||||
unlink(e->filename);
|
||||
rmdir(e->filename);
|
||||
if (S_ISDIR(e->mode)) {
|
||||
mkdir(e->filename, e->mode & 0777);
|
||||
} else if (S_ISREG(e->mode)) {
|
||||
int fd = creat(e->filename, e->mode & 0777);
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
fchown(fd, e->uid, e->gid);
|
||||
close(fd);
|
||||
} else if (S_ISLNK(e->mode)) {
|
||||
char *target = xcalloc(e->filesize + 1, 1);
|
||||
memcpy(target, e->data, e->filesize);
|
||||
symlink(target, e->filename);
|
||||
}
|
||||
}
|
||||
}
|
447
core/jni/utils/file.c
Normal file
447
core/jni/utils/file.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/* file.c - Contains all files related utilities
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#ifdef SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
char **excl_list = NULL;
|
||||
|
||||
static int is_excl(const char *name) {
|
||||
if (excl_list)
|
||||
for (int i = 0; excl_list[i]; ++i)
|
||||
if (strcmp(name, excl_list[i]) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_getpath(int fd, char *path, size_t size) {
|
||||
snprintf(path, size, "/proc/self/fd/%d", fd);
|
||||
if (xreadlink(path, path, size) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdir_p(const char *pathname, mode_t mode) {
|
||||
char *path = strdup(pathname), *p;
|
||||
errno = 0;
|
||||
for (p = path + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*)) {
|
||||
struct dirent *entry;
|
||||
int newfd;
|
||||
DIR *dir = fdopendir(dirfd);
|
||||
if (dir == NULL) return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
in_order_walk(newfd, callback);
|
||||
close(newfd);
|
||||
}
|
||||
callback(dirfd, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_cb(int dirfd, struct dirent *entry) {
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
default:
|
||||
unlinkat(dirfd, entry->d_name, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
frm_rf(fd);
|
||||
close(fd);
|
||||
}
|
||||
remove(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
in_order_walk(dirfd, rm_cb);
|
||||
}
|
||||
|
||||
/* This will only on the same file system */
|
||||
void mv_f(const char *source, const char *destination) {
|
||||
struct stat st;
|
||||
xlstat(source, &st);
|
||||
int src, dest;
|
||||
struct file_attr a;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
xmkdir_p(destination, st.st_mode & 0777);
|
||||
src = xopen(source, O_RDONLY | O_CLOEXEC);
|
||||
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
|
||||
fclone_attr(src, dest);
|
||||
mv_dir(src, dest);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else{
|
||||
getattr(source, &a);
|
||||
xrename(source, destination);
|
||||
setattr(destination, &a);
|
||||
}
|
||||
rmdir(source);
|
||||
}
|
||||
|
||||
/* This will only on the same file system */
|
||||
void mv_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int newsrc, newdest;
|
||||
struct file_attr a;
|
||||
|
||||
dir = xfdopendir(src);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
|
||||
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
fsetattr(newdest, &a);
|
||||
mv_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
unlinkat(src, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
case DT_LNK:
|
||||
case DT_REG:
|
||||
renameat(src, entry->d_name, dest, entry->d_name);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cp_afc(const char *source, const char *destination) {
|
||||
int src, dest;
|
||||
struct file_attr a;
|
||||
getattr(source, &a);
|
||||
|
||||
if (S_ISDIR(a.st.st_mode)) {
|
||||
xmkdir_p(destination, a.st.st_mode & 0777);
|
||||
src = xopen(source, O_RDONLY | O_CLOEXEC);
|
||||
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
|
||||
fsetattr(dest, &a);
|
||||
clone_dir(src, dest);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else{
|
||||
unlink(destination);
|
||||
if (S_ISREG(a.st.st_mode)) {
|
||||
src = xopen(source, O_RDONLY);
|
||||
dest = xopen(destination, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
xsendfile(dest, src, NULL, a.st.st_size);
|
||||
fsetattr(src, &a);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else if (S_ISLNK(a.st.st_mode)) {
|
||||
char buf[PATH_MAX];
|
||||
xreadlink(source, buf, sizeof(buf));
|
||||
xsymlink(buf, destination);
|
||||
setattr(destination, &a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clone_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int srcfd, destfd, newsrc, newdest;
|
||||
char buf[PATH_MAX];
|
||||
struct file_attr a;
|
||||
|
||||
dir = xfdopendir(src);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
clone_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
break;
|
||||
case DT_REG:
|
||||
destfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
|
||||
srcfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
xsendfile(destfd, srcfd, 0, a.st.st_size);
|
||||
fsetattr(destfd, &a);
|
||||
close(destfd);
|
||||
close(srcfd);
|
||||
break;
|
||||
case DT_LNK:
|
||||
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
symlinkat(buf, dest, entry->d_name);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getattr(const char *path, struct file_attr *a) {
|
||||
if (xlstat(path, &a->st) == -1)
|
||||
return -1;
|
||||
#ifdef SELINUX
|
||||
char *con = "";
|
||||
if (lgetfilecon(path, &con) == -1)
|
||||
return -1;
|
||||
strcpy(a->con, con);
|
||||
freecon(con);
|
||||
#else
|
||||
a->con[0] = '\0';
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
int ret = fgetattr(fd, a);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fgetattr(int fd, struct file_attr *a) {
|
||||
#ifdef SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return getattr(path, a);
|
||||
#else
|
||||
if (fstat(fd, &a->st) == -1)
|
||||
return -1;
|
||||
a->con[0] = '\0';
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int setattr(const char *path, struct file_attr *a) {
|
||||
if (chmod(path, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
#ifdef SELINUX
|
||||
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
|
||||
return -1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
int ret = fsetattr(fd, a);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsetattr(int fd, struct file_attr *a) {
|
||||
#ifdef SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return setattr(path, a);
|
||||
#else
|
||||
if (fchmod(fd, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void clone_attr(const char *source, const char *target) {
|
||||
struct file_attr a;
|
||||
getattr(source, &a);
|
||||
setattr(target, &a);
|
||||
}
|
||||
|
||||
void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
struct file_attr a;
|
||||
fgetattr(sourcefd, &a);
|
||||
fsetattr(targetfd, &a);
|
||||
}
|
||||
|
||||
#ifdef SELINUX
|
||||
|
||||
#define UNLABEL_CON "u:object_r:unlabeled:s0"
|
||||
#define SYSTEM_CON "u:object_r:system_file:s0"
|
||||
|
||||
void restorecon(int dirfd, int force) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int fd;
|
||||
char path[PATH_MAX], *con;
|
||||
|
||||
fd_getpath(dirfd, path, sizeof(path));
|
||||
lgetfilecon(path, &con);
|
||||
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
|
||||
dir = xfdopendir(dirfd);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(fd, force);
|
||||
} else {
|
||||
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
lgetfilecon(path, &con);
|
||||
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SELINUX
|
||||
|
||||
static int _mmap(int rw, const char *filename, void **buf, size_t *size) {
|
||||
struct stat st;
|
||||
int fd = xopen(filename, rw ? O_RDWR : O_RDONLY);
|
||||
fstat(fd, &st);
|
||||
if (S_ISBLK(st.st_mode))
|
||||
ioctl(fd, BLKGETSIZE64, size);
|
||||
else
|
||||
*size = st.st_size;
|
||||
*buf = *size > 0 ? xmmap(NULL, *size, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, fd, 0) : NULL;
|
||||
close(fd);
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
int mmap_ro(const char *filename, void **buf, size_t *size) {
|
||||
return _mmap(0, filename, buf, size);
|
||||
}
|
||||
|
||||
int mmap_rw(const char *filename, void **buf, size_t *size) {
|
||||
return _mmap(1, filename, buf, size);
|
||||
}
|
||||
|
||||
void fd_full_read(int fd, void **buf, size_t *size) {
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmalloc(*size);
|
||||
xxread(fd, *buf, *size);
|
||||
}
|
||||
|
||||
void full_read(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
fd_full_read(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopenat(dirfd, filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
fd_full_read(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void stream_full_read(int fd, void **buf, size_t *size) {
|
||||
size_t cap = 1 << 20;
|
||||
uint8_t tmp[1 << 20];
|
||||
*buf = xmalloc(cap);
|
||||
ssize_t read;
|
||||
*size = 0;
|
||||
while (1) {
|
||||
read = xread(fd, tmp, sizeof(tmp));
|
||||
if (read <= 0)
|
||||
break;
|
||||
if (*size + read > cap) {
|
||||
cap *= 2;
|
||||
*buf = realloc(*buf, cap);
|
||||
}
|
||||
memcpy(*buf + *size, tmp, read);
|
||||
*size += read;
|
||||
}
|
||||
}
|
||||
|
||||
void write_zero(int fd, size_t size) {
|
||||
size_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
ftruncate(fd, pos + size);
|
||||
lseek(fd, pos + size, SEEK_SET);
|
||||
}
|
||||
|
||||
void mem_align(size_t *pos, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
if (*pos & mask) {
|
||||
*pos += align - (*pos & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void file_align(int fd, size_t align, int out) {
|
||||
size_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
size_t mask = align - 1;
|
||||
size_t off;
|
||||
if (pos & mask) {
|
||||
off = align - (pos & mask);
|
||||
if (out) {
|
||||
write_zero(fd, off);
|
||||
} else {
|
||||
lseek(fd, pos + off, SEEK_SET);
|
||||
}
|
||||
}
|
||||
}
|
233
core/jni/utils/img.c
Normal file
233
core/jni/utils/img.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/* img.c - All image related functions
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/loop.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int e2fsck(const char *img) {
|
||||
// Check and repair ext4 image
|
||||
char buffer[128];
|
||||
int pid, fd = -1;
|
||||
pid = exec_command(1, &fd, NULL, "e2fsck", "-yf", img, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
waitpid(pid, NULL, 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *loopsetup(const char *img) {
|
||||
char device[20];
|
||||
struct loop_info64 info;
|
||||
int i, lfd, ffd;
|
||||
memset(&info, 0, sizeof(info));
|
||||
// First get an empty loop device
|
||||
for (i = 0; i <= 7; ++i) {
|
||||
sprintf(device, "/dev/block/loop%d", i);
|
||||
lfd = xopen(device, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
|
||||
break;
|
||||
close(lfd);
|
||||
}
|
||||
if (i == 8) return NULL;
|
||||
ffd = xopen(img, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
|
||||
return NULL;
|
||||
strcpy((char *) info.lo_file_name, img);
|
||||
ioctl(lfd, LOOP_SET_STATUS64, &info);
|
||||
close(lfd);
|
||||
close(ffd);
|
||||
return strdup(device);
|
||||
}
|
||||
|
||||
int create_img(const char *img, int size) {
|
||||
if (size == 128) /* WTF...? */
|
||||
size = 132;
|
||||
unlink(img);
|
||||
LOGI("Create %s with size %dM\n", img, size);
|
||||
int ret;
|
||||
|
||||
char buffer[16];
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
ret = exec_command_sync("make_ext4fs", "-l", buffer, img, NULL);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_img_size(const char *img, int *used, int *total) {
|
||||
if (access(img, R_OK) == -1)
|
||||
return 1;
|
||||
char buffer[PATH_MAX];
|
||||
int pid, fd = -1, status = 1;
|
||||
pid = exec_command(1, &fd, NULL, "e2fsck", "-n", img, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd)) {
|
||||
if (strstr(buffer, img)) {
|
||||
char *tok = strtok(buffer, ",");
|
||||
while(tok != NULL) {
|
||||
if (strstr(tok, "blocks")) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
tok = strtok(NULL, ",");
|
||||
}
|
||||
if (status) continue;
|
||||
sscanf(tok, "%d/%d", used, total);
|
||||
*used = *used / 256 + 1;
|
||||
*total /= 256;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
waitpid(pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resize_img(const char *img, int size) {
|
||||
LOGI("Resize %s to %dM\n", img, size);
|
||||
if (e2fsck(img))
|
||||
return 1;
|
||||
char buffer[128];
|
||||
int pid, fd = -1, used, total;
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
close(fd);
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
// Double check our image size
|
||||
get_img_size(img, &used, &total);
|
||||
if (total != size) {
|
||||
// Sammy crap occurs or resize2fs failed, lets create a new image!
|
||||
char *dir = dirname(img);
|
||||
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
|
||||
create_img(buffer, size);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(img, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(buffer, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
cp_afc(SOURCE_TMP, TARGET_TMP);
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
rename(buffer, img);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *mount_image(const char *img, const char *target) {
|
||||
if (access(img, F_OK) == -1)
|
||||
return NULL;
|
||||
if (access(target, F_OK) == -1) {
|
||||
if (xmkdir_p(target, 0755) == -1) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir_p(target, 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (e2fsck(img))
|
||||
return NULL;
|
||||
|
||||
char *device = loopsetup(img);
|
||||
if (device)
|
||||
xmount(device, target, "ext4", 0, NULL);
|
||||
return device;
|
||||
}
|
||||
|
||||
void umount_image(const char *target, const char *device) {
|
||||
xumount(target);
|
||||
int fd = xopen(device, O_RDWR);
|
||||
ioctl(fd, LOOP_CLR_FD);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int merge_img(const char *source, const char *target) {
|
||||
if (access(source, F_OK) == -1)
|
||||
return 0;
|
||||
LOGI("* Merging %s -> %s\n", source, target);
|
||||
if (access(target, F_OK) == -1) {
|
||||
xrename(source, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
// resize target to worst case
|
||||
int s_used, s_total, t_used, t_total, n_total;
|
||||
get_img_size(source, &s_used, &s_total);
|
||||
get_img_size(target, &t_used, &t_total);
|
||||
n_total = round_size(s_used + t_used);
|
||||
if (n_total > t_total)
|
||||
resize_img(target, n_total);
|
||||
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
xmkdir(TARGET_TMP, 0755);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(source, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(target, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if (!(dir = xopendir(SOURCE_TMP)))
|
||||
return 1;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
// Cleanup old module if exists
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buffer, F_OK) == 0) {
|
||||
LOGI("Upgrade module: %s\n", entry->d_name);
|
||||
rm_rf(buffer);
|
||||
} else {
|
||||
LOGI("New module: %s\n", entry->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
cp_afc(SOURCE_TMP, TARGET_TMP);
|
||||
|
||||
// Unmount all loop devices
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
unlink(source);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trim_img(const char *img) {
|
||||
int used, total, new_size;
|
||||
get_img_size(img, &used, &total);
|
||||
new_size = round_size(used);
|
||||
if (new_size != total)
|
||||
resize_img(img, new_size);
|
||||
}
|
@@ -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) {
|
@@ -5,24 +5,22 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
|
||||
#include "resetprop.h"
|
||||
|
||||
unsigned get_shell_uid() {
|
||||
struct passwd* ppwd = getpwnam("shell");
|
||||
@@ -49,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!! */
|
||||
@@ -69,7 +71,7 @@ int file_to_vector(const char* filename, struct vector *v) {
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
FILE *fp = fopen(filename, "r");
|
||||
FILE *fp = xfopen(filename, "r");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
@@ -97,7 +99,7 @@ int vector_to_file(const char *filename, struct vector *v) {
|
||||
}
|
||||
|
||||
/* Check if the string only contains digits */
|
||||
int isNum(const char *s) {
|
||||
static int is_num(const char *s) {
|
||||
int len = strlen(s);
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (s[i] < '0' || s[i] > '9')
|
||||
@@ -129,11 +131,11 @@ void ps(void (*func)(int)) {
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (isNum(entry->d_name))
|
||||
if (is_num(entry->d_name))
|
||||
func(atoi(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
@@ -144,12 +146,12 @@ static void proc_name_filter(int pid) {
|
||||
char buf[64];
|
||||
int fd;
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
if (fdgets(buf, sizeof(buf), fd) == 0) {
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
|
||||
close(fd);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
fdgets(buf, sizeof(buf), fd);
|
||||
}
|
||||
@@ -166,48 +168,25 @@ void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
|
||||
ps(proc_name_filter);
|
||||
}
|
||||
|
||||
int create_links(const char *bin, const char *path) {
|
||||
char self[PATH_MAX], linkpath[PATH_MAX];
|
||||
if (bin == NULL) {
|
||||
xreadlink("/proc/self/exe", self, sizeof(self));
|
||||
bin = self;
|
||||
}
|
||||
int ret = 0;
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
|
||||
unlink(linkpath);
|
||||
ret |= symlink(bin, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DEV_BLOCK "/dev/block"
|
||||
|
||||
void unlock_blocks() {
|
||||
char path[PATH_MAX];
|
||||
void unlock_blocks() {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int fd, OFF = 0;
|
||||
int fd, dev, OFF = 0;
|
||||
|
||||
if (!(dir = xopendir(DEV_BLOCK)))
|
||||
if ((dev = xopen("/dev/block", O_RDONLY | O_CLOEXEC)) < 0)
|
||||
return;
|
||||
dir = xfdopendir(dev);
|
||||
|
||||
while((entry = readdir(dir))) {
|
||||
if (entry->d_type == DT_BLK &&
|
||||
strstr(entry->d_name, "ram") == NULL &&
|
||||
strstr(entry->d_name, "loop") == NULL &&
|
||||
strstr(entry->d_name, "dm-0") == NULL) {
|
||||
snprintf(path, sizeof(path), "%s/%s", DEV_BLOCK, entry->d_name);
|
||||
if ((fd = xopen(path, O_RDONLY)) < 0)
|
||||
if (entry->d_type == DT_BLK) {
|
||||
if ((fd = openat(dev, entry->d_name, O_RDONLY)) < 0)
|
||||
continue;
|
||||
|
||||
if (ioctl(fd, BLKROSET, &OFF) == -1)
|
||||
PLOGE("unlock %s", path);
|
||||
PLOGE("unlock %s", entry->d_name);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
close(dev);
|
||||
}
|
||||
|
||||
void setup_sighandlers(void (*handler)(int)) {
|
||||
@@ -223,8 +202,9 @@ void setup_sighandlers(void (*handler)(int)) {
|
||||
fd == NULL -> Ignore output
|
||||
*fd < 0 -> Open pipe and set *fd to the read end
|
||||
*fd >= 0 -> STDOUT (or STDERR) will be redirected to *fd
|
||||
*cb -> A callback function which runs after fork
|
||||
*/
|
||||
int run_command(int err, int *fd, const char *path, char *const argv[]) {
|
||||
static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, va_list argv) {
|
||||
int pipefd[2], writeEnd = -1;
|
||||
|
||||
if (fd) {
|
||||
@@ -237,13 +217,35 @@ int run_command(int err, int *fd, const char *path, char *const argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
// Collect va_list into vector
|
||||
struct vector args;
|
||||
vec_init(&args);
|
||||
vec_push_back(&args, strdup(argv0));
|
||||
for (void *arg = va_arg(argv, void*); arg; arg = va_arg(argv, void*))
|
||||
vec_push_back(&args, strdup(arg));
|
||||
vec_push_back(&args, NULL);
|
||||
|
||||
// Setup environment
|
||||
char *const *envp;
|
||||
struct vector env;
|
||||
vec_init(&env);
|
||||
if (setupenv) {
|
||||
setupenv(&env);
|
||||
envp = (char **) vec_entry(&env);
|
||||
} else {
|
||||
extern char **environ;
|
||||
envp = environ;
|
||||
}
|
||||
|
||||
int pid = xfork();
|
||||
if (pid != 0) {
|
||||
if (fd && *fd < 0) {
|
||||
// Give the read end and close write end
|
||||
*fd = pipefd[0];
|
||||
close(pipefd[1]);
|
||||
}
|
||||
vec_deep_destroy(&args);
|
||||
vec_deep_destroy(&env);
|
||||
return pid;
|
||||
}
|
||||
|
||||
@@ -252,141 +254,41 @@ int run_command(int err, int *fd, const char *path, char *const argv[]) {
|
||||
if (err) xdup2(writeEnd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
execv(path, argv);
|
||||
PLOGE("execv");
|
||||
execvpe(argv0, (char **) vec_entry(&args), envp);
|
||||
PLOGE("execvpe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mkdir_p(const char *pathname, mode_t mode) {
|
||||
char *path = strdup(pathname), *p;
|
||||
errno = 0;
|
||||
for (p = path + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
int exec_command_sync(char *const argv0, ...) {
|
||||
va_list argv;
|
||||
va_start(argv, argv0);
|
||||
int pid, status;
|
||||
pid = v_exec_command(0, NULL, NULL, argv0, argv);
|
||||
va_end(argv);
|
||||
if (pid < 0)
|
||||
return pid;
|
||||
waitpid(pid, &status, 0);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...) {
|
||||
va_list argv;
|
||||
va_start(argv, argv0);
|
||||
int pid = v_exec_command(err, fd, setupenv, argv0, argv);
|
||||
va_end(argv);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int bind_mount(const char *from, const char *to) {
|
||||
int ret = xmount(from, to, NULL, MS_BIND, NULL);
|
||||
#ifdef DEBUG
|
||||
LOGD("bind_mount: %s -> %s\n", from, to);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("bind_mount: %s -> %s\n", from, to);
|
||||
#else
|
||||
LOGI("bind_mount: %s\n", to);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int open_new(const char *filename) {
|
||||
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
// file/link -> file/link only!!
|
||||
int cp_afc(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
xlstat(source, &buf);
|
||||
unlink(target);
|
||||
char *con;
|
||||
if (S_ISREG(buf.st_mode)) {
|
||||
int sfd, tfd;
|
||||
sfd = xopen(source, O_RDONLY);
|
||||
tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
xsendfile(tfd, sfd, NULL, buf.st_size);
|
||||
fclone_attr(sfd, tfd);
|
||||
close(sfd);
|
||||
close(tfd);
|
||||
} else if (S_ISLNK(buf.st_mode)) {
|
||||
char buffer[PATH_MAX];
|
||||
xreadlink(source, buffer, sizeof(buffer));
|
||||
xsymlink(buffer, target);
|
||||
lchown(target, buf.st_uid, buf.st_gid);
|
||||
lgetfilecon(source, &con);
|
||||
lsetfilecon(target, con);
|
||||
free(con);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clone_dir(const char *source, const char *target) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char *s_path, *t_path;
|
||||
|
||||
if (!(dir = xopendir(source)))
|
||||
return 1;
|
||||
|
||||
s_path = xmalloc(PATH_MAX);
|
||||
t_path = xmalloc(PATH_MAX);
|
||||
|
||||
mkdir_p(target, 0755);
|
||||
clone_attr(source, target);
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name);
|
||||
snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
clone_dir(s_path, t_path);
|
||||
break;
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
cp_afc(s_path, t_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(s_path);
|
||||
free(t_path);
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rm_rf(const char *target) {
|
||||
if (access(target, F_OK) == -1)
|
||||
return 0;
|
||||
// Use external rm command, saves a lot of headache and issues
|
||||
char command[PATH_MAX];
|
||||
snprintf(command, sizeof(command), "rm -rf %s", target);
|
||||
return system(command);
|
||||
}
|
||||
|
||||
void clone_attr(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
xstat(source, &buf);
|
||||
chmod(target, buf.st_mode & 0777);
|
||||
chown(target, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
lgetfilecon(source, &con);
|
||||
lsetfilecon(target, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
struct stat buf;
|
||||
fstat(sourcefd, &buf);
|
||||
fchmod(targetfd, buf.st_mode & 0777);
|
||||
fchown(targetfd, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
fgetfilecon(sourcefd, &con);
|
||||
fsetfilecon(sourcefd, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void get_client_cred(int fd, struct ucred *cred) {
|
||||
socklen_t ucred_length = sizeof(*cred);
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
|
||||
@@ -406,3 +308,31 @@ int switch_mnt_ns(int pid) {
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fork_dont_care() {
|
||||
int pid = xfork();
|
||||
if (pid) {
|
||||
waitpid(pid, NULL, 0);
|
||||
return pid;
|
||||
} else if ((pid = xfork())) {
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wait_till_exists(const char *target) {
|
||||
if (access(target, F_OK) == 0)
|
||||
return;
|
||||
int fd = inotify_init();
|
||||
char *dir = dirname(target);
|
||||
char crap[PATH_MAX];
|
||||
inotify_add_watch(fd, dir, IN_CREATE);
|
||||
while (1) {
|
||||
struct inotify_event event;
|
||||
read(fd, &event, sizeof(event));
|
||||
read(fd, crap, event.len);
|
||||
if (access(target, F_OK) == 0)
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
92
core/jni/utils/pattern.c
Normal file
92
core/jni/utils/pattern.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static int check_verity_pattern(const char *s) {
|
||||
int pos = 0;
|
||||
if (s[0] == ',') ++pos;
|
||||
if (strncmp(s + pos, "verify", 6) == 0)
|
||||
pos += 6;
|
||||
else if (strncmp(s + pos, "avb", 3) == 0)
|
||||
pos += 3;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (s[pos] == '=') {
|
||||
while (s[pos] != '\0' && s[pos] != ' ' && s[pos] != '\n' && s[pos] != ',') ++pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
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, found = 0;
|
||||
for (int pos = 0; pos < *size; ++pos) {
|
||||
if ((skip = check_verity_pattern(*buf + pos)) > 0) {
|
||||
found = 1;
|
||||
fprintf(stderr, "%s pattern [%.*s]\n", patch ? "Remove" : "Found", skip, (char *) *buf + pos);
|
||||
if (patch) {
|
||||
memcpy(*buf + pos, *buf + pos + skip, *size - pos - skip);
|
||||
memset(*buf + *size - skip, '\0', skip);
|
||||
*size -= skip;
|
||||
} else {
|
||||
pos += skip - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void patch_encryption(void **buf, uint32_t *size) {
|
||||
int skip;
|
||||
for (int pos = 0; pos < *size; ++pos) {
|
||||
if ((skip = check_encryption_pattern(*buf + pos)) > 0) {
|
||||
fprintf(stderr, "Replace pattern [%.*s] with [encryptable]\n", skip, (char *) *buf + pos);
|
||||
memcpy(*buf + pos, "encryptable", 11);
|
||||
memcpy(*buf + pos + 11, *buf + pos + skip, *size - pos - skip);
|
||||
memset(*buf + *size - skip + 11, '\0', skip - 11);
|
||||
*size -= (skip - 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
@@ -1,8 +1,8 @@
|
||||
/* xwrap.c - wrappers around existing library functions.
|
||||
*
|
||||
* Functions with the x prefix are wrappers that either succeed or kill the
|
||||
* program with an error message, but never return failure. They usually have
|
||||
* the same arguments and return value as the function they wrap.
|
||||
* Functions with the x prefix are wrappers that either succeed or log the
|
||||
* error message. They usually have the same arguments and return value
|
||||
* as the function they wrap.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -21,7 +22,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode) {
|
||||
@@ -56,6 +57,14 @@ int xopen3(const char *pathname, int flags, mode_t mode) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopenat(int dirfd, const char *pathname, int flags) {
|
||||
int fd = openat(dirfd, pathname, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("openat: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count) {
|
||||
int ret = write(fd, buf, count);
|
||||
if (count != ret) {
|
||||
@@ -106,6 +115,14 @@ DIR *xopendir(const char *name) {
|
||||
return d;
|
||||
}
|
||||
|
||||
DIR *xfdopendir(int fd) {
|
||||
DIR *d = fdopendir(fd);
|
||||
if (d == NULL) {
|
||||
PLOGE("fdopendir");
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
struct dirent *xreaddir(DIR *dirp) {
|
||||
errno = 0;
|
||||
struct dirent *e = readdir(dirp);
|
||||
@@ -142,7 +159,7 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = connect(sockfd, addr, addrlen);
|
||||
if (ret == -1) {
|
||||
PLOGE("bind");
|
||||
PLOGE("connect");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -250,7 +267,16 @@ ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
PLOGE("readlink %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
|
||||
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
if (ret == -1) {
|
||||
PLOGE("readlinkat %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -289,14 +315,6 @@ int xumount2(const char *target, int flags) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xchmod(const char *pathname, mode_t mode) {
|
||||
int ret = chmod(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("chmod %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xrename(const char *oldpath, const char *newpath) {
|
||||
int ret = rename(oldpath, newpath);
|
||||
if (ret == -1) {
|
||||
@@ -313,6 +331,22 @@ int xmkdir(const char *pathname, mode_t mode) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir_p(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir_p(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("mkdir_p %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
|
||||
int ret = mkdirat(dirfd, pathname, mode);
|
||||
if (ret == -1 && errno != EEXIST) {
|
||||
PLOGE("mkdirat %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset) {
|
||||
void *ret = mmap(addr, length, prot, flags, fd, offset);
|
||||
@@ -330,10 +364,10 @@ ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir_p(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir_p(pathname, mode);
|
||||
pid_t xfork() {
|
||||
int ret = fork();
|
||||
if (ret == -1) {
|
||||
PLOGE("mkdir_p %s", pathname);
|
||||
PLOGE("fork");
|
||||
}
|
||||
return ret;
|
||||
}
|
1
core/src/main/AndroidManifest.xml
Normal file
1
core/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="com.topjohnwu.core.magisk" />
|
1
crypto/.gitignore
vendored
Normal file
1
crypto/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
38
crypto/build.gradle
Normal file
38
crypto/build.gradle
Normal 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'
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
136
crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java
Normal file
136
crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
122
crypto/src/main/java/com/topjohnwu/crypto/JarMap.java
Normal file
122
crypto/src/main/java/com/topjohnwu/crypto/JarMap.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
502
crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java
Normal file
502
crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java
Normal 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);
|
||||
}
|
||||
}
|
231
crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java
Normal file
231
crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
42
crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java
Normal file
42
crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
28
docs/README.MD
Normal file
28
docs/README.MD
Normal file
@@ -0,0 +1,28 @@
|
||||
# Magisk Documentations
|
||||
(Updated on 2017.9.28) ([Changelog](changelog.md))
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Procedure Diagram](https://cdn.rawgit.com/topjohnwu/Magisk/cc1809688299f1f8b5db494a234850852712c0c9/docs/procedures.html)
|
||||
- [Magisk Details](details.md)
|
||||
- [Boot Stages](details.md#boot-stages)
|
||||
- [Magic Mount Details](details.md#magic-mount-details)
|
||||
- [Simple Mount Details](details.md#simple-mount-details)
|
||||
- [Available Applets](applets.md)
|
||||
- [magisk](applets.md#magisk)
|
||||
- [su](applets.md#su)
|
||||
- [resetprop](applets.md#resetprop)
|
||||
- [magiskpolicy](applets.md#magiskpolicy)
|
||||
- [magiskhide](applets.md#magiskhide)
|
||||
- [Modules and Repos](module_repo.md)
|
||||
- [Modules and Templates](module_repo.md#magisk-module-format)
|
||||
- [Submit Modules to Repo](module_repo.md#submit-your-module-to-magisk-modules-repo)
|
||||
- [Tips and Tricks](tips.md)
|
||||
- [OTA Installation Tips](tips.md#ota-installation-tips)
|
||||
- [Remove Files](tips.md#remove-files)
|
||||
- [Remove Folders](tips.md#remove-folders)
|
||||
|
||||
|
||||
## Introduction
|
||||
Magisk is a suite of open source tools for devices running Android version higher than 5.0 Lollipop (API 21). It establishes an environment which covers most Android aftermarket customization needs, such as root, boot scripts, SELinux patches, dm-verity/forceencrypt patches etc.. Furthermore, Magisk also provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact, all of which accomplished by only patching the boot image, and adding some files into `/data`. With its systemless nature along with several other hacks, Magisk can hide modifications from device integrity verifications, one of the main target is to hide from [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).
|
166
docs/applets.md
Normal file
166
docs/applets.md
Normal file
@@ -0,0 +1,166 @@
|
||||
## Available Applets
|
||||
Magisk has a core binary which acts as a multi-call program with many applets. Here is an introduction to all available applets.
|
||||
|
||||
### magisk
|
||||
The magisk binary itself provides a lot of utility functions when called with the name `magisk`. They are used in both Magisk installation and module installation. The entry point for `init` to invoke magisk's boot procedures are also listed here.
|
||||
|
||||
Command help message:
|
||||
|
||||
```
|
||||
Usage: magisk [applet [arguments]...]
|
||||
or: magisk [options]...
|
||||
|
||||
Options:
|
||||
-c print current binary version
|
||||
-v print running daemon version
|
||||
-V print running daemon version code
|
||||
--list list all availible applets
|
||||
--install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional
|
||||
--createimg IMG SIZE create ext4 image. SIZE is interpreted in MB
|
||||
--imgsize IMG report ext4 image used/total size
|
||||
--resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB
|
||||
--mountimg IMG PATH mount IMG to PATH and prints the loop device
|
||||
--umountimg PATH LOOP unmount PATH and delete LOOP device
|
||||
--[boot stage] start boot stage service
|
||||
--unlock-blocks set BLKROSET flag to OFF for all block devices
|
||||
|
||||
Supported boot stages:
|
||||
post-fs, post-fs-data, service
|
||||
|
||||
Supported applets:
|
||||
su, resetprop, magiskpolicy, supolicy, magiskhide
|
||||
```
|
||||
|
||||
### su
|
||||
The MagiskSU entrypoint. Call `su` to gain a root shell.
|
||||
|
||||
Command help message:
|
||||
|
||||
```
|
||||
Usage: su [options] [--] [-] [LOGIN] [--] [args...]
|
||||
|
||||
Options:
|
||||
-c, --command COMMAND pass COMMAND to the invoked shell
|
||||
-h, --help display this help message and exit
|
||||
-, -l, --login pretend the shell to be a login shell
|
||||
-m, -p,
|
||||
--preserve-environment do not change environment variables
|
||||
-s, --shell SHELL use SHELL instead of the default /system/bin/sh
|
||||
-u display the multiuser mode and exit
|
||||
-v, --version display version number and exit
|
||||
-V display version code and exit,
|
||||
this is used almost exclusively by Superuser.apk
|
||||
-mm, -M,
|
||||
--mount-master run in the global mount namespace,
|
||||
use if you need to publicly apply mounts
|
||||
```
|
||||
|
||||
Note: even though the `-Z, --context` option is not listed above, it actually still exists. However MagiskSU will silently ignore the option since it's not needed anymore. It is still left over because some apps still request root shell with specific contexts as an option.
|
||||
|
||||
### resetprop
|
||||
An advanced system prop manipulation utility; you can arbitrarily alter system props using this tool. Here's some background knowledge:
|
||||
|
||||
> System props are stored in a hybrid trie/binary tree data structure in memory; it was originally designed to only support adding nodes, and no nodes should be removed. Props can be read by many processes (e.g. via the `getprop` command); however, only the `init` process have write access to the property data. `init` provides a `property_service` to accept property update requests, so all property changes are monitored and controlled by `init` Restrictions such as **read-only** props (props that starts with `ro.`), which can only be set once and cannot be changed afterwards, is therefore implemented in `init`.
|
||||
|
||||
**resetprop** acts just like `init`: directly access the data structure, bypassing the whole `property_service` part. Doing so, we gain **arbitrary modification** power, including altering read-only props and deleting properties. Delete properties, which was stated *"forbidden"* in the data structure, is implemented through some tricks in the data structure.
|
||||
|
||||
One subtle thing to be aware of is that if we change props by directly modifying the data structure, `on property:foo=bar` triggers registered in `*.rc` scripts will not be triggered properly. This may be a good thing or a bad thing, depending on what behavior you expect. I made the default behavior to match the original setprop command, which **WILL** trigger events, but of course I provide a flag (`-n`) to disable it if you need this special behavior.
|
||||
|
||||
Command help message:
|
||||
|
||||
```
|
||||
Usage: resetprop [options] [args...]
|
||||
|
||||
Options:
|
||||
-v show verbose output
|
||||
-n only modify property in memory
|
||||
|
||||
resetprop NAME VALUE set property entry NAME with VALUE
|
||||
resetprop --file FILE load props from FILE
|
||||
resetprop --delete NAME remove prop entry NAME
|
||||
```
|
||||
|
||||
### magiskpolicy
|
||||
(This tool is aliased to `supolicy` for compatibility)
|
||||
|
||||
A tool to patch `sepolicy`. **magiskpolicy** also comes with built-in rules to unleash restrictions to make Magisk work properly. `sepolicy` is a compiled binary containing SELinux rules; we directly patch rules in the binary format since we don't have access to the SELinux policy source (`*.te`) files.
|
||||
|
||||
The Magisk daemon itself, the root shell, and all processes spawned from the daemon and root shell are all running in the context `u:r:su:s0`. This context is not only patched to be permissive, but also patched to allow any transition from `u:r:su:s0` to any domain. This was done because Samsung devices do not support permissive out of the box.
|
||||
|
||||
The built in patches are split to 3 parts: minimal, medium, and large. The full patch will result in a huge policy file, which might cause the `sepolicy` file unable to fit in `boot.img`.
|
||||
|
||||
- The minimal patch is just enough to start Magisk daemon and allow the daemon to further patch the policy during boot time (which is called **live patch**). It is done at installation and directly into `boot.img`.
|
||||
- The medium patch covers most common operations, and is live patched as soon as Magisk daemon is started (blocking boot process).
|
||||
- The large patch contains the full patch. Due to the concern of greatly increasing the boot time, it is designed to run in the background until it's joined in the non-blocking late_start bootstage.
|
||||
|
||||
What this all means is that **only late_start service mode is guaranteed to run in a fully patched environment**. If any script is not time critical, it is **highly recommended to run those scripts in late_start service mode**.
|
||||
|
||||
Command help message:
|
||||
|
||||
```
|
||||
Usage: magiskpolicy [--options...] [policystatements...]
|
||||
|
||||
Options:
|
||||
--live directly load patched policy to device
|
||||
--minimal minimal patches, used for boot image patches
|
||||
--load <infile> load policies from <infile>
|
||||
(load from live policies if not specified)
|
||||
--save <outfile> save policies to <outfile>
|
||||
|
||||
One policy statement should be treated as one parameter;
|
||||
this means a full policy statement should be enclosed in quotes;
|
||||
multiple policy statements can be provided in a single command
|
||||
|
||||
The statements has a format of "<action> [args...]"
|
||||
Use '*' in args to represent every possible match.
|
||||
Collections wrapped in curly brackets can also be used as args.
|
||||
|
||||
Supported policy statements:
|
||||
|
||||
Type 1:
|
||||
"<action> source-class target-class permission-class permission"
|
||||
Action: allow, deny, auditallow, auditdeny
|
||||
|
||||
Type 2:
|
||||
"<action> source-class target-class permission-class ioctl range"
|
||||
Action: allowxperm, auditallowxperm, dontauditxperm
|
||||
|
||||
Type 3:
|
||||
"<action> class"
|
||||
Action: create, permissive, enforcing
|
||||
|
||||
Type 4:
|
||||
"attradd class attribute"
|
||||
|
||||
Type 5:
|
||||
"typetrans source-class target-class permission-class default-class (optional: object-name)"
|
||||
|
||||
Notes:
|
||||
- typetrans does not support the all match '*' syntax
|
||||
- permission-class cannot be collections
|
||||
- source-class and target-class can also be attributes
|
||||
|
||||
Example: allow { source1 source2 } { target1 target2 } permission-class *
|
||||
Will be expanded to:
|
||||
|
||||
allow source1 target1 permission-class { all-permissions }
|
||||
allow source1 target2 permission-class { all-permissions }
|
||||
allow source2 target1 permission-class { all-permissions }
|
||||
allow source2 target2 permission-class { all-permissions }
|
||||
```
|
||||
|
||||
### magiskhide
|
||||
This is the CLI to control the state of MagiskHide.
|
||||
|
||||
Command help message:
|
||||
|
||||
```
|
||||
Usage: magiskhide [--options [arguments...] ]
|
||||
|
||||
Options:
|
||||
--enable Start magiskhide
|
||||
--disable Stop magiskhide
|
||||
--add PROCESS Add PROCESS to the hide list
|
||||
--rm PROCESS Remove PROCESS from the hide list
|
||||
--ls Print out the current hide list
|
||||
```
|
6
docs/changelog.md
Normal file
6
docs/changelog.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Changelog
|
||||
- 2017.8.16
|
||||
- Initial version for Magisk v13.5
|
||||
- 2017.9.28
|
||||
- Update applets info to Magisk v14.1
|
||||
- Add OTA tips
|
60
docs/details.md
Normal file
60
docs/details.md
Normal file
@@ -0,0 +1,60 @@
|
||||
## Boot Stages
|
||||
If you are working on complicated projects, you shall need more control to the whole process. Magisk can run scripts in different boot stages, so you can fine tune exactly what you want to do. It's recommended to read this documentation along with the procedure graph.
|
||||
|
||||
- post-fs mode
|
||||
- **This stage is BLOCKING. Boot process will NOT continue until everything is done, or 20 seconds has passed**
|
||||
- Happens after most partitions are mounted, except `/data`
|
||||
- Magisk will bind mount files under `/cache/magisk_mount/system` to corresponding paths
|
||||
- It is only **Simple Mount**, which means it will replace existing files, but cannot add/remove files.
|
||||
- post-fs-data mode
|
||||
- **This stage is BLOCKING. Boot process will NOT continue until everything is done, or 60 seconds has passed**
|
||||
- Happens after `/data` is ready (including the case when `/data` is encrypted)
|
||||
- Happens before Zygote and system servers are started (which means pretty much everything)
|
||||
- Mirrors will be mounted. These mirrors play a critical role in **Magic Mount**
|
||||
- `/data/magisk.img` will be merged, trimmed, and mounted to `/magisk`
|
||||
- Magisk will run scripts under `/magisk/.core/post-fs-data.d`
|
||||
- Magisk will run scripts: `/magisk/$MODID/post-fs-data.sh` (placed in each module directory)
|
||||
- Magisk will finally **Magisk Mount** module files
|
||||
- late_start service mode
|
||||
- **This stage is NON-BLOCKING, it will run in parallel with other processes**
|
||||
- Happens when class late_start is started
|
||||
- Put time consuming but non time critical tasks here. Boot process will be stuck if it took too long to finish your tasks in previous modes
|
||||
- SELinux is guaranteed to be fully patched in this stage; **it is recommended to run most scripts in this stage**, unless your scripts require some time critical operations
|
||||
- Magisk will run scripts under `/magisk/.core/service.d`
|
||||
- Magisk will run scripts: `/magisk/$MODID/service.sh` (placed in each module directory)
|
||||
|
||||
## Magic Mount Details
|
||||
### Terminology
|
||||
- **Item**: A folder, file, or symbolic link
|
||||
- **Leaf**: The very end of a directory structure tree, can be either a file or symbolic link
|
||||
- **`$MODPATH`**: A variable to represent the path of a module folder
|
||||
- **Source item**: An item under `$MODPATH/system`, for example, `$MODPATH/system/build.prop` is a source item
|
||||
- **Existing item**: An item under `/system`, for example, `/system/bin/app_process` is an existing item
|
||||
- **Target item**: A corresponding item of a source item. For example, the target item of `$MODPATH/system/build.prop` is `/system/build.prop`
|
||||
|
||||
Note: A target item does not imply it is an existing item. A target item might not exist in the actual `/system`
|
||||
|
||||
### Policies
|
||||
- For a source leaf: if its target item is also an existing item, the existing item will be replaced with the source leaf
|
||||
- For a source leaf: if its target item is not an existing item, the source leaf will be added to the path of its target item
|
||||
- For any existing item that's not a target item, it will stay intact
|
||||
|
||||
Above is the rule of thumb. Basically it means that Magic Mount merges the files from `$MODPATH/system` into the real `/system`. A simpler way to understand is to think as it dirty copies the contents from `$MODPATH/system` into `/system`.
|
||||
|
||||
However, an addition rule will override the above policies:
|
||||
|
||||
- For a source folder containing the file `.replace`, the source folder will be treated as if it is a source leaf. That is, the items within the target folder will be completely discarded, and the target folder will be replaced with the source folder.
|
||||
|
||||
Directories containing a file named `.replace` will NOT be merged into the system. It will directly replace the target directory. A simpler way to understand is to think as if it wipes the target folder, and then copies the whole folder to the target path.
|
||||
|
||||
### Notes
|
||||
- Sometimes, completely replacing a folder is inevitable. For example you want to replace `/system/priv-app/SystemUI` in your stock rom. In stock roms, system apps usually comes with pre-optimized files. If your replacement `SystemUI.apk` is deodexed (which is most likely the case), you would want to replace the whole `/system/priv-app/SystemUI` to make sure the folder only contains the modified `SystemUI.apk` and **NOT** merge with the pre-optimized files.
|
||||
- If you are using the [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template), you can create a list of folders you want to replace in the file `config.sh`. The installation scripts will handle the creation of `.replace` files into the listed folders for you.
|
||||
- Adding non-existing target items is a relatively expensive operation. Magisk would need to do **MANY** under-the-hood tasks to achieve it. Replacing a whole folder is recommended if viable, it reduces the complexity of the construction of the mounting tree and could speed up the booting time
|
||||
|
||||
## Simple Mount Details
|
||||
Some files require to be mounted much earlier in the boot process, currently known are bootanimation and some libs (most users won't change them). You can simply place your modified files into the corresponding path under `/cache/magisk_mount`. At boot time, Magisk will **clone all the attributes from the target file**, which includes selinux context, permission mode, owner, group. It'll then bind mount the file to the target. This means you don't need to worry about the metadatas for files placed under `/cache/magisk_mount`: copy the file to the correct place, reboot then you're done!
|
||||
|
||||
This mode does not feature the same complex Magic Mount implementation, it will mount a source leaf to an existing target item under `/system`.
|
||||
|
||||
For example, you want to replace `/system/media/bootanimation.zip`, copy your new boot animation zip to `/cache/magisk_mount/system/media/bootanimation.zip,` Magisk will mount your files in the next reboot.
|
BIN
docs/images/disable_auto_ota.png
Normal file
BIN
docs/images/disable_auto_ota.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 245 KiB |
BIN
docs/images/flashfire.png
Normal file
BIN
docs/images/flashfire.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
BIN
docs/images/install_second_slot.png
Normal file
BIN
docs/images/install_second_slot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 237 KiB |
BIN
docs/images/ota_step2.png
Normal file
BIN
docs/images/ota_step2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 176 KiB |
BIN
docs/images/repo_description.png
Normal file
BIN
docs/images/repo_description.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
docs/images/restore_boot.png
Normal file
BIN
docs/images/restore_boot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 258 KiB |
76
docs/module_repo.md
Normal file
76
docs/module_repo.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Magisk Modules and Online Repo
|
||||
## Magisk Module Format
|
||||
The Magisk module is a folder under `magisk`, which has a structure as described below:
|
||||
|
||||
```
|
||||
magisk
|
||||
├── .
|
||||
├── .
|
||||
├── a_module <--- The ID of the module, should match with module.prop
|
||||
│ ├── auto_mount <--- If this file exists, auto mount is enabled
|
||||
│ ├── disable <--- If this file exists, the module is disabled
|
||||
│ ├── module.prop <--- This files stores the indentity and properties of the module
|
||||
│ ├── post-fs-data.sh <--- This script will be executed in post-fs-data
|
||||
│ ├── remove <--- If this file exists, the module will be removed next reboot
|
||||
│ ├── service.sh <--- This script will be executed in late_start service
|
||||
│ ├── system.prop <--- This file will be loaded as system props
|
||||
│ ├── system <--- If auto mount is enabled, Magisk will "Magic Mount" this folder
|
||||
│ │ ├── app
|
||||
│ │ ├── .
|
||||
│ │ └── .
|
||||
│ ├── vendor <--- Auto generated. A symlink to $MODID/system/vendor
|
||||
│ ├── . <--- Any other files/folders are allowed
|
||||
│ └── .
|
||||
├── another_module
|
||||
│ ├── .
|
||||
│ └── .
|
||||
├── .
|
||||
├── .
|
||||
```
|
||||
|
||||
## Magisk Module Template
|
||||
The **Magisk Module Template** is hosted **[here](https://github.com/topjohnwu/magisk-module-template)**.
|
||||
|
||||
It is a template to create a flashable zip to install Magisk Modules. It is created to be simple to use so that anyone can create their own modules easily. The template itself contains minimal scripting for installation; most of the functions are located externally in [util_functions.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/util_functions.sh), which will be installed along with Magisk, and can be upgraded through a Magisk upgrade without the need of a template update.
|
||||
|
||||
The template should meet most modules' needs. Add files into `system`, which will be cloned into `/system` by Magisk after installing the module, and in addition to module files, `system.prop`, `post-fs-data.sh`, `service.sh` are also installable through this template.
|
||||
|
||||
Here are some files you would want to know:
|
||||
|
||||
- `config.sh`: A simple script used as a configuration file for the actual installation file to correctly install your module. It is the place you can select which features your module needs/disables.
|
||||
- `module.prop`: This file contains your module's indentity and properties, including name and versions etc.. This file will be used to identify your module on an actual device and in the [Magisk Modules Repo](https://github.com/Magisk-Modules-Repo)
|
||||
- `common/*`: These files will be installed to the module with the correspond role
|
||||
- `META-INF/com/google/android/update-binary`: The actual installation script. Modify this file for advanced custom behavior
|
||||
|
||||
## Create a Magisk Module With The Template
|
||||
1. Clone / download [this repo](https://github.com/topjohnwu/magisk-module-template)
|
||||
1. Open `config.sh` and carefully read the fully documented file. Follow the instructions within the script
|
||||
1. You should at least have `config.sh` and `module.prop` modified
|
||||
1. Directly zip all files; the result zip file is a flashable zip that can be used in both Magisk Manager and custom recoveries
|
||||
1. Please check [**Notes**](#notes) for several precautions
|
||||
|
||||
## Submit Your Module to Magisk Modules Repo
|
||||
If you want to share your module with others, you can submit your modules to [Magisk Modules Repo](https://github.com/Magisk-Modules-Repo). You would need some basic `git` knowledge here.
|
||||
|
||||
1. Create a module as stated above, and test if it works properly
|
||||
1. Fork [this repo](https://github.com/topjohnwu/magisk-module-template) to your account
|
||||
1. Commit and push your changes to your forked repo
|
||||
1. Open an issue in [topjohnwu/Magisk_Repo_Central](https://github.com/topjohnwu/Magisk_Repo_Central/issues/new) with your repo link
|
||||
1. I will review your module, and once accepted, your repo should be cloned into [Magisk-Modules-Repo](https://github.com/Magisk-Modules-Repo)
|
||||
1. You should receive an email to become the collaborator so you can edit the repo in the future.
|
||||
|
||||
#### Once your module is live on the Modules Repo, the description of your repo should be the ID of your module. Please do NOT change the description, repeat, do NOT change the description.
|
||||
|
||||

|
||||
|
||||
## Notes
|
||||
- The Module Template depends on external scripts, be aware of the minimal required Magisk version of the template.
|
||||
- **Windows users please check here!!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad.
|
||||
- In `module.prop`, `version` can be an arbitrary string, so any fancy version name (e.g. ultra-beta-v1.1.1.1) is allowed. However, `versionCode` **MUST** be an integer. The value is used for version comparison.
|
||||
- Make sure your module ID **does not contain any spaces**.
|
||||
|
||||
## Notes For Repo Developers
|
||||
|
||||
- Magisk Manager monitors all repo's `master` branch. Any changes to the branch `master` will be reflected to all users immediately. If you are working on an update for a module, please work on another branch, make sure it works, then finally merge the changes back to `master`.
|
||||
- Once you finished upgrading your repo, increase at least the `versionCode` in `module.prop`. Magisk Manager uses this value to compare with the local installed module to determine whether an update is availible.
|
||||
- The description of your repo should be the same as your module ID. If you changed your description, Magisk Manager will fail to identify your repo, and cannot relate installed module to the online repo together.
|
11
docs/procedures.html
Normal file
11
docs/procedures.html
Normal file
File diff suppressed because one or more lines are too long
53
docs/tips.md
Normal file
53
docs/tips.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Tips and Tricks
|
||||
|
||||
## OTA Installation Tips
|
||||
Magisk do modifications systemless-ly, which means applying official OTAs is much simpler. Here I provide a few tutorials for several different kind of devices to apply OTAs and preserve Magisk after the installation if possible.
|
||||
|
||||
**This tutorial is only for Magisk v14.1+**
|
||||
|
||||
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in anyway, even remounting the partition to rw will tamper block verification!!**
|
||||
|
||||
#### Prerequisites
|
||||
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
|
||||
<img src="images/disable_auto_ota.png" width="250">
|
||||
1. When an OTA is available, please go to Magisk Manager → Uninstall → Restore Stock Boot. **Do not reboot immediately or you will have Magisk uninstalled.** This will restore your boot back to 100% untouched stock boot image in order to pass boot verification. **This step is required before doing any of the following steps written below!**
|
||||
<img src="images/restore_boot.png" width="250">
|
||||
|
||||
#### Devices with A/B Partitions
|
||||
(Includes Pixel family)
|
||||
|
||||
Due to the fact that these devices have two separate partitions and the OTA installation happens live when the system is still running, these devices have the best support: the out-of-the-box OTA installation works seamlessly and Magisk will be preserved after the installation.
|
||||
|
||||
1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates)
|
||||
1. Once the installation passed step 1 and starting step 2, go to Magisk Manager → Install → Install to Second Slot. This will install Magisk into the second boot image slot, which is the updated slot.
|
||||
<img src="images/ota_step2.png" width="250"> <img src="images/install_second_slot.png" width="250">
|
||||
1. Let the OTA finish its job. After a reboot, the bootloader will switch to the updated system. Magisk should still be installed since we already patched the new boot image.
|
||||
|
||||
#### Devices with FlashFire Support
|
||||
(Includes Pixel family, Nexus family, Samsung devices)
|
||||
(If you are using a device with A/B partitions, I **strongly** recommend you to use the method stated above since it uses the stock OTA installation mechanism and will always work under any circumstances)
|
||||
|
||||
The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash) app developed by Chainfire is a great app to apply OTAs and preserve root at the same time. However, whether it supports your device/system combination depends on the application itself, and support may also change in the future. If you face any issues, please directly [report to Chainfire](https://forum.xda-developers.com/general/paid-software/flashfire-t3075433).
|
||||
|
||||
1. After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.**
|
||||
1. Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
|
||||
1. Please use the options shown in the screenshot below. The key point is to disable EverRoot (or it will install SuperSU), and add a new action to flash Magisk zip **after** the OTA update.zip (the update.zip is auto generated in the previous step).
|
||||
<img src="images/flashfire.png" width="250">
|
||||
1. Press the big **Flash** button, after a few minutes it should reboot updated with Magisk installed.
|
||||
|
||||
#### Other Devices - General Case
|
||||
Unfortunately, there are no real good ways to apply OTAs on all devices. Also, the tutorial provided below will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. Here I share my personal experience with my daily driver - HTC U11.
|
||||
|
||||
1. To properly install OTAs, you should have your stock recovery installed on your device. If you have custom recovery installed, you can restore it from your previous backup, or dumps found online, or factory images provided by OEMs.
|
||||
If you decide to start by installing Magisk without touching your recovery partition, you have a few choices, either way you will end up with a Magisk rooted device, but recovery remain stock untouched:
|
||||
- If supported, use `fastboot boot <recovery_img>` to boot the custom recovery and install Magisk.
|
||||
- If you have a copy of your stock boot image dump, install Magisk by patching boot image via Magisk Manager, and manually flash it through download mode / fastboot mode / Odin
|
||||
1. Once your device have stock recovery and stock boot image restored, download the OTA. Optionally, once you downloaded the OTA update zip, you can find a way to copy the zip out since you are still rooted. Personally I will extract the stock boot image and recovery image from the OTA zip for future usage (to patch via Magisk Manager or restore stock recovery etc.)
|
||||
1. Apply and reboot your device. This will use the official stock OTA installation mechanism of your device to upgrade your system.
|
||||
1. Once it's done you will be left with an upgraded, 100% stock, un-rooted device. You will have to manually flash Magisk back. Consider using the methods stated in step 1. to flash Magisk without touching the recovery partition if you want to receive stock OTAs frequently.
|
||||
|
||||
## Remove Files
|
||||
How to efficiently remove a file systemless-ly? To actually make the file **disappear** is complicated (possible, not worth the effort). **Replacing it with a dummy file should be good enough**! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
|
||||
|
||||
## Remove Folders
|
||||
Same as mentioned above, actually making the folder to **disappear** is not worth the effort. **Replacing it with an empty folder should be good enough**! A handy trick for module developers using [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) is to add the folder you want to remove into the `REPLACE` list within `config.sh`. If your module doesn't provide a correspond folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
|
22
gradle.properties
Normal file
22
gradle.properties
Normal 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
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
160
gradlew
vendored
Executable 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
90
gradlew.bat
vendored
Normal 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
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user