Compare commits

..

55 Commits
v14.2 ... v14.5

Author SHA1 Message Date
topjohnwu
687e3b13ea Bump Magisk Manager version 2017-11-23 23:56:17 +08:00
topjohnwu
8c6bb383b7 Add support to build with NDK r10e 2017-11-23 23:55:33 +08:00
topjohnwu
bc592c1d13 Fix bootloops on some devices 2017-11-23 18:38:12 +08:00
topjohnwu
968bd8be67 Update Magisk Manager 2017-11-23 01:33:33 +08:00
topjohnwu
d8b8adb88c Fix is_mounted function 2017-11-22 22:22:29 +08:00
topjohnwu
f42d820891 Several small tweaks 2017-11-22 16:12:08 +08:00
topjohnwu
bc21a1fb71 Update internal paths 2017-11-22 16:04:24 +08:00
topjohnwu
3bc31374ac Fix issue of touch command on Android 5.0 2017-11-20 04:13:51 +08:00
topjohnwu
858e7bae2b More precise size for mmap 2017-11-20 03:40:37 +08:00
Patryk Szalanski
8c02d120a2 Fix missing includes for ndk r16 2017-11-17 02:54:16 +08:00
topjohnwu
07e353f4ff Fix typo when handling MTK 2017-11-16 22:26:28 +08:00
topjohnwu
bb33d9e600 Use real tmp files 2017-11-15 05:48:31 +08:00
topjohnwu
68eb0bdec9 Allow specific signal to specific threads 2017-11-15 05:48:31 +08:00
topjohnwu
32ee8e462c Properly and fully support dtbo patching 2017-11-15 05:48:31 +08:00
topjohnwu
e79aa54b70 Proper Windows building support 2017-11-12 04:17:56 +08:00
topjohnwu
9a95652034 Simplify compress code 2017-11-12 04:08:52 +08:00
topjohnwu
912c188b53 Add dtbo.img patch support 2017-11-11 01:33:50 +08:00
topjohnwu
e9d0f615ba Add dtb test command 2017-11-11 01:30:33 +08:00
topjohnwu
9136573596 Add stdin/stdout support 2017-11-10 20:25:41 +08:00
topjohnwu
2487ec94e6 Add support to remove avb verity 2017-11-10 03:45:06 +08:00
topjohnwu
811489f157 Small reorganization 2017-11-10 01:51:41 +08:00
topjohnwu
b438cc9335 Remove unnecessary monogisk 2017-11-10 00:54:54 +08:00
topjohnwu
1d3d30fa45 Get potential slot info 2017-11-09 03:05:25 +08:00
topjohnwu
72b5985398 Prevent race condition on /magisk symlink 2017-11-09 03:05:01 +08:00
topjohnwu
2db60e0a6b Add Pixel 2 XL support 2017-11-06 22:33:41 +08:00
topjohnwu
e710848345 Unify Magisk configuration
Introduce monogisk tool
2017-11-06 06:22:45 +08:00
topjohnwu
8d6f3c2450 Fix build error on release builds 2017-11-02 14:48:22 +08:00
topjohnwu
f863d127e7 Fix Xiaomi A1 support 2017-11-01 22:49:06 +08:00
topjohnwu
a831110816 Add boot signing to installation 2017-10-31 17:05:24 +08:00
topjohnwu
e97bdb53f4 Adjust java paths 2017-10-30 03:45:50 +08:00
topjohnwu
fe1439fbac Support changing su requester package name 2017-10-28 16:20:31 +08:00
topjohnwu
2bc30e5c22 Hide /magisk 2017-10-28 16:12:01 +08:00
topjohnwu
7244c02a0d Small adjustments 2017-10-28 16:11:01 +08:00
topjohnwu
6c229ffa68 Update external sources 2017-10-19 00:46:39 +08:00
topjohnwu
cdc5d983f3 Bump MagiskManager version 2017-10-15 03:21:33 +08:00
topjohnwu
96688e4dac Fix proper Lollipop selinux support 2017-10-14 22:37:02 +08:00
topjohnwu
28a945fee9 Fix SEGFAULT in magisk log dumper 2017-10-14 21:10:52 +08:00
topjohnwu
c7e777255a Reduce unnecessary stack memory allocation 2017-10-14 21:10:51 +08:00
topjohnwu
2dd4cf040e Prevent multiple process clashes to start daemon 2017-10-14 21:10:51 +08:00
topjohnwu
d1b9eca5eb Fix bug that cause boot delay 2017-10-14 00:19:13 +08:00
topjohnwu
594a67fe28 Cleanup and add more xwraps 2017-10-14 00:08:12 +08:00
topjohnwu
cddeaffada Remove err_handler 2017-10-13 22:26:42 +08:00
topjohnwu
2a8898e7c3 Fix lz4 legacy on LG zImages 2017-10-13 00:18:40 +08:00
topjohnwu
ce3f3b09b4 Brute force resizeimg for Crapsung device :) 2017-10-12 14:32:40 +08:00
topjohnwu
fe4b3df7e9 Fix selinux context on Magisk files 2017-10-12 14:32:40 +08:00
topjohnwu
25bdbcf526 Add new file operations 2017-10-12 14:32:40 +08:00
topjohnwu
df7eaa5598 Reduce update-binary size 2017-10-11 02:26:43 +08:00
topjohnwu
bb7099376b Improve daemon startup and log management 2017-10-11 02:26:28 +08:00
topjohnwu
0327fd9710 Restart MagiskHide if daemon restarted 2017-10-10 19:49:15 +08:00
topjohnwu
e645c6e465 Refactor resetprop 2017-10-10 02:04:50 +08:00
topjohnwu
78a3d36ccc Allow devices without separate vendor partition 2017-10-09 21:53:50 +08:00
topjohnwu
3942858ccd Introduce a single general purpose logcat monitor 2017-10-09 05:39:40 +08:00
topjohnwu
03c8d716cc Introduce Invincible Mode: Self recover service 2017-10-08 22:00:22 +08:00
topjohnwu
60181c4fcb MagiskManager -> java 2017-10-07 22:48:16 +08:00
topjohnwu
c215447405 Fix Pixel C installation 2017-10-07 22:08:10 +08:00
70 changed files with 3484 additions and 3101 deletions

4
.gitignore vendored
View File

@@ -1,7 +1,9 @@
out/
obj/
libs/
*.zip
*.jks
*.apk
# Copied binaries
# Built binaries
ziptools/zipadjust

14
.gitmodules vendored
View File

@@ -4,14 +4,11 @@
[submodule "jni/su"]
path = jni/su
url = https://github.com/topjohnwu/MagiskSU.git
[submodule "jni/ndk-compression"]
path = jni/external/ndk-compression
url = https://github.com/topjohnwu/ndk-compression.git
[submodule "jni/magiskpolicy"]
path = jni/magiskpolicy
url = https://github.com/topjohnwu/magiskpolicy.git
[submodule "MagiskManager"]
path = MagiskManager
path = java
url = https://github.com/topjohnwu/MagiskManager.git
[submodule "jni/busybox"]
path = jni/external/busybox
@@ -19,3 +16,12 @@
[submodule "jni/external/dtc"]
path = jni/external/dtc
url = https://github.com/dgibson/dtc
[submodule "jni/external/lz4"]
path = jni/external/lz4
url = https://github.com/lz4/lz4.git
[submodule "jni/external/bzip2"]
path = jni/external/bzip2
url = https://github.com/nemequ/bzip2.git
[submodule "jni/external/xz"]
path = jni/external/xz
url = https://github.com/xz-mirror/xz.git

Submodule MagiskManager deleted from 773c24b7fc

View File

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

291
build.py
View File

@@ -3,6 +3,10 @@ import sys
import os
import subprocess
if os.name == 'nt':
from colorama import init
init()
def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1)
@@ -37,14 +41,32 @@ import errno
import shutil
import lzma
import base64
import tempfile
def silentremove(file):
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)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def mkdir(path, mode=0o777):
try:
os.mkdir(path, mode)
except:
pass
def mkdir_p(path, mode=0o777):
os.makedirs(path, mode, exist_ok=True)
def zip_with_msg(zipfile, source, target):
if not os.path.exists(source):
error('{} does not exist! Try build \'binary\' and \'apk\' before zipping!'.format(source))
@@ -56,6 +78,7 @@ def build_all(args):
build_apk(args)
zip_main(args)
zip_uninstaller(args)
build_snet(args)
def build_binary(args):
header('* Building Magisk binaries')
@@ -63,46 +86,64 @@ def build_binary(args):
# Force update Android.mk timestamp to trigger recompilation
os.utime(os.path.join('jni', 'Android.mk'))
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
proc = subprocess.run('{} APP_CFLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\" -j{}'.format(
ndk_build, args.versionString, args.versionCode, debug_flag, multiprocessing.cpu_count()), shell=True)
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
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')
# Prebuild
proc = subprocess.run('{} 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('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('{} {} -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('libs', arch, binary), os.path.join('out', arch, binary))
except:
pass
def build_apk(args):
header('* Building Magisk Manager')
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
source = os.path.join('ziptools', key)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
target = os.path.join('java', 'app', 'src', 'main', 'assets', key)
cp(source, target)
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
source = os.path.join('scripts', script)
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', script)
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
target = os.path.join('java', 'app', 'src', 'main', 'assets', script)
cp(source, target)
os.chdir('MagiskManager')
# Build unhide app and place in assets
proc = subprocess.run('{} unhide::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
source = os.path.join('unhide', 'build', 'outputs', 'apk', 'release', 'unhide-release-unsigned.apk')
target = os.path.join('app', 'src', 'main', 'assets', 'unhide.apk')
print('cp: {} -> {}'.format(source, target))
shutil.copyfile(source, target)
print('')
os.chdir('java')
if args.release:
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
error('Please generate a java keystore and place it in \'release_signature.jks\'')
proc = subprocess.run('{} app::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
@@ -113,8 +154,8 @@ def build_apk(args):
# Find the latest build tools
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
silentremove(aligned)
silentremove(release)
rm(aligned)
rm(release)
proc = subprocess.run([
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'),
@@ -138,81 +179,59 @@ def build_apk(args):
if proc.returncode != 0:
error('Release sign Magisk Manager failed!')
silentremove(unsigned)
silentremove(aligned)
rm(unsigned)
rm(aligned)
mkdir(os.path.join('..', 'out'))
target = os.path.join('..', 'out', 'app-release.apk')
print('')
mv(release, target)
else:
proc = subprocess.run('{} app::assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
mkdir(os.path.join('..', 'out'))
target = os.path.join('..', 'out', 'app-debug.apk')
print('')
mv(source, target)
# Return to upper directory
os.chdir('..')
def sign_adjust_zip(unsigned, output):
zipsigner = os.path.join('ziptools', 'zipsigner', 'build', 'libs', 'zipsigner.jar')
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
header('* Building zipadjust')
# Compile zipadjust
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
if proc.returncode != 0:
error('Build zipadjust failed!')
if not os.path.exists(zipsigner):
header('* Building zipsigner.jar')
os.chdir(os.path.join('ziptools', 'zipsigner'))
proc = subprocess.run('{} shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build zipsigner.jar failed!')
os.chdir(os.path.join('..', '..'))
header('* Signing / Adjusting Zip')
publicKey = os.path.join('ziptools', 'public.certificate.x509.pem')
privateKey = os.path.join('ziptools', 'private.key.pk8')
# Unsigned->signed
proc = subprocess.run(['java', '-jar', zipsigner,
publicKey, privateKey, unsigned, 'tmp_signed.zip'])
def build_snet(args):
os.chdir('java')
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('First sign flashable zip failed!')
# Adjust zip
proc = subprocess.run([os.path.join('ziptools', 'zipadjust'), 'tmp_signed.zip', 'tmp_adjusted.zip'])
if proc.returncode != 0:
error('Adjust flashable zip failed!')
# Adjusted -> output
proc = subprocess.run(['java', '-jar', zipsigner,
"-m", publicKey, privateKey, 'tmp_adjusted.zip', output])
if proc.returncode != 0:
error('Second sign flashable zip failed!')
# Cleanup
silentremove(unsigned)
silentremove('tmp_signed.zip')
silentremove('tmp_adjusted.zip')
error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
mkdir(os.path.join('..', 'out'))
target = os.path.join('..', 'out', 'snet.apk')
print('')
mv(source, target)
os.chdir('..')
def gen_update_binary():
update_bin = []
binary = os.path.join('libs', 'armeabi-v7a', 'b64xz')
binary = os.path.join('out', 'armeabi-v7a', 'b64xz')
if not os.path.exists(binary):
error('Please build \'binary\' before zipping!')
with open(binary, 'rb') as b64xz:
update_bin.append('#! /sbin/sh\nEX_ARM=')
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('libs', 'x86', 'b64xz')
update_bin.append('#! /sbin/sh\nEX_ARM=\'')
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('out', 'x86', 'b64xz')
with open(binary, 'rb') as b64xz:
update_bin.append('\nEX_X86=')
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('libs', 'armeabi-v7a', 'busybox')
update_bin.append('\'\nEX_X86=\'')
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
binary = os.path.join('out', 'armeabi-v7a', 'busybox')
with open(binary, 'rb') as busybox:
update_bin.append('\nBB_ARM=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii'))
binary = os.path.join('libs', 'x86', 'busybox')
update_bin.append('\'\nBB_ARM=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
binary = os.path.join('out', 'x86', 'busybox')
with open(binary, 'rb') as busybox:
update_bin.append('\nBB_X86=')
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii'))
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
update_bin.append('\n')
with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script:
update_bin.append(script.read())
@@ -221,7 +240,9 @@ def gen_update_binary():
def zip_main(args):
header('* Packing Flashable Zip')
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -234,17 +255,13 @@ def zip_main(args):
# Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
for binary in ['magisk', 'magiskboot']:
source = os.path.join('libs', lib_dir, binary)
for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('out', lib_dir, binary)
target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target)
source = os.path.join('libs', 'arm64-v8a', 'magiskinit')
target = os.path.join('arm64', 'magiskinit')
zip_with_msg(zipf, source, target)
# APK
source = os.path.join('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)
@@ -266,10 +283,6 @@ def zip_main(args):
source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('addon.d', '99-magisk.sh')
zip_with_msg(zipf, source, target)
# init.magisk.rc
source = os.path.join('scripts', 'init.magisk.rc')
target = os.path.join('common', 'init.magisk.rc')
zip_with_msg(zipf, source, target)
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
@@ -278,13 +291,15 @@ def zip_main(args):
# End of zipping
output = 'Magisk-v{}.zip'.format(args.versionString)
sign_adjust_zip('tmp_unsigned.zip', output)
output = os.path.join('out', 'Magisk-v{}.zip'.format(args.versionString))
sign_adjust_zip(unsigned, output)
def zip_uninstaller(args):
header('* Packing Uninstaller Zip')
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
@@ -297,7 +312,7 @@ def zip_uninstaller(args):
# Binaries
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
source = os.path.join('libs', lib_dir, 'magiskboot')
source = os.path.join('out', lib_dir, 'magiskboot')
target = os.path.join(zip_dir, 'magiskboot')
zip_with_msg(zipf, source, target)
@@ -323,29 +338,82 @@ 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.0.jar'
jarsigner = os.path.join('java', '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)
os.chdir('java')
proc = subprocess.run('{} crypto:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build {} failed!'.format(signer_name))
os.chdir('..')
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')
header('* Cleaning binaries')
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' 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')
if 'java' in args.target:
header('* Cleaning java')
os.chdir('java')
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
os.chdir('..')
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')
@@ -364,6 +432,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)
@@ -372,7 +443,7 @@ 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)

1
java Submodule

Submodule java added at f5ceee547c

View File

@@ -1,52 +1,45 @@
LOCAL_PATH := $(call my-dir)
# Some handy paths
JNI_ROOT := jni
SELINUX_PATH := jni/external/selinux
COMPRESS_LIB := jni/external/ndk-compression
DTC_PATH := jni/external/dtc
LIBSELINUX := $(SELINUX_PATH)/libselinux/include
LIBSEPOL := $(SELINUX_PATH)/libsepol/include $(SELINUX_PATH)/libsepol/cil/include
LIBZ := $(COMPRESS_LIB)/zlib
LIBLZMA := $(COMPRESS_LIB)/xz/src/liblzma/api
LIBLZ4 := $(COMPRESS_LIB)/lz4/lib
LIBBZ2 := $(COMPRESS_LIB)/bzip2
LIBFDT := $(DTC_PATH)/libfdt
EXT_PATH := jni/external
SE_PATH := $(EXT_PATH)/selinux
LIBSELINUX := $(SE_PATH)/libselinux/include
LIBSEPOL := $(SE_PATH)/libsepol/include $(SE_PATH)/libsepol/cil/include
LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
LIBLZ4 := $(EXT_PATH)/lz4/lib
LIBBZ2 := $(EXT_PATH)/bzip2
LIBFDT := $(EXT_PATH)/dtc/libfdt
########################
# Binaries
########################
ifdef PRECOMPILE
# magisk main binary
include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_STATIC_LIBRARIES := libsepol
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
LOCAL_C_INCLUDES := \
jni/include \
jni/external \
$(LIBSELINUX) \
$(LIBSEPOL)
$(LIBSELINUX)
LOCAL_SRC_FILES := \
daemon/magisk.c \
daemon/daemon.c \
daemon/socket_trans.c \
daemon/log_monitor.c \
daemon/bootstages.c \
core/magisk.c \
core/daemon.c \
core/log_monitor.c \
core/bootstages.c \
utils/misc.c \
utils/vector.c \
utils/xwrap.c \
utils/list.c \
utils/img.c \
utils/file.c \
magiskhide/magiskhide.c \
magiskhide/proc_monitor.c \
magiskhide/hide_utils.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
magiskpolicy/api.c \
resetprop/resetprop.cpp \
resetprop/system_properties.cpp \
su/su.c \
@@ -56,18 +49,43 @@ LOCAL_SRC_FILES := \
su/su_daemon.c \
su/su_socket.c
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch -DIS_DAEMON
LOCAL_CPPFLAGS := -std=c++11
LOCAL_CFLAGS := -DIS_DAEMON
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
# precompile
else
# magiskinit
include $(CLEAR_VARS)
LOCAL_MODULE := magiskinit
LOCAL_STATIC_LIBRARIES := libsepol liblzma
LOCAL_C_INCLUDES := \
jni/include \
out/$(TARGET_ARCH_ABI) \
$(LIBSEPOL) \
$(LIBLZMA)
LOCAL_SRC_FILES := \
core/magiskinit.c \
utils/vector.c \
utils/file.c \
utils/xwrap.c \
magiskpolicy/api.c \
magiskpolicy/magiskpolicy.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c
LOCAL_CFLAGS := -DNO_SELINUX
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
# magiskboot
include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := libz liblzma liblz4 libbz2 libfdt
LOCAL_STATIC_LIBRARIES := liblzma liblz4 libbz2 libfdt
LOCAL_C_INCLUDES := \
jni/include \
$(LIBZ) \
$(LIBLZMA) \
$(LIBLZ4) \
$(LIBBZ2) \
@@ -78,32 +96,17 @@ LOCAL_SRC_FILES := \
magiskboot/bootimg.c \
magiskboot/hexpatch.c \
magiskboot/compress.c \
magiskboot/boot_utils.c \
magiskboot/cpio.c \
magiskboot/sha1.c \
magiskboot/types.c \
magiskboot/dtb.c \
utils/xwrap.c \
utils/file.c \
utils/vector.c
LOCAL_CFLAGS := -DZLIB_CONST
include $(BUILD_EXECUTABLE)
# magiskinit
ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
include $(CLEAR_VARS)
LOCAL_MODULE := magiskinit
LOCAL_STATIC_LIBRARIES := libsepol
LOCAL_C_INCLUDES := jni/include $(LIBSEPOL)
LOCAL_SRC_FILES := \
magiskinit.c \
magiskboot/boot_utils.c \
utils/xwrap.c \
magiskpolicy/rules.c \
magiskpolicy/sepolicy.c \
magiskpolicy/api.c
LOCAL_LDFLAGS := -static
LOCAL_CFLAGS := -DNO_SELINUX
LOCAL_LDLIBS := -lz
include $(BUILD_EXECUTABLE)
endif
# 32-bit static binaries
ifneq ($(TARGET_ARCH_ABI), x86_64)
@@ -121,6 +124,9 @@ include jni/external/busybox/Android.mk
endif
endif
# Precompile
endif
########################
# Externals
########################

View File

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

View File

@@ -28,10 +28,6 @@ static int seperate_vendor = 0;
extern char **environ;
#ifdef MAGISK_DEBUG
static int debug_log_pid, debug_log_fd;
#endif
/******************
* Node structure *
******************/
@@ -149,7 +145,7 @@ static void exec_common_script(const char* stage) {
struct dirent *entry;
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
if (!(dir = opendir(buf)))
if (!(dir = xopendir(buf)))
return;
while ((entry = xreaddir(dir))) {
@@ -194,7 +190,7 @@ static void construct_tree(const char *module, struct node_entry *parent) {
char *parent_path = get_full_path(parent);
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
if (!(dir = opendir(buf)))
if (!(dir = xopendir(buf)))
goto cleanup;
while ((entry = xreaddir(dir))) {
@@ -262,7 +258,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)
@@ -282,7 +278,7 @@ static void clone_skeleton(struct node_entry *node) {
xstat(full_path, &s);
getfilecon(full_path, &con);
LOGI("tmpfs: %s\n", full_path);
mount("tmpfs", full_path, "tmpfs", 0, NULL);
xmount("tmpfs", full_path, "tmpfs", 0, NULL);
chmod(full_path, s.st_mode & 0777);
chown(full_path, s.st_uid, s.st_gid);
setfilecon(full_path, con);
@@ -296,7 +292,7 @@ static void clone_skeleton(struct node_entry *node) {
if (IS_DIR(child))
xmkdir(buf, 0755);
else if (IS_REG(child))
close(open_new(buf));
close(creat(buf, 0644));
// Links will be handled later
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
@@ -399,7 +395,46 @@ static void simple_mount(const char *path) {
* Miscellaneous *
*****************/
static void mount_mirrors() {
// A one time setup
static void daemon_init() {
LOGI("* Creating /sbin overlay");
DIR *dir;
struct dirent *entry;
int root, sbin;
// Setup links under /sbin
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xmkdir("/root", 0755);
chmod("/root", 0755);
root = xopen("/root", O_RDONLY | O_CLOEXEC);
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
dir = xfdopendir(sbin);
while((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
linkat(sbin, entry->d_name, root, entry->d_name, 0);
if (strcmp(entry->d_name, "magisk") == 0)
unlinkat(sbin, entry->d_name, 0);
}
close(sbin);
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
fchmod(sbin, 0755);
fsetfilecon(sbin, "u:object_r:rootfs:s0");
dir = xfdopendir(root);
while((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
xsymlink(buf, buf2);
}
for (int i = 0; applet[i]; ++i) {
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
xsymlink("/root/magisk", buf2);
}
xsymlink(MOUNTPOINT, FAKEPOINT);
close(root);
close(sbin);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
LOGI("* Mounting mirrors");
struct vector mounts;
vec_init(&mounts);
@@ -425,9 +460,7 @@ static void mount_mirrors() {
#else
LOGI("mount: %s\n", MIRRDIR "/system");
#endif
continue;
}
if (strstr(line, " /vendor ")) {
} else if (strstr(line, " /vendor ")) {
seperate_vendor = 1;
sscanf(line, "%s", buf);
xmkdir_p(MIRRDIR "/vendor", 0755);
@@ -437,29 +470,34 @@ static void mount_mirrors() {
#else
LOGI("mount: %s\n", MIRRDIR "/vendor");
#endif
continue;
}
free(line);
}
vec_deep_destroy(&mounts);
vec_destroy(&mounts);
if (!seperate_vendor) {
symlink(MIRRDIR "/system/vendor", MIRRDIR "/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
}
mkdir_p(MIRRDIR "/bin", 0755);
xmkdir_p(MIRRDIR "/bin", 0755);
bind_mount(DATABIN, MIRRDIR "/bin");
}
static void link_busybox() {
mkdir_p(BBPATH, 0755);
LOGI("* Setting up internal busybox");
xmkdir_p(BBPATH, 0755);
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
symlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
}
static int prepare_img() {
// First merge images
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
LOGE("Image merge /data/magisk_merge.img -> " MAINIMG " failed!\n");
return 1;
}
if (access(MAINIMG, F_OK) == -1) {
if (create_img(MAINIMG, 64))
return 1;
@@ -490,7 +528,7 @@ static int prepare_img() {
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
if (access(buf, F_OK) == 0) {
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
exec_command_sync(BBPATH "/rm", "-rf", buf, NULL);
rm_rf(buf);
continue;
}
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
@@ -511,32 +549,35 @@ static int prepare_img() {
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;
LOGI("** post-fs mode running\n");
// ack
write_int(client, 0);
close(client);
// Allow magiskinit to full patch
close(creat(PATCHSTART, 0));
// Uninstall or core only mode
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
goto unblock;
@@ -553,24 +594,14 @@ unblock:
}
void post_fs_data(int client) {
// Error handler
err_handler = unblock_boot_process;
// ack
write_int(client, 0);
close(client);
if (!check_data())
goto unblock;
// Start log monitor
monitor_logs();
#ifdef MAGISK_DEBUG
// Log everything initially
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
xwrite(debug_log_fd, "Boot logs:\n", 11);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", NULL);
#endif
// Start the debug log
start_debug_full_log();
LOGI("** post-fs-data mode running\n");
@@ -587,23 +618,16 @@ void post_fs_data(int client) {
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
if (bin_path) {
exec_command_sync("rm", "-rf", DATABIN, NULL);
exec_command_sync("cp", "-r", bin_path, DATABIN, NULL);
exec_command_sync("rm", "-rf", bin_path, NULL);
exec_command_sync("chmod", "-R", "755", bin_path, NULL);
// Lazy.... use shell blob to match files
exec_command_sync("sh", "-c", "mv /data/magisk/stock_boot* /data", NULL);
rm_rf(DATABIN);
cp_afc(bin_path, DATABIN);
rm_rf(bin_path);
}
// Link busybox
mount_mirrors();
link_busybox();
// Lazy.... use shell blob to match files
exec_command_sync("sh", "-c", "mv -f /data/magisk/stock_*.img.gz /data", NULL);
// Merge images
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
goto unblock;
}
// Initialize
daemon_init();
// uninstaller
if (access(UNINSTALLER, F_OK) == 0) {
@@ -613,7 +637,7 @@ void post_fs_data(int client) {
return;
}
// Trim, mount magisk.img, which will also travel through the modules
// Merge, trim, mount magisk.img, which will also travel through the modules
// After this, it will create the module list
if (prepare_img())
goto core_only; // Mounting fails, we can only do core only stuffs
@@ -665,7 +689,7 @@ void post_fs_data(int client) {
if (access(buf, F_OK) == 0) {
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
unlink(buf2);
symlink(buf, buf2);
xsymlink(buf, buf2);
}
construct_tree(module, sys_root);
}
@@ -703,14 +727,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();
@@ -727,7 +744,9 @@ void late_start(int client) {
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
// Wait till the full patch is done
pthread_join(sepol_patch, NULL);
while (access(PATCHDONE, F_OK) == -1)
usleep(500); /* Wait 0.5ms */
unlink(PATCHDONE);
// Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n");
@@ -768,12 +787,5 @@ core_only:
buf = buf2 = NULL;
vec_deep_destroy(&module_list);
#ifdef MAGISK_DEBUG
// Stop recording the boot logcat after every boot task is done
kill(debug_log_pid, SIGTERM);
waitpid(debug_log_pid, NULL, 0);
// Then start to log Magisk verbosely
xwrite(debug_log_fd, "\nVerbose logs:\n", 15);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", "-s", "Magisk", NULL);
#endif
stop_debug_full_log();
}

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

@@ -0,0 +1,334 @@
/* daemon.c - Magisk Daemon
*
* Start the daemon and wait for requests
* Connect the daemon and send requests through sockets
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <selinux/selinux.h>
#include "magisk.h"
#include "utils.h"
#include "daemon.h"
#include "magiskpolicy.h"
#include "resetprop.h"
pthread_t sepol_patch;
int is_restart = 0;
static void *request_handler(void *args) {
int client = *((int *) args);
free(args);
client_request req = read_int(client);
struct ucred credentials;
get_client_cred(client, &credentials);
switch (req) {
case LAUNCH_MAGISKHIDE:
case STOP_MAGISKHIDE:
case ADD_HIDELIST:
case RM_HIDELIST:
case LS_HIDELIST:
case POST_FS:
case POST_FS_DATA:
case LATE_START:
if (credentials.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
return NULL;
}
default:
break;
}
switch (req) {
case LAUNCH_MAGISKHIDE:
launch_magiskhide(client);
break;
case STOP_MAGISKHIDE:
stop_magiskhide(client);
break;
case ADD_HIDELIST:
add_hide_list(client);
break;
case RM_HIDELIST:
rm_hide_list(client);
break;
case LS_HIDELIST:
ls_hide_list(client);
break;
case SUPERUSER:
su_daemon_receiver(client);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VER_STR);
close(client);
break;
case CHECK_VERSION_CODE:
write_int(client, MAGISK_VER_CODE);
close(client);
break;
case POST_FS:
post_fs(client);
break;
case POST_FS_DATA:
post_fs_data(client);
break;
case LATE_START:
late_start(client);
break;
default:
break;
}
return NULL;
}
/* Setup the address and return socket fd */
static int setup_socket(struct sockaddr_un *sun) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_LOCAL;
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
return fd;
}
static void *start_magisk_hide(void *args) {
launch_magiskhide(-1);
return NULL;
}
void auto_start_magiskhide() {
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
pthread_t thread;
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
pthread_detach(thread);
}
free(hide_prop);
}
void start_daemon() {
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);
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
xlisten(fd, 10);
if ((is_restart = 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();
}
// 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();
// Notifiy init the daemon is started
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
// Loop forever to listen for requests
while(1) {
int *client = xmalloc(sizeof(int));
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
pthread_t thread;
xpthread_create(&thread, NULL, request_handler, client);
// Detach the thread, we will never join it
pthread_detach(thread);
}
}
/* Connect the daemon, and return a socketfd */
int connect_daemon() {
struct sockaddr_un sun;
int fd = setup_socket(&sun);
if (xconnect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
// If we cannot access the daemon, we start a daemon in the child process if possible
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
fprintf(stderr, "No daemon is currently running!\n");
exit(1);
}
if (xfork() == 0) {
LOGD("client: connect fail, try launching new daemon process\n");
close(fd);
start_daemon();
}
do {
// Wait for 10ms
usleep(10);
} while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)));
}
return fd;
}
/*
* Receive a file descriptor from a Unix socket.
* Contributed by @mkasick
*
* Returns the file descriptor on success, or -1 if a file
* descriptor was not actually included in the message
*
* On error the function terminates by calling exit(-1)
*/
int recv_fd(int sockfd) {
// Need to receive data from the message, otherwise don't care about it.
char iovbuf;
struct iovec iov = {
.iov_base = &iovbuf,
.iov_len = 1,
};
char cmsgbuf[CMSG_SPACE(sizeof(int))];
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
xrecvmsg(sockfd, &msg, MSG_WAITALL);
// Was a control message actually sent?
switch (msg.msg_controllen) {
case 0:
// No, so the file descriptor was closed and won't be used.
return -1;
case sizeof(cmsgbuf):
// Yes, grab the file descriptor from it.
break;
default:
goto error;
}
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == NULL ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
error:
LOGE("unable to read fd");
exit(-1);
}
return *(int *)CMSG_DATA(cmsg);
}
/*
* Send a file descriptor through a Unix socket.
* Contributed by @mkasick
*
* On error the function terminates by calling exit(-1)
*
* fd may be -1, in which case the dummy data is sent,
* but no control message with the FD is sent.
*/
void send_fd(int sockfd, int fd) {
// Need to send some data in the message, this will do.
struct iovec iov = {
.iov_base = "",
.iov_len = 1,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
char cmsgbuf[CMSG_SPACE(sizeof(int))];
if (fd != -1) {
// Is the file descriptor actually open?
if (fcntl(fd, F_GETFD) == -1) {
if (errno != EBADF) {
PLOGE("unable to send fd");
}
// It's closed, don't send a control message or sendmsg will EBADF.
} else {
// It's open, send the file descriptor in a control message.
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd;
}
}
xsendmsg(sockfd, &msg, 0);
}
int read_int(int fd) {
int val;
xxread(fd, &val, sizeof(int));
return val;
}
void write_int(int fd, int val) {
if (fd < 0) return;
xwrite(fd, &val, sizeof(int));
}
char* read_string(int fd) {
int len = read_int(fd);
if (len > PATH_MAX || len < 0) {
LOGE("invalid string length %d", len);
exit(1);
}
char* val = xmalloc(sizeof(char) * (len + 1));
xxread(fd, val, len);
val[len] = '\0';
return val;
}
void write_string(int fd, const char* val) {
if (fd < 0) return;
int len = strlen(val);
write_int(fd, len);
xwrite(fd, val, len);
}

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

@@ -0,0 +1,155 @@
/* 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 <limits.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include "magisk.h"
#include "utils.h"
int logcat_events[] = { -1, -1, -1 };
extern int is_restart;
#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(logcat_events) / sizeof(int)); ++i) {
if (logcat_events[i] > 0) {
char *s = strdup(line);
xwrite(logcat_events[i], &s, sizeof(s));
}
}
if (kill(log_pid, 0))
break;
}
// Clear buffer if restart required
exec_command_sync("logcat", "-b", "all", "-c", NULL);
}
// Should never be here, but well...
return NULL;
}
static void *magisk_log_thread(void *args) {
int have_data = 0;
// Temp buffer for logs before we have data access
struct vector logs;
vec_init(&logs);
FILE *log;
int pipefd[2];
if (xpipe2(pipefd, O_CLOEXEC) == -1)
return NULL;
// Register our listener
logcat_events[LOG_EVENT] = pipefd[1];
LOGD("log_monitor: magisk log dumper start");
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
char *ss;
if ((ss = strstr(line, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V')) {
if (!have_data) {
if ((have_data = check_data())) {
// Dump buffered logs to file
if (!is_restart)
rename(LOGFILE, LASTLOG);
log = xfopen(LOGFILE, "a");
setbuf(log, NULL);
char *tmp;
vec_for_each(&logs, tmp) {
fprintf(log, "%s", tmp);
free(tmp);
}
vec_destroy(&logs);
} else {
vec_push_back(&logs, strdup(line));
}
}
if (have_data)
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
logcat_events[DEBUG_EVENT] = pipefd[1];
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
char *ss;
if ((ss = strstr(line, "Magisk")))
fprintf(log, "%s", line);
}
return NULL;
}
/* Start new threads to monitor logcat and dump to logfile */
void monitor_logs() {
pthread_t thread;
// Start log file dumper before monitor
xpthread_create(&thread, NULL, magisk_log_thread, NULL);
pthread_detach(thread);
// Start logcat monitor
xpthread_create(&thread, NULL, logger_thread, NULL);
pthread_detach(thread);
}
void start_debug_full_log() {
#ifdef MAGISK_DEBUG
// Log everything initially
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "threadtime", NULL);
close(debug_log_fd);
#endif
}
void stop_debug_full_log() {
#ifdef MAGISK_DEBUG
// Stop recording the boot logcat after every boot task is done
kill(debug_log_pid, SIGTERM);
waitpid(debug_log_pid, NULL, 0);
// Start debug thread
start_debug_log();
#endif
}
void start_debug_log() {
#ifdef MAGISK_DEBUG
pthread_t thread;
// Start debug thread
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
pthread_detach(thread);
#endif
}

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include "utils.h"
#include "magisk.h"
@@ -12,10 +13,10 @@
char *argv0;
char *applet[] =
{ "su", "resetprop", "magiskpolicy", "supolicy", "magiskhide", NULL };
{ "su", "resetprop", "magiskhide", NULL };
int (*applet_main[]) (int, char *[]) =
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
{ su_client_main, resetprop_main, magiskhide_main, NULL };
int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX];
@@ -32,10 +33,6 @@ int create_links(const char *bin, const char *path) {
return ret;
}
// Global error hander function
// Should be changed each thread/process
__thread void (*err_handler)(void);
static void usage() {
fprintf(stderr,
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
@@ -48,17 +45,19 @@ static void usage() {
" -v print running daemon version\n"
" -V print running daemon version code\n"
" --list list all availible applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
" --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"
" --[boot stage] start boot stage service\n"
" --[init service] start init service\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
" --restorecon fix selinux context on Magisk files and folders\n"
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
"\n"
"Supported boot stages:\n"
" post-fs, post-fs-data, service\n"
"Supported init services:\n"
" daemon, post-fs, post-fs-data, service\n"
"\n"
"Supported applets:\n"
, argv0, argv0);
@@ -71,12 +70,7 @@ static void usage() {
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);
@@ -146,6 +140,16 @@ int main(int argc, char *argv[]) {
} 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) {
// Start daemon, this process won't return
start_daemon();
} else if (strcmp(argv[1], "--post-fs") == 0) {
int fd = connect_daemon();
write_int(fd, POST_FS);
@@ -162,16 +166,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;
}

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

@@ -0,0 +1,441 @@
/* 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)
*/
#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 "magisk.h"
#include "utils.h"
#include "magiskpolicy.h"
#include "magiskrc.h"
#include "dump.h"
// #define VLOG(fmt, ...) printf(fmt, __VA_ARGS__) /* Enable to debug */
#define VLOG(fmt, ...)
struct cmdline {
int skip_initramfs;
char slot[3];
};
struct device {
dev_t major;
dev_t minor;
char devname[32];
char partname[32];
char path[64];
};
extern policydb_t *policydb;
static void parse_cmdline(struct cmdline *cmd) {
// cleanup
cmd->skip_initramfs = 0;
cmd->slot[0] = '\0';
char *tok;
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");
tok = strtok(cmdline, " ");
while (tok != 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;
}
tok = strtok(NULL, " ");
}
}
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 *patch_init_rc(char *data, uint32_t *size) {
int injected = 0;
char *new_data = malloc(*size + 23);
char *old_data = data;
uint32_t pos = 0;
for (char *tok = strsep(&old_data, "\n"); tok; tok = strsep(&old_data, "\n")) {
if (!injected && strncmp(tok, "import", 6) == 0) {
if (strstr(tok, "init.magisk.rc")) {
injected = 1;
} else {
strcpy(new_data + pos, "import /init.magisk.rc\n");
pos += 23;
injected = 1;
}
} else if (strstr(tok, "selinux.reload_policy")) {
continue;
}
// Copy the line
strcpy(new_data + pos, tok);
pos += strlen(tok);
new_data[pos++] = '\n';
}
*size = pos;
return new_data;
}
static void patch_ramdisk() {
void *addr;
size_t size;
mmap_rw("/init", &addr, &size);
for (int i = 0; i < size; ++i) {
if (memcmp(addr + i, "/system/etc/selinux/plat_sepolicy.cil", 37) == 0) {
memcpy(addr + i, "/system/etc/selinux/plat_sepolicy.xxx", 37);
break;
}
}
munmap(addr, size);
mmap_rw("/init.rc", &addr, &size);
uint32_t new_size = size;
void *init_rc = patch_init_rc(addr, &new_size);
munmap(addr, size);
int fd = open("/init.rc", O_WRONLY | O_TRUNC | O_CLOEXEC);
write(fd, init_rc, new_size);
close(fd);
free(init_rc);
}
static int strend(const char *s1, const char *s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
return strcmp(s1 + l1 - l2, s2);
}
static 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_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("/system/etc/selinux/plat_sepolicy.cil", &addr, &size);
cil_add_file(db, "/system/etc/selinux/plat_sepolicy.cil", addr, size);
munmap(addr, size);
// mapping
char plat[10];
int fd = open("/vendor/etc/selinux/plat_sepolicy_vers.txt", O_RDONLY | O_CLOEXEC);
if (fd > 0) {
plat[read(fd, plat, sizeof(plat)) - 1] = '\0';
snprintf(path, sizeof(path), "/system/etc/selinux/mapping/%s.cil", plat);
mmap_ro(path, &addr, &size);
cil_add_file(db, path, addr, size);
munmap(addr, size);
close(fd);
}
dir = opendir("/vendor/etc/selinux");
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".cil") == 0) {
snprintf(path, sizeof(path), "/vendor/etc/selinux/%s", entry->d_name);
mmap_ro(path, &addr, &size);
cil_add_file(db, path, addr, size);
munmap(addr, size);
}
}
closedir(dir);
cil_compile(db);
cil_build_policydb(db, &pdb);
cil_db_destroy(&db);
policydb = &pdb->p;
return 0;
}
static int verify_precompiled() {
DIR *dir;
struct dirent *entry;
int fd;
char sys_sha[70], ven_sha[70];
dir = opendir("/vendor/etc/selinux");
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
ven_sha[read(fd, ven_sha, sizeof(ven_sha))] = '\0';
close(fd);
break;
}
}
closedir(dir);
dir = opendir("/system/etc/selinux");
while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
sys_sha[read(fd, sys_sha, sizeof(sys_sha))] = '\0';
close(fd);
break;
}
}
closedir(dir);
return strcmp(sys_sha, ven_sha);
}
static void patch_sepolicy() {
if (access("/sepolicy", R_OK) == 0) {
load_policydb("/sepolicy");
} else if (access("/vendor/etc/selinux/precompiled_sepolicy", R_OK) == 0
&& verify_precompiled() == 0) {
load_policydb("/vendor/etc/selinux/precompiled_sepolicy");
} else if (access("/system/etc/selinux/plat_sepolicy.cil", R_OK) == 0) {
compile_cil();
}
sepol_med_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;
}
int main(int argc, char *argv[]) {
umask(0);
if (strcmp(basename(argv[0]), "magiskpolicy") == 0 || strcmp(basename(argv[0]), "supolicy") == 0)
return magiskpolicy_main(argc, argv);
if (argc > 1 && (strcmp(argv[1], "magiskpolicy") == 0 || strcmp(argv[1], "supolicy") == 0))
return magiskpolicy_main(argc - 1, argv + 1);
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);
}
// 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");
symlink("/root/magiskinit", "/overlay/root/magiskpolicy");
symlink("/root/magiskinit", "/overlay/root/supolicy");
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 {
// Revert original init binary
unlink("/init");
link("/.backup/init", "/init");
}
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
mv_dir(overlay, root);
// Clean up
rmdir("/overlay");
close(overlay);
close(root);
patch_ramdisk();
patch_sepolicy();
umount("/vendor");
if (fork() == 0) {
// Fork a new process for full patch
setsid();
sepol_allow("su", ALL, ALL, ALL);
while (access(PATCHSTART, W_OK) == -1)
usleep(500); /* Wait 0.5ms */
unlink(PATCHSTART);
dump_policydb(SELINUX_LOAD);
close(open(PATCHDONE, O_RDONLY | O_CREAT, 0));
destroy_policydb();
return 0;
}
// Finally, give control back!
execv("/init", argv);
}

View File

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

View File

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

View File

@@ -1,148 +0,0 @@
/* socket_trans.c - Functions to transfer data through socket
*/
#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"
/*
* Receive a file descriptor from a Unix socket.
* Contributed by @mkasick
*
* Returns the file descriptor on success, or -1 if a file
* descriptor was not actually included in the message
*
* On error the function terminates by calling exit(-1)
*/
int recv_fd(int sockfd) {
// Need to receive data from the message, otherwise don't care about it.
char iovbuf;
struct iovec iov = {
.iov_base = &iovbuf,
.iov_len = 1,
};
char cmsgbuf[CMSG_SPACE(sizeof(int))];
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
xrecvmsg(sockfd, &msg, MSG_WAITALL);
// Was a control message actually sent?
switch (msg.msg_controllen) {
case 0:
// No, so the file descriptor was closed and won't be used.
return -1;
case sizeof(cmsgbuf):
// Yes, grab the file descriptor from it.
break;
default:
goto error;
}
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == NULL ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
error:
LOGE("unable to read fd");
exit(-1);
}
return *(int *)CMSG_DATA(cmsg);
}
/*
* Send a file descriptor through a Unix socket.
* Contributed by @mkasick
*
* On error the function terminates by calling exit(-1)
*
* fd may be -1, in which case the dummy data is sent,
* but no control message with the FD is sent.
*/
void send_fd(int sockfd, int fd) {
// Need to send some data in the message, this will do.
struct iovec iov = {
.iov_base = "",
.iov_len = 1,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
char cmsgbuf[CMSG_SPACE(sizeof(int))];
if (fd != -1) {
// Is the file descriptor actually open?
if (fcntl(fd, F_GETFD) == -1) {
if (errno != EBADF) {
PLOGE("unable to send fd");
}
// It's closed, don't send a control message or sendmsg will EBADF.
} else {
// It's open, send the file descriptor in a control message.
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd;
}
}
xsendmsg(sockfd, &msg, 0);
}
int read_int(int fd) {
int val;
xxread(fd, &val, sizeof(int));
return val;
}
void write_int(int fd, int val) {
if (fd < 0) return;
xwrite(fd, &val, sizeof(int));
}
char* read_string(int fd) {
int len = read_int(fd);
if (len > PATH_MAX || len < 0) {
LOGE("invalid string length %d", len);
exit(1);
}
char* val = xmalloc(sizeof(char) * (len + 1));
xxread(fd, val, len);
val[len] = '\0';
return val;
}
void write_string(int fd, const char* val) {
if (fd < 0) return;
int len = strlen(val);
write_int(fd, len);
xwrite(fd, val, len);
}

View File

@@ -1,5 +1,4 @@
LOCAL_PATH:= $(call my-dir)
EXTERNAL := $(LOCAL_PATH)
# libsqlite.so (stub)
include $(CLEAR_VARS)
@@ -14,7 +13,7 @@ LOCAL_C_INCLUDES := $(LIBSELINUX)
LOCAL_SRC_FILES := stubs/selinux_stub.c
include $(BUILD_SHARED_LIBRARY)
# libfdt
# libfdt.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt
LOCAL_C_INCLUDES := $(LIBFDT)
@@ -30,11 +29,129 @@ LOCAL_SRC_FILES := \
dtc/libfdt/fdt_wip.c
include $(BUILD_STATIC_LIBRARY)
# libsepol, static library
include $(SELINUX_PATH)/libsepol/Android.mk
# 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)
# Compression libraries for magiskboot
include $(COMPRESS_LIB)/zlib/Android.mk
include $(COMPRESS_LIB)/xz/src/liblzma/Android.mk
include $(COMPRESS_LIB)/lz4/lib/Android.mk
include $(COMPRESS_LIB)/bzip2/Android.mk
# 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)/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
jni/external/bzip2 vendored Submodule

Submodule jni/external/bzip2 added at 67d818584d

1
jni/external/lz4 vendored Submodule

Submodule jni/external/lz4 added at c10863b98e

1
jni/external/xz vendored Submodule

Submodule jni/external/xz added at 3d566cd519

498
jni/external/xz_config/config.h vendored Normal file
View File

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

View File

@@ -7,6 +7,7 @@
#include <pthread.h>
extern pthread_t sepol_patch;
extern int is_restart;
// Commands require connecting to daemon
typedef enum {
@@ -38,11 +39,9 @@ typedef enum {
// daemon.c
void start_daemon(int client);
void start_daemon();
int connect_daemon();
// socket_trans.c
void auto_start_magiskhide();
int recv_fd(int sockfd);
void send_fd(int sockfd, int fd);
int read_int(int fd);
@@ -50,10 +49,6 @@ void write_int(int fd, int val);
char* read_string(int fd);
void write_string(int fd, const char* val);
// log_monitor.c
void monitor_logs();
/***************
* Boot Stages *
***************/
@@ -61,6 +56,7 @@ void monitor_logs();
void post_fs(int client);
void post_fs_data(int client);
void late_start(int client);
void fix_filecon();
/**************
* MagiskHide *

View File

@@ -8,6 +8,9 @@
#include <errno.h>
#include <stdlib.h>
#define str(a) #a
#define xstr(a) str(a)
#ifdef IS_DAEMON
#include <pthread.h>
@@ -15,24 +18,28 @@
#define LOG_TAG "Magisk"
// Global handler for PLOGE
extern __thread void (*err_handler)(void);
// Common error handlers
static inline void exit_proc() { exit(1); }
static inline void exit_thread() { pthread_exit(NULL); }
static inline void do_nothing() {}
#ifdef MAGISK_DEBUG
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#else
#define LOGD(...) {}
#define LOGD(...)
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) { LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler(); }
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
enum {
HIDE_EVENT,
LOG_EVENT,
DEBUG_EVENT
};
extern int logcat_events[];
void monitor_logs();
void start_debug_full_log();
void stop_debug_full_log();
void start_debug_log();
#else // IS_DAEMON

View File

@@ -6,9 +6,6 @@
#include "logging.h"
#define str(a) #a
#define xstr(a) str(a)
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
#define REQUESTOR_DAEMON_PATH "\0MAGISK"
@@ -20,19 +17,24 @@
#define LASTLOG "/cache/last_magisk.log"
#define DEBUG_LOG "/data/magisk_debug.log"
#define UNBLOCKFILE "/dev/.magisk.unblock"
#define PATCHSTART "/dev/.magisk.patch.start"
#define PATCHDONE "/dev/.magisk.patch.done"
#define DISABLEFILE "/cache/.disable_magisk"
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
#define MOUNTPOINT "/magisk"
#define CACHEMOUNT "/cache/magisk_mount"
#define MAGISKTMP "/sbin/.core"
#define MIRRDIR MAGISKTMP "/mirror"
#define BBPATH MAGISKTMP "/busybox"
#define MOUNTPOINT MAGISKTMP "/img"
#define FAKEPOINT "/magisk"
#define COREDIR MOUNTPOINT "/.core"
#define HOSTSFILE COREDIR "/hosts"
#define HIDELIST COREDIR "/hidelist"
#define MAINIMG "/data/magisk.img"
#define DATABIN "/data/magisk"
#define MANAGERAPK DATABIN "/magisk.apk"
#define MAGISKTMP "/dev/magisk"
#define MIRRDIR MAGISKTMP "/mirror"
#define BBPATH MAGISKTMP "/bin"
#define CACHEMOUNT "/cache/magisk_mount"
#define MANAGERAPK MIRRDIR "/bin/magisk.apk"
#define MAGISKRC "/init.magisk.rc"
#define SELINUX_PATH "/sys/fs/selinux/"
#define SELINUX_ENFORCE SELINUX_PATH "enforce"

46
jni/include/magiskrc.h Normal file
View File

@@ -0,0 +1,46 @@
const char magiskrc[] =
// Triggers
"on post-fs\n"
" start logd\n"
" start magisk_daemon\n"
" wait /dev/.magisk.unblock 5\n"
" rm /dev/.magisk.unblock\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"
"\n"
"service magisk_pfs /sbin/magisk --post-fs\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
"\n"
"service magisk_pfsd /sbin/magisk --post-fs-data\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
"\n"
"service magisk_service /sbin/magisk --service\n"
" class late_start\n"
" user root\n"
" seclabel u:r:su:s0\n"
" oneshot\n"
;

View File

@@ -12,9 +12,11 @@ int prop_exist(const char *name);
int setprop(const char *name, const char *value);
int setprop2(const char *name, const char *value, const int trigger);
char *getprop(const char *name);
int deleteprop(const char *name, const int trigger);
char *getprop2(const char *name, int persist);
int deleteprop(const char *name);
int deleteprop2(const char *name, const int persist);
int read_prop_file(const char* filename, const int trigger);
void getprop_all(void (*cbk)(const char *name));
void getprop_all(void (*callback)(const char*, const char*));
#ifdef __cplusplus
}

View File

@@ -7,7 +7,6 @@
#include <stdio.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -18,8 +17,6 @@
#define UID_SYSTEM (get_system_uid())
#define UID_RADIO (get_radio_uid())
extern int quit_signals[];
// xwrap.c
FILE *xfopen(const char *pathname, const char *mode);
@@ -28,12 +25,14 @@ FILE *xfdopen(int fd, const char *mode);
#define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
int xopen2(const char *pathname, int flags);
int xopen3(const char *pathname, int flags, mode_t mode);
int xopenat(int dirfd, const char *pathname, int flags);
ssize_t xwrite(int fd, const void *buf, size_t count);
ssize_t xread(int fd, void *buf, size_t count);
ssize_t xxread(int fd, void *buf, size_t count);
int xpipe2(int pipefd[2], int flags);
int xsetns(int fd, int nstype);
DIR *xopendir(const char *name);
DIR *xfdopendir(int fd);
struct dirent *xreaddir(DIR *dirp);
pid_t xsetsid();
int xsocket(int domain, int type, int protocol);
@@ -53,22 +52,26 @@ int xstat(const char *pathname, struct stat *buf);
int xlstat(const char *pathname, struct stat *buf);
int xdup2(int oldfd, int newfd);
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
int xsymlink(const char *target, const char *linkpath);
int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
int xumount(const char *target);
int xumount2(const char *target, int flags);
int xchmod(const char *pathname, mode_t mode);
int xrename(const char *oldpath, const char *newpath);
int xmkdir(const char *pathname, mode_t mode);
int xmkdir_p(const char *pathname, mode_t mode);
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
int xmkdir_p(const char *pathname, mode_t mode);
pid_t xfork();
// misc.c
extern int quit_signals[];
unsigned get_shell_uid();
unsigned get_system_uid();
unsigned get_radio_uid();
@@ -82,14 +85,43 @@ void unlock_blocks();
void setup_sighandlers(void (*handler)(int));
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...);
int exec_command_sync(char *const argv0, ...);
int mkdir_p(const char *pathname, mode_t mode);
int bind_mount(const char *from, const char *to);
int open_new(const char *filename);
int cp_afc(const char *source, const char *target);
void fclone_attr(const int sourcefd, const int targetfd);
void clone_attr(const char *source, const char *target);
void get_client_cred(int fd, struct ucred *cred);
int switch_mnt_ns(int pid);
int fork_dont_care();
// 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 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);
void mmap_ro(const char *filename, void **buf, size_t *size);
void mmap_rw(const char *filename, void **buf, size_t *size);
void 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

View File

@@ -1,102 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include "utils.h"
void mmap_ro(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDONLY);
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
*buf = xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
}
void mmap_rw(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDWR);
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
*buf = xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
}
void write_zero(int fd, size_t size) {
size_t pos = lseek(fd, 0, SEEK_CUR);
ftruncate(fd, pos + size);
lseek(fd, pos + size, SEEK_SET);
}
void mem_align(size_t *pos, size_t align) {
size_t mask = align - 1;
if (*pos & mask) {
*pos += align - (*pos & mask);
}
}
void file_align(int fd, size_t align, int out) {
size_t pos = lseek(fd, 0, SEEK_CUR);
size_t mask = align - 1;
size_t off;
if (pos & mask) {
off = align - (pos & mask);
if (out) {
write_zero(fd, off);
} else {
lseek(fd, pos + off, SEEK_SET);
}
}
}
int open_new(const char *filename) {
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
void *patch_init_rc(char *data, uint32_t *size) {
int injected = 0;
char *new_data = xmalloc(*size + 23);
char *old_data = data;
uint32_t pos = 0;
for (char *tok = strsep(&old_data, "\n"); tok; tok = strsep(&old_data, "\n")) {
if (!injected && strncmp(tok, "import", 6) == 0) {
if (strstr(tok, "init.magisk.rc")) {
injected = 1;
} else {
fprintf(stderr, "Inject [import /init.magisk.rc] to [init.rc]\n");
strcpy(new_data + pos, "import /init.magisk.rc\n");
pos += 23;
injected = 1;
}
} else if (strstr(tok, "selinux.reload_policy")) {
continue;
}
// Copy the line
strcpy(new_data + pos, tok);
pos += strlen(tok);
new_data[pos++] = '\n';
}
*size = pos;
return new_data;
}
int check_verity_pattern(const char *s) {
int pos = 0;
if (s[0] == ',') ++pos;
if (strncmp(s + pos, "verify", 6) != 0) return -1;
pos += 6;
if (s[pos] == '=') {
while (s[pos] != '\0' && s[pos] != ' ' && s[pos] != '\n' && s[pos] != ',') ++pos;
}
return pos;
}
int check_encryption_pattern(const char *s) {
const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe", NULL };
for (int i = 0 ; encrypt_list[i]; ++i) {
int len = strlen(encrypt_list[i]);
if (strncmp(s, encrypt_list[i], len) == 0)
return len;
}
return -1;
}

View File

@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "bootimg.h"
@@ -8,7 +9,7 @@
#include "logging.h"
static void dump(void *buf, size_t size, const char *filename) {
int fd = open_new(filename);
int fd = creat(filename, 0644);
xwrite(fd, buf, size);
close(fd);
}
@@ -30,7 +31,7 @@ static void print_hdr(const boot_img_hdr *hdr) {
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr->kernel_size, hdr->kernel_addr);
fprintf(stderr, "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, "DTB [%d] @ 0x%08x\n", hdr->dt_size, hdr->tags_addr);
fprintf(stderr, "EXTRA [%d] @ 0x%08x\n", hdr->extra_size, hdr->tags_addr);
fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size);
if (hdr->os_version != 0) {
int a,b,c,y,m = 0;
@@ -88,26 +89,24 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
mem_align(&pos, boot->hdr.page_size);
}
if (boot->hdr.dt_size) {
boot->dtb = base + pos;
pos += boot->hdr.dt_size;
if (boot->hdr.extra_size) {
boot->extra = base + pos;
pos += boot->hdr.extra_size;
mem_align(&pos, boot->hdr.page_size);
}
if (pos < size) {
boot->extra = base + pos;
boot->tail = base + pos;
boot->tail_size = end - base - pos;
}
// Search for dtb in kernel if not found
if (boot->hdr.dt_size == 0) {
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
boot->flags |= APPEND_DTB;
boot->dtb = boot->kernel + i;
boot->hdr.dt_size = boot->hdr.kernel_size - i;
boot->hdr.kernel_size = i;
fprintf(stderr, "APPEND_DTB [%d]\n", boot->hdr.dt_size);
}
// Search for dtb in kernel
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
boot->dtb = boot->kernel + i;
boot->dt_size = boot->hdr.kernel_size - i;
boot->hdr.kernel_size = i;
fprintf(stderr, "DTB [%d]\n", boot->dt_size);
}
}
@@ -129,7 +128,7 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
memcpy(&boot->mtk_ramdisk_hdr, boot->ramdisk, sizeof(mtk_hdr));
boot->ramdisk += 512;
boot->hdr.ramdisk_size -= 512;
boot->ramdisk_type = check_type(boot->ramdisk + 512);
boot->ramdisk_type = check_type(boot->ramdisk);
}
char fmt[16];
@@ -146,6 +145,7 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
}
}
LOGE("No boot image magic found!\n");
return 1;
}
void unpack(const char* image) {
@@ -160,22 +160,27 @@ void unpack(const char* image) {
int ret = parse_img(orig, size, &boot);
// Dump kernel
if (boot.kernel_type == UNKNOWN) {
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE);
} else {
fd = open_new(KERNEL_FILE);
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 (boot.ramdisk_type == UNKNOWN) {
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw");
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
} else {
fd = open_new(RAMDISK_FILE);
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) {
@@ -183,9 +188,9 @@ void unpack(const char* image) {
dump(boot.second, boot.hdr.second_size, SECOND_FILE);
}
if (boot.hdr.dt_size) {
// Dump dtb
dump(boot.dtb, boot.hdr.dt_size, DTB_FILE);
if (boot.hdr.extra_size) {
// Dump extra
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
}
munmap(orig, size);
@@ -210,7 +215,7 @@ void repack(const char* orig_image, const char* out_image) {
fprintf(stderr, "Repack to boot image: [%s]\n\n", out_image);
// Create new image
int fd = open_new(out_image);
int fd = creat(out_image, 0644);
// Skip a page for header
write_zero(fd, boot.hdr.page_size);
@@ -220,18 +225,18 @@ void repack(const char* orig_image, const char* out_image) {
mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
write_zero(fd, 512);
}
if (boot.kernel_type == UNKNOWN) {
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
} else {
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);
}
if (boot.flags & APPEND_DTB) {
// Restore dtb
if (boot.dt_size && access(DTB_FILE, R_OK) == 0) {
boot.hdr.kernel_size += restore(DTB_FILE, fd);
boot.hdr.dt_size = 0;
}
file_align(fd, boot.hdr.page_size, 1);
@@ -270,18 +275,17 @@ void repack(const char* orig_image, const char* out_image) {
file_align(fd, boot.hdr.page_size, 1);
}
// Restore dtb
if (boot.hdr.dt_size && access(DTB_FILE, R_OK) == 0) {
printf("Here\n");
boot.hdr.dt_size = restore(DTB_FILE, fd);
// 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 extra info, currently only for LG Bump and Samsung SEANDROIDENFORCE
if (boot.extra) {
if (memcmp(boot.extra, "SEANDROIDENFORCE", 16) == 0 ||
memcmp(boot.extra, LG_BUMP_MAGIC, 16) == 0 ) {
restore_buf(fd, boot.extra, 16);
// 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);
}
}

View File

@@ -44,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":
@@ -74,13 +74,13 @@ struct boot_img_hdr
** +-----------------+
** | 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)
@@ -103,16 +103,18 @@ typedef struct mtk_hdr {
// Flags
#define MTK_KERNEL 0x1
#define MTK_RAMDISK 0x2
#define APPEND_DTB 0x4
typedef struct boot_img {
boot_img_hdr hdr;
void *kernel;
void *dtb;
uint32_t dt_size;
void *ramdisk;
void *second;
void *dtb;
void *extra;
int flags;
void *tail;
uint32_t tail_size;
uint32_t flags;
file_t kernel_type, ramdisk_type;
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
} boot_img;

View File

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

View File

@@ -1,5 +1,6 @@
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "magiskboot.h"
#include "cpio.h"
@@ -91,7 +92,7 @@ static void parse_cpio(const char *filename, struct vector *v) {
static void dump_cpio(const char *filename, struct vector *v) {
fprintf(stderr, "\nDump cpio: [%s]\n\n", filename);
int fd = open_new(filename);
int fd = creat(filename, 0644);
unsigned inode = 300000;
char header[111];
// Sort by name
@@ -186,7 +187,7 @@ static void cpio_test(struct vector *v) {
int ret = STOCK_BOOT;
cpio_entry *f;
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
const char *MAGISK_LIST[] = { "init.magisk.rc", "overlay/init.magisk.rc", NULL };
const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL };
vec_for_each(v, f) {
for (int i = 0; OTHER_LIST[i]; ++i) {
if (strcmp(f->filename, OTHER_LIST[i]) == 0) {
@@ -204,46 +205,66 @@ static void cpio_test(struct vector *v) {
exit(ret);
}
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;
}
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;
}
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
cpio_entry *f;
int skip, write;
vec_for_each(v, f) {
if (strcmp(f->filename, "init.rc") == 0) {
void *new_data = patch_init_rc(f->data, &f->filesize);
free(f->data);
f->data = new_data;
} else {
if (!keepverity) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_verity_pattern(f->data + read)) > 0) {
fprintf(stderr, "Remove pattern [%.*s] in [%s]\n", skip, f->data + read, f->filename);
read += skip;
}
f->data[write] = f->data[read];
if (!keepverity) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_verity_pattern(f->data + read)) > 0) {
fprintf(stderr, "Remove pattern [%.*s] in [%s]\n", skip, f->data + read, f->filename);
read += skip;
}
f->filesize = write;
} else if (strcmp(f->filename, "verity_key") == 0) {
fprintf(stderr, "Remove [verity_key]\n");
f->remove = 1;
f->data[write] = f->data[read];
}
f->filesize = write;
} else if (strcmp(f->filename, "verity_key") == 0) {
fprintf(stderr, "Remove [verity_key]\n");
f->remove = 1;
}
if (!keepforceencrypt) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_encryption_pattern(f->data + read)) > 0) {
// assert(skip > 11)!
fprintf(stderr, "Replace pattern [%.*s] with [encryptable] in [%s]\n", skip, f->data + read, f->filename);
memcpy(f->data + write, "encryptable", 11);
write += 11;
read += skip;
}
f->data[write] = f->data[read];
}
if (!keepforceencrypt) {
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
write = 0;
for (int read = 0; read < f->filesize; ++write, ++read) {
if ((skip = check_encryption_pattern(f->data + read)) > 0) {
// assert(skip > 11)!
fprintf(stderr, "Replace pattern [%.*s] with [encryptable] in [%s]\n", skip, f->data + read, f->filename);
memcpy(f->data + write, "encryptable", 11);
write += 11;
read += skip;
}
f->filesize = write;
f->data[write] = f->data[read];
}
f->filesize = write;
}
}
}
@@ -254,7 +275,7 @@ static void cpio_extract(const char *entry, const char *filename, struct vector
vec_for_each(v, f) {
if (strcmp(f->filename, entry) == 0 && S_ISREG(f->mode)) {
fprintf(stderr, "Extracting [%s] to [%s]\n\n", entry, filename);
int fd = open_new(filename);
int fd = creat(filename, 0644);
xwrite(fd, f->data, f->filesize);
fchmod(fd, f->mode);
fchown(fd, f->uid, f->gid);
@@ -265,19 +286,35 @@ static void cpio_extract(const char *entry, const char *filename, struct vector
LOGE("Cannot find the file entry [%s]\n", entry);
}
static void cpio_backup(const char *orig, struct vector *v) {
static void cpio_backup(const char *orig, const char *sha1, struct vector *v) {
struct vector o_body, *o = &o_body, bak;
cpio_entry *m, *n, *dir, *rem;
cpio_entry *m, *n, *rem, *cksm;
char buf[PATH_MAX];
int res, doBak;
dir = xcalloc(sizeof(*dir), 1);
rem = xcalloc(sizeof(*rem), 1);
if (sha1) cksm = xcalloc(sizeof(*cksm), 1);
vec_init(o);
vec_init(&bak);
// First push back the directory and the rmlist
vec_push_back(&bak, dir);
m = xcalloc(sizeof(*m), 1);
m->filename = strdup(".backup");
m->namesize = strlen(m->filename) + 1;
m->mode = S_IFDIR;
vec_push_back(&bak, m);
m = xcalloc(sizeof(*m), 1);
m->filename = strdup(".backup/.magisk");
m->namesize = strlen(m->filename) + 1;
m->mode = S_IFREG;
vec_push_back(&bak, m);
rem = xcalloc(sizeof(*rem), 1);
rem->filename = strdup(".backup/.rmlist");
rem->namesize = strlen(rem->filename) + 1;
rem->mode = S_IFREG;
vec_push_back(&bak, rem);
if (sha1) vec_push_back(&bak, cksm);
parse_cpio(orig, o);
// Remove possible backups in original ramdisk
cpio_rm(1, ".backup", o);
@@ -287,13 +324,14 @@ static void cpio_backup(const char *orig, struct vector *v) {
vec_sort(v, cpio_cmp);
vec_sort(o, cpio_cmp);
// Init the directory and rmlist
dir->filename = strdup(".backup");
dir->namesize = strlen(dir->filename) + 1;
dir->mode = S_IFDIR;
rem->filename = strdup(".backup/.rmlist");
rem->namesize = strlen(rem->filename) + 1;
rem->mode = S_IFREG;
if (sha1) {
fprintf(stderr, "Save SHA1: [%s] -> [.backup/.sha1]\n", sha1);
cksm->filename = strdup(".backup/.sha1");
cksm->namesize = strlen(cksm->filename) + 1;
cksm->mode = S_IFREG;
cksm->data = strdup(sha1);
cksm->filesize = strlen(sha1) + 1;
}
// Start comparing
size_t i = 0, j = 0;
@@ -349,12 +387,8 @@ static void cpio_backup(const char *orig, struct vector *v) {
vec_push_back(v, m);
}
// Don't include if empty
if (rem->filesize == 0) {
if (rem->filesize == 0)
rem->remove = 1;
if (bak.size == 2)
dir->remove = 1;
}
// Cleanup
cpio_vec_destroy(o);
@@ -367,21 +401,24 @@ static int cpio_restore(struct vector *v) {
if (strstr(f->filename, ".backup") != NULL) {
ret = 0;
f->remove = 1;
if (strcmp(f->filename, ".backup") == 0) continue;
if (strcmp(f->filename, ".backup/.rmlist") == 0) {
for (int pos = 0; pos < f->filesize; pos += strlen(f->data + pos) + 1)
cpio_rm(0, f->data + pos, v);
if (f->filename[7] == '\0') continue;
if (f->filename[8] == '.') {
if (strcmp(f->filename, ".backup/.rmlist") == 0) {
for (int pos = 0; pos < f->filesize; pos += strlen(f->data + pos) + 1)
cpio_rm(0, f->data + pos, v);
}
continue;
} else {
n = xcalloc(sizeof(*n), 1);
memcpy(n, f, sizeof(*f));
n->namesize -= 8;
n->filename = strdup(f->filename + 8);
n->data = f->data;
f->data = NULL;
n->remove = 0;
fprintf(stderr, "Restore [%s] -> [%s]\n", f->filename, n->filename);
cpio_vec_insert(v, n);
}
n = xcalloc(sizeof(*n), 1);
memcpy(n, f, sizeof(*f));
n->namesize -= 8;
n->filename = strdup(f->filename + 8);
n->data = f->data;
f->data = NULL;
n->remove = 0;
fprintf(stderr, "Restoring [%s] -> [%s]\n", f->filename, n->filename);
cpio_vec_insert(v, n);
}
}
// Some known stuff we can remove
@@ -406,6 +443,8 @@ static void cpio_stocksha1(struct vector *v) {
return;
}
}
} else if (strcmp(f->filename, ".backup/.sha1") == 0) {
printf("%s\n", f->data);
}
}
}
@@ -443,7 +482,7 @@ int cpio_commands(const char *command, int argc, char *argv[]) {
cmd = RESTORE;
} else if (strcmp(command, "stocksha1") == 0) {
cmd = STOCKSHA1;
} else if (argc == 1 && strcmp(command, "backup") == 0) {
} else if (argc >= 1 && strcmp(command, "backup") == 0) {
cmd = BACKUP;
} else if (argc > 0 && strcmp(command, "rm") == 0) {
cmd = RM;
@@ -479,7 +518,7 @@ int cpio_commands(const char *command, int argc, char *argv[]) {
cpio_stocksha1(&v);
return 0;
case BACKUP:
cpio_backup(argv[0], &v);
cpio_backup(argv[0], argc > 1 ? argv[1] : NULL, &v);
case RM:
cpio_rm(recursive, argv[0], &v);
break;

View File

@@ -5,16 +5,29 @@
#include "magiskboot.h"
#include "utils.h"
/* Left here for debugging */
extern int check_verity_pattern(const char *s);
// static void print_subnode(const void *fdt, int parent, int depth) {
// int node;
// fdt_for_each_subnode(node, fdt, parent) {
// for (int i = 0; i < depth; ++i) printf(" ");
// printf("%d: %s\n", node, fdt_get_name(fdt, node, NULL));
// print_subnode(fdt, node, depth + 1);
// }
// }
static 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;
@@ -28,13 +41,35 @@ static int find_fstab(const void *fdt, int parent) {
return -1;
}
void dtb_patch(const char *file) {
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\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\n", file);
mmap_rw(file, &dtb, &size);
if (patch)
mmap_rw(file, &dtb, &size);
else
mmap_ro(file, &dtb, &size);
// Loop through all the dtbs
int dtb_num = 0, patched = 0;
int dtb_num = 0, found = 0;
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
fdt = dtb + i;
@@ -48,10 +83,15 @@ void dtb_patch(const char *file) {
char *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
for (int i = 0; i < value_size; ++i) {
if ((skip = check_verity_pattern(value + i)) > 0) {
fprintf(stderr, "Remove pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
memcpy(value + i, value + i + skip, value_size - i - skip);
memset(value + value_size - skip, '\0', skip);
patched = 1;
if (patch) {
fprintf(stderr, "Remove pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
memcpy(value + i, value + i + skip, value_size - i - skip);
memset(value + value_size - skip, '\0', skip);
} else {
fprintf(stderr, "Found pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
i += skip - 1;
}
found = 1;
}
}
}
@@ -60,7 +100,18 @@ void dtb_patch(const char *file) {
}
fprintf(stderr, "\n");
munmap(dtb, size);
exit(!patched);
exit(!found);
}
int dtb_commands(const char *cmd, int argc, char *argv[]) {
if (argc == 0) return 1;
if (strcmp(cmd, "dump") == 0)
dtb_dump(argv[0]);
else if (strcmp(cmd, "patch") == 0)
dtb_patch(argv[0], 1);
else if (strcmp(cmd, "test") == 0)
dtb_patch(argv[0], 0);
return 0;
}

View File

@@ -24,7 +24,7 @@ void hexpatch(const char *image, const char *from, const char *to) {
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);
memset(file + i, 0, patternsize);

View File

@@ -3,26 +3,25 @@
#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"
#define str(a) #a
#define xstr(a) str(a)
// Main entries
void unpack(const char *image);
void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to);
int parse_img(void *orig, size_t size, boot_img *boot);
int cpio_commands(const char *command, int argc, char *argv[]);
int cpio_commands(const char *cmd, int argc, char *argv[]);
void comp_file(const char *method, const char *from, const char *to);
void decomp_file(char *from, const char *to);
void dtb_patch(const char *file);
int dtb_commands(const char *cmd, int argc, char *argv[]);
// Compressions
size_t gzip(int mode, int fd, const void *buf, size_t size);
@@ -33,15 +32,4 @@ size_t lz4_legacy(int mode, int fd, const void *buf, size_t size);
long long comp(file_t type, int to, const void *from, size_t size);
long long decomp(file_t type, int to, const void *from, size_t size);
// Utils
extern void mmap_ro(const char *filename, void **buf, size_t *size);
extern void mmap_rw(const char *filename, void **buf, size_t *size);
extern void write_zero(int fd, size_t size);
extern void mem_align(size_t *pos, size_t align);
extern void file_align(int fd, size_t align, int out);
extern int open_new(const char *filename);
extern void *patch_init_rc(char *data, uint32_t *size);
extern int check_verity_pattern(const char *s);
extern int check_encryption_pattern(const char *s);
#endif

View File

@@ -5,6 +5,7 @@
#include <sys/mman.h>
#include "magiskboot.h"
#include "utils.h"
#include "sha1.h"
/********************
@@ -17,8 +18,8 @@ static void usage(char *arg0) {
"\n"
"Supported actions:\n"
" --unpack <bootimg>\n"
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb) into the\n"
" current directory\n"
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into\n"
" the current directory\n"
"\n"
" --repack <origbootimg> [outbootimg]\n"
" Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"
@@ -43,22 +44,29 @@ static void usage(char *arg0) {
" Move <from-entry> to <to-entry>\n"
" -extract <entry> <outfile>\n"
" Extract <entry> to <outfile>\n"
" -test \n"
" Return value: 0/stock 1/Magisk 2/other (e.g. phh, SuperSU)\n"
" -test\n"
" Return value: 0/stock 1/Magisk 2/other (phh, SuperSU, Xposed)\n"
" -patch <KEEPVERITY> <KEEPFORCEENCRYPT>\n"
" Patch cpio for Magisk. KEEP**** are true/false values\n"
" -backup <origcpio>\n"
" -backup <origcpio> [SHA1]\n"
" Create ramdisk backups into <incpio> from <origcpio>\n"
" SHA1 of stock boot image is optional\n"
" -restore\n"
" Restore ramdisk from ramdisk backup within <incpio>\n"
" -stocksha1\n"
" Get stock boot SHA1 recorded within <incpio>\n"
"\n"
" --dtb-patch <dtb>\n"
" Search for fstab in <dtb> and remove verity checks\n"
" --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"
" -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)
@@ -67,7 +75,9 @@ static void usage(char *arg0) {
"\n"
"\n"
" --decompress <infile> [outfile]\n"
" Detect method and decompress <infile>, optionally to [outfile]\n Supported methods: ");
" 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,
@@ -94,6 +104,7 @@ int main(int argc, char *argv[]) {
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);
@@ -113,8 +124,6 @@ int main(int argc, char *argv[]) {
repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT);
} else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) {
decomp_file(argv[2], argc > 3 ? argv[3] : NULL);
} else if (argc > 2 && strcmp(argv[1], "--dtb-patch") == 0) {
dtb_patch(argv[2]);
} else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) {
char *method;
method = strchr(argv[1], '=');
@@ -124,11 +133,15 @@ int main(int argc, char *argv[]) {
} else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) {
hexpatch(argv[2], argv[3], argv[4]);
} else if (argc > 2 && strncmp(argv[1], "--cpio", 6) == 0) {
char *command;
command = strchr(argv[1] + 2, '-');
if (command == NULL) usage(argv[0]);
else ++command;
if (cpio_commands(command, argc - 2, argv + 2)) usage(argv[0]);
char *cmd = argv[1] + 6;
if (*cmd == '\0') usage(argv[0]);
else ++cmd;
if (cpio_commands(cmd, 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]);
}

View File

@@ -18,6 +18,8 @@ typedef enum {
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"

View File

@@ -58,9 +58,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 +69,6 @@ void clean_magisk_props() {
getprop_all(rm_magisk_prop);
}
void relink_sbin() {
struct stat st;
if (stat("/sbin_orig", &st) == -1 && errno == ENOENT) {
// Re-link all binaries and bind mount
DIR *dir;
struct dirent *entry;
char from[PATH_MAX], to[PATH_MAX];
LOGI("hide_utils: Re-linking /sbin\n");
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
xrename("/sbin", "/sbin_orig");
xmkdir("/sbin", 0755);
xchmod("/sbin", 0755);
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
xmkdir("/dev/sbin_bind", 0755);
xchmod("/dev/sbin_bind", 0755);
dir = xopendir("/sbin_orig");
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, "..") == 0)
continue;
snprintf(from, sizeof(from), "/sbin_orig/%s", entry->d_name);
if (entry->d_type == DT_LNK)
xreadlink(from, from, sizeof(from));
snprintf(to, sizeof(to), "/dev/sbin_bind/%s", entry->d_name);
symlink(from, to);
lsetfilecon(to, "u:object_r:rootfs:s0");
}
closedir(dir);
xmount("/dev/sbin_bind", "/sbin", NULL, MS_BIND, NULL);
}
}
int add_list(char *proc) {
if (!hideEnabled) {
free(proc);
@@ -223,7 +187,6 @@ int destroy_list() {
}
void add_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client);
// ack
write_int(client, add_list(proc));
@@ -231,7 +194,6 @@ void add_hide_list(int client) {
}
void rm_hide_list(int client) {
err_handler = do_nothing;
char *proc = read_string(client);
// ack
write_int(client, rm_list(proc));
@@ -239,7 +201,6 @@ void rm_hide_list(int client) {
}
void ls_hide_list(int client) {
err_handler = do_nothing;
if (!hideEnabled) {
write_int(client, HIDE_NOT_ENABLED);
return;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -56,249 +56,352 @@
#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) printf(__VA_ARGS__); }
#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 bool is_legal_property_name(const char *name, size_t namelen) {
static int check_legal_property_name(const char *name) {
int namelen = strlen(name);
if (namelen < 1) return false;
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
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] == '.') return false;
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;
return false;
}
/* 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 true;
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 [options] [args...]\n"
"\n"
"Options:\n"
" -v show verbose output\n"
" -n only modify property in memory\n"
"\n"
"%s NAME VALUE set property entry NAME with VALUE\n"
"%s --file FILE load props from FILE\n"
"%s --delete NAME remove prop entry NAME\n"
"\n"
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, arg0, arg0, arg0);
return 1;
, arg0);
return 1;
}
static int init_resetprop() {
if (__system_properties_init2()) {
PRINT_E("resetprop: Initialize error\n");
return -1;
}
return 0;
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;
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);
strcpy((char *) cookie, value);
}
char *getprop(const char *name) {
return getprop2(name, 0);
}
// Get prop by name, return string (should free manually!)
char *getprop(const char *name) {
if (init_resetprop()) return NULL;
const prop_info *pi = __system_property_find2(name);
if (pi == NULL) {
PRINT_D("resetprop: prop [%s] does not exist\n", name);
return NULL;
}
char value[PROP_VALUE_MAX];
__system_property_read_callback2(pi, read_prop_info, value);
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
return strdup(value);
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);
}
static void (*cb)(const char *);
struct wrapper {
void (*func)(const char *, const char *);
};
static void run_actual_cb(void* cookie, const char *name, const char *value, uint32_t serial) {
cb(name);
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, run_actual_cb, NULL);
__system_property_read_callback2(pi, cb_wrapper, cookie);
}
void getprop_all(void (*cbk)(const char *name)) {
if (init_resetprop()) return;
cb = cbk;
__system_property_foreach2(prop_foreach_cb, NULL);
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);
return setprop2(name, value, 1);
}
int setprop2(const char *name, const char *value, const int trigger) {
if (init_resetprop()) return -1;
int ret;
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)) deleteprop(name, trigger);
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));
}
}
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");
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");
if (ret)
PRINT_E("resetprop: setprop error\n");
return ret;
return ret;
}
int deleteprop(const char *name, const int trigger) {
if (init_resetprop()) return -1;
PRINT_D("resetprop: deleteprop [%s]\n", name);
if (trigger && strstr(name, "persist.")) {
char buffer[PATH_MAX];
snprintf(buffer, sizeof(buffer), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
unlink(buffer);
}
return __system_property_del(name);
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;
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;
int del = 0, file = 0, trigger = 1;
--argc;
++argv;
int exp_arg = 2;
char *name, *value, *filename;
// 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;
}
if (argc < 3) {
return usage(argv[0]);
}
for (int i = 1; i < argc; ++i) {
if (!strcmp("-v", argv[i])) {
verbose = 1;
} else if (!strcmp("-n", argv[i])) {
trigger = 0;
} else if (!strcmp("--file", argv[i])) {
file = 1;
exp_arg = 1;
} else if (!strcmp("--delete", argv[i])) {
del = 1;
exp_arg = 1;
} else {
if (i + exp_arg > argc) {
return usage(argv[0]);
}
if (file) {
filename = argv[i];
break;
} else {
if(!is_legal_property_name(argv[i], strlen(argv[i]))) {
PRINT_E("Illegal property name: [%s]\n", argv[i]);
return 1;
}
name = argv[i];
if (exp_arg > 1) {
if (strlen(argv[i + 1]) >= PROP_VALUE_MAX) {
PRINT_E("Value too long: [%s]\n", argv[i + 1]);
return 1;
}
value = argv[i + 1];
}
break;
}
}
}
if (file) {
return read_prop_file(filename, trigger);
} else if (del) {
return deleteprop(name, trigger);
} else {
return setprop2(name, value, trigger);
}
return 0;
switch (argc) {
case 0:
print_all_props(persist);
return 0;
case 1:
prop = getprop2(argv[0], persist);
if (prop == NULL) return 1;
printf("%s\n", prop);
free(prop);
return 0;
case 2:
return setprop2(argv[0], argv[1], trigger);
default:
usage(argv0);
return 1;
}
}

View File

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

View File

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

2
jni/su

Submodule jni/su updated: c912c192e0...a9d966dc7a

405
jni/utils/file.c Normal file
View File

@@ -0,0 +1,405 @@
/* file.c - Contains all files related utilities
*/
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/sendfile.h>
#include <sys/mman.h>
#include <linux/fs.h>
#ifndef NO_SELINUX
#include <selinux/selinux.h>
#endif
#include "utils.h"
char **excl_list = (char *[]) { NULL };
static int is_excl(const char *name) {
for (int i = 0; excl_list[i]; ++i) {
if (strcmp(name, excl_list[i]) == 0)
return 1;
}
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 rm_rf(const char *path) {
int fd = xopen(path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return;
frm_rf(fd);
close(fd);
rmdir(path);
}
void frm_rf(int dirfd) {
struct dirent *entry;
int newfd;
DIR *dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (is_excl(entry->d_name))
continue;
switch (entry->d_type) {
case DT_DIR:
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
frm_rf(newfd);
close(newfd);
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
break;
default:
unlinkat(dirfd, entry->d_name, 0);
break;
}
}
}
/* 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;
char *con = "";
#ifndef NO_SELINUX
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) {
#ifndef NO_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;
#ifndef NO_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) {
#ifndef NO_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);
}
#ifndef NO_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 // NO_SELINUX
static void _mmap(int rw, const char *filename, void **buf, size_t *size) {
struct stat st;
stat(filename, &st);
int fd = xopen(filename, rw ? O_RDWR : O_RDONLY);
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);
}
void mmap_ro(const char *filename, void **buf, size_t *size) {
_mmap(0, filename, buf, size);
}
void mmap_rw(const char *filename, void **buf, size_t *size) {
_mmap(1, filename, buf, size);
}
void full_read(int fd, void **buf, size_t *size) {
size_t cap = 1 << 20;
uint8_t tmp[1 << 20];
*buf = xmalloc(cap);
ssize_t read;
*size = 0;
while (1) {
read = xread(fd, tmp, sizeof(tmp));
if (read <= 0)
break;
if (*size + read > cap) {
cap *= 2;
*buf = realloc(*buf, cap);
}
memcpy(*buf + *size, tmp, read);
*size += read;
}
}
void write_zero(int fd, size_t size) {
size_t pos = lseek(fd, 0, SEEK_CUR);
ftruncate(fd, pos + size);
lseek(fd, pos + size, SEEK_SET);
}
void mem_align(size_t *pos, size_t align) {
size_t mask = align - 1;
if (*pos & mask) {
*pos += align - (*pos & mask);
}
}
void file_align(int fd, size_t align, int out) {
size_t pos = lseek(fd, 0, SEEK_CUR);
size_t mask = align - 1;
size_t off;
if (pos & mask) {
off = align - (pos & mask);
if (out) {
write_zero(fd, off);
} else {
lseek(fd, pos + off, SEEK_SET);
}
}
}

View File

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

View File

@@ -6,19 +6,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <errno.h>
#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <selinux/selinux.h>
#include "logging.h"
#include "utils.h"
@@ -145,12 +141,12 @@ static void proc_name_filter(int pid) {
char buf[64];
int fd;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
if ((fd = open(buf, O_RDONLY)) == -1)
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return;
if (fdgets(buf, sizeof(buf), fd) == 0) {
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
close(fd);
if ((fd = open(buf, O_RDONLY)) == -1)
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
return;
fdgets(buf, sizeof(buf), fd);
}
@@ -167,32 +163,25 @@ void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
ps(proc_name_filter);
}
#define DEV_BLOCK "/dev/block"
void unlock_blocks() {
char path[PATH_MAX];
DIR *dir;
struct dirent *entry;
int fd, OFF = 0;
int fd, dev, OFF = 0;
if (!(dir = xopendir(DEV_BLOCK)))
if ((dev = xopen("/dev/block", O_RDONLY | O_CLOEXEC)) < 0)
return;
dir = xfdopendir(dev);
while((entry = readdir(dir))) {
if (entry->d_type == DT_BLK &&
strstr(entry->d_name, "ram") == NULL &&
strstr(entry->d_name, "loop") == NULL) {
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)) {
@@ -243,7 +232,7 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
envp = environ;
}
int pid = fork();
int pid = xfork();
if (pid != 0) {
if (fd && *fd < 0) {
// Give the read end and close write end
@@ -255,9 +244,6 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
return pid;
}
// Don't return to the daemon if anything goes wrong
err_handler = exit_proc;
if (fd) {
xdup2(writeEnd, STDOUT_FILENO);
if (err) xdup2(writeEnd, STDERR_FILENO);
@@ -288,27 +274,6 @@ int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char
return pid;
}
int mkdir_p(const char *pathname, mode_t mode) {
char *path = strdup(pathname), *p;
errno = 0;
for (p = path + 1; *p; ++p) {
if (*p == '/') {
*p = '\0';
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
*p = '/';
}
}
if (mkdir(path, mode) == -1) {
if (errno != EEXIST)
return -1;
}
free(path);
return 0;
}
int bind_mount(const char *from, const char *to) {
int ret = xmount(from, to, NULL, MS_BIND, NULL);
#ifdef MAGISK_DEBUG
@@ -319,83 +284,6 @@ int bind_mount(const char *from, const char *to) {
return ret;
}
int open_new(const char *filename) {
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
int cp_afc(const char *source, const char *target) {
struct stat buf;
xlstat(source, &buf);
if (S_ISDIR(buf.st_mode)) {
DIR *dir;
struct dirent *entry;
char *s_path, *t_path;
if (!(dir = xopendir(source)))
return 1;
s_path = xmalloc(PATH_MAX);
t_path = xmalloc(PATH_MAX);
mkdir_p(target, 0755);
clone_attr(source, target);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name);
snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name);
cp_afc(s_path, t_path);
}
free(s_path);
free(t_path);
closedir(dir);
} else{
unlink(target);
if (S_ISREG(buf.st_mode)) {
int sfd, tfd;
sfd = xopen(source, O_RDONLY);
tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC);
xsendfile(tfd, sfd, NULL, buf.st_size);
fclone_attr(sfd, tfd);
close(sfd);
close(tfd);
} else if (S_ISLNK(buf.st_mode)) {
char buffer[PATH_MAX];
xreadlink(source, buffer, sizeof(buffer));
xsymlink(buffer, target);
clone_attr(source, target);
} else {
return 1;
}
}
return 0;
}
void clone_attr(const char *source, const char *target) {
struct stat buf;
lstat(target, &buf);
chmod(target, buf.st_mode & 0777);
chown(target, buf.st_uid, buf.st_gid);
char *con;
lgetfilecon(source, &con);
lsetfilecon(target, con);
free(con);
}
void fclone_attr(const int sourcefd, const int targetfd) {
struct stat buf;
fstat(sourcefd, &buf);
fchmod(targetfd, buf.st_mode & 0777);
fchown(targetfd, buf.st_uid, buf.st_gid);
char *con;
fgetfilecon(sourcefd, &con);
fsetfilecon(targetfd, con);
free(con);
}
void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred);
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
@@ -415,3 +303,14 @@ 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;
}

View File

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

View File

@@ -43,7 +43,7 @@ main() {
find_boot_image
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
ui_print "- Found Boot Image: $BOOTIMAGE"
ui_print "- Found boot image: $BOOTIMAGE"
SOURCEDMODE=true
cd $MAGISKBIN
@@ -51,13 +51,20 @@ main() {
# Source the boot patcher
. $MAGISKBIN/boot_patch.sh "$BOOTIMAGE"
flash_boot_image new-boot.img "$BOOTIMAGE"
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
mv stock_boot* /data
is_mounted /data && mv stock_boot* /data
fi
flash_boot_image new-boot.img $BOOTIMAGE
rm -f new-boot.img
patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
is_mounted /data && mv stock_dtbo* /data
fi
cd /
recovery_cleanup

View File

@@ -11,18 +11,15 @@
# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter.
# (this file) The script will use binaries and files in its same directory
# to complete the patching process
# magisk binary The main binary for all Magisk operations.
# It is also used to patch the sepolicy in the ramdisk.
# monogisk binary The monolithic binary to replace /init
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk
# , and patch the ramdisk for Magisk support
# init.magisk.rc script A new line will be added to init.rc to import this script.
# All magisk entrypoints are defined here
# chromeos folder This folder should store all the utilities and keys to sign
# (optional) a chromeos device, used in the tablet Pixel C
#
# If the script is not running as root, then the input boot image should be a stock image
# or have a backup included in ramdisk internally, since we cannot access the stock boot
# image placed under /data we've created when previously installing
# image placed under /data we've created when previously installed
#
##########################################################################################
##########################################################################################
@@ -47,36 +44,10 @@ basename_wrap() {
echo ${1##*/}
}
# --cpio-add <incpio> <mode> <entry> <infile>
cpio_add() {
./magiskboot --cpio-add ramdisk.cpio $1 $2 $3
}
# --cpio-extract <incpio> <entry> <outfile>
cpio_extract() {
./magiskboot --cpio-extract ramdisk.cpio $1 $2
}
# --cpio-mkdir <incpio> <mode> <entry>
cpio_mkdir() {
./magiskboot --cpio-mkdir ramdisk.cpio $1 $2
}
##########################################################################################
# Initialization
##########################################################################################
BOOTIMAGE="$1"
[ -e "$BOOTIMAGE" ] || (echo "$BOOTIMAGE does not exist!" && exit 1)
# Presets
[ -z $KEEPVERITY ] && KEEPVERITY=false
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
# Detect whether running as root
id | grep "uid=0" >/dev/null 2>&1 && ROOT=true || ROOT=false
if [ -z $SOURCEDMODE ]; then
# Switch to the location of the script file
cd "`dirname_wrap "${BASH_SOURCE:-$0}"`"
@@ -86,8 +57,19 @@ if [ -z $SOURCEDMODE ]; then
mount_partitions
fi
BOOTIMAGE="$1"
[ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!"
# Presets
[ -z $KEEPVERITY ] && KEEPVERITY=false
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
chmod -R 755 .
# Extract magisk if doesn't exist
[ -e magisk ] || ./magiskinit -x magisk magisk
##########################################################################################
# Unpack
##########################################################################################
@@ -104,6 +86,7 @@ case $? in
abort "! Unable to unpack boot image"
;;
2 )
ui_print "- ChromeOS boot image detected"
CHROMEOS=true
;;
3 )
@@ -127,9 +110,8 @@ case $? in
ui_print "- Stock boot image detected!"
ui_print "- Backing up stock boot image"
SHA1=`./magiskboot --sha1 "$BOOTIMAGE" 2>/dev/null`
STOCKDUMP=stock_boot_${SHA1}.img
dd if="$BOOTIMAGE" of=$STOCKDUMP
./magiskboot --compress $STOCKDUMP
STOCKDUMP=stock_boot_${SHA1}.img.gz
./magiskboot --compress "$BOOTIMAGE" $STOCKDUMP
cp -af ramdisk.cpio ramdisk.cpio.orig
;;
1 ) # Magisk patched
@@ -145,11 +127,11 @@ case $? in
# Restore failed
ui_print "! Cannot restore from internal backup"
# If we are root and SHA1 known, we try to find the stock backup
if $ROOT && [ ! -z $SHA1 ]; then
STOCKDUMP=/data/stock_boot_${SHA1}.img
if [ -f ${STOCKDUMP}.gz ]; then
if [ ! -z $SHA1 ]; then
STOCKDUMP=/data/stock_boot_${SHA1}.img.gz
if [ -f $STOCKDUMP ]; then
ui_print "- Stock boot image backup found"
./magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img
./magiskboot --decompress $STOCKDUMP stock_boot.img
./magiskboot --unpack stock_boot.img
rm -f stock_boot.img
OK=true
@@ -174,45 +156,21 @@ esac
ui_print "- Patching ramdisk"
if [ ! -z $SHA1 ]; then
cp init.magisk.rc init.magisk.rc.bak
echo "# STOCKSHA1=$SHA1" >> init.magisk.rc
fi
if $SKIP_INITRAMFS; then
cpio_add 750 init magiskinit
cpio_mkdir 000 overlay
cpio_add 750 overlay/init.magisk.rc init.magisk.rc
cpio_mkdir 750 overlay/sbin
cpio_add 755 overlay/sbin/magisk magisk
else
./magiskboot --cpio-patch ramdisk.cpio $KEEPVERITY $KEEPFORCEENCRYPT
cpio_extract sepolicy sepolicy
./magisk magiskpolicy --load sepolicy --save sepolicy --minimal
cpio_add 644 sepolicy sepolicy
rm -f sepolicy
cpio_add 750 init.magisk.rc init.magisk.rc
cpio_add 755 sbin/magisk magisk
fi
mv init.magisk.rc.bak init.magisk.rc 2>/dev/null
./magiskboot --cpio-add ramdisk.cpio 750 init magiskinit
./magiskboot --cpio-patch ramdisk.cpio $KEEPVERITY $KEEPFORCEENCRYPT
# Create ramdisk backups
./magiskboot --cpio-backup ramdisk.cpio ramdisk.cpio.orig
if ! $KEEPVERITY && [ -f dtb ]; then
./magiskboot --dtb-patch dtb && ui_print "- Patching fstab in dtb to remove dm-verity"
fi
./magiskboot --cpio-backup ramdisk.cpio ramdisk.cpio.orig $SHA1
rm -f ramdisk.cpio.orig
##########################################################################################
# Repack and flash
# Binary patches
##########################################################################################
# Hexpatches
if ! $KEEPVERITY && [ -f dtb ]; then
./magiskboot --dtb-patch dtb && ui_print "- Patching fstab in dtb to remove dm-verity"
fi
# Remove Samsung RKP in stock kernel
./magiskboot --hexpatch kernel \
@@ -220,10 +178,14 @@ rm -f ramdisk.cpio.orig
A1020054011440B93FA00F7140020054010840B93FA00F71E0010054001840B91FA00F7181010054
# skip_initramfs -> want_initramfs
$SKIP_INITRAMFS && ./magiskboot --hexpatch kernel \
./magiskboot --hexpatch kernel \
736B69705F696E697472616D6673 \
77616E745F696E697472616D6673
##########################################################################################
# Repack and flash
##########################################################################################
ui_print "- Repacking boot image"
./magiskboot --repack "$BOOTIMAGE" || abort "! Unable to repack boot image!"

View File

@@ -22,6 +22,7 @@ TMPDIR=/dev/tmp
INSTALLER=$TMPDIR/install
COMMONDIR=$INSTALLER/common
APK=$COMMONDIR/magisk.apk
CHROMEDIR=$INSTALLER/chromeos
COREDIR=/magisk/.core
@@ -78,18 +79,12 @@ ui_print "- Constructing environment"
is_mounted /data && MAGISKBIN=/data/magisk || MAGISKBIN=/cache/data_bin
if $BOOTMODE; then
# Cleanup binary mirrors
umount -l /dev/magisk/mirror/bin 2>/dev/null
rm -rf /dev/magisk/mirror/bin 2>/dev/null
fi
# Save our stock boot image dump before removing it
mv /data/magisk/stock_boot* /data 2>/dev/null
# Copy required files
rm -rf $MAGISKBIN 2>/dev/null
mkdir -p $MAGISKBIN
rm -rf $MAGISKBIN/* 2>/dev/null
mkdir -p $MAGISKBIN 2>/dev/null
cp -af $BINDIR/. $COMMONDIR/. $CHROMEDIR $TMPDIR/bin/busybox $MAGISKBIN
chmod -R 755 $MAGISKBIN
@@ -107,9 +102,12 @@ $BOOTMODE || recovery_actions
# Boot patching
##########################################################################################
find_boot_image
[ -z $BOOTIMAGE ] && find_boot_image
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
ui_print "- Found Boot Image: $BOOTIMAGE"
ui_print "- Found boot image: $BOOTIMAGE"
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
$BOOTSIGNED && ui_print "- Signed boot image detected"
SOURCEDMODE=true
cd $MAGISKBIN
@@ -117,14 +115,21 @@ cd $MAGISKBIN
# Source the boot patcher
. $COMMONDIR/boot_patch.sh "$BOOTIMAGE"
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
mv stock_boot* /data
fi
flash_boot_image new-boot.img "$BOOTIMAGE"
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
is_mounted /data && mv stock_boot* /data
fi
patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
is_mounted /data && mv stock_dtbo* /data
fi
cd /
# Cleanups
$BOOTMODE || recovery_cleanup

View File

@@ -1,36 +0,0 @@
# Triggers
on post-fs
start logd
start magisk_pfs
wait /dev/.magisk.unblock 20
on post-fs-data
rm /dev/.magisk.unblock
load_persist_props
start magisk_pfsd
wait /dev/.magisk.unblock 60
on property:magisk.restart_pfsd=1
trigger post-fs-data
# Services
# launch post-fs script
service magisk_pfs /sbin/magisk --post-fs
user root
seclabel u:r:su:s0
oneshot
# launch post-fs-data script
service magisk_pfsd /sbin/magisk --post-fs-data
user root
seclabel u:r:su:s0
oneshot
# launch late_start script
service magisk_service /sbin/magisk --service
class late_start
user root
seclabel u:r:su:s0
oneshot

View File

@@ -79,17 +79,16 @@ case $? in
ui_print "- Magisk patched image detected!"
# Find SHA1 of stock boot image
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio-stocksha1 ramdisk.cpio 2>/dev/null`
[ ! -z $SHA1 ] && STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
if [ -f "$STOCKBOOT" ]; then
ui_print "- Boot image backup found!"
else
OK=false
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true
if ! $OK; then
ui_print "! Boot image backup unavailable"
ui_print "- Restoring ramdisk with internal backup"
./magiskboot --cpio-restore ramdisk.cpio
./magiskboot --repack $BOOTIMAGE
# Sign chromeos boot
$CHROMEOS && sign_chromeos
STOCKBOOT=new-boot.img
flash_boot_image new-boot.img "$BOOTIMAGE"
fi
;;
2 ) # Other patched
@@ -98,13 +97,11 @@ case $? in
;;
esac
flash_boot_image $STOCKBOOT "$BOOTIMAGE"
cd /
ui_print "- Removing Magisk files"
rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh \
/data/app/com.topjohnwu.magisk* /data/user*/*/com.topjohnwu.magisk 2>/dev/null
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/app/com.topjohnwu.magisk* \
/data/user*/*/magisk.db /data/user*/*/com.topjohnwu.magisk 2>/dev/null
$BOOTMODE && reboot

View File

@@ -11,7 +11,7 @@ INSTALLER=$TMPDIR/install; BBDIR=$TMPDIR/bin
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox
$INDEP || rm -rf $TMPDIR 2>/dev/null;
mkdir -p $BBDIR 2>/dev/null
touch $EXBIN $BBBIN; chmod 755 $EXBIN $BBBIN
touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN
echo -ne $EX_ARM > $EXBIN
if $EXBIN --test 2>/dev/null; then
echo $BB_ARM | $EXBIN > $BBBIN

View File

@@ -13,6 +13,9 @@ SCRIPT_VERSION=$MAGISK_VER_CODE
# Default location, will override if needed
MAGISKBIN=/data/magisk
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.topjohnwu.magisk.utils.BootSigner"
BOOTSIGNED=false
get_outfd() {
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
if [ "$?" -eq "0" ]; then
@@ -32,17 +35,16 @@ get_outfd() {
}
ui_print() {
if $BOOTMODE; then
echo "$1"
else
echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
fi
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
}
mount_partitions() {
# Check A/B slot
SLOT=`getprop ro.boot.slot_suffix`
if [ -z $SLOT ]; then
SLOT=_`getprop ro.boot.slot`
[ $SLOT = "_" ] && SLOT=
fi
[ -z $SLOT ] || ui_print "- A/B partition detected, current slot: $SLOT"
ui_print "- Mounting /system, /vendor"
is_mounted /system || [ -f /system/build.prop ] || mount -o ro /system 2>/dev/null
@@ -98,15 +100,14 @@ resolve_link() {
}
find_boot_image() {
if [ -z "$BOOTIMAGE" ]; then
if [ ! -z $SLOT ]; then
BOOTIMAGE=`find /dev/block -iname boot$SLOT | head -n 1` 2>/dev/null
else
for BLOCK in boot_a kern-a android_boot kernel boot lnx bootimg; do
BOOTIMAGE=`find /dev/block -iname $BLOCK | head -n 1` 2>/dev/null
[ ! -z $BOOTIMAGE ] && break
done
fi
BOOTIMAGE=
if [ ! -z $SLOT ]; then
BOOTIMAGE=`find /dev/block -iname boot$SLOT | head -n 1` 2>/dev/null
else
for BLOCK in boot_a kern-a android_boot kernel boot lnx bootimg; do
BOOTIMAGE=`find /dev/block -iname $BLOCK | head -n 1` 2>/dev/null
[ ! -z $BOOTIMAGE ] && break
done
fi
# Recovery fallback
if [ -z "$BOOTIMAGE" ]; then
@@ -121,8 +122,7 @@ find_boot_image() {
migrate_boot_backup() {
# Update the broken boot backup
if [ -f /data/stock_boot_.img.gz ]; then
$MAGISKBIN/magiskboot --decompress /data/stock_boot_.img.gz
mv /data/stock_boot_.img /data/stock_boot.img
$MAGISKBIN/magiskboot --decompress /data/stock_boot_.img.gz /data/stock_boot.img
fi
# Update our previous backup to new format if exists
if [ -f /data/stock_boot.img ]; then
@@ -137,26 +137,69 @@ migrate_boot_backup() {
flash_boot_image() {
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
case "$1" in
*.gz) COMMAND="gzip -d < \"$1\"";;
*) COMMAND="cat \"$1\"";;
*.gz) COMMAND="gzip -d < '$1'";;
*) COMMAND="cat '$1'";;
esac
$BOOTSIGNED && SIGNCOM="$BOOTSIGNER -sign" || SIGNCOM="cat -"
case "$2" in
/dev/block/*)
ui_print "- Flashing new boot image"
eval $COMMAND | cat - /dev/zero | dd of="$2" bs=4096 >/dev/null 2>&1
eval $COMMAND | eval $SIGNCOM | cat - /dev/zero 2>/dev/null | dd of="$2" bs=4096 2>/dev/null
;;
*)
ui_print "- Storing new boot image"
eval $COMMAND | dd of="$2" bs=4096 >/dev/null 2>&1
eval $COMMAND | eval $SIGNCOM | dd of="$2" bs=4096 2>/dev/null
;;
esac
}
sign_chromeos() {
echo > empty
find_dtbo_image() {
DTBOIMAGE=`find /dev/block -iname dtbo$SLOT | head -n 1` 2>/dev/null
[ ! -z $DTBOIMAGE ] && DTBOIMAGE=`resolve_link $DTBOIMAGE`
}
patch_dtbo_image() {
find_dtbo_image
if [ ! -z $DTBOIMAGE ]; then
ui_print "- Found dtbo image: $DTBOIMAGE"
if $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
ui_print "- Backing up stock dtbo image"
$MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
ui_print "- Patching fstab in dtbo to remove avb-verity"
$MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE
return 0
fi
fi
return 1
}
restore_imgs() {
STOCKBOOT=/data/stock_boot_${1}.img.gz
STOCKDTBO=/data/stock_dtbo.img.gz
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
find_dtbo_image
if [ ! -z "$DTBOIMAGE" -a -f "$STOCKDTBO" ]; then
ui_print "- Restoring stock dtbo image"
gzip -d < $STOCKDTBO | dd of=$DTBOIMAGE
fi
BOOTSIGNED=false
find_boot_image
if [ ! -z "$BOOTIMAGE" -a -f "$STOCKBOOT" ]; then
ui_print "- Restoring stock boot image"
gzip -d < $STOCKBOOT | cat - /dev/zero 2>/dev/null | dd of="$BOOTIMAGE" bs=4096 2>/dev/null
return 0
fi
return 1
}
sign_chromeos() {
ui_print "- Signing ChromeOS boot image"
echo > empty
./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
--keyblock ./chromeos/kernel.keyblock --signprivate ./chromeos/kernel_data_key.vbprivk \
--version 1 --vmlinuz new-boot.img --config empty --arch arm --bootloader empty --flags 0x1
@@ -166,11 +209,8 @@ sign_chromeos() {
}
is_mounted() {
if [ ! -z "$2" ]; then
cat /proc/mounts | grep $1 | grep $2, >/dev/null
else
cat /proc/mounts | grep $1 >/dev/null
fi
TARGET="`resolve_link $1`"
cat /proc/mounts | grep " $TARGET " >/dev/null
return $?
}
@@ -214,11 +254,11 @@ api_level_arch_detect() {
}
boot_actions() {
if [ ! -d /dev/magisk/mirror/bin ]; then
mkdir -p /dev/magisk/mirror/bin
mount -o bind $MAGISKBIN /dev/magisk/mirror/bin
if [ ! -d /sbin/.core/mirror/bin ]; then
mkdir -p /sbin/.core/mirror/bin
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
fi
MAGISKBIN=/dev/magisk/mirror/bin
MAGISKBIN=/sbin/.core/mirror/bin
}
recovery_actions() {

View File

@@ -1,7 +0,0 @@
*.iml
.gradle
/local.properties
.idea/
/build
*.hprof
app/.externalNativeBuild/

View File

@@ -1,36 +0,0 @@
group 'com.topjohnwu'
version '1.0.0'
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
jar {
manifest {
attributes 'Main-Class': 'com.topjohnwu.ZipSigner'
}
}
shadowJar {
classifier = null
version = null
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
}
}
repositories {
mavenCentral()
}
dependencies {
compile 'org.bouncycastle:bcprov-jdk15on:1.57'
compile 'org.bouncycastle:bcpkix-jdk15on:1.57'
}

View File

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

View File

@@ -1,172 +0,0 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# 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
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
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" -a "$nonstop" = "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
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@@ -1,84 +0,0 @@
@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
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
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 Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
: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

View File

@@ -1,4 +0,0 @@
rootProject.name = 'zipsigner'
include 'apksigner'
rootProject.name = 'zipsigner'

View File

@@ -1,567 +0,0 @@
package com.topjohnwu;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
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.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
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 ZipSigner {
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
private static Provider sBouncyCastleProvider;
// 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;
/**
* 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) + ")$");
private static X509Certificate readPublicKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(input);
} finally {
input.close();
}
}
/** Read a PKCS#8 format private key. */
private static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
byte[] buffer = new byte[4096];
int size = input.read(buffer);
byte[] bytes = Arrays.copyOf(buffer, size);
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
/*
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
* OID and use that to construct a KeyFactory.
*/
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
return KeyFactory.getInstance(algOid).generatePrivate(spec);
} finally {
input.close();
}
}
/**
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private static Manifest addDigestsToManifest(JarFile jar, int hashes)
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, JarFile in, JarOutputStream out,
long timestamp, int alignment) throws IOException {
byte[] buffer = new byte[4096];
int num;
Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<String>(entries.keySet());
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 File file;
private ASN1ObjectIdentifier type;
private byte[] buffer;
int bufferSize = 0;
CMSProcessableFile(File file) {
this.file = file;
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
buffer = new byte[4096];
}
@Override
public ASN1ObjectIdentifier getContentType() {
return type;
}
@Override
public void write(OutputStream out) throws IOException, CMSException {
FileInputStream input = new FileInputStream(file);
long len = file.length() - 2;
while ((bufferSize = input.read(buffer)) > 0) {
if (len <= bufferSize) {
out.write(buffer, 0, (int) len);
break;
} else {
out.write(buffer, 0, bufferSize);
}
len -= bufferSize;
}
}
@Override
public Object getContent() {
return file;
}
byte[] getTail() {
return Arrays.copyOfRange(buffer, 0, bufferSize);
}
}
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, JarFile 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);
}
public static void main(String[] args) {
boolean minSign = false;
int argStart = 0;
if (args.length < 4) {
System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar");
System.exit(2);
}
if (args[0].equals("-m")) {
minSign = true;
argStart = 1;
}
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
File pubKey = new File(args[argStart]);
File privKey = new File(args[argStart + 1]);
File input = new File(args[argStart + 2]);
File output = new File(args[argStart + 3]);
int alignment = 4;
JarFile inputJar = null;
FileOutputStream outputFile = null;
int hashes = 0;
try {
X509Certificate publicKey = readPublicKey(new FileInputStream(pubKey));
hashes |= getDigestAlgorithm(publicKey);
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
PrivateKey privateKey = readPrivateKey(new FileInputStream(privKey));
outputFile = new FileOutputStream(output);
if (minSign) {
ZipSigner.signWholeFile(input, publicKey, privateKey, outputFile);
} else {
inputJar = new JarFile(input, false); // Don't verify.
JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(inputJar, hashes);
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
outputJar.close();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
try {
if (inputJar != null) inputJar.close();
if (outputFile != null) outputFile.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
}