mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 08:07:25 +00:00
Compare commits
109 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 | ||
![]() |
3a9a3ed184 | ||
![]() |
88fae36b8a | ||
![]() |
fc9d4034a9 | ||
![]() |
cecc0b932d | ||
![]() |
0faed7159c | ||
![]() |
fb491cfdcf | ||
![]() |
fc706dcb40 | ||
![]() |
a2c1b024f3 | ||
![]() |
3d865394d7 | ||
![]() |
76ef1d0d86 | ||
![]() |
9484ec0c17 | ||
![]() |
614c552e55 | ||
![]() |
7db3d84ba2 | ||
![]() |
87f6018468 | ||
![]() |
9194c50590 | ||
![]() |
7ff45974c6 | ||
![]() |
2533a4fc4a | ||
![]() |
42284c5efb | ||
![]() |
7d7686da33 | ||
![]() |
65e455ef0b | ||
![]() |
ac05e2f2e2 | ||
![]() |
787f7b3035 | ||
![]() |
31bd642b80 | ||
![]() |
f0bac6b154 | ||
![]() |
cc7e74ca11 |
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
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,3 +25,6 @@
|
||||
[submodule "xz"]
|
||||
path = native/jni/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
[submodule "nanopb"]
|
||||
path = native/jni/external/nanopb
|
||||
url = https://github.com/nanopb/nanopb.git
|
||||
|
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: 0c782edf21...b885ccbd63
@@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -19,9 +19,16 @@ allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
compileSdkVersion = 28
|
||||
buildToolsVersion = "28.0.0"
|
||||
supportLibVersion = "27.1.1"
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
349
build.py
349
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 ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
mkdir_p(os.path.join('out', arch))
|
||||
with open(os.path.join('out', arch, 'dump.h'), 'w') as dump:
|
||||
dump.write('#include "stdlib.h"\n')
|
||||
mv(os.path.join('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 ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
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'))
|
||||
@@ -234,14 +298,14 @@ def zip_main(args):
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Binaries
|
||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
for 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('addon.d', '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 [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
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 lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
|
||||
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
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Mon Dec 04 11:24:34 CST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
100
gradlew
vendored
100
gradlew
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,42 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -85,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -150,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
14
gradlew.bat
vendored
14
gradlew.bat
vendored
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
@@ -1,7 +1,8 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
@@ -12,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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
|
||||
LIBLZ4 := $(EXT_PATH)/lz4/lib
|
||||
LIBBZ2 := $(EXT_PATH)/bzip2
|
||||
LIBFDT := $(EXT_PATH)/dtc/libfdt
|
||||
LIBNANOPB := $(EXT_PATH)/nanopb
|
||||
UTIL_SRC := utils/cpio.c \
|
||||
utils/file.c \
|
||||
utils/img.c \
|
||||
@@ -22,32 +23,35 @@ UTIL_SRC := utils/cpio.c \
|
||||
# Binaries
|
||||
########################
|
||||
|
||||
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
|
||||
ifdef B_MAGISK
|
||||
|
||||
# magisk main binary
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magisk
|
||||
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libnanopb
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/magiskpolicy \
|
||||
$(EXT_PATH)/include \
|
||||
$(LIBSELINUX)
|
||||
$(LIBSELINUX) \
|
||||
$(LIBNANOPB)
|
||||
|
||||
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 \
|
||||
resetprop/resetprop.cpp \
|
||||
resetprop/persist_props.c \
|
||||
resetprop/resetprop.c \
|
||||
resetprop/system_properties.cpp \
|
||||
su/su.c \
|
||||
su/activity.c \
|
||||
su/db.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c \
|
||||
su/su_socket.c \
|
||||
@@ -59,7 +63,7 @@ include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
ifndef PRECOMPILE
|
||||
ifdef B_INIT
|
||||
|
||||
# magiskinit
|
||||
include $(CLEAR_VARS)
|
||||
@@ -68,13 +72,13 @@ LOCAL_STATIC_LIBRARIES := libsepol liblzma
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/magiskpolicy \
|
||||
../out/$(TARGET_ARCH_ABI) \
|
||||
out \
|
||||
out/$(TARGET_ARCH_ABI) \
|
||||
$(LIBSEPOL) \
|
||||
$(LIBLZMA)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/magiskinit.c \
|
||||
core/socket.c \
|
||||
magiskpolicy/api.c \
|
||||
magiskpolicy/magiskpolicy.c \
|
||||
magiskpolicy/rules.c \
|
||||
@@ -84,6 +88,10 @@ LOCAL_SRC_FILES := \
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
ifdef B_BOOT
|
||||
|
||||
# magiskboot
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
@@ -110,10 +118,10 @@ LOCAL_CFLAGS := -DXWRAP_EXIT
|
||||
LOCAL_LDLIBS := -lz
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# 32-bit static binaries
|
||||
ifndef GRADLE # Do not run gradle sync on these binaries
|
||||
ifneq ($(TARGET_ARCH_ABI), x86_64)
|
||||
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
|
||||
endif
|
||||
|
||||
ifdef B_BXZ
|
||||
|
||||
# b64xz
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := b64xz
|
||||
@@ -122,13 +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
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
# Precompile
|
||||
ifdef B_BB
|
||||
|
||||
# Busybox
|
||||
include jni/external/busybox/Android.mk
|
||||
|
||||
endif
|
||||
|
||||
########################
|
||||
|
@@ -1,5 +1,10 @@
|
||||
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
|
||||
APP_PLATFORM := android-21
|
||||
APP_CFLAGS := $(MAGISK_FLAGS) -std=gnu99
|
||||
APP_ABI := x86 armeabi-v7a
|
||||
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,9 +16,11 @@
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "db.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
static char *buf, *buf2;
|
||||
static struct vector module_list;
|
||||
@@ -112,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]));
|
||||
}
|
||||
@@ -124,15 +126,6 @@ static void bb_setenv(struct vector *v) {
|
||||
vec_push_back(v, NULL);
|
||||
}
|
||||
|
||||
static void pm_setenv(struct vector *v) {
|
||||
for (int i = 0; environ[i]; ++i) {
|
||||
if (strncmp(environ[i], "CLASSPATH=", 10) != 0)
|
||||
vec_push_back(v, strdup(environ[i]));
|
||||
}
|
||||
vec_push_back(v, strdup("CLASSPATH=/system/framework/pm.jar"));
|
||||
vec_push_back(v, NULL);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Scripts *
|
||||
***********/
|
||||
@@ -151,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);
|
||||
}
|
||||
@@ -168,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);
|
||||
}
|
||||
@@ -314,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);
|
||||
@@ -359,7 +352,7 @@ static void simple_mount(const char *path) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, path);
|
||||
snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, path);
|
||||
if (!(dir = opendir(buf)))
|
||||
return;
|
||||
|
||||
@@ -377,7 +370,7 @@ static void simple_mount(const char *path) {
|
||||
free(new_path);
|
||||
} else if (entry->d_type == DT_REG) {
|
||||
// Actual file path
|
||||
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, buf2);
|
||||
snprintf(buf, PATH_MAX, "%s%s", SIMPLEMOUNT, buf2);
|
||||
// Clone all attributes
|
||||
clone_attr(buf2, buf);
|
||||
// Finally, mount the file
|
||||
@@ -435,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, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
/****************
|
||||
@@ -473,61 +477,222 @@ static void unblock_boot_process() {
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void post_fs(int client) {
|
||||
LOGI("** post-fs mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
static const char wrapper[] =
|
||||
"#!/system/bin/sh\n"
|
||||
"unset LD_LIBRARY_PATH\n"
|
||||
"unset LD_PRELOAD\n"
|
||||
"exec /sbin/magisk.bin \"${0##*/}\" \"$@\"\n";
|
||||
|
||||
// Uninstall or core only mode
|
||||
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
|
||||
goto unblock;
|
||||
void startup() {
|
||||
if (!check_data())
|
||||
unblock_boot_process();
|
||||
|
||||
// Allocate buffer
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
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();
|
||||
}
|
||||
|
||||
simple_mount("/system");
|
||||
simple_mount("/vendor");
|
||||
// No uninstaller or core-only mode
|
||||
if (access(DISABLEFILE, F_OK) != 0) {
|
||||
// Allocate buffer
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
simple_mount("/system");
|
||||
simple_mount("/vendor");
|
||||
}
|
||||
|
||||
LOGI("** Initializing Magisk\n");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin, fd;
|
||||
char buf[PATH_MAX];
|
||||
void *magisk, *init;
|
||||
size_t magisk_size, init_size;
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
|
||||
// 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);
|
||||
unlink("/sbin/magiskinit");
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
link_dir(sbin, root);
|
||||
close(sbin);
|
||||
|
||||
// Mount the /sbin tmpfs overlay
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
chmod("/sbin", 0755);
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
// 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.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);
|
||||
}
|
||||
|
||||
// Setup magiskinit symlinks
|
||||
fd = creat("/sbin/magiskinit", 0755);
|
||||
xwrite(fd, init, init_size);
|
||||
close(fd);
|
||||
free(init);
|
||||
setfilecon("/sbin/magiskinit", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
snprintf(buf, PATH_MAX, "/sbin/%s", init_applet[i]);
|
||||
xsymlink("/sbin/magiskinit", buf);
|
||||
}
|
||||
|
||||
// Create symlinks pointing back to /root
|
||||
dir = xfdopendir(root);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
|
||||
symlinkat(buf, sbin, entry->d_name);
|
||||
}
|
||||
|
||||
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);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
} else if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/system", buf);
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s <- %s\n", MIRRDIR "/vendor", MIRRDIR "/system/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdirs(DATABIN, 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
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
|
||||
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", NULL);
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (!is_daemon_init && !check_data())
|
||||
goto unblock;
|
||||
|
||||
// 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");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
full_patch_pid = exec_command(0, NULL, NULL, "/sbin/magiskpolicy", "--live", "allow "SEPOL_PROC_DOMAIN" * * *", NULL);
|
||||
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
vec_init(&module_list);
|
||||
|
||||
// Initialize
|
||||
if (!is_daemon_init)
|
||||
daemon_init();
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
setenv("BOOTMODE", "true", 1);
|
||||
exec_command(0, NULL, bb_setenv, "sh", UNINSTALLER, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge, trim, mount magisk.img, which will also travel through the modules
|
||||
// After this, it will create the module list
|
||||
if (prepare_img())
|
||||
goto core_only; // Mounting fails, we can only do core only stuffs
|
||||
|
||||
restorecon();
|
||||
chmod(SECURE_DIR, 0700);
|
||||
|
||||
// Run common scripts
|
||||
LOGI("* Running post-fs-data.d scripts\n");
|
||||
exec_common_script("post-fs-data");
|
||||
@@ -614,8 +779,6 @@ core_only:
|
||||
}
|
||||
|
||||
auto_start_magiskhide();
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
@@ -625,13 +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
|
||||
wait_till_exists(PATCHDONE);
|
||||
unlink(PATCHDONE);
|
||||
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");
|
||||
@@ -645,32 +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:su_file:s0");
|
||||
while (1) {
|
||||
sleep(5);
|
||||
int apk_res = -1, pid;
|
||||
pid = exec_command(1, &apk_res, pm_setenv,
|
||||
"app_process",
|
||||
"/system/bin", "com.android.commands.pm.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
|
||||
@@ -678,6 +841,4 @@ core_only:
|
||||
free(buf2);
|
||||
buf = buf2 = NULL;
|
||||
vec_deep_destroy(&module_list);
|
||||
|
||||
stop_debug_full_log();
|
||||
}
|
||||
|
@@ -19,8 +19,11 @@
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
int is_daemon_init = 0, seperate_vendor = 0;
|
||||
int setup_done = 0;
|
||||
int seperate_vendor = 0;
|
||||
int full_patch_pid = -1;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
@@ -36,7 +39,6 @@ static void *request_handler(void *args) {
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case LS_HIDELIST:
|
||||
case POST_FS:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
if (credential.uid != 0) {
|
||||
@@ -75,16 +77,17 @@ static void *request_handler(void *args) {
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
close(client);
|
||||
break;
|
||||
case POST_FS:
|
||||
post_fs(client);
|
||||
break;
|
||||
case POST_FS_DATA:
|
||||
post_fs_data(client);
|
||||
break;
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
/* Do NOT close the client, make it hold */
|
||||
break;
|
||||
default:
|
||||
close(client);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
@@ -106,184 +109,48 @@ void auto_start_magiskhide() {
|
||||
free(hide_prop);
|
||||
}
|
||||
|
||||
void daemon_init() {
|
||||
is_daemon_init = 1;
|
||||
|
||||
// Magisk binaries
|
||||
char *bin_path = NULL;
|
||||
if (access("/cache/data_bin", F_OK) == 0)
|
||||
bin_path = "/cache/data_bin";
|
||||
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/data/com.topjohnwu.magisk/install";
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Migration
|
||||
rm_rf("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink("/data/magisk_debug.log");
|
||||
chmod("/data/adb", 0700);
|
||||
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin;
|
||||
char buf[PATH_MAX], buf2[PATH_MAX];
|
||||
char magisk_name[10], init_name[10];
|
||||
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir("/root", 0755);
|
||||
chmod("/root", 0755);
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
link_dir(sbin, root);
|
||||
unlink("/sbin/magisk");
|
||||
close(sbin);
|
||||
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
chmod("/sbin", 0755);
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
dir = xfdopendir(root);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
|
||||
gen_rand_str(magisk_name, sizeof(magisk_name));
|
||||
snprintf(buf, PATH_MAX, "/root/%s", magisk_name);
|
||||
unlink("/sbin/magisk");
|
||||
rename("/root/magisk", buf);
|
||||
xsymlink(buf, "/sbin/magisk");
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
|
||||
gen_rand_str(init_name, sizeof(init_name));
|
||||
snprintf(buf, PATH_MAX, "/root/%s", init_name);
|
||||
unlink("/sbin/magiskinit");
|
||||
rename("/root/magiskinit", buf);
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", init_applet[i]);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
|
||||
close(root);
|
||||
|
||||
// Backward compatibility
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
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 ")) {
|
||||
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);
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdirs(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s <- %s\n", MIRRDIR "/vendor", MIRRDIR "/system/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdirs(MIRRDIR "/bin", 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");
|
||||
}
|
||||
|
||||
void start_daemon() {
|
||||
void main_daemon() {
|
||||
setsid();
|
||||
setcon("u:r:su:s0");
|
||||
umask(0);
|
||||
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);
|
||||
|
||||
// Block user signals
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 ((is_daemon_init = (access(MAGISKTMP, F_OK) == 0))) {
|
||||
// Restart stuffs if the daemon is restarted
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
auto_start_magiskhide();
|
||||
start_debug_log();
|
||||
} else if (check_data()) {
|
||||
daemon_init();
|
||||
}
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
// 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) {
|
||||
@@ -296,26 +163,38 @@ void start_daemon() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon() {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
if (xfork() == 0) {
|
||||
if (fork_dont_care() == 0) {
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
close(fd);
|
||||
start_daemon();
|
||||
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,211 +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"
|
||||
|
||||
extern int is_daemon_init;
|
||||
int logd = 0;
|
||||
|
||||
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 check_logd() {
|
||||
char *prop = getprop("init.svc.logd");
|
||||
if (prop != NULL) {
|
||||
free(prop);
|
||||
logd = 1;
|
||||
} else {
|
||||
LOGD("log_monitor: logd not started, disable logging");
|
||||
logd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void *logger_thread(void *args) {
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
|
||||
LOGD("log_monitor: logger start");
|
||||
|
||||
while (1) {
|
||||
if (!logd) {
|
||||
// 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);
|
||||
|
||||
check_logd();
|
||||
}
|
||||
|
||||
// Should never be here, but well...
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *magisk_log_thread(void *args) {
|
||||
// Buffer logs before we have data access
|
||||
struct vector logs;
|
||||
vec_init(&logs);
|
||||
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
// Register our listener
|
||||
log_events[LOG_EVENT].fd = pipefd[1];
|
||||
|
||||
LOGD("log_monitor: magisk log dumper start");
|
||||
|
||||
FILE *log = NULL;
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
if (!is_daemon_init) {
|
||||
vec_push_back(&logs, strdup(line));
|
||||
} else {
|
||||
if (log == NULL) {
|
||||
// Dump buffered logs to file
|
||||
log = xfopen(LOGFILE, "w");
|
||||
setbuf(log, NULL);
|
||||
char *tmp;
|
||||
vec_for_each(&logs, tmp) {
|
||||
fprintf(log, "%s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
vec_destroy(&logs);
|
||||
}
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *debug_magisk_log_thread(void *args) {
|
||||
FILE *log = xfopen(DEBUG_LOG, "a");
|
||||
setbuf(log, NULL);
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
LOGD("log_monitor: debug log dumper start");
|
||||
|
||||
// Register our listener
|
||||
log_events[DEBUG_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
|
||||
fprintf(log, "%s", line);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start new threads to monitor logcat and dump to logfile */
|
||||
void monitor_logs() {
|
||||
pthread_t thread;
|
||||
|
||||
check_logd();
|
||||
|
||||
if (logd) {
|
||||
// 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 (logd) {
|
||||
// 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 (logd) {
|
||||
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,21 +43,16 @@ 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"
|
||||
" --[init service] start init service\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"
|
||||
" --restorecon fix selinux context on Magisk files and folders\n"
|
||||
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
|
||||
"\n"
|
||||
"Supported init services:\n"
|
||||
" daemon, post-fs, post-fs-data, service\n"
|
||||
"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]);
|
||||
@@ -64,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();
|
||||
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], "--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) {
|
||||
if (xfork() == 0)
|
||||
start_daemon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS);
|
||||
return read_int(fd);
|
||||
} 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);
|
||||
} else {
|
||||
if (strcmp(basename(argv0), "magisk.bin") == 0) {
|
||||
if (argc >= 2) {
|
||||
// It's calling applets
|
||||
--argc;
|
||||
++argv;
|
||||
@@ -168,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,70 +343,24 @@ 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);
|
||||
}
|
||||
|
||||
static void magisk_init_daemon() {
|
||||
setsid();
|
||||
|
||||
// Full patch
|
||||
sepol_allow("su", ALL, ALL, ALL);
|
||||
|
||||
// Wait till init cold boot done
|
||||
while (access("/dev/.coldboot_done", F_OK))
|
||||
usleep(1);
|
||||
|
||||
int null = open("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
dup3(null, STDIN_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDOUT_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDERR_FILENO, O_CLOEXEC);
|
||||
if (null > STDERR_FILENO)
|
||||
close(null);
|
||||
|
||||
// Transit our context to su (mimic setcon)
|
||||
int fd, crap;
|
||||
fd = open("/proc/self/attr/current", O_WRONLY);
|
||||
write(fd, "u:r:su:s0", 9);
|
||||
close(fd);
|
||||
|
||||
// Dump full patch to kernel
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
close(creat(PATCHDONE, 0));
|
||||
destroy_policydb();
|
||||
|
||||
// Keep Magisk daemon always alive
|
||||
while (1) {
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
memcpy(sun.sun_path + 1, RAND_SOCKET_NAME, sizeof(SOCKET_NAME));
|
||||
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000); /* Wait 10 ms after each try */
|
||||
|
||||
/* Should hold forever */
|
||||
read(fd, &crap, sizeof(crap));
|
||||
|
||||
/* If things went here, it means the other side of the socket is closed
|
||||
* We restart the daemon again */
|
||||
close(fd);
|
||||
if (fork_dont_care() == 0) {
|
||||
execv("/sbin/magisk", (char *[]) { "magisk", "--daemon", NULL } );
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
umask(0);
|
||||
|
||||
@@ -410,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);
|
||||
}
|
||||
@@ -424,14 +388,8 @@ int main(int argc, char *argv[]) {
|
||||
if (null > STDERR_FILENO)
|
||||
close(null);
|
||||
|
||||
// Extract and link files
|
||||
mkdir("/overlay", 0000);
|
||||
dump_magiskrc("/overlay/init.magisk.rc", 0750);
|
||||
mkdir("/overlay/sbin", 0755);
|
||||
dump_magisk("/overlay/sbin/magisk", 0755);
|
||||
patch_socket_name("/overlay/sbin/magisk");
|
||||
mkdir("/overlay/root", 0755);
|
||||
link("/init", "/overlay/root/magiskinit");
|
||||
// Backup self
|
||||
link("/init", "/init.bak");
|
||||
|
||||
struct cmdline cmd;
|
||||
parse_cmdline(&cmd);
|
||||
@@ -444,7 +402,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (cmd.skip_initramfs) {
|
||||
// Clear rootfs
|
||||
excl_list = (char *[]) { "overlay", ".backup", NULL };
|
||||
excl_list = (char *[]) { "overlay", ".backup", "init.bak", NULL };
|
||||
frm_rf(root);
|
||||
} else if (access("/ramdisk.cpio.xz", R_OK) == 0) {
|
||||
// High compression mode
|
||||
@@ -473,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];
|
||||
@@ -499,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;
|
||||
}
|
||||
}
|
||||
|
||||
/* *************
|
||||
@@ -513,30 +477,32 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Only patch rootfs if not intended to run in recovery
|
||||
if (access("/etc/recovery.fstab", F_OK) != 0) {
|
||||
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
|
||||
mv_dir(overlay, root);
|
||||
close(overlay);
|
||||
rmdir("/overlay");
|
||||
int fd;
|
||||
|
||||
// Handle ramdisk overlays
|
||||
fd = open("/overlay", O_RDONLY | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
mv_dir(fd, root);
|
||||
close(fd);
|
||||
rmdir("/overlay");
|
||||
}
|
||||
|
||||
patch_ramdisk();
|
||||
patch_sepolicy();
|
||||
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
if (fork_dont_care() == 0) {
|
||||
strcpy(argv[0], "magiskinit");
|
||||
close(root);
|
||||
magisk_init_daemon();
|
||||
}
|
||||
// Dump binaries
|
||||
dump_magiskrc("/init.magisk.rc", 0750);
|
||||
dump_magisk("/sbin/magisk", 0755);
|
||||
patch_socket_name("/sbin/magisk");
|
||||
rename("/init.bak", "/sbin/magiskinit");
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
||||
char socket_name[] = SOCKET_NAME;
|
||||
|
||||
/* 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
|
||||
|
14
native/jni/external/Android.mk
vendored
14
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)
|
||||
@@ -28,6 +28,16 @@ LOCAL_SRC_FILES := \
|
||||
mincrypt/sha256.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libnanopb.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libnanopb
|
||||
LOCAL_C_INCLUDES := $(LIBNANOPB)
|
||||
LOCAL_SRC_FILES := \
|
||||
nanopb/pb_common.c \
|
||||
nanopb/pb_decode.c \
|
||||
nanopb/pb_encode.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libfdt.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libfdt
|
||||
@@ -165,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: 183821743b...47a1fdda34
1
native/jni/external/nanopb
vendored
Submodule
1
native/jni/external/nanopb
vendored
Submodule
Submodule native/jni/external/nanopb added at 04cd1f94cc
2
native/jni/external/selinux
vendored
2
native/jni/external/selinux
vendored
Submodule native/jni/external/selinux updated: 8e849a5639...4d28de872c
@@ -8,7 +8,9 @@
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
extern int is_daemon_init, seperate_vendor;
|
||||
extern int setup_done;
|
||||
extern int seperate_vendor;
|
||||
extern int full_patch_pid;
|
||||
|
||||
// Commands require connecting to daemon
|
||||
enum {
|
||||
@@ -16,14 +18,15 @@ enum {
|
||||
SUPERUSER,
|
||||
CHECK_VERSION,
|
||||
CHECK_VERSION_CODE,
|
||||
POST_FS,
|
||||
POST_FS_DATA,
|
||||
LATE_START,
|
||||
LAUNCH_MAGISKHIDE,
|
||||
STOP_MAGISKHIDE,
|
||||
ADD_HIDELIST,
|
||||
RM_HIDELIST,
|
||||
LS_HIDELIST
|
||||
LS_HIDELIST,
|
||||
HIDE_CONNECT,
|
||||
HANDSHAKE
|
||||
};
|
||||
|
||||
// Return codes for daemon
|
||||
@@ -31,23 +34,33 @@ enum {
|
||||
DAEMON_ERROR = -1,
|
||||
DAEMON_SUCCESS = 0,
|
||||
ROOT_REQUIRED,
|
||||
LOGD_DISABLED,
|
||||
LOGCAT_DISABLED,
|
||||
HIDE_IS_ENABLED,
|
||||
HIDE_NOT_ENABLED,
|
||||
HIDE_ITEM_EXIST,
|
||||
HIDE_ITEM_NOT_EXIST,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MAIN_DAEMON,
|
||||
LOG_DAEMON
|
||||
} daemon_t;
|
||||
|
||||
// daemon.c
|
||||
|
||||
void start_daemon();
|
||||
void main_daemon();
|
||||
int connect_daemon();
|
||||
int connect_daemon2(daemon_t d, int *sockfd);
|
||||
void auto_start_magiskhide();
|
||||
void daemon_init();
|
||||
|
||||
// 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);
|
||||
@@ -59,10 +72,9 @@ void write_string(int fd, const char* val);
|
||||
* Boot Stages *
|
||||
***************/
|
||||
|
||||
void post_fs(int client);
|
||||
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 logd;
|
||||
|
||||
void monitor_logs();
|
||||
void start_debug_full_log();
|
||||
void stop_debug_full_log();
|
||||
void start_debug_log();
|
||||
|
||||
#endif
|
||||
|
||||
/********************
|
||||
|
@@ -7,37 +7,40 @@
|
||||
#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
|
||||
#endif
|
||||
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define DEBUG_LOG "/data/adb/magisk_debug.log"
|
||||
#define UNBLOCKFILE "/dev/.magisk.unblock"
|
||||
#define PATCHDONE "/dev/.magisk.patch.done"
|
||||
#define DISABLEFILE "/cache/.disable_magisk"
|
||||
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
|
||||
#define CACHEMOUNT "/cache/magisk_mount"
|
||||
#define MAGISKTMP "/sbin/.core"
|
||||
#define 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 MAINIMG "/data/adb/magisk.img"
|
||||
#define DATABIN "/data/adb/magisk"
|
||||
#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/"
|
||||
@@ -51,11 +54,10 @@
|
||||
|
||||
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 *[]);
|
||||
extern char socket_name[]; /* Workaround compiler bug pre NDK r13 */
|
||||
|
||||
int create_links(const char *bin, const char *path);
|
||||
|
||||
@@ -63,13 +65,7 @@ int create_links(const char *bin, const char *path);
|
||||
int magiskhide_main(int argc, char *argv[]);
|
||||
int magiskpolicy_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
int imgtool_main(int argc, char *argv[]);
|
||||
|
||||
#endif
|
||||
|
@@ -1,18 +1,19 @@
|
||||
#include "magisk.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
const char magiskrc[] =
|
||||
|
||||
// Triggers
|
||||
|
||||
"on post-fs\n"
|
||||
" start logd\n"
|
||||
" start magisk_pfs\n"
|
||||
" wait /dev/.magisk.unblock 10\n"
|
||||
"\n"
|
||||
|
||||
"on post-fs-data\n"
|
||||
" load_persist_props\n"
|
||||
" rm /dev/.magisk.unblock\n"
|
||||
" start magisk_pfsd\n"
|
||||
" wait /dev/.magisk.unblock 10\n"
|
||||
" rm "UNBLOCKFILE"\n"
|
||||
" start magisk_startup\n"
|
||||
" wait "UNBLOCKFILE" 10\n"
|
||||
" rm /dev/.magisk.unblock\n"
|
||||
"\n"
|
||||
|
||||
@@ -20,25 +21,19 @@ const char magiskrc[] =
|
||||
|
||||
"service magisk_daemon /sbin/magisk --daemon\n"
|
||||
" user root\n"
|
||||
" seclabel u:r:su:s0\n"
|
||||
" seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
|
||||
" oneshot\n"
|
||||
"\n"
|
||||
|
||||
"service magisk_pfs /sbin/magisk --post-fs\n"
|
||||
"service magisk_startup /sbin/magisk --startup\n"
|
||||
" user root\n"
|
||||
" seclabel u:r:su:s0\n"
|
||||
" seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
|
||||
" oneshot\n"
|
||||
"\n"
|
||||
|
||||
"service magisk_pfsd /sbin/magisk --post-fs-data\n"
|
||||
" user root\n"
|
||||
" seclabel u:r:su:s0\n"
|
||||
" oneshot\n"
|
||||
"\n"
|
||||
|
||||
"service magisk_service /sbin/magisk --service\n"
|
||||
"service magisk_service /sbin/magisk --service\n"
|
||||
" class late_start\n"
|
||||
" user root\n"
|
||||
" seclabel u:r:su:s0\n"
|
||||
" seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n"
|
||||
" oneshot\n"
|
||||
;
|
||||
|
@@ -4,10 +4,6 @@
|
||||
#ifndef _RESETPROP_H_
|
||||
#define _RESETPROP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int prop_exist(const char *name);
|
||||
int setprop(const char *name, const char *value);
|
||||
int setprop2(const char *name, const char *value, const int trigger);
|
||||
@@ -16,10 +12,6 @@ char *getprop2(const char *name, int persist);
|
||||
int deleteprop(const char *name);
|
||||
int deleteprop2(const char *name, const int persist);
|
||||
int read_prop_file(const char* filename, const int trigger);
|
||||
void getprop_all(void (*callback)(const char*, const char*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie);
|
||||
|
||||
#endif
|
||||
|
@@ -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, int force);
|
||||
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"
|
||||
@@ -168,7 +169,7 @@ int parse_img(const char *image, boot_img *boot) {
|
||||
|
||||
if (pos < boot->map_size) {
|
||||
boot->tail = head + pos;
|
||||
boot->tail_size = boot->map_size - pos;
|
||||
boot->tail_size = boot->map_size - (boot->tail - boot->map_addr);
|
||||
}
|
||||
|
||||
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,15 +53,19 @@ void hide_sensitive_props() {
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_magisk_prop(const char *name, const char *value) {
|
||||
static void rm_magisk_prop(const char *name, const char *value, void *v) {
|
||||
if (strstr(name, "magisk")) {
|
||||
deleteprop2(name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
getprop_all(rm_magisk_prop, NULL);
|
||||
}
|
||||
|
||||
int add_list(char *proc) {
|
||||
|
@@ -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"
|
||||
@@ -49,9 +45,9 @@ void launch_magiskhide(int client) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!logd) {
|
||||
if (!loggable) {
|
||||
if (client > 0) {
|
||||
write_int(client, LOGD_DISABLED);
|
||||
write_int(client, LOGCAT_DISABLED);
|
||||
close(client);
|
||||
}
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
@@ -147,14 +143,14 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
case ROOT_REQUIRED:
|
||||
fprintf(stderr, "Root is required for this operation\n");
|
||||
return code;
|
||||
case LOGD_DISABLED:
|
||||
fprintf(stderr, "Logd is not running, cannot run logcat\n");
|
||||
case LOGCAT_DISABLED:
|
||||
fprintf(stderr, "Logcat is disabled, cannot start MagiskHide\n");
|
||||
return (code);
|
||||
case HIDE_NOT_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is not enabled yet\n");
|
||||
fprintf(stderr, "MagiskHide is not enabled yet\n");
|
||||
return code;
|
||||
case HIDE_IS_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is already enabled\n");
|
||||
fprintf(stderr, "MagiskHide is already enabled\n");
|
||||
return code;
|
||||
case HIDE_ITEM_EXIST:
|
||||
fprintf(stderr, "Process [%s] already exists in hide list\n", argv[2]);
|
||||
|
@@ -4,10 +4,6 @@
|
||||
#include <pthread.h>
|
||||
|
||||
#define TERM_THREAD SIGUSR1
|
||||
#define HIDE_DONE SIGUSR2
|
||||
|
||||
// Kill process
|
||||
void kill_proc(int pid);
|
||||
|
||||
// Process monitor
|
||||
void proc_monitor();
|
||||
|
@@ -16,65 +16,53 @@
|
||||
#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 hide_queue = 0, 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");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void hide_done(int sig) {
|
||||
--hide_queue;
|
||||
if (hide_queue == 0) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
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 void hide_daemon(int pid, int ppid) {
|
||||
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;
|
||||
@@ -89,31 +77,9 @@ static void hide_daemon(int pid, int ppid) {
|
||||
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);
|
||||
}
|
||||
@@ -126,23 +92,19 @@ static void hide_daemon(int pid, int ppid) {
|
||||
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);
|
||||
// Wait a while and link it back
|
||||
sleep(10);
|
||||
kill(ppid, HIDE_DONE);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
@@ -151,7 +113,6 @@ void proc_monitor() {
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, TERM_THREAD);
|
||||
sigaddset(&block_set, HIDE_DONE);
|
||||
pthread_sigmask(SIG_UNBLOCK, &block_set, NULL);
|
||||
|
||||
// Register the cancel signal
|
||||
@@ -159,107 +120,88 @@ void proc_monitor() {
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = term_thread;
|
||||
sigaction(TERM_THREAD, &act, NULL);
|
||||
act.sa_handler = hide_done;
|
||||
sigaction(HIDE_DONE, &act, NULL);
|
||||
|
||||
cache_block[0] = '\0';
|
||||
|
||||
// Get the mount namespace of init
|
||||
if (read_namespace(1, init_ns, 32)) {
|
||||
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;
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
unlink("/magisk");
|
||||
unlink("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink(MAGISKRC);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
++hide_queue;
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
int selfpid = getpid();
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid, selfpid);
|
||||
|
||||
break;
|
||||
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: 21a557a184...52a6a7bce8
31
native/jni/resetprop/_resetprop.h
Normal file
31
native/jni/resetprop/_resetprop.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* resetprop.h - Internal struct definitions
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MAGISK_PROPS_H
|
||||
#define MAGISK_PROPS_H
|
||||
|
||||
#include "system_properties.h"
|
||||
#include "logging.h"
|
||||
|
||||
extern int prop_verbose;
|
||||
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (prop_verbose) fprintf(stderr, __VA_ARGS__); }
|
||||
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
|
||||
|
||||
struct prop_t {
|
||||
char *name;
|
||||
char value[PROP_VALUE_MAX];
|
||||
};
|
||||
|
||||
struct read_cb_t {
|
||||
void (*func)(const char *name, const char *value, void *cookie);
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
char *persist_getprop(const char *name);
|
||||
void persist_getprop_all(struct read_cb_t *read_cb);
|
||||
bool persist_deleteprop(const char *name);
|
||||
void collect_props(const char *name, const char *value, void *prop_list);
|
||||
|
||||
#endif //MAGISK_PROPS_H
|
252
native/jni/resetprop/persist_props.c
Normal file
252
native/jni/resetprop/persist_props.c
Normal file
@@ -0,0 +1,252 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
#include "_resetprop.h"
|
||||
#include "utils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#define PERSISTENT_PROPERTY_DIR "/data/property"
|
||||
|
||||
/* ***********************************************************************
|
||||
* Auto generated header and constant definitions compiled from
|
||||
* android/platform/system/core/master/init/persistent_properties.proto
|
||||
* using Nanopb's protoc
|
||||
* Nanopb: https://github.com/nanopb/nanopb
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.3.9.1 at Sun Apr 22 14:36:22 2018. */
|
||||
|
||||
/* @@protoc_insertion_point(includes) */
|
||||
#if PB_PROTO_HEADER_VERSION != 30
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _PersistentProperties {
|
||||
pb_callback_t properties;
|
||||
/* @@protoc_insertion_point(struct:PersistentProperties) */
|
||||
} PersistentProperties;
|
||||
|
||||
typedef struct _PersistentProperties_PersistentPropertyRecord {
|
||||
pb_callback_t name;
|
||||
bool has_value;
|
||||
char value[92];
|
||||
/* @@protoc_insertion_point(struct:PersistentProperties_PersistentPropertyRecord) */
|
||||
} PersistentProperties_PersistentPropertyRecord;
|
||||
|
||||
/* Default values for struct fields */
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define PersistentProperties_init_default {{{NULL}, NULL}}
|
||||
#define PersistentProperties_PersistentPropertyRecord_init_default {{{NULL}, NULL}, false, ""}
|
||||
#define PersistentProperties_init_zero {{{NULL}, NULL}}
|
||||
#define PersistentProperties_PersistentPropertyRecord_init_zero {{{NULL}, NULL}, false, ""}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define PersistentProperties_properties_tag 1
|
||||
#define PersistentProperties_PersistentPropertyRecord_name_tag 1
|
||||
#define PersistentProperties_PersistentPropertyRecord_value_tag 2
|
||||
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.3.9.1 at Sun Apr 22 14:36:22 2018. */
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
const pb_field_t PersistentProperties_PersistentPropertyRecord_fields[3] = {
|
||||
PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, PersistentProperties_PersistentPropertyRecord, name, name, 0),
|
||||
PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, PersistentProperties_PersistentPropertyRecord, value, name, 0),
|
||||
PB_LAST_FIELD
|
||||
};
|
||||
|
||||
const pb_field_t PersistentProperties_fields[2] = {
|
||||
PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, PersistentProperties, properties, properties, &PersistentProperties_PersistentPropertyRecord_fields),
|
||||
PB_LAST_FIELD
|
||||
};
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* PersistentProperties_size depends on runtime parameters */
|
||||
/* PersistentProperties_PersistentPropertyRecord_size depends on runtime parameters */
|
||||
|
||||
/* Message IDs (where set with "msgid" option) */
|
||||
#ifdef PB_MSGID
|
||||
|
||||
#define PROPS_MESSAGES \
|
||||
|
||||
#endif
|
||||
/* @@protoc_insertion_point(eof) */
|
||||
|
||||
|
||||
/* ***************************
|
||||
* End of auto generated code
|
||||
* ***************************/
|
||||
|
||||
static bool name_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
uint8_t *name = xmalloc(stream->bytes_left + 1);
|
||||
name[stream->bytes_left] = '\0';
|
||||
if (!pb_read(stream, name, stream->bytes_left))
|
||||
return false;
|
||||
*arg = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool name_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
|
||||
return pb_encode_tag_for_field(stream, field) && pb_encode_string(stream, *arg, strlen(*arg));
|
||||
}
|
||||
|
||||
static bool prop_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
PersistentProperties_PersistentPropertyRecord prop = {};
|
||||
prop.name.funcs.decode = name_decode;
|
||||
if (!pb_decode(stream, PersistentProperties_PersistentPropertyRecord_fields, &prop))
|
||||
return false;
|
||||
struct read_cb_t *read_cb = *arg;
|
||||
read_cb->func(prop.name.arg, prop.value, read_cb->cookie);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prop_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
|
||||
PersistentProperties_PersistentPropertyRecord prop = {};
|
||||
prop.name.funcs.encode = name_encode;
|
||||
prop.has_value = true;
|
||||
struct vector *v = *arg;
|
||||
struct prop_t *e;
|
||||
vec_for_each(v, e) {
|
||||
if (e == NULL)
|
||||
continue;
|
||||
if (!pb_encode_tag_for_field(stream, field))
|
||||
return false;
|
||||
prop.name.arg = e->name;
|
||||
strcpy(prop.value, e->value);
|
||||
if (!pb_encode_submessage(stream, PersistentProperties_PersistentPropertyRecord_fields, &prop))
|
||||
return false;
|
||||
free(e->name);
|
||||
free(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) {
|
||||
int fd = (intptr_t)stream->state;
|
||||
return xwrite(fd, buf, count) == count;
|
||||
}
|
||||
|
||||
static pb_ostream_t create_ostream(const char *filename) {
|
||||
int fd = creat(filename, 0644);
|
||||
pb_ostream_t o = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
|
||||
return o;
|
||||
}
|
||||
|
||||
static void pb_getprop_cb(const char *name, const char *value, void *v) {
|
||||
struct prop_t *prop = v;
|
||||
if (prop->name && strcmp(name, prop->name) == 0) {
|
||||
strcpy(prop->value, value);
|
||||
prop->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void persist_getprop_all(struct read_cb_t *read_cb) {
|
||||
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
|
||||
PRINT_D("resetprop: decode with protobuf from [" PERSISTENT_PROPERTY_DIR "/persistent_properties]\n");
|
||||
PersistentProperties props = PersistentProperties_init_zero;
|
||||
props.properties.funcs.decode = prop_decode;
|
||||
props.properties.arg = read_cb;
|
||||
uint8_t *buf;
|
||||
size_t size;
|
||||
mmap_ro(PERSISTENT_PROPERTY_DIR "/persistent_properties", (void **) &buf, &size);
|
||||
pb_istream_t stream = pb_istream_from_buffer(buf, size);
|
||||
pb_decode(&stream, PersistentProperties_fields, &props);
|
||||
munmap(buf, size);
|
||||
} else {
|
||||
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
|
||||
continue;
|
||||
char *value = persist_getprop(entry->d_name);
|
||||
if (value) {
|
||||
read_cb->func(strdup(entry->d_name), value, read_cb->cookie);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *persist_getprop(const char *name) {
|
||||
struct prop_t prop;
|
||||
prop.name = (char *) name;
|
||||
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
|
||||
struct read_cb_t read_cb = {
|
||||
.func = pb_getprop_cb,
|
||||
.cookie = &prop
|
||||
};
|
||||
persist_getprop_all(&read_cb);
|
||||
if (prop.name)
|
||||
return NULL;
|
||||
} else {
|
||||
// Try to read from file
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
PRINT_D("resetprop: read prop from [%s]\n", path);
|
||||
prop.value[read(fd, prop.value, sizeof(PROP_VALUE_MAX))] = '\0'; // Null terminate the read value
|
||||
close(fd);
|
||||
}
|
||||
return strdup(prop.value);
|
||||
}
|
||||
|
||||
bool persist_deleteprop(const char *name) {
|
||||
if (access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0) {
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
struct read_cb_t read_cb = {
|
||||
.func = collect_props,
|
||||
.cookie = &v
|
||||
};
|
||||
persist_getprop_all(&read_cb);
|
||||
struct prop_t *p;
|
||||
bool reencode = false;
|
||||
vec_for_each(&v, p) {
|
||||
if (strcmp(p->name, name) == 0) {
|
||||
// Remove the prop from the list
|
||||
free(p->name);
|
||||
free(p);
|
||||
vec_cur(&v) = NULL;
|
||||
reencode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reencode) {
|
||||
// Dump the props back
|
||||
PersistentProperties props = PersistentProperties_init_zero;
|
||||
pb_ostream_t ostream = create_ostream(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
|
||||
props.properties.funcs.encode = prop_encode;
|
||||
props.properties.arg = &v;
|
||||
PRINT_D("resetprop: encode with protobuf to [" PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp]\n");
|
||||
if (!pb_encode(&ostream, PersistentProperties_fields, &props))
|
||||
return false;
|
||||
clone_attr(PERSISTENT_PROPERTY_DIR "/persistent_properties", PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
|
||||
rename(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp", PERSISTENT_PROPERTY_DIR "/persistent_properties");
|
||||
}
|
||||
|
||||
vec_destroy(&v);
|
||||
return reencode;
|
||||
} else {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
if (unlink(path) == 0) {
|
||||
PRINT_D("resetprop: unlink [%s]\n", path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
@@ -2,52 +2,6 @@
|
||||
*
|
||||
* Copyright 2016 nkk71 <nkk71x@gmail.com>
|
||||
* Copyright 2016 topjohnwu <topjohnwu@gmail.com>
|
||||
*
|
||||
* Info:
|
||||
*
|
||||
* all changes are in
|
||||
*
|
||||
* bionic/libc/bionic/system_properties.cpp
|
||||
*
|
||||
* Functions that need to be patched/added in system_properties.cpp
|
||||
*
|
||||
* int __system_properties_init2()
|
||||
* on android 7, first tear down the everything then let it initialize again:
|
||||
* if (initialized) {
|
||||
* //list_foreach(contexts, [](context_node* l) { l->reset_access(); });
|
||||
* //return 0;
|
||||
* free_and_unmap_contexts();
|
||||
* initialized = false;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* static prop_area* map_prop_area(const char* filename, bool is_legacy)
|
||||
* we dont want this read only so change: 'O_RDONLY' to 'O_RDWR'
|
||||
*
|
||||
* static prop_area* map_fd_ro(const int fd)
|
||||
* we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE'
|
||||
*
|
||||
*
|
||||
* Copy the code of prop_info *prop_area::find_property, and modify to delete props
|
||||
* const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
|
||||
* {
|
||||
* ...
|
||||
* ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed
|
||||
* ...
|
||||
*
|
||||
* if (prop_offset != 0) {
|
||||
* atomic_store_explicit(¤t->prop, 0, memory_order_release); // Add this line to nullify the prop entry
|
||||
* return to_prop_info(¤t->prop);
|
||||
* } else {
|
||||
*
|
||||
* ....
|
||||
* }
|
||||
*
|
||||
*
|
||||
* by patching just those functions directly, all other functions should be ok
|
||||
* as is.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -65,15 +19,11 @@
|
||||
|
||||
#include "magisk.h"
|
||||
#include "resetprop.h"
|
||||
extern "C" {
|
||||
#include "_resetprop.h"
|
||||
#include "vector.h"
|
||||
}
|
||||
#include "utils.h"
|
||||
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) fprintf(stderr, __VA_ARGS__); }
|
||||
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
|
||||
#define PERSISTENT_PROPERTY_DIR "/data/property"
|
||||
|
||||
static int verbose = 0;
|
||||
int prop_verbose = 0;
|
||||
|
||||
static int check_legal_property_name(const char *name) {
|
||||
int namelen = strlen(name);
|
||||
@@ -129,6 +79,49 @@ static int usage(char* arg0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The callback passes to __system_property_read_callback2, actually runs the callback in read_cb
|
||||
static void callback_wrapper(void *read_cb, const char *name, const char *value, uint32_t serial) {
|
||||
((struct read_cb_t *) read_cb)->func(name, value, ((struct read_cb_t *) read_cb)->cookie);
|
||||
}
|
||||
|
||||
/* **********************************
|
||||
* Callback functions for read_cb_t
|
||||
* **********************************/
|
||||
|
||||
void collect_props(const char *name, const char *value, void *prop_list) {
|
||||
struct prop_t *p = (struct prop_t *) xmalloc(sizeof(*p));
|
||||
p->name = strdup(name);
|
||||
strcpy(p->value, value);
|
||||
vec_push_back(prop_list, p);
|
||||
}
|
||||
|
||||
static void collect_unique_props(const char *name, const char *value, void *prop_list) {
|
||||
struct vector *v = prop_list;
|
||||
struct prop_t *p;
|
||||
bool uniq = true;
|
||||
vec_for_each(v, p) {
|
||||
if (strcmp(name, p->name) == 0) {
|
||||
uniq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uniq)
|
||||
collect_props(name, value, prop_list);
|
||||
}
|
||||
|
||||
static void store_prop_value(const char *name, const char *value, void *dst) {
|
||||
strcpy(dst, value);
|
||||
}
|
||||
|
||||
static void prop_foreach_cb(const prop_info* pi, void* read_cb) {
|
||||
__system_property_read_callback2(pi, callback_wrapper, read_cb);
|
||||
}
|
||||
|
||||
// Comparision function used to sort prop vectors
|
||||
static int prop_cmp(const void *p1, const void *p2) {
|
||||
return strcmp(((struct prop_t *) p1)->name, ((struct prop_t *) p2)->name);
|
||||
}
|
||||
|
||||
static int init_resetprop() {
|
||||
if (__system_properties_init2()) {
|
||||
PRINT_E("resetprop: Initialize error\n");
|
||||
@@ -137,16 +130,36 @@ static int init_resetprop() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_props(int persist) {
|
||||
struct prop_t *p;
|
||||
struct vector prop_list;
|
||||
vec_init(&prop_list);
|
||||
getprop_all(collect_props, &prop_list);
|
||||
if (persist) {
|
||||
struct read_cb_t read_cb = {
|
||||
.func = collect_unique_props,
|
||||
.cookie = &prop_list
|
||||
};
|
||||
persist_getprop_all(&read_cb);
|
||||
}
|
||||
vec_sort(&prop_list, prop_cmp);
|
||||
vec_for_each(&prop_list, p) {
|
||||
printf("[%s]: [%s]\n", p->name, p->value);
|
||||
free(p->name);
|
||||
free(p);
|
||||
}
|
||||
vec_destroy(&prop_list);
|
||||
}
|
||||
|
||||
/* **************************************************
|
||||
* Implementations of functions in resetprop.h (APIs)
|
||||
* **************************************************/
|
||||
|
||||
int prop_exist(const char *name) {
|
||||
if (init_resetprop()) return 0;
|
||||
return __system_property_find2(name) != NULL;
|
||||
}
|
||||
|
||||
static void read_prop_info(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
strcpy((char *) cookie, value);
|
||||
}
|
||||
|
||||
|
||||
char *getprop(const char *name) {
|
||||
return getprop2(name, 0);
|
||||
}
|
||||
@@ -155,103 +168,35 @@ char *getprop(const char *name) {
|
||||
char *getprop2(const char *name, int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return NULL;
|
||||
char value[PROP_VALUE_MAX];
|
||||
if (init_resetprop()) return NULL;
|
||||
const prop_info *pi = __system_property_find2(name);
|
||||
if (pi == NULL) {
|
||||
if (persist && strncmp(name, "persist.", 8) == 0) {
|
||||
// Try to read from file
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) goto no_prop;
|
||||
PRINT_D("resetprop: read prop from [%s]\n", path);
|
||||
size_t len = read(fd, value, sizeof(value));
|
||||
value[len] = '\0'; // Null terminate the read value
|
||||
} else {
|
||||
no_prop:
|
||||
char *value = persist_getprop(name);
|
||||
if (value)
|
||||
return value;
|
||||
}
|
||||
PRINT_D("resetprop: prop [%s] does not exist\n", name);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
__system_property_read_callback2(pi, read_prop_info, value);
|
||||
char value[PROP_VALUE_MAX];
|
||||
struct read_cb_t read_cb = {
|
||||
.func = store_prop_value,
|
||||
.cookie = value
|
||||
};
|
||||
__system_property_read_callback2(pi, callback_wrapper, &read_cb);
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
}
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
struct wrapper {
|
||||
void (*func)(const char *, const char *);
|
||||
};
|
||||
|
||||
static void cb_wrapper(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
((wrapper *) cookie)->func(name, value);
|
||||
}
|
||||
|
||||
static void prop_foreach_cb(const prop_info* pi, void* cookie) {
|
||||
__system_property_read_callback2(pi, cb_wrapper, cookie);
|
||||
}
|
||||
|
||||
class property {
|
||||
public:
|
||||
property(const char *n, const char *v) {
|
||||
name = strdup(n);
|
||||
value = strdup(v);
|
||||
}
|
||||
~property() {
|
||||
free((void *)name);
|
||||
free((void *)value);
|
||||
}
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
vector prop_list;
|
||||
|
||||
static int prop_cmp(const void *p1, const void *p2) {
|
||||
return strcmp(((property *) p1)->name, ((property *) p2)->name);
|
||||
}
|
||||
|
||||
static void print_all_props_cb(const char *name, const char *value) {
|
||||
vec_push_back(&prop_list, new property(name, value));
|
||||
}
|
||||
|
||||
static void print_all_props(int persist) {
|
||||
void *p;
|
||||
vec_init(&prop_list);
|
||||
getprop_all(print_all_props_cb);
|
||||
if (persist) {
|
||||
// Check all persist props in data
|
||||
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
|
||||
continue;
|
||||
int found = 0;
|
||||
vec_for_each(&prop_list, p) {
|
||||
if (strcmp(((property *) p)->name, entry->d_name) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
vec_push_back(&prop_list, new property(entry->d_name, getprop2(entry->d_name, 1)));
|
||||
}
|
||||
}
|
||||
vec_sort(&prop_list, prop_cmp);
|
||||
vec_for_each(&prop_list, p) {
|
||||
printf("[%s]: [%s]\n", ((property *) p)->name, ((property *) p)->value);
|
||||
delete((property *) p);
|
||||
}
|
||||
vec_destroy(&prop_list);
|
||||
}
|
||||
|
||||
void getprop_all(void (*callback)(const char*, const char*)) {
|
||||
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie) {
|
||||
if (init_resetprop()) return;
|
||||
struct wrapper wrap = {
|
||||
.func = callback
|
||||
struct read_cb_t read_cb = {
|
||||
.func = callback,
|
||||
.cookie = cookie
|
||||
};
|
||||
__system_property_foreach2(prop_foreach_cb, &wrap);
|
||||
__system_property_foreach2(prop_foreach_cb, &read_cb);
|
||||
}
|
||||
|
||||
int setprop(const char *name, const char *value) {
|
||||
@@ -294,7 +239,7 @@ int deleteprop(const char *name) {
|
||||
return deleteprop2(name, 1);
|
||||
}
|
||||
|
||||
int deleteprop2(const char *name, const int persist) {
|
||||
int deleteprop2(const char *name, int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
@@ -302,8 +247,8 @@ int deleteprop2(const char *name, const int persist) {
|
||||
path[0] = '\0';
|
||||
PRINT_D("resetprop: deleteprop [%s]\n", name);
|
||||
if (persist && strncmp(name, "persist.", 8) == 0)
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
return __system_property_del(name) && unlink(path);
|
||||
persist = persist_deleteprop(name);
|
||||
return __system_property_del(name) && !(persist && strncmp(name, "persist.", 8) == 0);
|
||||
}
|
||||
|
||||
int read_prop_file(const char* filename, const int trigger) {
|
||||
@@ -336,7 +281,7 @@ int read_prop_file(const char* filename, const int trigger) {
|
||||
}
|
||||
if (comment) continue;
|
||||
pch = strchr(line, '=');
|
||||
// Ignore ivalid formats
|
||||
// Ignore invalid formats
|
||||
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
@@ -367,7 +312,7 @@ int resetprop_main(int argc, char *argv[]) {
|
||||
goto usage;
|
||||
}
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
prop_verbose = 1;
|
||||
continue;
|
||||
case 'p':
|
||||
persist = 1;
|
||||
@@ -390,7 +335,7 @@ int resetprop_main(int argc, char *argv[]) {
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
print_all_props(persist);
|
||||
print_props(persist);
|
||||
return 0;
|
||||
case 1:
|
||||
prop = getprop2(argv[0], persist);
|
@@ -153,7 +153,7 @@ class prop_area {
|
||||
}
|
||||
|
||||
const prop_info* find(const char* name);
|
||||
bool del(const char *name); // resetprop add
|
||||
bool del(const char *name); /* resetprop add */
|
||||
bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen);
|
||||
|
||||
bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
|
||||
@@ -184,7 +184,7 @@ class prop_area {
|
||||
const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen,
|
||||
const char* value, uint32_t valuelen, bool alloc_if_needed);
|
||||
|
||||
bool find_property_and_del(prop_bt *const trie, const char *name); // resetprop add
|
||||
bool find_property_and_del(prop_bt *const trie, const char *name); /* resetprop add */
|
||||
|
||||
bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie),
|
||||
void* cookie);
|
||||
@@ -283,7 +283,8 @@ static prop_area* map_prop_area_rw(const char* filename, const char* context,
|
||||
return pa;
|
||||
}
|
||||
|
||||
static prop_area* map_fd_ro(const int fd) {
|
||||
// resetprop: map the memory as rw
|
||||
static prop_area* map_fd_rw(const int fd) {
|
||||
struct stat fd_stat;
|
||||
if (fstat(fd, &fd_stat) < 0) {
|
||||
return nullptr;
|
||||
@@ -298,7 +299,7 @@ static prop_area* map_fd_ro(const int fd) {
|
||||
pa_size = fd_stat.st_size;
|
||||
pa_data_size = pa_size - sizeof(prop_area);
|
||||
|
||||
void* const map_result = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // resetprop: add PROT_WRITE
|
||||
void* const map_result = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); /* resetprop: add PROT_WRITE */
|
||||
if (map_result == MAP_FAILED) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -313,10 +314,10 @@ static prop_area* map_fd_ro(const int fd) {
|
||||
}
|
||||
|
||||
static prop_area* map_prop_area(const char* filename) {
|
||||
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // resetprop: O_RDONLY -> O_RDWR
|
||||
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); /* resetprop: O_RDONLY -> O_RDWR */
|
||||
if (fd == -1) return nullptr;
|
||||
|
||||
prop_area* map_result = map_fd_ro(fd);
|
||||
prop_area* map_result = map_fd_rw(fd);
|
||||
close(fd);
|
||||
|
||||
return map_result;
|
||||
@@ -530,7 +531,7 @@ bool prop_area::find_property_and_del(prop_bt* const trie, const char* name) {
|
||||
|
||||
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
|
||||
if (prop_offset != 0) {
|
||||
atomic_store_explicit(¤t->prop, 0, memory_order_release); // resetprop: nullify the offset to delete the prop
|
||||
atomic_store_explicit(¤t->prop, 0, memory_order_release);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1116,17 +1117,16 @@ static bool initialize_properties() {
|
||||
if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
|
||||
return false;
|
||||
}
|
||||
if (!initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts")
|
||||
&& !initialize_properties_from_file("/vendor/etc/selinux/vendor_property_contexts")) {
|
||||
return false;
|
||||
}
|
||||
// Don't check for failure here, so we always have a sane list of properties.
|
||||
// E.g. In case of recovery, the vendor partition will not have mounted and we
|
||||
// still need the system / platform properties to function.
|
||||
initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts") ||
|
||||
initialize_properties_from_file("/vendor/etc/selinux/vendor_property_contexts");
|
||||
} else {
|
||||
if (!initialize_properties_from_file("/plat_property_contexts")) {
|
||||
return false;
|
||||
}
|
||||
if (!initialize_properties_from_file("/nonplat_property_contexts")) {
|
||||
return false;
|
||||
}
|
||||
initialize_properties_from_file("/nonplat_property_contexts");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
Submodule native/jni/su updated: ed5dd827e9...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,18 +340,23 @@ 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, int force) {
|
||||
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);
|
||||
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
size_t len = strlen(path);
|
||||
getfilecon(path, &con);
|
||||
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
|
||||
@@ -361,25 +365,70 @@ void restorecon(int dirfd, int force) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(fd, force);
|
||||
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 (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
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) {
|
||||
struct stat st;
|
||||
int fd = xopen(filename, rw ? O_RDWR : O_RDONLY);
|
||||
int fd = xopen(filename, (rw ? O_RDWR : O_RDONLY) | O_CLOEXEC);
|
||||
fstat(fd, &st);
|
||||
if (S_ISBLK(st.st_mode))
|
||||
ioctl(fd, BLKGETSIZE64, size);
|
||||
@@ -407,7 +456,7 @@ void fd_full_read(int fd, void **buf, size_t *size) {
|
||||
}
|
||||
|
||||
void full_read(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
@@ -418,7 +467,7 @@ void full_read(const char *filename, void **buf, size_t *size) {
|
||||
}
|
||||
|
||||
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopenat(dirfd, filename, O_RDONLY);
|
||||
int fd = xopenat(dirfd, filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
|
@@ -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
|
||||
@@ -157,7 +145,7 @@ fi
|
||||
ui_print "- Patching ramdisk"
|
||||
|
||||
./magiskboot --cpio ramdisk.cpio \
|
||||
'add 750 init magiskinit' \
|
||||
"add 750 init magiskinit" \
|
||||
"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1"
|
||||
|
||||
rm -f ramdisk.cpio.orig
|
||||
@@ -166,8 +154,9 @@ rm -f ramdisk.cpio.orig
|
||||
# Binary patches
|
||||
##########################################################################################
|
||||
|
||||
if ! $KEEPVERITY && [ -f dtb ]; then
|
||||
./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
|
||||
|
@@ -48,28 +48,23 @@ 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"
|
||||
|
||||
BINDIR=$INSTALLER/$ARCH
|
||||
BINDIR=$INSTALLER/$ARCH32
|
||||
chmod -R 755 $CHROMEDIR $BINDIR
|
||||
|
||||
# Check if system root is installed and remove
|
||||
@@ -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/addon.d/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"
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
CHROMEOS=false
|
||||
|
||||
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"
|
||||
@@ -71,22 +94,29 @@ ui_print "- Checking ramdisk status"
|
||||
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=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
OK=false
|
||||
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true
|
||||
if ! $OK; then
|
||||
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"
|
||||
./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
|
||||
@@ -24,15 +23,11 @@ BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.top
|
||||
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,36 +143,10 @@ 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
|
||||
@@ -140,11 +162,9 @@ run_migrations() {
|
||||
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,6 +172,19 @@ 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
|
||||
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
|
||||
@@ -178,16 +211,15 @@ 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 $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
|
||||
ui_print "- Backing up stock dtbo image"
|
||||
ui_print "- Backing up stock DTBO image"
|
||||
$MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
|
||||
ui_print "- Patching fstab in dtbo to remove avb-verity"
|
||||
ui_print "- Patching DTBO to remove avb-verity"
|
||||
$MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE
|
||||
return 0
|
||||
fi
|
||||
@@ -195,27 +227,6 @@ patch_dtbo_image() {
|
||||
return 1
|
||||
}
|
||||
|
||||
restore_imgs() {
|
||||
STOCKBOOT=/data/stock_boot_${1}.img.gz
|
||||
STOCKDTBO=/data/stock_dtbo.img.gz
|
||||
|
||||
# Make sure all blocks are writable
|
||||
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
|
||||
find_dtbo_image
|
||||
if [ ! -z "$DTBOIMAGE" -a -f "$STOCKDTBO" ]; then
|
||||
ui_print "- Restoring stock dtbo image"
|
||||
gzip -d < $STOCKDTBO | dd of=$DTBOIMAGE
|
||||
fi
|
||||
BOOTSIGNED=false
|
||||
find_boot_image
|
||||
if [ ! -z "$BOOTIMAGE" -a -f "$STOCKBOOT" ]; then
|
||||
ui_print "- Restoring stock boot image"
|
||||
gzip -d < $STOCKBOOT | cat - /dev/zero 2>/dev/null | dd of="$BOOTIMAGE" bs=4096 2>/dev/null
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
sign_chromeos() {
|
||||
ui_print "- Signing ChromeOS boot image"
|
||||
|
||||
@@ -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 $?
|
||||
}
|
||||
|
||||
@@ -266,11 +276,39 @@ api_level_arch_detect() {
|
||||
ABILONG=`grep_prop ro.product.cpu.abi`
|
||||
|
||||
ARCH=arm
|
||||
ARCH32=arm
|
||||
IS64BIT=false
|
||||
if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
|
||||
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
|
||||
if [ "$ABI" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
|
||||
if [ "$ABI2" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
|
||||
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; ARCH32=arm; IS64BIT=true; fi;
|
||||
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() {
|
||||
@@ -279,6 +317,7 @@ boot_actions() {
|
||||
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
|
||||
fi
|
||||
MAGISKBIN=/sbin/.core/mirror/bin
|
||||
setup_bb
|
||||
}
|
||||
|
||||
recovery_actions() {
|
||||
@@ -286,24 +325,21 @@ recovery_actions() {
|
||||
mount -o bind /dev/urandom /dev/random
|
||||
# Preserve environment varibles
|
||||
OLD_PATH=$PATH
|
||||
OLD_LD_PATH=$LD_LIBRARY_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
|
||||
# Add all possible library paths
|
||||
$IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export 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
|
||||
export LD_LIBRARY_PATH=$OLD_LD_PATH
|
||||
[ -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="`$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"
|
||||
$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"
|
||||
$MAGISKBIN/magisk --createimg $IMG $newSizeM >&2
|
||||
$MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
|
||||
mount_snippet
|
||||
fi
|
||||
|
||||
ui_print "- Mounting $IMG to $MOUNTPATH"
|
||||
MAGISKLOOP=`$MAGISKBIN/magisk --mountimg $IMG $MOUNTPATH`
|
||||
is_mounted $MOUNTPATH || abort "! $IMG mount failed..."
|
||||
}
|
||||
|
||||
unmount_magisk_img() {
|
||||
$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"
|
||||
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM
|
||||
$MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
@@ -1,13 +1,13 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.3"
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.topjohnwu.snet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 27
|
||||
minSdkVersion 14
|
||||
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@@ -15,16 +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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
}
|
||||
|
||||
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