mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-20 09:07:31 +00:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
375cd0e42b | ||
![]() |
3934821436 | ||
![]() |
c3b473e4bc | ||
![]() |
7ed2c077de | ||
![]() |
1283167595 | ||
![]() |
23c2e22910 | ||
![]() |
f44b2dbd45 | ||
![]() |
46ee2c3f4e | ||
![]() |
5d5ec08566 | ||
![]() |
c3a6179a21 | ||
![]() |
ef175e3cbe | ||
![]() |
4de51d93ef | ||
![]() |
e7a2144def | ||
![]() |
52a2c6958b | ||
![]() |
70243d7a47 | ||
![]() |
b5b8c4b725 | ||
![]() |
6c4d81b1e9 | ||
![]() |
c88dc8795b | ||
![]() |
a8030c39b1 | ||
![]() |
7243b9e72f | ||
![]() |
d149af9628 | ||
![]() |
c0ac2d540b | ||
![]() |
528634d755 | ||
![]() |
3283439fd4 | ||
![]() |
c8216f9bc5 | ||
![]() |
e579f314a6 | ||
![]() |
d1a7372bd2 | ||
![]() |
e837bdc8ad | ||
![]() |
7265450e2e | ||
![]() |
058dbc9f9e | ||
![]() |
daf9b019c6 | ||
![]() |
14eebd582f | ||
![]() |
9a8eeacee8 | ||
![]() |
45b0bf5bc5 | ||
![]() |
88db822c43 | ||
![]() |
fbf3588fdf | ||
![]() |
a82ef6bd35 | ||
![]() |
312466aaf8 | ||
![]() |
c0ca99f4b4 | ||
![]() |
196f15d240 | ||
![]() |
bfddef2671 | ||
![]() |
44395e8ff0 | ||
![]() |
835ece5469 | ||
![]() |
d93fc67a75 | ||
![]() |
838f3cc01e | ||
![]() |
4d5841332a | ||
![]() |
9b41976252 | ||
![]() |
d08fd0561a | ||
![]() |
a6958ac139 | ||
![]() |
d7d76f54cc | ||
![]() |
970a2e87b3 | ||
![]() |
cabaae8403 | ||
![]() |
f2064a84ed | ||
![]() |
6db27c7758 | ||
![]() |
3f83919e09 | ||
![]() |
72a5b83544 | ||
![]() |
d2e8ecc646 | ||
![]() |
30eb4074cb | ||
![]() |
9929e7d8e8 | ||
![]() |
f6ee252572 | ||
![]() |
90d218ebc8 | ||
![]() |
b0a5dbb4c2 | ||
![]() |
0abdfda5a2 | ||
![]() |
a7ceb04cb7 | ||
![]() |
274efb49e7 | ||
![]() |
b3cd83bbca | ||
![]() |
b8bd83ba05 | ||
![]() |
34dcf49fbc | ||
![]() |
ef2f8d485b | ||
![]() |
9fb9212b0a | ||
![]() |
f31a24b16d | ||
![]() |
b436bce565 | ||
![]() |
886286a819 | ||
![]() |
6d93831488 | ||
![]() |
bcdadc6581 | ||
![]() |
36448191b7 | ||
![]() |
be5be108c3 | ||
![]() |
c9ca42aaa9 | ||
![]() |
c0e2f44092 | ||
![]() |
1412fcbb22 | ||
![]() |
9b445d89a1 | ||
![]() |
c3c78428c4 | ||
![]() |
c6d2bf577f | ||
![]() |
25703c1750 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,9 +2,13 @@ out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
config.prop
|
||||
|
||||
# Manually dumped jars
|
||||
snet/libs
|
||||
|
||||
# Built binaries
|
||||
ziptools/zipadjust
|
||||
native/out
|
||||
|
||||
# Android Studio / Gradle
|
||||
*.iml
|
||||
|
21
README.MD
21
README.MD
@@ -1,24 +1,19 @@
|
||||
# Magisk
|
||||
|
||||
## How to build Magisk
|
||||
|
||||
#### Building has been tested on 3 major platforms: macOS, Ubuntu, Windows 10
|
||||
|
||||
### Environment Requirements
|
||||
## Building Environment Requirements
|
||||
|
||||
1. Python 3.5+: run `build.py` script
|
||||
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
|
||||
3. Latest Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
|
||||
4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or specify custom path `ANDROID_NDK`
|
||||
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
|
||||
4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or optionally specify a custom path `ANDROID_NDK`
|
||||
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
|
||||
|
||||
### Instructions and Notes
|
||||
1. Magisk can be built with the latest NDK (r16 as of writing), however binaries released officially will be built with NDK r10e due to ELF incompatibilities with the binaries built from the newer NDKs.
|
||||
2. The easiest way to setup the environment is by importing the folder as an Android Studio project. The IDE will download required components and construct the environment for you. You still have to set the `ANDROID_HOME` environment variable to point to the SDK path.
|
||||
## Building Notes and Instructions
|
||||
1. Building is tested on macOS, Ubuntu, and Windows 10 using the latest stable NDK and NDK r10e. Officially released binaries were built with NDK r10e.
|
||||
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
|
||||
3. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
||||
4. Build everything with `build.py`, don't directly call `gradlew` or `ndk-build`, since most requires special setup / dependencies.
|
||||
5. By default, `build.py` will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place a Java Keystore file at `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||
|
||||
4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `--release` flag), you need a Java Keystore file `release-key.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 SafetyNet extension pack requires the full Magisk Manager as a `compileOnly` dependency. Build the **release** APK, convert it back to Java `.class` files (I use [dex2jar](https://github.com/pxb1988/dex2jar)), and place the converted JAR under `snet/libs` before compiling.
|
||||
|
||||
## License
|
||||
|
||||
|
2
app
2
app
Submodule app updated: 15ed3e52f2...b885ccbd63
@@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -24,8 +24,8 @@ allprojects {
|
||||
}
|
||||
|
||||
ext {
|
||||
compileSdkVersion = 27
|
||||
buildToolsVersion = "28.0.0-rc1"
|
||||
compileSdkVersion = 28
|
||||
buildToolsVersion = "28.0.0"
|
||||
supportLibVersion = "27.1.1"
|
||||
}
|
||||
|
||||
|
345
build.py
345
build.py
@@ -4,8 +4,8 @@ import os
|
||||
import subprocess
|
||||
|
||||
if os.name == 'nt':
|
||||
from colorama import init
|
||||
init()
|
||||
import colorama
|
||||
colorama.init()
|
||||
|
||||
def error(str):
|
||||
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
|
||||
@@ -16,7 +16,7 @@ def header(str):
|
||||
|
||||
# Environment checks
|
||||
if not sys.version_info >= (3, 5):
|
||||
error('Requires Python >= 3.5')
|
||||
error('Requires Python 3.5+')
|
||||
|
||||
if 'ANDROID_HOME' not in os.environ:
|
||||
error('Please add Android SDK path to ANDROID_HOME environment variable!')
|
||||
@@ -24,7 +24,7 @@ if 'ANDROID_HOME' not in os.environ:
|
||||
try:
|
||||
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
error('Please install Java and make sure \'java\' is available in PATH')
|
||||
error('Please install JDK and make sure \'java\' is available in PATH')
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
@@ -41,13 +41,21 @@ if 'ANDROID_NDK' in os.environ:
|
||||
else:
|
||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||
|
||||
cpu_count = multiprocessing.cpu_count()
|
||||
archs = ['armeabi-v7a', 'x86']
|
||||
|
||||
def mv(source, target):
|
||||
print('mv: {} -> {}'.format(source, target))
|
||||
shutil.move(source, target)
|
||||
try:
|
||||
shutil.move(source, target)
|
||||
except:
|
||||
pass
|
||||
|
||||
def cp(source, target):
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
shutil.copyfile(source, target)
|
||||
try:
|
||||
shutil.copyfile(source, target)
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
except:
|
||||
pass
|
||||
|
||||
def rm(file):
|
||||
try:
|
||||
@@ -72,143 +80,199 @@ def zip_with_msg(zipfile, source, target):
|
||||
zipfile.write(source, target)
|
||||
|
||||
def build_all(args):
|
||||
build_binary(args)
|
||||
build_apk(args)
|
||||
build_binary(args)
|
||||
zip_main(args)
|
||||
zip_uninstaller(args)
|
||||
build_snet(args)
|
||||
|
||||
def collect_binary():
|
||||
for arch in archs:
|
||||
mkdir_p(os.path.join('native', 'out', arch))
|
||||
for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox', 'b64xz']:
|
||||
source = os.path.join('native', 'libs', arch, bin)
|
||||
target = os.path.join('native', 'out', arch, bin)
|
||||
mv(source, target)
|
||||
|
||||
def build_binary(args):
|
||||
header('* Building Magisk binaries')
|
||||
# If nothing specified, build everything
|
||||
try:
|
||||
targets = args.target
|
||||
except:
|
||||
targets = []
|
||||
|
||||
# Force update logging.h timestamp to trigger recompilation
|
||||
if len(targets) == 0:
|
||||
targets = ['magisk', 'magiskinit', 'magiskboot', 'busybox', 'b64xz']
|
||||
|
||||
header('* Building binaries: ' + ' '.join(targets))
|
||||
|
||||
# Force update logging.h timestamp to trigger recompilation for the flags to make a difference
|
||||
os.utime(os.path.join('native', 'jni', 'include', 'logging.h'))
|
||||
|
||||
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
|
||||
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
|
||||
# Basic flags
|
||||
base_flags = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={} MAGISK_DEBUG={}'.format(config['version'], config['versionCode'],
|
||||
'' if args.release else '-DMAGISK_DEBUG')
|
||||
|
||||
# Prebuild
|
||||
proc = subprocess.run('{} -C native PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
if 'magisk' in targets:
|
||||
# Magisk is special case as it is a dependency of magiskinit
|
||||
proc = subprocess.run('{} -C native {} B_MAGISK=1 -j{}'.format(ndk_build, base_flags, cpu_count), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
collect_binary()
|
||||
|
||||
old_platform = False
|
||||
flags = base_flags
|
||||
|
||||
if 'b64xz' in targets:
|
||||
flags += ' B_BXZ=1'
|
||||
old_platform = True
|
||||
|
||||
if old_platform:
|
||||
proc = subprocess.run('{} -C native OLD_PLAT=1 {} -j{}'.format(ndk_build, flags, cpu_count), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build binaries failed!')
|
||||
collect_binary()
|
||||
|
||||
other = False
|
||||
flags = base_flags
|
||||
|
||||
if 'magiskinit' in targets:
|
||||
for arch in archs:
|
||||
bin_file = os.path.join('native', 'out', arch, 'magisk')
|
||||
if not os.path.exists(bin_file):
|
||||
error('Build "magisk" before building "magiskinit"')
|
||||
with open(os.path.join('native', 'out', arch, 'binaries_arch_xz.h'), 'w') as out:
|
||||
with open(bin_file, 'rb') as src:
|
||||
xz_dump(src, out, 'magisk_xz')
|
||||
|
||||
stub_apk = os.path.join(config['outdir'], 'stub-release.apk')
|
||||
if not os.path.exists(stub_apk):
|
||||
error('Build release stub APK before building "magiskinit"')
|
||||
|
||||
with open(os.path.join('native', 'out', 'binaries_xz.h'), 'w') as out:
|
||||
with open(stub_apk, 'rb') as src:
|
||||
xz_dump(src, out, 'manager_xz')
|
||||
|
||||
flags += ' B_INIT=1'
|
||||
other = True
|
||||
|
||||
if 'magiskboot' in targets:
|
||||
flags += ' B_BOOT=1'
|
||||
other = True
|
||||
|
||||
if 'busybox' in targets:
|
||||
flags += ' B_BB=1'
|
||||
other = True
|
||||
|
||||
if other:
|
||||
proc = subprocess.run('{} -C native {} -j{}'.format(ndk_build, flags, cpu_count), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build binaries failed!')
|
||||
collect_binary()
|
||||
|
||||
def sign_apk(source, target):
|
||||
# Find the latest build tools
|
||||
build_tool = os.path.join(os.environ['ANDROID_HOME'], 'build-tools',
|
||||
sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1])
|
||||
|
||||
proc = subprocess.run([os.path.join(build_tool, 'zipalign'), '-vpf', '4', source, target], stdout=subprocess.DEVNULL)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
error('Zipalign Magisk Manager failed!')
|
||||
|
||||
print('')
|
||||
for arch in ['armeabi-v7a', 'x86']:
|
||||
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('native', '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')
|
||||
# Find apksigner.jar
|
||||
apksigner = ''
|
||||
for root, dirs, files in os.walk(build_tool):
|
||||
if 'apksigner.jar' in files:
|
||||
apksigner = os.path.join(root, 'apksigner.jar')
|
||||
break
|
||||
if not apksigner:
|
||||
error('Cannot find apksigner.jar in Android SDK build tools')
|
||||
|
||||
print('')
|
||||
|
||||
proc = subprocess.run('{} -C native {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
proc = subprocess.run('java -jar {} sign --ks release-key.jks --ks-pass pass:{} --ks-key-alias {} --key-pass pass:{} {}'.format(
|
||||
apksigner, config['keyStorePass'], config['keyAlias'], config['keyPass'], target), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
|
||||
print('')
|
||||
for arch in ['armeabi-v7a', 'x86']:
|
||||
for binary in ['magiskinit', 'magiskboot', 'b64xz', 'busybox']:
|
||||
try:
|
||||
mv(os.path.join('native', 'libs', arch, binary), os.path.join('out', arch, binary))
|
||||
except:
|
||||
pass
|
||||
error('Release sign Magisk Manager failed!')
|
||||
|
||||
def build_apk(args):
|
||||
header('* Building Magisk Manager')
|
||||
|
||||
mkdir(os.path.join('app', 'src', 'main', 'assets'))
|
||||
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
|
||||
source = os.path.join('scripts', script)
|
||||
target = os.path.join('app', 'src', 'main', 'assets', script)
|
||||
cp(source, target)
|
||||
source = os.path.join('scripts', 'util_functions.sh')
|
||||
target = os.path.join('app', 'src', 'full', 'res', 'raw', 'util_functions.sh')
|
||||
cp(source, target)
|
||||
|
||||
if args.release:
|
||||
if not os.path.exists('release_signature.jks'):
|
||||
error('Please generate a java keystore and place it in \'release_signature.jks\'')
|
||||
if not os.path.exists('release-key.jks'):
|
||||
error('Please generate a java keystore and place it in \'release-key.jks\'')
|
||||
|
||||
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-unsigned.apk')
|
||||
aligned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-aligned.apk')
|
||||
release = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release.apk')
|
||||
|
||||
# Find the latest build tools
|
||||
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
|
||||
|
||||
rm(aligned)
|
||||
rm(release)
|
||||
|
||||
proc = subprocess.run([
|
||||
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'),
|
||||
'-v', '-p', '4', unsigned, aligned], stdout=subprocess.DEVNULL)
|
||||
if proc.returncode != 0:
|
||||
error('Zipalign Magisk Manager failed!')
|
||||
|
||||
# Find apksigner.jar
|
||||
apksigner = ''
|
||||
for root, dirs, files in os.walk(os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool)):
|
||||
if 'apksigner.jar' in files:
|
||||
apksigner = os.path.join(root, 'apksigner.jar')
|
||||
break
|
||||
if not apksigner:
|
||||
error('Cannot find apksigner.jar in Android SDK build tools')
|
||||
|
||||
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
|
||||
apksigner, 'release_signature.jks', release, aligned), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Release sign Magisk Manager failed!')
|
||||
|
||||
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'release', 'app-full-release-unsigned.apk')
|
||||
release = os.path.join(config['outdir'], 'app-release.apk')
|
||||
sign_apk(unsigned, release)
|
||||
header('Output: ' + release)
|
||||
rm(unsigned)
|
||||
rm(aligned)
|
||||
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'app-release.apk')
|
||||
print('')
|
||||
mv(release, target)
|
||||
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'stub', 'release', 'app-stub-release-unsigned.apk')
|
||||
release = os.path.join(config['outdir'], 'stub-release.apk')
|
||||
sign_apk(unsigned, release)
|
||||
header('Output: ' + release)
|
||||
rm(unsigned)
|
||||
else:
|
||||
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('out')
|
||||
target = os.path.join('out', 'app-debug.apk')
|
||||
print('')
|
||||
source = os.path.join('app', 'build', 'outputs', 'apk', 'full', 'debug', 'app-full-debug.apk')
|
||||
target = os.path.join(config['outdir'], 'app-debug.apk')
|
||||
mv(source, target)
|
||||
header('Output: ' + target)
|
||||
|
||||
source = os.path.join('app', 'build', 'outputs', 'apk', 'stub', 'debug', 'app-stub-debug.apk')
|
||||
target = os.path.join(config['outdir'], 'stub-debug.apk')
|
||||
mv(source, target)
|
||||
header('Output: ' + target)
|
||||
|
||||
def build_snet(args):
|
||||
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build snet extention failed!')
|
||||
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'snet.apk')
|
||||
print('')
|
||||
mv(source, target)
|
||||
target = os.path.join(config['outdir'], 'snet.apk')
|
||||
# Re-compress the whole APK for smaller size
|
||||
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
|
||||
with zipfile.ZipFile(source) as zin:
|
||||
for item in zin.infolist():
|
||||
zout.writestr(item.filename, zin.read(item))
|
||||
rm(source)
|
||||
header('Output: ' + target)
|
||||
|
||||
def xz_dump(src, out, var_name):
|
||||
out.write('const static unsigned char {}[] = {{'.format(var_name))
|
||||
for i, c in enumerate(lzma.compress(src.read(), preset=9)):
|
||||
if i % 16 == 0:
|
||||
out.write('\n')
|
||||
out.write('0x{:02X},'.format(c))
|
||||
out.write('\n};\n')
|
||||
out.flush()
|
||||
|
||||
def gen_update_binary():
|
||||
update_bin = []
|
||||
binary = os.path.join('out', 'armeabi-v7a', 'b64xz')
|
||||
binary = os.path.join('native', 'out', 'armeabi-v7a', 'b64xz')
|
||||
if not os.path.exists(binary):
|
||||
error('Please build \'binary\' before zipping!')
|
||||
with open(binary, 'rb') as b64xz:
|
||||
update_bin.append('#! /sbin/sh\nEX_ARM=\'')
|
||||
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
binary = os.path.join('out', 'x86', 'b64xz')
|
||||
binary = os.path.join('native', 'out', 'x86', 'b64xz')
|
||||
with open(binary, 'rb') as b64xz:
|
||||
update_bin.append('\'\nEX_X86=\'')
|
||||
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
binary = os.path.join('out', 'armeabi-v7a', 'busybox')
|
||||
binary = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
update_bin.append('\'\nBB_ARM=')
|
||||
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
|
||||
binary = os.path.join('out', 'x86', 'busybox')
|
||||
binary = os.path.join('native', 'out', 'x86', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
update_bin.append('\nBB_X86=')
|
||||
update_bin.append(base64.b64encode(lzma.compress(busybox.read(), preset=9)).decode('ascii'))
|
||||
@@ -236,12 +300,12 @@ def zip_main(args):
|
||||
# Binaries
|
||||
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
|
||||
for binary in ['magiskinit', 'magiskboot']:
|
||||
source = os.path.join('out', lib_dir, binary)
|
||||
source = os.path.join('native', 'out', lib_dir, binary)
|
||||
target = os.path.join(zip_dir, binary)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# APK
|
||||
source = os.path.join('out', 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
source = os.path.join(config['outdir'], 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
target = os.path.join('common', 'magisk.apk')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
@@ -254,14 +318,14 @@ def zip_main(args):
|
||||
source = os.path.join('scripts', 'util_functions.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info util_functions.sh
|
||||
util_func = script.read().replace(
|
||||
'#MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode))
|
||||
util_func = script.read().replace('#MAGISK_VERSION_STUB',
|
||||
'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(config['version'], config['versionCode']))
|
||||
target = os.path.join('common', 'util_functions.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, util_func)
|
||||
# addon.d.sh
|
||||
source = os.path.join('scripts', 'addon.d.sh')
|
||||
target = os.path.join('common', '99-magisk.sh')
|
||||
target = os.path.join('common', 'addon.d.sh')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Prebuilts
|
||||
@@ -271,8 +335,10 @@ def zip_main(args):
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = os.path.join('out', 'Magisk-v{}.zip'.format(args.versionString))
|
||||
output = os.path.join(config['outdir'], 'Magisk-v{}.zip'.format(config['version']) if config['prettyName'] else
|
||||
'magisk-release.zip' if args.release else 'magisk-debug.zip')
|
||||
sign_adjust_zip(unsigned, output)
|
||||
header('Output: ' + output)
|
||||
|
||||
def zip_uninstaller(args):
|
||||
header('* Packing Uninstaller Zip')
|
||||
@@ -286,19 +352,16 @@ def zip_uninstaller(args):
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, gen_update_binary())
|
||||
# updater-script
|
||||
source = os.path.join('scripts', 'uninstaller_loader.sh')
|
||||
source = os.path.join('scripts', 'magisk_uninstaller.sh')
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Binaries
|
||||
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
|
||||
source = os.path.join('out', lib_dir, 'magiskboot')
|
||||
target = os.path.join(zip_dir, 'magiskboot')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
source = os.path.join('scripts', 'magisk_uninstaller.sh')
|
||||
target = 'magisk_uninstaller.sh'
|
||||
zip_with_msg(zipf, source, target)
|
||||
for bin in ['magisk', 'magiskboot']:
|
||||
source = os.path.join('native', 'out', lib_dir, bin)
|
||||
target = os.path.join(zip_dir, bin)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Scripts
|
||||
# util_functions.sh
|
||||
@@ -316,11 +379,13 @@ def zip_uninstaller(args):
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = os.path.join('out', 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')))
|
||||
output = os.path.join(config['outdir'], 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))
|
||||
if config['prettyName'] else 'magisk-uninstaller.zip')
|
||||
sign_adjust_zip(unsigned, output)
|
||||
header('Output: ' + output)
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
signer_name = 'zipsigner-2.1.jar'
|
||||
signer_name = 'zipsigner-2.2.jar'
|
||||
jarsigner = os.path.join('utils', 'build', 'libs', signer_name)
|
||||
|
||||
if not os.path.exists(jarsigner):
|
||||
@@ -339,40 +404,56 @@ def sign_adjust_zip(unsigned, output):
|
||||
|
||||
def cleanup(args):
|
||||
if len(args.target) == 0:
|
||||
args.target = ['binary', 'java', 'zip']
|
||||
args.target = ['native', 'java']
|
||||
|
||||
if 'binary' in args.target:
|
||||
header('* Cleaning binaries')
|
||||
subprocess.run(ndk_build + ' -C native PRECOMPILE=true clean', shell=True)
|
||||
subprocess.run(ndk_build + ' -C native clean', shell=True)
|
||||
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
|
||||
if 'native' in args.target:
|
||||
header('* Cleaning native')
|
||||
subprocess.run(ndk_build + ' -C native B_MAGISK=1 B_INIT=1 B_BOOT=1 B_BXZ=1 B_BB=1 clean', shell=True)
|
||||
shutil.rmtree(os.path.join('native', 'out'), ignore_errors=True)
|
||||
|
||||
if 'java' in args.target:
|
||||
header('* Cleaning java')
|
||||
subprocess.run('{} app:clean snet:clean utils:clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
for f in os.listdir('out'):
|
||||
if '.apk' in f:
|
||||
rm(os.path.join('out', f))
|
||||
|
||||
if 'zip' in args.target:
|
||||
header('* Cleaning zip files')
|
||||
for f in os.listdir('out'):
|
||||
if '.zip' in f:
|
||||
rm(os.path.join('out', f))
|
||||
def parse_config():
|
||||
c = {}
|
||||
with open('config.prop', 'r') as f:
|
||||
for line in [l.strip(' \t\r\n') for l in f]:
|
||||
if line.startswith('#') or len(line) == 0:
|
||||
continue
|
||||
prop = line.split('=')
|
||||
c[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
|
||||
|
||||
if 'version' not in c or 'versionCode' not in c:
|
||||
error('"version" and "versionCode" is required in "config.prop"')
|
||||
|
||||
try:
|
||||
c['versionCode'] = int(c['versionCode'])
|
||||
except ValueError:
|
||||
error('"versionCode" is required to be an integer')
|
||||
|
||||
if 'prettyName' not in c:
|
||||
c['prettyName'] = 'false'
|
||||
|
||||
c['prettyName'] = c['prettyName'].lower() == 'true'
|
||||
|
||||
if 'outdir' not in c:
|
||||
c['outdir'] = 'out'
|
||||
|
||||
mkdir_p(c['outdir'])
|
||||
return c
|
||||
|
||||
config = parse_config()
|
||||
|
||||
parser = argparse.ArgumentParser(description='Magisk build script')
|
||||
parser.add_argument('--release', action='store_true', help='compile Magisk for release')
|
||||
subparsers = parser.add_subparsers(title='actions')
|
||||
|
||||
all_parser = subparsers.add_parser('all', help='build everything and create flashable zip with uninstaller')
|
||||
all_parser.add_argument('versionString')
|
||||
all_parser.add_argument('versionCode', type=int)
|
||||
all_parser = subparsers.add_parser('all', help='build everything (binaries/apks/zips)')
|
||||
all_parser.set_defaults(func=build_all)
|
||||
|
||||
binary_parser = subparsers.add_parser('binary', help='build Magisk binaries')
|
||||
binary_parser.add_argument('versionString')
|
||||
binary_parser.add_argument('versionCode', type=int)
|
||||
binary_parser = subparsers.add_parser('binary', help='build binaries. target: magisk magiskinit magiskboot busybox b64xz')
|
||||
binary_parser.add_argument('target', nargs='*')
|
||||
binary_parser.set_defaults(func=build_binary)
|
||||
|
||||
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
|
||||
@@ -382,14 +463,12 @@ snet_parser = subparsers.add_parser('snet', help='build snet extention for Magis
|
||||
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)
|
||||
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 java zip')
|
||||
clean_parser = subparsers.add_parser('clean', help='cleanup. target: binary java zip')
|
||||
clean_parser.add_argument('target', nargs='*')
|
||||
clean_parser.set_defaults(func=cleanup)
|
||||
|
||||
|
15
config.prop.sample
Normal file
15
config.prop.sample
Normal file
@@ -0,0 +1,15 @@
|
||||
# The version string and version code of Magisk
|
||||
version=
|
||||
versionCode=
|
||||
|
||||
outdir=out
|
||||
|
||||
# Whether use pretty names for zips, e.g. Magisk-v${version}.zip, Magisk-uninstaller-${date}.zip
|
||||
# The default output names are magisk-${release/debug/uninstaller}.zip
|
||||
prettyName=false
|
||||
|
||||
# These pwds are passed to apksigner for release-key.jks. Necessary when building release apks
|
||||
# keyPass is the pwd for the specified keyAlias
|
||||
keyStorePass=
|
||||
keyAlias=
|
||||
keyPass=
|
16
docs/tips.md
16
docs/tips.md
@@ -5,7 +5,7 @@ Magisk does modifications systemless-ly, which means applying official OTAs is m
|
||||
|
||||
**This tutorial is only for Magisk v14.1+**
|
||||
|
||||
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in anyway, even remounting the partition to rw will tamper block verification!!**
|
||||
**NOTE: In order to apply OTAs, you HAVE to make sure you haven't modified `/system` (and `/vendor` if available) in any way. Even remounting the partition to rw will tamper block verification!!**
|
||||
|
||||
#### Prerequisites
|
||||
1. Please disable *Automatic system updates* in developer options, so it won't install OTAs without your acknowledgement.
|
||||
@@ -16,10 +16,10 @@ Magisk does modifications systemless-ly, which means applying official OTAs is m
|
||||
#### Devices with A/B Partitions
|
||||
(Includes Pixel family)
|
||||
|
||||
Due to the fact that these devices have two separate partitions and the OTA installation happens live when the system is still running, these devices have the best support: the out-of-the-box OTA installation works seamlessly and Magisk will be preserved after the installation.
|
||||
Due to the fact that these devices have two separate partitions, and the OTA installation happens live while the system is still running, these devices have the best support: the out-of-the-box OTA installation works seamlessly and Magisk will be preserved after the installation.
|
||||
|
||||
1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates)
|
||||
1. Once the installation passed step 1 and starting step 2, go to Magisk Manager → Install → Install to Second Slot. This will install Magisk into the second boot image slot, which is the updated slot.
|
||||
1. After restoring stock boot image, apply OTAs as you normally would (Settings → System → System Updates).
|
||||
1. Once the installation has passed step 1 and is starting step 2, go to Magisk Manager → Install → Install to Second Slot. This will install Magisk into the second boot image slot, which is the updated slot.
|
||||
<img src="images/ota_step2.png" width="250"> <img src="images/install_second_slot.png" width="250">
|
||||
1. Let the OTA finish its job. After a reboot, the bootloader will switch to the updated system. Magisk should still be installed since we already patched the new boot image.
|
||||
|
||||
@@ -27,7 +27,7 @@ Due to the fact that these devices have two separate partitions and the OTA inst
|
||||
(Includes Pixel family, Nexus family, Samsung devices)
|
||||
(If you are using a device with A/B partitions, I **strongly** recommend you to use the method stated above since it uses the stock OTA installation mechanism and will always work under any circumstances)
|
||||
|
||||
The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash) app developed by Chainfire is a great app to apply OTAs and preserve root at the same time. However, whether it supports your device/system combination depends on the application itself, and support may also change in the future. If you face any issues, please directly [report to Chainfire](https://forum.xda-developers.com/general/paid-software/flashfire-t3075433).
|
||||
The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash) app developed by Chainfire is a great app to apply OTAs and preserve root at the same time. However, whether it supports your device/system combination depends on the application itself, and support may change in the future. If you face any issues, please directly [report to Chainfire](https://forum.xda-developers.com/general/paid-software/flashfire-t3075433).
|
||||
|
||||
1. After restoring the stock boot image, download the OTA (Settings → System → System Updates), **do not press reboot to install.**
|
||||
1. Open FlashFire, it should detect your OTA zip. Select OK in the popup dialog to let it do its setup.
|
||||
@@ -39,10 +39,10 @@ The [FlashFire](https://play.google.com/store/apps/details?id=eu.chainfire.flash
|
||||
Unfortunately, there are no real good ways to apply OTAs on all devices. Also, the tutorial provided below will not preserve Magisk - you will have to manually re-root your device after the upgrade, and this will require PC access. Here I share my personal experience with HTC U11.
|
||||
|
||||
1. To properly install OTAs, you should have your stock recovery installed on your device. If you have custom recovery installed, you can restore it from your previous backup, or dumps found online, or factory images provided by OEMs.
|
||||
If you decide to start by installing Magisk without touching your recovery partition, you have a few choices, either way you will end up with a Magisk rooted device, but recovery remain stock untouched:
|
||||
If you decide to start by installing Magisk without touching your recovery partition, you have a few choices, either way you will end up with a Magisk rooted device, but recovery remains stock untouched:
|
||||
- If supported, use `fastboot boot <recovery_img>` to boot the custom recovery and install Magisk.
|
||||
- If you have a copy of your stock boot image dump, install Magisk by patching boot image via Magisk Manager, and manually flash it through download mode / fastboot mode / Odin
|
||||
1. Once your device have stock recovery and stock boot image restored, download the OTA. Optionally, once you downloaded the OTA update zip, you can find a way to copy the zip out since you are still rooted. Personally I will extract the stock boot image and recovery image from the OTA zip for future usage (to patch via Magisk Manager or restore stock recovery etc.)
|
||||
1. Once your device has stock recovery and stock boot image restored, download the OTA. Optionally, once you have downloaded the OTA update zip, you can find a way to copy the zip out, since you are still rooted. Personally, I extract the stock boot image and recovery image from the OTA zip for future usage (to patch via Magisk Manager or restore stock recovery etc.)
|
||||
1. Apply and reboot your device. This will use the official stock OTA installation mechanism of your device to upgrade your system.
|
||||
1. Once it's done you will be left with an upgraded, 100% stock, un-rooted device. You will have to manually flash Magisk back. Consider using the methods stated in step 1. to flash Magisk without touching the recovery partition if you want to receive stock OTAs frequently.
|
||||
|
||||
@@ -50,4 +50,4 @@ If you decide to start by installing Magisk without touching your recovery parti
|
||||
How to remove a file systemless-ly? To actually make the file **disappear** is complicated (possible, not worth the effort). **Replacing it with a dummy file should be good enough**! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
|
||||
|
||||
## Remove Folders
|
||||
Same as mentioned above, actually making the folder to **disappear** is not worth the effort. **Replacing it with an empty folder should be good enough**! A handy trick for module developers using [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) is to add the folder you want to remove into the `REPLACE` list within `config.sh`. If your module doesn't provide a correspond folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
|
||||
Same as mentioned above, actually making the folder **disappear** is not worth the effort. **Replacing it with an empty folder should be good enough**! A handy trick for module developers using [Magisk Module Template](https://github.com/topjohnwu/magisk-module-template) is to add the folder you want to remove into the `REPLACE` list within `config.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
|
||||
|
@@ -20,3 +20,6 @@ org.gradle.parallel=true
|
||||
# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property.
|
||||
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
|
||||
org.gradle.daemon=true
|
||||
|
||||
# Configuration on demand is not supported by the current version of the Android Gradle plugin since you are using Gradle version 4.6 or above
|
||||
org.gradle.configureondemand=false
|
||||
|
@@ -13,8 +13,9 @@ android {
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
// Passes an optional argument to ndk-build.
|
||||
arguments "GRADLE=true"
|
||||
// Pass arguments to ndk-build.
|
||||
arguments('B_MAGISK=1', 'B_INIT=1', 'B_BOOT=1', 'MAGISK_VERSION=debug',
|
||||
'MAGISK_VER_CODE=99999', 'MAGISK_DEBUG=-DMAGISK_DEBUG')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ UTIL_SRC := utils/cpio.c \
|
||||
# Binaries
|
||||
########################
|
||||
|
||||
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
|
||||
ifdef B_MAGISK
|
||||
|
||||
# magisk main binary
|
||||
include $(CLEAR_VARS)
|
||||
@@ -40,9 +40,10 @@ LOCAL_C_INCLUDES := \
|
||||
LOCAL_SRC_FILES := \
|
||||
core/magisk.c \
|
||||
core/daemon.c \
|
||||
core/log_monitor.c \
|
||||
core/log_daemon.c \
|
||||
core/bootstages.c \
|
||||
core/socket.c \
|
||||
core/db.c \
|
||||
magiskhide/magiskhide.c \
|
||||
magiskhide/proc_monitor.c \
|
||||
magiskhide/hide_utils.c \
|
||||
@@ -51,7 +52,6 @@ LOCAL_SRC_FILES := \
|
||||
resetprop/system_properties.cpp \
|
||||
su/su.c \
|
||||
su/activity.c \
|
||||
su/db.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c \
|
||||
su/su_socket.c \
|
||||
@@ -63,7 +63,7 @@ include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
ifndef PRECOMPILE
|
||||
ifdef B_INIT
|
||||
|
||||
# magiskinit
|
||||
include $(CLEAR_VARS)
|
||||
@@ -72,7 +72,8 @@ LOCAL_STATIC_LIBRARIES := libsepol liblzma
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/magiskpolicy \
|
||||
../out/$(TARGET_ARCH_ABI) \
|
||||
out \
|
||||
out/$(TARGET_ARCH_ABI) \
|
||||
$(LIBSEPOL) \
|
||||
$(LIBLZMA)
|
||||
|
||||
@@ -87,6 +88,10 @@ LOCAL_SRC_FILES := \
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
ifdef B_BOOT
|
||||
|
||||
# magiskboot
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
@@ -113,8 +118,10 @@ LOCAL_CFLAGS := -DXWRAP_EXIT
|
||||
LOCAL_LDLIBS := -lz
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# static binaries
|
||||
ifndef GRADLE # Do not run gradle sync on these binaries
|
||||
endif
|
||||
|
||||
ifdef B_BXZ
|
||||
|
||||
# b64xz
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := b64xz
|
||||
@@ -123,11 +130,14 @@ LOCAL_C_INCLUDES := $(LIBLZMA)
|
||||
LOCAL_SRC_FILES := b64xz.c
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
# Busybox
|
||||
include jni/external/busybox/Android.mk
|
||||
|
||||
endif
|
||||
|
||||
# Precompile
|
||||
ifdef B_BB
|
||||
|
||||
# Busybox
|
||||
include jni/external/busybox/Android.mk
|
||||
|
||||
endif
|
||||
|
||||
########################
|
||||
|
@@ -1,5 +1,10 @@
|
||||
APP_ABI := x86 armeabi-v7a
|
||||
APP_PLATFORM := android-21
|
||||
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99
|
||||
APP_CFLAGS := -std=gnu99 ${MAGISK_DEBUG} \
|
||||
-DMAGISK_VERSION="${MAGISK_VERSION}" -DMAGISK_VER_CODE=${MAGISK_VER_CODE}
|
||||
APP_CPPFLAGS := -std=c++11
|
||||
APP_SHORT_COMMANDS := true
|
||||
ifdef OLD_PLAT
|
||||
APP_PLATFORM := android-9
|
||||
else
|
||||
APP_PLATFORM := android-21
|
||||
endif
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "db.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
@@ -113,11 +114,11 @@ static struct node_entry *insert_child(struct node_entry *p, struct node_entry *
|
||||
* setenvs *
|
||||
***********/
|
||||
|
||||
static void bb_setenv(struct vector *v) {
|
||||
static void set_path(struct vector *v) {
|
||||
for (int i = 0; environ[i]; ++i) {
|
||||
if (strncmp(environ[i], "PATH=", 5) == 0) {
|
||||
snprintf(buf, PATH_MAX, "PATH=%s:%s", BBPATH, strchr(environ[i], '=') + 1);
|
||||
vec_push_back(v, strdup(buf));
|
||||
vec_push_back(v, strdup("PATH=" BBPATH ":/sbin:" MIRRDIR "/system/bin:"
|
||||
MIRRDIR "/system/xbin:" MIRRDIR "/vendor/bin"));
|
||||
} else {
|
||||
vec_push_back(v, strdup(environ[i]));
|
||||
}
|
||||
@@ -143,7 +144,7 @@ static void exec_common_script(const char* stage) {
|
||||
if (access(buf2, X_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL);
|
||||
int pid = exec_command(0, NULL, set_path, "sh", buf2, NULL);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
@@ -160,7 +161,7 @@ static void exec_module_script(const char* stage) {
|
||||
if (access(buf2, F_OK) == -1 || access(buf, F_OK) == 0)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
int pid = exec_command(0, NULL, bb_setenv, "sh", buf2, NULL);
|
||||
int pid = exec_command(0, NULL, set_path, "sh", buf2, NULL);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
@@ -306,11 +307,11 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
if (IS_LNK(child)) {
|
||||
// Copy symlinks directly
|
||||
cp_afc(buf2, buf);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("creat_link: %s <- %s\n",buf, buf2);
|
||||
#else
|
||||
LOGI("creat_link: %s\n", buf);
|
||||
#endif
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("creat_link: %s <- %s\n",buf, buf2);
|
||||
#else
|
||||
LOGI("creat_link: %s\n", buf);
|
||||
#endif
|
||||
} else {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
bind_mount(buf2, buf);
|
||||
@@ -427,33 +428,44 @@ static int prepare_img() {
|
||||
rm_rf(buf);
|
||||
continue;
|
||||
}
|
||||
snprintf(buf, PATH_MAX, "%s/%s/update", MOUNTPOINT, entry->d_name);
|
||||
unlink(buf);
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
|
||||
if (access(buf, F_OK) == 0)
|
||||
continue;
|
||||
vec_push_back(&module_list, strdup(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
// Trim image
|
||||
umount_image(MOUNTPOINT, magiskloop);
|
||||
if (trim_img(MAINIMG, MOUNTPOINT, magiskloop))
|
||||
return 1;
|
||||
free(magiskloop);
|
||||
trim_img(MAINIMG);
|
||||
|
||||
// Remount them back :)
|
||||
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
free(magiskloop);
|
||||
|
||||
// Fix file selinux contexts
|
||||
fix_filecon();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fix_filecon() {
|
||||
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(dirfd);
|
||||
close(dirfd);
|
||||
void install_apk(const char *apk) {
|
||||
setfilecon(apk, "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
while (1) {
|
||||
sleep(5);
|
||||
LOGD("apk_install: attempting to install APK");
|
||||
int apk_res = -1, pid;
|
||||
pid = exec_command(1, &apk_res, NULL, "/system/bin/pm", "install", "-r", apk, NULL);
|
||||
if (pid != -1) {
|
||||
int err = 0;
|
||||
while (fdgets(buf, PATH_MAX, apk_res) > 0) {
|
||||
LOGD("apk_install: %s", buf);
|
||||
err |= strstr(buf, "Error:") != NULL;
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (err)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlink(apk);
|
||||
}
|
||||
|
||||
/****************
|
||||
@@ -465,48 +477,39 @@ static void unblock_boot_process() {
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static const char wrapper[] =
|
||||
"#!/system/bin/sh\n"
|
||||
"unset LD_LIBRARY_PATH\n"
|
||||
"unset LD_PRELOAD\n"
|
||||
"exec /sbin/magisk.bin \"${0##*/}\" \"$@\"\n";
|
||||
|
||||
void startup() {
|
||||
if (!check_data())
|
||||
return;
|
||||
unblock_boot_process();
|
||||
|
||||
// uninstaller or core-only mode
|
||||
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
|
||||
goto initialize;
|
||||
if (access(SECURE_DIR, F_OK) != 0) {
|
||||
/* If the folder is not automatically created by the system,
|
||||
* do NOT proceed further. Manual creation of the folder
|
||||
* will cause bootloops on FBE devices. */
|
||||
LOGE(SECURE_DIR" is not present, abort...");
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
// No uninstaller or core-only mode
|
||||
if (access(DISABLEFILE, F_OK) != 0) {
|
||||
// Allocate buffer
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
simple_mount("/system");
|
||||
simple_mount("/vendor");
|
||||
simple_mount("/system");
|
||||
simple_mount("/vendor");
|
||||
}
|
||||
|
||||
initialize:
|
||||
LOGI("** Initializing Magisk\n");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Magisk binaries
|
||||
char *bin_path = NULL;
|
||||
if (access("/cache/data_bin", F_OK) == 0)
|
||||
bin_path = "/cache/data_bin";
|
||||
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/data/com.topjohnwu.magisk/install";
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Migration
|
||||
rm_rf("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink("/data/magisk_debug.log");
|
||||
xmkdir("/data/adb", 0700);
|
||||
chmod("/data/adb", 0700);
|
||||
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
@@ -520,8 +523,15 @@ initialize:
|
||||
// Remove some traits of Magisk
|
||||
unlink("/init.magisk.rc");
|
||||
|
||||
// GSIs will have to override /sbin/adbd with /system/bin/adbd
|
||||
if (access("/sbin/adbd", F_OK) == 0 && access("/system/bin/adbd", F_OK) == 0) {
|
||||
umount2("/sbin/adbd", MNT_DETACH);
|
||||
cp_afc("/system/bin/adbd", "/sbin/adbd");
|
||||
}
|
||||
|
||||
// Create hardlink mirror of /sbin to /root
|
||||
mkdir("/root", 0750);
|
||||
clone_attr("/sbin", "/root");
|
||||
full_read("/sbin/magisk", &magisk, &magisk_size);
|
||||
unlink("/sbin/magisk");
|
||||
full_read("/sbin/magiskinit", &init, &init_size);
|
||||
@@ -537,12 +547,17 @@ initialize:
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
// Setup magisk symlinks
|
||||
// Create wrapper
|
||||
fd = creat("/sbin/magisk", 0755);
|
||||
xwrite(fd, wrapper, sizeof(wrapper) - 1);
|
||||
close(fd);
|
||||
setfilecon("/sbin/magisk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
// Setup magisk symlinks
|
||||
fd = creat("/sbin/magisk.bin", 0755);
|
||||
xwrite(fd, magisk, magisk_size);
|
||||
close(fd);
|
||||
free(magisk);
|
||||
setfilecon("/sbin/magisk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
setfilecon("/sbin/magisk.bin", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(buf, PATH_MAX, "/sbin/%s", applet[i]);
|
||||
xsymlink("/sbin/magisk", buf);
|
||||
@@ -570,6 +585,35 @@ initialize:
|
||||
close(sbin);
|
||||
close(root);
|
||||
|
||||
// Alternative binaries paths
|
||||
char *alt_bin[] = { "/cache/data_bin", "/data/.magisk",
|
||||
"/data/data/com.topjohnwu.magisk/install",
|
||||
"/data/user_de/0/com.topjohnwu.magisk/install", NULL };
|
||||
char *bin_path = NULL;
|
||||
for (int i = 0; alt_bin[i]; ++i) {
|
||||
if (access(alt_bin[i], F_OK) == 0) {
|
||||
bin_path = alt_bin[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Remove legacy stuffs
|
||||
rm_rf("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink("/data/magisk_debug.log");
|
||||
|
||||
// Create directories in tmpfs overlay
|
||||
xmkdirs(MIRRDIR "/system", 0755);
|
||||
xmkdir(MIRRDIR "/bin", 0755);
|
||||
xmkdir(BBPATH, 0755);
|
||||
xmkdir(MOUNTPOINT, 0755);
|
||||
xmkdir(BLOCKDIR, 0755);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
@@ -579,16 +623,10 @@ initialize:
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
xmkdirs(MIRRDIR "/system", 0755);
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vec_for_each(&mounts, line) {
|
||||
if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
} else if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdirs(MIRRDIR "/system", 0755);
|
||||
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/system", buf);
|
||||
@@ -598,7 +636,7 @@ initialize:
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdirs(MIRRDIR "/vendor", 0755);
|
||||
xmkdir(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
|
||||
@@ -617,24 +655,16 @@ initialize:
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdirs(MIRRDIR "/bin", 0755);
|
||||
xmkdirs(DATABIN, 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
|
||||
LOGI("* Setting up internal busybox");
|
||||
xmkdirs(BBPATH, 0755);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
|
||||
// uninstall
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
setenv("BOOTMODE", "true", 1);
|
||||
exec_command(0, NULL, bb_setenv, "sh", UNINSTALLER, NULL);
|
||||
return;
|
||||
if (access(MIRRDIR "/bin/busybox", X_OK) == 0) {
|
||||
LOGI("* Setting up internal busybox");
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
|
||||
// Start post-fs-data mode
|
||||
execv("/sbin/magisk", (char *[]) { "magisk", "--post-fs-data", NULL });
|
||||
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", NULL);
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
@@ -642,8 +672,8 @@ void post_fs_data(int client) {
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
// Start the debug log
|
||||
start_debug_full_log();
|
||||
// If post-fs-data mode is started, it means startup succeeded
|
||||
setup_done = 1;
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
@@ -660,6 +690,9 @@ void post_fs_data(int client) {
|
||||
if (prepare_img())
|
||||
goto core_only; // Mounting fails, we can only do core only stuffs
|
||||
|
||||
restorecon();
|
||||
chmod(SECURE_DIR, 0700);
|
||||
|
||||
// Run common scripts
|
||||
LOGI("* Running post-fs-data.d scripts\n");
|
||||
exec_common_script("post-fs-data");
|
||||
@@ -755,12 +788,24 @@ void late_start(int client) {
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
if (access(SECURE_DIR, F_OK) != 0) {
|
||||
// It's safe to create the folder at this point if the system didn't create it
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
}
|
||||
|
||||
if (!setup_done) {
|
||||
// The setup failed for some reason, reboot and try again
|
||||
exec_command_sync("/system/bin/reboot", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
// Wait till the full patch is done
|
||||
waitpid(full_patch_pid, NULL, 0);
|
||||
if (full_patch_pid > 0)
|
||||
waitpid(full_patch_pid, NULL, 0);
|
||||
|
||||
// Run scripts after full patch, most reliable way to run scripts
|
||||
LOGI("* Running service.d scripts\n");
|
||||
@@ -774,31 +819,21 @@ void late_start(int client) {
|
||||
exec_module_script("service");
|
||||
|
||||
core_only:
|
||||
// Install Magisk Manager if exists
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
// Install Magisk Manager if exists
|
||||
rename(MANAGERAPK, "/data/magisk.apk");
|
||||
setfilecon("/data/magisk.apk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
while (1) {
|
||||
sleep(5);
|
||||
LOGD("apk_install: attempting to install APK");
|
||||
int apk_res = -1, pid;
|
||||
pid = exec_command(1, &apk_res, NULL,
|
||||
"/system/bin/pm", "install", "-r", "/data/magisk.apk", NULL);
|
||||
if (pid != -1) {
|
||||
int err = 0;
|
||||
while (fdgets(buf, PATH_MAX, apk_res) > 0) {
|
||||
LOGD("apk_install: %s", buf);
|
||||
err |= strstr(buf, "Error:") != NULL;
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (err)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
install_apk("/data/magisk.apk");
|
||||
} else {
|
||||
// Check whether we have a valid manager installed
|
||||
sqlite3 *db = get_magiskdb();
|
||||
struct db_strings str;
|
||||
INIT_DB_STRINGS(&str);
|
||||
get_db_strings(db, SU_MANAGER, &str);
|
||||
if (validate_manager(str.s[SU_MANAGER], 0, NULL)) {
|
||||
// There is no manager installed, install the stub
|
||||
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk", NULL);
|
||||
install_apk("/data/magisk.apk");
|
||||
}
|
||||
unlink("/data/magisk.apk");
|
||||
}
|
||||
|
||||
// All boot stage done, cleanup everything
|
||||
@@ -806,6 +841,4 @@ core_only:
|
||||
free(buf2);
|
||||
buf = buf2 = NULL;
|
||||
vec_deep_destroy(&module_list);
|
||||
|
||||
stop_debug_full_log();
|
||||
}
|
||||
|
@@ -21,8 +21,9 @@
|
||||
#include "resetprop.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
int setup_done = 0;
|
||||
int seperate_vendor = 0;
|
||||
int full_patch_pid;
|
||||
int full_patch_pid = -1;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
@@ -82,7 +83,11 @@ static void *request_handler(void *args) {
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
/* Do NOT close the client, make it hold */
|
||||
break;
|
||||
default:
|
||||
close(client);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
@@ -94,30 +99,6 @@ static void *start_magisk_hide(void *args) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void daemon_saver() {
|
||||
int fd, val;
|
||||
struct sockaddr_un sun;
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_saver");
|
||||
|
||||
while (1) {
|
||||
fd = setup_socket(&sun);
|
||||
while(connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000);
|
||||
|
||||
write_int(fd, DO_NOTHING);
|
||||
|
||||
// Should hold forever unless the other side is closed
|
||||
read(fd, &val, sizeof(int));
|
||||
|
||||
// If it came here, the daemon is terminated
|
||||
close(fd);
|
||||
if (fork_dont_care() == 0)
|
||||
start_daemon(0);
|
||||
}
|
||||
}
|
||||
|
||||
void auto_start_magiskhide() {
|
||||
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
@@ -128,46 +109,48 @@ void auto_start_magiskhide() {
|
||||
free(hide_prop);
|
||||
}
|
||||
|
||||
void start_daemon(int post_fs_data) {
|
||||
void main_daemon() {
|
||||
setsid();
|
||||
setcon("u:r:"SEPOL_PROC_DOMAIN":s0");
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
fd = xopen("/dev/zero", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
close(fd);
|
||||
|
||||
if (post_fs_data && fork_dont_care() == 0)
|
||||
daemon_saver();
|
||||
// Start the log monitor
|
||||
loggable = exec_command_sync("/system/bin/logcat", "-d", "-f", "/dev/null", NULL) == 0;
|
||||
if (loggable) {
|
||||
connect_daemon2(LOG_DAEMON, &fd);
|
||||
write_int(fd, HANDSHAKE);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// Block user signals
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun, MAIN_DAEMON);
|
||||
|
||||
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
exit(1);
|
||||
xlisten(fd, 10);
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magiskd");
|
||||
|
||||
// Block all user signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
exit(1);
|
||||
xlisten(fd, 10);
|
||||
|
||||
// Start the log monitor
|
||||
monitor_logs();
|
||||
|
||||
if (!post_fs_data && (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();
|
||||
}
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magiskd");
|
||||
// Ignore SIGPIPE
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
@@ -180,13 +163,11 @@ void start_daemon(int post_fs_data) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon(int post_fs_data) {
|
||||
/* Connect the daemon, set sockfd, and return if new daemon is spawned */
|
||||
int connect_daemon2(daemon_t d, int *sockfd) {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
// If we cannot access the daemon, we start a daemon in the child process if possible
|
||||
|
||||
*sockfd = setup_socket(&sun, d);
|
||||
if (connect(*sockfd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "No daemon is currently running!\n");
|
||||
exit(1);
|
||||
@@ -194,12 +175,26 @@ int connect_daemon(int post_fs_data) {
|
||||
|
||||
if (fork_dont_care() == 0) {
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
close(fd);
|
||||
start_daemon(post_fs_data);
|
||||
close(*sockfd);
|
||||
switch (d) {
|
||||
case MAIN_DAEMON:
|
||||
main_daemon();
|
||||
break;
|
||||
case LOG_DAEMON:
|
||||
log_daemon();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
while (connect(*sockfd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connect_daemon() {
|
||||
int fd;
|
||||
connect_daemon2(MAIN_DAEMON, &fd);
|
||||
return fd;
|
||||
}
|
||||
|
158
native/jni/core/db.c
Normal file
158
native/jni/core/db.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "db.h"
|
||||
|
||||
void INIT_DB_STRINGS(struct db_strings *str) {
|
||||
for (int i = 0; i < DB_STRING_NUM; ++i)
|
||||
str->s[i][0] = '\0';
|
||||
}
|
||||
|
||||
static int policy_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct su_access *su = v;
|
||||
for (int i = 0; i < col_num; i++) {
|
||||
if (strcmp(col_name[i], "policy") == 0)
|
||||
su->policy = (policy_t) atoi(data[i]);
|
||||
else if (strcmp(col_name[i], "logging") == 0)
|
||||
su->log = atoi(data[i]);
|
||||
else if (strcmp(col_name[i], "notification") == 0)
|
||||
su->notify = atoi(data[i]);
|
||||
}
|
||||
LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", su->policy, su->log, su->notify);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int settings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct db_settings *dbs = v;
|
||||
int key = -1, value;
|
||||
for (int i = 0; i < col_num; ++i) {
|
||||
if (strcmp(col_name[i], "key") == 0) {
|
||||
for (int k = 0; k < DB_SETTINGS_NUM; ++k) {
|
||||
if (strcmp(data[i], DB_SETTING_KEYS[k]) == 0)
|
||||
key = k;
|
||||
}
|
||||
} else if (strcmp(col_name[i], "value") == 0) {
|
||||
value = atoi(data[i]);
|
||||
}
|
||||
}
|
||||
if (key >= 0) {
|
||||
dbs->v[key] = value;
|
||||
LOGD("magiskdb: query %s=[%d]\n", DB_SETTING_KEYS[key], value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int strings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct db_strings *dbs = v;
|
||||
int key = -1;
|
||||
char *value;
|
||||
for (int i = 0; i < col_num; ++i) {
|
||||
if (strcmp(col_name[i], "key") == 0) {
|
||||
for (int k = 0; k < DB_STRING_NUM; ++k) {
|
||||
if (strcmp(data[i], DB_STRING_KEYS[k]) == 0)
|
||||
key = k;
|
||||
}
|
||||
} else if (strcmp(col_name[i], "value") == 0) {
|
||||
value = data[i];
|
||||
}
|
||||
}
|
||||
if (key >= 0) {
|
||||
strcpy(dbs->s[key], value);
|
||||
LOGD("magiskdb: query %s=[%s]\n", DB_STRING_KEYS[key], value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3 *get_magiskdb() {
|
||||
sqlite3 *db = NULL;
|
||||
if (access(MAGISKDB, R_OK) == 0) {
|
||||
// Open database
|
||||
int ret = sqlite3_open_v2(MAGISKDB, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (ret) {
|
||||
LOGE("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
|
||||
sqlite3_close(db);
|
||||
db = NULL;
|
||||
}
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs) {
|
||||
if (db == NULL)
|
||||
return 1;
|
||||
char *err;
|
||||
if (key > 0) {
|
||||
char query[128];
|
||||
sprintf(query, "SELECT key, value FROM settings WHERE key=%d", key);
|
||||
sqlite3_exec(db, query, settings_cb, dbs, &err);
|
||||
} else {
|
||||
sqlite3_exec(db, "SELECT key, value FROM settings", settings_cb, dbs, &err);
|
||||
}
|
||||
if (err) {
|
||||
LOGE("sqlite3_exec: %s\n", err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_db_strings(sqlite3 *db, int key, struct db_strings *str) {
|
||||
if (db == NULL)
|
||||
return 1;
|
||||
char *err;
|
||||
if (key > 0) {
|
||||
char query[128];
|
||||
sprintf(query, "SELECT key, value FROM strings WHERE key=%d", key);
|
||||
sqlite3_exec(db, query, strings_cb, str, &err);
|
||||
} else {
|
||||
sqlite3_exec(db, "SELECT key, value FROM strings", strings_cb, str, &err);
|
||||
}
|
||||
if (err) {
|
||||
LOGE("sqlite3_exec: %s\n", err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_uid_policy(sqlite3 *db, int uid, struct su_access *su) {
|
||||
if (db == NULL)
|
||||
return 1;
|
||||
char query[256], *err;
|
||||
sprintf(query, "SELECT policy, logging, notification FROM policies "
|
||||
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(NULL));
|
||||
sqlite3_exec(db, query, policy_cb, su, &err);
|
||||
if (err) {
|
||||
LOGE("sqlite3_exec: %s\n", err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_manager(char *pkg, int userid, struct stat *st) {
|
||||
if (st == NULL) {
|
||||
struct stat stat;
|
||||
st = &stat;
|
||||
}
|
||||
// Prefer DE storage
|
||||
const char *base = access("/data/user_de", F_OK) == 0 ? "/data/user_de" : "/data/user";
|
||||
char app_path[128];
|
||||
sprintf(app_path, "%s/%d/%s", base, userid, pkg[0] ? pkg : "xxx");
|
||||
if (stat(app_path, st)) {
|
||||
// Check the official package name
|
||||
sprintf(app_path, "%s/%d/"JAVA_PACKAGE_NAME, base, userid);
|
||||
if (stat(app_path, st)) {
|
||||
LOGE("su: cannot find manager");
|
||||
memset(st, 0, sizeof(*st));
|
||||
pkg[0] = '\0';
|
||||
return 1;
|
||||
} else {
|
||||
// Switch to official package if exists
|
||||
strcpy(pkg, JAVA_PACKAGE_NAME);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
166
native/jni/core/log_daemon.c
Normal file
166
native/jni/core/log_daemon.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/* log_daemon.c - A dedicated daemon to monitor logcat
|
||||
*
|
||||
* A universal logcat monitor for many usages. Add listeners to the list,
|
||||
* and the new log line will be sent through sockets to trigger
|
||||
* asynchronous events without polling
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
|
||||
int loggable = 1;
|
||||
static struct vector log_cmd, clear_cmd;
|
||||
static int sockfd;
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
enum {
|
||||
HIDE_EVENT,
|
||||
LOG_EVENT
|
||||
};
|
||||
|
||||
struct log_listener {
|
||||
int fd;
|
||||
int (*filter) (const char*);
|
||||
};
|
||||
|
||||
static int am_proc_start_filter(const char *log) {
|
||||
return strstr(log, "am_proc_start") != NULL;
|
||||
}
|
||||
|
||||
static int magisk_log_filter(const char *log) {
|
||||
return !am_proc_start_filter(log);
|
||||
}
|
||||
|
||||
static struct log_listener events[] = {
|
||||
{ /* HIDE_EVENT */
|
||||
.fd = -1,
|
||||
.filter = am_proc_start_filter
|
||||
},
|
||||
{ /* LOG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_log_filter
|
||||
}
|
||||
};
|
||||
#define EVENT_NUM (sizeof(events) / sizeof(struct log_listener))
|
||||
|
||||
static void sigpipe_handler(int sig) {
|
||||
close(events[HIDE_EVENT].fd);
|
||||
events[HIDE_EVENT].fd = -1;
|
||||
}
|
||||
|
||||
static void *monitor_thread(void *args) {
|
||||
// Block SIGPIPE to prevent interruption
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGPIPE);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
// Give the main daemon some time before we monitor it
|
||||
sleep(5);
|
||||
int fd;
|
||||
char b;
|
||||
while (1) {
|
||||
fd = connect_daemon();
|
||||
write_int(fd, HANDSHAKE);
|
||||
// This should hold unless the daemon is killed
|
||||
read(fd, &b, sizeof(b));
|
||||
// The main daemon crashed, spawn a new one
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void *logcat_thread(void *args) {
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
while (1) {
|
||||
// Start logcat
|
||||
log_pid = exec_array(0, &log_fd, NULL, (char **) vec_entry(&log_cmd));
|
||||
FILE *logs = fdopen(log_fd, "r");
|
||||
while (fgets(line, sizeof(line), logs)) {
|
||||
if (line[0] == '-')
|
||||
continue;
|
||||
size_t len = strlen(line);
|
||||
pthread_mutex_lock(&lock);
|
||||
for (int i = 0; i < EVENT_NUM; ++i) {
|
||||
if (events[i].fd > 0 && events[i].filter(line))
|
||||
write(events[i].fd, line, len);
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
fclose(logs);
|
||||
log_fd = -1;
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
|
||||
LOGI("magisklogd: logcat output EOF");
|
||||
// Clear buffer
|
||||
log_pid = exec_array(0, NULL, NULL, (char **) vec_entry(&clear_cmd));
|
||||
waitpid(log_pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void log_daemon() {
|
||||
setsid();
|
||||
struct sockaddr_un sun;
|
||||
sockfd = setup_socket(&sun, LOG_DAEMON);
|
||||
if (xbind(sockfd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
exit(1);
|
||||
xlisten(sockfd, 10);
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") logger started\n");
|
||||
strcpy(argv0, "magisklogd");
|
||||
|
||||
// Set SIGPIPE handler
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = sigpipe_handler;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
// Setup log dumps
|
||||
rename(LOGFILE, LOGFILE ".bak");
|
||||
events[LOG_EVENT].fd = xopen(LOGFILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC | O_APPEND, 0644);
|
||||
|
||||
// Construct cmdline
|
||||
vec_init(&log_cmd);
|
||||
vec_push_back(&log_cmd, "/system/bin/logcat");
|
||||
// Test whether these buffers actually works
|
||||
const char* b[] = { "main", "events", "crash" };
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (exec_command_sync("/system/bin/logcat", "-b", b[i], "-d", "-f", "/dev/null", NULL) == 0)
|
||||
vec_push_back_all(&log_cmd, "-b", b[i], NULL);
|
||||
}
|
||||
vec_dup(&log_cmd, &clear_cmd);
|
||||
vec_push_back_all(&log_cmd, "-v", "threadtime", "-s", "am_proc_start", "Magisk", "*:F", NULL);
|
||||
vec_push_back(&log_cmd, NULL);
|
||||
vec_push_back(&clear_cmd, "-c");
|
||||
vec_push_back(&clear_cmd, NULL);
|
||||
|
||||
// Start worker threads
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, monitor_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
xpthread_create(&thread, NULL, logcat_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
|
||||
while(1) {
|
||||
int fd = xaccept4(sockfd, NULL, NULL, SOCK_CLOEXEC);
|
||||
switch(read_int(fd)) {
|
||||
case HIDE_CONNECT:
|
||||
pthread_mutex_lock(&lock);
|
||||
close(events[HIDE_EVENT].fd);
|
||||
events[HIDE_EVENT].fd = fd;
|
||||
pthread_mutex_unlock(&lock);
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
default:
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,193 +0,0 @@
|
||||
/* log_monitor.c - New thread to monitor logcat
|
||||
*
|
||||
* A universal logcat monitor for many usages. Add listeners to the list,
|
||||
* and the pointer of the new log line will be sent through pipes to trigger
|
||||
* asynchronous events without polling
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
int loggable = 1;
|
||||
|
||||
static int am_proc_start_filter(const char *log) {
|
||||
return strstr(log, "am_proc_start") != NULL;
|
||||
}
|
||||
|
||||
static int magisk_log_filter(const char *log) {
|
||||
char *ss;
|
||||
return (ss = strstr(log, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V');
|
||||
}
|
||||
|
||||
static int magisk_debug_log_filter(const char *log) {
|
||||
return strstr(log, "Magisk") != NULL;
|
||||
}
|
||||
|
||||
struct log_listener log_events[] = {
|
||||
{ /* HIDE_EVENT */
|
||||
.fd = -1,
|
||||
.filter = am_proc_start_filter
|
||||
},
|
||||
{ /* LOG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_log_filter
|
||||
},
|
||||
{ /* DEBUG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_debug_log_filter
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
static int debug_log_pid = -1, debug_log_fd = -1;
|
||||
#endif
|
||||
|
||||
static void test_logcat() {
|
||||
int log_fd = -1, log_pid;
|
||||
char buf[1];
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", NULL);
|
||||
if (read(log_fd, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
loggable = 0;
|
||||
LOGD("log_monitor: cannot read from logcat, disable logging");
|
||||
}
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
}
|
||||
|
||||
static void *logger_thread(void *args) {
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
|
||||
LOGD("log_monitor: logger start");
|
||||
|
||||
while (1) {
|
||||
if (!loggable) {
|
||||
// Disable all services
|
||||
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
|
||||
close(log_events[i].fd);
|
||||
log_events[i].fd = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Start logcat
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-b", "main", "-v", "threadtime", "-s", "am_proc_start", "-s", "Magisk", NULL);
|
||||
while (fdgets(line, sizeof(line), log_fd)) {
|
||||
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
|
||||
if (log_events[i].fd > 0 && log_events[i].filter(line)) {
|
||||
char *s = strdup(line);
|
||||
xwrite(log_events[i].fd, &s, sizeof(s));
|
||||
}
|
||||
}
|
||||
if (kill(log_pid, 0))
|
||||
break;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
close(log_fd);
|
||||
log_fd = -1;
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
|
||||
// Clear buffer before restart
|
||||
exec_command_sync("logcat", "-b", "events", "-b", "main", "-c", NULL);
|
||||
|
||||
test_logcat();
|
||||
}
|
||||
|
||||
// Should never be here, but well...
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *magisk_log_thread(void *args) {
|
||||
FILE *log = xfopen(LOGFILE, "w");
|
||||
setbuf(log, NULL);
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
LOGD("log_monitor: magisk log dumper start");
|
||||
|
||||
// Register our listener
|
||||
log_events[LOG_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
|
||||
fprintf(log, "%s", line);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *debug_magisk_log_thread(void *args) {
|
||||
FILE *log = xfopen(DEBUG_LOG, "a");
|
||||
setbuf(log, NULL);
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
LOGD("log_monitor: debug log dumper start");
|
||||
|
||||
// Register our listener
|
||||
log_events[DEBUG_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
|
||||
fprintf(log, "%s", line);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start new threads to monitor logcat and dump to logfile */
|
||||
void monitor_logs() {
|
||||
pthread_t thread;
|
||||
|
||||
test_logcat();
|
||||
|
||||
if (loggable) {
|
||||
// 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
|
||||
if (loggable) {
|
||||
// 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
|
||||
if (debug_log_pid > 0) {
|
||||
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
|
||||
if (loggable) {
|
||||
pthread_t thread;
|
||||
// Start debug thread
|
||||
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -12,7 +12,8 @@
|
||||
|
||||
char *argv0;
|
||||
|
||||
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
|
||||
int (*applet_main[]) (int, char *[]) =
|
||||
{ su_client_main, resetprop_main, magiskhide_main, imgtool_main, NULL };
|
||||
|
||||
int create_links(const char *bin, const char *path) {
|
||||
char self[PATH_MAX], linkpath[PATH_MAX];
|
||||
@@ -33,8 +34,8 @@ static void usage() {
|
||||
fprintf(stderr,
|
||||
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
|
||||
"\n"
|
||||
"Usage: %s [applet [arguments]...]\n"
|
||||
" or: %s [options]...\n"
|
||||
"Usage: magisk [applet [arguments]...]\n"
|
||||
" or: magisk [options]...\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c print current binary version\n"
|
||||
@@ -42,11 +43,6 @@ static void usage() {
|
||||
" -V print running daemon version code\n"
|
||||
" --list list all available applets\n"
|
||||
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n"
|
||||
" --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
|
||||
" --imgsize IMG report ext4 image used/total size\n"
|
||||
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
|
||||
" --mountimg IMG PATH mount IMG to PATH and prints the loop device\n"
|
||||
" --umountimg PATH LOOP unmount PATH and delete LOOP device\n"
|
||||
" --daemon manually start magisk daemon\n"
|
||||
" --[init trigger] start service for init trigger\n"
|
||||
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
|
||||
@@ -56,8 +52,7 @@ static void usage() {
|
||||
"Supported init triggers:\n"
|
||||
" startup, post-fs-data, service\n"
|
||||
"\n"
|
||||
"Supported applets:\n"
|
||||
, argv0, argv0);
|
||||
"Supported applets:\n");
|
||||
|
||||
for (int i = 0; applet[i]; ++i)
|
||||
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
|
||||
@@ -65,102 +60,78 @@ static void usage() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int magisk_main(int argc, char *argv[]) {
|
||||
if (argc < 2)
|
||||
usage();
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-v") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, CHECK_VERSION);
|
||||
char *v = read_string(fd);
|
||||
printf("%s\n", v);
|
||||
free(v);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-V") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, CHECK_VERSION_CODE);
|
||||
printf("%d\n", read_int(fd));
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--install") == 0) {
|
||||
if (argc < 3) usage();
|
||||
if (argc == 3) return create_links(NULL, argv[2]);
|
||||
else return create_links(argv[2], argv[3]);
|
||||
} else if (strcmp(argv[1], "--list") == 0) {
|
||||
for (int i = 0; applet[i]; ++i)
|
||||
printf("%s\n", applet[i]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
|
||||
unlock_blocks();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--restorecon") == 0) {
|
||||
restorecon();
|
||||
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) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, DO_NOTHING);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--startup") == 0) {
|
||||
startup();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS_DATA);
|
||||
return read_int(fd);
|
||||
} else if (strcmp(argv[1], "--service") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, LATE_START);
|
||||
return read_int(fd);
|
||||
}
|
||||
|
||||
// Applets
|
||||
argc--;
|
||||
argv++;
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
if (strcmp(basename(argv[0]), applet[i]) == 0) {
|
||||
strcpy(argv0, basename(argv[0]));
|
||||
return (*applet_main[i])(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
umask(0);
|
||||
argv0 = argv[0];
|
||||
if (strcmp(basename(argv[0]), "magisk") == 0) {
|
||||
if (argc < 2) usage();
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-v") == 0) {
|
||||
int fd = connect_daemon(0);
|
||||
write_int(fd, CHECK_VERSION);
|
||||
char *v = read_string(fd);
|
||||
printf("%s\n", v);
|
||||
free(v);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-V") == 0) {
|
||||
int fd = connect_daemon(0);
|
||||
write_int(fd, CHECK_VERSION_CODE);
|
||||
printf("%d\n", read_int(fd));
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--install") == 0) {
|
||||
if (argc < 3) usage();
|
||||
if (argc == 3) return create_links(NULL, argv[2]);
|
||||
else return create_links(argv[2], argv[3]);
|
||||
} else if (strcmp(argv[1], "--list") == 0) {
|
||||
for (int i = 0; applet[i]; ++i)
|
||||
printf("%s\n", applet[i]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--createimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
int size;
|
||||
sscanf(argv[3], "%d", &size);
|
||||
return create_img(argv[2], size);
|
||||
} else if (strcmp(argv[1], "--imgsize") == 0) {
|
||||
if (argc < 3) usage();
|
||||
int used, total;
|
||||
if (get_img_size(argv[2], &used, &total)) {
|
||||
fprintf(stderr, "Cannot check %s size\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
printf("%d %d\n", used, total);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--resizeimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
int used, total, size;
|
||||
sscanf(argv[3], "%d", &size);
|
||||
if (get_img_size(argv[2], &used, &total)) {
|
||||
fprintf(stderr, "Cannot check %s size\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
if (size <= used) {
|
||||
fprintf(stderr, "Cannot resize smaller than %dM\n", used);
|
||||
return 1;
|
||||
}
|
||||
return resize_img(argv[2], size);
|
||||
} else if (strcmp(argv[1], "--mountimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
char *loop = mount_image(argv[2], argv[3]);
|
||||
if (loop == NULL) {
|
||||
fprintf(stderr, "Cannot mount image!\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("%s\n", loop);
|
||||
free(loop);
|
||||
return 0;
|
||||
}
|
||||
} else if (strcmp(argv[1], "--umountimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
umount_image(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
|
||||
unlock_blocks();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--restorecon") == 0) {
|
||||
fix_filecon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--clone-attr") == 0) {
|
||||
if (argc < 4) usage();
|
||||
clone_attr(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--daemon") == 0) {
|
||||
int fd = connect_daemon(0);
|
||||
close(fd);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--startup") == 0) {
|
||||
startup();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
|
||||
int fd = connect_daemon(1);
|
||||
write_int(fd, POST_FS_DATA);
|
||||
return read_int(fd);
|
||||
} else if (strcmp(argv[1], "--service") == 0) {
|
||||
int fd = connect_daemon(0);
|
||||
write_int(fd, LATE_START);
|
||||
return read_int(fd);
|
||||
} else {
|
||||
if (strcmp(basename(argv0), "magisk.bin") == 0) {
|
||||
if (argc >= 2) {
|
||||
// It's calling applets
|
||||
--argc;
|
||||
++argv;
|
||||
@@ -169,10 +140,12 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Applets
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
if (strcmp(basename(argv[0]), applet[i]) == 0)
|
||||
if (strcmp(basename(argv[0]), applet[i]) == 0) {
|
||||
strcpy(argv0, basename(argv[0]));
|
||||
return (*applet_main[i])(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
|
||||
return 1;
|
||||
// Not an applet
|
||||
return magisk_main(argc, argv);
|
||||
}
|
||||
|
@@ -39,7 +39,9 @@
|
||||
#include <lzma.h>
|
||||
#include <cil/cil.h>
|
||||
|
||||
#include "dump.h"
|
||||
#include "binaries_xz.h"
|
||||
#include "binaries_arch_xz.h"
|
||||
|
||||
#include "magiskrc.h"
|
||||
#include "utils.h"
|
||||
#include "magiskpolicy.h"
|
||||
@@ -55,8 +57,6 @@
|
||||
|
||||
extern policydb_t *policydb;
|
||||
int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL };
|
||||
static char RAND_SOCKET_NAME[sizeof(SOCKET_NAME)];
|
||||
static int SOCKET_OFF = -1;
|
||||
|
||||
struct cmdline {
|
||||
char skip_initramfs;
|
||||
@@ -73,7 +73,7 @@ struct device {
|
||||
|
||||
static void parse_cmdline(struct cmdline *cmd) {
|
||||
// cleanup
|
||||
memset(cmd, 0, sizeof(&cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
|
||||
char cmdline[4096];
|
||||
mkdir("/proc", 0555);
|
||||
@@ -131,7 +131,7 @@ static int setup_block(struct device *dev, const char *partname) {
|
||||
buffer[size] = '\0';
|
||||
close(fd);
|
||||
parse_device(dev, buffer);
|
||||
if (strcmp(dev->partname, partname) == 0) {
|
||||
if (strcasecmp(dev->partname, partname) == 0) {
|
||||
snprintf(dev->path, sizeof(dev->path), "/dev/block/%s", dev->devname);
|
||||
found = 1;
|
||||
break;
|
||||
@@ -321,7 +321,15 @@ static int unxz(const void *buf, size_t size, int fd) {
|
||||
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);
|
||||
int ret = unxz(magisk_xz, sizeof(magisk_xz), fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dump_manager(const char *path, mode_t mode) {
|
||||
unlink(path);
|
||||
int fd = creat(path, mode);
|
||||
int ret = unxz(manager_xz, sizeof(manager_xz), fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
@@ -335,18 +343,21 @@ static int dump_magiskrc(const char *path, mode_t mode) {
|
||||
|
||||
static void patch_socket_name(const char *path) {
|
||||
void *buf;
|
||||
char name[sizeof(MAIN_SOCKET)];
|
||||
size_t size;
|
||||
mmap_rw(path, &buf, &size);
|
||||
if (SOCKET_OFF < 0) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(buf + i, SOCKET_NAME, sizeof(SOCKET_NAME)) == 0) {
|
||||
SOCKET_OFF = i;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
|
||||
gen_rand_str(name, sizeof(name));
|
||||
memcpy(buf + i, name, sizeof(name));
|
||||
i += sizeof(name);
|
||||
}
|
||||
if (memcmp(buf + i, LOG_SOCKET, sizeof(LOG_SOCKET)) == 0) {
|
||||
gen_rand_str(name, sizeof(name));
|
||||
memcpy(buf + i, name, sizeof(name));
|
||||
i += sizeof(name);
|
||||
}
|
||||
}
|
||||
gen_rand_str(RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
|
||||
memcpy(buf + SOCKET_OFF, RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
|
||||
munmap(buf, size);
|
||||
}
|
||||
|
||||
@@ -361,6 +372,8 @@ int main(int argc, char *argv[]) {
|
||||
if (argc > 1 && strcmp(argv[1], "-x") == 0) {
|
||||
if (strcmp(argv[2], "magisk") == 0)
|
||||
return dump_magisk(argv[3], 0755);
|
||||
else if (strcmp(argv[2], "manager") == 0)
|
||||
return dump_manager(argv[3], 0644);
|
||||
else if (strcmp(argv[2], "magiskrc") == 0)
|
||||
return dump_magiskrc(argv[3], 0755);
|
||||
}
|
||||
@@ -418,6 +431,9 @@ int main(int argc, char *argv[]) {
|
||||
* Early Mount
|
||||
* ************/
|
||||
|
||||
int mounted_system = 0;
|
||||
int mounted_vendor = 0;
|
||||
|
||||
// If skip_initramfs or using split policies, we need early mount
|
||||
if (cmd.skip_initramfs || access("/sepolicy", R_OK) != 0) {
|
||||
char partname[32];
|
||||
@@ -444,12 +460,15 @@ int main(int argc, char *argv[]) {
|
||||
xmount("/system_root/system", "/system", NULL, MS_BIND, NULL);
|
||||
} else {
|
||||
xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL);
|
||||
mounted_system = 1;
|
||||
}
|
||||
|
||||
// Mount vendor
|
||||
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot);
|
||||
if (setup_block(&dev, partname) == 0)
|
||||
if (setup_block(&dev, partname) == 0) {
|
||||
xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
mounted_vendor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* *************
|
||||
@@ -480,9 +499,10 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Clean up
|
||||
close(root);
|
||||
if (!cmd.skip_initramfs)
|
||||
if (mounted_system)
|
||||
umount("/system");
|
||||
umount("/vendor");
|
||||
if (mounted_vendor)
|
||||
umount("/vendor");
|
||||
|
||||
// Finally, give control back!
|
||||
execv("/init", argv);
|
||||
|
@@ -8,19 +8,25 @@
|
||||
#include "utils.h"
|
||||
#include "magisk.h"
|
||||
|
||||
static char socket_name[] = SOCKET_NAME; /* Workaround compiler bug pre NDK r13 */
|
||||
|
||||
/* Setup the address and return socket fd */
|
||||
int setup_socket(struct sockaddr_un *sun) {
|
||||
int setup_socket(struct sockaddr_un *sun, daemon_t d) {
|
||||
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
memset(sun, 0, sizeof(*sun));
|
||||
sun->sun_family = AF_LOCAL;
|
||||
sun->sun_path[0] = '\0';
|
||||
memcpy(sun->sun_path + 1, socket_name, sizeof(SOCKET_NAME));
|
||||
const char *name;
|
||||
switch (d) {
|
||||
case MAIN_DAEMON:
|
||||
name = MAIN_SOCKET;
|
||||
break;
|
||||
case LOG_DAEMON:
|
||||
name = LOG_SOCKET;
|
||||
break;
|
||||
}
|
||||
strcpy(sun->sun_path + 1, name);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Receive a file descriptor from a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
|
4
native/jni/external/Android.mk
vendored
4
native/jni/external/Android.mk
vendored
@@ -1,4 +1,4 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
# libsqlite.so (stub)
|
||||
include $(CLEAR_VARS)
|
||||
@@ -175,7 +175,7 @@ LOCAL_SRC_FILES := \
|
||||
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
|
||||
LOCAL_CFLAGS += -DHAVE_CONFIG_H -Wno-implicit-function-declaration
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libsepol.a
|
||||
|
2
native/jni/external/busybox
vendored
2
native/jni/external/busybox
vendored
Submodule native/jni/external/busybox updated: 4f5bf4ad83...47a1fdda34
@@ -8,7 +8,7 @@
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
extern int is_daemon_init;
|
||||
extern int setup_done;
|
||||
extern int seperate_vendor;
|
||||
extern int full_patch_pid;
|
||||
|
||||
@@ -24,7 +24,9 @@ enum {
|
||||
STOP_MAGISKHIDE,
|
||||
ADD_HIDELIST,
|
||||
RM_HIDELIST,
|
||||
LS_HIDELIST
|
||||
LS_HIDELIST,
|
||||
HIDE_CONNECT,
|
||||
HANDSHAKE
|
||||
};
|
||||
|
||||
// Return codes for daemon
|
||||
@@ -39,15 +41,26 @@ enum {
|
||||
HIDE_ITEM_NOT_EXIST,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MAIN_DAEMON,
|
||||
LOG_DAEMON
|
||||
} daemon_t;
|
||||
|
||||
// daemon.c
|
||||
|
||||
void start_daemon(int post_fs_data);
|
||||
int connect_daemon(int post_fs_data);
|
||||
void main_daemon();
|
||||
int connect_daemon();
|
||||
int connect_daemon2(daemon_t d, int *sockfd);
|
||||
void auto_start_magiskhide();
|
||||
|
||||
// log_monitor.c
|
||||
|
||||
extern int loggable;
|
||||
void log_daemon();
|
||||
|
||||
// socket.c
|
||||
|
||||
int setup_socket(struct sockaddr_un *sun);
|
||||
int setup_socket(struct sockaddr_un *sun, daemon_t d);
|
||||
int recv_fd(int sockfd);
|
||||
void send_fd(int sockfd, int fd);
|
||||
int read_int(int fd);
|
||||
@@ -62,7 +75,6 @@ void write_string(int fd, const char* val);
|
||||
void startup();
|
||||
void post_fs_data(int client);
|
||||
void late_start(int client);
|
||||
void fix_filecon();
|
||||
|
||||
/**************
|
||||
* MagiskHide *
|
||||
|
123
native/jni/include/db.h
Normal file
123
native/jni/include/db.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef DB_H
|
||||
#define DB_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/***************
|
||||
* DB Settings *
|
||||
***************/
|
||||
|
||||
#define DB_SETTING_KEYS ((char *[]) { \
|
||||
"root_access", \
|
||||
"multiuser_mode", \
|
||||
"mnt_ns" \
|
||||
})
|
||||
|
||||
#define DB_SETTINGS_NUM (sizeof(DB_SETTING_KEYS) / sizeof(char*))
|
||||
|
||||
// Settings indices
|
||||
enum {
|
||||
ROOT_ACCESS = 0,
|
||||
SU_MULTIUSER_MODE,
|
||||
SU_MNT_NS
|
||||
};
|
||||
|
||||
// Values for root_access
|
||||
enum {
|
||||
ROOT_ACCESS_DISABLED = 0,
|
||||
ROOT_ACCESS_APPS_ONLY,
|
||||
ROOT_ACCESS_ADB_ONLY,
|
||||
ROOT_ACCESS_APPS_AND_ADB
|
||||
};
|
||||
|
||||
// Values for multiuser_mode
|
||||
enum {
|
||||
MULTIUSER_MODE_OWNER_ONLY = 0,
|
||||
MULTIUSER_MODE_OWNER_MANAGED,
|
||||
MULTIUSER_MODE_USER
|
||||
};
|
||||
|
||||
// Values for mnt_ns
|
||||
enum {
|
||||
NAMESPACE_MODE_GLOBAL = 0,
|
||||
NAMESPACE_MODE_REQUESTER,
|
||||
NAMESPACE_MODE_ISOLATE
|
||||
};
|
||||
|
||||
struct db_settings {
|
||||
int v[DB_SETTINGS_NUM];
|
||||
};
|
||||
|
||||
#define DEFAULT_DB_SETTINGS (struct db_settings) { .v = {\
|
||||
ROOT_ACCESS_APPS_AND_ADB, \
|
||||
MULTIUSER_MODE_OWNER_ONLY, \
|
||||
NAMESPACE_MODE_REQUESTER \
|
||||
}}
|
||||
|
||||
/**************
|
||||
* DB Strings *
|
||||
**************/
|
||||
|
||||
#define DB_STRING_KEYS ((char *[]) { \
|
||||
"requester" \
|
||||
})
|
||||
|
||||
#define DB_STRING_NUM (sizeof(DB_STRING_KEYS) / sizeof(char*))
|
||||
|
||||
// Strings indices
|
||||
enum {
|
||||
SU_MANAGER = 0
|
||||
};
|
||||
|
||||
struct db_strings {
|
||||
char s[DB_STRING_NUM][128];
|
||||
};
|
||||
|
||||
void INIT_DB_STRINGS(struct db_strings *str);
|
||||
|
||||
/*************
|
||||
* SU Access *
|
||||
*************/
|
||||
|
||||
typedef enum {
|
||||
QUERY = 0,
|
||||
DENY = 1,
|
||||
ALLOW = 2,
|
||||
} policy_t;
|
||||
|
||||
struct su_access {
|
||||
policy_t policy;
|
||||
int log;
|
||||
int notify;
|
||||
};
|
||||
|
||||
#define DEFAULT_SU_ACCESS (struct su_access) { \
|
||||
.policy = QUERY, \
|
||||
.log = 1, \
|
||||
.notify = 1 \
|
||||
}
|
||||
|
||||
#define SILENT_SU_ACCESS (struct su_access) { \
|
||||
.policy = ALLOW, \
|
||||
.log = 0, \
|
||||
.notify = 0 \
|
||||
}
|
||||
|
||||
#define NO_SU_ACCESS (struct su_access) { \
|
||||
.policy = DENY, \
|
||||
.log = 0, \
|
||||
.notify = 0 \
|
||||
}
|
||||
|
||||
/********************
|
||||
* Public Functions *
|
||||
********************/
|
||||
|
||||
sqlite3 *get_magiskdb();
|
||||
int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs);
|
||||
int get_db_strings(sqlite3 *db, int key, struct db_strings *str);
|
||||
int get_uid_policy(sqlite3 *db, int uid, struct su_access *su);
|
||||
int validate_manager(char *pkg, int userid, struct stat *st);
|
||||
|
||||
#endif //DB_H
|
@@ -46,25 +46,6 @@
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
|
||||
enum {
|
||||
HIDE_EVENT,
|
||||
LOG_EVENT,
|
||||
DEBUG_EVENT
|
||||
};
|
||||
|
||||
struct log_listener {
|
||||
int fd;
|
||||
int (*filter) (const char*);
|
||||
};
|
||||
|
||||
extern struct log_listener log_events[];
|
||||
extern int loggable;
|
||||
|
||||
void monitor_logs();
|
||||
void start_debug_full_log();
|
||||
void stop_debug_full_log();
|
||||
void start_debug_log();
|
||||
|
||||
#endif
|
||||
|
||||
/********************
|
||||
|
@@ -7,7 +7,9 @@
|
||||
#include "logging.h"
|
||||
|
||||
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
|
||||
#define SOCKET_NAME "d30138f2310a9fb9c54a3e0c21f58591"
|
||||
#define MAIN_SOCKET "d30138f2310a9fb9c54a3e0c21f58591"
|
||||
#define LOG_SOCKET "5864cd77f2f8c59b3882e2d35dbf51e4"
|
||||
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
|
||||
|
||||
#ifndef ARG_MAX
|
||||
#define ARG_MAX 4096
|
||||
@@ -16,29 +18,29 @@
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define UNBLOCKFILE "/dev/.magisk.unblock"
|
||||
#define DISABLEFILE "/cache/.disable_magisk"
|
||||
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
|
||||
#define MAGISKTMP "/sbin/.core"
|
||||
#define BLOCKDIR MAGISKTMP "/block"
|
||||
#define MIRRDIR MAGISKTMP "/mirror"
|
||||
#define BBPATH MAGISKTMP "/busybox"
|
||||
#define MOUNTPOINT MAGISKTMP "/img"
|
||||
#define COREDIR MOUNTPOINT "/.core"
|
||||
#define HOSTSFILE COREDIR "/hosts"
|
||||
#define HIDELIST COREDIR "/hidelist"
|
||||
#define SECURE_DIR "/data/adb/"
|
||||
#define MAINIMG SECURE_DIR "magisk.img"
|
||||
#define DATABIN SECURE_DIR "magisk"
|
||||
#define MAGISKDB SECURE_DIR "magisk.db"
|
||||
#define SIMPLEMOUNT SECURE_DIR "magisk_simple"
|
||||
#define DEBUG_LOG SECURE_DIR "magisk_debug.log"
|
||||
#define SECURE_DIR "/data/adb"
|
||||
#define MAINIMG SECURE_DIR "/magisk.img"
|
||||
#define DATABIN SECURE_DIR "/magisk"
|
||||
#define MAGISKDB SECURE_DIR "/magisk.db"
|
||||
#define SIMPLEMOUNT SECURE_DIR "/magisk_simple"
|
||||
#define MANAGERAPK DATABIN "/magisk.apk"
|
||||
#define MAGISKRC "/init.magisk.rc"
|
||||
|
||||
|
||||
// selinuxfs paths
|
||||
#define SELINUX_PATH "/sys/fs/selinux/"
|
||||
#define SELINUX_ENFORCE SELINUX_PATH "enforce"
|
||||
#define SELINUX_POLICY SELINUX_PATH "policy"
|
||||
#define SELINUX_LOAD SELINUX_PATH "load"
|
||||
#define SELINUX_PATH "/sys/fs/selinux"
|
||||
#define SELINUX_ENFORCE SELINUX_PATH "/enforce"
|
||||
#define SELINUX_POLICY SELINUX_PATH "/policy"
|
||||
#define SELINUX_LOAD SELINUX_PATH "/load"
|
||||
#define SELINUX_CONTEXT SELINUX_PATH "/context"
|
||||
|
||||
// split policy paths
|
||||
#define PLAT_POLICY_DIR "/system/etc/selinux/"
|
||||
@@ -52,7 +54,7 @@
|
||||
|
||||
extern char *argv0; /* For changing process name */
|
||||
|
||||
#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL })
|
||||
#define applet ((char *[]) { "su", "resetprop", "magiskhide", "imgtool", NULL })
|
||||
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
|
||||
|
||||
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
|
||||
@@ -64,5 +66,6 @@ int magiskhide_main(int argc, char *argv[]);
|
||||
int magiskpolicy_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
int imgtool_main(int argc, char *argv[]);
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#include "magisk.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
const char magiskrc[] =
|
||||
@@ -10,9 +11,9 @@ const char magiskrc[] =
|
||||
|
||||
"on post-fs-data\n"
|
||||
" load_persist_props\n"
|
||||
" rm /dev/.magisk.unblock\n"
|
||||
" rm "UNBLOCKFILE"\n"
|
||||
" start magisk_startup\n"
|
||||
" wait /dev/.magisk.unblock 10\n"
|
||||
" wait "UNBLOCKFILE" 10\n"
|
||||
" rm /dev/.magisk.unblock\n"
|
||||
"\n"
|
||||
|
||||
|
@@ -83,7 +83,8 @@ void ps(void (*func)(int));
|
||||
void ps_filter_proc_name(const char *filter, void (*func)(int));
|
||||
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_array(int err, int *fd, void (*setenv)(struct vector *), char *const *argv);
|
||||
int exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, ...);
|
||||
int exec_command_sync(char *const argv0, ...);
|
||||
int bind_mount(const char *from, const char *to);
|
||||
void get_client_cred(int fd, struct ucred *cred);
|
||||
@@ -122,7 +123,7 @@ 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);
|
||||
void restorecon();
|
||||
int mmap_ro(const char *filename, void **buf, size_t *size);
|
||||
int mmap_rw(const char *filename, void **buf, size_t *size);
|
||||
void fd_full_read(int fd, void **buf, size_t *size);
|
||||
@@ -133,17 +134,12 @@ void write_zero(int fd, size_t size);
|
||||
|
||||
// img.c
|
||||
|
||||
#define round_size(a) ((((a) / 32) + 2) * 32)
|
||||
#define SOURCE_TMP "/dev/source"
|
||||
#define TARGET_TMP "/dev/target"
|
||||
|
||||
int create_img(const char *img, int size);
|
||||
int get_img_size(const char *img, int *used, int *total);
|
||||
int resize_img(const char *img, int size);
|
||||
char *mount_image(const char *img, const char *target);
|
||||
void umount_image(const char *target, const char *device);
|
||||
int umount_image(const char *target, const char *device);
|
||||
int merge_img(const char *source, const char *target);
|
||||
void trim_img(const char *img);
|
||||
int trim_img(const char *img, const char *mount, char *loop);
|
||||
|
||||
// pattern.c
|
||||
|
||||
|
@@ -14,11 +14,12 @@ struct vector {
|
||||
|
||||
void vec_init(struct vector *v);
|
||||
void vec_push_back(struct vector *v, void *p);
|
||||
void vec_push_back_all(struct vector *v, void *p, ...);
|
||||
void *vec_pop_back(struct vector *v);
|
||||
void vec_sort(struct vector *v, int (*compar)(const void *, const void *));
|
||||
void vec_destroy(struct vector *v);
|
||||
void vec_deep_destroy(struct vector *v);
|
||||
struct vector *vec_dup(struct vector *v);
|
||||
void vec_dup(struct vector *v, struct vector *vv);
|
||||
|
||||
#define vec_size(v) (v)->size
|
||||
#define vec_cap(v) (v)->cap
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libfdt.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
@@ -181,6 +182,29 @@ int parse_img(const char *image, boot_img *boot) {
|
||||
// Search for dtb in kernel
|
||||
for (uint32_t i = 0; i < header(boot, kernel_size); ++i) {
|
||||
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
|
||||
// Check that fdt_header.totalsize does not overflow kernel image size
|
||||
uint32_t dt_size = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 4));
|
||||
if (dt_size > header(boot, kernel_size) - i) {
|
||||
fprintf(stderr, "Invalid DTB detection at 0x%x: size (%u) > remaining (%u)\n",
|
||||
i, dt_size, header(boot, kernel_size) - i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that fdt_header.off_dt_struct does not overflow kernel image size
|
||||
uint32_t dt_struct_offset = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + 8));
|
||||
if (dt_struct_offset > header(boot, kernel_size) - i) {
|
||||
fprintf(stderr, "Invalid DTB detection at 0x%x: struct offset (%u) > remaining (%u)\n",
|
||||
i, dt_struct_offset, header(boot, kernel_size) - i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
|
||||
uint32_t dt_begin_node = fdt32_to_cpu(*(uint32_t *)(boot->kernel + i + dt_struct_offset));
|
||||
if (dt_begin_node != FDT_BEGIN_NODE) {
|
||||
fprintf(stderr, "Invalid DTB detection at 0x%x: header tag of first node != FDT_BEGIN_NODE\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
boot->dtb = boot->kernel + i;
|
||||
boot->dt_size = header(boot, kernel_size) - i;
|
||||
lheader(boot, kernel_size, = i);
|
||||
|
@@ -17,92 +17,92 @@ static void usage(char *arg0) {
|
||||
"Usage: %s <action> [args...]\n"
|
||||
"\n"
|
||||
"Supported actions:\n"
|
||||
" --parse <bootimg>\n"
|
||||
" Parse <bootimg> only, do not unpack. Return values: \n"
|
||||
" --parse <bootimg>\n"
|
||||
" Parse <bootimg> only, do not unpack. Return values: \n"
|
||||
" 0:OK 1:error 2:insufficient boot partition size\n"
|
||||
" 3:chromeos 4:ELF32 5:ELF64\n"
|
||||
"\n"
|
||||
" --unpack <bootimg>\n"
|
||||
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into\n"
|
||||
" the current directory. Return value is the same as --parse\n"
|
||||
" --unpack <bootimg>\n"
|
||||
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb), (extra) into\n"
|
||||
" the current directory. Return value is the same as --parse\n"
|
||||
"\n"
|
||||
" --repack <origbootimg> [outbootimg]\n"
|
||||
" Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"
|
||||
" to [outbootimg], or new-boot.img if not specified.\n"
|
||||
" It will compress ramdisk.cpio with the same method used in <origbootimg>,\n"
|
||||
" or attempt to find ramdisk.cpio.[ext], and repack directly with the\n"
|
||||
" compressed ramdisk file\n"
|
||||
" --repack <origbootimg> [outbootimg]\n"
|
||||
" Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"
|
||||
" to [outbootimg], or new-boot.img if not specified.\n"
|
||||
" It will compress ramdisk.cpio with the same method used in <origbootimg>,\n"
|
||||
" or attempt to find ramdisk.cpio.[ext], and repack directly with the\n"
|
||||
" compressed ramdisk file\n"
|
||||
"\n"
|
||||
" --hexpatch <file> <hexpattern1> <hexpattern2>\n"
|
||||
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
|
||||
" --hexpatch <file> <hexpattern1> <hexpattern2>\n"
|
||||
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
|
||||
"\n"
|
||||
" --cpio <incpio> [commands...]\n"
|
||||
" Do cpio commands to <incpio> (modifications are done directly)\n"
|
||||
" Each command is a single argument, use quotes if necessary\n"
|
||||
" Supported commands:\n"
|
||||
" rm [-r] ENTRY\n"
|
||||
" Remove ENTRY, specify [-r] to remove recursively\n"
|
||||
" mkdir MODE ENTRY\n"
|
||||
" Create directory ENTRY in permissions MODE\n"
|
||||
" ln TARGET ENTRY\n"
|
||||
" Create a symlink to TARGET with the name ENTRY\n"
|
||||
" mv SOURCE DEST\n"
|
||||
" Move SOURCE to DEST\n"
|
||||
" add MODE ENTRY INFILE\n"
|
||||
" Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists\n"
|
||||
" extract [ENTRY OUT]\n"
|
||||
" Extract ENTRY to OUT, or extract all entries to current directory\n"
|
||||
" test\n"
|
||||
" Test the current cpio's patch status\n"
|
||||
" Return value: 0/stock 1/Magisk 2/other (phh, SuperSU, Xposed)\n"
|
||||
" patch KEEPVERITY KEEPFORCEENCRYPT\n"
|
||||
" Ramdisk patches. KEEP**** are boolean values\n"
|
||||
" backup ORIG [SHA1]\n"
|
||||
" Create ramdisk backups from ORIG\n"
|
||||
" SHA1 of stock boot image is optional\n"
|
||||
" restore\n"
|
||||
" Restore ramdisk from ramdisk backup stored within incpio\n"
|
||||
" magisk ORIG HIGHCOMP KEEPVERITY KEEPFORCEENCRYPT [SHA1]\n"
|
||||
" Do Magisk patches and backups all in one step\n"
|
||||
" Create ramdisk backups from ORIG\n"
|
||||
" HIGHCOMP, KEEP**** are boolean values\n"
|
||||
" SHA1 of stock boot image is optional\n"
|
||||
" sha1\n"
|
||||
" Print stock boot SHA1 if previously stored\n"
|
||||
" --cpio <incpio> [commands...]\n"
|
||||
" Do cpio commands to <incpio> (modifications are done directly)\n"
|
||||
" Each command is a single argument, use quotes if necessary\n"
|
||||
" Supported commands:\n"
|
||||
" rm [-r] ENTRY\n"
|
||||
" Remove ENTRY, specify [-r] to remove recursively\n"
|
||||
" mkdir MODE ENTRY\n"
|
||||
" Create directory ENTRY in permissions MODE\n"
|
||||
" ln TARGET ENTRY\n"
|
||||
" Create a symlink to TARGET with the name ENTRY\n"
|
||||
" mv SOURCE DEST\n"
|
||||
" Move SOURCE to DEST\n"
|
||||
" add MODE ENTRY INFILE\n"
|
||||
" Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists\n"
|
||||
" extract [ENTRY OUT]\n"
|
||||
" Extract ENTRY to OUT, or extract all entries to current directory\n"
|
||||
" test\n"
|
||||
" Test the current cpio's patch status\n"
|
||||
" Return value: 0/stock 1/Magisk 2/other (phh, SuperSU, Xposed)\n"
|
||||
" patch KEEPVERITY KEEPFORCEENCRYPT\n"
|
||||
" Ramdisk patches. KEEP**** are boolean values\n"
|
||||
" backup ORIG [SHA1]\n"
|
||||
" Create ramdisk backups from ORIG\n"
|
||||
" SHA1 of stock boot image is optional\n"
|
||||
" restore\n"
|
||||
" Restore ramdisk from ramdisk backup stored within incpio\n"
|
||||
" magisk ORIG HIGHCOMP KEEPVERITY KEEPFORCEENCRYPT [SHA1]\n"
|
||||
" Do Magisk patches and backups all in one step\n"
|
||||
" Create ramdisk backups from ORIG\n"
|
||||
" HIGHCOMP, KEEP**** are boolean values\n"
|
||||
" SHA1 of stock boot image is optional\n"
|
||||
" sha1\n"
|
||||
" Print stock boot SHA1 if previously stored\n"
|
||||
"\n"
|
||||
" --dtb-<cmd> <dtb>\n"
|
||||
" Do dtb related cmds to <dtb> (modifications are done directly)\n"
|
||||
" Supported commands:\n"
|
||||
" dump\n"
|
||||
" Dump all contents from dtb for debugging\n"
|
||||
" test\n"
|
||||
" Check if fstab has verity/avb flags\n"
|
||||
" Return value: 0/no flags 1/flag exists\n"
|
||||
" patch\n"
|
||||
" Search for fstab and remove verity/avb\n"
|
||||
" --dtb-<cmd> <dtb>\n"
|
||||
" Do dtb related cmds to <dtb> (modifications are done directly)\n"
|
||||
" Supported commands:\n"
|
||||
" dump\n"
|
||||
" Dump all contents from dtb for debugging\n"
|
||||
" test\n"
|
||||
" Check if fstab has verity/avb flags\n"
|
||||
" Return value: 0/no flags 1/flag exists\n"
|
||||
" patch\n"
|
||||
" Search for fstab and remove verity/avb\n"
|
||||
"\n"
|
||||
" --compress[=method] <infile> [outfile]\n"
|
||||
" Compress <infile> with [method] (default: gzip), optionally to [outfile]\n"
|
||||
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
|
||||
" Supported methods: "
|
||||
" --compress[=method] <infile> [outfile]\n"
|
||||
" Compress <infile> with [method] (default: gzip), optionally to [outfile]\n"
|
||||
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
|
||||
" Supported methods: "
|
||||
, arg0);
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n\n"
|
||||
" --decompress <infile> [outfile]\n"
|
||||
" Detect method and decompress <infile>, optionally to [outfile]\n"
|
||||
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
|
||||
" Supported methods: ");
|
||||
" --decompress <infile> [outfile]\n"
|
||||
" Detect method and decompress <infile>, optionally to [outfile]\n"
|
||||
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
|
||||
" Supported methods: ");
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n\n"
|
||||
" --sha1 <file>\n"
|
||||
" Print the SHA1 checksum for <file>\n"
|
||||
" --sha1 <file>\n"
|
||||
" Print the SHA1 checksum for <file>\n"
|
||||
"\n"
|
||||
" --cleanup\n"
|
||||
" Cleanup the current working directory\n"
|
||||
" --cleanup\n"
|
||||
" Cleanup the current working directory\n"
|
||||
"\n");
|
||||
|
||||
exit(1);
|
||||
|
@@ -17,27 +17,24 @@
|
||||
#include "daemon.h"
|
||||
|
||||
static char *prop_key[] =
|
||||
{ "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
|
||||
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", NULL };
|
||||
{ "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode",
|
||||
"ro.boot.warranty_bit", "ro.warranty_bit", "ro.debuggable", "ro.secure",
|
||||
"ro.build.type", "ro.build.tags", "ro.build.selinux", NULL };
|
||||
|
||||
static char *prop_value[] =
|
||||
{ "green", "1", "enforcing", "0", "0", "0", "1", "user", "release-keys", "0", NULL };
|
||||
|
||||
static int mocked = 0;
|
||||
{ "locked", "green", "1", "enforcing",
|
||||
"0", "0", "0", "1",
|
||||
"user", "release-keys", "0", NULL };
|
||||
|
||||
void manage_selinux() {
|
||||
if (mocked) return;
|
||||
char val[1];
|
||||
char val;
|
||||
int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
|
||||
xxread(fd, val, 1);
|
||||
xxread(fd, &val, sizeof(val));
|
||||
close(fd);
|
||||
// Permissive
|
||||
if (val[0] == '0') {
|
||||
LOGI("hide_daemon: Permissive detected, hide the state\n");
|
||||
|
||||
if (val == '0') {
|
||||
chmod(SELINUX_ENFORCE, 0640);
|
||||
chmod(SELINUX_POLICY, 0440);
|
||||
mocked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +59,10 @@ static void rm_magisk_prop(const char *name, const char *value, void *v) {
|
||||
}
|
||||
}
|
||||
|
||||
static void kill_proc(int pid) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
void clean_magisk_props() {
|
||||
LOGD("hide_utils: Cleaning magisk props\n");
|
||||
getprop_all(rm_magisk_prop, NULL);
|
||||
|
@@ -22,10 +22,6 @@ int hideEnabled = 0;
|
||||
static pthread_t proc_monitor_thread;
|
||||
pthread_mutex_t hide_lock, file_lock;
|
||||
|
||||
void kill_proc(int pid) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
"MagiskHide v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Hide Magisk!\n\n"
|
||||
@@ -135,7 +131,7 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
int fd = connect_daemon(0);
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, req);
|
||||
if (req == ADD_HIDELIST || req == RM_HIDELIST) {
|
||||
write_string(fd, argv[2]);
|
||||
|
@@ -5,9 +5,6 @@
|
||||
|
||||
#define TERM_THREAD SIGUSR1
|
||||
|
||||
// Kill process
|
||||
void kill_proc(int pid);
|
||||
|
||||
// Process monitor
|
||||
void proc_monitor();
|
||||
|
||||
|
@@ -16,22 +16,19 @@
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "daemon.h"
|
||||
#include "utils.h"
|
||||
#include "magiskhide.h"
|
||||
|
||||
static char init_ns[32], zygote_ns[2][32], cache_block[256];
|
||||
static int zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
|
||||
static int sockfd = -1;
|
||||
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void term_thread(int sig) {
|
||||
LOGD("proc_monitor: running cleanup\n");
|
||||
destroy_list();
|
||||
hideEnabled = 0;
|
||||
// Unregister listener
|
||||
log_events[HIDE_EVENT].fd = -1;
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
pipefd[0] = pipefd[1] = -1;
|
||||
close(sockfd);
|
||||
sockfd = -1;
|
||||
pthread_mutex_destroy(&hide_lock);
|
||||
pthread_mutex_destroy(&file_lock);
|
||||
LOGD("proc_monitor: terminating...\n");
|
||||
@@ -40,30 +37,32 @@ static void term_thread(int sig) {
|
||||
|
||||
static int read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||
if (access(path, R_OK) == -1)
|
||||
return 1;
|
||||
xreadlink(path, target, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void store_zygote_ns(int pid) {
|
||||
if (zygote_num == 2) return;
|
||||
do {
|
||||
usleep(500);
|
||||
read_namespace(pid, zygote_ns[zygote_num], 32);
|
||||
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
|
||||
++zygote_num;
|
||||
}
|
||||
|
||||
static void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
static int parse_ppid(int pid) {
|
||||
char stat[512], path[32];
|
||||
int fd, ppid;
|
||||
sprintf(path, "/proc/%d/stat", pid);
|
||||
fd = xopen(path, O_RDONLY);
|
||||
xread(fd, stat, sizeof(stat));
|
||||
close(fd);
|
||||
/* PID COMM STATE PPID ..... */
|
||||
sscanf(stat, "%*d %*s %*c %d", &ppid);
|
||||
return ppid;
|
||||
}
|
||||
|
||||
static void hide_daemon(int pid) {
|
||||
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
|
||||
strcpy(argv0, "hide_daemon");
|
||||
|
||||
char *line, buffer[PATH_MAX];
|
||||
struct vector mount_list;
|
||||
@@ -78,31 +77,9 @@ static void hide_daemon(int pid) {
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Find the cache block name if not found yet
|
||||
if (has_cache && cache_block[0] == '\0') {
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, " /cache ")) {
|
||||
sscanf(line, "%256s", cache_block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strlen(cache_block) == 0)
|
||||
has_cache = 0;
|
||||
}
|
||||
|
||||
// Unmout cache mounts
|
||||
if (has_cache) {
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, cache_block) && (strstr(line, " /system/") || strstr(line, " /vendor/"))) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unmount dummy skeletons, /sbin links
|
||||
// Unmount dummy skeletons and /sbin links
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) {
|
||||
if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
@@ -115,20 +92,19 @@ static void hide_daemon(int pid) {
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Unmount any loop mounts
|
||||
// Unmount everything under /system, /vendor, and loop mounts
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "/dev/block/loop")) {
|
||||
if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mount_list);
|
||||
|
||||
exit:
|
||||
// Send resume signal
|
||||
kill(pid, SIGCONT);
|
||||
// Free up memory
|
||||
vec_destroy(&mount_list);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
@@ -145,103 +121,87 @@ void proc_monitor() {
|
||||
act.sa_handler = term_thread;
|
||||
sigaction(TERM_THREAD, &act, NULL);
|
||||
|
||||
cache_block[0] = '\0';
|
||||
|
||||
// Get the mount namespace of init
|
||||
if (read_namespace(1, init_ns, 32)) {
|
||||
if (access("/proc/1/ns/mnt", F_OK) != 0) {
|
||||
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
|
||||
term_thread(TERM_THREAD);
|
||||
}
|
||||
LOGI("proc_monitor: init ns=%s\n", init_ns);
|
||||
|
||||
// Get the mount namespace of zygote
|
||||
zygote_num = 0;
|
||||
while(!zygote_num) {
|
||||
// Check zygote every 10 ms
|
||||
usleep(10000);
|
||||
ps_filter_proc_name("zygote", store_zygote_ns);
|
||||
}
|
||||
ps_filter_proc_name("zygote64", store_zygote_ns);
|
||||
while(1) {
|
||||
// Connect to the log daemon
|
||||
connect_daemon2(LOG_DAEMON, &sockfd);
|
||||
write_int(sockfd, HIDE_CONNECT);
|
||||
|
||||
switch(zygote_num) {
|
||||
case 1:
|
||||
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]);
|
||||
break;
|
||||
case 2:
|
||||
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]);
|
||||
break;
|
||||
}
|
||||
FILE *log_in = fdopen(sockfd, "r");
|
||||
char buf[4096];
|
||||
while (fgets(buf, sizeof(buf), log_in)) {
|
||||
char *ss = strchr(buf, '[');
|
||||
int pid, ppid, num = 0;
|
||||
char *pos = ss, proc[256], ns[32], pns[32];
|
||||
|
||||
// Register our listener to logcat monitor
|
||||
xpipe2(pipefd, O_CLOEXEC);
|
||||
log_events[HIDE_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *log, *line;; free(log)) {
|
||||
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
|
||||
/* It might be interrupted */
|
||||
log = NULL;
|
||||
continue;
|
||||
}
|
||||
char *ss = strchr(log, '[');
|
||||
int pid, ret, comma = 0;
|
||||
char *pos = ss, processName[256], ns[32];
|
||||
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
++comma;
|
||||
}
|
||||
|
||||
if (comma == 6)
|
||||
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
else
|
||||
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
|
||||
// Allow hiding sub-services of applications
|
||||
char *colon = strchr(processName, ':');
|
||||
if (colon)
|
||||
*colon = '\0';
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
// Restore the colon so we can log the actual process name
|
||||
if (colon)
|
||||
*colon = ':';
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid);
|
||||
|
||||
break;
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
++num;
|
||||
}
|
||||
|
||||
if(sscanf(ss, num == 6 ? "[%*d %d %*d %*d %256s" : "[%*d %d %*d %256s", &pid, proc) != 2)
|
||||
continue;
|
||||
|
||||
// Make sure our target is alive
|
||||
if (kill(pid, 0))
|
||||
continue;
|
||||
|
||||
// Allow hiding sub-services of applications
|
||||
char *colon = strchr(proc, ':');
|
||||
if (colon)
|
||||
*colon = '\0';
|
||||
|
||||
int hide = 0;
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
char *line;
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(proc, line) == 0) {
|
||||
hide = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
if (!hide)
|
||||
continue;
|
||||
|
||||
ppid = parse_ppid(pid);
|
||||
read_namespace(ppid, pns, sizeof(pns));
|
||||
do {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, pns) == 0)
|
||||
usleep(50);
|
||||
else
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1)
|
||||
continue;
|
||||
|
||||
// Restore the colon so we can log the actual process name
|
||||
if (colon)
|
||||
*colon = ':';
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("proc_monitor: %s (PID=[%d] ns=%s)(PPID=[%d] ns=%s)\n",
|
||||
proc, pid, ns + 4, ppid, pns + 4);
|
||||
#else
|
||||
LOGI("proc_monitor: %s\n", proc);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid);
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
// The other end EOF, restart the connection
|
||||
}
|
||||
}
|
||||
|
Submodule native/jni/magiskpolicy updated: c694776162...52a6a7bce8
Submodule native/jni/su updated: 42eab87edc...69b226b005
@@ -15,6 +15,7 @@
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
char **excl_list = NULL;
|
||||
@@ -165,7 +166,6 @@ void cp_afc(const char *source, const char *destination) {
|
||||
xmkdirs(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);
|
||||
@@ -175,16 +175,15 @@ void cp_afc(const char *source, const char *destination) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
setattr(destination, &a);
|
||||
}
|
||||
|
||||
void clone_dir(int src, int dest) {
|
||||
@@ -341,17 +340,22 @@ void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
|
||||
#ifdef SELINUX
|
||||
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
#define UNLABEL_CON "u:object_r:unlabeled:s0"
|
||||
#define SYSTEM_CON "u:object_r:system_file:s0"
|
||||
#define ADB_CON "u:object_r:adb_data_file:s0"
|
||||
#define MAGISK_CON "u:object_r:"SEPOL_FILE_DOMAIN":s0"
|
||||
|
||||
void restorecon(int dirfd) {
|
||||
static void restore_syscon(int dirfd) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int fd;
|
||||
|
||||
char path[PATH_MAX], *con;
|
||||
|
||||
fd_getpath(dirfd, path, sizeof(path));
|
||||
lgetfilecon(path, &con);
|
||||
size_t len = strlen(path);
|
||||
getfilecon(path, &con);
|
||||
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
@@ -361,20 +365,65 @@ void restorecon(int dirfd) {
|
||||
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);
|
||||
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
restore_syscon(fd);
|
||||
close(fd);
|
||||
} else {
|
||||
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
path[len] = '/';
|
||||
strcpy(path + len + 1, entry->d_name);
|
||||
lgetfilecon(path, &con);
|
||||
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
path[len] = '\0';
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_magiskcon(int dirfd) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
|
||||
char path[PATH_MAX];
|
||||
|
||||
fd_getpath(dirfd, path, sizeof(path));
|
||||
size_t len = strlen(path);
|
||||
lsetfilecon(path, MAGISK_CON);
|
||||
lchown(path, 0, 0);
|
||||
|
||||
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) {
|
||||
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
restore_magiskcon(fd);
|
||||
close(fd);
|
||||
} else {
|
||||
path[len] = '/';
|
||||
strcpy(path + len + 1, entry->d_name);
|
||||
lsetfilecon(path, MAGISK_CON);
|
||||
lchown(path, 0, 0);
|
||||
path[len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void restorecon() {
|
||||
int fd;
|
||||
fd = xopen(SELINUX_CONTEXT, O_WRONLY | O_CLOEXEC);
|
||||
if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0) {
|
||||
lsetfilecon(SECURE_DIR, ADB_CON);
|
||||
}
|
||||
close(fd);
|
||||
fd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
|
||||
restore_syscon(fd);
|
||||
close(fd);
|
||||
fd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
|
||||
restore_magiskcon(fd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif // SELINUX
|
||||
|
||||
static int _mmap(int rw, const char *filename, void **buf, size_t *size) {
|
||||
|
@@ -8,169 +8,196 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/loop.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int e2fsck(const char *img) {
|
||||
// Check and repair ext4 image
|
||||
char buffer[128];
|
||||
int pid, fd = -1;
|
||||
pid = exec_command(1, &fd, NULL, "/system/bin/e2fsck", "-yf", img, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
waitpid(pid, NULL, 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
#define round_size(a) ((((a) / 32) + 2) * 32)
|
||||
#define SOURCE_TMP "/dev/.img_src"
|
||||
#define TARGET_TMP "/dev/.img_tgt"
|
||||
#define MERGE_TMP "/dev/.img_mrg"
|
||||
|
||||
struct fs_info {
|
||||
unsigned size;
|
||||
unsigned free;
|
||||
unsigned used;
|
||||
};
|
||||
|
||||
static char *loopsetup(const char *img) {
|
||||
char device[20];
|
||||
char device[32];
|
||||
struct loop_info64 info;
|
||||
int i, lfd, ffd;
|
||||
int lfd = -1, ffd;
|
||||
memset(&info, 0, sizeof(info));
|
||||
// First get an empty loop device
|
||||
for (i = 0; i <= 7; ++i) {
|
||||
sprintf(device, "/dev/block/loop%d", i);
|
||||
lfd = xopen(device, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
|
||||
break;
|
||||
close(lfd);
|
||||
if (access(BLOCKDIR, F_OK) == 0) {
|
||||
for (int i = 8; i < 100; ++i) {
|
||||
sprintf(device, BLOCKDIR "/loop%02d", i);
|
||||
if (access(device, F_OK) != 0)
|
||||
mknod(device, S_IFBLK | 0600, makedev(7, i * 8));
|
||||
lfd = open(device, O_RDWR);
|
||||
if (lfd < 0) /* Kernel does not support this */
|
||||
break;
|
||||
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
|
||||
break;
|
||||
close(lfd);
|
||||
lfd = -1;
|
||||
}
|
||||
}
|
||||
if (i == 8) return NULL;
|
||||
// Fallback to existing loop in dev, but in reverse order
|
||||
if (lfd < 0) {
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
sprintf(device, "/dev/block/loop%d", i);
|
||||
lfd = xopen(device, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
|
||||
break;
|
||||
close(lfd);
|
||||
lfd = -1;
|
||||
}
|
||||
}
|
||||
if (lfd < 0)
|
||||
return NULL;
|
||||
ffd = xopen(img, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
|
||||
return NULL;
|
||||
strcpy((char *) info.lo_file_name, img);
|
||||
strncpy((char *) info.lo_file_name, img, sizeof(info.lo_file_name));
|
||||
ioctl(lfd, LOOP_SET_STATUS64, &info);
|
||||
close(lfd);
|
||||
close(ffd);
|
||||
return strdup(device);
|
||||
}
|
||||
|
||||
static void check_filesystem(struct fs_info *info, const char *img, const char *mount) {
|
||||
struct stat st;
|
||||
struct statvfs vfs;
|
||||
stat(img, &st);
|
||||
statvfs(mount, &vfs);
|
||||
info->size = st.st_size / 1048576;
|
||||
info->free = vfs.f_bfree * vfs.f_frsize / 1048576;
|
||||
info->used = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize / 1048576;
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr,
|
||||
"ImgTool v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - EXT4 Image Tools\n"
|
||||
"\n"
|
||||
"Usage: imgtool <action> [args...]\n"
|
||||
"\n"
|
||||
"Actions:\n"
|
||||
" create IMG SIZE create ext4 image. SIZE is interpreted in MB\n"
|
||||
" resize IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
|
||||
" mount IMG PATH mount IMG to PATH and prints the loop device\n"
|
||||
" umount PATH LOOP unmount PATH and delete LOOP device\n"
|
||||
" merge SRC TGT merge SRC to TGT\n"
|
||||
" trim IMG trim IMG to save space\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int imgtool_main(int argc, char *argv[]) {
|
||||
if (argc < 2)
|
||||
usage();
|
||||
if (strcmp(argv[1], "create") == 0) {
|
||||
if (argc < 4)
|
||||
usage();
|
||||
return create_img(argv[2], atoi(argv[3]));
|
||||
} else if (strcmp(argv[1], "resize") == 0) {
|
||||
if (argc < 4)
|
||||
usage();
|
||||
return resize_img(argv[2], atoi(argv[3]));
|
||||
} else if (strcmp(argv[1], "mount") == 0) {
|
||||
if (argc < 4)
|
||||
usage();
|
||||
// Redirect 1 > /dev/null
|
||||
int fd = open("/dev/null", O_WRONLY);
|
||||
int out = dup(STDOUT_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
char *loop = mount_image(argv[2], argv[3]);
|
||||
// Restore stdin
|
||||
xdup2(out, STDOUT_FILENO);
|
||||
close(fd);
|
||||
close(out);
|
||||
if (loop == NULL) {
|
||||
fprintf(stderr, "Cannot mount image!\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("%s\n", loop);
|
||||
free(loop);
|
||||
return 0;
|
||||
}
|
||||
} else if (strcmp(argv[1], "umount") == 0) {
|
||||
if (argc < 4)
|
||||
usage();
|
||||
umount_image(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "merge") == 0) {
|
||||
if (argc < 4)
|
||||
usage();
|
||||
return merge_img(argv[2], argv[3]);
|
||||
} else if (strcmp(argv[1], "trim") == 0) {
|
||||
if (argc < 3)
|
||||
usage();
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
char *loop = mount_image(argv[2], SOURCE_TMP);
|
||||
int ret = trim_img(argv[2], SOURCE_TMP, loop);
|
||||
umount_image(SOURCE_TMP, loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
free(loop);
|
||||
return ret;
|
||||
}
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int create_img(const char *img, int size) {
|
||||
if (size == 128) /* WTF...? */
|
||||
size = 132;
|
||||
unlink(img);
|
||||
LOGI("Create %s with size %dM\n", img, size);
|
||||
int ret;
|
||||
|
||||
char size_str[16];
|
||||
snprintf(size_str, sizeof(size_str), "%dM", size);
|
||||
ret = exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL);
|
||||
if (ret < 0) {
|
||||
if (access("/system/bin/make_ext4fs", X_OK) == 0)
|
||||
return exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL);
|
||||
else if (access("/system/bin/mke2fs", X_OK) == 0)
|
||||
// On Android P there is no make_ext4fs, use mke2fs
|
||||
ret = exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_img_size(const char *img, int *used, int *total) {
|
||||
if (access(img, R_OK) == -1)
|
||||
return exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
|
||||
else
|
||||
return 1;
|
||||
char buffer[PATH_MAX];
|
||||
int pid, fd = -1, status = 1;
|
||||
pid = exec_command(1, &fd, NULL, "/system/bin/e2fsck", "-n", img, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd)) {
|
||||
if (strstr(buffer, img)) {
|
||||
char *tok = strtok(buffer, ",");
|
||||
while(tok != NULL) {
|
||||
if (strstr(tok, "blocks")) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
tok = strtok(NULL, ",");
|
||||
}
|
||||
if (status) continue;
|
||||
sscanf(tok, "%d/%d", used, total);
|
||||
*used = (*used + 255) / 256;
|
||||
*total = (*total + 128) / 256;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
waitpid(pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resize_img(const char *img, int size) {
|
||||
LOGI("Resize %s to %dM\n", img, size);
|
||||
if (e2fsck(img))
|
||||
return 1;
|
||||
char buffer[128];
|
||||
int pid, fd = -1, used, total;
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
pid = exec_command(1, &fd, NULL, "/system/bin/resize2fs", img, buffer, NULL);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
close(fd);
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
// Double check our image size
|
||||
get_img_size(img, &used, &total);
|
||||
if (total != size) {
|
||||
// Sammy crap occurs or resize2fs failed, lets create a new image!
|
||||
char *dir = dirname(img);
|
||||
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
|
||||
create_img(buffer, size);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(img, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(buffer, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
cp_afc(SOURCE_TMP, TARGET_TMP);
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
rename(buffer, img);
|
||||
}
|
||||
|
||||
return 0;
|
||||
exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
|
||||
char ss[16];
|
||||
snprintf(ss, sizeof(ss), "%dM", size);
|
||||
return exec_command_sync("/system/bin/resize2fs", img, ss, NULL);
|
||||
}
|
||||
|
||||
char *mount_image(const char *img, const char *target) {
|
||||
if (access(img, F_OK) == -1)
|
||||
if (access(img, F_OK) == -1 || access(target, F_OK) == -1)
|
||||
return NULL;
|
||||
if (access(target, F_OK) == -1) {
|
||||
if (xmkdirs(target, 0755) == -1) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdirs(target, 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (e2fsck(img))
|
||||
return NULL;
|
||||
|
||||
exec_command_sync("/system/bin/e2fsck", "-yf", img, NULL);
|
||||
char *device = loopsetup(img);
|
||||
if (device)
|
||||
xmount(device, target, "ext4", 0, NULL);
|
||||
return device;
|
||||
}
|
||||
|
||||
void umount_image(const char *target, const char *device) {
|
||||
xumount(target);
|
||||
int umount_image(const char *target, const char *device) {
|
||||
int ret = 0;
|
||||
ret |= xumount(target);
|
||||
int fd = xopen(device, O_RDWR);
|
||||
ioctl(fd, LOOP_CLR_FD);
|
||||
ret |= ioctl(fd, LOOP_CLR_FD);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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) {
|
||||
LOGI("* Move %s -> %s\n", source, target);
|
||||
if (rename(source, target) < 0) {
|
||||
// Copy and remove
|
||||
int tgt = creat(target, 0644);
|
||||
@@ -183,24 +210,22 @@ int merge_img(const char *source, const char *target) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
// resize target to worst case
|
||||
int s_used, s_total, t_used, t_total, n_total;
|
||||
get_img_size(source, &s_used, &s_total);
|
||||
get_img_size(target, &t_used, &t_total);
|
||||
n_total = round_size(s_used + t_used);
|
||||
if (n_total > t_total)
|
||||
resize_img(target, n_total);
|
||||
char buf[PATH_MAX];
|
||||
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
xmkdir(TARGET_TMP, 0755);
|
||||
char *s_loop, *t_loop;
|
||||
char *s_loop, *t_loop, *m_loop;
|
||||
s_loop = mount_image(source, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
if (s_loop == NULL)
|
||||
return 1;
|
||||
t_loop = mount_image(target, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
if (t_loop == NULL)
|
||||
return 1;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", SOURCE_TMP, "lost+found");
|
||||
rm_rf(buf);
|
||||
snprintf(buf, sizeof(buf), "%s/%s", TARGET_TMP, "lost+found");
|
||||
rm_rf(buf);
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if (!(dir = xopendir(SOURCE_TMP)))
|
||||
@@ -209,37 +234,60 @@ int merge_img(const char *source, const char *target) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
strcmp(entry->d_name, ".core") == 0)
|
||||
continue;
|
||||
// Cleanup old module if exists
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buffer, F_OK) == 0) {
|
||||
LOGI("Upgrade module: %s\n", entry->d_name);
|
||||
rm_rf(buffer);
|
||||
} else {
|
||||
LOGI("New module: %s\n", entry->d_name);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buf, F_OK) == 0)
|
||||
rm_rf(buf);
|
||||
LOGI("Upgrade/New module: %s\n", entry->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
cp_afc(SOURCE_TMP, TARGET_TMP);
|
||||
|
||||
struct fs_info src, tgt;
|
||||
check_filesystem(&src, source, SOURCE_TMP);
|
||||
check_filesystem(&tgt, target, TARGET_TMP);
|
||||
snprintf(buf, sizeof(buf), "%s/tmp.img", dirname(target));
|
||||
create_img(buf, round_size(src.used + tgt.used));
|
||||
xmkdir(MERGE_TMP, 0755);
|
||||
m_loop = mount_image(buf, MERGE_TMP);
|
||||
if (m_loop == NULL)
|
||||
return 1;
|
||||
|
||||
LOGI("* Merging %s + %s -> %s", source, target, buf);
|
||||
cp_afc(TARGET_TMP, MERGE_TMP);
|
||||
cp_afc(SOURCE_TMP, MERGE_TMP);
|
||||
|
||||
// Unmount all loop devices
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
umount_image(MERGE_TMP, m_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
rmdir(MERGE_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
free(m_loop);
|
||||
// Cleanup
|
||||
unlink(source);
|
||||
LOGI("* Move %s -> %s", buf, target);
|
||||
rename(buf, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trim_img(const char *img) {
|
||||
int used, total, new_size;
|
||||
get_img_size(img, &used, &total);
|
||||
new_size = round_size(used);
|
||||
if (new_size != total)
|
||||
int trim_img(const char *img, const char *mount, char *loop) {
|
||||
struct fs_info info;
|
||||
check_filesystem(&info, img, mount);
|
||||
int new_size = round_size(info.used);
|
||||
if (info.size > new_size) {
|
||||
umount_image(mount, loop);
|
||||
resize_img(img, new_size);
|
||||
char *loop2 = mount_image(img, mount);
|
||||
if (loop2 == NULL)
|
||||
return 1;
|
||||
strcpy(loop, loop2);
|
||||
free(loop2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -87,6 +87,8 @@ int check_data() {
|
||||
|
||||
/* All the string should be freed manually!! */
|
||||
int file_to_vector(const char* filename, struct vector *v) {
|
||||
if (access(filename, R_OK) != 0)
|
||||
return 1;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
@@ -168,22 +170,38 @@ void ps(void (*func)(int)) {
|
||||
static void (*ps_filter_cb)(int);
|
||||
static const char *ps_filter_pattern;
|
||||
static void proc_name_filter(int pid) {
|
||||
char buf[64];
|
||||
int fd;
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
char buf[128];
|
||||
FILE *f;
|
||||
sprintf(buf, "/proc/%d/comm", pid);
|
||||
if ((f = fopen(buf, "r"))) {
|
||||
fgets(buf, sizeof(buf), f);
|
||||
if (strcmp(buf, ps_filter_pattern) == 0)
|
||||
goto run_cb;
|
||||
} else {
|
||||
// The PID is already killed
|
||||
return;
|
||||
if (fdgets(buf, sizeof(buf), fd) == 0) {
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
|
||||
close(fd);
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
fdgets(buf, sizeof(buf), fd);
|
||||
}
|
||||
if (strcmp(buf, ps_filter_pattern) == 0) {
|
||||
ps_filter_cb(pid);
|
||||
}
|
||||
close(fd);
|
||||
fclose(f);
|
||||
|
||||
sprintf(buf, "/proc/%d/cmdline", pid);
|
||||
f = fopen(buf, "r");
|
||||
fgets(buf, sizeof(buf), f);
|
||||
if (strcmp(basename(buf), ps_filter_pattern) == 0)
|
||||
goto run_cb;
|
||||
fclose(f);
|
||||
|
||||
sprintf(buf, "/proc/%d/exe", pid);
|
||||
if (access(buf, F_OK) != 0)
|
||||
return;
|
||||
xreadlink(buf, buf, sizeof(buf));
|
||||
if (strcmp(basename(buf), ps_filter_pattern) == 0)
|
||||
goto run_cb;
|
||||
|
||||
return;
|
||||
run_cb:
|
||||
ps_filter_cb(pid);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Call func with process name filtered with pattern */
|
||||
@@ -227,9 +245,9 @@ void setup_sighandlers(void (*handler)(int)) {
|
||||
fd == NULL -> Ignore output
|
||||
*fd < 0 -> Open pipe and set *fd to the read end
|
||||
*fd >= 0 -> STDOUT (or STDERR) will be redirected to *fd
|
||||
*cb -> A callback function which runs after fork
|
||||
*setenv -> A callback function which sets up a vector of environment variables
|
||||
*/
|
||||
static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, va_list argv) {
|
||||
int exec_array(int err, int *fd, void (*setenv)(struct vector *), char *const *argv) {
|
||||
int pipefd[2], writeEnd = -1;
|
||||
|
||||
if (fd) {
|
||||
@@ -242,20 +260,12 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
|
||||
}
|
||||
}
|
||||
|
||||
// Collect va_list into vector
|
||||
struct vector args;
|
||||
vec_init(&args);
|
||||
vec_push_back(&args, strdup(argv0));
|
||||
for (void *arg = va_arg(argv, void*); arg; arg = va_arg(argv, void*))
|
||||
vec_push_back(&args, strdup(arg));
|
||||
vec_push_back(&args, NULL);
|
||||
|
||||
// Setup environment
|
||||
char *const *envp;
|
||||
struct vector env;
|
||||
vec_init(&env);
|
||||
if (setupenv) {
|
||||
setupenv(&env);
|
||||
if (setenv) {
|
||||
setenv(&env);
|
||||
envp = (char **) vec_entry(&env);
|
||||
} else {
|
||||
extern char **environ;
|
||||
@@ -269,21 +279,34 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
|
||||
*fd = pipefd[0];
|
||||
close(pipefd[1]);
|
||||
}
|
||||
vec_deep_destroy(&args);
|
||||
vec_deep_destroy(&env);
|
||||
return pid;
|
||||
}
|
||||
|
||||
if (fd) {
|
||||
xdup2(writeEnd, STDOUT_FILENO);
|
||||
if (err) xdup2(writeEnd, STDERR_FILENO);
|
||||
if (err)
|
||||
xdup2(writeEnd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
execvpe(argv0, (char **) vec_entry(&args), envp);
|
||||
PLOGE("execvpe %s", argv0);
|
||||
execvpe(argv[0], argv, envp);
|
||||
PLOGE("execvpe %s", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int v_exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, va_list argv) {
|
||||
// Collect va_list into vector
|
||||
struct vector args;
|
||||
vec_init(&args);
|
||||
vec_push_back(&args, strdup(argv0));
|
||||
for (void *arg = va_arg(argv, void*); arg; arg = va_arg(argv, void*))
|
||||
vec_push_back(&args, strdup(arg));
|
||||
vec_push_back(&args, NULL);
|
||||
int pid = exec_array(err, fd, setenv, (char **) vec_entry(&args));
|
||||
vec_deep_destroy(&args);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int exec_command_sync(char *const argv0, ...) {
|
||||
va_list argv;
|
||||
va_start(argv, argv0);
|
||||
@@ -296,10 +319,10 @@ int exec_command_sync(char *const argv0, ...) {
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...) {
|
||||
int exec_command(int err, int *fd, void (*setenv)(struct vector*), const char *argv0, ...) {
|
||||
va_list argv;
|
||||
va_start(argv, argv0);
|
||||
int pid = v_exec_command(err, fd, setupenv, argv0, argv);
|
||||
int pid = v_exec_command(err, fd, setenv, argv0, argv);
|
||||
va_end(argv);
|
||||
return pid;
|
||||
}
|
||||
|
@@ -59,15 +59,18 @@ void patch_init_rc(void **buf, size_t *size) {
|
||||
}
|
||||
|
||||
int patch_verity(void **buf, uint32_t *size, int patch) {
|
||||
int skip, src_size = *size;
|
||||
int skip, src_size = *size, found = 0;
|
||||
char *src = *buf, *patched = patch ? xcalloc(src_size, 1) : NULL;
|
||||
for (int read = 0, write = 0; read < src_size; ++read, ++write) {
|
||||
if ((skip = check_verity_pattern(src + read)) > 0) {
|
||||
if (!patch)
|
||||
return 1;
|
||||
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
|
||||
if (patch) {
|
||||
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
|
||||
*size -= skip;
|
||||
} else {
|
||||
fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read);
|
||||
}
|
||||
read += skip;
|
||||
*size -= skip;
|
||||
found = 1;
|
||||
}
|
||||
if (patch)
|
||||
patched[write] = src[read];
|
||||
@@ -76,7 +79,7 @@ int patch_verity(void **buf, uint32_t *size, int patch) {
|
||||
free(*buf);
|
||||
*buf = patched;
|
||||
}
|
||||
return 0;
|
||||
return found;
|
||||
}
|
||||
|
||||
void patch_encryption(void **buf, uint32_t *size) {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
@@ -23,6 +24,15 @@ void vec_push_back(struct vector *v, void *p) {
|
||||
++vec_size(v);
|
||||
}
|
||||
|
||||
void vec_push_back_all(struct vector *v, void *p, ...) {
|
||||
va_list argv;
|
||||
va_start(argv, p);
|
||||
vec_push_back(v, p);
|
||||
for (void *arg = va_arg(argv, char*); arg; arg = va_arg(argv, char*))
|
||||
vec_push_back(v, arg);
|
||||
va_end(argv);
|
||||
}
|
||||
|
||||
void *vec_pop_back(struct vector *v) {
|
||||
void *ret = vec_entry(v)[vec_size(v) - 1];
|
||||
--vec_size(v);
|
||||
@@ -73,11 +83,9 @@ void vec_deep_destroy(struct vector *v) {
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
struct vector *vec_dup(struct vector *v) {
|
||||
struct vector *ret = malloc(sizeof(*ret));
|
||||
vec_size(ret) = vec_size(v);
|
||||
vec_cap(ret) = vec_cap(v);
|
||||
vec_entry(v) = malloc(sizeof(void*) * vec_cap(ret));
|
||||
memcpy(vec_entry(ret), vec_entry(v), sizeof(void*) * vec_cap(ret));
|
||||
return ret;
|
||||
void vec_dup(struct vector *v, struct vector *vv) {
|
||||
vec_size(vv) = vec_size(v);
|
||||
vec_cap(vv) = vec_cap(v);
|
||||
vec_entry(vv) = malloc(sizeof(void*) * vec_cap(v));
|
||||
memcpy(vec_entry(vv), vec_entry(v), sizeof(void*) * vec_cap(v));
|
||||
}
|
||||
|
@@ -9,60 +9,99 @@
|
||||
##########################################################################################
|
||||
|
||||
. /tmp/backuptool.functions
|
||||
[ -z $backuptool_ab ] && backuptool_ab=false
|
||||
|
||||
main() {
|
||||
# Magisk binaries
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
initialize() {
|
||||
# This path should work in any cases
|
||||
TMPDIR=/dev/tmp
|
||||
|
||||
mount /data 2>/dev/null
|
||||
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
if [ ! -d $MAGISKBIN ]; then
|
||||
echo "! Cannot find Magisk binaries!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for post addon.d processes to finish
|
||||
sleep 5
|
||||
|
||||
# Load utility functions
|
||||
. $MAGISKBIN/util_functions.sh
|
||||
|
||||
APK=/data/adb/magisk.apk
|
||||
[ -f $APK ] || APK=/data/.magisk/magisk.apk
|
||||
[ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk
|
||||
}
|
||||
|
||||
show_logo() {
|
||||
ui_print "************************"
|
||||
ui_print "* Magisk v$MAGISK_VER addon.d"
|
||||
ui_print "************************"
|
||||
}
|
||||
|
||||
mount_partitions
|
||||
detection() {
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
|
||||
ui_print "- Target image: $BOOTIMAGE"
|
||||
[ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
|
||||
get_flags
|
||||
}
|
||||
|
||||
api_level_arch_detect
|
||||
|
||||
recovery_actions
|
||||
|
||||
remove_system_su
|
||||
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
ui_print "- Found boot image: $BOOTIMAGE"
|
||||
installation() {
|
||||
[ -f $APK ] && eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
|
||||
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
|
||||
|
||||
SOURCEDMODE=true
|
||||
cd $MAGISKBIN
|
||||
|
||||
# Source the boot patcher
|
||||
. $MAGISKBIN/boot_patch.sh "$BOOTIMAGE"
|
||||
. ./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
|
||||
$DATA && mv stock_boot* /data
|
||||
fi
|
||||
|
||||
$KEEPVERITY || patch_dtbo_image
|
||||
|
||||
if [ -f stock_dtbo* ]; then
|
||||
rm -f /data/stock_dtbo* 2>/dev/null
|
||||
$DATA && mv stock_dtbo* /data
|
||||
fi
|
||||
|
||||
cd /
|
||||
recovery_cleanup
|
||||
}
|
||||
|
||||
finalize() {
|
||||
ui_print "- Done"
|
||||
exit 0
|
||||
}
|
||||
|
||||
main_v1() {
|
||||
# Wait for post addon.d processes to finish
|
||||
sleep 5
|
||||
recovery_actions
|
||||
show_logo
|
||||
mount_partitions
|
||||
detection
|
||||
installation
|
||||
recovery_cleanup
|
||||
finalize
|
||||
}
|
||||
|
||||
main_v2() {
|
||||
boot_actions
|
||||
show_logo
|
||||
mount_partitions
|
||||
# Swap the slot
|
||||
if [ ! -z $SLOT ]; then [ $SLOT = _a ] && SLOT=_b || SLOT=_a; fi
|
||||
detection
|
||||
installation
|
||||
finalize
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
backup)
|
||||
# Stub
|
||||
@@ -80,9 +119,15 @@ case "$1" in
|
||||
# Stub
|
||||
;;
|
||||
post-restore)
|
||||
# Get the FD for ui_print
|
||||
OUTFD=`ps | grep -v grep | grep -oE "update(.*)" | cut -d" " -f3`
|
||||
# Run the main function in a parallel subshell
|
||||
(main) &
|
||||
initialize
|
||||
if $backuptool_ab; then
|
||||
# addon.d-v2
|
||||
main_v2
|
||||
else
|
||||
OUTFD=
|
||||
get_outfd
|
||||
# Run in background, hack for addon.d-v1
|
||||
(main_v1) &
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
@@ -4,18 +4,25 @@
|
||||
# Magisk Boot Image Patcher
|
||||
# by topjohnwu
|
||||
#
|
||||
# Usage: sh boot_patch.sh <bootimage>
|
||||
#
|
||||
# The following additional flags can be set in environment variables:
|
||||
# KEEPVERITY, KEEPFORCEENCRYPT, HIGHCOMP
|
||||
#
|
||||
# This script should be placed in a directory with the following files:
|
||||
#
|
||||
# File name type Description
|
||||
# File name Type Description
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# chromeos folder This folder should store all the utilities and keys to sign
|
||||
# (optional) a chromeos device, used in the tablet Pixel C
|
||||
# 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
|
||||
# util_functions.sh script A script which hosts all functions requires for this script
|
||||
# to work properly
|
||||
# magiskinit binary The binary to replace /init, which has the magisk binary embedded
|
||||
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk,
|
||||
# and patch the ramdisk for Magisk support
|
||||
# chromeos folder This folder should store all the utilities and keys to sign
|
||||
# (optional) a chromeos device. Used for 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
|
||||
@@ -27,51 +34,32 @@
|
||||
##########################################################################################
|
||||
|
||||
# Pure bash dirname implementation
|
||||
dirname_wrap() {
|
||||
getdir() {
|
||||
case "$1" in
|
||||
*/*)
|
||||
dir=${1%/*}
|
||||
[ -z $dir ] && echo "/" || echo $dir
|
||||
;;
|
||||
*)
|
||||
echo "."
|
||||
;;
|
||||
*/*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
|
||||
*) echo "." ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Pure bash basename implementation
|
||||
basename_wrap() {
|
||||
echo ${1##*/}
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
# Initialization
|
||||
##########################################################################################
|
||||
|
||||
if [ -z $SOURCEDMODE ]; then
|
||||
# Switch to the location of the script file
|
||||
cd "`dirname_wrap "${BASH_SOURCE:-$0}"`"
|
||||
cd "`getdir "${BASH_SOURCE:-$0}"`"
|
||||
# Load utility functions
|
||||
. ./util_functions.sh
|
||||
fi
|
||||
|
||||
BOOTIMAGE="$1"
|
||||
|
||||
[ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!"
|
||||
|
||||
# Presets
|
||||
# Flags
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=false
|
||||
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
|
||||
[ -z $HIGHCOMP ] && HIGHCOMP=false
|
||||
|
||||
if [ -z $KEEPFORCEENCRYPT ]; then
|
||||
if [ "`getprop ro.crypto.state`" = "encrypted" ]; then
|
||||
KEEPFORCEENCRYPT=true
|
||||
ui_print "- Encrypted data detected, keep forceencrypt"
|
||||
else
|
||||
KEEPFORCEENCRYPT=false
|
||||
fi
|
||||
fi
|
||||
|
||||
chmod -R 755 .
|
||||
|
||||
# Extract magisk if doesn't exist
|
||||
@@ -84,7 +72,7 @@ chmod -R 755 .
|
||||
CHROMEOS=false
|
||||
|
||||
ui_print "- Unpacking boot image"
|
||||
eval $LIB32PFX ./magiskboot --unpack "$BOOTIMAGE"
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
|
||||
case $? in
|
||||
1 )
|
||||
@@ -113,14 +101,14 @@ esac
|
||||
# Test patch status and do restore, after this section, ramdisk.cpio.orig is guaranteed to exist
|
||||
ui_print "- Checking ramdisk status"
|
||||
MAGISK_PATCHED=false
|
||||
eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio test
|
||||
./magiskboot --cpio ramdisk.cpio test
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print "- Stock boot image detected"
|
||||
ui_print "- Backing up stock boot image"
|
||||
SHA1=`eval $LIB32PFX ./magiskboot --sha1 "$BOOTIMAGE" 2>/dev/null`
|
||||
SHA1=`./magiskboot --sha1 "$BOOTIMAGE" 2>/dev/null`
|
||||
STOCKDUMP=stock_boot_${SHA1}.img.gz
|
||||
eval $LIB32PFX ./magiskboot --compress "$BOOTIMAGE" $STOCKDUMP
|
||||
./magiskboot --compress "$BOOTIMAGE" $STOCKDUMP
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
@@ -140,8 +128,8 @@ esac
|
||||
if $MAGISK_PATCHED; then
|
||||
ui_print "- Magisk patched image detected"
|
||||
# Find SHA1 of stock boot image
|
||||
[ -z $SHA1 ] && SHA1=`eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio restore
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
./magiskboot --cpio ramdisk.cpio restore
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
fi
|
||||
|
||||
@@ -156,9 +144,9 @@ fi
|
||||
|
||||
ui_print "- Patching ramdisk"
|
||||
|
||||
eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio \
|
||||
\"add 750 init magiskinit\" \
|
||||
\"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1\"
|
||||
./magiskboot --cpio ramdisk.cpio \
|
||||
"add 750 init magiskinit" \
|
||||
"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1"
|
||||
|
||||
rm -f ramdisk.cpio.orig
|
||||
|
||||
@@ -166,18 +154,19 @@ rm -f ramdisk.cpio.orig
|
||||
# Binary patches
|
||||
##########################################################################################
|
||||
|
||||
if ! $KEEPVERITY && [ -f dtb ]; then
|
||||
eval $LIB32PFX ./magiskboot --dtb-patch dtb && ui_print "- Patching fstab in dtb to remove dm-verity"
|
||||
if ! $KEEPVERITY; then
|
||||
[ -f dtb ] && ./magiskboot --dtb-patch dtb && ui_print "- Removing dm(avb)-verity in dtb"
|
||||
[ -f extra ] && ./magiskboot --dtb-patch extra && ui_print "- Removing dm(avb)-verity in extra-dtb"
|
||||
fi
|
||||
|
||||
if [ -f kernel ]; then
|
||||
# Remove Samsung RKP in stock kernel
|
||||
eval $LIB32PFX ./magiskboot --hexpatch kernel \
|
||||
./magiskboot --hexpatch kernel \
|
||||
49010054011440B93FA00F71E9000054010840B93FA00F7189000054001840B91FA00F7188010054 \
|
||||
A1020054011440B93FA00F7140020054010840B93FA00F71E0010054001840B91FA00F7181010054
|
||||
|
||||
# skip_initramfs -> want_initramfs
|
||||
eval $LIB32PFX ./magiskboot --hexpatch kernel \
|
||||
./magiskboot --hexpatch kernel \
|
||||
736B69705F696E697472616D6673 \
|
||||
77616E745F696E697472616D6673
|
||||
fi
|
||||
@@ -187,9 +176,9 @@ fi
|
||||
##########################################################################################
|
||||
|
||||
ui_print "- Repacking boot image"
|
||||
eval $LIB32PFX ./magiskboot --repack "$BOOTIMAGE" || abort "! Unable to repack boot image!"
|
||||
./magiskboot --repack "$BOOTIMAGE" || abort "! Unable to repack boot image!"
|
||||
|
||||
# Sign chromeos boot
|
||||
$CHROMEOS && sign_chromeos
|
||||
|
||||
eval $LIB32PFX ./magiskboot --cleanup
|
||||
./magiskboot --cleanup
|
||||
|
@@ -48,24 +48,19 @@ ui_print "************************"
|
||||
is_mounted /data || mount /data || is_mounted /cache || mount /cache || abort "! Unable to mount partitions"
|
||||
mount_partitions
|
||||
|
||||
# read override variables
|
||||
getvar KEEPVERITY
|
||||
getvar KEEPFORCEENCRYPT
|
||||
getvar BOOTIMAGE
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
ui_print "- Found boot/ramdisk image: $BOOTIMAGE"
|
||||
get_flags
|
||||
|
||||
if [ ! -z $DTBOIMAGE ]; then
|
||||
ui_print "- Found dtbo image: $DTBOIMAGE"
|
||||
# Disable dtbo patch by default
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=true
|
||||
fi
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
|
||||
ui_print "- Target image: $BOOTIMAGE"
|
||||
[ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
|
||||
|
||||
# Detect version and architecture
|
||||
api_level_arch_detect
|
||||
|
||||
[ $API -lt 21 ] && abort "! Magisk is only for Lollipop 5.0+ (SDK 21+)"
|
||||
[ $API -lt 21 ] && abort "! Magisk is only for Lollipop and above (5.0+) (SDK 21+)"
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
|
||||
@@ -81,15 +76,11 @@ remove_system_su
|
||||
|
||||
ui_print "- Constructing environment"
|
||||
|
||||
# Check if we can actually access the data (DE storage)
|
||||
DATA=false
|
||||
if grep ' /data ' /proc/mounts | grep -vq 'tmpfs'; then
|
||||
[ ! -d /data/adb ] && mkdir /data/adb
|
||||
touch /data/adb/.write_test && rm /data/adb/.write_test && DATA=true
|
||||
fi
|
||||
check_data
|
||||
|
||||
if $DATA; then
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
MAGISKBIN=/data/.magisk
|
||||
$DATA_DE && MAGISKBIN=/data/adb/magisk
|
||||
run_migrations
|
||||
else
|
||||
MAGISKBIN=/cache/data_bin
|
||||
@@ -105,7 +96,9 @@ chmod -R 755 $MAGISKBIN
|
||||
if [ -d /system/addon.d ]; then
|
||||
ui_print "- Adding addon.d survival script"
|
||||
mount -o rw,remount /system
|
||||
cp -af $INSTALLER/common/99-magisk.sh /system/addon.d/99-magisk.sh
|
||||
echo "#!/sbin/sh" > /system/addon.d/99-magisk.sh
|
||||
echo "# ADDOND_VERSION=2" >> /system/addon.d/99-magisk.sh
|
||||
echo ". /data/adb/magisk/addon.d.sh" >> /system/addon.d/99-magisk.sh
|
||||
chmod 755 /system/addon.d/99-magisk.sh
|
||||
fi
|
||||
|
||||
@@ -122,7 +115,7 @@ SOURCEDMODE=true
|
||||
cd $MAGISKBIN
|
||||
|
||||
# Source the boot patcher
|
||||
. $COMMONDIR/boot_patch.sh "$BOOTIMAGE"
|
||||
. ./boot_patch.sh "$BOOTIMAGE"
|
||||
|
||||
flash_boot_image new-boot.img "$BOOTIMAGE"
|
||||
rm -f new-boot.img
|
||||
|
@@ -1,52 +1,75 @@
|
||||
#!/system/bin/sh
|
||||
#MAGISK
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Uninstaller
|
||||
# Magisk Uninstaller (used in recovery)
|
||||
# by topjohnwu
|
||||
#
|
||||
# This script can be placed in /cache/magisk_uninstaller.sh
|
||||
# The Magisk main binary will pick up the script, and uninstall itself, following a reboot
|
||||
# This script can also be used in flashable zip with the uninstaller_loader.sh
|
||||
#
|
||||
# This script will try to do restoration with the following:
|
||||
# 1-1. Find and restore the original stock boot image dump (OTA proof)
|
||||
# 1-2. If 1-1 fails, restore ramdisk from the internal backup
|
||||
# (ramdisk fully restored, not OTA friendly)
|
||||
# 1-3. If 1-2 fails, it will remove added files in ramdisk, however modified files
|
||||
# are remained modified, because we have no backups. By doing so, Magisk will
|
||||
# not be started at boot, but this isn't actually 100% cleaned up
|
||||
# 2. Remove all Magisk related files
|
||||
# (The list is LARGE, most likely due to bad decision in early versions
|
||||
# the latest versions has much less bloat to cleanup)
|
||||
# This script will load the real uninstaller in a flashable zip
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
##########################################################################################
|
||||
# Preparation
|
||||
##########################################################################################
|
||||
|
||||
[ -d /data/adb/magisk ] && MAGISKBIN=/data/adb/magisk || MAGISKBIN=/data/magisk
|
||||
CHROMEDIR=$MAGISKBIN/chromeos
|
||||
# This path should work in any cases
|
||||
TMPDIR=/dev/tmp
|
||||
|
||||
if [ ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
|
||||
echo "! Cannot find $MAGISKBIN"
|
||||
INSTALLER=$TMPDIR/install
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
OUTFD=$2
|
||||
ZIP=$3
|
||||
|
||||
if [ ! -f $INSTALLER/util_functions.sh ]; then
|
||||
echo "! Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if $BOOTMODE; then
|
||||
# Load utility functions
|
||||
. $MAGISKBIN/util_functions.sh
|
||||
BOOTMODE=true
|
||||
boot_actions
|
||||
mount_partitions
|
||||
fi
|
||||
# Load utility functions
|
||||
. $INSTALLER/util_functions.sh
|
||||
|
||||
get_outfd
|
||||
|
||||
ui_print "************************"
|
||||
ui_print " Magisk Uninstaller "
|
||||
ui_print "************************"
|
||||
|
||||
is_mounted /data || mount /data || abort "! Unable to mount partitions"
|
||||
is_mounted /cache || mount /cache 2>/dev/null
|
||||
mount_partitions
|
||||
|
||||
api_level_arch_detect
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
MAGISKBIN=$INSTALLER/$ARCH32
|
||||
mv $CHROMEDIR $MAGISKBIN
|
||||
chmod -R 755 $MAGISKBIN
|
||||
|
||||
check_data
|
||||
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
|
||||
$BOOTMODE || recovery_actions
|
||||
|
||||
##########################################################################################
|
||||
# Uninstall
|
||||
##########################################################################################
|
||||
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
|
||||
[ -e $BOOTIMAGE ] || abort "! Unable to detect boot image"
|
||||
ui_print "- Found boot/ramdisk image: $BOOTIMAGE"
|
||||
[ -z $DTBOIMAGE ] || ui_print "- Found dtbo image: $DTBOIMAGE"
|
||||
|
||||
cd $MAGISKBIN
|
||||
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
ui_print "- Found Boot Image: $BOOTIMAGE"
|
||||
CHROMEOS=false
|
||||
|
||||
ui_print "- Unpacking boot image"
|
||||
eval $LIB32PFX ./magiskboot --unpack "$BOOTIMAGE"
|
||||
CHROMEOS=false
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
|
||||
case $? in
|
||||
1 )
|
||||
@@ -58,7 +81,7 @@ case $? in
|
||||
;;
|
||||
4 )
|
||||
ui_print "! Sony ELF32 format detected"
|
||||
abort "! Please use BootBridge from @AdrianDC to flash Magisk"
|
||||
abort "! Please use BootBridge from @AdrianDC"
|
||||
;;
|
||||
5 )
|
||||
ui_print "! Sony ELF64 format detected"
|
||||
@@ -67,26 +90,33 @@ esac
|
||||
|
||||
# Detect boot image state
|
||||
ui_print "- Checking ramdisk status"
|
||||
eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio test
|
||||
./magiskboot --cpio ramdisk.cpio test
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print "- Stock boot image detected"
|
||||
abort "! Magisk is not installed!"
|
||||
;;
|
||||
1|2 ) # Magisk patched
|
||||
ui_print "- Magisk patched image detected"
|
||||
./magisk --unlock-blocks 2>/dev/null
|
||||
# Find SHA1 of stock boot image
|
||||
[ -z $SHA1 ] && SHA1=`eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
OK=false
|
||||
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true
|
||||
if ! $OK; then
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
|
||||
STOCKDTBO=/data/stock_dtbo.img.gz
|
||||
if [ -f $STOCKBOOT ]; then
|
||||
ui_print "- Restoring stock boot image"
|
||||
gzip -d < $STOCKBOOT | cat - /dev/zero > $BOOTIMAGE 2>/dev/null
|
||||
if [ -f $DTBOIMAGE -a -f $STOCKDTBO ]; then
|
||||
ui_print "- Restoring stock dtbo image"
|
||||
gzip -d < $STOCKDTBO > $DTBOIMAGE
|
||||
fi
|
||||
else
|
||||
ui_print "! Boot image backup unavailable"
|
||||
ui_print "- Restoring ramdisk with internal backup"
|
||||
eval $LIB32PFX ./magiskboot --cpio ramdisk.cpio restore
|
||||
eval $LIB32PFX ./magiskboot --repack $BOOTIMAGE
|
||||
./magiskboot --cpio ramdisk.cpio restore
|
||||
./magiskboot --repack $BOOTIMAGE
|
||||
# Sign chromeos boot
|
||||
$CHROMEOS && sign_chromeos
|
||||
flash_boot_image new-boot.img "$BOOTIMAGE"
|
||||
flash_boot_image new-boot.img $BOOTIMAGE
|
||||
fi
|
||||
;;
|
||||
3 ) # Other patched
|
||||
@@ -95,12 +125,33 @@ case $? in
|
||||
;;
|
||||
esac
|
||||
|
||||
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*/*/magisk.db /data/user*/*/com.topjohnwu.magisk /data/user*/*/.tmp.magisk.config \
|
||||
/data/adb/*magisk* 2>/dev/null
|
||||
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* 2>/dev/null
|
||||
|
||||
$BOOTMODE && /system/bin/reboot
|
||||
if [ -f /system/addon.d/99-magisk.sh ]; then
|
||||
mount -o rw,remount /system
|
||||
rm -f /system/addon.d/99-magisk.sh
|
||||
fi
|
||||
|
||||
# Remove persist props (for Android P+ using protobuf)
|
||||
for prop in `./magisk resetprop -p | grep -E 'persist.*magisk' | grep -oE '^\[[a-zA-Z0-9.@:_-]+\]' | tr -d '[]'`; do
|
||||
./magisk resetprop -p --delete $prop
|
||||
done
|
||||
|
||||
cd /
|
||||
|
||||
if $BOOTMODE; then
|
||||
ui_print "**********************************************"
|
||||
ui_print "* Magisk Manager will uninstall itself, and *"
|
||||
ui_print "* the device will reboot after a few seconds *"
|
||||
ui_print "**********************************************"
|
||||
(sleep 8; /system/bin/reboot)&
|
||||
else
|
||||
rm -rf /data/user*/*/*magisk* /data/app/*magisk*
|
||||
recovery_cleanup
|
||||
ui_print "- Done"
|
||||
fi
|
||||
|
||||
rm -rf $TMPDIR
|
||||
exit 0
|
||||
|
@@ -1,80 +0,0 @@
|
||||
#!/sbin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Uninstaller (used in recovery)
|
||||
# by topjohnwu
|
||||
#
|
||||
# This script will load the real uninstaller in a flashable zip
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
##########################################################################################
|
||||
# Preparation
|
||||
##########################################################################################
|
||||
|
||||
BOOTMODE=false
|
||||
# This path should work in any cases
|
||||
TMPDIR=/dev/tmp
|
||||
|
||||
INSTALLER=$TMPDIR/install
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
OUTFD=$2
|
||||
ZIP=$3
|
||||
|
||||
if [ ! -f $INSTALLER/util_functions.sh ]; then
|
||||
echo "! Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load utility functions
|
||||
. $INSTALLER/util_functions.sh
|
||||
get_outfd
|
||||
|
||||
##########################################################################################
|
||||
# Main
|
||||
##########################################################################################
|
||||
|
||||
ui_print "************************"
|
||||
ui_print " Magisk Uninstaller "
|
||||
ui_print "************************"
|
||||
|
||||
is_mounted /data || mount /data || abort "! Unable to mount partitions"
|
||||
is_mounted /cache || mount /cache 2>/dev/null
|
||||
mount_partitions
|
||||
|
||||
api_level_arch_detect
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
BINDIR=$INSTALLER/$ARCH
|
||||
|
||||
##########################################################################################
|
||||
# Detection all done, start installing
|
||||
##########################################################################################
|
||||
|
||||
if is_mounted /data; then
|
||||
recovery_actions
|
||||
# Save our stock boot image dump before removing it
|
||||
mv $MAGISKBIN/stock_boot* /data 2>/dev/null
|
||||
# Copy the binaries to /data/magisk, in case they do not exist
|
||||
rm -rf $MAGISKBIN 2>/dev/null
|
||||
mkdir -p $MAGISKBIN
|
||||
cp -af $BINDIR/. $CHROMEDIR $TMPDIR/bin/busybox $INSTALLER/util_functions.sh $MAGISKBIN
|
||||
chmod -R 755 $MAGISKBIN
|
||||
# Run the actual uninstallation
|
||||
. $INSTALLER/magisk_uninstaller.sh
|
||||
recovery_cleanup
|
||||
else
|
||||
ui_print "! Data unavailable"
|
||||
ui_print "! Placing uninstall script to /cache"
|
||||
ui_print "! The device might reboot multiple times"
|
||||
cp -af $INSTALLER/magisk_uninstaller.sh /cache/magisk_uninstaller.sh
|
||||
ui_print "- Rebooting....."
|
||||
sleep 5
|
||||
reboot
|
||||
fi
|
||||
|
||||
ui_print "- Done"
|
||||
exit 0
|
@@ -1,31 +1,40 @@
|
||||
# EX_ARM, EX_X86, BB_ARM, and BB_X86 should be generated in build.py
|
||||
dirname_wrap() {
|
||||
getdir() {
|
||||
case "$1" in
|
||||
*/*) dir=${1%/*}; [ -z $dir ] && echo "/" || echo $dir ;;
|
||||
*) echo "." ;;
|
||||
esac
|
||||
}
|
||||
[ "$1" = "indep" ] && INDEP=true || INDEP=false
|
||||
$INDEP && TMPDIR="`dirname_wrap "${BASH_SOURCE:-$0}"`" || TMPDIR=/dev/tmp
|
||||
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; touch $BBBIN; chmod 755 $EXBIN $BBBIN
|
||||
echo -ne $EX_ARM > $EXBIN
|
||||
if $EXBIN --test 2>/dev/null; then
|
||||
echo $BB_ARM | $EXBIN > $BBBIN
|
||||
else
|
||||
echo -ne $EX_X86 > $EXBIN
|
||||
echo $BB_X86 | $EXBIN > $BBBIN
|
||||
fi
|
||||
$BBBIN --install -s $TMPDIR/bin
|
||||
export PATH=$BBDIR:$PATH
|
||||
if $INDEP; then
|
||||
shift
|
||||
exec sh "$@"
|
||||
else
|
||||
mkdir -p $INSTALLER
|
||||
unzip -o "$3" -d $INSTALLER
|
||||
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@
|
||||
fi
|
||||
extract_bb() {
|
||||
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox
|
||||
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
|
||||
else
|
||||
echo -ne $EX_X86 > $EXBIN
|
||||
echo $BB_X86 | $EXBIN > $BBBIN
|
||||
fi
|
||||
rm $EXBIN
|
||||
}
|
||||
setup_bb() {
|
||||
BBDIR=$TMPDIR/bin; mkdir -p $BBDIR 2>/dev/null
|
||||
extract_bb
|
||||
$BBBIN --install -s $BBDIR
|
||||
export PATH=$BBDIR:$PATH
|
||||
}
|
||||
case "$1" in
|
||||
"extract")
|
||||
[ -z "$2" ] && BBDIR=. || BBDIR="$2"
|
||||
extract_bb
|
||||
;;
|
||||
"indep")
|
||||
TMPDIR="`getdir "${BASH_SOURCE:-$0}"`"; setup_bb
|
||||
shift; exec sh "$@"
|
||||
;;
|
||||
*)
|
||||
TMPDIR=/dev/tmp; rm -rf $TMPDIR 2>/dev/null; setup_bb
|
||||
INSTALLER=$TMPDIR/install; mkdir -p $INSTALLER; unzip -o "$3" -d $INSTALLER >&2
|
||||
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@
|
||||
;;
|
||||
esac
|
||||
|
@@ -8,7 +8,6 @@
|
||||
##########################################################################################
|
||||
|
||||
#MAGISK_VERSION_STUB
|
||||
SCRIPT_VERSION=$MAGISK_VER_CODE
|
||||
|
||||
# Detect whether in boot mode
|
||||
ps | grep zygote | grep -v grep >/dev/null && BOOTMODE=true || BOOTMODE=false
|
||||
@@ -20,19 +19,15 @@ MAGISKBIN=/data/adb/magisk
|
||||
[ -z $MOUNTPATH ] && MOUNTPATH=/sbin/.core/img
|
||||
[ -z $IMG ] && IMG=/data/adb/magisk.img
|
||||
|
||||
BOOTSIGNER="eval \$LIBPFX /system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.topjohnwu.magisk.utils.BootSigner"
|
||||
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
|
||||
OUTFD=0
|
||||
|
||||
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
|
||||
# We will have to manually find out OUTFD
|
||||
for FD in `ls /proc/$$/fd`; do
|
||||
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
ps | grep " 3 $FD " | grep -v grep >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
if readlink /proc/$$/fd/$FD | grep -q pipe; then
|
||||
if ps | grep -v grep | grep -q " 3 $FD "; then
|
||||
OUTFD=$FD
|
||||
break
|
||||
fi
|
||||
@@ -45,49 +40,102 @@ ui_print() {
|
||||
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
|
||||
}
|
||||
|
||||
toupper() {
|
||||
echo "$@" | tr '[:lower:]' '[:upper:]'
|
||||
}
|
||||
|
||||
find_block() {
|
||||
for BLOCK in "$@"; do
|
||||
DEVICE=`find /dev/block -type l -iname $BLOCK | head -n 1` 2>/dev/null
|
||||
if [ ! -z $DEVICE ]; then
|
||||
readlink -f $DEVICE
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
# Fallback by parsing sysfs uevents
|
||||
for uevent in /sys/dev/block/*/uevent; do
|
||||
local DEVNAME=`grep_prop DEVNAME $uevent`
|
||||
local PARTNAME=`grep_prop PARTNAME $uevent`
|
||||
for p in "$@"; do
|
||||
if [ "`toupper $p`" = "`toupper $PARTNAME`" ]; then
|
||||
echo /dev/block/$DEVNAME
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
mount_partitions() {
|
||||
# Check A/B slot
|
||||
SLOT=`getprop ro.boot.slot_suffix`
|
||||
SLOT=`grep_cmdline androidboot.slot_suffix`
|
||||
if [ -z $SLOT ]; then
|
||||
SLOT=_`getprop ro.boot.slot`
|
||||
SLOT=_`grep_cmdline androidboot.slot`
|
||||
[ $SLOT = "_" ] && SLOT=
|
||||
fi
|
||||
|
||||
# Check the boot image to make sure the slot actually make sense
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
[ -z $SLOT ] || ui_print "- A/B partition detected, current slot: $SLOT"
|
||||
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
|
||||
|
||||
ui_print "- Mounting /system, /vendor"
|
||||
is_mounted /system || [ -f /system/build.prop ] || mount -o ro /system 2>/dev/null
|
||||
[ -f /system/build.prop ] || is_mounted /system || mount -o ro /system 2>/dev/null
|
||||
if ! is_mounted /system && ! [ -f /system/build.prop ]; then
|
||||
SYSTEMBLOCK=`find /dev/block -iname system$SLOT | head -n 1`
|
||||
SYSTEMBLOCK=`find_block system$SLOT`
|
||||
mount -t ext4 -o ro $SYSTEMBLOCK /system
|
||||
fi
|
||||
is_mounted /system || [ -f /system/build.prop ] || abort "! Cannot mount /system"
|
||||
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SKIP_INITRAMFS=true || SKIP_INITRAMFS=false
|
||||
if [ -f /system/init.rc ]; then
|
||||
SKIP_INITRAMFS=true
|
||||
[ -f /system/build.prop ] || is_mounted /system || abort "! Cannot mount /system"
|
||||
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SYSTEM_ROOT=true || SYSTEM_ROOT=false
|
||||
if [ -f /system/init ]; then
|
||||
SYSTEM_ROOT=true
|
||||
mkdir /system_root 2>/dev/null
|
||||
mount --move /system /system_root
|
||||
mount -o bind /system_root/system /system
|
||||
fi
|
||||
$SKIP_INITRAMFS && ui_print "- Device skip_initramfs detected"
|
||||
$SYSTEM_ROOT && ui_print "- Device using system_root_image"
|
||||
if [ -L /system/vendor ]; then
|
||||
# Seperate /vendor partition
|
||||
is_mounted /vendor || mount -o ro /vendor 2>/dev/null
|
||||
if ! is_mounted /vendor; then
|
||||
VENDORBLOCK=`find /dev/block -iname vendor$SLOT | head -n 1`
|
||||
VENDORBLOCK=`find_block vendor$SLOT`
|
||||
mount -t ext4 -o ro $VENDORBLOCK /vendor
|
||||
fi
|
||||
is_mounted /vendor || abort "! Cannot mount /vendor"
|
||||
fi
|
||||
}
|
||||
|
||||
get_flags() {
|
||||
# override variables
|
||||
getvar KEEPVERITY
|
||||
getvar KEEPFORCEENCRYPT
|
||||
HIGHCOMP=false
|
||||
if [ -z $KEEPVERITY ]; then
|
||||
KEEPVERITY=false
|
||||
hardware=`grep_cmdline androidboot.hardware`
|
||||
for hw in taimen walleye; do
|
||||
if [ "$hw" = "$hardware" ]; then
|
||||
KEEPVERITY=true
|
||||
ui_print "- Device on whitelist, keep avb-verity"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ -z $KEEPFORCEENCRYPT ]; then
|
||||
if [ "`getprop ro.crypto.state`" = "encrypted" ]; then
|
||||
KEEPFORCEENCRYPT=true
|
||||
ui_print "- Encrypted data detected, keep forceencrypt"
|
||||
else
|
||||
KEEPFORCEENCRYPT=false
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
grep_cmdline() {
|
||||
local REGEX="s/^$1=//p"
|
||||
sed -E 's/ +/\n/g' /proc/cmdline | sed -n "$REGEX" 2>/dev/null
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
local REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
local FILES=$@
|
||||
[ -z "$FILES" ] && FILES='/system/build.prop'
|
||||
sed -n "$REGEX" $FILES 2>/dev/null | head -n 1
|
||||
}
|
||||
@@ -95,56 +143,28 @@ grep_prop() {
|
||||
getvar() {
|
||||
local VARNAME=$1
|
||||
local VALUE=
|
||||
for DIR in /.backup /dev /data /cache /system; do
|
||||
VALUE=`grep_prop $VARNAME $DIR/.magisk`
|
||||
[ ! -z $VALUE ] && break;
|
||||
done
|
||||
VALUE=`grep_prop $VARNAME /.backup/.magisk /data/.magisk /cache/.magisk /system/.magisk`
|
||||
[ ! -z $VALUE ] && eval $VARNAME=\$VALUE
|
||||
}
|
||||
|
||||
find_boot_image() {
|
||||
BOOTIMAGE=
|
||||
if [ ! -z $SLOT ]; then
|
||||
BOOTIMAGE=`find /dev/block -iname boot$SLOT | head -n 1` 2>/dev/null
|
||||
fi
|
||||
if [ -z $BOOTIMAGE ]; then
|
||||
# The slot info is incorrect...
|
||||
SLOT=
|
||||
for BLOCK in ramdisk 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
|
||||
for FSTAB in /etc/*fstab*; do
|
||||
BOOTIMAGE=`grep -v '#' $FSTAB | grep -E '/boot[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
|
||||
[ ! -z $BOOTIMAGE ] && break
|
||||
done
|
||||
fi
|
||||
[ ! -z $BOOTIMAGE ] && BOOTIMAGE=`readlink -f $BOOTIMAGE`
|
||||
}
|
||||
|
||||
run_migrations() {
|
||||
# Update the broken boot backup
|
||||
if [ -f /data/stock_boot_.img.gz ]; then
|
||||
eval $LIB32PFX $MAGISKBIN/magiskboot --decompress /data/stock_boot_.img.gz /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
|
||||
ui_print "- Migrating boot image backup"
|
||||
SHA1=`eval $LIB32PFX $MAGISKBIN/magiskboot --sha1 /data/stock_boot.img 2>/dev/null`
|
||||
SHA1=`$MAGISKBIN/magiskboot --sha1 /data/stock_boot.img 2>/dev/null`
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
mv /data/stock_boot.img $STOCKDUMP
|
||||
eval $LIB32PFX $MAGISKBIN/magiskboot --compress $STOCKDUMP
|
||||
$MAGISKBIN/magiskboot --compress $STOCKDUMP
|
||||
fi
|
||||
# Move the stock backups
|
||||
if [ -f /data/magisk/stock_boot* ]; then
|
||||
rm -rf /data/stock_boot*
|
||||
mv /data/magisk/stock_boot* /data 2>/dev/null
|
||||
fi
|
||||
if [ -f /data/adb/magisk/stock_boot* ]; then
|
||||
rm -rf /data/stock_boot*
|
||||
mv /data/adb/magisk/stock_boot* /data 2>/dev/null
|
||||
fi
|
||||
# Remove old dbs
|
||||
@@ -152,9 +172,22 @@ run_migrations() {
|
||||
[ -L /data/magisk.img ] || mv /data/magisk.img /data/adb/magisk.img 2>/dev/null
|
||||
}
|
||||
|
||||
find_boot_image() {
|
||||
BOOTIMAGE=
|
||||
if [ ! -z $SLOT ]; then
|
||||
BOOTIMAGE=`find_block boot$SLOT ramdisk$SLOT`
|
||||
else
|
||||
BOOTIMAGE=`find_block boot_a kern-a android_boot kernel boot lnx bootimg`
|
||||
fi
|
||||
if [ -z $BOOTIMAGE ]; then
|
||||
# Lets see what fstabs tells me
|
||||
BOOTIMAGE=`grep -v '#' /etc/*fstab* | grep -E '/boot[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*' | head -n 1`
|
||||
fi
|
||||
}
|
||||
|
||||
flash_boot_image() {
|
||||
# Make sure all blocks are writable
|
||||
eval $LIB32PFX $MAGISKBIN/magisk --unlock-blocks 2>/dev/null
|
||||
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
|
||||
case "$1" in
|
||||
*.gz) COMMAND="gzip -d < '$1'";;
|
||||
*) COMMAND="cat '$1'";;
|
||||
@@ -178,49 +211,27 @@ flash_boot_image() {
|
||||
}
|
||||
|
||||
find_dtbo_image() {
|
||||
DTBOIMAGE=`find /dev/block -iname dtbo$SLOT | head -n 1` 2>/dev/null
|
||||
[ ! -z $DTBOIMAGE ] && DTBOIMAGE=`readlink -f $DTBOIMAGE`
|
||||
DTBOIMAGE=`find_block dtbo$SLOT`
|
||||
}
|
||||
|
||||
patch_dtbo_image() {
|
||||
if [ ! -z $DTBOIMAGE ]; then
|
||||
if eval $LIB32PFX $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
|
||||
ui_print "- Backing up stock dtbo image"
|
||||
eval $LIB32PFX $MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
|
||||
ui_print "- Patching fstab in dtbo to remove avb-verity"
|
||||
eval $LIB32PFX $MAGISKBIN/magiskboot --dtb-patch $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 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
|
||||
eval $LIB32PFX $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
|
||||
eval $LIBPFX ./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
|
||||
./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
|
||||
|
||||
@@ -229,8 +240,7 @@ sign_chromeos() {
|
||||
}
|
||||
|
||||
is_mounted() {
|
||||
TARGET="`readlink -f $1`"
|
||||
cat /proc/mounts | grep " $TARGET " >/dev/null
|
||||
cat /proc/mounts | grep -q " `readlink -f $1` " 2>/dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
@@ -274,12 +284,40 @@ api_level_arch_detect() {
|
||||
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; ARCH32=x86; IS64BIT=true; fi;
|
||||
}
|
||||
|
||||
check_data() {
|
||||
DATA=false
|
||||
DATA_DE=false
|
||||
if grep ' /data ' /proc/mounts | grep -vq 'tmpfs'; then
|
||||
# Test if data is writable
|
||||
touch /data/.rw && rm /data/.rw && DATA=true
|
||||
# Test if DE storage is writable
|
||||
$DATA && [ -d /data/adb ] && touch /data/adb/.rw && rm /data/adb/.rw && DATA_DE=true
|
||||
fi
|
||||
}
|
||||
|
||||
setup_bb() {
|
||||
if [ -x /sbin/.core/busybox/busybox ]; then
|
||||
# Make sure this path is in the front
|
||||
echo $PATH | grep -q '^/sbin/.core/busybox' || export PATH=/sbin/.core/busybox:$PATH
|
||||
elif [ -x $TMPDIR/bin/busybox ]; then
|
||||
# Make sure this path is in the front
|
||||
echo $PATH | grep -q "^$TMPDIR/bin" || export PATH=$TMPDIR/bin:$PATH
|
||||
else
|
||||
# Construct the PATH
|
||||
mkdir -p $TMPDIR/bin
|
||||
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
|
||||
$MAGISKBIN/busybox --install -s $TMPDIR/bin
|
||||
export PATH=$TMPDIR/bin:$PATH
|
||||
fi
|
||||
}
|
||||
|
||||
boot_actions() {
|
||||
if [ ! -d /sbin/.core/mirror/bin ]; then
|
||||
mkdir -p /sbin/.core/mirror/bin
|
||||
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
|
||||
fi
|
||||
MAGISKBIN=/sbin/.core/mirror/bin
|
||||
setup_bb
|
||||
}
|
||||
|
||||
recovery_actions() {
|
||||
@@ -287,23 +325,21 @@ recovery_actions() {
|
||||
mount -o bind /dev/urandom /dev/random
|
||||
# Preserve environment varibles
|
||||
OLD_PATH=$PATH
|
||||
if [ ! -d $TMPDIR/bin ]; then
|
||||
# Add busybox to PATH
|
||||
mkdir -p $TMPDIR/bin
|
||||
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
|
||||
$MAGISKBIN/busybox --install -s $TMPDIR/bin
|
||||
export PATH=$TMPDIR/bin:$PATH
|
||||
fi
|
||||
setup_bb
|
||||
# Temporarily block out all custom recovery binaries/libs
|
||||
mv /sbin /sbin_tmp
|
||||
# Set library paths
|
||||
$IS64BIT && LIBPFX="LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64" || LIBPFX="LD_LIBRARY_PATH=/system/lib:/system/vendor/lib"
|
||||
LIB32PFX="LD_LIBRARY_PATH=/system/lib:/system/vendor/lib"
|
||||
# Unset library paths
|
||||
OLD_LD_LIB=$LD_LIBRARY_PATH
|
||||
OLD_LD_PRE=$LD_PRELOAD
|
||||
unset LD_LIBRARY_PATH
|
||||
unset LD_PRELOAD
|
||||
}
|
||||
|
||||
recovery_cleanup() {
|
||||
mv /sbin_tmp /sbin 2>/dev/null
|
||||
[ -z $OLD_PATH ] || export PATH=$OLD_PATH
|
||||
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
|
||||
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
|
||||
ui_print "- Unmounting partitions"
|
||||
umount -l /system_root 2>/dev/null
|
||||
umount -l /system 2>/dev/null
|
||||
@@ -339,51 +375,55 @@ mktouch() {
|
||||
}
|
||||
|
||||
request_size_check() {
|
||||
reqSizeM=`du -s $1 | cut -f1`
|
||||
reqSizeM=$((reqSizeM / 1024 + 1))
|
||||
reqSizeM=`du -ms $1 | cut -f1`
|
||||
}
|
||||
|
||||
request_zip_size_check() {
|
||||
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int($1 / 1048567 + 1) }'`
|
||||
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
|
||||
}
|
||||
|
||||
image_size_check() {
|
||||
SIZE="`eval $LIB32PFX $MAGISKBIN/magisk --imgsize $IMG`"
|
||||
curUsedM=`echo "$SIZE" | cut -d" " -f1`
|
||||
curSizeM=`echo "$SIZE" | cut -d" " -f2`
|
||||
curFreeM=$((curSizeM - curUsedM))
|
||||
check_filesystem() {
|
||||
curSizeM=`wc -c < $1`
|
||||
curSizeM=$((curSizeM / 1048576))
|
||||
local DF=`df -P $2 | grep $2`
|
||||
curUsedM=`echo $DF | awk '{ print int($3 / 1024) }'`
|
||||
curFreeM=`echo $DF | awk '{ print int($4 / 1024) }'`
|
||||
}
|
||||
|
||||
mount_snippet() {
|
||||
MAGISKLOOP=`$MAGISKBIN/magisk imgtool mount $IMG $MOUNTPATH`
|
||||
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
|
||||
}
|
||||
|
||||
mount_magisk_img() {
|
||||
[ -z reqSizeM ] && reqSizeM=0
|
||||
mkdir -p $MOUNTPATH 2>/dev/null
|
||||
if [ -f "$IMG" ]; then
|
||||
ui_print "- Found $IMG"
|
||||
image_size_check $IMG
|
||||
if [ "$reqSizeM" -gt "$curFreeM" ]; then
|
||||
newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64))
|
||||
mount_snippet
|
||||
check_filesystem $IMG $MOUNTPATH
|
||||
if [ $reqSizeM -gt $curFreeM ]; then
|
||||
newSizeM=$(((curSizeM + reqSizeM - curFreeM) / 32 * 32 + 64))
|
||||
ui_print "- Resizing $IMG to ${newSizeM}M"
|
||||
eval $LIB32PFX $MAGISKBIN/magisk --resizeimg $IMG $newSizeM >&2
|
||||
$MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
|
||||
$MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
|
||||
mount_snippet
|
||||
fi
|
||||
ui_print "- Mount $IMG to $MOUNTPATH"
|
||||
else
|
||||
newSizeM=$((reqSizeM / 32 * 32 + 64));
|
||||
newSizeM=$((reqSizeM / 32 * 32 + 64))
|
||||
ui_print "- Creating $IMG with size ${newSizeM}M"
|
||||
eval $LIB32PFX $MAGISKBIN/magisk --createimg $IMG $newSizeM >&2
|
||||
$MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
|
||||
mount_snippet
|
||||
fi
|
||||
|
||||
ui_print "- Mounting $IMG to $MOUNTPATH"
|
||||
MAGISKLOOP=`eval $LIB32PFX $MAGISKBIN/magisk --mountimg $IMG $MOUNTPATH`
|
||||
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
|
||||
}
|
||||
|
||||
unmount_magisk_img() {
|
||||
eval $LIB32PFX $MAGISKBIN/magisk --umountimg $MOUNTPATH $MAGISKLOOP
|
||||
|
||||
# Shrink the image if possible
|
||||
image_size_check $IMG
|
||||
check_filesystem $IMG $MOUNTPATH
|
||||
newSizeM=$((curUsedM / 32 * 32 + 64))
|
||||
$MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
|
||||
if [ $curSizeM -gt $newSizeM ]; then
|
||||
ui_print "- Shrinking $IMG to ${newSizeM}M"
|
||||
eval $LIB32PFX $MAGISKBIN/magisk --resizeimg $IMG $newSizeM
|
||||
$MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.topjohnwu.snet"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 14
|
||||
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
@@ -15,12 +15,13 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'com.google.android.gms:play-services-safetynet:7.0.0' /* The oldest version */
|
||||
}
|
||||
|
@@ -1,5 +0,0 @@
|
||||
package com.topjohnwu.snet;
|
||||
|
||||
public interface SafetyNetCallback {
|
||||
void onResponse(int responseCode);
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
package com.topjohnwu.snet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -11,18 +9,19 @@ import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.safetynet.SafetyNet;
|
||||
import com.google.android.gms.safetynet.SafetyNetApi;
|
||||
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class SafetyNetHelper
|
||||
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
|
||||
public class SafetyNetHelper implements ISafetyNetHelper, GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener, ResultCallback<SafetyNetApi.AttestationResult> {
|
||||
|
||||
public static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
||||
public static final int CAUSE_NETWORK_LOST = 0x02;
|
||||
@@ -32,36 +31,24 @@ public class SafetyNetHelper
|
||||
public static final int BASIC_PASS = 0x10;
|
||||
public static final int CTS_PASS = 0x20;
|
||||
|
||||
public static final int SNET_EXT_VER = 7;
|
||||
public static final int SNET_EXT_VER = 8;
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private Activity mActivity;
|
||||
private int responseCode;
|
||||
private SafetyNetCallback cb;
|
||||
private String dexPath;
|
||||
private boolean isDarkTheme;
|
||||
private Callback callback;
|
||||
|
||||
public static int getVersion() {
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return SNET_EXT_VER;
|
||||
}
|
||||
|
||||
public SafetyNetHelper(Activity activity, String dexPath, SafetyNetCallback cb) {
|
||||
public SafetyNetHelper(Activity activity, Callback cb) {
|
||||
mActivity = activity;
|
||||
this.cb = cb;
|
||||
this.dexPath = dexPath;
|
||||
responseCode = 0;
|
||||
|
||||
// Get theme
|
||||
try {
|
||||
Context context = activity.getApplicationContext();
|
||||
Field theme = context.getClass().getField("isDarkTheme");
|
||||
isDarkTheme = (boolean) theme.get(context);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
// Entry point to start test
|
||||
@Override
|
||||
public void attest() {
|
||||
// Connect Google Service
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
|
||||
@@ -74,26 +61,15 @@ public class SafetyNetHelper
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
cb.onResponse(i);
|
||||
callback.onResponse(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(@NonNull ConnectionResult result) {
|
||||
Class<? extends Activity> clazz = mActivity.getClass();
|
||||
try {
|
||||
// Use external resources
|
||||
clazz.getMethod("swapResources", String.class, int.class).invoke(mActivity, dexPath,
|
||||
isDarkTheme ? android.R.style.Theme_Material : android.R.style.Theme_Material_Light);
|
||||
try {
|
||||
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
clazz.getMethod("restoreResources").invoke(mActivity);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
cb.onResponse(CONNECTION_FAIL);
|
||||
mActivity.swapResources(CheckSafetyNet.dexPath.getPath());
|
||||
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
|
||||
mActivity.restoreResources();
|
||||
callback.onResponse(CONNECTION_FAIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,28 +79,28 @@ public class SafetyNetHelper
|
||||
new SecureRandom().nextBytes(nonce);
|
||||
|
||||
// Call SafetyNet
|
||||
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
|
||||
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {
|
||||
@Override
|
||||
public void onResult(@NonNull SafetyNetApi.AttestationResult result) {
|
||||
Status status = result.getStatus();
|
||||
try {
|
||||
if (!status.isSuccess()) throw new JSONException("");
|
||||
String json = new String(Base64.decode(
|
||||
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
|
||||
JSONObject decoded = new JSONObject(json);
|
||||
responseCode |= decoded.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
|
||||
responseCode |= decoded.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
|
||||
} catch (JSONException e) {
|
||||
responseCode = RESPONSE_ERR;
|
||||
}
|
||||
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce).setResultCallback(this);
|
||||
}
|
||||
|
||||
// Disconnect
|
||||
mGoogleApiClient.disconnect();
|
||||
@Override
|
||||
public void onResult(SafetyNetApi.AttestationResult result) {
|
||||
int code = 0;
|
||||
try {
|
||||
if (!result.getStatus().isSuccess())
|
||||
throw new JSONException("");
|
||||
String jsonStr = new String(Base64.decode(
|
||||
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
|
||||
JSONObject json = new JSONObject(jsonStr);
|
||||
code |= json.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
|
||||
code |= json.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
|
||||
} catch (JSONException e) {
|
||||
code = RESPONSE_ERR;
|
||||
}
|
||||
|
||||
// Return results
|
||||
cb.onResponse(responseCode);
|
||||
}
|
||||
});
|
||||
// Disconnect
|
||||
mGoogleApiClient.disconnect();
|
||||
|
||||
// Return results
|
||||
callback.onResponse(code);
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ jar {
|
||||
shadowJar {
|
||||
baseName = 'zipsigner'
|
||||
classifier = null
|
||||
version = 2.1
|
||||
version = 2.2
|
||||
}
|
||||
|
||||
buildscript {
|
||||
@@ -23,7 +23,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,6 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.58'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.58'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.59'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.59'
|
||||
}
|
||||
|
@@ -24,10 +24,11 @@ import java.util.zip.ZipFile;
|
||||
* */
|
||||
|
||||
public class JarMap implements Closeable, AutoCloseable {
|
||||
|
||||
private JarFile jarFile;
|
||||
private JarInputStream jis;
|
||||
private boolean isInputStream = false;
|
||||
private LinkedHashMap<String, JarEntry> bufMap;
|
||||
private Manifest manifest;
|
||||
|
||||
public JarMap(File file) throws IOException {
|
||||
this(file, true);
|
||||
@@ -39,6 +40,7 @@ public class JarMap implements Closeable, AutoCloseable {
|
||||
|
||||
public JarMap(File file, boolean verify, int mode) throws IOException {
|
||||
jarFile = new JarFile(file, verify, mode);
|
||||
manifest = jarFile.getManifest();
|
||||
}
|
||||
|
||||
public JarMap(String name) throws IOException {
|
||||
@@ -54,48 +56,54 @@ public class JarMap implements Closeable, AutoCloseable {
|
||||
}
|
||||
|
||||
public JarMap(InputStream is, boolean verify) throws IOException {
|
||||
isInputStream = true;
|
||||
bufMap = new LinkedHashMap<>();
|
||||
jis = new JarInputStream(is, verify);
|
||||
bufMap = new LinkedHashMap<>();
|
||||
JarEntry entry;
|
||||
while ((entry = jis.getNextJarEntry()) != null) {
|
||||
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
|
||||
}
|
||||
manifest = jis.getManifest();
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return isInputStream ? null : new File(jarFile.getName());
|
||||
return jarFile == null ? null : new File(jarFile.getName());
|
||||
}
|
||||
|
||||
public Manifest getManifest() throws IOException {
|
||||
return isInputStream ? jis.getManifest() : jarFile.getManifest();
|
||||
public Manifest getManifest() {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ZipEntry ze) throws IOException {
|
||||
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() :
|
||||
jarFile.getInputStream(ze);
|
||||
if (bufMap != null) {
|
||||
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
|
||||
if (e != null)
|
||||
return e.data.getInputStream();
|
||||
}
|
||||
return jarFile.getInputStream(ze);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream(ZipEntry ze) {
|
||||
if (!isInputStream) // Only support InputStream mode
|
||||
return null;
|
||||
ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data;
|
||||
bs.reset();
|
||||
return bs;
|
||||
manifest = null; /* Invalidate the manifest */
|
||||
if (bufMap == null)
|
||||
bufMap = new LinkedHashMap<>();
|
||||
JarMapEntry e = new JarMapEntry(ze.getName());
|
||||
bufMap.put(ze.getName(), e);
|
||||
return e.data;
|
||||
}
|
||||
|
||||
public byte[] getRawData(ZipEntry ze) throws IOException {
|
||||
if (isInputStream) {
|
||||
return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray();
|
||||
} else {
|
||||
ByteArrayStream bytes = new ByteArrayStream();
|
||||
bytes.readFrom(jarFile.getInputStream(ze));
|
||||
return bytes.toByteArray();
|
||||
if (bufMap != null) {
|
||||
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName());
|
||||
if (e != null)
|
||||
return e.data.toByteArray();
|
||||
}
|
||||
ByteArrayStream bytes = new ByteArrayStream();
|
||||
bytes.readFrom(jarFile.getInputStream(ze));
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
|
||||
public Enumeration<JarEntry> entries() {
|
||||
return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries();
|
||||
return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries();
|
||||
}
|
||||
|
||||
public ZipEntry getEntry(String name) {
|
||||
@@ -103,20 +111,29 @@ public class JarMap implements Closeable, AutoCloseable {
|
||||
}
|
||||
|
||||
public JarEntry getJarEntry(String name) {
|
||||
return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name);
|
||||
JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name);
|
||||
if (e == null && bufMap != null)
|
||||
return bufMap.get(name);
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
(isInputStream ? jis : jarFile).close();
|
||||
(jarFile == null ? jis : jarFile).close();
|
||||
}
|
||||
|
||||
private static class JarMapEntry extends JarEntry {
|
||||
ByteArrayStream data;
|
||||
|
||||
JarMapEntry(JarEntry je, InputStream is) {
|
||||
super(je);
|
||||
data = new ByteArrayStream();
|
||||
data.readFrom(is);
|
||||
}
|
||||
|
||||
JarMapEntry(String s) {
|
||||
super(s);
|
||||
data = new ByteArrayStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,28 @@
|
||||
package com.topjohnwu.utils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ReusableInputStream extends BufferedInputStream {
|
||||
|
||||
public ReusableInputStream(InputStream in) {
|
||||
super(in);
|
||||
mark(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public ReusableInputStream(InputStream in, int size) {
|
||||
super(in, size);
|
||||
mark(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
/* Reset at close so we can reuse it */
|
||||
reset();
|
||||
}
|
||||
|
||||
public void destroy() throws IOException {
|
||||
super.close();
|
||||
}
|
||||
}
|
@@ -18,6 +18,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -73,19 +74,31 @@ public class SignAPK {
|
||||
JarMap input, OutputStream output) throws Exception {
|
||||
File temp1 = File.createTempFile("signAPK", null);
|
||||
File temp2 = File.createTempFile("signAPK", null);
|
||||
if (cert == null) {
|
||||
cert = SignAPK.class.getResourceAsStream("/keys/testkey.x509.pem");
|
||||
}
|
||||
if (key == null) {
|
||||
key = SignAPK.class.getResourceAsStream("/keys/testkey.pk8");
|
||||
}
|
||||
|
||||
ReusableInputStream c = new ReusableInputStream(cert);
|
||||
ReusableInputStream k = new ReusableInputStream(key);
|
||||
|
||||
try {
|
||||
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))) {
|
||||
signZip(cert, key, input, out, false);
|
||||
signZip(c, k, input, out, false);
|
||||
}
|
||||
|
||||
ZipAdjust.adjust(temp1, temp2);
|
||||
|
||||
try (JarMap map = new JarMap(temp2, false)) {
|
||||
signZip(cert, key, map, output, true);
|
||||
signZip(c, k, map, output, true);
|
||||
}
|
||||
} finally {
|
||||
temp1.delete();
|
||||
temp2.delete();
|
||||
c.destroy();
|
||||
k.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -28,8 +28,8 @@ public class ZipSigner {
|
||||
InputStream key = null;
|
||||
|
||||
if (args.length - argStart == 4) {
|
||||
cert = new BufferedInputStream(new FileInputStream(new File(args[argStart])));
|
||||
key = new BufferedInputStream(new FileInputStream(new File(args[argStart + 1])));
|
||||
cert = new FileInputStream(new File(args[argStart]));
|
||||
key = new FileInputStream(new File(args[argStart + 1]));
|
||||
argStart += 2;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user