mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 15:27:25 +00:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d8bb3af06b | ||
![]() |
e139e8777b | ||
![]() |
d52d7cfbd9 | ||
![]() |
4f74a259e3 | ||
![]() |
74da6e1dc0 | ||
![]() |
84ffdf0ed5 | ||
![]() |
022b18c8ce | ||
![]() |
b92b1dcddb | ||
![]() |
1472dbb291 | ||
![]() |
d58a8dc868 | ||
![]() |
e94be0b70e | ||
![]() |
f6ae7e1bf1 | ||
![]() |
f7b4935677 | ||
![]() |
a3c49de6a5 | ||
![]() |
e8dd1b292f | ||
![]() |
d21264d01b | ||
![]() |
b0567eadfd | ||
![]() |
5fc2058336 | ||
![]() |
d0567d29d2 | ||
![]() |
4db0ad32f0 | ||
![]() |
d065040321 | ||
![]() |
17f0fea3fc | ||
![]() |
8ca1e43533 | ||
![]() |
bd01c314dc | ||
![]() |
e404476609 | ||
![]() |
942c870981 | ||
![]() |
baff9256c5 | ||
![]() |
b4c0a255fc | ||
![]() |
9f6a27c20d | ||
![]() |
742dc137ed | ||
![]() |
39a6bd33ce | ||
![]() |
4672a5fad6 | ||
![]() |
e649b0a2df | ||
![]() |
fd8dbe3eff | ||
![]() |
bb97cc594d | ||
![]() |
70a322263e | ||
![]() |
c6f144d482 | ||
![]() |
3709489b3a | ||
![]() |
145ef32e28 | ||
![]() |
2212800a23 | ||
![]() |
2e25431bb6 | ||
![]() |
32c8e7522f | ||
![]() |
a5e4f3cc6b | ||
![]() |
a30777bd9f | ||
![]() |
e989195a68 | ||
![]() |
997d58932e | ||
![]() |
b4015f877f | ||
![]() |
d15fff95b9 |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,9 +1,16 @@
|
||||
out/
|
||||
obj/
|
||||
libs/
|
||||
out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
|
||||
# Built binaries
|
||||
ziptools/zipadjust
|
||||
|
||||
# Android Studio / Gradle
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
18
.gitmodules
vendored
18
.gitmodules
vendored
@@ -1,27 +1,27 @@
|
||||
[submodule "jni/selinux"]
|
||||
path = jni/external/selinux
|
||||
path = core/jni/external/selinux
|
||||
url = https://github.com/topjohnwu/selinux.git
|
||||
[submodule "jni/su"]
|
||||
path = jni/su
|
||||
path = core/jni/su
|
||||
url = https://github.com/topjohnwu/MagiskSU.git
|
||||
[submodule "jni/magiskpolicy"]
|
||||
path = jni/magiskpolicy
|
||||
path = core/jni/magiskpolicy
|
||||
url = https://github.com/topjohnwu/magiskpolicy.git
|
||||
[submodule "MagiskManager"]
|
||||
path = java
|
||||
path = app
|
||||
url = https://github.com/topjohnwu/MagiskManager.git
|
||||
[submodule "jni/busybox"]
|
||||
path = jni/external/busybox
|
||||
path = core/jni/external/busybox
|
||||
url = https://github.com/topjohnwu/ndk-busybox.git
|
||||
[submodule "jni/external/dtc"]
|
||||
path = jni/external/dtc
|
||||
path = core/jni/external/dtc
|
||||
url = https://github.com/dgibson/dtc
|
||||
[submodule "jni/external/lz4"]
|
||||
path = jni/external/lz4
|
||||
path = core/jni/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "jni/external/bzip2"]
|
||||
path = jni/external/bzip2
|
||||
path = core/jni/external/bzip2
|
||||
url = https://github.com/nemequ/bzip2.git
|
||||
[submodule "jni/external/xz"]
|
||||
path = jni/external/xz
|
||||
path = core/jni/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
|
33
README.MD
33
README.MD
@@ -11,14 +11,15 @@
|
||||
3. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
|
||||
4. Latest Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
|
||||
5. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or specify custom path `ANDROID_NDK`
|
||||
6. (Windows Only) Colorama: Install Colorama with `pip install colorama`, used for ANSI color codes
|
||||
6. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
|
||||
7. (Unix only) C compiler: Build `zipadjust`. Windows users can use the pre-built `zipadjust.exe`
|
||||
|
||||
### Instructions and Notes
|
||||
1. Magisk can be built with the latest NDK (r16 as of writing), however official released binaries will be built with NDK r10e due to ELF incompatibilities with the binaries built from newer NDKs.
|
||||
2. The easiest way to setup a working environment is to open Magisk Manager with Android Studio. The IDE will download required components and construct the environment for you. Don't forget to set `ANDROID_HOME` environment variable to the SDK path.
|
||||
3. Run the script with `-h` as an argument to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
||||
4. By default, the script will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place your Java Keystore file at `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||
1. Magisk can be built with the latest NDK (r16 as of writing), however binaries released officially will be built with NDK r10e due to ELF incompatibilities with the binaries built from the newer NDKs.
|
||||
2. The easiest way to setup the environment is by importing this folder as an Android Studio project. The IDE will download required components and construct the environment for you. You still have to set the `ANDROID_HOME` environment variable to point to the SDK path.
|
||||
3. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
||||
4. Build everything with `build.py`, don't directly call `gradlew` or `ndk-build`, since most requires special setup / dependencies.
|
||||
5. By default, `build.py` will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the flag `--release`), you will need to place a Java Keystore file at `release_signature.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||
|
||||
|
||||
## License
|
||||
@@ -40,12 +41,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Credits
|
||||
|
||||
**MagiskManager** (`java`)
|
||||
**MagiskManager** (`app`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* All contributors and translators
|
||||
* All contributors and translators on Github
|
||||
|
||||
**MagiskSU** (`jni/su`)
|
||||
**MagiskSU** (`core/jni/su`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
@@ -53,28 +54,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
* Copyright 2008, Zinx Verituse (@zinxv)
|
||||
|
||||
**MagiskPolicy** (`jni/magiskpolicy`)
|
||||
**MagiskPolicy** (`core/jni/magiskpolicy`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
* Copyright 2015, Joshua Brindle (@joshua_brindle)
|
||||
|
||||
**MagiskHide** (`jni/magiskhide`)
|
||||
**MagiskHide** (`core/jni/magiskhide`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me)
|
||||
|
||||
**resetprop** (`jni/resetprop`)
|
||||
**resetprop** (`core/jni/resetprop`)
|
||||
|
||||
* Copyright 2016-2017 John Wu (@topjohnwu)
|
||||
* Copyright 2016 nkk71 (nkk71x@gmail.com)
|
||||
|
||||
**ndk-busybox** (`jni/external/busybox`)
|
||||
**External Dependencies** (`core/jni/external`)
|
||||
|
||||
* Makefile for NDK, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen): Copyright 2017, John Wu (@topjohnwu)
|
||||
* Patches for NDK: Many contributors along the way, all placed in [osm0sis/android-busybox-ndk](https://github.com/osm0sis/android-busybox-ndk)
|
||||
* The copyright message for busybox should be included in its own directory
|
||||
* Makefile for busybox, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen)
|
||||
* Each dependencies has its own license/copyright information in each subdirectory.
|
||||
All of them are either GPL or GPL compatible.
|
||||
|
||||
**Others Not Mentioned** (exclude `jni/external`)
|
||||
**Others Not Mentioned**
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
|
1
app
Submodule
1
app
Submodule
Submodule app added at 1adf331268
27
build.gradle
Normal file
27
build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
76
build.py
76
build.py
@@ -43,6 +43,11 @@ import lzma
|
||||
import base64
|
||||
import tempfile
|
||||
|
||||
if 'ANDROID_NDK' in os.environ:
|
||||
ndk_build = os.path.join(os.environ['ANDROID_NDK'], 'ndk-build')
|
||||
else:
|
||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||
|
||||
def mv(source, target):
|
||||
print('mv: {} -> {}'.format(source, target))
|
||||
shutil.move(source, target)
|
||||
@@ -53,7 +58,7 @@ def cp(source, target):
|
||||
|
||||
def rm(file):
|
||||
try:
|
||||
os.remove(file)
|
||||
os.remove(file)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
@@ -83,19 +88,14 @@ def build_all(args):
|
||||
def build_binary(args):
|
||||
header('* Building Magisk binaries')
|
||||
|
||||
# Force update Android.mk timestamp to trigger recompilation
|
||||
os.utime(os.path.join('jni', 'Android.mk'))
|
||||
# Force update logging.h timestamp to trigger recompilation
|
||||
os.utime(os.path.join('core', 'jni', 'include', 'logging.h'))
|
||||
|
||||
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
|
||||
cflag = 'MAGISK_FLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\"'.format(args.versionString, args.versionCode, debug_flag)
|
||||
|
||||
if 'ANDROID_NDK' in os.environ:
|
||||
ndk_build = os.path.join(os.environ['ANDROID_NDK'], 'ndk-build')
|
||||
else:
|
||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||
|
||||
# Prebuild
|
||||
proc = subprocess.run('{} PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
proc = subprocess.run('{} -C core PRECOMPILE=true {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
|
||||
@@ -104,7 +104,7 @@ def build_binary(args):
|
||||
mkdir_p(os.path.join('out', arch))
|
||||
with open(os.path.join('out', arch, 'dump.h'), 'w') as dump:
|
||||
dump.write('#include "stdlib.h"\n')
|
||||
mv(os.path.join('libs', arch, 'magisk'), os.path.join('out', arch, 'magisk'))
|
||||
mv(os.path.join('core', 'libs', arch, 'magisk'), os.path.join('out', arch, 'magisk'))
|
||||
with open(os.path.join('out', arch, 'magisk'), 'rb') as bin:
|
||||
dump.write('const uint8_t magisk_dump[] = "')
|
||||
dump.write(''.join("\\x{:02X}".format(c) for c in lzma.compress(bin.read(), preset=9)))
|
||||
@@ -112,7 +112,7 @@ def build_binary(args):
|
||||
|
||||
print('')
|
||||
|
||||
proc = subprocess.run('{} {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
proc = subprocess.run('{} -C core {} -j{}'.format(ndk_build, cflag, multiprocessing.cpu_count()), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
|
||||
@@ -120,7 +120,7 @@ def build_binary(args):
|
||||
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
for binary in ['magiskinit', 'magiskboot', 'b64xz', 'busybox']:
|
||||
try:
|
||||
mv(os.path.join('libs', arch, binary), os.path.join('out', arch, binary))
|
||||
mv(os.path.join('core', 'libs', arch, binary), os.path.join('out', arch, binary))
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -129,18 +129,16 @@ def build_apk(args):
|
||||
|
||||
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
|
||||
source = os.path.join('ziptools', key)
|
||||
target = os.path.join('java', 'app', 'src', 'main', 'assets', key)
|
||||
target = os.path.join('app', 'src', 'main', 'assets', key)
|
||||
cp(source, target)
|
||||
|
||||
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
|
||||
source = os.path.join('scripts', script)
|
||||
target = os.path.join('java', 'app', 'src', 'main', 'assets', script)
|
||||
target = os.path.join('app', 'src', 'main', 'assets', script)
|
||||
cp(source, target)
|
||||
|
||||
os.chdir('java')
|
||||
|
||||
if args.release:
|
||||
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
|
||||
if not os.path.exists('release_signature.jks'):
|
||||
error('Please generate a java keystore and place it in \'release_signature.jks\'')
|
||||
|
||||
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
@@ -173,17 +171,15 @@ def build_apk(args):
|
||||
error('Cannot find apksigner.jar in Android SDK build tools')
|
||||
|
||||
proc = subprocess.run('java -jar {} sign --ks {} --out {} {}'.format(
|
||||
apksigner,
|
||||
os.path.join('..', 'release_signature.jks'),
|
||||
release, aligned), shell=True)
|
||||
apksigner, 'release_signature.jks', release, aligned), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Release sign Magisk Manager failed!')
|
||||
|
||||
rm(unsigned)
|
||||
rm(aligned)
|
||||
|
||||
mkdir(os.path.join('..', 'out'))
|
||||
target = os.path.join('..', 'out', 'app-release.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'app-release.apk')
|
||||
print('')
|
||||
mv(release, target)
|
||||
else:
|
||||
@@ -192,25 +188,20 @@ def build_apk(args):
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
source = os.path.join('app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
|
||||
mkdir(os.path.join('..', 'out'))
|
||||
target = os.path.join('..', 'out', 'app-debug.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'app-debug.apk')
|
||||
print('')
|
||||
mv(source, target)
|
||||
|
||||
# Return to upper directory
|
||||
os.chdir('..')
|
||||
|
||||
def build_snet(args):
|
||||
os.chdir('java')
|
||||
proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build snet extention failed!')
|
||||
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
|
||||
mkdir(os.path.join('..', 'out'))
|
||||
target = os.path.join('..', 'out', 'snet.apk')
|
||||
mkdir('out')
|
||||
target = os.path.join('out', 'snet.apk')
|
||||
print('')
|
||||
mv(source, target)
|
||||
os.chdir('..')
|
||||
|
||||
def gen_update_binary():
|
||||
update_bin = []
|
||||
@@ -275,7 +266,7 @@ def zip_main(args):
|
||||
with open(source, 'r') as script:
|
||||
# Add version info util_functions.sh
|
||||
util_func = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode))
|
||||
'#MAGISK_VERSION_STUB', 'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(args.versionString, args.versionCode))
|
||||
target = os.path.join('common', 'util_functions.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, util_func)
|
||||
@@ -325,11 +316,9 @@ def zip_uninstaller(args):
|
||||
source = os.path.join('scripts', 'util_functions.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Remove the stub
|
||||
util_func = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', '')
|
||||
target = os.path.join('util_functions.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, util_func)
|
||||
zipf.writestr(target, script.read())
|
||||
|
||||
# Prebuilts
|
||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
@@ -342,8 +331,8 @@ def zip_uninstaller(args):
|
||||
sign_adjust_zip(unsigned, output)
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
signer_name = 'zipsigner-1.0.jar'
|
||||
jarsigner = os.path.join('java', 'crypto', 'build', 'libs', signer_name)
|
||||
signer_name = 'zipsigner-1.1.jar'
|
||||
jarsigner = os.path.join('crypto', 'build', 'libs', signer_name)
|
||||
|
||||
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
|
||||
header('* Building zipadjust')
|
||||
@@ -353,11 +342,9 @@ def sign_adjust_zip(unsigned, output):
|
||||
error('Build zipadjust failed!')
|
||||
if not os.path.exists(jarsigner):
|
||||
header('* Building ' + signer_name)
|
||||
os.chdir('java')
|
||||
proc = subprocess.run('{} crypto:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build {} failed!'.format(signer_name))
|
||||
os.chdir('..')
|
||||
|
||||
header('* Signing / Adjusting Zip')
|
||||
|
||||
@@ -396,15 +383,14 @@ def cleanup(args):
|
||||
|
||||
if 'binary' in args.target:
|
||||
header('* Cleaning binaries')
|
||||
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
|
||||
subprocess.run(ndk_build + ' -C core PRECOMPILE=true clean', shell=True)
|
||||
subprocess.run(ndk_build + ' -C core clean', shell=True)
|
||||
for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
|
||||
shutil.rmtree(os.path.join('out', arch), ignore_errors=True)
|
||||
|
||||
if 'java' in args.target:
|
||||
header('* Cleaning java')
|
||||
os.chdir('java')
|
||||
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
os.chdir('..')
|
||||
subprocess.run('{} app:clean snet:clean crypto:clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
for f in os.listdir('out'):
|
||||
if '.apk' in f:
|
||||
rm(os.path.join('out', f))
|
||||
@@ -448,8 +434,8 @@ clean_parser.add_argument('target', nargs='*')
|
||||
clean_parser.set_defaults(func=cleanup)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
3
core/.gitignore
vendored
Normal file
3
core/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/build
|
||||
obj
|
||||
libs
|
20
core/build.gradle
Normal file
20
core/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path 'jni/Android.mk'
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
// Passes an optional argument to ndk-build.
|
||||
arguments "GRADLE=true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,12 +9,20 @@ LIBLZMA := $(EXT_PATH)/xz/src/liblzma/api
|
||||
LIBLZ4 := $(EXT_PATH)/lz4/lib
|
||||
LIBBZ2 := $(EXT_PATH)/bzip2
|
||||
LIBFDT := $(EXT_PATH)/dtc/libfdt
|
||||
UTIL_SRC := utils/cpio.c \
|
||||
utils/file.c \
|
||||
utils/img.c \
|
||||
utils/list.c \
|
||||
utils/misc.c \
|
||||
utils/pattern.c \
|
||||
utils/vector.c \
|
||||
utils/xwrap.c
|
||||
|
||||
########################
|
||||
# Binaries
|
||||
########################
|
||||
|
||||
ifdef PRECOMPILE
|
||||
ifneq "$(or $(PRECOMPILE), $(GRADLE))" ""
|
||||
|
||||
# magisk main binary
|
||||
include $(CLEAR_VARS)
|
||||
@@ -23,7 +31,7 @@ LOCAL_SHARED_LIBRARIES := libsqlite libselinux
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/external \
|
||||
jni/external/include \
|
||||
$(LIBSELINUX)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
@@ -31,12 +39,7 @@ LOCAL_SRC_FILES := \
|
||||
core/daemon.c \
|
||||
core/log_monitor.c \
|
||||
core/bootstages.c \
|
||||
utils/misc.c \
|
||||
utils/vector.c \
|
||||
utils/xwrap.c \
|
||||
utils/list.c \
|
||||
utils/img.c \
|
||||
utils/file.c \
|
||||
core/socket.c \
|
||||
magiskhide/magiskhide.c \
|
||||
magiskhide/proc_monitor.c \
|
||||
magiskhide/hide_utils.c \
|
||||
@@ -47,14 +50,16 @@ LOCAL_SRC_FILES := \
|
||||
su/db.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c \
|
||||
su/su_socket.c
|
||||
su/su_socket.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_CFLAGS := -DIS_DAEMON
|
||||
LOCAL_CFLAGS := -DIS_DAEMON -DSELINUX
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# precompile
|
||||
else
|
||||
endif
|
||||
|
||||
ifndef PRECOMPILE
|
||||
|
||||
# magiskinit
|
||||
include $(CLEAR_VARS)
|
||||
@@ -62,21 +67,20 @@ LOCAL_MODULE := magiskinit
|
||||
LOCAL_STATIC_LIBRARIES := libsepol liblzma
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
out/$(TARGET_ARCH_ABI) \
|
||||
jni/magiskpolicy \
|
||||
../out/$(TARGET_ARCH_ABI) \
|
||||
$(LIBSEPOL) \
|
||||
$(LIBLZMA)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/magiskinit.c \
|
||||
utils/vector.c \
|
||||
utils/file.c \
|
||||
utils/xwrap.c \
|
||||
core/socket.c \
|
||||
magiskpolicy/api.c \
|
||||
magiskpolicy/magiskpolicy.c \
|
||||
magiskpolicy/rules.c \
|
||||
magiskpolicy/sepolicy.c
|
||||
magiskpolicy/sepolicy.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_CFLAGS := -DNO_SELINUX
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
@@ -86,29 +90,29 @@ LOCAL_MODULE := magiskboot
|
||||
LOCAL_STATIC_LIBRARIES := liblzma liblz4 libbz2 libfdt
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/external/include \
|
||||
$(LIBLZMA) \
|
||||
$(LIBLZ4) \
|
||||
$(LIBBZ2) \
|
||||
$(LIBFDT)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
external/sha1/sha1.c \
|
||||
magiskboot/main.c \
|
||||
magiskboot/bootimg.c \
|
||||
magiskboot/hexpatch.c \
|
||||
magiskboot/compress.c \
|
||||
magiskboot/cpio.c \
|
||||
magiskboot/sha1.c \
|
||||
magiskboot/types.c \
|
||||
magiskboot/dtb.c \
|
||||
utils/xwrap.c \
|
||||
utils/file.c \
|
||||
utils/vector.c
|
||||
magiskboot/ramdisk.c \
|
||||
$(UTIL_SRC)
|
||||
|
||||
LOCAL_CFLAGS := -DNO_SELINUX
|
||||
LOCAL_CFLAGS := -DXWRAP_EXIT
|
||||
LOCAL_LDLIBS := -lz
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# 32-bit static binaries
|
||||
ifndef GRADLE # Do not run gradle sync on these binaries
|
||||
ifneq ($(TARGET_ARCH_ABI), x86_64)
|
||||
ifneq ($(TARGET_ARCH_ABI), arm64-v8a)
|
||||
# b64xz
|
||||
@@ -123,6 +127,7 @@ include $(BUILD_EXECUTABLE)
|
||||
include jni/external/busybox/Android.mk
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Precompile
|
||||
endif
|
@@ -11,8 +11,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <linux/loop.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <selinux/selinux.h>
|
||||
@@ -24,7 +22,6 @@
|
||||
|
||||
static char *buf, *buf2;
|
||||
static struct vector module_list;
|
||||
static int seperate_vendor = 0;
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@@ -395,107 +392,16 @@ static void simple_mount(const char *path) {
|
||||
* Miscellaneous *
|
||||
*****************/
|
||||
|
||||
// A one time setup
|
||||
static void daemon_init() {
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin;
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir("/root", 0755);
|
||||
chmod("/root", 0755);
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
dir = xfdopendir(sbin);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
linkat(sbin, entry->d_name, root, entry->d_name, 0);
|
||||
if (strcmp(entry->d_name, "magisk") == 0)
|
||||
unlinkat(sbin, entry->d_name, 0);
|
||||
}
|
||||
close(sbin);
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
fchmod(sbin, 0755);
|
||||
fsetfilecon(sbin, "u:object_r:rootfs:s0");
|
||||
dir = xfdopendir(root);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
|
||||
xsymlink("/root/magisk", buf2);
|
||||
}
|
||||
xsymlink(MOUNTPOINT, FAKEPOINT);
|
||||
close(root);
|
||||
close(sbin);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vec_for_each(&mounts, line) {
|
||||
if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/system");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s -> %s\n", MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdir_p(MIRRDIR "/bin", 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
|
||||
LOGI("* Setting up internal busybox");
|
||||
xmkdir_p(BBPATH, 0755);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
#define alt_img ((char *[]) \
|
||||
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
|
||||
|
||||
static int prepare_img() {
|
||||
// First merge images
|
||||
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
|
||||
LOGE("Image merge /data/magisk_merge.img -> " MAINIMG " failed!\n");
|
||||
return 1;
|
||||
// Merge images
|
||||
for (int i = 0; alt_img[i]; ++i) {
|
||||
if (merge_img(alt_img[i], MAINIMG)) {
|
||||
LOGE("Image merge %s -> " MAINIMG " failed!\n", alt_img[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (access(MAINIMG, F_OK) == -1) {
|
||||
@@ -509,8 +415,6 @@ static int prepare_img() {
|
||||
if (magiskloop == NULL)
|
||||
return 1;
|
||||
|
||||
vec_init(&module_list);
|
||||
|
||||
xmkdir(COREDIR, 0755);
|
||||
xmkdir(COREDIR "/post-fs-data.d", 0755);
|
||||
xmkdir(COREDIR "/service.d", 0755);
|
||||
@@ -575,9 +479,6 @@ void post_fs(int client) {
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
// Allow magiskinit to full patch
|
||||
close(creat(PATCHSTART, 0));
|
||||
|
||||
// Uninstall or core only mode
|
||||
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
|
||||
goto unblock;
|
||||
@@ -597,7 +498,7 @@ void post_fs_data(int client) {
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (!check_data())
|
||||
if (!is_daemon_init && !check_data())
|
||||
goto unblock;
|
||||
|
||||
// Start the debug log
|
||||
@@ -608,26 +509,11 @@ void post_fs_data(int client) {
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
// Magisk binaries
|
||||
char *bin_path = NULL;
|
||||
if (access("/cache/data_bin", F_OK) == 0)
|
||||
bin_path = "/cache/data_bin";
|
||||
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/data/com.topjohnwu.magisk/install";
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Lazy.... use shell blob to match files
|
||||
exec_command_sync("sh", "-c", "mv -f /data/magisk/stock_*.img.gz /data", NULL);
|
||||
vec_init(&module_list);
|
||||
|
||||
// Initialize
|
||||
daemon_init();
|
||||
if (!is_daemon_init)
|
||||
daemon_init();
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
@@ -744,8 +630,7 @@ void late_start(int client) {
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
// Wait till the full patch is done
|
||||
while (access(PATCHDONE, F_OK) == -1)
|
||||
usleep(500); /* Wait 0.5ms */
|
||||
wait_till_exists(PATCHDONE);
|
||||
unlink(PATCHDONE);
|
||||
|
||||
// Run scripts after full patch, most reliable way to run scripts
|
318
core/jni/core/daemon.c
Normal file
318
core/jni/core/daemon.c
Normal file
@@ -0,0 +1,318 @@
|
||||
/* daemon.c - Magisk Daemon
|
||||
*
|
||||
* Start the daemon and wait for requests
|
||||
* Connect the daemon and send requests through sockets
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
int is_daemon_init = 0, seperate_vendor = 0;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
client_request req = read_int(client);
|
||||
|
||||
struct ucred credential;
|
||||
get_client_cred(client, &credential);
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
case STOP_MAGISKHIDE:
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case LS_HIDELIST:
|
||||
case POST_FS:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
if (credential.uid != 0) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
close(client);
|
||||
return NULL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
launch_magiskhide(client);
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
stop_magiskhide(client);
|
||||
break;
|
||||
case ADD_HIDELIST:
|
||||
add_hide_list(client);
|
||||
break;
|
||||
case RM_HIDELIST:
|
||||
rm_hide_list(client);
|
||||
break;
|
||||
case LS_HIDELIST:
|
||||
ls_hide_list(client);
|
||||
break;
|
||||
case SUPERUSER:
|
||||
su_daemon_receiver(client, &credential);
|
||||
break;
|
||||
case CHECK_VERSION:
|
||||
write_string(client, MAGISK_VER_STR);
|
||||
close(client);
|
||||
break;
|
||||
case CHECK_VERSION_CODE:
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
close(client);
|
||||
break;
|
||||
case POST_FS:
|
||||
post_fs(client);
|
||||
break;
|
||||
case POST_FS_DATA:
|
||||
post_fs_data(client);
|
||||
break;
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void auto_start_magiskhide() {
|
||||
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
}
|
||||
|
||||
void daemon_init() {
|
||||
is_daemon_init = 1;
|
||||
|
||||
// Magisk binaries
|
||||
char *bin_path = NULL;
|
||||
if (access("/cache/data_bin", F_OK) == 0)
|
||||
bin_path = "/cache/data_bin";
|
||||
else if (access("/data/data/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/data/com.topjohnwu.magisk/install";
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(bin_path, DATABIN);
|
||||
rm_rf(bin_path);
|
||||
}
|
||||
|
||||
// Migration
|
||||
rm_rf("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink("/data/magisk_debug.log");
|
||||
chmod("/data/adb", 0700);
|
||||
|
||||
// Use shell glob to match files
|
||||
exec_command_sync("sh", "-c",
|
||||
"mv -f /data/adb/magisk/stock_*.img.gz /data;"
|
||||
"rm -f /data/user*/*/magisk.db;", NULL);
|
||||
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin;
|
||||
char buf[PATH_MAX], buf2[PATH_MAX];
|
||||
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir("/root", 0755);
|
||||
chmod("/root", 0755);
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
dir = xfdopendir(sbin);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
linkat(sbin, entry->d_name, root, entry->d_name, 0);
|
||||
if (strcmp(entry->d_name, "magisk") == 0)
|
||||
unlinkat(sbin, entry->d_name, 0);
|
||||
}
|
||||
close(sbin);
|
||||
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
chmod("/sbin", 0755);
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
dir = xfdopendir(root);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(buf, PATH_MAX, "/root/%s", entry->d_name);
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", entry->d_name);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", applet[i]);
|
||||
xsymlink("/root/magisk", buf2);
|
||||
}
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
snprintf(buf2, PATH_MAX, "/sbin/%s", init_applet[i]);
|
||||
xsymlink("/root/magiskinit", buf2);
|
||||
}
|
||||
close(root);
|
||||
|
||||
// Backward compatibility
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vec_for_each(&mounts, line) {
|
||||
if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/system", 0755);
|
||||
xmount(buf, MIRRDIR "/system", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/system");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s -> %s\n", buf, MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s -> %s\n", MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
xmkdir_p(MIRRDIR "/bin", 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
|
||||
LOGI("* Setting up internal busybox");
|
||||
xmkdir_p(BBPATH, 0755);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
|
||||
void start_daemon() {
|
||||
setsid();
|
||||
setcon("u:r:su:s0");
|
||||
umask(0);
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
// Block user signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
exit(1);
|
||||
xlisten(fd, 10);
|
||||
|
||||
if ((is_daemon_init = (access(MAGISKTMP, F_OK) == 0))) {
|
||||
// Restart stuffs if the daemon is restarted
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
auto_start_magiskhide();
|
||||
start_debug_log();
|
||||
} else if (check_data()) {
|
||||
daemon_init();
|
||||
}
|
||||
|
||||
// Start the log monitor
|
||||
monitor_logs();
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
int *client = xmalloc(sizeof(int));
|
||||
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, request_handler, client);
|
||||
// Detach the thread, we will never join it
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon() {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
// If we cannot access the daemon, we start a daemon in the child process if possible
|
||||
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "No daemon is currently running!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (xfork() == 0) {
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
close(fd);
|
||||
start_daemon();
|
||||
}
|
||||
|
||||
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000);
|
||||
}
|
||||
return fd;
|
||||
}
|
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -14,8 +13,35 @@
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
int logcat_events[] = { -1, -1, -1 };
|
||||
extern int is_restart;
|
||||
extern int is_daemon_init;
|
||||
|
||||
static int am_proc_start_filter(const char *log) {
|
||||
return strstr(log, "am_proc_start") != NULL;
|
||||
}
|
||||
|
||||
static int magisk_log_filter(const char *log) {
|
||||
char *ss;
|
||||
return (ss = strstr(log, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V');
|
||||
}
|
||||
|
||||
static int magisk_debug_log_filter(const char *log) {
|
||||
return strstr(log, "Magisk") != NULL;
|
||||
}
|
||||
|
||||
struct log_listener log_events[] = {
|
||||
{ /* HIDE_EVENT */
|
||||
.fd = -1,
|
||||
.filter = am_proc_start_filter
|
||||
},
|
||||
{ /* LOG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_log_filter
|
||||
},
|
||||
{ /* DEBUG_EVENT */
|
||||
.fd = -1,
|
||||
.filter = magisk_debug_log_filter
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
@@ -31,16 +57,23 @@ static void *logger_thread(void *args) {
|
||||
// Start logcat
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "all" , "-v", "threadtime", "-s", "am_proc_start", "Magisk", NULL);
|
||||
while (fdgets(line, sizeof(line), log_fd)) {
|
||||
for (int i = 0; i < (sizeof(logcat_events) / sizeof(int)); ++i) {
|
||||
if (logcat_events[i] > 0) {
|
||||
for (int i = 0; i < (sizeof(log_events) / sizeof(struct log_listener)); ++i) {
|
||||
if (log_events[i].fd > 0 && log_events[i].filter(line)) {
|
||||
char *s = strdup(line);
|
||||
xwrite(logcat_events[i], &s, sizeof(s));
|
||||
xwrite(log_events[i].fd, &s, sizeof(s));
|
||||
}
|
||||
}
|
||||
if (kill(log_pid, 0))
|
||||
break;
|
||||
}
|
||||
// Clear buffer if restart required
|
||||
|
||||
// Cleanup
|
||||
close(log_fd);
|
||||
log_fd = -1;
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
|
||||
// Clear buffer before restart
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
}
|
||||
|
||||
@@ -49,44 +82,36 @@ static void *logger_thread(void *args) {
|
||||
}
|
||||
|
||||
static void *magisk_log_thread(void *args) {
|
||||
int have_data = 0;
|
||||
|
||||
// Temp buffer for logs before we have data access
|
||||
// Buffer logs before we have data access
|
||||
struct vector logs;
|
||||
vec_init(&logs);
|
||||
|
||||
FILE *log;
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
// Register our listener
|
||||
logcat_events[LOG_EVENT] = pipefd[1];
|
||||
log_events[LOG_EVENT].fd = pipefd[1];
|
||||
|
||||
LOGD("log_monitor: magisk log dumper start");
|
||||
|
||||
FILE *log = NULL;
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
char *ss;
|
||||
if ((ss = strstr(line, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V')) {
|
||||
if (!have_data) {
|
||||
if ((have_data = check_data())) {
|
||||
// Dump buffered logs to file
|
||||
if (!is_restart)
|
||||
rename(LOGFILE, LASTLOG);
|
||||
log = xfopen(LOGFILE, "a");
|
||||
setbuf(log, NULL);
|
||||
char *tmp;
|
||||
vec_for_each(&logs, tmp) {
|
||||
fprintf(log, "%s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
vec_destroy(&logs);
|
||||
} else {
|
||||
vec_push_back(&logs, strdup(line));
|
||||
if (!is_daemon_init) {
|
||||
vec_push_back(&logs, strdup(line));
|
||||
} else {
|
||||
if (log == NULL) {
|
||||
// Dump buffered logs to file
|
||||
log = xfopen(LOGFILE, "w");
|
||||
setbuf(log, NULL);
|
||||
char *tmp;
|
||||
vec_for_each(&logs, tmp) {
|
||||
fprintf(log, "%s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
vec_destroy(&logs);
|
||||
}
|
||||
if (have_data)
|
||||
fprintf(log, "%s", line);
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -102,13 +127,11 @@ static void *debug_magisk_log_thread(void *args) {
|
||||
LOGD("log_monitor: debug log dumper start");
|
||||
|
||||
// Register our listener
|
||||
logcat_events[DEBUG_EVENT] = pipefd[1];
|
||||
log_events[DEBUG_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line))
|
||||
fprintf(log, "%s", line);
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
char *ss;
|
||||
if ((ss = strstr(line, "Magisk")))
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -12,11 +12,7 @@
|
||||
|
||||
char *argv0;
|
||||
|
||||
char *applet[] =
|
||||
{ "su", "resetprop", "magiskhide", NULL };
|
||||
|
||||
int (*applet_main[]) (int, char *[]) =
|
||||
{ su_client_main, resetprop_main, magiskhide_main, NULL };
|
||||
int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL };
|
||||
|
||||
int create_links(const char *bin, const char *path) {
|
||||
char self[PATH_MAX], linkpath[PATH_MAX];
|
||||
@@ -44,7 +40,7 @@ static void usage() {
|
||||
" -c print current binary version\n"
|
||||
" -v print running daemon version\n"
|
||||
" -V print running daemon version code\n"
|
||||
" --list list all availible applets\n"
|
||||
" --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"
|
||||
@@ -73,7 +69,7 @@ int main(int argc, char *argv[]) {
|
||||
if (strcmp(basename(argv[0]), "magisk") == 0) {
|
||||
if (argc < 2) usage();
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
printf("%s\n", MAGISK_VER_STR);
|
||||
printf("%s (%d)\n", MAGISK_VER_STR, MAGISK_VER_CODE);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-v") == 0) {
|
||||
int fd = connect_daemon();
|
||||
@@ -148,8 +144,9 @@ int main(int argc, char *argv[]) {
|
||||
clone_attr(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--daemon") == 0) {
|
||||
// Start daemon, this process won't return
|
||||
start_daemon();
|
||||
if (xfork() == 0)
|
||||
start_daemon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS);
|
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@@ -38,14 +39,23 @@
|
||||
#include <lzma.h>
|
||||
#include <cil/cil.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "dump.h"
|
||||
#include "magiskrc.h"
|
||||
#include "utils.h"
|
||||
#include "magiskpolicy.h"
|
||||
#include "magiskrc.h"
|
||||
#include "dump.h"
|
||||
#include "daemon.h"
|
||||
#include "cpio.h"
|
||||
#include "magisk.h"
|
||||
|
||||
// #define VLOG(fmt, ...) printf(fmt, __VA_ARGS__) /* Enable to debug */
|
||||
#ifdef MAGISK_DEBUG
|
||||
#define VLOG(fmt, ...) printf(fmt, __VA_ARGS__)
|
||||
#else
|
||||
#define VLOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
extern policydb_t *policydb;
|
||||
int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL };
|
||||
static int keepverity = 0, keepencrypt = 0;
|
||||
|
||||
struct cmdline {
|
||||
int skip_initramfs;
|
||||
@@ -60,14 +70,11 @@ struct device {
|
||||
char path[64];
|
||||
};
|
||||
|
||||
extern policydb_t *policydb;
|
||||
|
||||
static void parse_cmdline(struct cmdline *cmd) {
|
||||
// cleanup
|
||||
cmd->skip_initramfs = 0;
|
||||
cmd->slot[0] = '\0';
|
||||
|
||||
char *tok;
|
||||
char cmdline[4096];
|
||||
mkdir("/proc", 0555);
|
||||
mount("proc", "/proc", "proc", 0, NULL);
|
||||
@@ -75,8 +82,7 @@ static void parse_cmdline(struct cmdline *cmd) {
|
||||
cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
|
||||
close(fd);
|
||||
umount("/proc");
|
||||
tok = strtok(cmdline, " ");
|
||||
while (tok != NULL) {
|
||||
for (char *tok = strtok(cmdline, " "); tok; tok = strtok(NULL, " ")) {
|
||||
if (strncmp(tok, "androidboot.slot_suffix", 23) == 0) {
|
||||
sscanf(tok, "androidboot.slot_suffix=%s", cmd->slot);
|
||||
} else if (strncmp(tok, "androidboot.slot", 16) == 0) {
|
||||
@@ -85,7 +91,6 @@ static void parse_cmdline(struct cmdline *cmd) {
|
||||
} else if (strcmp(tok, "skip_initramfs") == 0) {
|
||||
cmd->skip_initramfs = 1;
|
||||
}
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,55 +146,60 @@ static int setup_block(struct device *dev, const char *partname) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *patch_init_rc(char *data, uint32_t *size) {
|
||||
int injected = 0;
|
||||
char *new_data = malloc(*size + 23);
|
||||
char *old_data = data;
|
||||
uint32_t pos = 0;
|
||||
|
||||
for (char *tok = strsep(&old_data, "\n"); tok; tok = strsep(&old_data, "\n")) {
|
||||
if (!injected && strncmp(tok, "import", 6) == 0) {
|
||||
if (strstr(tok, "init.magisk.rc")) {
|
||||
injected = 1;
|
||||
} else {
|
||||
strcpy(new_data + pos, "import /init.magisk.rc\n");
|
||||
pos += 23;
|
||||
injected = 1;
|
||||
}
|
||||
} else if (strstr(tok, "selinux.reload_policy")) {
|
||||
continue;
|
||||
}
|
||||
// Copy the line
|
||||
strcpy(new_data + pos, tok);
|
||||
pos += strlen(tok);
|
||||
new_data[pos++] = '\n';
|
||||
static void fstab_patch_cb(int dirfd, struct dirent *entry) {
|
||||
if (entry->d_type == DT_REG && strstr(entry->d_name, "fstab")) {
|
||||
void *buf;
|
||||
size_t _size;
|
||||
uint32_t size;
|
||||
full_read_at(dirfd, entry->d_name, &buf, &_size);
|
||||
size = _size; /* Type conversion */
|
||||
if (!keepverity)
|
||||
patch_verity(&buf, &size, 1);
|
||||
if (!keepencrypt)
|
||||
patch_encryption(&buf, &size);
|
||||
int fstab = xopenat(dirfd, entry->d_name, O_WRONLY | O_CLOEXEC);
|
||||
write(fstab, buf, size);
|
||||
close(fstab);
|
||||
}
|
||||
|
||||
*size = pos;
|
||||
return new_data;
|
||||
}
|
||||
|
||||
static void patch_ramdisk() {
|
||||
static void patch_ramdisk(int root) {
|
||||
void *addr;
|
||||
size_t size;
|
||||
mmap_rw("/init", &addr, &size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(addr + i, "/system/etc/selinux/plat_sepolicy.cil", 37) == 0) {
|
||||
memcpy(addr + i, "/system/etc/selinux/plat_sepolicy.xxx", 37);
|
||||
if (memcmp(addr + i, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL) - 1) == 0) {
|
||||
memcpy(addr + i + sizeof(SPLIT_PLAT_CIL) - 4, "xxx", 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
munmap(addr, size);
|
||||
|
||||
mmap_rw("/init.rc", &addr, &size);
|
||||
uint32_t new_size = size;
|
||||
void *init_rc = patch_init_rc(addr, &new_size);
|
||||
munmap(addr, size);
|
||||
|
||||
int fd = open("/init.rc", O_WRONLY | O_TRUNC | O_CLOEXEC);
|
||||
write(fd, init_rc, new_size);
|
||||
full_read("/init.rc", &addr, &size);
|
||||
patch_init_rc(&addr, &size);
|
||||
int fd = creat("/init.rc", 0750);
|
||||
write(fd, addr, size);
|
||||
close(fd);
|
||||
free(init_rc);
|
||||
free(addr);
|
||||
|
||||
/* Disabled for now */
|
||||
|
||||
// char *key, *value;
|
||||
// full_read("/.backup/.magisk", &addr, &size);
|
||||
// for (char *tok = strtok(addr, "\n"); tok; tok = strtok(NULL, "\n")) {
|
||||
// key = tok;
|
||||
// value = strchr(tok, '=') + 1;
|
||||
// value[-1] = '\0';
|
||||
// if (strcmp(key, "KEEPVERITY") == 0)
|
||||
// keepverity = strcmp(value, "true") == 0;
|
||||
// else if (strcmp(key, "KEEPFORCEENCRYPT") == 0)
|
||||
// keepencrypt = strcmp(value, "true") == 0;
|
||||
// }
|
||||
|
||||
// excl_list = (char *[]) { "system_root", "system", "vendor", NULL };
|
||||
// in_order_walk(root, fstab_patch_cb);
|
||||
// if (!keepverity)
|
||||
// unlink("/verity_key");
|
||||
}
|
||||
|
||||
static int strend(const char *s1, const char *s2) {
|
||||
@@ -210,34 +220,38 @@ static int compile_cil() {
|
||||
|
||||
cil_db_init(&db);
|
||||
cil_set_mls(db, 1);
|
||||
cil_set_multiple_decls(db, 1);
|
||||
cil_set_disable_neverallow(db, 1);
|
||||
cil_set_target_platform(db, SEPOL_TARGET_SELINUX);
|
||||
cil_set_policy_version(db, POLICYDB_VERSION_XPERMS_IOCTL);
|
||||
cil_set_attrs_expand_generated(db, 0);
|
||||
|
||||
// plat
|
||||
mmap_ro("/system/etc/selinux/plat_sepolicy.cil", &addr, &size);
|
||||
cil_add_file(db, "/system/etc/selinux/plat_sepolicy.cil", addr, size);
|
||||
mmap_ro(SPLIT_PLAT_CIL, &addr, &size);
|
||||
VLOG("cil_add[%s]\n", SPLIT_PLAT_CIL);
|
||||
cil_add_file(db, SPLIT_PLAT_CIL, addr, size);
|
||||
munmap(addr, size);
|
||||
|
||||
// mapping
|
||||
char plat[10];
|
||||
int fd = open("/vendor/etc/selinux/plat_sepolicy_vers.txt", O_RDONLY | O_CLOEXEC);
|
||||
if (fd > 0) {
|
||||
plat[read(fd, plat, sizeof(plat)) - 1] = '\0';
|
||||
snprintf(path, sizeof(path), "/system/etc/selinux/mapping/%s.cil", plat);
|
||||
mmap_ro(path, &addr, &size);
|
||||
cil_add_file(db, path, addr, size);
|
||||
munmap(addr, size);
|
||||
close(fd);
|
||||
}
|
||||
int fd = open(SPLIT_NONPLAT_VER, O_RDONLY | O_CLOEXEC);
|
||||
plat[read(fd, plat, sizeof(plat)) - 1] = '\0';
|
||||
snprintf(path, sizeof(path), SPLIT_PLAT_MAPPING, plat);
|
||||
mmap_ro(path, &addr, &size);
|
||||
VLOG("cil_add[%s]\n", path);
|
||||
cil_add_file(db, path, addr, size);
|
||||
munmap(addr, size);
|
||||
close(fd);
|
||||
|
||||
dir = opendir("/vendor/etc/selinux");
|
||||
// nonplat
|
||||
dir = opendir(NONPLAT_POLICY_DIR);
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (strend(entry->d_name, ".cil") == 0) {
|
||||
snprintf(path, sizeof(path), "/vendor/etc/selinux/%s", entry->d_name);
|
||||
snprintf(path, sizeof(path), NONPLAT_POLICY_DIR "%s", entry->d_name);
|
||||
mmap_ro(path, &addr, &size);
|
||||
VLOG("cil_add[%s]\n", path);
|
||||
cil_add_file(db, path, addr, size);
|
||||
munmap(addr, size);
|
||||
}
|
||||
@@ -259,44 +273,47 @@ static int verify_precompiled() {
|
||||
int fd;
|
||||
char sys_sha[70], ven_sha[70];
|
||||
|
||||
dir = opendir("/vendor/etc/selinux");
|
||||
// init the strings with different value
|
||||
sys_sha[0] = 0;
|
||||
ven_sha[0] = 1;
|
||||
|
||||
dir = opendir(NONPLAT_POLICY_DIR);
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (strend(entry->d_name, ".sha256") == 0) {
|
||||
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
ven_sha[read(fd, ven_sha, sizeof(ven_sha))] = '\0';
|
||||
ven_sha[read(fd, ven_sha, sizeof(ven_sha)) - 1] = '\0';
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
dir = opendir("/system/etc/selinux");
|
||||
dir = opendir(PLAT_POLICY_DIR);
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (strend(entry->d_name, ".sha256") == 0) {
|
||||
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
sys_sha[read(fd, sys_sha, sizeof(sys_sha))] = '\0';
|
||||
sys_sha[read(fd, sys_sha, sizeof(sys_sha)) - 1] = '\0';
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return strcmp(sys_sha, ven_sha);
|
||||
VLOG("sys_sha[%s]\nven_sha[%s]\n", sys_sha, ven_sha);
|
||||
return strcmp(sys_sha, ven_sha) == 0;
|
||||
}
|
||||
|
||||
static void patch_sepolicy() {
|
||||
if (access("/sepolicy", R_OK) == 0) {
|
||||
if (access("/sepolicy", R_OK) == 0)
|
||||
load_policydb("/sepolicy");
|
||||
} else if (access("/vendor/etc/selinux/precompiled_sepolicy", R_OK) == 0
|
||||
&& verify_precompiled() == 0) {
|
||||
load_policydb("/vendor/etc/selinux/precompiled_sepolicy");
|
||||
} else if (access("/system/etc/selinux/plat_sepolicy.cil", R_OK) == 0) {
|
||||
else if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled())
|
||||
load_policydb(SPLIT_PRECOMPILE);
|
||||
else if (access(SPLIT_PLAT_CIL, R_OK) == 0)
|
||||
compile_cil();
|
||||
}
|
||||
|
||||
sepol_med_rules();
|
||||
sepol_magisk_rules();
|
||||
dump_policydb("/sepolicy");
|
||||
}
|
||||
|
||||
@@ -340,13 +357,59 @@ static int dump_magiskrc(const char *path, mode_t mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void magisk_init_daemon() {
|
||||
setsid();
|
||||
|
||||
// Full patch
|
||||
sepol_allow("su", ALL, ALL, ALL);
|
||||
|
||||
// Wait till init cold boot done
|
||||
wait_till_exists("/dev/.coldboot_done");
|
||||
|
||||
int null = open("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
dup3(null, STDIN_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDOUT_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDERR_FILENO, O_CLOEXEC);
|
||||
close(null);
|
||||
|
||||
// Transit our context to su (mimic setcon)
|
||||
int fd, crap;
|
||||
fd = open("/proc/self/attr/current", O_WRONLY);
|
||||
write(fd, "u:r:su:s0", 9);
|
||||
close(fd);
|
||||
|
||||
// Dump full patch to kernel
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
close(creat(PATCHDONE, 0));
|
||||
destroy_policydb();
|
||||
|
||||
// Keep Magisk daemon always alive
|
||||
while (1) {
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)))
|
||||
usleep(10000); /* Wait 10 ms after each try */
|
||||
|
||||
/* Should hold forever */
|
||||
read(fd, &crap, sizeof(crap));
|
||||
|
||||
/* If things went here, it means the other side of the socket is closed
|
||||
* We restart the daemon again */
|
||||
close(fd);
|
||||
if (fork_dont_care() == 0) {
|
||||
execv("/sbin/magisk", (char *[]) { "magisk", "--daemon", NULL } );
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
umask(0);
|
||||
|
||||
if (strcmp(basename(argv[0]), "magiskpolicy") == 0 || strcmp(basename(argv[0]), "supolicy") == 0)
|
||||
return magiskpolicy_main(argc, argv);
|
||||
if (argc > 1 && (strcmp(argv[1], "magiskpolicy") == 0 || strcmp(argv[1], "supolicy") == 0))
|
||||
return magiskpolicy_main(argc - 1, argv + 1);
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
if (strcmp(basename(argv[0]), init_applet[i]) == 0)
|
||||
return (*init_applet_main[i])(argc, argv);
|
||||
}
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "-x") == 0) {
|
||||
if (strcmp(argv[2], "magisk") == 0)
|
||||
@@ -355,6 +418,16 @@ int main(int argc, char *argv[]) {
|
||||
return dump_magiskrc(argv[3], 0755);
|
||||
}
|
||||
|
||||
// Prevent file descriptor confusion
|
||||
mknod("/null", S_IFCHR | 0666, makedev(1, 3));
|
||||
int null = open("/null", O_RDWR | O_CLOEXEC);
|
||||
unlink("/null");
|
||||
dup3(null, STDIN_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDOUT_FILENO, O_CLOEXEC);
|
||||
dup3(null, STDERR_FILENO, O_CLOEXEC);
|
||||
if (null > STDERR_FILENO)
|
||||
close(null);
|
||||
|
||||
// Extract and link files
|
||||
mkdir("/overlay", 0000);
|
||||
dump_magiskrc("/overlay/init.magisk.rc", 0750);
|
||||
@@ -362,8 +435,6 @@ int main(int argc, char *argv[]) {
|
||||
dump_magisk("/overlay/sbin/magisk", 0755);
|
||||
mkdir("/overlay/root", 0755);
|
||||
link("/init", "/overlay/root/magiskinit");
|
||||
symlink("/root/magiskinit", "/overlay/root/magiskpolicy");
|
||||
symlink("/root/magiskinit", "/overlay/root/supolicy");
|
||||
|
||||
struct cmdline cmd;
|
||||
parse_cmdline(&cmd);
|
||||
@@ -405,36 +476,52 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
close(system_root);
|
||||
} else {
|
||||
// Revert original init binary
|
||||
unlink("/init");
|
||||
link("/.backup/init", "/init");
|
||||
if (access("/ramdisk.cpio.xz", R_OK) == 0) {
|
||||
// High compression mode
|
||||
void *addr;
|
||||
size_t size;
|
||||
mmap_ro("/ramdisk.cpio.xz", &addr, &size);
|
||||
int fd = creat("/ramdisk.cpio", 0);
|
||||
unxz(addr, size, fd);
|
||||
munmap(addr, size);
|
||||
close(fd);
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
parse_cpio(&v, "/ramdisk.cpio");
|
||||
excl_list = (char *[]) { "overlay", ".backup", NULL };
|
||||
frm_rf(root);
|
||||
chdir("/");
|
||||
cpio_extract_all(&v);
|
||||
cpio_vec_destroy(&v);
|
||||
} else {
|
||||
// Revert original init binary
|
||||
unlink("/init");
|
||||
link("/.backup/init", "/init");
|
||||
}
|
||||
}
|
||||
|
||||
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
|
||||
mv_dir(overlay, root);
|
||||
|
||||
// Only patch rootfs if not intended to run in recovery
|
||||
if (access("/etc/recovery.fstab", F_OK) != 0) {
|
||||
mv_dir(overlay, root);
|
||||
|
||||
patch_ramdisk(root);
|
||||
patch_sepolicy();
|
||||
|
||||
if (fork_dont_care() == 0) {
|
||||
strcpy(argv[0], "magiskinit");
|
||||
close(overlay);
|
||||
close(root);
|
||||
magisk_init_daemon();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
rmdir("/overlay");
|
||||
close(overlay);
|
||||
close(root);
|
||||
|
||||
patch_ramdisk();
|
||||
patch_sepolicy();
|
||||
|
||||
umount("/vendor");
|
||||
|
||||
if (fork() == 0) {
|
||||
// Fork a new process for full patch
|
||||
setsid();
|
||||
sepol_allow("su", ALL, ALL, ALL);
|
||||
while (access(PATCHSTART, W_OK) == -1)
|
||||
usleep(500); /* Wait 0.5ms */
|
||||
unlink(PATCHSTART);
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
close(open(PATCHDONE, O_RDONLY | O_CREAT, 0));
|
||||
destroy_policydb();
|
||||
return 0;
|
||||
}
|
||||
rmdir("/overlay");
|
||||
|
||||
// Finally, give control back!
|
||||
execv("/init", argv);
|
152
core/jni/core/socket.c
Normal file
152
core/jni/core/socket.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/* socket.c - All socket related operations
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
#include "magisk.h"
|
||||
|
||||
/* Setup the address and return socket fd */
|
||||
int setup_socket(struct sockaddr_un *sun) {
|
||||
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
memset(sun, 0, sizeof(*sun));
|
||||
sun->sun_family = AF_LOCAL;
|
||||
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Receive a file descriptor from a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* Returns the file descriptor on success, or -1 if a file
|
||||
* descriptor was not actually included in the message
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*/
|
||||
int recv_fd(int sockfd) {
|
||||
// Need to receive data from the message, otherwise don't care about it.
|
||||
char iovbuf;
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = &iovbuf,
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsgbuf,
|
||||
.msg_controllen = sizeof(cmsgbuf),
|
||||
};
|
||||
|
||||
xrecvmsg(sockfd, &msg, MSG_WAITALL);
|
||||
|
||||
// Was a control message actually sent?
|
||||
switch (msg.msg_controllen) {
|
||||
case 0:
|
||||
// No, so the file descriptor was closed and won't be used.
|
||||
return -1;
|
||||
case sizeof(cmsgbuf):
|
||||
// Yes, grab the file descriptor from it.
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (cmsg == NULL ||
|
||||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
error:
|
||||
LOGE("unable to read fd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return *(int *)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a file descriptor through a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*
|
||||
* fd may be -1, in which case the dummy data is sent,
|
||||
* but no control message with the FD is sent.
|
||||
*/
|
||||
void send_fd(int sockfd, int fd) {
|
||||
// Need to send some data in the message, this will do.
|
||||
struct iovec iov = {
|
||||
.iov_base = "",
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
if (fd != -1) {
|
||||
// Is the file descriptor actually open?
|
||||
if (fcntl(fd, F_GETFD) == -1) {
|
||||
if (errno != EBADF) {
|
||||
PLOGE("unable to send fd");
|
||||
}
|
||||
// It's closed, don't send a control message or sendmsg will EBADF.
|
||||
} else {
|
||||
// It's open, send the file descriptor in a control message.
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
*(int *)CMSG_DATA(cmsg) = fd;
|
||||
}
|
||||
}
|
||||
|
||||
xsendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
int read_int(int fd) {
|
||||
int val;
|
||||
xxread(fd, &val, sizeof(int));
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_int(int fd, int val) {
|
||||
if (fd < 0) return;
|
||||
xwrite(fd, &val, sizeof(int));
|
||||
}
|
||||
|
||||
char* read_string(int fd) {
|
||||
int len = read_int(fd);
|
||||
if (len > PATH_MAX || len < 0) {
|
||||
LOGE("invalid string length %d", len);
|
||||
exit(1);
|
||||
}
|
||||
char* val = xmalloc(sizeof(char) * (len + 1));
|
||||
xxread(fd, val, len);
|
||||
val[len] = '\0';
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_string(int fd, const char* val) {
|
||||
if (fd < 0) return;
|
||||
int len = strlen(val);
|
||||
write_int(fd, len);
|
||||
xwrite(fd, val, len);
|
||||
}
|
@@ -3,6 +3,7 @@ LOCAL_PATH:= $(call my-dir)
|
||||
# libsqlite.so (stub)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libsqlite
|
||||
LOCAL_C_INCLUDES := jni/external/include
|
||||
LOCAL_SRC_FILES := stubs/sqlite3_stub.c
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -58,7 +59,7 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := liblzma
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(EXT_PATH)/xz_config \
|
||||
$(EXT_PATH)/include/xz_config \
|
||||
$(EXT_PATH)/xz/src/common \
|
||||
$(EXT_PATH)/xz/src/liblzma/api \
|
||||
$(EXT_PATH)/xz/src/liblzma/check \
|
1
core/jni/external/selinux
vendored
Submodule
1
core/jni/external/selinux
vendored
Submodule
Submodule core/jni/external/selinux added at 8e849a5639
0
jni/external/xz → core/jni/external/xz
vendored
0
jni/external/xz → core/jni/external/xz
vendored
60
core/jni/include/cpio.h
Normal file
60
core/jni/include/cpio.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef _CPIO_H_
|
||||
#define _CPIO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
typedef struct cpio_entry {
|
||||
// uint32_t ino;
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
// uint32_t nlink;
|
||||
// uint32_t mtime;
|
||||
uint32_t filesize;
|
||||
// uint32_t devmajor;
|
||||
// uint32_t devminor;
|
||||
// uint32_t rdevmajor;
|
||||
// uint32_t rdevminor;
|
||||
// uint32_t namesize;
|
||||
// uint32_t check;
|
||||
char *filename;
|
||||
void *data;
|
||||
int remove;
|
||||
} cpio_entry;
|
||||
|
||||
typedef struct cpio_newc_header {
|
||||
char magic[6];
|
||||
char ino[8];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char nlink[8];
|
||||
char mtime[8];
|
||||
char filesize[8];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char rdevmajor[8];
|
||||
char rdevminor[8];
|
||||
char namesize[8];
|
||||
char check[8];
|
||||
} cpio_newc_header;
|
||||
|
||||
// Basic cpio functions
|
||||
void cpio_free(cpio_entry *e);
|
||||
int cpio_find(struct vector *v, const char *entry);
|
||||
int cpio_cmp(const void *a, const void *b);
|
||||
void parse_cpio(struct vector *v, const char *filename);
|
||||
void dump_cpio(struct vector *v, const char *filename);
|
||||
void cpio_vec_insert(struct vector *v, cpio_entry *n);
|
||||
void cpio_vec_destroy(struct vector *v);
|
||||
void cpio_rm(struct vector *v, int recursive, const char *entry);
|
||||
void cpio_mkdir(struct vector *v, mode_t mode, const char *entry);
|
||||
void cpio_ln(struct vector *v, const char *target, const char *entry);
|
||||
void cpio_add(struct vector *v, mode_t mode, const char *entry, const char *filename);
|
||||
int cpio_mv(struct vector *v, const char *from, const char *to);
|
||||
int cpio_extract(struct vector *v, const char *entry, const char *filename);
|
||||
void cpio_extract_all(struct vector *v);
|
||||
|
||||
#endif
|
@@ -5,9 +5,10 @@
|
||||
#define _DAEMON_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
extern pthread_t sepol_patch;
|
||||
extern int is_restart;
|
||||
extern int is_daemon_init, seperate_vendor;
|
||||
|
||||
// Commands require connecting to daemon
|
||||
typedef enum {
|
||||
@@ -42,6 +43,11 @@ typedef enum {
|
||||
void start_daemon();
|
||||
int connect_daemon();
|
||||
void auto_start_magiskhide();
|
||||
void daemon_init();
|
||||
|
||||
// socket.c
|
||||
|
||||
int setup_socket(struct sockaddr_un *sun);
|
||||
int recv_fd(int sockfd);
|
||||
void send_fd(int sockfd, int fd);
|
||||
int read_int(int fd);
|
||||
@@ -72,6 +78,6 @@ void ls_hide_list(int client);
|
||||
* Superuser *
|
||||
*************/
|
||||
|
||||
void su_daemon_receiver(int client);
|
||||
void su_daemon_receiver(int client, struct ucred *credential);
|
||||
|
||||
#endif
|
@@ -11,22 +11,39 @@
|
||||
#define str(a) #a
|
||||
#define xstr(a) str(a)
|
||||
|
||||
/**************
|
||||
* No logging *
|
||||
**************/
|
||||
|
||||
#define LOGD(...)
|
||||
#define LOGI(...)
|
||||
#define LOGW(...)
|
||||
#define LOGE(...)
|
||||
#define PLOGE(...)
|
||||
|
||||
/******************
|
||||
* Daemon logging *
|
||||
******************/
|
||||
|
||||
#ifdef IS_DAEMON
|
||||
|
||||
#undef LOGI
|
||||
#undef LOGW
|
||||
#undef LOGE
|
||||
#undef PLOGE
|
||||
|
||||
#include <pthread.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "Magisk"
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
#undef LOGD
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...)
|
||||
#endif
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
|
||||
enum {
|
||||
@@ -34,20 +51,36 @@ enum {
|
||||
LOG_EVENT,
|
||||
DEBUG_EVENT
|
||||
};
|
||||
extern int logcat_events[];
|
||||
|
||||
struct log_listener {
|
||||
int fd;
|
||||
int (*filter) (const char*);
|
||||
};
|
||||
|
||||
extern struct log_listener log_events[];
|
||||
|
||||
void monitor_logs();
|
||||
void start_debug_full_log();
|
||||
void stop_debug_full_log();
|
||||
void start_debug_log();
|
||||
|
||||
#else // IS_DAEMON
|
||||
#endif
|
||||
|
||||
/********************
|
||||
* Tools Log & Exit *
|
||||
********************/
|
||||
|
||||
#ifdef XWRAP_EXIT
|
||||
|
||||
#undef LOGE
|
||||
#undef PLOGE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
|
||||
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
|
||||
|
||||
#endif // IS_DAEMON
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _LOGGING_H_
|
@@ -14,10 +14,8 @@
|
||||
#endif
|
||||
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define LASTLOG "/cache/last_magisk.log"
|
||||
#define DEBUG_LOG "/data/magisk_debug.log"
|
||||
#define DEBUG_LOG "/data/adb/magisk_debug.log"
|
||||
#define UNBLOCKFILE "/dev/.magisk.unblock"
|
||||
#define PATCHSTART "/dev/.magisk.patch.start"
|
||||
#define PATCHDONE "/dev/.magisk.patch.done"
|
||||
#define DISABLEFILE "/cache/.disable_magisk"
|
||||
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
|
||||
@@ -26,27 +24,37 @@
|
||||
#define MIRRDIR MAGISKTMP "/mirror"
|
||||
#define BBPATH MAGISKTMP "/busybox"
|
||||
#define MOUNTPOINT MAGISKTMP "/img"
|
||||
#define FAKEPOINT "/magisk"
|
||||
#define COREDIR MOUNTPOINT "/.core"
|
||||
#define HOSTSFILE COREDIR "/hosts"
|
||||
#define HIDELIST COREDIR "/hidelist"
|
||||
#define MAINIMG "/data/magisk.img"
|
||||
#define DATABIN "/data/magisk"
|
||||
#define MANAGERAPK MIRRDIR "/bin/magisk.apk"
|
||||
#define MAINIMG "/data/adb/magisk.img"
|
||||
#define DATABIN "/data/adb/magisk"
|
||||
#define MANAGERAPK DATABIN "/magisk.apk"
|
||||
#define MAGISKRC "/init.magisk.rc"
|
||||
|
||||
|
||||
// selinuxfs paths
|
||||
#define SELINUX_PATH "/sys/fs/selinux/"
|
||||
#define SELINUX_ENFORCE SELINUX_PATH "enforce"
|
||||
#define SELINUX_POLICY SELINUX_PATH "policy"
|
||||
#define SELINUX_LOAD SELINUX_PATH "load"
|
||||
|
||||
// split policy paths
|
||||
#define PLAT_POLICY_DIR "/system/etc/selinux/"
|
||||
#define NONPLAT_POLICY_DIR "/vendor/etc/selinux/"
|
||||
#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil"
|
||||
#define SPLIT_PLAT_MAPPING PLAT_POLICY_DIR "mapping/%s.cil"
|
||||
#define SPLIT_PRECOMPILE NONPLAT_POLICY_DIR "precompiled_sepolicy"
|
||||
#define SPLIT_NONPLAT_VER NONPLAT_POLICY_DIR "plat_sepolicy_vers.txt"
|
||||
|
||||
#define MAGISKHIDE_PROP "persist.magisk.hide"
|
||||
|
||||
extern char *argv0; /* For changing process name */
|
||||
|
||||
extern char *applet[];
|
||||
extern int (*applet_main[]) (int, char *[]);
|
||||
#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL })
|
||||
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
|
||||
|
||||
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
|
||||
|
||||
int create_links(const char *bin, const char *path);
|
||||
|
@@ -4,9 +4,6 @@ const char magiskrc[] =
|
||||
|
||||
"on post-fs\n"
|
||||
" start logd\n"
|
||||
" start magisk_daemon\n"
|
||||
" wait /dev/.magisk.unblock 5\n"
|
||||
" rm /dev/.magisk.unblock\n"
|
||||
" start magisk_pfs\n"
|
||||
" wait /dev/.magisk.unblock 10\n"
|
||||
"\n"
|
||||
@@ -24,6 +21,7 @@ const char magiskrc[] =
|
||||
"service magisk_daemon /sbin/magisk --daemon\n"
|
||||
" user root\n"
|
||||
" seclabel u:r:su:s0\n"
|
||||
" oneshot\n"
|
||||
"\n"
|
||||
|
||||
"service magisk_pfs /sbin/magisk --post-fs\n"
|
@@ -70,7 +70,7 @@ pid_t xfork();
|
||||
|
||||
// misc.c
|
||||
|
||||
extern int quit_signals[];
|
||||
#define quit_signals ((int []) { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 })
|
||||
|
||||
unsigned get_shell_uid();
|
||||
unsigned get_system_uid();
|
||||
@@ -89,6 +89,7 @@ int bind_mount(const char *from, const char *to);
|
||||
void get_client_cred(int fd, struct ucred *cred);
|
||||
int switch_mnt_ns(int pid);
|
||||
int fork_dont_care();
|
||||
void wait_till_exists(const char *target);
|
||||
|
||||
// file.c
|
||||
|
||||
@@ -101,6 +102,7 @@ struct file_attr {
|
||||
|
||||
int fd_getpath(int fd, char *path, size_t size);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*));
|
||||
void rm_rf(const char *path);
|
||||
void frm_rf(int dirfd);
|
||||
void mv_f(const char *source, const char *destination);
|
||||
@@ -116,9 +118,12 @@ int fsetattr(int fd, struct file_attr *a);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void restorecon(int dirfd, int force);
|
||||
void mmap_ro(const char *filename, void **buf, size_t *size);
|
||||
void mmap_rw(const char *filename, void **buf, size_t *size);
|
||||
void full_read(int fd, void **buf, size_t *size);
|
||||
int mmap_ro(const char *filename, void **buf, size_t *size);
|
||||
int mmap_rw(const char *filename, void **buf, size_t *size);
|
||||
void fd_full_read(int fd, void **buf, size_t *size);
|
||||
void full_read(const char *filename, void **buf, size_t *size);
|
||||
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size);
|
||||
void stream_full_read(int fd, void **buf, size_t *size);
|
||||
void write_zero(int fd, size_t size);
|
||||
void mem_align(size_t *pos, size_t align);
|
||||
void file_align(int fd, size_t align, int out);
|
||||
@@ -137,4 +142,10 @@ void umount_image(const char *target, const char *device);
|
||||
int merge_img(const char *source, const char *target);
|
||||
void trim_img(const char *img);
|
||||
|
||||
// pattern.c
|
||||
|
||||
void patch_init_rc(void **buf, size_t *size);
|
||||
int patch_verity(void **buf, uint32_t *size, int patch);
|
||||
void patch_encryption(void **buf, uint32_t *size);
|
||||
|
||||
#endif
|
@@ -7,8 +7,8 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
struct vector {
|
||||
size_t size;
|
||||
size_t cap;
|
||||
unsigned size;
|
||||
unsigned cap;
|
||||
void **data;
|
||||
};
|
||||
|
||||
@@ -26,11 +26,11 @@ struct vector *vec_dup(struct vector *v);
|
||||
/* Usage: vec_for_each(vector *v, void *e) */
|
||||
#define vec_for_each(v, e) \
|
||||
e = v ? (v)->data[0] : NULL; \
|
||||
for (size_t _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
|
||||
for (int _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
|
||||
|
||||
#define vec_for_each_r(v, e) \
|
||||
e = v ? (v)->data[(v)->size - 1] : NULL; \
|
||||
for (size_t _ = (v)->size; v && _ > 0; --_, e = (v)->data[_ - 1])
|
||||
e = (v && (v)->size > 0) ? (v)->data[(v)->size - 1] : NULL; \
|
||||
for (int _ = ((int) (v)->size) - 1; v && _ >= 0; --_, e = (v)->data[_])
|
||||
|
||||
#define vec_cur(v) vec_entry(v)[_]
|
||||
|
@@ -8,6 +8,11 @@
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define INSUF_BLOCK_RET 2
|
||||
#define CHROMEOS_RET 3
|
||||
#define ELF32_RET 4
|
||||
#define ELF64_RET 5
|
||||
|
||||
static void dump(void *buf, size_t size, const char *filename) {
|
||||
int fd = creat(filename, 0644);
|
||||
xwrite(fd, buf, size);
|
||||
@@ -50,63 +55,63 @@ static void print_hdr(const boot_img_hdr *hdr) {
|
||||
}
|
||||
fprintf(stderr, "NAME [%s]\n", hdr->name);
|
||||
fprintf(stderr, "CMDLINE [%s]\n", hdr->cmdline);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int parse_img(void *orig, size_t size, boot_img *boot) {
|
||||
void *base, *end;
|
||||
size_t pos = 0;
|
||||
int ret = 0;
|
||||
int parse_img(const char *image, boot_img *boot) {
|
||||
memset(boot, 0, sizeof(*boot));
|
||||
for(base = orig, end = orig + size; base < end; base += 256, size -= 256) {
|
||||
switch (check_type(base)) {
|
||||
int is_blk = mmap_ro(image, &boot->map_addr, &boot->map_size);
|
||||
|
||||
// Parse image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
||||
for (size_t pos = 0; pos < boot->map_size; pos += 256) {
|
||||
switch (check_type(boot->map_addr + pos)) {
|
||||
case CHROMEOS:
|
||||
// The caller should know it's chromeos, as it needs additional signing
|
||||
ret = 2;
|
||||
boot->flags |= CHROMEOS_FLAG;
|
||||
continue;
|
||||
case ELF32:
|
||||
exit(3);
|
||||
exit(ELF32_RET);
|
||||
case ELF64:
|
||||
exit(4);
|
||||
exit(ELF64_RET);
|
||||
case AOSP:
|
||||
// Read the header
|
||||
memcpy(&boot->hdr, base, sizeof(boot->hdr));
|
||||
memcpy(&boot->hdr, boot->map_addr + pos, sizeof(boot->hdr));
|
||||
pos += boot->hdr.page_size;
|
||||
|
||||
print_hdr(&boot->hdr);
|
||||
|
||||
boot->kernel = base + pos;
|
||||
boot->kernel = boot->map_addr + pos;
|
||||
pos += boot->hdr.kernel_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
|
||||
boot->ramdisk = base + pos;
|
||||
boot->ramdisk = boot->map_addr + pos;
|
||||
pos += boot->hdr.ramdisk_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
|
||||
if (boot->hdr.second_size) {
|
||||
boot->second = base + pos;
|
||||
boot->second = boot->map_addr + pos;
|
||||
pos += boot->hdr.second_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (boot->hdr.extra_size) {
|
||||
boot->extra = base + pos;
|
||||
boot->extra = boot->map_addr + pos;
|
||||
pos += boot->hdr.extra_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (pos < size) {
|
||||
boot->tail = base + pos;
|
||||
boot->tail_size = end - base - pos;
|
||||
if (pos < boot->map_size) {
|
||||
boot->tail = boot->map_addr + pos;
|
||||
boot->tail_size = boot->map_size - pos;
|
||||
}
|
||||
|
||||
// Search for dtb in kernel
|
||||
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
|
||||
for (uint32_t i = 0; i < boot->hdr.kernel_size; ++i) {
|
||||
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
|
||||
boot->dtb = boot->kernel + i;
|
||||
boot->dt_size = boot->hdr.kernel_size - i;
|
||||
boot->hdr.kernel_size = i;
|
||||
fprintf(stderr, "DTB [%d]\n", boot->dt_size);
|
||||
fprintf(stderr, "DTB [%u]\n", boot->dt_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,27 +142,20 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
|
||||
fprintf(stderr, "KERNEL_FMT [%s]\n", fmt);
|
||||
get_type_name(boot->ramdisk_type, fmt);
|
||||
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
return ret;
|
||||
return boot->flags & CHROMEOS_FLAG ? CHROMEOS_RET :
|
||||
((is_blk && boot->tail_size < 500 * 1024) ? INSUF_BLOCK_RET : 0);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOGE("No boot image magic found!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void unpack(const char* image) {
|
||||
size_t size;
|
||||
void *orig;
|
||||
mmap_ro(image, &orig, &size);
|
||||
int fd;
|
||||
boot_img boot;
|
||||
|
||||
// Parse image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n\n", image);
|
||||
int ret = parse_img(orig, size, &boot);
|
||||
int ret = parse_img(image, &boot);
|
||||
int fd;
|
||||
|
||||
// Dump kernel
|
||||
if (COMPRESSED(boot.kernel_type)) {
|
||||
@@ -193,26 +191,20 @@ void unpack(const char* image) {
|
||||
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
|
||||
}
|
||||
|
||||
munmap(orig, size);
|
||||
munmap(boot.map_addr, boot.map_size);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void repack(const char* orig_image, const char* out_image) {
|
||||
size_t size;
|
||||
void *orig;
|
||||
boot_img boot;
|
||||
|
||||
// There are possible two MTK headers
|
||||
size_t mtk_kernel_off, mtk_ramdisk_off;
|
||||
|
||||
// Load original image
|
||||
mmap_ro(orig_image, &orig, &size);
|
||||
|
||||
// Parse original image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n\n", orig_image);
|
||||
parse_img(orig, size, &boot);
|
||||
parse_img(orig_image, &boot);
|
||||
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n\n", out_image);
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_image);
|
||||
|
||||
// Create new image
|
||||
int fd = creat(out_image, 0644);
|
||||
@@ -309,7 +301,6 @@ void repack(const char* orig_image, const char* out_image) {
|
||||
// Print new image info
|
||||
print_hdr(&boot.hdr);
|
||||
|
||||
munmap(orig, size);
|
||||
munmap(boot.map_addr, boot.map_size);
|
||||
close(fd);
|
||||
}
|
||||
|
@@ -103,20 +103,25 @@ typedef struct mtk_hdr {
|
||||
// Flags
|
||||
#define MTK_KERNEL 0x1
|
||||
#define MTK_RAMDISK 0x2
|
||||
#define CHROMEOS_FLAG 0x4
|
||||
|
||||
typedef struct boot_img {
|
||||
size_t map_size;
|
||||
uint32_t dt_size;
|
||||
size_t tail_size;
|
||||
uint8_t flags;
|
||||
file_t kernel_type, ramdisk_type;
|
||||
|
||||
boot_img_hdr hdr;
|
||||
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
|
||||
|
||||
void *map_addr;
|
||||
void *kernel;
|
||||
void *dtb;
|
||||
uint32_t dt_size;
|
||||
void *ramdisk;
|
||||
void *second;
|
||||
void *extra;
|
||||
void *tail;
|
||||
uint32_t tail_size;
|
||||
uint32_t flags;
|
||||
file_t kernel_type, ramdisk_type;
|
||||
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
|
||||
} boot_img;
|
||||
|
||||
#endif
|
@@ -392,7 +392,7 @@ void decomp_file(char *from, const char *to) {
|
||||
void *file;
|
||||
size_t size = 0;
|
||||
if (strcmp(from, "-") == 0)
|
||||
full_read(STDIN_FILENO, &file, &size);
|
||||
stream_full_read(STDIN_FILENO, &file, &size);
|
||||
else
|
||||
mmap_ro(from, &file, &size);
|
||||
file_t type = check_type(file);
|
||||
@@ -437,7 +437,7 @@ void decomp_file(char *from, const char *to) {
|
||||
fd = STDOUT_FILENO;
|
||||
} else {
|
||||
fd = creat(to, 0644);
|
||||
fprintf(stderr, "Decompressing to [%s]\n\n", to);
|
||||
fprintf(stderr, "Decompressing to [%s]\n", to);
|
||||
}
|
||||
|
||||
decomp(type, fd, file, size);
|
||||
@@ -483,7 +483,7 @@ void comp_file(const char *method, const char *from, const char *to) {
|
||||
void *file;
|
||||
size_t size;
|
||||
if (strcmp(from, "-") == 0)
|
||||
full_read(STDIN_FILENO, &file, &size);
|
||||
stream_full_read(STDIN_FILENO, &file, &size);
|
||||
else
|
||||
mmap_ro(from, &file, &size);
|
||||
if (to == NULL) {
|
||||
@@ -498,7 +498,7 @@ void comp_file(const char *method, const char *from, const char *to) {
|
||||
fd = STDOUT_FILENO;
|
||||
} else {
|
||||
fd = creat(dest, 0644);
|
||||
fprintf(stderr, "Compressing to [%s]\n\n", dest);
|
||||
fprintf(stderr, "Compressing to [%s]\n", dest);
|
||||
}
|
||||
comp(type, fd, file, size);
|
||||
close(fd);
|
@@ -5,8 +5,6 @@
|
||||
#include "magiskboot.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern int check_verity_pattern(const char *s);
|
||||
|
||||
static void print_props(const void *fdt, int node, int depth) {
|
||||
int prop;
|
||||
fdt_for_each_property_offset(prop, fdt, node) {
|
||||
@@ -51,7 +49,7 @@ static void dtb_dump(const char *file) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
fdt = dtb + i;
|
||||
fprintf(stderr, "Dumping dtb.%04d\n\n", dtb_num++);
|
||||
fprintf(stderr, "Dumping dtb.%04d\n", dtb_num++);
|
||||
print_subnode(fdt, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -63,7 +61,7 @@ static void dtb_dump(const char *file) {
|
||||
static void dtb_patch(const char *file, int patch) {
|
||||
size_t size ;
|
||||
void *dtb, *fdt;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n\n", file);
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
if (patch)
|
||||
mmap_rw(file, &dtb, &size);
|
||||
else
|
||||
@@ -79,26 +77,13 @@ static void dtb_patch(const char *file, int patch) {
|
||||
int block;
|
||||
fdt_for_each_subnode(block, fdt, fstab) {
|
||||
fprintf(stderr, "Found block [%s] in fstab\n", fdt_get_name(fdt, block, NULL));
|
||||
int skip, value_size;
|
||||
char *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
|
||||
for (int i = 0; i < value_size; ++i) {
|
||||
if ((skip = check_verity_pattern(value + i)) > 0) {
|
||||
if (patch) {
|
||||
fprintf(stderr, "Remove pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
|
||||
memcpy(value + i, value + i + skip, value_size - i - skip);
|
||||
memset(value + value_size - skip, '\0', skip);
|
||||
} else {
|
||||
fprintf(stderr, "Found pattern [%.*s] in [fsmgr_flags]\n", skip, value + i);
|
||||
i += skip - 1;
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
uint32_t value_size;
|
||||
void *value = (char *) fdt_getprop(fdt, block, "fsmgr_flags", &value_size);
|
||||
found |= patch_verity(&value, &value_size, patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
munmap(dtb, size);
|
||||
exit(!found);
|
||||
}
|
@@ -26,7 +26,7 @@ void hexpatch(const char *image, const char *from, const char *to) {
|
||||
hex2byte(to, patch);
|
||||
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
|
||||
if (memcmp(file + i, pattern, patternsize) == 0) {
|
||||
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
|
||||
fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to);
|
||||
memset(file + i, 0, patternsize);
|
||||
memcpy(file + i, patch, patchsize);
|
||||
i += patternsize - 1;
|
@@ -17,8 +17,8 @@
|
||||
void unpack(const char *image);
|
||||
void repack(const char* orig_image, const char* out_image);
|
||||
void hexpatch(const char *image, const char *from, const char *to);
|
||||
int parse_img(void *orig, size_t size, boot_img *boot);
|
||||
int cpio_commands(const char *cmd, int argc, char *argv[]);
|
||||
int parse_img(const char *image, boot_img *boot);
|
||||
int cpio_commands(int argc, char *argv[]);
|
||||
void comp_file(const char *method, const char *from, const char *to);
|
||||
void decomp_file(char *from, const char *to);
|
||||
int dtb_commands(const char *cmd, int argc, char *argv[]);
|
@@ -17,51 +17,68 @@ 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"
|
||||
" 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\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"
|
||||
" if exists, or attempt to find ramdisk.cpio.[ext], and repack\n"
|
||||
" directly with the compressed ramdisk file\n"
|
||||
" It will compress ramdisk.cpio with the same method used in <origbootimg>,\n"
|
||||
" or attempt to find ramdisk.cpio.[ext], and repack directly with the\n"
|
||||
" compressed ramdisk file\n"
|
||||
"\n"
|
||||
" --hexpatch <file> <hexpattern1> <hexpattern2>\n"
|
||||
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
|
||||
"\n"
|
||||
" --cpio-<cmd> <incpio> [flags...] [args...]\n"
|
||||
" Do cpio related cmds to <incpio> (modifications are done directly)\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 from <incpio>, flag -r to remove recursively\n"
|
||||
" -mkdir <mode> <entry>\n"
|
||||
" Create directory as an <entry>\n"
|
||||
" -add <mode> <entry> <infile>\n"
|
||||
" Add <infile> as an <entry>; replaces <entry> if already exists\n"
|
||||
" -mv <from-entry> <to-entry>\n"
|
||||
" Move <from-entry> to <to-entry>\n"
|
||||
" -extract <entry> <outfile>\n"
|
||||
" Extract <entry> to <outfile>\n"
|
||||
" -test\n"
|
||||
" 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"
|
||||
" Patch cpio for Magisk. KEEP**** are true/false values\n"
|
||||
" -backup <origcpio> [SHA1]\n"
|
||||
" Create ramdisk backups into <incpio> from <origcpio>\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 within <incpio>\n"
|
||||
" -stocksha1\n"
|
||||
" Get stock boot SHA1 recorded within <incpio>\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\n"
|
||||
" Dump all contents from dtb for debugging\n"
|
||||
" -patch\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"
|
||||
@@ -72,8 +89,7 @@ static void usage(char *arg0) {
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"\n"
|
||||
"\n\n"
|
||||
" --decompress <infile> [outfile]\n"
|
||||
" Detect method and decompress <infile>, optionally to [outfile]\n"
|
||||
" <infile>/[outfile] can be '-' to be STDIN/STDOUT\n"
|
||||
@@ -81,8 +97,7 @@ static void usage(char *arg0) {
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"\n"
|
||||
"\n\n"
|
||||
" --sha1 <file>\n"
|
||||
" Print the SHA1 checksum for <file>\n"
|
||||
"\n"
|
||||
@@ -94,10 +109,11 @@ static void usage(char *arg0) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "MagiskBoot v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Boot Image Modification Tool\n\n");
|
||||
fprintf(stderr, "MagiskBoot v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Boot Image Modification Tool\n");
|
||||
|
||||
umask(0);
|
||||
if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) {
|
||||
fprintf(stderr, "Cleaning up...\n\n");
|
||||
fprintf(stderr, "Cleaning up...\n");
|
||||
char name[PATH_MAX];
|
||||
unlink(KERNEL_FILE);
|
||||
unlink(RAMDISK_FILE);
|
||||
@@ -118,6 +134,9 @@ int main(int argc, char *argv[]) {
|
||||
printf("%02x", sha1[i]);
|
||||
printf("\n");
|
||||
munmap(buf, size);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--parse") == 0) {
|
||||
boot_img boot;
|
||||
exit(parse_img(argv[2], &boot));
|
||||
} else if (argc > 2 && strcmp(argv[1], "--unpack") == 0) {
|
||||
unpack(argv[2]);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--repack") == 0) {
|
||||
@@ -132,11 +151,8 @@ int main(int argc, char *argv[]) {
|
||||
comp_file(method, argv[2], argc > 3 ? argv[3] : NULL);
|
||||
} else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) {
|
||||
hexpatch(argv[2], argv[3], argv[4]);
|
||||
} else if (argc > 2 && strncmp(argv[1], "--cpio", 6) == 0) {
|
||||
char *cmd = argv[1] + 6;
|
||||
if (*cmd == '\0') usage(argv[0]);
|
||||
else ++cmd;
|
||||
if (cpio_commands(cmd, argc - 2, argv + 2)) usage(argv[0]);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--cpio") == 0) {
|
||||
if (cpio_commands(argc - 2, argv + 2)) usage(argv[0]);
|
||||
} else if (argc > 2 && strncmp(argv[1], "--dtb", 5) == 0) {
|
||||
char *cmd = argv[1] + 5;
|
||||
if (*cmd == '\0') usage(argv[0]);
|
334
core/jni/magiskboot/ramdisk.c
Normal file
334
core/jni/magiskboot/ramdisk.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utils.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "cpio.h"
|
||||
|
||||
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
|
||||
fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n",
|
||||
keepverity ? "true" : "false", keepforceencrypt ? "true" : "false");
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (!keepverity) {
|
||||
if (strncmp(e->filename, ".backup", 7) && strstr(e->filename, "fstab") && S_ISREG(e->mode)) {
|
||||
patch_verity(&e->data, &e->filesize, 1);
|
||||
} else if (strcmp(e->filename, "verity_key") == 0) {
|
||||
fprintf(stderr, "Remove [verity_key]\n");
|
||||
cpio_free(e);
|
||||
vec_cur(v) = NULL;
|
||||
}
|
||||
}
|
||||
if (!keepforceencrypt) {
|
||||
if (strstr(e->filename, "fstab") != NULL && S_ISREG(e->mode)) {
|
||||
patch_encryption(&e->data, &e->filesize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define STOCK_BOOT 0x0
|
||||
#define MAGISK_PATCH 0x1
|
||||
#define OTHER_PATCH 0x2
|
||||
|
||||
static int cpio_test(struct vector *v) {
|
||||
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
|
||||
const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL };
|
||||
|
||||
for (int i = 0; OTHER_LIST[i]; ++i)
|
||||
if (cpio_find(v, OTHER_LIST[i]) > 0)
|
||||
return OTHER_PATCH;
|
||||
|
||||
for (int i = 0; MAGISK_LIST[i]; ++i)
|
||||
if (cpio_find(v, MAGISK_LIST[i]) > 0)
|
||||
return MAGISK_PATCH;
|
||||
|
||||
return STOCK_BOOT;
|
||||
}
|
||||
|
||||
static char *cpio_sha1(struct vector *v) {
|
||||
cpio_entry *e;
|
||||
char sha1[41];
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strcmp(e->filename, "init.magisk.rc") == 0
|
||||
|| strcmp(e->filename, "overlay/init.magisk.rc") == 0) {
|
||||
for (void *pos = e->data; pos < e->data + e->filesize; pos = strchr(pos + 1, '\n') + 1) {
|
||||
if (memcmp(pos, "# STOCKSHA1=", 12) == 0) {
|
||||
pos += 12;
|
||||
memcpy(sha1, pos, 40);
|
||||
sha1[40] = '\0';
|
||||
return strdup(sha1);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(e->filename, ".backup/.sha1") == 0) {
|
||||
return e->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cpio_backup(struct vector *v, struct vector *bak, const char *orig, const char *sha1) {
|
||||
struct vector o_body, *o = &o_body;
|
||||
cpio_entry *m, *n, *rem, *cksm;
|
||||
char buf[PATH_MAX];
|
||||
int res, backup;
|
||||
|
||||
m = xcalloc(sizeof(*m), 1);
|
||||
m->filename = strdup(".backup");
|
||||
m->mode = S_IFDIR;
|
||||
vec_push_back(bak, m);
|
||||
|
||||
rem = xcalloc(sizeof(*rem), 1);
|
||||
rem->filename = strdup(".backup/.rmlist");
|
||||
rem->mode = S_IFREG;
|
||||
|
||||
if (sha1) {
|
||||
fprintf(stderr, "Save SHA1: [%s] -> [.backup/.sha1]\n", sha1);
|
||||
cksm = xcalloc(sizeof(*cksm), 1);
|
||||
vec_push_back(bak, cksm);
|
||||
cksm->filename = strdup(".backup/.sha1");
|
||||
cksm->mode = S_IFREG;
|
||||
cksm->data = strdup(sha1);
|
||||
cksm->filesize = strlen(sha1) + 1;
|
||||
}
|
||||
|
||||
vec_init(o);
|
||||
parse_cpio(o, orig);
|
||||
// Remove possible backups in original ramdisk
|
||||
cpio_rm(o, 1, ".backup");
|
||||
cpio_rm(v, 1, ".backup");
|
||||
|
||||
// Sort both vectors before comparing
|
||||
vec_sort(o, cpio_cmp);
|
||||
vec_sort(v, cpio_cmp);
|
||||
|
||||
// Start comparing
|
||||
size_t i = 0, j = 0;
|
||||
while(i != vec_size(o) || j != vec_size(v)) {
|
||||
backup = 0;
|
||||
if (i != vec_size(o) && j != vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
n = vec_entry(v)[j];
|
||||
res = strcmp(m->filename, n->filename);
|
||||
} else if (i == vec_size(o)) {
|
||||
n = vec_entry(v)[j];
|
||||
res = 1;
|
||||
} else if (j == vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
// Something is missing in new ramdisk, backup!
|
||||
++i;
|
||||
backup = 1;
|
||||
fprintf(stderr, "Backup missing entry: ");
|
||||
} else if (res == 0) {
|
||||
++i; ++j;
|
||||
if (m->filesize == n->filesize && memcmp(m->data, n->data, m->filesize) == 0)
|
||||
continue;
|
||||
// Not the same!
|
||||
backup = 1;
|
||||
fprintf(stderr, "Backup mismatch entry: ");
|
||||
} else {
|
||||
// Something new in ramdisk, record in rem
|
||||
++j;
|
||||
rem->data = xrealloc(rem->data, rem->filesize + strlen(n->filename) + 1);
|
||||
memcpy(rem->data + rem->filesize, n->filename, strlen(n->filename) + 1);
|
||||
rem->filesize += strlen(n->filename) + 1;
|
||||
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", n->filename);
|
||||
}
|
||||
if (backup) {
|
||||
sprintf(buf, ".backup/%s", m->filename);
|
||||
fprintf(stderr, "[%s] -> [%s]\n", m->filename, buf);
|
||||
free(m->filename);
|
||||
m->filename = strdup(buf);
|
||||
vec_push_back(bak, m);
|
||||
// NULL the original entry, so it won't be freed
|
||||
vec_entry(o)[i - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (rem->filesize)
|
||||
vec_push_back(bak, rem);
|
||||
else
|
||||
cpio_free(rem);
|
||||
|
||||
// Cleanup
|
||||
cpio_vec_destroy(o);
|
||||
}
|
||||
|
||||
static void cpio_restore(struct vector *v) {
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strncmp(e->filename, ".backup", 7) == 0) {
|
||||
if (e->filename[7] == '\0') continue;
|
||||
if (e->filename[8] == '.') {
|
||||
if (strcmp(e->filename, ".backup/.rmlist") == 0) {
|
||||
for (int pos = 0; pos < e->filesize; pos += strlen(e->data + pos) + 1)
|
||||
cpio_rm(v, 0, e->data + pos);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
fprintf(stderr, "Restore [%s] -> [%s]\n", e->filename, e->filename + 8);
|
||||
vec_cur(v) = NULL;
|
||||
char *new_name = strdup(e->filename + 8);
|
||||
free(e->filename);
|
||||
e->filename = new_name;
|
||||
cpio_vec_insert(v, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Some known stuff we can remove
|
||||
cpio_rm(v, 1, ".backup");
|
||||
cpio_rm(v, 1, "overlay");
|
||||
cpio_rm(v, 0, "sbin/magic_mask.sh");
|
||||
cpio_rm(v, 0, "init.magisk.rc");
|
||||
cpio_rm(v, 0, "magisk");
|
||||
cpio_rm(v, 0, "ramdisk-recovery.xz");
|
||||
}
|
||||
|
||||
static void restore_high_compress(struct vector *v, const char *incpio) {
|
||||
// Check if the ramdisk is in high compression mode
|
||||
if (cpio_extract(v, "ramdisk.cpio.xz", incpio) == 0) {
|
||||
void *xz;
|
||||
size_t size;
|
||||
full_read(incpio, &xz, &size);
|
||||
int fd = creat(incpio, 0644);
|
||||
lzma(0, fd, xz, size);
|
||||
close(fd);
|
||||
free(xz);
|
||||
cpio_rm(v, 0, "ramdisk.cpio.xz");
|
||||
cpio_rm(v, 0, "init");
|
||||
struct vector vv;
|
||||
vec_init(&vv);
|
||||
parse_cpio(&vv, incpio);
|
||||
cpio_entry *e;
|
||||
vec_for_each(&vv, e)
|
||||
vec_push_back(v, e);
|
||||
vec_destroy(&vv);
|
||||
}
|
||||
}
|
||||
|
||||
static void enable_high_compress(struct vector *v, struct vector *b, const char *incpio) {
|
||||
cpio_entry *init, *magiskinit;
|
||||
|
||||
// Swap magiskinit with original init
|
||||
int i = cpio_find(b, ".backup/init"), j = cpio_find(v, "init");
|
||||
init = vec_entry(b)[i];
|
||||
magiskinit = vec_entry(v)[j];
|
||||
free(init->filename);
|
||||
init->filename = strdup("init");
|
||||
vec_entry(v)[j] = init;
|
||||
vec_entry(b)[i] = NULL;
|
||||
|
||||
dump_cpio(v, incpio);
|
||||
cpio_vec_destroy(v);
|
||||
void *cpio;
|
||||
size_t size;
|
||||
full_read(incpio, &cpio, &size);
|
||||
int fd = creat(incpio, 0644);
|
||||
lzma(1, fd, cpio, size);
|
||||
close(fd);
|
||||
free(cpio);
|
||||
vec_init(v);
|
||||
vec_push_back(v, magiskinit);
|
||||
cpio_add(v, 0, "ramdisk.cpio.xz", incpio);
|
||||
}
|
||||
|
||||
int cpio_commands(int argc, char *argv[]) {
|
||||
char *incpio = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
parse_cpio(&v, incpio);
|
||||
|
||||
int cmdc;
|
||||
char *cmdv[6];
|
||||
|
||||
while (argc) {
|
||||
cmdc = 0;
|
||||
for (char *tok = strtok(argv[0], " "); tok; tok = strtok(NULL, " "))
|
||||
cmdv[cmdc++] = tok;
|
||||
|
||||
if (strcmp(cmdv[0], "test") == 0) {
|
||||
exit(cpio_test(&v));
|
||||
} else if (strcmp(cmdv[0], "restore") == 0) {
|
||||
restore_high_compress(&v, incpio);
|
||||
cpio_restore(&v);
|
||||
} else if (strcmp(cmdv[0], "sha1") == 0) {
|
||||
char *sha1 = cpio_sha1(&v);
|
||||
if (sha1)
|
||||
printf("%s\n", sha1);
|
||||
return 0;
|
||||
} else if (cmdc >= 2 && strcmp(cmdv[0], "backup") == 0) {
|
||||
struct vector back;
|
||||
vec_init(&back);
|
||||
cpio_backup(&v, &back, cmdv[1], cmdc > 2 ? cmdv[2] : NULL);
|
||||
cpio_entry *e;
|
||||
vec_for_each(&back, e)
|
||||
if (e) vec_push_back(&v, e);
|
||||
vec_destroy(&back);
|
||||
} else if (cmdc >= 5 && strcmp(cmdv[0], "magisk") == 0) {
|
||||
cpio_patch(&v, strcmp(cmdv[3], "true") == 0, strcmp(cmdv[4], "true") == 0);
|
||||
|
||||
struct vector back;
|
||||
vec_init(&back);
|
||||
cpio_backup(&v, &back, cmdv[1], cmdc > 5 ? cmdv[5] : NULL);
|
||||
|
||||
cpio_entry *e;
|
||||
e = xcalloc(sizeof(*e), 1);
|
||||
e->filename = strdup(".backup/.magisk");
|
||||
e->mode = S_IFREG;
|
||||
e->data = xmalloc(50);
|
||||
snprintf(e->data, 50, "KEEPVERITY=%s\nKEEPFORCEENCRYPT=%s\n", cmdv[3], cmdv[4]);
|
||||
e->filesize = strlen(e->data) + 1;
|
||||
vec_push_back(&back, e);
|
||||
|
||||
// Enable high compression mode
|
||||
if (strcmp(cmdv[2], "true") == 0)
|
||||
enable_high_compress(&v, &back, incpio);
|
||||
|
||||
vec_for_each(&back, e)
|
||||
if (e) vec_push_back(&v, e);
|
||||
vec_destroy(&back);
|
||||
} else if (cmdc >= 2 && strcmp(cmdv[0], "rm") == 0) {
|
||||
int recur = cmdc > 2 && strcmp(cmdv[1], "-r") == 0;
|
||||
cpio_rm(&v, recur, cmdv[1 + recur]);
|
||||
} else if (cmdc == 3 && strcmp(cmdv[0], "mv") == 0) {
|
||||
cpio_mv(&v, cmdv[1], cmdv[2]);
|
||||
} else if (cmdc == 3 && strcmp(cmdv[0], "patch") == 0) {
|
||||
cpio_patch(&v, strcmp(cmdv[1], "true") == 0, strcmp(cmdv[2], "true") == 0);
|
||||
} else if (strcmp(cmdv[0], "extract") == 0) {
|
||||
if (cmdc == 3) {
|
||||
return cpio_extract(&v, cmdv[1], cmdv[2]);
|
||||
} else {
|
||||
cpio_extract_all(&v);
|
||||
return 0;
|
||||
}
|
||||
} else if (cmdc == 3 && strcmp(cmdv[0], "mkdir") == 0) {
|
||||
cpio_mkdir(&v, strtoul(cmdv[1], NULL, 8), cmdv[2]);
|
||||
} else if (cmdc == 3 && strcmp(cmdv[0], "ln") == 0) {
|
||||
cpio_ln(&v, cmdv[1], cmdv[2]);
|
||||
} else if (cmdc == 4 && strcmp(cmdv[0], "add") == 0) {
|
||||
cpio_add(&v, strtoul(cmdv[1], NULL, 8), cmdv[2], cmdv[3]);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
dump_cpio(&v, incpio);
|
||||
cpio_vec_destroy(&v);
|
||||
return 0;
|
||||
}
|
@@ -3,10 +3,6 @@
|
||||
#include "bootimg.h"
|
||||
#include "types.h"
|
||||
|
||||
char *SUP_LIST[] = { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL };
|
||||
char *SUP_EXT_LIST[] = { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL };
|
||||
file_t SUP_TYPE_LIST[] = { GZIP, XZ, LZMA, BZIP2, LZ4, LZ4_LEGACY, 0 };
|
||||
|
||||
file_t check_type(const void *buf) {
|
||||
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
|
||||
return CHROMEOS;
|
@@ -33,9 +33,8 @@ typedef enum {
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
|
||||
|
||||
extern char *SUP_LIST[];
|
||||
extern char *SUP_EXT_LIST[];
|
||||
extern file_t SUP_TYPE_LIST[];
|
||||
#define SUP_LIST ((char *[]) { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL })
|
||||
#define SUP_EXT_LIST ((char *[]) { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL })
|
||||
|
||||
file_t check_type(const void *buf);
|
||||
void get_type_name(file_t type, char *name);
|
@@ -4,10 +4,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <selinux/selinux.h>
|
@@ -28,7 +28,7 @@ static void term_thread(int sig) {
|
||||
destroy_list();
|
||||
hideEnabled = 0;
|
||||
// Unregister listener
|
||||
logcat_events[HIDE_EVENT] = -1;
|
||||
log_events[HIDE_EVENT].fd = -1;
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
pipefd[0] = pipefd[1] = -1;
|
||||
@@ -42,7 +42,9 @@ static void hide_done(int sig) {
|
||||
--hide_queue;
|
||||
if (hide_queue == 0) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xsymlink(MOUNTPOINT, FAKEPOINT);
|
||||
xsymlink(DATABIN, "/data/magisk");
|
||||
xsymlink(MAINIMG, "/data/magisk.img");
|
||||
xsymlink(MOUNTPOINT, "/magisk");
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +76,7 @@ static void lazy_unmount(const char* mountpoint) {
|
||||
|
||||
static void hide_daemon(int pid, int ppid) {
|
||||
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
|
||||
strcpy(argv0, "hide_daemon");
|
||||
|
||||
char *line, buffer[PATH_MAX];
|
||||
struct vector mount_list;
|
||||
@@ -190,76 +193,75 @@ void proc_monitor() {
|
||||
|
||||
// Register our listener to logcat monitor
|
||||
xpipe2(pipefd, O_CLOEXEC);
|
||||
logcat_events[HIDE_EVENT] = pipefd[1];
|
||||
log_events[HIDE_EVENT].fd = pipefd[1];
|
||||
|
||||
for (char *log, *line; 1; free(log)) {
|
||||
for (char *log, *line;; free(log)) {
|
||||
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
|
||||
/* It might be interrupted */
|
||||
log = NULL;
|
||||
continue;
|
||||
}
|
||||
char *ss;
|
||||
if ((ss = strstr(log, "am_proc_start")) && (ss = strchr(ss, '['))) {
|
||||
int pid, ret, comma = 0;
|
||||
char *pos = ss, processName[256], ns[32];
|
||||
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;
|
||||
|
||||
ret = 0;
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
unlink(FAKEPOINT);
|
||||
unlink(MAGISKRC);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
++hide_queue;
|
||||
int selfpid = getpid();
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid, selfpid);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
++comma;
|
||||
}
|
||||
|
||||
if (comma == 6)
|
||||
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
else
|
||||
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
unlink("/magisk");
|
||||
unlink("/data/magisk");
|
||||
unlink("/data/magisk.img");
|
||||
unlink(MAGISKRC);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
++hide_queue;
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
int selfpid = getpid();
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid, selfpid);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
}
|
||||
}
|
1
core/jni/magiskpolicy
Submodule
1
core/jni/magiskpolicy
Submodule
Submodule core/jni/magiskpolicy added at edab891427
@@ -209,7 +209,7 @@ public:
|
||||
vector prop_list;
|
||||
|
||||
static int prop_cmp(const void *p1, const void *p2) {
|
||||
return strcmp((*((property **) p1))->name, (*((property **) p2))->name);
|
||||
return strcmp(((property *) p1)->name, ((property *) p2)->name);
|
||||
}
|
||||
|
||||
static void print_all_props_cb(const char *name, const char *value) {
|
1
core/jni/su
Submodule
1
core/jni/su
Submodule
Submodule core/jni/su added at ed5dd827e9
256
core/jni/utils/cpio.c
Normal file
256
core/jni/utils/cpio.c
Normal file
@@ -0,0 +1,256 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "cpio.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
static uint32_t x8u(char *hex) {
|
||||
uint32_t val, inpos = 8, outpos;
|
||||
char pattern[6];
|
||||
|
||||
while (*hex == '0') {
|
||||
hex++;
|
||||
if (!--inpos) return 0;
|
||||
}
|
||||
// Because scanf gratuitously treats %*X differently than printf does.
|
||||
sprintf(pattern, "%%%dx%%n", inpos);
|
||||
sscanf(hex, pattern, &val, &outpos);
|
||||
if (inpos != outpos) LOGE("bad cpio header\n");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void cpio_free(cpio_entry *e) {
|
||||
if (e) {
|
||||
free(e->filename);
|
||||
free(e->data);
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
int cpio_find(struct vector *v, const char *entry) {
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strcmp(e->filename, entry) == 0)
|
||||
return _;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cpio_cmp(const void *a, const void *b) {
|
||||
return strcmp(((cpio_entry *) a)->filename, ((cpio_entry *) b)->filename);
|
||||
}
|
||||
|
||||
void cpio_vec_insert(struct vector *v, cpio_entry *n) {
|
||||
int i = cpio_find(v, n->filename);
|
||||
if (i > 0) {
|
||||
// Replace, then all is done
|
||||
cpio_free(vec_entry(v)[i]);
|
||||
vec_entry(v)[i] = n;
|
||||
return;
|
||||
}
|
||||
vec_push_back(v, n);
|
||||
}
|
||||
|
||||
// Parse cpio file to a vector of cpio_entry
|
||||
void parse_cpio(struct vector *v, const char *filename) {
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) return;
|
||||
fprintf(stderr, "Loading cpio: [%s]\n", filename);
|
||||
cpio_newc_header header;
|
||||
cpio_entry *f;
|
||||
while(xxread(fd, &header, 110) != -1) {
|
||||
f = xcalloc(sizeof(*f), 1);
|
||||
// f->ino = x8u(header.ino);
|
||||
f->mode = x8u(header.mode);
|
||||
f->uid = x8u(header.uid);
|
||||
f->gid = x8u(header.gid);
|
||||
// f->nlink = x8u(header.nlink);
|
||||
// f->mtime = x8u(header.mtime);
|
||||
f->filesize = x8u(header.filesize);
|
||||
// f->devmajor = x8u(header.devmajor);
|
||||
// f->devminor = x8u(header.devminor);
|
||||
// f->rdevmajor = x8u(header.rdevmajor);
|
||||
// f->rdevminor = x8u(header.rdevminor);
|
||||
uint32_t namesize = x8u(header.namesize);
|
||||
// f->check = x8u(header.check);
|
||||
f->filename = xmalloc(namesize);
|
||||
xxread(fd, f->filename, namesize);
|
||||
file_align(fd, 4, 0);
|
||||
if (strcmp(f->filename, ".") == 0 || strcmp(f->filename, "..") == 0) {
|
||||
cpio_free(f);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(f->filename, "TRAILER!!!") == 0) {
|
||||
cpio_free(f);
|
||||
break;
|
||||
}
|
||||
if (f->filesize) {
|
||||
f->data = xmalloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 0);
|
||||
}
|
||||
vec_push_back(v, f);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void dump_cpio(struct vector *v, const char *filename) {
|
||||
fprintf(stderr, "Dump cpio: [%s]\n", filename);
|
||||
unsigned inode = 300000;
|
||||
char header[111];
|
||||
// Sort by name
|
||||
vec_sort(v, cpio_cmp);
|
||||
cpio_entry *e;
|
||||
int fd = creat(filename, 0644);
|
||||
vec_for_each(v, e) {
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, // e->ino
|
||||
e->mode,
|
||||
e->uid,
|
||||
e->gid,
|
||||
1, // e->nlink
|
||||
0, // e->mtime
|
||||
e->filesize,
|
||||
0, // e->devmajor
|
||||
0, // e->devminor
|
||||
0, // e->rdevmajor
|
||||
0, // e->rdevminor
|
||||
(uint32_t) strlen(e->filename) + 1,
|
||||
0 // e->check
|
||||
);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, e->filename, strlen(e->filename) + 1);
|
||||
file_align(fd, 4, 1);
|
||||
if (e->filesize) {
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
file_align(fd, 4, 1);
|
||||
}
|
||||
}
|
||||
// Write trailer
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", inode++, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, "TRAILER!!!\0", 11);
|
||||
file_align(fd, 4, 1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void cpio_vec_destroy(struct vector *v) {
|
||||
// Free each cpio_entry
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e)
|
||||
cpio_free(e);
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
void cpio_rm(struct vector *v, int recursive, const char *entry) {
|
||||
cpio_entry *e;
|
||||
size_t len = strlen(entry);
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
if (strncmp(e->filename, entry, len) == 0) {
|
||||
if ((recursive && e->filename[len] == '/') || e->filename[len] == '\0') {
|
||||
fprintf(stderr, "Remove [%s]\n", e->filename);
|
||||
cpio_free(e);
|
||||
vec_cur(v) = NULL;
|
||||
if (!recursive) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpio_mkdir(struct vector *v, mode_t mode, const char *entry) {
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFDIR | mode;
|
||||
e->filename = strdup(entry);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
|
||||
}
|
||||
|
||||
void cpio_ln(struct vector *v, const char *target, const char *entry) {
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFLNK;
|
||||
e->filename = strdup(entry);
|
||||
e->filesize = strlen(target);
|
||||
e->data = strdup(target);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Create symlink [%s] -> [%s]\n", entry, target);
|
||||
}
|
||||
|
||||
void cpio_add(struct vector *v, mode_t mode, const char *entry, const char *filename) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_entry *e = xcalloc(sizeof(*e), 1);
|
||||
e->mode = S_IFREG | mode;
|
||||
e->filename = strdup(entry);
|
||||
e->filesize = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
e->data = xmalloc(e->filesize);
|
||||
xxread(fd, e->data, e->filesize);
|
||||
close(fd);
|
||||
cpio_vec_insert(v, e);
|
||||
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
|
||||
}
|
||||
|
||||
int cpio_mv(struct vector *v, const char *from, const char *to) {
|
||||
struct cpio_entry *e;
|
||||
int f = cpio_find(v, from), t = cpio_find(v, to);
|
||||
if (f > 0) {
|
||||
if (t > 0) {
|
||||
cpio_free(vec_entry(v)[t]);
|
||||
vec_entry(v)[t] = NULL;
|
||||
}
|
||||
e = vec_entry(v)[f];
|
||||
free(e->filename);
|
||||
e->filename = strdup(to);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Cannot find entry %s\n", from);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cpio_extract(struct vector *v, const char *entry, const char *filename) {
|
||||
int i = cpio_find(v, entry);
|
||||
if (i > 0) {
|
||||
cpio_entry *e = vec_entry(v)[i];
|
||||
fprintf(stderr, "Extracting [%s] to [%s]\n", entry, filename);
|
||||
if (S_ISREG(e->mode)) {
|
||||
int fd = creat(filename, e->mode & 0777);
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
fchown(fd, e->uid, e->gid);
|
||||
close(fd);
|
||||
} else if (S_ISLNK(e->mode)) {
|
||||
char *target = xcalloc(e->filesize + 1, 1);
|
||||
memcpy(target, e->data, e->filesize);
|
||||
unlink(filename);
|
||||
symlink(target, filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Cannot find the file entry [%s]\n", entry);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cpio_extract_all(struct vector *v) {
|
||||
cpio_entry *e;
|
||||
vec_for_each(v, e) {
|
||||
if (!e) continue;
|
||||
fprintf(stderr, "Extracting [%s]\n", e->filename);
|
||||
unlink(e->filename);
|
||||
rmdir(e->filename);
|
||||
if (S_ISDIR(e->mode)) {
|
||||
mkdir(e->filename, e->mode & 0777);
|
||||
} else if (S_ISREG(e->mode)) {
|
||||
int fd = creat(e->filename, e->mode & 0777);
|
||||
xwrite(fd, e->data, e->filesize);
|
||||
fchown(fd, e->uid, e->gid);
|
||||
close(fd);
|
||||
} else if (S_ISLNK(e->mode)) {
|
||||
char *target = xcalloc(e->filesize + 1, 1);
|
||||
memcpy(target, e->data, e->filesize);
|
||||
symlink(target, e->filename);
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,23 +6,24 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#ifndef NO_SELINUX
|
||||
#ifdef SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
char **excl_list = (char *[]) { NULL };
|
||||
char **excl_list = NULL;
|
||||
|
||||
static int is_excl(const char *name) {
|
||||
for (int i = 0; excl_list[i]; ++i) {
|
||||
if (strcmp(name, excl_list[i]) == 0)
|
||||
return 1;
|
||||
}
|
||||
if (excl_list)
|
||||
for (int i = 0; excl_list[i]; ++i)
|
||||
if (strcmp(name, excl_list[i]) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -54,39 +55,50 @@ int mkdir_p(const char *pathname, mode_t mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
int fd = xopen(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
frm_rf(fd);
|
||||
close(fd);
|
||||
rmdir(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
void in_order_walk(int dirfd, void (*callback)(int, struct dirent*)) {
|
||||
struct dirent *entry;
|
||||
int newfd;
|
||||
DIR *dir = xfdopendir(dirfd);
|
||||
DIR *dir = fdopendir(dirfd);
|
||||
if (dir == NULL) return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
if (entry->d_type == DT_DIR) {
|
||||
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
frm_rf(newfd);
|
||||
in_order_walk(newfd, callback);
|
||||
close(newfd);
|
||||
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
default:
|
||||
unlinkat(dirfd, entry->d_name, 0);
|
||||
break;
|
||||
}
|
||||
callback(dirfd, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_cb(int dirfd, struct dirent *entry) {
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
default:
|
||||
unlinkat(dirfd, entry->d_name, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
frm_rf(fd);
|
||||
close(fd);
|
||||
}
|
||||
remove(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
in_order_walk(dirfd, rm_cb);
|
||||
}
|
||||
|
||||
/* This will only on the same file system */
|
||||
void mv_f(const char *source, const char *destination) {
|
||||
struct stat st;
|
||||
@@ -219,8 +231,8 @@ void clone_dir(int src, int dest) {
|
||||
int getattr(const char *path, struct file_attr *a) {
|
||||
if (xlstat(path, &a->st) == -1)
|
||||
return -1;
|
||||
#ifdef SELINUX
|
||||
char *con = "";
|
||||
#ifndef NO_SELINUX
|
||||
if (lgetfilecon(path, &con) == -1)
|
||||
return -1;
|
||||
strcpy(a->con, con);
|
||||
@@ -241,7 +253,7 @@ int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
}
|
||||
|
||||
int fgetattr(int fd, struct file_attr *a) {
|
||||
#ifndef NO_SELINUX
|
||||
#ifdef SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return getattr(path, a);
|
||||
@@ -258,7 +270,7 @@ int setattr(const char *path, struct file_attr *a) {
|
||||
return -1;
|
||||
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
#ifndef NO_SELINUX
|
||||
#ifdef SELINUX
|
||||
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
|
||||
return -1;
|
||||
#endif
|
||||
@@ -275,7 +287,7 @@ int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
}
|
||||
|
||||
int fsetattr(int fd, struct file_attr *a) {
|
||||
#ifndef NO_SELINUX
|
||||
#ifdef SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return setattr(path, a);
|
||||
@@ -300,7 +312,7 @@ void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
fsetattr(targetfd, &a);
|
||||
}
|
||||
|
||||
#ifndef NO_SELINUX
|
||||
#ifdef SELINUX
|
||||
|
||||
#define UNLABEL_CON "u:object_r:unlabeled:s0"
|
||||
#define SYSTEM_CON "u:object_r:system_file:s0"
|
||||
@@ -336,29 +348,59 @@ void restorecon(int dirfd, int force) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NO_SELINUX
|
||||
#endif // SELINUX
|
||||
|
||||
static void _mmap(int rw, const char *filename, void **buf, size_t *size) {
|
||||
static int _mmap(int rw, const char *filename, void **buf, size_t *size) {
|
||||
struct stat st;
|
||||
stat(filename, &st);
|
||||
int fd = xopen(filename, rw ? O_RDWR : O_RDONLY);
|
||||
fstat(fd, &st);
|
||||
if (S_ISBLK(st.st_mode))
|
||||
ioctl(fd, BLKGETSIZE64, size);
|
||||
else
|
||||
*size = st.st_size;
|
||||
*buf = *size > 0 ? xmmap(NULL, *size, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, fd, 0) : NULL;
|
||||
close(fd);
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
void mmap_ro(const char *filename, void **buf, size_t *size) {
|
||||
_mmap(0, filename, buf, size);
|
||||
int mmap_ro(const char *filename, void **buf, size_t *size) {
|
||||
return _mmap(0, filename, buf, size);
|
||||
}
|
||||
|
||||
void mmap_rw(const char *filename, void **buf, size_t *size) {
|
||||
_mmap(1, filename, buf, size);
|
||||
int mmap_rw(const char *filename, void **buf, size_t *size) {
|
||||
return _mmap(1, filename, buf, size);
|
||||
}
|
||||
|
||||
void full_read(int fd, void **buf, size_t *size) {
|
||||
void fd_full_read(int fd, void **buf, size_t *size) {
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmalloc(*size);
|
||||
xxread(fd, *buf, *size);
|
||||
}
|
||||
|
||||
void full_read(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
fd_full_read(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopenat(dirfd, filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
*buf = NULL;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
fd_full_read(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void stream_full_read(int fd, void **buf, size_t *size) {
|
||||
size_t cap = 1 << 20;
|
||||
uint8_t tmp[1 << 20];
|
||||
*buf = xmalloc(cap);
|
@@ -50,6 +50,8 @@ static char *loopsetup(const char *img) {
|
||||
}
|
||||
|
||||
int create_img(const char *img, int size) {
|
||||
if (size == 128) /* WTF...? */
|
||||
size = 132;
|
||||
unlink(img);
|
||||
LOGI("Create %s with size %dM\n", img, size);
|
||||
int ret;
|
@@ -1,9 +1,6 @@
|
||||
/* list.h - Double link list implementation
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
void init_list_head(struct list_head *head) {
|
@@ -11,15 +11,16 @@
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
|
||||
#include "resetprop.h"
|
||||
|
||||
unsigned get_shell_uid() {
|
||||
struct passwd* ppwd = getpwnam("shell");
|
||||
@@ -46,18 +47,22 @@ unsigned get_radio_uid() {
|
||||
}
|
||||
|
||||
int check_data() {
|
||||
int ret = 0;
|
||||
char buffer[4096];
|
||||
FILE *fp = xfopen("/proc/mounts", "r");
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
if (strstr(buffer, " /data ")) {
|
||||
if (strstr(buffer, "tmpfs") == NULL)
|
||||
ret = 1;
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
file_to_vector("/proc/mounts", &v);
|
||||
char *line, *crypto;
|
||||
int mnt = 0;
|
||||
vec_for_each(&v, line) {
|
||||
if (strstr(line, " /data ")) {
|
||||
if (strstr(line, "tmpfs") == NULL)
|
||||
mnt = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return ret;
|
||||
vec_deep_destroy(&v);
|
||||
// /data is mounted and not tmpfs and data is unencrypted or vold is started
|
||||
return mnt && (((crypto = getprop("ro.crypto.state")) && strcmp(crypto, "unencrypted") == 0)
|
||||
|| getprop("init.svc.vold"));
|
||||
}
|
||||
|
||||
/* All the string should be freed manually!! */
|
||||
@@ -314,3 +319,20 @@ int fork_dont_care() {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wait_till_exists(const char *target) {
|
||||
if (access(target, F_OK) == 0)
|
||||
return;
|
||||
int fd = inotify_init();
|
||||
char *dir = dirname(target);
|
||||
char crap[PATH_MAX];
|
||||
inotify_add_watch(fd, dir, IN_CREATE);
|
||||
while (1) {
|
||||
struct inotify_event event;
|
||||
read(fd, &event, sizeof(event));
|
||||
read(fd, crap, event.len);
|
||||
if (access(target, F_OK) == 0)
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
97
core/jni/utils/pattern.c
Normal file
97
core/jni/utils/pattern.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static int check_verity_pattern(const char *s) {
|
||||
int skip = 0;
|
||||
if (s[0] == ',') ++skip;
|
||||
if (strncmp(s + skip, "verify", 6) == 0)
|
||||
skip += 6;
|
||||
else if (strncmp(s + skip, "avb", 3) == 0)
|
||||
skip += 3;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (s[skip] == '=') {
|
||||
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') ++skip;
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
static int check_encryption_pattern(const char *s) {
|
||||
const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe", NULL };
|
||||
for (int i = 0 ; encrypt_list[i]; ++i) {
|
||||
int len = strlen(encrypt_list[i]);
|
||||
if (strncmp(s, encrypt_list[i], len) == 0)
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void patch_init_rc(void **buf, size_t *size) {
|
||||
int injected = 0;
|
||||
char *new_data = malloc(*size + 23);
|
||||
char *old_data = *buf;
|
||||
size_t pos = 0;
|
||||
|
||||
for (char *tok = strsep(&old_data, "\n"); tok; tok = strsep(&old_data, "\n")) {
|
||||
if (!injected && strncmp(tok, "import", 6) == 0) {
|
||||
if (strstr(tok, "init.magisk.rc")) {
|
||||
injected = 1;
|
||||
} else {
|
||||
strcpy(new_data + pos, "import /init.magisk.rc\n");
|
||||
pos += 23;
|
||||
injected = 1;
|
||||
}
|
||||
} else if (strstr(tok, "selinux.reload_policy")) {
|
||||
continue;
|
||||
}
|
||||
// Copy the line
|
||||
strcpy(new_data + pos, tok);
|
||||
pos += strlen(tok);
|
||||
new_data[pos++] = '\n';
|
||||
}
|
||||
|
||||
free(*buf);
|
||||
*size = pos;
|
||||
*buf = new_data;
|
||||
}
|
||||
|
||||
int patch_verity(void **buf, uint32_t *size, int patch) {
|
||||
int skip, src_size = *size;
|
||||
char *src = *buf, *patched = patch ? xcalloc(src_size, 1) : NULL;
|
||||
for (int read = 0, write = 0; read < src_size; ++read, ++write) {
|
||||
if ((skip = check_verity_pattern(src + read)) > 0) {
|
||||
if (!patch)
|
||||
return 1;
|
||||
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
|
||||
read += skip;
|
||||
*size -= skip;
|
||||
}
|
||||
if (patch)
|
||||
patched[write] = src[read];
|
||||
}
|
||||
free(*buf);
|
||||
*buf = patched;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void patch_encryption(void **buf, uint32_t *size) {
|
||||
int skip, src_size = *size;
|
||||
char *src = *buf, *patched = xcalloc(src_size, 1);
|
||||
for (int read = 0, write = 0; read < src_size; ++read, ++write) {
|
||||
if ((skip = check_encryption_pattern(src + read)) > 0) {
|
||||
fprintf(stderr, "Replace pattern [%.*s] with [encryptable]\n", skip, src + read);
|
||||
memcpy(patched + read, "encryptable", 11);
|
||||
read += skip;
|
||||
write += 11;
|
||||
*size -= (skip - 11);
|
||||
}
|
||||
patched[write] = src[read];
|
||||
}
|
||||
free(*buf);
|
||||
*buf = patched;
|
||||
}
|
||||
|
||||
|
@@ -29,9 +29,25 @@ void *vec_pop_back(struct vector *v) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*cmp)(const void *, const void *);
|
||||
|
||||
static int vec_comp(const void *a, const void *b) {
|
||||
void *aa = *((void **)a), *bb = *((void **)b);
|
||||
if (aa == NULL && bb == NULL) return 0;
|
||||
else if (aa == NULL) return 1;
|
||||
else if (bb == NULL) return -1;
|
||||
else return cmp ? cmp(aa, bb) : 0;
|
||||
}
|
||||
|
||||
void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) {
|
||||
if (v == NULL) return;
|
||||
qsort(vec_entry(v), vec_size(v), sizeof(void*), compar);
|
||||
cmp = compar;
|
||||
qsort(vec_entry(v), vec_size(v), sizeof(void*), vec_comp);
|
||||
void *e;
|
||||
vec_for_each_r(v, e) {
|
||||
if (e) break;
|
||||
--vec_size(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Will cleanup only the vector itself
|
1
core/src/main/AndroidManifest.xml
Normal file
1
core/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="com.topjohnwu.core.magisk" />
|
1
crypto/.gitignore
vendored
Normal file
1
crypto/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
38
crypto/build.gradle
Normal file
38
crypto/build.gradle
Normal file
@@ -0,0 +1,38 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'java'
|
||||
|
||||
sourceCompatibility = "1.8"
|
||||
targetCompatibility = "1.8"
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'com.topjohnwu.crypto.ZipSigner'
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = 'zipsigner'
|
||||
classifier = null
|
||||
version = 1.1
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.58'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.58'
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ByteArrayStream extends ByteArrayOutputStream {
|
||||
public byte[] getBuf() {
|
||||
return buf;
|
||||
}
|
||||
public synchronized void readFrom(InputStream is) {
|
||||
readFrom(is, Integer.MAX_VALUE);
|
||||
}
|
||||
public synchronized void readFrom(InputStream is, int len) {
|
||||
int read;
|
||||
byte buffer[] = new byte[4096];
|
||||
try {
|
||||
while ((read = is.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
|
||||
write(buffer, 0, read);
|
||||
len -= read;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public synchronized void writeTo(OutputStream out, int off, int len) throws IOException {
|
||||
out.write(buf, off, len);
|
||||
}
|
||||
public ByteArrayInputStream getInputStream() {
|
||||
return new ByteArrayInputStream(buf, 0, count);
|
||||
}
|
||||
}
|
136
crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java
Normal file
136
crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class CryptoUtils {
|
||||
|
||||
private static final Map<String, String> ID_TO_ALG;
|
||||
private static final Map<String, String> ALG_TO_ID;
|
||||
|
||||
static {
|
||||
ID_TO_ALG = new HashMap<>();
|
||||
ALG_TO_ID = new HashMap<>();
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA");
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA");
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA");
|
||||
ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId());
|
||||
ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId());
|
||||
ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId());
|
||||
ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
|
||||
ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId());
|
||||
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
|
||||
}
|
||||
|
||||
private static String getSignatureAlgorithm(Key key) throws Exception {
|
||||
if ("EC".equals(key.getAlgorithm())) {
|
||||
int curveSize;
|
||||
KeyFactory factory = KeyFactory.getInstance("EC");
|
||||
if (key instanceof PublicKey) {
|
||||
ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class);
|
||||
curveSize = spec.getParams().getCurve().getField().getFieldSize();
|
||||
} else if (key instanceof PrivateKey) {
|
||||
ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class);
|
||||
curveSize = spec.getParams().getCurve().getField().getFieldSize();
|
||||
} else {
|
||||
throw new InvalidKeySpecException();
|
||||
}
|
||||
if (curveSize <= 256) {
|
||||
return "SHA256withECDSA";
|
||||
} else if (curveSize <= 384) {
|
||||
return "SHA384withECDSA";
|
||||
} else {
|
||||
return "SHA512withECDSA";
|
||||
}
|
||||
} else if ("RSA".equals(key.getAlgorithm())) {
|
||||
return "SHA256withRSA";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception {
|
||||
String id = ALG_TO_ID.get(getSignatureAlgorithm(key));
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
|
||||
}
|
||||
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
|
||||
}
|
||||
|
||||
static boolean verify(PublicKey key, byte[] input, byte[] signature,
|
||||
AlgorithmIdentifier algId) throws Exception {
|
||||
String algName = ID_TO_ALG.get(algId.getAlgorithm().getId());
|
||||
if (algName == null) {
|
||||
throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm());
|
||||
}
|
||||
Signature verifier = Signature.getInstance(algName);
|
||||
verifier.initVerify(key);
|
||||
verifier.update(input);
|
||||
return verifier.verify(signature);
|
||||
}
|
||||
|
||||
static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
|
||||
Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey));
|
||||
signer.initSign(privateKey);
|
||||
signer.update(input);
|
||||
return signer.sign();
|
||||
}
|
||||
|
||||
static X509Certificate readPublicKey(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) cf.generateCertificate(input);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a PKCS#8 format private key. */
|
||||
static PrivateKey readPrivateKey(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
int size = input.read(buffer);
|
||||
byte[] bytes = Arrays.copyOf(buffer, size);
|
||||
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
|
||||
/*
|
||||
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
|
||||
* OID and use that to construct a KeyFactory.
|
||||
*/
|
||||
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
|
||||
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
|
||||
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
|
||||
return KeyFactory.getInstance(algOid).generatePrivate(spec);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
}
|
122
crypto/src/main/java/com/topjohnwu/crypto/JarMap.java
Normal file
122
crypto/src/main/java/com/topjohnwu/crypto/JarMap.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/*
|
||||
* A universal random access interface for both JarFile and JarInputStream
|
||||
*
|
||||
* In the case when JarInputStream is provided to constructor, the whole stream
|
||||
* will be loaded into memory for random access purposes.
|
||||
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
|
||||
* */
|
||||
|
||||
public class JarMap implements Closeable, AutoCloseable {
|
||||
private JarFile jarFile;
|
||||
private JarInputStream jis;
|
||||
private boolean isInputStream = false;
|
||||
private LinkedHashMap<String, JarEntry> bufMap;
|
||||
|
||||
public JarMap(File file) throws IOException {
|
||||
this(file, true);
|
||||
}
|
||||
|
||||
public JarMap(File file, boolean verify) throws IOException {
|
||||
this(file, verify, ZipFile.OPEN_READ);
|
||||
}
|
||||
|
||||
public JarMap(File file, boolean verify, int mode) throws IOException {
|
||||
jarFile = new JarFile(file, verify, mode);
|
||||
}
|
||||
|
||||
public JarMap(String name) throws IOException {
|
||||
this(new File(name));
|
||||
}
|
||||
|
||||
public JarMap(String name, boolean verify) throws IOException {
|
||||
this(new File(name), verify);
|
||||
}
|
||||
|
||||
public JarMap(InputStream is) throws IOException {
|
||||
this(is, true);
|
||||
}
|
||||
|
||||
public JarMap(InputStream is, boolean verify) throws IOException {
|
||||
isInputStream = true;
|
||||
bufMap = new LinkedHashMap<>();
|
||||
jis = new JarInputStream(is, verify);
|
||||
JarEntry entry;
|
||||
while ((entry = jis.getNextJarEntry()) != null) {
|
||||
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return isInputStream ? null : new File(jarFile.getName());
|
||||
}
|
||||
|
||||
public Manifest getManifest() throws IOException {
|
||||
return isInputStream ? jis.getManifest() : jarFile.getManifest();
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ZipEntry ze) throws IOException {
|
||||
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() :
|
||||
jarFile.getInputStream(ze);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream(ZipEntry ze) {
|
||||
if (!isInputStream) // Only support InputStream mode
|
||||
return null;
|
||||
ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data;
|
||||
bs.reset();
|
||||
return bs;
|
||||
}
|
||||
|
||||
public byte[] getRawData(ZipEntry ze) throws IOException {
|
||||
if (isInputStream) {
|
||||
return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray();
|
||||
} else {
|
||||
ByteArrayStream bytes = new ByteArrayStream();
|
||||
bytes.readFrom(jarFile.getInputStream(ze));
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public Enumeration<JarEntry> entries() {
|
||||
return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries();
|
||||
}
|
||||
|
||||
public ZipEntry getEntry(String name) {
|
||||
return getJarEntry(name);
|
||||
}
|
||||
|
||||
public JarEntry getJarEntry(String name) {
|
||||
return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
(isInputStream ? jis : jarFile).close();
|
||||
}
|
||||
|
||||
private static class JarMapEntry extends JarEntry {
|
||||
ByteArrayStream data;
|
||||
JarMapEntry(JarEntry je, InputStream is) {
|
||||
super(je);
|
||||
data = new ByteArrayStream();
|
||||
data.readFrom(is);
|
||||
}
|
||||
}
|
||||
}
|
502
crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java
Normal file
502
crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java
Normal file
@@ -0,0 +1,502 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DEROutputStream;
|
||||
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
|
||||
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||
import org.bouncycastle.cms.CMSException;
|
||||
import org.bouncycastle.cms.CMSProcessableByteArray;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||
import org.bouncycastle.cms.CMSTypedData;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/*
|
||||
* Modified from from AOSP(Marshmallow) SignAPK.java
|
||||
* */
|
||||
|
||||
public class SignAPK {
|
||||
|
||||
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
|
||||
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
|
||||
|
||||
public static Provider sBouncyCastleProvider;
|
||||
// bitmasks for which hash algorithms we need the manifest to include.
|
||||
private static final int USE_SHA1 = 1;
|
||||
private static final int USE_SHA256 = 2;
|
||||
|
||||
static {
|
||||
sBouncyCastleProvider = new BouncyCastleProvider();
|
||||
Security.insertProviderAt(sBouncyCastleProvider, 1);
|
||||
}
|
||||
|
||||
public static void signZip(InputStream publicIn, InputStream privateIn,
|
||||
JarMap input, File output, boolean minSign) throws Exception {
|
||||
int alignment = 4;
|
||||
BufferedOutputStream outputFile;
|
||||
int hashes = 0;
|
||||
X509Certificate publicKey = CryptoUtils.readPublicKey(publicIn);
|
||||
hashes |= getDigestAlgorithm(publicKey);
|
||||
|
||||
// Set the ZIP file timestamp to the starting valid time
|
||||
// of the 0th certificate plus one hour (to match what
|
||||
// we've historically done).
|
||||
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
|
||||
PrivateKey privateKey = CryptoUtils.readPrivateKey(privateIn);
|
||||
|
||||
outputFile = new BufferedOutputStream(new FileOutputStream(output));
|
||||
if (minSign) {
|
||||
signWholeFile(input.getFile(), publicKey, privateKey, outputFile);
|
||||
} else {
|
||||
JarOutputStream outputJar = new JarOutputStream(outputFile);
|
||||
// For signing .apks, use the maximum compression to make
|
||||
// them as small as possible (since they live forever on
|
||||
// the system partition). For OTA packages, use the
|
||||
// default compression level, which is much much faster
|
||||
// and produces output that is only a tiny bit larger
|
||||
// (~0.1% on full OTA packages I tested).
|
||||
outputJar.setLevel(9);
|
||||
Manifest manifest = addDigestsToManifest(input, hashes);
|
||||
copyFiles(manifest, input, outputJar, timestamp, alignment);
|
||||
signFile(manifest, input, publicKey, privateKey, outputJar);
|
||||
outputJar.close();
|
||||
}
|
||||
input.close();
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return one of USE_SHA1 or USE_SHA256 according to the signature
|
||||
* algorithm specified in the cert.
|
||||
*/
|
||||
private static int getDigestAlgorithm(X509Certificate cert) {
|
||||
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
|
||||
if ("SHA1WITHRSA".equals(sigAlg) ||
|
||||
"MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
|
||||
return USE_SHA1;
|
||||
} else if (sigAlg.startsWith("SHA256WITH")) {
|
||||
return USE_SHA256;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
|
||||
"\" in cert [" + cert.getSubjectDN());
|
||||
}
|
||||
}
|
||||
/** Returns the expected signature algorithm for this key type. */
|
||||
private static String getSignatureAlgorithm(X509Certificate cert) {
|
||||
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
|
||||
String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
|
||||
if ("RSA".equalsIgnoreCase(keyType)) {
|
||||
if (getDigestAlgorithm(cert) == USE_SHA256) {
|
||||
return "SHA256withRSA";
|
||||
} else {
|
||||
return "SHA1withRSA";
|
||||
}
|
||||
} else if ("EC".equalsIgnoreCase(keyType)) {
|
||||
return "SHA256withECDSA";
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported key type: " + keyType);
|
||||
}
|
||||
}
|
||||
// Files matching this pattern are not copied to the output.
|
||||
private static Pattern stripPattern =
|
||||
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
|
||||
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
|
||||
|
||||
/**
|
||||
* Add the hash(es) of every file to the manifest, creating it if
|
||||
* necessary.
|
||||
*/
|
||||
private static Manifest addDigestsToManifest(JarMap jar, int hashes)
|
||||
throws IOException, GeneralSecurityException {
|
||||
Manifest input = jar.getManifest();
|
||||
Manifest output = new Manifest();
|
||||
Attributes main = output.getMainAttributes();
|
||||
if (input != null) {
|
||||
main.putAll(input.getMainAttributes());
|
||||
} else {
|
||||
main.putValue("Manifest-Version", "1.0");
|
||||
main.putValue("Created-By", "1.0 (Android SignApk)");
|
||||
}
|
||||
MessageDigest md_sha1 = null;
|
||||
MessageDigest md_sha256 = null;
|
||||
if ((hashes & USE_SHA1) != 0) {
|
||||
md_sha1 = MessageDigest.getInstance("SHA1");
|
||||
}
|
||||
if ((hashes & USE_SHA256) != 0) {
|
||||
md_sha256 = MessageDigest.getInstance("SHA256");
|
||||
}
|
||||
byte[] buffer = new byte[4096];
|
||||
int num;
|
||||
// We sort the input entries by name, and add them to the
|
||||
// output manifest in sorted order. We expect that the output
|
||||
// map will be deterministic.
|
||||
TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();
|
||||
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
|
||||
JarEntry entry = e.nextElement();
|
||||
byName.put(entry.getName(), entry);
|
||||
}
|
||||
for (JarEntry entry: byName.values()) {
|
||||
String name = entry.getName();
|
||||
if (!entry.isDirectory() &&
|
||||
(stripPattern == null || !stripPattern.matcher(name).matches())) {
|
||||
InputStream data = jar.getInputStream(entry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
if (md_sha1 != null) md_sha1.update(buffer, 0, num);
|
||||
if (md_sha256 != null) md_sha256.update(buffer, 0, num);
|
||||
}
|
||||
Attributes attr = null;
|
||||
if (input != null) attr = input.getAttributes(name);
|
||||
attr = attr != null ? new Attributes(attr) : new Attributes();
|
||||
if (md_sha1 != null) {
|
||||
attr.putValue("SHA1-Digest",
|
||||
new String(Base64.encode(md_sha1.digest()), "ASCII"));
|
||||
}
|
||||
if (md_sha256 != null) {
|
||||
attr.putValue("SHA-256-Digest",
|
||||
new String(Base64.encode(md_sha256.digest()), "ASCII"));
|
||||
}
|
||||
output.getEntries().put(name, attr);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/** Write to another stream and track how many bytes have been
|
||||
* written.
|
||||
*/
|
||||
private static class CountOutputStream extends FilterOutputStream {
|
||||
private int mCount;
|
||||
public CountOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
mCount = 0;
|
||||
}
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
super.write(b);
|
||||
mCount++;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
super.write(b, off, len);
|
||||
mCount += len;
|
||||
}
|
||||
public int size() {
|
||||
return mCount;
|
||||
}
|
||||
}
|
||||
/** Write a .SF file with a digest of the specified manifest. */
|
||||
private static void writeSignatureFile(Manifest manifest, OutputStream out,
|
||||
int hash)
|
||||
throws IOException, GeneralSecurityException {
|
||||
Manifest sf = new Manifest();
|
||||
Attributes main = sf.getMainAttributes();
|
||||
main.putValue("Signature-Version", "1.0");
|
||||
main.putValue("Created-By", "1.0 (Android SignApk)");
|
||||
MessageDigest md = MessageDigest.getInstance(
|
||||
hash == USE_SHA256 ? "SHA256" : "SHA1");
|
||||
PrintStream print = new PrintStream(
|
||||
new DigestOutputStream(new ByteArrayOutputStream(), md),
|
||||
true, "UTF-8");
|
||||
// Digest of the entire manifest
|
||||
manifest.write(print);
|
||||
print.flush();
|
||||
main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
|
||||
new String(Base64.encode(md.digest()), "ASCII"));
|
||||
Map<String, Attributes> entries = manifest.getEntries();
|
||||
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
|
||||
// Digest of the manifest stanza for this entry.
|
||||
print.print("Name: " + entry.getKey() + "\r\n");
|
||||
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
|
||||
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
|
||||
}
|
||||
print.print("\r\n");
|
||||
print.flush();
|
||||
Attributes sfAttr = new Attributes();
|
||||
sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest",
|
||||
new String(Base64.encode(md.digest()), "ASCII"));
|
||||
sf.getEntries().put(entry.getKey(), sfAttr);
|
||||
}
|
||||
CountOutputStream cout = new CountOutputStream(out);
|
||||
sf.write(cout);
|
||||
// A bug in the java.util.jar implementation of Android platforms
|
||||
// up to version 1.6 will cause a spurious IOException to be thrown
|
||||
// if the length of the signature file is a multiple of 1024 bytes.
|
||||
// As a workaround, add an extra CRLF in this case.
|
||||
if ((cout.size() % 1024) == 0) {
|
||||
cout.write('\r');
|
||||
cout.write('\n');
|
||||
}
|
||||
}
|
||||
/** Sign data and write the digital signature to 'out'. */
|
||||
private static void writeSignatureBlock(
|
||||
CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
|
||||
OutputStream out)
|
||||
throws IOException,
|
||||
CertificateEncodingException,
|
||||
OperatorCreationException,
|
||||
CMSException {
|
||||
ArrayList<X509Certificate> certList = new ArrayList<>(1);
|
||||
certList.add(publicKey);
|
||||
JcaCertStore certs = new JcaCertStore(certList);
|
||||
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
|
||||
ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
|
||||
.setProvider(sBouncyCastleProvider)
|
||||
.build(privateKey);
|
||||
gen.addSignerInfoGenerator(
|
||||
new JcaSignerInfoGeneratorBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder()
|
||||
.setProvider(sBouncyCastleProvider)
|
||||
.build())
|
||||
.setDirectSignature(true)
|
||||
.build(signer, publicKey));
|
||||
gen.addCertificates(certs);
|
||||
CMSSignedData sigData = gen.generate(data, false);
|
||||
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
|
||||
DEROutputStream dos = new DEROutputStream(out);
|
||||
dos.writeObject(asn1.readObject());
|
||||
}
|
||||
/**
|
||||
* Copy all the files in a manifest from input to output. We set
|
||||
* the modification times in the output to a fixed time, so as to
|
||||
* reduce variation in the output file and make incremental OTAs
|
||||
* more efficient.
|
||||
*/
|
||||
private static void copyFiles(Manifest manifest, JarMap in, JarOutputStream out,
|
||||
long timestamp, int alignment) throws IOException {
|
||||
byte[] buffer = new byte[4096];
|
||||
int num;
|
||||
Map<String, Attributes> entries = manifest.getEntries();
|
||||
ArrayList<String> names = new ArrayList<>(entries.keySet());
|
||||
Collections.sort(names);
|
||||
boolean firstEntry = true;
|
||||
long offset = 0L;
|
||||
// We do the copy in two passes -- first copying all the
|
||||
// entries that are STORED, then copying all the entries that
|
||||
// have any other compression flag (which in practice means
|
||||
// DEFLATED). This groups all the stored entries together at
|
||||
// the start of the file and makes it easier to do alignment
|
||||
// on them (since only stored entries are aligned).
|
||||
for (String name : names) {
|
||||
JarEntry inEntry = in.getJarEntry(name);
|
||||
JarEntry outEntry = null;
|
||||
if (inEntry.getMethod() != JarEntry.STORED) continue;
|
||||
// Preserve the STORED method of the input entry.
|
||||
outEntry = new JarEntry(inEntry);
|
||||
outEntry.setTime(timestamp);
|
||||
// 'offset' is the offset into the file at which we expect
|
||||
// the file data to begin. This is the value we need to
|
||||
// make a multiple of 'alignement'.
|
||||
offset += JarFile.LOCHDR + outEntry.getName().length();
|
||||
if (firstEntry) {
|
||||
// The first entry in a jar file has an extra field of
|
||||
// four bytes that you can't get rid of; any extra
|
||||
// data you specify in the JarEntry is appended to
|
||||
// these forced four bytes. This is JAR_MAGIC in
|
||||
// JarOutputStream; the bytes are 0xfeca0000.
|
||||
offset += 4;
|
||||
firstEntry = false;
|
||||
}
|
||||
if (alignment > 0 && (offset % alignment != 0)) {
|
||||
// Set the "extra data" of the entry to between 1 and
|
||||
// alignment-1 bytes, to make the file data begin at
|
||||
// an aligned offset.
|
||||
int needed = alignment - (int)(offset % alignment);
|
||||
outEntry.setExtra(new byte[needed]);
|
||||
offset += needed;
|
||||
}
|
||||
out.putNextEntry(outEntry);
|
||||
InputStream data = in.getInputStream(inEntry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, num);
|
||||
offset += num;
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
// Copy all the non-STORED entries. We don't attempt to
|
||||
// maintain the 'offset' variable past this point; we don't do
|
||||
// alignment on these entries.
|
||||
for (String name : names) {
|
||||
JarEntry inEntry = in.getJarEntry(name);
|
||||
JarEntry outEntry = null;
|
||||
if (inEntry.getMethod() == JarEntry.STORED) continue;
|
||||
// Create a new entry so that the compressed len is recomputed.
|
||||
outEntry = new JarEntry(name);
|
||||
outEntry.setTime(timestamp);
|
||||
out.putNextEntry(outEntry);
|
||||
InputStream data = in.getInputStream(inEntry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, num);
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
// This class is to provide a file's content, but trimming out the last two bytes
|
||||
// Used for signWholeFile
|
||||
private static class CMSProcessableFile implements CMSTypedData {
|
||||
|
||||
private ASN1ObjectIdentifier type;
|
||||
private RandomAccessFile file;
|
||||
|
||||
CMSProcessableFile(File file) throws FileNotFoundException {
|
||||
this.file = new RandomAccessFile(file, "r");
|
||||
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASN1ObjectIdentifier getContentType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream out) throws IOException, CMSException {
|
||||
file.seek(0);
|
||||
int read;
|
||||
byte buffer[] = new byte[4096];
|
||||
int len = (int) file.length() - 2;
|
||||
while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
|
||||
out.write(buffer, 0, read);
|
||||
len -= read;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getContent() {
|
||||
return file;
|
||||
}
|
||||
|
||||
byte[] getTail() throws IOException {
|
||||
byte tail[] = new byte[22];
|
||||
file.seek(file.length() - 22);
|
||||
file.readFully(tail);
|
||||
return tail;
|
||||
}
|
||||
}
|
||||
|
||||
private static void signWholeFile(File input, X509Certificate publicKey,
|
||||
PrivateKey privateKey, OutputStream outputStream)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream temp = new ByteArrayOutputStream();
|
||||
// put a readable message and a null char at the start of the
|
||||
// archive comment, so that tools that display the comment
|
||||
// (hopefully) show something sensible.
|
||||
// TODO: anything more useful we can put in this message?
|
||||
byte[] message = "signed by SignApk".getBytes("UTF-8");
|
||||
temp.write(message);
|
||||
temp.write(0);
|
||||
|
||||
CMSProcessableFile cmsFile = new CMSProcessableFile(input);
|
||||
writeSignatureBlock(cmsFile, publicKey, privateKey, temp);
|
||||
|
||||
// For a zip with no archive comment, the
|
||||
// end-of-central-directory record will be 22 bytes long, so
|
||||
// we expect to find the EOCD marker 22 bytes from the end.
|
||||
byte[] zipData = cmsFile.getTail();
|
||||
if (zipData[zipData.length-22] != 0x50 ||
|
||||
zipData[zipData.length-21] != 0x4b ||
|
||||
zipData[zipData.length-20] != 0x05 ||
|
||||
zipData[zipData.length-19] != 0x06) {
|
||||
throw new IllegalArgumentException("zip data already has an archive comment");
|
||||
}
|
||||
int total_size = temp.size() + 6;
|
||||
if (total_size > 0xffff) {
|
||||
throw new IllegalArgumentException("signature is too big for ZIP file comment");
|
||||
}
|
||||
// signature starts this many bytes from the end of the file
|
||||
int signature_start = total_size - message.length - 1;
|
||||
temp.write(signature_start & 0xff);
|
||||
temp.write((signature_start >> 8) & 0xff);
|
||||
// Why the 0xff bytes? In a zip file with no archive comment,
|
||||
// bytes [-6:-2] of the file are the little-endian offset from
|
||||
// the start of the file to the central directory. So for the
|
||||
// two high bytes to be 0xff 0xff, the archive would have to
|
||||
// be nearly 4GB in size. So it's unlikely that a real
|
||||
// commentless archive would have 0xffs here, and lets us tell
|
||||
// an old signed archive from a new one.
|
||||
temp.write(0xff);
|
||||
temp.write(0xff);
|
||||
temp.write(total_size & 0xff);
|
||||
temp.write((total_size >> 8) & 0xff);
|
||||
temp.flush();
|
||||
// Signature verification checks that the EOCD header is the
|
||||
// last such sequence in the file (to avoid minzip finding a
|
||||
// fake EOCD appended after the signature in its scan). The
|
||||
// odds of producing this sequence by chance are very low, but
|
||||
// let's catch it here if it does.
|
||||
byte[] b = temp.toByteArray();
|
||||
for (int i = 0; i < b.length-3; ++i) {
|
||||
if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
|
||||
throw new IllegalArgumentException("found spurious EOCD header at " + i);
|
||||
}
|
||||
}
|
||||
cmsFile.write(outputStream);
|
||||
outputStream.write(total_size & 0xff);
|
||||
outputStream.write((total_size >> 8) & 0xff);
|
||||
temp.writeTo(outputStream);
|
||||
}
|
||||
private static void signFile(Manifest manifest, JarMap inputJar,
|
||||
X509Certificate publicKey, PrivateKey privateKey,
|
||||
JarOutputStream outputJar)
|
||||
throws Exception {
|
||||
// Assume the certificate is valid for at least an hour.
|
||||
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
|
||||
// MANIFEST.MF
|
||||
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
manifest.write(outputJar);
|
||||
je = new JarEntry(CERT_SF_NAME);
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey));
|
||||
byte[] signedData = baos.toByteArray();
|
||||
outputJar.write(signedData);
|
||||
// CERT.{EC,RSA} / CERT#.{EC,RSA}
|
||||
final String keyType = publicKey.getPublicKey().getAlgorithm();
|
||||
je = new JarEntry(String.format(CERT_SIG_NAME, keyType));
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
writeSignatureBlock(new CMSProcessableByteArray(signedData),
|
||||
publicKey, privateKey, outputJar);
|
||||
}
|
||||
}
|
231
crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java
Normal file
231
crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java
Normal file
@@ -0,0 +1,231 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1Object;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.ASN1Primitive;
|
||||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.DEROctetString;
|
||||
import org.bouncycastle.asn1.DERPrintableString;
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SignBoot {
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
||||
InputStream keyIn, InputStream certIn) {
|
||||
try {
|
||||
ByteArrayStream bas = new ByteArrayStream();
|
||||
bas.readFrom(imgIn);
|
||||
byte[] image = bas.toByteArray();
|
||||
bas.close();
|
||||
int signableSize = getSignableImageSize(image);
|
||||
if (signableSize < image.length) {
|
||||
System.err.println("NOTE: truncating input from " +
|
||||
image.length + " to " + signableSize + " bytes");
|
||||
image = Arrays.copyOf(image, signableSize);
|
||||
} else if (signableSize > image.length) {
|
||||
throw new IllegalArgumentException("Invalid image: too short, expected " +
|
||||
signableSize + " bytes");
|
||||
}
|
||||
BootSignature bootsig = new BootSignature(target, image.length);
|
||||
X509Certificate cert = CryptoUtils.readPublicKey(certIn);
|
||||
bootsig.setCertificate(cert);
|
||||
PrivateKey key = CryptoUtils.readPrivateKey(keyIn);
|
||||
bootsig.setSignature(bootsig.sign(image, key),
|
||||
CryptoUtils.getSignatureAlgorithmIdentifier(key));
|
||||
byte[] encoded_bootsig = bootsig.getEncoded();
|
||||
imgOut.write(image);
|
||||
imgOut.write(encoded_bootsig);
|
||||
imgOut.flush();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean verifySignature(InputStream imgIn, InputStream certPath) {
|
||||
try {
|
||||
ByteArrayStream bas = new ByteArrayStream();
|
||||
bas.readFrom(imgIn);
|
||||
byte[] image = bas.toByteArray();
|
||||
bas.close();
|
||||
int signableSize = getSignableImageSize(image);
|
||||
if (signableSize >= image.length) {
|
||||
System.err.println("Invalid image: not signed");
|
||||
return false;
|
||||
}
|
||||
byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
|
||||
BootSignature bootsig = new BootSignature(signature);
|
||||
if (certPath != null) {
|
||||
bootsig.setCertificate(CryptoUtils.readPublicKey(certPath));
|
||||
}
|
||||
if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
|
||||
System.err.println("Signature is VALID");
|
||||
return true;
|
||||
} else {
|
||||
System.err.println("Signature is INVALID");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
System.err.println("Invalid image: not signed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getSignableImageSize(byte[] data) throws Exception {
|
||||
if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
|
||||
"ANDROID!".getBytes("US-ASCII"))) {
|
||||
throw new IllegalArgumentException("Invalid image header: missing magic");
|
||||
}
|
||||
ByteBuffer image = ByteBuffer.wrap(data);
|
||||
image.order(ByteOrder.LITTLE_ENDIAN);
|
||||
image.getLong(); // magic
|
||||
int kernelSize = image.getInt();
|
||||
image.getInt(); // kernel_addr
|
||||
int ramdskSize = image.getInt();
|
||||
image.getInt(); // ramdisk_addr
|
||||
int secondSize = image.getInt();
|
||||
image.getLong(); // second_addr + tags_addr
|
||||
int pageSize = image.getInt();
|
||||
int length = pageSize // include the page aligned image header
|
||||
+ ((kernelSize + pageSize - 1) / pageSize) * pageSize
|
||||
+ ((ramdskSize + pageSize - 1) / pageSize) * pageSize
|
||||
+ ((secondSize + pageSize - 1) / pageSize) * pageSize;
|
||||
length = ((length + pageSize - 1) / pageSize) * pageSize;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Invalid image header: invalid length");
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static class BootSignature extends ASN1Object {
|
||||
private ASN1Integer formatVersion;
|
||||
private ASN1Encodable certificate;
|
||||
private AlgorithmIdentifier algorithmIdentifier;
|
||||
private DERPrintableString target;
|
||||
private ASN1Integer length;
|
||||
private DEROctetString signature;
|
||||
private PublicKey publicKey;
|
||||
private static final int FORMAT_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Initializes the object for signing an image file
|
||||
* @param target Target name, included in the signed data
|
||||
* @param length Length of the image, included in the signed data
|
||||
*/
|
||||
public BootSignature(String target, int length) {
|
||||
this.formatVersion = new ASN1Integer(FORMAT_VERSION);
|
||||
this.target = new DERPrintableString(target);
|
||||
this.length = new ASN1Integer(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object for verifying a signed image file
|
||||
* @param signature Signature footer
|
||||
*/
|
||||
public BootSignature(byte[] signature)
|
||||
throws Exception {
|
||||
ASN1InputStream stream = new ASN1InputStream(signature);
|
||||
ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
|
||||
formatVersion = (ASN1Integer) sequence.getObjectAt(0);
|
||||
if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
|
||||
throw new IllegalArgumentException("Unsupported format version");
|
||||
}
|
||||
certificate = sequence.getObjectAt(1);
|
||||
byte[] encoded = ((ASN1Object) certificate).getEncoded();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
|
||||
publicKey = c.getPublicKey();
|
||||
ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
|
||||
algorithmIdentifier = new AlgorithmIdentifier(
|
||||
(ASN1ObjectIdentifier) algId.getObjectAt(0));
|
||||
ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
|
||||
target = (DERPrintableString) attrs.getObjectAt(0);
|
||||
length = (ASN1Integer) attrs.getObjectAt(1);
|
||||
this.signature = (DEROctetString) sequence.getObjectAt(4);
|
||||
}
|
||||
|
||||
public ASN1Object getAuthenticatedAttributes() {
|
||||
ASN1EncodableVector attrs = new ASN1EncodableVector();
|
||||
attrs.add(target);
|
||||
attrs.add(length);
|
||||
return new DERSequence(attrs);
|
||||
}
|
||||
|
||||
public byte[] getEncodedAuthenticatedAttributes() throws IOException {
|
||||
return getAuthenticatedAttributes().getEncoded();
|
||||
}
|
||||
|
||||
public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
|
||||
algorithmIdentifier = algId;
|
||||
signature = new DEROctetString(sig);
|
||||
}
|
||||
|
||||
public void setCertificate(X509Certificate cert)
|
||||
throws Exception, IOException, CertificateEncodingException {
|
||||
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
|
||||
certificate = s.readObject();
|
||||
publicKey = cert.getPublicKey();
|
||||
}
|
||||
|
||||
public byte[] generateSignableImage(byte[] image) throws IOException {
|
||||
byte[] attrs = getEncodedAuthenticatedAttributes();
|
||||
byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
|
||||
for (int i=0; i < attrs.length; i++) {
|
||||
signable[i+image.length] = attrs[i];
|
||||
}
|
||||
return signable;
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] image, PrivateKey key) throws Exception {
|
||||
byte[] signable = generateSignableImage(image);
|
||||
return CryptoUtils.sign(key, signable);
|
||||
}
|
||||
|
||||
public boolean verify(byte[] image) throws Exception {
|
||||
if (length.getValue().intValue() != image.length) {
|
||||
throw new IllegalArgumentException("Invalid image length");
|
||||
}
|
||||
byte[] signable = generateSignableImage(image);
|
||||
return CryptoUtils.verify(publicKey, signable, signature.getOctets(),
|
||||
algorithmIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASN1Primitive toASN1Primitive() {
|
||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||
v.add(formatVersion);
|
||||
v.add(certificate);
|
||||
v.add(algorithmIdentifier);
|
||||
v.add(getAuthenticatedAttributes());
|
||||
v.add(signature);
|
||||
return new DERSequence(v);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
42
crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java
Normal file
42
crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.Security;
|
||||
|
||||
public class ZipSigner {
|
||||
public static void main(String[] args) {
|
||||
boolean minSign = false;
|
||||
int argStart = 0;
|
||||
|
||||
if (args.length < 4) {
|
||||
System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (args[0].equals("-m")) {
|
||||
minSign = true;
|
||||
argStart = 1;
|
||||
}
|
||||
|
||||
SignAPK.sBouncyCastleProvider = new BouncyCastleProvider();
|
||||
Security.insertProviderAt(SignAPK.sBouncyCastleProvider, 1);
|
||||
|
||||
File pubKey = new File(args[argStart]);
|
||||
File privKey = new File(args[argStart + 1]);
|
||||
File input = new File(args[argStart + 2]);
|
||||
File output = new File(args[argStart + 3]);
|
||||
|
||||
try (InputStream pub = new FileInputStream(pubKey);
|
||||
InputStream priv = new FileInputStream(privKey);
|
||||
JarMap jar = new JarMap(input, false)) {
|
||||
SignAPK.signZip(pub, priv, jar, output, minSign);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
22
gradle.properties
Normal file
22
gradle.properties
Normal file
@@ -0,0 +1,22 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
org.gradle.jvmargs=-Xmx2560m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
|
||||
# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property.
|
||||
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
|
||||
org.gradle.daemon=true
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Mon Dec 04 11:24:34 CST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
160
gradlew
vendored
Executable file
160
gradlew
vendored
Executable file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
java
1
java
Submodule java deleted from f5ceee547c
@@ -1,334 +0,0 @@
|
||||
/* daemon.c - Magisk Daemon
|
||||
*
|
||||
* Start the daemon and wait for requests
|
||||
* Connect the daemon and send requests through sockets
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "magiskpolicy.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
pthread_t sepol_patch;
|
||||
int is_restart = 0;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
client_request req = read_int(client);
|
||||
|
||||
struct ucred credentials;
|
||||
get_client_cred(client, &credentials);
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
case STOP_MAGISKHIDE:
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case LS_HIDELIST:
|
||||
case POST_FS:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
if (credentials.uid != 0) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
close(client);
|
||||
return NULL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
launch_magiskhide(client);
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
stop_magiskhide(client);
|
||||
break;
|
||||
case ADD_HIDELIST:
|
||||
add_hide_list(client);
|
||||
break;
|
||||
case RM_HIDELIST:
|
||||
rm_hide_list(client);
|
||||
break;
|
||||
case LS_HIDELIST:
|
||||
ls_hide_list(client);
|
||||
break;
|
||||
case SUPERUSER:
|
||||
su_daemon_receiver(client);
|
||||
break;
|
||||
case CHECK_VERSION:
|
||||
write_string(client, MAGISK_VER_STR);
|
||||
close(client);
|
||||
break;
|
||||
case CHECK_VERSION_CODE:
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
close(client);
|
||||
break;
|
||||
case POST_FS:
|
||||
post_fs(client);
|
||||
break;
|
||||
case POST_FS_DATA:
|
||||
post_fs_data(client);
|
||||
break;
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Setup the address and return socket fd */
|
||||
static int setup_socket(struct sockaddr_un *sun) {
|
||||
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
memset(sun, 0, sizeof(*sun));
|
||||
sun->sun_family = AF_LOCAL;
|
||||
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, sizeof(REQUESTOR_DAEMON_PATH) - 1);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void auto_start_magiskhide() {
|
||||
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
}
|
||||
|
||||
void start_daemon() {
|
||||
setsid();
|
||||
setcon("u:r:su:s0");
|
||||
umask(0);
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
// Block user signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
|
||||
xlisten(fd, 10);
|
||||
|
||||
if ((is_restart = access(MAGISKTMP, F_OK) == 0)) {
|
||||
// Restart stuffs if the daemon is restarted
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
auto_start_magiskhide();
|
||||
start_debug_log();
|
||||
}
|
||||
|
||||
// Start the log monitor
|
||||
monitor_logs();
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Notifiy init the daemon is started
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
int *client = xmalloc(sizeof(int));
|
||||
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, request_handler, client);
|
||||
// Detach the thread, we will never join it
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon() {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (xconnect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
// If we cannot access the daemon, we start a daemon in the child process if possible
|
||||
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "No daemon is currently running!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (xfork() == 0) {
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
close(fd);
|
||||
start_daemon();
|
||||
}
|
||||
|
||||
do {
|
||||
// Wait for 10ms
|
||||
usleep(10);
|
||||
} while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a file descriptor from a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* Returns the file descriptor on success, or -1 if a file
|
||||
* descriptor was not actually included in the message
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*/
|
||||
int recv_fd(int sockfd) {
|
||||
// Need to receive data from the message, otherwise don't care about it.
|
||||
char iovbuf;
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = &iovbuf,
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsgbuf,
|
||||
.msg_controllen = sizeof(cmsgbuf),
|
||||
};
|
||||
|
||||
xrecvmsg(sockfd, &msg, MSG_WAITALL);
|
||||
|
||||
// Was a control message actually sent?
|
||||
switch (msg.msg_controllen) {
|
||||
case 0:
|
||||
// No, so the file descriptor was closed and won't be used.
|
||||
return -1;
|
||||
case sizeof(cmsgbuf):
|
||||
// Yes, grab the file descriptor from it.
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (cmsg == NULL ||
|
||||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
error:
|
||||
LOGE("unable to read fd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return *(int *)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a file descriptor through a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*
|
||||
* fd may be -1, in which case the dummy data is sent,
|
||||
* but no control message with the FD is sent.
|
||||
*/
|
||||
void send_fd(int sockfd, int fd) {
|
||||
// Need to send some data in the message, this will do.
|
||||
struct iovec iov = {
|
||||
.iov_base = "",
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
if (fd != -1) {
|
||||
// Is the file descriptor actually open?
|
||||
if (fcntl(fd, F_GETFD) == -1) {
|
||||
if (errno != EBADF) {
|
||||
PLOGE("unable to send fd");
|
||||
}
|
||||
// It's closed, don't send a control message or sendmsg will EBADF.
|
||||
} else {
|
||||
// It's open, send the file descriptor in a control message.
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
*(int *)CMSG_DATA(cmsg) = fd;
|
||||
}
|
||||
}
|
||||
|
||||
xsendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
int read_int(int fd) {
|
||||
int val;
|
||||
xxread(fd, &val, sizeof(int));
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_int(int fd, int val) {
|
||||
if (fd < 0) return;
|
||||
xwrite(fd, &val, sizeof(int));
|
||||
}
|
||||
|
||||
char* read_string(int fd) {
|
||||
int len = read_int(fd);
|
||||
if (len > PATH_MAX || len < 0) {
|
||||
LOGE("invalid string length %d", len);
|
||||
exit(1);
|
||||
}
|
||||
char* val = xmalloc(sizeof(char) * (len + 1));
|
||||
xxread(fd, val, len);
|
||||
val[len] = '\0';
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_string(int fd, const char* val) {
|
||||
if (fd < 0) return;
|
||||
int len = strlen(val);
|
||||
write_int(fd, len);
|
||||
xwrite(fd, val, len);
|
||||
}
|
1
jni/external/selinux
vendored
1
jni/external/selinux
vendored
Submodule jni/external/selinux deleted from 2fefdfc40f
@@ -1,36 +0,0 @@
|
||||
/* magiskpolicy.h - Public API for policy patching
|
||||
*/
|
||||
|
||||
#ifndef _MAGISKPOLICY_H
|
||||
#define _MAGISKPOLICY_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ALL NULL
|
||||
|
||||
// policydb functions
|
||||
int load_policydb(const char *filename);
|
||||
int dump_policydb(const char *filename);
|
||||
void destroy_policydb();
|
||||
|
||||
// Handy functions
|
||||
int sepol_allow(char *s, char *t, char *c, char *p);
|
||||
int sepol_deny(char *s, char *t, char *c, char *p);
|
||||
int sepol_auditallow(char *s, char *t, char *c, char *p);
|
||||
int sepol_auditdeny(char *s, char *t, char *c, char *p);
|
||||
int sepol_typetrans(char *s, char *t, char *c, char *d, char *o);
|
||||
int sepol_allowxperm(char *s, char *t, char *c, char *range);
|
||||
int sepol_auditallowxperm(char *s, char *t, char *c, char *range);
|
||||
int sepol_dontauditxperm(char *s, char *t, char *c, char *range);
|
||||
int sepol_create(char *s);
|
||||
int sepol_permissive(char *s);
|
||||
int sepol_enforce(char *s);
|
||||
int sepol_attradd(char *s, char *a);
|
||||
int sepol_exists(char *source);
|
||||
|
||||
// Built in rules
|
||||
void sepol_min_rules();
|
||||
void sepol_med_rules();
|
||||
void sepol_full_rules();
|
||||
|
||||
#endif
|
@@ -1,546 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "cpio.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
static uint32_t x8u(char *hex) {
|
||||
uint32_t val, inpos = 8, outpos;
|
||||
char pattern[6];
|
||||
|
||||
while (*hex == '0') {
|
||||
hex++;
|
||||
if (!--inpos) return 0;
|
||||
}
|
||||
// Because scanf gratuitously treats %*X differently than printf does.
|
||||
sprintf(pattern, "%%%dx%%n", inpos);
|
||||
sscanf(hex, pattern, &val, &outpos);
|
||||
if (inpos != outpos) LOGE("bad cpio header\n");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void cpio_free(cpio_entry *f) {
|
||||
if (f) {
|
||||
free(f->filename);
|
||||
free(f->data);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_vec_insert(struct vector *v, cpio_entry *n) {
|
||||
cpio_entry *f;
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, n->filename) == 0) {
|
||||
// Replace, then all is done
|
||||
cpio_free(f);
|
||||
vec_cur(v) = n;
|
||||
return;
|
||||
}
|
||||
}
|
||||
vec_push_back(v, n);
|
||||
}
|
||||
|
||||
static int cpio_cmp(const void *a, const void *b) {
|
||||
return strcmp((*(cpio_entry **) a)->filename, (*(cpio_entry **) b)->filename);
|
||||
}
|
||||
|
||||
// Parse cpio file to a vector of cpio_entry
|
||||
static void parse_cpio(const char *filename, struct vector *v) {
|
||||
fprintf(stderr, "Loading cpio: [%s]\n\n", filename);
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_newc_header header;
|
||||
cpio_entry *f;
|
||||
while(xxread(fd, &header, 110) != -1) {
|
||||
f = xcalloc(sizeof(*f), 1);
|
||||
// f->ino = x8u(header.ino);
|
||||
f->mode = x8u(header.mode);
|
||||
f->uid = x8u(header.uid);
|
||||
f->gid = x8u(header.gid);
|
||||
// f->nlink = x8u(header.nlink);
|
||||
// f->mtime = x8u(header.mtime);
|
||||
f->filesize = x8u(header.filesize);
|
||||
// f->devmajor = x8u(header.devmajor);
|
||||
// f->devminor = x8u(header.devminor);
|
||||
// f->rdevmajor = x8u(header.rdevmajor);
|
||||
// f->rdevminor = x8u(header.rdevminor);
|
||||
f->namesize = x8u(header.namesize);
|
||||
// f->check = x8u(header.check);
|
||||
f->filename = xmalloc(f->namesize);
|
||||
xxread(fd, f->filename, f->namesize);
|
||||
file_align(fd, 4, 0);
|
||||
if (strcmp(f->filename, ".") == 0 || strcmp(f->filename, "..") == 0) {
|
||||
cpio_free(f);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(f->filename, "TRAILER!!!") == 0) {
|
||||
cpio_free(f);
|
||||
break;
|
||||
}
|
||||
if (f->filesize) {
|
||||
f->data = malloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 0);
|
||||
}
|
||||
vec_push_back(v, f);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void dump_cpio(const char *filename, struct vector *v) {
|
||||
fprintf(stderr, "\nDump cpio: [%s]\n\n", filename);
|
||||
int fd = creat(filename, 0644);
|
||||
unsigned inode = 300000;
|
||||
char header[111];
|
||||
// Sort by name
|
||||
vec_sort(v, cpio_cmp);
|
||||
cpio_entry *f;
|
||||
vec_for_each(v, f) {
|
||||
if (f->remove) continue;
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, // f->ino
|
||||
f->mode,
|
||||
f->uid,
|
||||
f->gid,
|
||||
1, // f->nlink
|
||||
0, // f->mtime
|
||||
f->filesize,
|
||||
0, // f->devmajor
|
||||
0, // f->devminor
|
||||
0, // f->rdevmajor
|
||||
0, // f->rdevminor
|
||||
f->namesize,
|
||||
0 // f->check
|
||||
);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, f->filename, f->namesize);
|
||||
file_align(fd, 4, 1);
|
||||
if (f->filesize) {
|
||||
xwrite(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 1);
|
||||
}
|
||||
}
|
||||
// Write trailer
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", inode++, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, "TRAILER!!!\0", 11);
|
||||
file_align(fd, 4, 1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void cpio_vec_destroy(struct vector *v) {
|
||||
// Free each cpio_entry
|
||||
cpio_entry *f;
|
||||
vec_for_each(v, f) {
|
||||
cpio_free(f);
|
||||
}
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
static void cpio_rm(int recursive, const char *entry, struct vector *v) {
|
||||
cpio_entry *f;
|
||||
vec_for_each(v, f) {
|
||||
if (strncmp(f->filename, entry, strlen(entry)) == 0) {
|
||||
char next = f->filename[strlen(entry)];
|
||||
if ((recursive && next == '/') || next == '\0') {
|
||||
if (!f->remove) {
|
||||
fprintf(stderr, "Remove [%s]\n", f->filename);
|
||||
f->remove = 1;
|
||||
}
|
||||
if (!recursive) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_mkdir(mode_t mode, const char *entry, struct vector *v) {
|
||||
cpio_entry *f = xcalloc(sizeof(*f), 1);
|
||||
f->mode = S_IFDIR | mode;
|
||||
f->namesize = strlen(entry) + 1;
|
||||
f->filename = strdup(entry);
|
||||
cpio_vec_insert(v, f);
|
||||
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
|
||||
}
|
||||
|
||||
static void cpio_add(mode_t mode, const char *entry, const char *filename, struct vector *v) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_entry *f = xcalloc(sizeof(*f), 1);
|
||||
f->mode = S_IFREG | mode;
|
||||
f->namesize = strlen(entry) + 1;
|
||||
f->filename = strdup(entry);
|
||||
f->filesize = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
f->data = xmalloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
close(fd);
|
||||
cpio_vec_insert(v, f);
|
||||
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
|
||||
}
|
||||
|
||||
static void cpio_test(struct vector *v) {
|
||||
#define STOCK_BOOT 0x0
|
||||
#define MAGISK_PATCH 0x1
|
||||
#define OTHER_PATCH 0x2
|
||||
int ret = STOCK_BOOT;
|
||||
cpio_entry *f;
|
||||
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "boot/sbin/launch_daemonsu.sh", NULL };
|
||||
const char *MAGISK_LIST[] = { ".backup/.magisk", "init.magisk.rc", "overlay/init.magisk.rc", NULL };
|
||||
vec_for_each(v, f) {
|
||||
for (int i = 0; OTHER_LIST[i]; ++i) {
|
||||
if (strcmp(f->filename, OTHER_LIST[i]) == 0) {
|
||||
ret |= OTHER_PATCH;
|
||||
// Already find other files, abort
|
||||
exit(OTHER_PATCH);
|
||||
}
|
||||
}
|
||||
for (int i = 0; MAGISK_LIST[i]; ++i) {
|
||||
if (strcmp(f->filename, MAGISK_LIST[i]) == 0)
|
||||
ret = MAGISK_PATCH;
|
||||
}
|
||||
}
|
||||
cpio_vec_destroy(v);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
int check_verity_pattern(const char *s) {
|
||||
int pos = 0;
|
||||
if (s[0] == ',') ++pos;
|
||||
if (strncmp(s + pos, "verify", 6) == 0)
|
||||
pos += 6;
|
||||
else if (strncmp(s + pos, "avb", 3) == 0)
|
||||
pos += 3;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (s[pos] == '=') {
|
||||
while (s[pos] != '\0' && s[pos] != ' ' && s[pos] != '\n' && s[pos] != ',') ++pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
int check_encryption_pattern(const char *s) {
|
||||
const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe", NULL };
|
||||
for (int i = 0 ; encrypt_list[i]; ++i) {
|
||||
int len = strlen(encrypt_list[i]);
|
||||
if (strncmp(s, encrypt_list[i], len) == 0)
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
|
||||
cpio_entry *f;
|
||||
int skip, write;
|
||||
vec_for_each(v, f) {
|
||||
if (!keepverity) {
|
||||
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
|
||||
write = 0;
|
||||
for (int read = 0; read < f->filesize; ++write, ++read) {
|
||||
if ((skip = check_verity_pattern(f->data + read)) > 0) {
|
||||
fprintf(stderr, "Remove pattern [%.*s] in [%s]\n", skip, f->data + read, f->filename);
|
||||
read += skip;
|
||||
}
|
||||
f->data[write] = f->data[read];
|
||||
}
|
||||
f->filesize = write;
|
||||
} else if (strcmp(f->filename, "verity_key") == 0) {
|
||||
fprintf(stderr, "Remove [verity_key]\n");
|
||||
f->remove = 1;
|
||||
}
|
||||
}
|
||||
if (!keepforceencrypt) {
|
||||
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
|
||||
write = 0;
|
||||
for (int read = 0; read < f->filesize; ++write, ++read) {
|
||||
if ((skip = check_encryption_pattern(f->data + read)) > 0) {
|
||||
// assert(skip > 11)!
|
||||
fprintf(stderr, "Replace pattern [%.*s] with [encryptable] in [%s]\n", skip, f->data + read, f->filename);
|
||||
memcpy(f->data + write, "encryptable", 11);
|
||||
write += 11;
|
||||
read += skip;
|
||||
}
|
||||
f->data[write] = f->data[read];
|
||||
}
|
||||
f->filesize = write;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_extract(const char *entry, const char *filename, struct vector *v) {
|
||||
cpio_entry *f;
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, entry) == 0 && S_ISREG(f->mode)) {
|
||||
fprintf(stderr, "Extracting [%s] to [%s]\n\n", entry, filename);
|
||||
int fd = creat(filename, 0644);
|
||||
xwrite(fd, f->data, f->filesize);
|
||||
fchmod(fd, f->mode);
|
||||
fchown(fd, f->uid, f->gid);
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
LOGE("Cannot find the file entry [%s]\n", entry);
|
||||
}
|
||||
|
||||
static void cpio_backup(const char *orig, const char *sha1, struct vector *v) {
|
||||
struct vector o_body, *o = &o_body, bak;
|
||||
cpio_entry *m, *n, *rem, *cksm;
|
||||
char buf[PATH_MAX];
|
||||
int res, doBak;
|
||||
|
||||
if (sha1) cksm = xcalloc(sizeof(*cksm), 1);
|
||||
vec_init(o);
|
||||
vec_init(&bak);
|
||||
|
||||
m = xcalloc(sizeof(*m), 1);
|
||||
m->filename = strdup(".backup");
|
||||
m->namesize = strlen(m->filename) + 1;
|
||||
m->mode = S_IFDIR;
|
||||
vec_push_back(&bak, m);
|
||||
|
||||
m = xcalloc(sizeof(*m), 1);
|
||||
m->filename = strdup(".backup/.magisk");
|
||||
m->namesize = strlen(m->filename) + 1;
|
||||
m->mode = S_IFREG;
|
||||
vec_push_back(&bak, m);
|
||||
|
||||
rem = xcalloc(sizeof(*rem), 1);
|
||||
rem->filename = strdup(".backup/.rmlist");
|
||||
rem->namesize = strlen(rem->filename) + 1;
|
||||
rem->mode = S_IFREG;
|
||||
vec_push_back(&bak, rem);
|
||||
|
||||
if (sha1) vec_push_back(&bak, cksm);
|
||||
parse_cpio(orig, o);
|
||||
// Remove possible backups in original ramdisk
|
||||
cpio_rm(1, ".backup", o);
|
||||
cpio_rm(1, ".backup", v);
|
||||
|
||||
// Sort both vectors before comparing
|
||||
vec_sort(v, cpio_cmp);
|
||||
vec_sort(o, cpio_cmp);
|
||||
|
||||
if (sha1) {
|
||||
fprintf(stderr, "Save SHA1: [%s] -> [.backup/.sha1]\n", sha1);
|
||||
cksm->filename = strdup(".backup/.sha1");
|
||||
cksm->namesize = strlen(cksm->filename) + 1;
|
||||
cksm->mode = S_IFREG;
|
||||
cksm->data = strdup(sha1);
|
||||
cksm->filesize = strlen(sha1) + 1;
|
||||
}
|
||||
|
||||
// Start comparing
|
||||
size_t i = 0, j = 0;
|
||||
while(i != vec_size(o) || j != vec_size(v)) {
|
||||
doBak = 0;
|
||||
if (i != vec_size(o) && j != vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
n = vec_entry(v)[j];
|
||||
res = strcmp(m->filename, n->filename);
|
||||
} else if (i == vec_size(o)) {
|
||||
n = vec_entry(v)[j];
|
||||
res = 1;
|
||||
} else if (j == vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
// Something is missing in new ramdisk, backup!
|
||||
++i;
|
||||
doBak = 1;
|
||||
fprintf(stderr, "Backup missing entry: ");
|
||||
} else if (res == 0) {
|
||||
++i; ++j;
|
||||
if (m->filesize == n->filesize && memcmp(m->data, n->data, m->filesize) == 0)
|
||||
continue;
|
||||
// Not the same!
|
||||
doBak = 1;
|
||||
fprintf(stderr, "Backup mismatch entry: ");
|
||||
} else {
|
||||
// Someting new in ramdisk, record in rem
|
||||
++j;
|
||||
if (n->remove) continue;
|
||||
rem->data = xrealloc(rem->data, rem->filesize + n->namesize);
|
||||
memcpy(rem->data + rem->filesize, n->filename, n->namesize);
|
||||
rem->filesize += n->namesize;
|
||||
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", n->filename);
|
||||
}
|
||||
if (doBak) {
|
||||
m->namesize += 8;
|
||||
m->filename = realloc(m->filename, m->namesize);
|
||||
strcpy(buf, m->filename);
|
||||
sprintf(m->filename, ".backup/%s", buf);
|
||||
fprintf(stderr, "[%s] -> [%s]\n", buf, m->filename);
|
||||
vec_push_back(&bak, m);
|
||||
// NULL the original entry, so it won't be freed
|
||||
vec_entry(o)[i - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the backup files to the original ramdisk
|
||||
vec_for_each(&bak, m) {
|
||||
vec_push_back(v, m);
|
||||
}
|
||||
|
||||
if (rem->filesize == 0)
|
||||
rem->remove = 1;
|
||||
|
||||
// Cleanup
|
||||
cpio_vec_destroy(o);
|
||||
}
|
||||
|
||||
static int cpio_restore(struct vector *v) {
|
||||
cpio_entry *f, *n;
|
||||
int ret = 1;
|
||||
vec_for_each(v, f) {
|
||||
if (strstr(f->filename, ".backup") != NULL) {
|
||||
ret = 0;
|
||||
f->remove = 1;
|
||||
if (f->filename[7] == '\0') continue;
|
||||
if (f->filename[8] == '.') {
|
||||
if (strcmp(f->filename, ".backup/.rmlist") == 0) {
|
||||
for (int pos = 0; pos < f->filesize; pos += strlen(f->data + pos) + 1)
|
||||
cpio_rm(0, f->data + pos, v);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
n = xcalloc(sizeof(*n), 1);
|
||||
memcpy(n, f, sizeof(*f));
|
||||
n->namesize -= 8;
|
||||
n->filename = strdup(f->filename + 8);
|
||||
n->data = f->data;
|
||||
f->data = NULL;
|
||||
n->remove = 0;
|
||||
fprintf(stderr, "Restore [%s] -> [%s]\n", f->filename, n->filename);
|
||||
cpio_vec_insert(v, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Some known stuff we can remove
|
||||
cpio_rm(0, "sbin/magic_mask.sh", v);
|
||||
cpio_rm(0, "init.magisk.rc", v);
|
||||
cpio_rm(0, "magisk", v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cpio_stocksha1(struct vector *v) {
|
||||
cpio_entry *f;
|
||||
char sha1[41];
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, "init.magisk.rc") == 0
|
||||
|| strcmp(f->filename, "overlay/init.magisk.rc") == 0) {
|
||||
for (char *pos = f->data; pos < f->data + f->filesize; pos = strchr(pos + 1, '\n') + 1) {
|
||||
if (memcmp(pos, "# STOCKSHA1=", 12) == 0) {
|
||||
pos += 12;
|
||||
memcpy(sha1, pos, 40);
|
||||
sha1[40] = '\0';
|
||||
printf("%s\n", sha1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(f->filename, ".backup/.sha1") == 0) {
|
||||
printf("%s\n", f->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_mv(struct vector *v, const char *from, const char *to) {
|
||||
struct cpio_entry *f, *t;
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, from) == 0) {
|
||||
fprintf(stderr, "Move [%s] -> [%s]\n", from, to);
|
||||
vec_for_each(v, t) {
|
||||
if (strcmp(t->filename, to) == 0) {
|
||||
t->remove = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(f->filename);
|
||||
f->namesize = strlen(to) + 1;
|
||||
f->filename = strdup(to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Cannot find entry %s\n", from);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cpio_commands(const char *command, int argc, char *argv[]) {
|
||||
int recursive = 0, ret = 0;
|
||||
command_t cmd;
|
||||
char *incpio = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
if (strcmp(command, "test") == 0) {
|
||||
cmd = TEST;
|
||||
} else if (strcmp(command, "restore") == 0) {
|
||||
cmd = RESTORE;
|
||||
} else if (strcmp(command, "stocksha1") == 0) {
|
||||
cmd = STOCKSHA1;
|
||||
} else if (argc >= 1 && strcmp(command, "backup") == 0) {
|
||||
cmd = BACKUP;
|
||||
} else if (argc > 0 && strcmp(command, "rm") == 0) {
|
||||
cmd = RM;
|
||||
if (argc == 2 && strcmp(argv[0], "-r") == 0) {
|
||||
recursive = 1;
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
} else if (argc == 2 && strcmp(command, "mv") == 0) {
|
||||
cmd = MV;
|
||||
} else if (argc == 2 && strcmp(command, "patch") == 0) {
|
||||
cmd = PATCH;
|
||||
} else if (argc == 2 && strcmp(command, "extract") == 0) {
|
||||
cmd = EXTRACT;
|
||||
} else if (argc == 2 && strcmp(command, "mkdir") == 0) {
|
||||
cmd = MKDIR;
|
||||
} else if (argc == 3 && strcmp(command, "add") == 0) {
|
||||
cmd = ADD;
|
||||
} else {
|
||||
cmd = NONE;
|
||||
}
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
parse_cpio(incpio, &v);
|
||||
switch(cmd) {
|
||||
case TEST:
|
||||
cpio_test(&v);
|
||||
break;
|
||||
case RESTORE:
|
||||
ret = cpio_restore(&v);
|
||||
break;
|
||||
case STOCKSHA1:
|
||||
cpio_stocksha1(&v);
|
||||
return 0;
|
||||
case BACKUP:
|
||||
cpio_backup(argv[0], argc > 1 ? argv[1] : NULL, &v);
|
||||
case RM:
|
||||
cpio_rm(recursive, argv[0], &v);
|
||||
break;
|
||||
case PATCH:
|
||||
cpio_patch(&v, strcmp(argv[0], "true") == 0, strcmp(argv[1], "true") == 0);
|
||||
break;
|
||||
case EXTRACT:
|
||||
cpio_extract(argv[0], argv[1], &v);
|
||||
break;
|
||||
case MKDIR:
|
||||
cpio_mkdir(strtoul(argv[0], NULL, 8), argv[1], &v);
|
||||
break;
|
||||
case ADD:
|
||||
cpio_add(strtoul(argv[0], NULL, 8), argv[1], argv[2], &v);
|
||||
break;
|
||||
case MV:
|
||||
cpio_mv(&v, argv[0], argv[1]);
|
||||
break;
|
||||
case NONE:
|
||||
return 1;
|
||||
}
|
||||
dump_cpio(incpio, &v);
|
||||
cpio_vec_destroy(&v);
|
||||
exit(ret);
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
#ifndef _CPIO_H_
|
||||
#define _CPIO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct cpio_entry {
|
||||
// uint32_t ino;
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
// uint32_t nlink;
|
||||
// uint32_t mtime;
|
||||
uint32_t filesize;
|
||||
// uint32_t devmajor;
|
||||
// uint32_t devminor;
|
||||
// uint32_t rdevmajor;
|
||||
// uint32_t rdevminor;
|
||||
uint32_t namesize;
|
||||
// uint32_t check;
|
||||
char *filename;
|
||||
char *data;
|
||||
int remove;
|
||||
} cpio_entry;
|
||||
|
||||
typedef struct cpio_newc_header {
|
||||
char magic[6];
|
||||
char ino[8];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char nlink[8];
|
||||
char mtime[8];
|
||||
char filesize[8];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char rdevmajor[8];
|
||||
char rdevminor[8];
|
||||
char namesize[8];
|
||||
char check[8];
|
||||
} cpio_newc_header;
|
||||
|
||||
typedef enum {
|
||||
NONE,
|
||||
RM,
|
||||
MKDIR,
|
||||
ADD,
|
||||
MV,
|
||||
EXTRACT,
|
||||
TEST,
|
||||
PATCH,
|
||||
BACKUP,
|
||||
RESTORE,
|
||||
STOCKSHA1
|
||||
} command_t;
|
||||
|
||||
#endif
|
Submodule jni/magiskpolicy deleted from e5b6121d17
1
jni/su
1
jni/su
Submodule jni/su deleted from a9d966dc7a
@@ -12,7 +12,7 @@
|
||||
|
||||
main() {
|
||||
# Magisk binaries
|
||||
MAGISKBIN=/data/magisk
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
# This script always runs in recovery
|
||||
BOOTMODE=false
|
||||
|
||||
@@ -56,14 +56,14 @@ main() {
|
||||
|
||||
if [ -f stock_boot* ]; then
|
||||
rm -f /data/stock_boot* 2>/dev/null
|
||||
is_mounted /data && mv stock_boot* /data
|
||||
mv stock_boot* /data
|
||||
fi
|
||||
|
||||
patch_dtbo_image
|
||||
|
||||
if [ -f stock_dtbo* ]; then
|
||||
rm -f /data/stock_dtbo* 2>/dev/null
|
||||
is_mounted /data && mv stock_dtbo* /data
|
||||
mv stock_dtbo* /data
|
||||
fi
|
||||
|
||||
cd /
|
||||
|
@@ -63,7 +63,16 @@ BOOTIMAGE="$1"
|
||||
|
||||
# Presets
|
||||
[ -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"
|
||||
else
|
||||
KEEPFORCEENCRYPT=false
|
||||
fi
|
||||
fi
|
||||
|
||||
chmod -R 755 .
|
||||
|
||||
@@ -86,14 +95,19 @@ case $? in
|
||||
abort "! Unable to unpack boot image"
|
||||
;;
|
||||
2 )
|
||||
ui_print "! Insufficient boot partition size detected"
|
||||
HIGHCOMP=true
|
||||
ui_print "- Enable high compression mode"
|
||||
;;
|
||||
3 )
|
||||
ui_print "- ChromeOS boot image detected"
|
||||
CHROMEOS=true
|
||||
;;
|
||||
3 )
|
||||
4 )
|
||||
ui_print "! Sony ELF32 format detected"
|
||||
abort "! Please use BootBridge from @AdrianDC to flash Magisk"
|
||||
;;
|
||||
4 )
|
||||
5 )
|
||||
ui_print "! Sony ELF64 format detected"
|
||||
abort "! Stock kernel cannot be patched, please use a custom kernel"
|
||||
esac
|
||||
@@ -104,10 +118,10 @@ esac
|
||||
|
||||
# Test patch status and do restore, after this section, ramdisk.cpio.orig is guaranteed to exist
|
||||
ui_print "- Checking ramdisk status"
|
||||
./magiskboot --cpio-test ramdisk.cpio
|
||||
./magiskboot --cpio ramdisk.cpio test
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print "- Stock boot image detected!"
|
||||
ui_print "- Stock boot image detected"
|
||||
ui_print "- Backing up stock boot image"
|
||||
SHA1=`./magiskboot --sha1 "$BOOTIMAGE" 2>/dev/null`
|
||||
STOCKDUMP=stock_boot_${SHA1}.img.gz
|
||||
@@ -115,37 +129,14 @@ case $? in
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
ui_print "- Magisk patched image detected!"
|
||||
ui_print "- Magisk patched image detected"
|
||||
# Find SHA1 of stock boot image
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio-stocksha1 ramdisk.cpio 2>/dev/null`
|
||||
OK=false
|
||||
./magiskboot --cpio-restore ramdisk.cpio
|
||||
if [ $? -eq 0 ]; then
|
||||
ui_print "- Ramdisk restored from internal backup"
|
||||
OK=true
|
||||
else
|
||||
# Restore failed
|
||||
ui_print "! Cannot restore from internal backup"
|
||||
# If we are root and SHA1 known, we try to find the stock backup
|
||||
if [ ! -z $SHA1 ]; then
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img.gz
|
||||
if [ -f $STOCKDUMP ]; then
|
||||
ui_print "- Stock boot image backup found"
|
||||
./magiskboot --decompress $STOCKDUMP stock_boot.img
|
||||
./magiskboot --unpack stock_boot.img
|
||||
rm -f stock_boot.img
|
||||
OK=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if ! $OK; then
|
||||
ui_print "! Ramdisk restoration incomplete"
|
||||
ui_print "! Will still try to continue installation"
|
||||
fi
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
./magiskboot --cpio ramdisk.cpio restore
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
;;
|
||||
2 ) # Other patched
|
||||
ui_print "! Boot image patched by other programs!"
|
||||
ui_print "! Boot image patched by other programs"
|
||||
abort "! Please restore stock boot image"
|
||||
;;
|
||||
esac
|
||||
@@ -156,11 +147,9 @@ esac
|
||||
|
||||
ui_print "- Patching ramdisk"
|
||||
|
||||
./magiskboot --cpio-add ramdisk.cpio 750 init magiskinit
|
||||
./magiskboot --cpio-patch ramdisk.cpio $KEEPVERITY $KEEPFORCEENCRYPT
|
||||
|
||||
# Create ramdisk backups
|
||||
./magiskboot --cpio-backup ramdisk.cpio ramdisk.cpio.orig $SHA1
|
||||
./magiskboot --cpio ramdisk.cpio \
|
||||
'add 750 init magiskinit' \
|
||||
"magisk ramdisk.cpio.orig $HIGHCOMP $KEEPVERITY $KEEPFORCEENCRYPT $SHA1"
|
||||
|
||||
rm -f ramdisk.cpio.orig
|
||||
|
||||
|
@@ -77,10 +77,17 @@ chmod -R 755 $CHROMEDIR $BINDIR
|
||||
|
||||
ui_print "- Constructing environment"
|
||||
|
||||
is_mounted /data && MAGISKBIN=/data/magisk || MAGISKBIN=/cache/data_bin
|
||||
if is_mounted /data; then
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
mkdir -p /data/adb 2>/dev/null
|
||||
chmod 700 /data/adb 2>/dev/null
|
||||
|
||||
# Save our stock boot image dump before removing it
|
||||
mv /data/magisk/stock_boot* /data 2>/dev/null
|
||||
# Some legacy migration
|
||||
mv /data/magisk/stock_boot* /data 2>/dev/null
|
||||
[ -L /data/magisk.img ] || mv /data/magisk.img /data/adb/magisk.img
|
||||
else
|
||||
MAGISKBIN=/cache/data_bin
|
||||
fi
|
||||
|
||||
# Copy required files
|
||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
||||
@@ -106,6 +113,13 @@ $BOOTMODE || recovery_actions
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
ui_print "- Found boot image: $BOOTIMAGE"
|
||||
|
||||
find_dtbo_image
|
||||
if [ ! -z $DTBOIMAGE ]; then
|
||||
ui_print "- Found dtbo image: $DTBOIMAGE"
|
||||
# Disable dtbo patch by default
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=true
|
||||
fi
|
||||
|
||||
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
|
||||
$BOOTSIGNED && ui_print "- Signed boot image detected"
|
||||
|
||||
@@ -123,7 +137,7 @@ if [ -f stock_boot* ]; then
|
||||
is_mounted /data && mv stock_boot* /data
|
||||
fi
|
||||
|
||||
patch_dtbo_image
|
||||
$KEEPVERITY || patch_dtbo_image
|
||||
|
||||
if [ -f stock_dtbo* ]; then
|
||||
rm -f /data/stock_dtbo* 2>/dev/null
|
||||
|
@@ -23,7 +23,7 @@
|
||||
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
|
||||
MAGISKBIN=/data/magisk
|
||||
[ -d /data/adb/magisk ] && MAGISKBIN=/data/adb/magisk || MAGISKBIN=/data/magisk
|
||||
CHROMEDIR=$MAGISKBIN/chromeos
|
||||
|
||||
if [ ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
|
||||
@@ -51,40 +51,42 @@ migrate_boot_backup
|
||||
ui_print "- Unpacking boot image"
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
CHROMEOS=false
|
||||
|
||||
case $? in
|
||||
1 )
|
||||
abort "! Unable to unpack boot image"
|
||||
;;
|
||||
2 )
|
||||
3 )
|
||||
ui_print "- ChromeOS boot image detected"
|
||||
CHROMEOS=true
|
||||
;;
|
||||
3 )
|
||||
4 )
|
||||
ui_print "! Sony ELF32 format detected"
|
||||
abort "! Please use BootBridge from @AdrianDC to flash Magisk"
|
||||
;;
|
||||
4 )
|
||||
5 )
|
||||
ui_print "! Sony ELF64 format detected"
|
||||
abort "! Stock kernel cannot be patched, please use a custom kernel"
|
||||
esac
|
||||
|
||||
# Detect boot image state
|
||||
ui_print "- Checking ramdisk status"
|
||||
./magiskboot --cpio-test ramdisk.cpio
|
||||
./magiskboot --cpio ramdisk.cpio test
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print "- Stock boot image detected!"
|
||||
ui_print "- Stock boot image detected"
|
||||
abort "! Magisk is not installed!"
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
ui_print "- Magisk patched image detected!"
|
||||
ui_print "- Magisk patched image detected"
|
||||
# Find SHA1 of stock boot image
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio-stocksha1 ramdisk.cpio 2>/dev/null`
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot --cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
OK=false
|
||||
[ ! -z $SHA1 ] && restore_imgs $SHA1 && OK=true
|
||||
if ! $OK; then
|
||||
ui_print "! Boot image backup unavailable"
|
||||
ui_print "- Restoring ramdisk with internal backup"
|
||||
./magiskboot --cpio-restore ramdisk.cpio
|
||||
./magiskboot --cpio ramdisk.cpio restore
|
||||
./magiskboot --repack $BOOTIMAGE
|
||||
# Sign chromeos boot
|
||||
$CHROMEOS && sign_chromeos
|
||||
@@ -92,7 +94,7 @@ case $? in
|
||||
fi
|
||||
;;
|
||||
2 ) # Other patched
|
||||
ui_print "! Boot image patched by other programs!"
|
||||
ui_print "! Boot image patched by other programs"
|
||||
abort "! Cannot uninstall"
|
||||
;;
|
||||
esac
|
||||
@@ -102,6 +104,7 @@ 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 2>/dev/null
|
||||
/data/user*/*/magisk.db /data/user*/*/com.topjohnwu.magisk /data/user*/*/.tmp.magisk.config \
|
||||
/data/adb/*magisk* 2>/dev/null
|
||||
|
||||
$BOOTMODE && reboot
|
||||
|
@@ -54,8 +54,6 @@ BINDIR=$INSTALLER/$ARCH
|
||||
# Detection all done, start installing
|
||||
##########################################################################################
|
||||
|
||||
MAGISKBIN=/data/magisk
|
||||
|
||||
if is_mounted /data; then
|
||||
recovery_actions
|
||||
# Save our stock boot image dump before removing it
|
||||
|
@@ -7,11 +7,13 @@
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
MAGISK_VERSION_STUB
|
||||
#MAGISK_VERSION_STUB
|
||||
SCRIPT_VERSION=$MAGISK_VER_CODE
|
||||
|
||||
# Default location, will override if needed
|
||||
MAGISKBIN=/data/magisk
|
||||
MAGISKBIN=/data/adb/magisk
|
||||
[ -z $MOUNTPATH ] && MOUNTPATH=/sbin/.core/img
|
||||
[ -z $IMG ] && IMG=/data/adb/magisk.img
|
||||
|
||||
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.topjohnwu.magisk.utils.BootSigner"
|
||||
BOOTSIGNED=false
|
||||
@@ -84,7 +86,7 @@ getvar() {
|
||||
local VARNAME=$1
|
||||
local VALUE=$(eval echo \$$VARNAME)
|
||||
[ ! -z $VALUE ] && return
|
||||
for DIR in /dev /data /cache /system; do
|
||||
for DIR in /.backup /dev /data /cache /system; do
|
||||
VALUE=`grep_prop $VARNAME $DIR/.magisk`
|
||||
[ ! -z $VALUE ] && break;
|
||||
done
|
||||
@@ -133,6 +135,7 @@ migrate_boot_backup() {
|
||||
$MAGISKBIN/magiskboot --compress $STOCKDUMP
|
||||
fi
|
||||
mv /data/magisk/stock_boot* /data 2>/dev/null
|
||||
mv /data/magisk/adb/stock_boot* /data 2>/dev/null
|
||||
}
|
||||
|
||||
flash_boot_image() {
|
||||
@@ -161,9 +164,7 @@ find_dtbo_image() {
|
||||
}
|
||||
|
||||
patch_dtbo_image() {
|
||||
find_dtbo_image
|
||||
if [ ! -z $DTBOIMAGE ]; then
|
||||
ui_print "- Found dtbo image: $DTBOIMAGE"
|
||||
if $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
|
||||
ui_print "- Backing up stock dtbo image"
|
||||
$MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
|
||||
@@ -334,3 +335,36 @@ image_size_check() {
|
||||
curFreeM=$((curSizeM - curUsedM))
|
||||
}
|
||||
|
||||
mount_magisk_img() {
|
||||
[ -z reqSizeM ] && reqSizeM=0
|
||||
if [ -f "$IMG" ]; then
|
||||
ui_print "- Found $IMG"
|
||||
image_size_check $IMG
|
||||
if [ "$reqSizeM" -gt "$curFreeM" ]; then
|
||||
newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64))
|
||||
ui_print "- Resizing $IMG to ${newSizeM}M"
|
||||
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM >&2
|
||||
fi
|
||||
else
|
||||
newSizeM=$((reqSizeM / 32 * 32 + 64));
|
||||
ui_print "- Creating $IMG with size ${newSizeM}M"
|
||||
$MAGISKBIN/magisk --createimg $IMG $newSizeM >&2
|
||||
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
|
||||
newSizeM=$((curUsedM / 32 * 32 + 64))
|
||||
if [ $curSizeM -gt $newSizeM ]; then
|
||||
ui_print "- Shrinking $IMG to ${newSizeM}M"
|
||||
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM
|
||||
fi
|
||||
}
|
||||
|
||||
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':app', ':core', ':crypto', ':snet'
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user