mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 09:57:29 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cdc5d983f3 | ||
![]() |
96688e4dac | ||
![]() |
28a945fee9 | ||
![]() |
c7e777255a | ||
![]() |
2dd4cf040e | ||
![]() |
d1b9eca5eb | ||
![]() |
594a67fe28 | ||
![]() |
cddeaffada | ||
![]() |
2a8898e7c3 | ||
![]() |
ce3f3b09b4 | ||
![]() |
fe4b3df7e9 | ||
![]() |
25bdbcf526 | ||
![]() |
df7eaa5598 | ||
![]() |
bb7099376b | ||
![]() |
0327fd9710 | ||
![]() |
e645c6e465 | ||
![]() |
78a3d36ccc | ||
![]() |
3942858ccd | ||
![]() |
03c8d716cc | ||
![]() |
60181c4fcb | ||
![]() |
c215447405 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,6 +2,7 @@ obj/
|
||||
libs/
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
|
||||
# Copied binaries
|
||||
# Built binaries
|
||||
ziptools/zipadjust
|
||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -11,7 +11,7 @@
|
||||
path = jni/magiskpolicy
|
||||
url = https://github.com/topjohnwu/magiskpolicy.git
|
||||
[submodule "MagiskManager"]
|
||||
path = MagiskManager
|
||||
path = java
|
||||
url = https://github.com/topjohnwu/MagiskManager.git
|
||||
[submodule "jni/busybox"]
|
||||
path = jni/external/busybox
|
||||
|
Submodule MagiskManager deleted from 773c24b7fc
@@ -45,7 +45,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Credits
|
||||
|
||||
**MagiskManager** (`MagiskManager`)
|
||||
**MagiskManager** (`java`)
|
||||
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* All contributors and translators
|
||||
|
85
build.py
85
build.py
@@ -51,6 +51,10 @@ def zip_with_msg(zipfile, source, target):
|
||||
print('zip: {} -> {}'.format(source, target))
|
||||
zipfile.write(source, target)
|
||||
|
||||
def cp(source, target):
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
shutil.copyfile(source, target)
|
||||
|
||||
def build_all(args):
|
||||
build_binary(args)
|
||||
build_apk(args)
|
||||
@@ -75,26 +79,23 @@ def build_apk(args):
|
||||
|
||||
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
|
||||
source = os.path.join('ziptools', key)
|
||||
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
shutil.copyfile(source, target)
|
||||
target = os.path.join('java', 'app', 'src', 'main', 'assets', key)
|
||||
cp(source, target)
|
||||
|
||||
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
|
||||
source = os.path.join('scripts', script)
|
||||
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', script)
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
shutil.copyfile(source, target)
|
||||
target = os.path.join('java', 'app', 'src', 'main', 'assets', script)
|
||||
cp(source, target)
|
||||
|
||||
os.chdir('MagiskManager')
|
||||
os.chdir('java')
|
||||
|
||||
# Build unhide app and place in assets
|
||||
proc = subprocess.run('{} unhide::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
proc = subprocess.run('{} unhide:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
source = os.path.join('unhide', 'build', 'outputs', 'apk', 'release', 'unhide-release-unsigned.apk')
|
||||
target = os.path.join('app', 'src', 'main', 'assets', 'unhide.apk')
|
||||
print('cp: {} -> {}'.format(source, target))
|
||||
shutil.copyfile(source, target)
|
||||
cp(source, target)
|
||||
|
||||
print('')
|
||||
|
||||
@@ -102,7 +103,7 @@ def build_apk(args):
|
||||
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
|
||||
error('Please generate a java keystore and place it in \'release_signature.jks\'')
|
||||
|
||||
proc = subprocess.run('{} app::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
@@ -141,16 +142,26 @@ def build_apk(args):
|
||||
silentremove(unsigned)
|
||||
silentremove(aligned)
|
||||
else:
|
||||
proc = subprocess.run('{} app::assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
# Return to upper directory
|
||||
os.chdir('..')
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
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')
|
||||
target = os.path.join('..', 'snet.apk')
|
||||
print('')
|
||||
cp(source, target)
|
||||
os.chdir('..')
|
||||
|
||||
zipsigner = os.path.join('ziptools', 'zipsigner', 'build', 'libs', 'zipsigner.jar')
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
jarsigner = os.path.join('java', 'jarsigner', 'build', 'libs', 'jarsigner-fat.jar')
|
||||
|
||||
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
|
||||
header('* Building zipadjust')
|
||||
@@ -158,13 +169,13 @@ def sign_adjust_zip(unsigned, output):
|
||||
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build zipadjust failed!')
|
||||
if not os.path.exists(zipsigner):
|
||||
header('* Building zipsigner.jar')
|
||||
os.chdir(os.path.join('ziptools', 'zipsigner'))
|
||||
proc = subprocess.run('{} shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if not os.path.exists(jarsigner):
|
||||
header('* Building jarsigner-fat.jar')
|
||||
os.chdir('java')
|
||||
proc = subprocess.run('{} jarsigner:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build zipsigner.jar failed!')
|
||||
os.chdir(os.path.join('..', '..'))
|
||||
error('Build jarsigner-fat.jar failed!')
|
||||
os.chdir('..')
|
||||
|
||||
header('* Signing / Adjusting Zip')
|
||||
|
||||
@@ -172,7 +183,7 @@ def sign_adjust_zip(unsigned, output):
|
||||
privateKey = os.path.join('ziptools', 'private.key.pk8')
|
||||
|
||||
# Unsigned->signed
|
||||
proc = subprocess.run(['java', '-jar', zipsigner,
|
||||
proc = subprocess.run(['java', '-jar', jarsigner,
|
||||
publicKey, privateKey, unsigned, 'tmp_signed.zip'])
|
||||
if proc.returncode != 0:
|
||||
error('First sign flashable zip failed!')
|
||||
@@ -183,7 +194,7 @@ def sign_adjust_zip(unsigned, output):
|
||||
error('Adjust flashable zip failed!')
|
||||
|
||||
# Adjusted -> output
|
||||
proc = subprocess.run(['java', '-jar', zipsigner,
|
||||
proc = subprocess.run(['java', '-jar', jarsigner,
|
||||
"-m", publicKey, privateKey, 'tmp_adjusted.zip', output])
|
||||
if proc.returncode != 0:
|
||||
error('Second sign flashable zip failed!')
|
||||
@@ -199,15 +210,15 @@ def gen_update_binary():
|
||||
if not os.path.exists(binary):
|
||||
error('Please build \'binary\' before zipping!')
|
||||
with open(binary, 'rb') as b64xz:
|
||||
update_bin.append('#! /sbin/sh\nEX_ARM=')
|
||||
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
update_bin.append('#! /sbin/sh\nEX_ARM=\'')
|
||||
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
binary = os.path.join('libs', 'x86', 'b64xz')
|
||||
with open(binary, 'rb') as b64xz:
|
||||
update_bin.append('\nEX_X86=')
|
||||
update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
update_bin.append('\'\nEX_X86=\'')
|
||||
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read()))
|
||||
binary = os.path.join('libs', 'armeabi-v7a', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
update_bin.append('\nBB_ARM=')
|
||||
update_bin.append('\'\nBB_ARM=')
|
||||
update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii'))
|
||||
binary = os.path.join('libs', 'x86', 'busybox')
|
||||
with open(binary, 'rb') as busybox:
|
||||
@@ -243,7 +254,7 @@ def zip_main(args):
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# APK
|
||||
source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk',
|
||||
source = os.path.join('java', 'app', 'build', 'outputs', 'apk',
|
||||
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
target = os.path.join('common', 'magisk.apk')
|
||||
zip_with_msg(zipf, source, target)
|
||||
@@ -328,20 +339,21 @@ def zip_uninstaller(args):
|
||||
|
||||
def cleanup(args):
|
||||
if len(args.target) == 0:
|
||||
args.target = ['binary', 'apk', 'zip']
|
||||
args.target = ['binary', 'java', 'zip']
|
||||
|
||||
if 'binary' in args.target:
|
||||
header('* Cleaning Magisk binaries')
|
||||
header('* Cleaning binaries')
|
||||
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
|
||||
|
||||
if 'apk' in args.target:
|
||||
header('* Cleaning Magisk Manager')
|
||||
os.chdir('MagiskManager')
|
||||
if 'java' in args.target:
|
||||
header('* Cleaning java')
|
||||
os.chdir('java')
|
||||
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
os.chdir('..')
|
||||
silentremove('snet.apk')
|
||||
|
||||
if 'zip' in args.target:
|
||||
header('* Cleaning created zip files')
|
||||
header('* Cleaning zip files')
|
||||
for f in os.listdir('.'):
|
||||
if '.zip' in f:
|
||||
print('rm {}'.format(f))
|
||||
@@ -364,6 +376,9 @@ binary_parser.set_defaults(func=build_binary)
|
||||
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
|
||||
apk_parser.set_defaults(func=build_apk)
|
||||
|
||||
snet_parser = subparsers.add_parser('snet', help='build snet extention for Magisk Manager')
|
||||
snet_parser.set_defaults(func=build_snet)
|
||||
|
||||
zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
|
||||
zip_parser.add_argument('versionString')
|
||||
zip_parser.add_argument('versionCode', type=int)
|
||||
@@ -372,7 +387,7 @@ zip_parser.set_defaults(func=zip_main)
|
||||
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
|
||||
uninstaller_parser.set_defaults(func=zip_uninstaller)
|
||||
|
||||
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary apk zip')
|
||||
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip')
|
||||
clean_parser.add_argument('target', nargs='*')
|
||||
clean_parser.set_defaults(func=cleanup)
|
||||
|
||||
|
1
java
Submodule
1
java
Submodule
Submodule java added at 13bf1b27b4
@@ -40,6 +40,7 @@ LOCAL_SRC_FILES := \
|
||||
utils/xwrap.c \
|
||||
utils/list.c \
|
||||
utils/img.c \
|
||||
utils/file.c \
|
||||
magiskhide/magiskhide.c \
|
||||
magiskhide/proc_monitor.c \
|
||||
magiskhide/hide_utils.c \
|
||||
@@ -97,10 +98,12 @@ LOCAL_C_INCLUDES := jni/include $(LIBSEPOL)
|
||||
LOCAL_SRC_FILES := \
|
||||
magiskinit.c \
|
||||
magiskboot/boot_utils.c \
|
||||
utils/file.c \
|
||||
utils/xwrap.c \
|
||||
magiskpolicy/rules.c \
|
||||
magiskpolicy/sepolicy.c \
|
||||
magiskpolicy/api.c
|
||||
LOCAL_CFLAGS := -DNO_SELINUX
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
endif
|
||||
|
@@ -28,10 +28,6 @@ static int seperate_vendor = 0;
|
||||
|
||||
extern char **environ;
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
#endif
|
||||
|
||||
/******************
|
||||
* Node structure *
|
||||
******************/
|
||||
@@ -149,7 +145,7 @@ static void exec_common_script(const char* stage) {
|
||||
struct dirent *entry;
|
||||
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
if (!(dir = xopendir(buf)))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
@@ -194,7 +190,7 @@ static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
char *parent_path = get_full_path(parent);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
@@ -262,7 +258,7 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
// Clone the structure
|
||||
char *full_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
|
||||
if (!(dir = opendir(buf)))
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
@@ -282,7 +278,7 @@ static void clone_skeleton(struct node_entry *node) {
|
||||
xstat(full_path, &s);
|
||||
getfilecon(full_path, &con);
|
||||
LOGI("tmpfs: %s\n", full_path);
|
||||
mount("tmpfs", full_path, "tmpfs", 0, NULL);
|
||||
xmount("tmpfs", full_path, "tmpfs", 0, NULL);
|
||||
chmod(full_path, s.st_mode & 0777);
|
||||
chown(full_path, s.st_uid, s.st_gid);
|
||||
setfilecon(full_path, con);
|
||||
@@ -399,7 +395,44 @@ static void simple_mount(const char *path) {
|
||||
* Miscellaneous *
|
||||
*****************/
|
||||
|
||||
static void mount_mirrors() {
|
||||
// A one time setup
|
||||
static void daemon_init() {
|
||||
LOGI("* Creating /sbin overlay");
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int root, sbin;
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir("/root", 0755);
|
||||
chmod("/root", 0755);
|
||||
root = xopen("/root", O_RDONLY | O_CLOEXEC);
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
dir = xfdopendir(sbin);
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
linkat(sbin, entry->d_name, root, entry->d_name, 0);
|
||||
if (strcmp(entry->d_name, "magisk") == 0)
|
||||
unlinkat(sbin, entry->d_name, 0);
|
||||
}
|
||||
close(sbin);
|
||||
mount("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);
|
||||
}
|
||||
xmkdir("/magisk", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
@@ -425,9 +458,7 @@ static void mount_mirrors() {
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/system");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (strstr(line, " /vendor ")) {
|
||||
} else if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
xmkdir_p(MIRRDIR "/vendor", 0755);
|
||||
@@ -437,29 +468,34 @@ static void mount_mirrors() {
|
||||
#else
|
||||
LOGI("mount: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_deep_destroy(&mounts);
|
||||
vec_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
symlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("link: %s -> %s\n", MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#else
|
||||
LOGI("link: %s\n", MIRRDIR "/vendor");
|
||||
#endif
|
||||
}
|
||||
mkdir_p(MIRRDIR "/bin", 0755);
|
||||
xmkdir_p(MIRRDIR "/bin", 0755);
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
}
|
||||
|
||||
static void link_busybox() {
|
||||
mkdir_p(BBPATH, 0755);
|
||||
LOGI("* Setting up internal busybox");
|
||||
xmkdir_p(BBPATH, 0755);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
symlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
|
||||
static int prepare_img() {
|
||||
// First merge images
|
||||
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
|
||||
LOGE("Image merge /data/magisk_merge.img -> " MAINIMG " failed!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (access(MAINIMG, F_OK) == -1) {
|
||||
if (create_img(MAINIMG, 64))
|
||||
return 1;
|
||||
@@ -490,7 +526,7 @@ static int prepare_img() {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/remove", MOUNTPOINT, entry->d_name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
|
||||
exec_command_sync(BBPATH "/rm", "-rf", buf, NULL);
|
||||
rm_rf(buf);
|
||||
continue;
|
||||
}
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
|
||||
@@ -511,27 +547,30 @@ static int prepare_img() {
|
||||
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
free(magiskloop);
|
||||
|
||||
// Fix file selinux contexts
|
||||
fix_filecon();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fix_filecon() {
|
||||
int dirfd = xopen(MOUNTPOINT, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(dirfd, 0);
|
||||
close(dirfd);
|
||||
dirfd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(dirfd, 1);
|
||||
close(dirfd);
|
||||
}
|
||||
|
||||
/****************
|
||||
* Entry points *
|
||||
****************/
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unblock_boot_process() {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void post_fs(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
LOGI("** post-fs mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
@@ -553,24 +592,14 @@ unblock:
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (!check_data())
|
||||
goto unblock;
|
||||
|
||||
// Start log monitor
|
||||
monitor_logs();
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Log everything initially
|
||||
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
xwrite(debug_log_fd, "Boot logs:\n", 11);
|
||||
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", NULL);
|
||||
#endif
|
||||
// Start the debug log
|
||||
start_debug_full_log();
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
@@ -587,23 +616,15 @@ void post_fs_data(int client) {
|
||||
else if (access("/data/user_de/0/com.topjohnwu.magisk/install", F_OK) == 0)
|
||||
bin_path = "/data/user_de/0/com.topjohnwu.magisk/install";
|
||||
if (bin_path) {
|
||||
exec_command_sync("rm", "-rf", DATABIN, NULL);
|
||||
exec_command_sync("cp", "-r", bin_path, DATABIN, NULL);
|
||||
exec_command_sync("rm", "-rf", bin_path, NULL);
|
||||
exec_command_sync("chmod", "-R", "755", bin_path, NULL);
|
||||
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 /data/magisk/stock_boot* /data", NULL);
|
||||
}
|
||||
|
||||
// Link busybox
|
||||
mount_mirrors();
|
||||
link_busybox();
|
||||
|
||||
// Merge images
|
||||
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
|
||||
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
|
||||
goto unblock;
|
||||
}
|
||||
// Initialize
|
||||
daemon_init();
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
@@ -613,7 +634,7 @@ void post_fs_data(int client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim, mount magisk.img, which will also travel through the modules
|
||||
// Merge, trim, mount magisk.img, which will also travel through the modules
|
||||
// After this, it will create the module list
|
||||
if (prepare_img())
|
||||
goto core_only; // Mounting fails, we can only do core only stuffs
|
||||
@@ -665,7 +686,7 @@ void post_fs_data(int client) {
|
||||
if (access(buf, F_OK) == 0) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
|
||||
unlink(buf2);
|
||||
symlink(buf, buf2);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
construct_tree(module, sys_root);
|
||||
}
|
||||
@@ -703,14 +724,7 @@ core_only:
|
||||
bind_mount(HOSTSFILE, "/system/etc/hosts");
|
||||
}
|
||||
|
||||
// Enable magiskhide by default, only disable when set explicitly
|
||||
char *hide_prop = getprop(MAGISKHIDE_PROP);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
auto_start_magiskhide();
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
@@ -768,12 +782,5 @@ core_only:
|
||||
buf = buf2 = NULL;
|
||||
vec_deep_destroy(&module_list);
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Stop recording the boot logcat after every boot task is done
|
||||
kill(debug_log_pid, SIGTERM);
|
||||
waitpid(debug_log_pid, NULL, 0);
|
||||
// Then start to log Magisk verbosely
|
||||
xwrite(debug_log_fd, "\nVerbose logs:\n", 15);
|
||||
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", "-s", "Magisk", NULL);
|
||||
#endif
|
||||
stop_debug_full_log();
|
||||
}
|
||||
|
@@ -21,13 +21,12 @@
|
||||
#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) {
|
||||
// Setup the default error handler for threads
|
||||
err_handler = exit_thread;
|
||||
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
client_request req = read_int(client);
|
||||
@@ -114,25 +113,22 @@ static void *large_sepol_patch(void *args) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void start_daemon(int client) {
|
||||
// Launch the daemon, create new session, set proper context
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "Starting daemon requires root: %s\n", strerror(errno));
|
||||
PLOGE("start daemon");
|
||||
}
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
PLOGE("fork");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
// First close the client, it's useless for us
|
||||
close(client);
|
||||
xsetsid();
|
||||
void start_daemon() {
|
||||
setcon("u:r:su:s0");
|
||||
umask(0);
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
@@ -146,32 +142,36 @@ void start_daemon(int client) {
|
||||
sepol_med_rules();
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
|
||||
// Continue the larger patch in another thread, we will join later
|
||||
pthread_create(&sepol_patch, NULL, large_sepol_patch, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
|
||||
if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun)) == -1)
|
||||
exit(1);
|
||||
xlisten(fd, 10);
|
||||
|
||||
if ((is_restart = access(UNBLOCKFILE, 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();
|
||||
|
||||
// Continue the larger patch in another thread, we will join later
|
||||
xpthread_create(&sepol_patch, NULL, large_sepol_patch, NULL);
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
// The root daemon should not do anything if an error occurs
|
||||
// It should stay intact under any circumstances
|
||||
err_handler = do_nothing;
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
create_links(NULL, "/sbin");
|
||||
xchmod("/sbin", 0755);
|
||||
xmkdir("/magisk", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
// Notifiy init the daemon is started
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
@@ -188,12 +188,21 @@ void start_daemon(int client) {
|
||||
int connect_daemon() {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
/* If we cannot access the daemon, we start the daemon
|
||||
* since there is no clear entry point when the daemon should be started
|
||||
*/
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
start_daemon(fd);
|
||||
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);
|
||||
xsetsid();
|
||||
start_daemon();
|
||||
}
|
||||
|
||||
do {
|
||||
// Wait for 10ms
|
||||
usleep(10);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/* log_monitor.c - New thread to monitor logcat
|
||||
*
|
||||
* Open a new thread to call logcat and get logs with tag "Magisk"
|
||||
* Also, write the logs to a log file for debugging purpose
|
||||
*
|
||||
* A universal logcat monitor for many usages. Add listeners to the list,
|
||||
* and the pointer of the new log line will be sent through pipes to trigger
|
||||
* asynchronous events without polling
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -13,33 +13,146 @@
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
|
||||
int logcat_events[] = { -1, -1, -1 };
|
||||
extern int is_restart;
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
#endif
|
||||
|
||||
static void *logger_thread(void *args) {
|
||||
// Setup error handler
|
||||
err_handler = exit_thread;
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
|
||||
rename(LOGFILE, LASTLOG);
|
||||
int log_fd, log_pid;
|
||||
|
||||
log_fd = xopen(LOGFILE, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
LOGD("log_monitor: logger start");
|
||||
|
||||
while (1) {
|
||||
// Start logcat
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-v", "thread", "Magisk:I", "*:S", NULL);
|
||||
if (log_pid > 0)
|
||||
waitpid(log_pid, NULL, 0);
|
||||
// For some reason it went here, clear buffer and restart
|
||||
exec_command_sync("logcat", "-c", NULL);
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "all" , "-v", "threadtime", "-s", "am_proc_start", "Magisk", NULL);
|
||||
while (fdgets(line, sizeof(line), log_fd)) {
|
||||
for (int i = 0; i < (sizeof(logcat_events) / sizeof(int)); ++i) {
|
||||
if (logcat_events[i] > 0) {
|
||||
char *s = strdup(line);
|
||||
xwrite(logcat_events[i], &s, sizeof(s));
|
||||
}
|
||||
}
|
||||
if (kill(log_pid, 0))
|
||||
break;
|
||||
}
|
||||
// Clear buffer if restart required
|
||||
exec_command_sync("logcat", "-b", "all", "-c", NULL);
|
||||
}
|
||||
|
||||
// Should never be here, but well...
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start a new thread to monitor logcat and dump to logfile */
|
||||
static void *magisk_log_thread(void *args) {
|
||||
int have_data = 0;
|
||||
|
||||
// Temp buffer for logs before we have data access
|
||||
struct vector logs;
|
||||
vec_init(&logs);
|
||||
|
||||
FILE *log;
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
// Register our listener
|
||||
logcat_events[LOG_EVENT] = pipefd[1];
|
||||
|
||||
LOGD("log_monitor: magisk log dumper start");
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
char *ss;
|
||||
if ((ss = strstr(line, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V')) {
|
||||
if (!have_data) {
|
||||
if ((have_data = check_data())) {
|
||||
// Dump buffered logs to file
|
||||
if (!is_restart)
|
||||
rename(LOGFILE, LASTLOG);
|
||||
log = xfopen(LOGFILE, "a");
|
||||
setbuf(log, NULL);
|
||||
char *tmp;
|
||||
vec_for_each(&logs, tmp) {
|
||||
fprintf(log, "%s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
vec_destroy(&logs);
|
||||
} else {
|
||||
vec_push_back(&logs, strdup(line));
|
||||
}
|
||||
}
|
||||
if (have_data)
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *debug_magisk_log_thread(void *args) {
|
||||
FILE *log = xfopen(DEBUG_LOG, "a");
|
||||
setbuf(log, NULL);
|
||||
int pipefd[2];
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return NULL;
|
||||
|
||||
LOGD("log_monitor: debug log dumper start");
|
||||
|
||||
// Register our listener
|
||||
logcat_events[DEBUG_EVENT] = pipefd[1];
|
||||
|
||||
for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) {
|
||||
char *ss;
|
||||
if ((ss = strstr(line, "Magisk")))
|
||||
fprintf(log, "%s", line);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start new threads to monitor logcat and dump to logfile */
|
||||
void monitor_logs() {
|
||||
pthread_t thread;
|
||||
|
||||
// Start log file dumper before monitor
|
||||
xpthread_create(&thread, NULL, magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
|
||||
// Start logcat monitor
|
||||
xpthread_create(&thread, NULL, logger_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
|
||||
}
|
||||
|
||||
void start_debug_full_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Log everything initially
|
||||
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "threadtime", NULL);
|
||||
close(debug_log_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void stop_debug_full_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
// Stop recording the boot logcat after every boot task is done
|
||||
kill(debug_log_pid, SIGTERM);
|
||||
waitpid(debug_log_pid, NULL, 0);
|
||||
pthread_t thread;
|
||||
// Start debug thread
|
||||
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
start_debug_log();
|
||||
#endif
|
||||
}
|
||||
|
||||
void start_debug_log() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
pthread_t thread;
|
||||
// Start debug thread
|
||||
xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
#endif
|
||||
}
|
||||
|
@@ -32,10 +32,6 @@ int create_links(const char *bin, const char *path) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Global error hander function
|
||||
// Should be changed each thread/process
|
||||
__thread void (*err_handler)(void);
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr,
|
||||
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
|
||||
@@ -54,11 +50,12 @@ static void usage() {
|
||||
" --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n"
|
||||
" --mountimg IMG PATH mount IMG to PATH and prints the loop device\n"
|
||||
" --umountimg PATH LOOP unmount PATH and delete LOOP device\n"
|
||||
" --[boot stage] start boot stage service\n"
|
||||
" --[init service] start init service\n"
|
||||
" --unlock-blocks set BLKROSET flag to OFF for all block devices\n"
|
||||
" --restorecon fix selinux context on Magisk files and folders\n"
|
||||
"\n"
|
||||
"Supported boot stages:\n"
|
||||
" post-fs, post-fs-data, service\n"
|
||||
"Supported init services:\n"
|
||||
" daemon post-fs, post-fs-data, service\n"
|
||||
"\n"
|
||||
"Supported applets:\n"
|
||||
, argv0, argv0);
|
||||
@@ -71,8 +68,6 @@ static void usage() {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
argv0 = argv[0];
|
||||
// Exit the whole app if error occurs by default
|
||||
err_handler = exit_proc;
|
||||
char * arg = strrchr(argv[0], '/');
|
||||
if (arg) ++arg;
|
||||
else arg = argv[0];
|
||||
@@ -146,6 +141,12 @@ int main(int argc, char *argv[]) {
|
||||
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
|
||||
unlock_blocks();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--restorecon") == 0) {
|
||||
fix_filecon();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--daemon") == 0) {
|
||||
// Start daemon, this process won't return
|
||||
start_daemon();
|
||||
} else if (strcmp(argv[1], "--post-fs") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS);
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <pthread.h>
|
||||
|
||||
extern pthread_t sepol_patch;
|
||||
extern int is_restart;
|
||||
|
||||
// Commands require connecting to daemon
|
||||
typedef enum {
|
||||
@@ -38,8 +39,9 @@ typedef enum {
|
||||
|
||||
// daemon.c
|
||||
|
||||
void start_daemon(int client);
|
||||
void start_daemon();
|
||||
int connect_daemon();
|
||||
void auto_start_magiskhide();
|
||||
|
||||
// socket_trans.c
|
||||
|
||||
@@ -50,10 +52,6 @@ void write_int(int fd, int val);
|
||||
char* read_string(int fd);
|
||||
void write_string(int fd, const char* val);
|
||||
|
||||
// log_monitor.c
|
||||
|
||||
void monitor_logs();
|
||||
|
||||
/***************
|
||||
* Boot Stages *
|
||||
***************/
|
||||
@@ -61,6 +59,7 @@ void monitor_logs();
|
||||
void post_fs(int client);
|
||||
void post_fs_data(int client);
|
||||
void late_start(int client);
|
||||
void fix_filecon();
|
||||
|
||||
/**************
|
||||
* MagiskHide *
|
||||
|
@@ -15,14 +15,6 @@
|
||||
|
||||
#define LOG_TAG "Magisk"
|
||||
|
||||
// Global handler for PLOGE
|
||||
extern __thread void (*err_handler)(void);
|
||||
|
||||
// Common error handlers
|
||||
static inline void exit_proc() { exit(1); }
|
||||
static inline void exit_thread() { pthread_exit(NULL); }
|
||||
static inline void do_nothing() {}
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
@@ -32,7 +24,19 @@ static inline void do_nothing() {}
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#define PLOGE(fmt, args...) { LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler(); }
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
|
||||
enum {
|
||||
HIDE_EVENT,
|
||||
LOG_EVENT,
|
||||
DEBUG_EVENT
|
||||
};
|
||||
extern int logcat_events[];
|
||||
|
||||
void monitor_logs();
|
||||
void start_debug_full_log();
|
||||
void stop_debug_full_log();
|
||||
void start_debug_log();
|
||||
|
||||
#else // IS_DAEMON
|
||||
|
||||
|
@@ -12,9 +12,11 @@ int prop_exist(const char *name);
|
||||
int setprop(const char *name, const char *value);
|
||||
int setprop2(const char *name, const char *value, const int trigger);
|
||||
char *getprop(const char *name);
|
||||
int deleteprop(const char *name, const int trigger);
|
||||
char *getprop2(const char *name, int persist);
|
||||
int deleteprop(const char *name);
|
||||
int deleteprop2(const char *name, const int persist);
|
||||
int read_prop_file(const char* filename, const int trigger);
|
||||
void getprop_all(void (*cbk)(const char *name));
|
||||
void getprop_all(void (*callback)(const char*, const char*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -18,8 +16,6 @@
|
||||
#define UID_SYSTEM (get_system_uid())
|
||||
#define UID_RADIO (get_radio_uid())
|
||||
|
||||
extern int quit_signals[];
|
||||
|
||||
// xwrap.c
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
@@ -28,12 +24,14 @@ FILE *xfdopen(int fd, const char *mode);
|
||||
#define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
|
||||
int xopen2(const char *pathname, int flags);
|
||||
int xopen3(const char *pathname, int flags, mode_t mode);
|
||||
int xopenat(int dirfd, const char *pathname, int flags);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
int xpipe2(int pipefd[2], int flags);
|
||||
int xsetns(int fd, int nstype);
|
||||
DIR *xopendir(const char *name);
|
||||
DIR *xfdopendir(int fd);
|
||||
struct dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xsocket(int domain, int type, int protocol);
|
||||
@@ -53,22 +51,26 @@ int xstat(const char *pathname, struct stat *buf);
|
||||
int xlstat(const char *pathname, struct stat *buf);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount(const char *target);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xchmod(const char *pathname, mode_t mode);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdir_p(const char *pathname, mode_t mode);
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
int xmkdir_p(const char *pathname, mode_t mode);
|
||||
pid_t xfork();
|
||||
|
||||
// misc.c
|
||||
|
||||
extern int quit_signals[];
|
||||
|
||||
unsigned get_shell_uid();
|
||||
unsigned get_system_uid();
|
||||
unsigned get_radio_uid();
|
||||
@@ -82,14 +84,38 @@ void unlock_blocks();
|
||||
void setup_sighandlers(void (*handler)(int));
|
||||
int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char *argv0, ...);
|
||||
int exec_command_sync(char *const argv0, ...);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
int bind_mount(const char *from, const char *to);
|
||||
int open_new(const char *filename);
|
||||
int cp_afc(const char *source, const char *target);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void get_client_cred(int fd, struct ucred *cred);
|
||||
int switch_mnt_ns(int pid);
|
||||
int fork_dont_care();
|
||||
|
||||
// file.c
|
||||
|
||||
extern char **excl_list;
|
||||
|
||||
struct file_attr {
|
||||
struct stat st;
|
||||
char con[128];
|
||||
};
|
||||
|
||||
int fd_getpath(int fd, char *path, size_t size);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
void rm_rf(const char *path);
|
||||
void frm_rf(int dirfd);
|
||||
void mv_f(const char *source, const char *destination);
|
||||
void mv_dir(int src, int dest);
|
||||
void cp_afc(const char *source, const char *destination);
|
||||
void clone_dir(int src, int dest);
|
||||
int getattr(const char *path, struct file_attr *a);
|
||||
int getattrat(int dirfd, const char *pathname, struct file_attr *a);
|
||||
int fgetattr(int fd, struct file_attr *a);
|
||||
int setattr(const char *path, struct file_attr *a);
|
||||
int setattrat(int dirfd, const char *pathname, struct file_attr *a);
|
||||
int fsetattr(int fd, struct file_attr *a);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void restorecon(int dirfd, int force);
|
||||
|
||||
// img.c
|
||||
|
||||
|
@@ -8,7 +8,7 @@ void mmap_ro(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
*buf = *size > 0 ? xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0) : NULL;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ void mmap_rw(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDWR);
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
*buf = *size > 0 ? xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) : NULL;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ static void print_hdr(const boot_img_hdr *hdr) {
|
||||
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr->kernel_size, hdr->kernel_addr);
|
||||
fprintf(stderr, "RAMDISK [%d] @ 0x%08x\n", hdr->ramdisk_size, hdr->ramdisk_addr);
|
||||
fprintf(stderr, "SECOND [%d] @ 0x%08x\n", hdr->second_size, hdr->second_addr);
|
||||
fprintf(stderr, "DTB [%d] @ 0x%08x\n", hdr->dt_size, hdr->tags_addr);
|
||||
fprintf(stderr, "EXTRA [%d] @ 0x%08x\n", hdr->extra_size, hdr->tags_addr);
|
||||
fprintf(stderr, "PAGESIZE [%d]\n", hdr->page_size);
|
||||
if (hdr->os_version != 0) {
|
||||
int a,b,c,y,m = 0;
|
||||
@@ -88,26 +88,24 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (boot->hdr.dt_size) {
|
||||
boot->dtb = base + pos;
|
||||
pos += boot->hdr.dt_size;
|
||||
if (boot->hdr.extra_size) {
|
||||
boot->extra = base + pos;
|
||||
pos += boot->hdr.extra_size;
|
||||
mem_align(&pos, boot->hdr.page_size);
|
||||
}
|
||||
|
||||
if (pos < size) {
|
||||
boot->extra = base + pos;
|
||||
boot->tail = base + pos;
|
||||
boot->tail_size = end - base - pos;
|
||||
}
|
||||
|
||||
// Search for dtb in kernel if not found
|
||||
if (boot->hdr.dt_size == 0) {
|
||||
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
|
||||
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
|
||||
boot->flags |= APPEND_DTB;
|
||||
boot->dtb = boot->kernel + i;
|
||||
boot->hdr.dt_size = boot->hdr.kernel_size - i;
|
||||
boot->hdr.kernel_size = i;
|
||||
fprintf(stderr, "APPEND_DTB [%d]\n", boot->hdr.dt_size);
|
||||
}
|
||||
// Search for dtb in kernel
|
||||
for (int i = 0; i < boot->hdr.kernel_size; ++i) {
|
||||
if (memcmp(boot->kernel + i, DTB_MAGIC, 4) == 0) {
|
||||
boot->dtb = boot->kernel + i;
|
||||
boot->dt_size = boot->hdr.kernel_size - i;
|
||||
boot->hdr.kernel_size = i;
|
||||
fprintf(stderr, "DTB [%d]\n", boot->dt_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +144,7 @@ int parse_img(void *orig, size_t size, boot_img *boot) {
|
||||
}
|
||||
}
|
||||
LOGE("No boot image magic found!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void unpack(const char* image) {
|
||||
@@ -160,22 +159,27 @@ void unpack(const char* image) {
|
||||
int ret = parse_img(orig, size, &boot);
|
||||
|
||||
// Dump kernel
|
||||
if (boot.kernel_type == UNKNOWN) {
|
||||
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE);
|
||||
} else {
|
||||
if (COMPRESSED(boot.kernel_type)) {
|
||||
fd = open_new(KERNEL_FILE);
|
||||
decomp(boot.kernel_type, fd, boot.kernel, boot.hdr.kernel_size);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.kernel, boot.hdr.kernel_size, KERNEL_FILE);
|
||||
}
|
||||
|
||||
if (boot.dt_size) {
|
||||
// Dump dtb
|
||||
dump(boot.dtb, boot.dt_size, DTB_FILE);
|
||||
}
|
||||
|
||||
// Dump ramdisk
|
||||
if (boot.ramdisk_type == UNKNOWN) {
|
||||
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw");
|
||||
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
|
||||
} else {
|
||||
if (COMPRESSED(boot.ramdisk_type)) {
|
||||
fd = open_new(RAMDISK_FILE);
|
||||
decomp(boot.ramdisk_type, fd, boot.ramdisk, boot.hdr.ramdisk_size);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.ramdisk, boot.hdr.ramdisk_size, RAMDISK_FILE ".raw");
|
||||
LOGE("Unknown ramdisk format! Dumped to %s\n", RAMDISK_FILE ".raw");
|
||||
}
|
||||
|
||||
if (boot.hdr.second_size) {
|
||||
@@ -183,9 +187,9 @@ void unpack(const char* image) {
|
||||
dump(boot.second, boot.hdr.second_size, SECOND_FILE);
|
||||
}
|
||||
|
||||
if (boot.hdr.dt_size) {
|
||||
// Dump dtb
|
||||
dump(boot.dtb, boot.hdr.dt_size, DTB_FILE);
|
||||
if (boot.hdr.extra_size) {
|
||||
// Dump extra
|
||||
dump(boot.extra, boot.hdr.extra_size, EXTRA_FILE);
|
||||
}
|
||||
|
||||
munmap(orig, size);
|
||||
@@ -220,18 +224,18 @@ void repack(const char* orig_image, const char* out_image) {
|
||||
mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
|
||||
write_zero(fd, 512);
|
||||
}
|
||||
if (boot.kernel_type == UNKNOWN) {
|
||||
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
|
||||
} else {
|
||||
if (COMPRESSED(boot.kernel_type)) {
|
||||
size_t raw_size;
|
||||
void *kernel_raw;
|
||||
mmap_ro(KERNEL_FILE, &kernel_raw, &raw_size);
|
||||
boot.hdr.kernel_size = comp(boot.kernel_type, fd, kernel_raw, raw_size);
|
||||
munmap(kernel_raw, raw_size);
|
||||
} else {
|
||||
boot.hdr.kernel_size = restore(KERNEL_FILE, fd);
|
||||
}
|
||||
if (boot.flags & APPEND_DTB) {
|
||||
// Restore dtb
|
||||
if (boot.dt_size && access(DTB_FILE, R_OK) == 0) {
|
||||
boot.hdr.kernel_size += restore(DTB_FILE, fd);
|
||||
boot.hdr.dt_size = 0;
|
||||
}
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
|
||||
@@ -270,18 +274,17 @@ void repack(const char* orig_image, const char* out_image) {
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Restore dtb
|
||||
if (boot.hdr.dt_size && access(DTB_FILE, R_OK) == 0) {
|
||||
printf("Here\n");
|
||||
boot.hdr.dt_size = restore(DTB_FILE, fd);
|
||||
// Restore extra
|
||||
if (boot.hdr.extra_size && access(EXTRA_FILE, R_OK) == 0) {
|
||||
boot.hdr.extra_size = restore(EXTRA_FILE, fd);
|
||||
file_align(fd, boot.hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Check extra info, currently only for LG Bump and Samsung SEANDROIDENFORCE
|
||||
if (boot.extra) {
|
||||
if (memcmp(boot.extra, "SEANDROIDENFORCE", 16) == 0 ||
|
||||
memcmp(boot.extra, LG_BUMP_MAGIC, 16) == 0 ) {
|
||||
restore_buf(fd, boot.extra, 16);
|
||||
// Check tail info, currently only for LG Bump and Samsung SEANDROIDENFORCE
|
||||
if (boot.tail_size >= 16) {
|
||||
if (memcmp(boot.tail, "SEANDROIDENFORCE", 16) == 0 ||
|
||||
memcmp(boot.tail, LG_BUMP_MAGIC, 16) == 0 ) {
|
||||
restore_buf(fd, boot.tail, 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,7 @@ struct boot_img_hdr
|
||||
|
||||
uint32_t tags_addr; /* physical addr for kernel tags */
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t dt_size; /* device tree in bytes */
|
||||
uint32_t extra_size; /* extra blob size in bytes */
|
||||
|
||||
/* operating system version and security patch level; for
|
||||
* version "A.B.C" and patch level "Y-M-D":
|
||||
@@ -74,13 +74,13 @@ struct boot_img_hdr
|
||||
** +-----------------+
|
||||
** | second stage | o pages
|
||||
** +-----------------+
|
||||
** | device tree | p pages
|
||||
** | extra blobs | p pages
|
||||
** +-----------------+
|
||||
**
|
||||
** n = (kernel_size + page_size - 1) / page_size
|
||||
** m = (ramdisk_size + page_size - 1) / page_size
|
||||
** o = (second_size + page_size - 1) / page_size
|
||||
** p = (dt_size + page_size - 1) / page_size
|
||||
** p = (extra_size + page_size - 1) / page_size
|
||||
**
|
||||
** 0. all entities are page_size aligned in flash
|
||||
** 1. kernel and ramdisk are required (size != 0)
|
||||
@@ -103,16 +103,18 @@ typedef struct mtk_hdr {
|
||||
// Flags
|
||||
#define MTK_KERNEL 0x1
|
||||
#define MTK_RAMDISK 0x2
|
||||
#define APPEND_DTB 0x4
|
||||
|
||||
typedef struct boot_img {
|
||||
boot_img_hdr hdr;
|
||||
void *kernel;
|
||||
void *dtb;
|
||||
uint32_t dt_size;
|
||||
void *ramdisk;
|
||||
void *second;
|
||||
void *dtb;
|
||||
void *extra;
|
||||
int flags;
|
||||
void *tail;
|
||||
uint32_t tail_size;
|
||||
uint32_t flags;
|
||||
file_t kernel_type, ramdisk_type;
|
||||
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
|
||||
} boot_img;
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
#include <lz4hc.h>
|
||||
#include <bzlib.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
@@ -37,8 +38,6 @@ size_t gzip(int mode, int fd, const void *buf, size_t size) {
|
||||
case 1:
|
||||
ret = deflateInit2(&strm, 9, Z_DEFLATED, windowBits | ZLIB_GZIP, memLevel, Z_DEFAULT_STRATEGY);
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported gzip mode!\n");
|
||||
}
|
||||
|
||||
if (ret != Z_OK)
|
||||
@@ -98,7 +97,7 @@ size_t lzma(int mode, int fd, const void *buf, size_t size) {
|
||||
unsigned char out[BUFSIZ];
|
||||
|
||||
// Initialize preset
|
||||
lzma_lzma_preset(&opt, LZMA_PRESET_DEFAULT);
|
||||
lzma_lzma_preset(&opt, 9);
|
||||
lzma_filter filters[] = {
|
||||
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
|
||||
{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
|
||||
@@ -114,8 +113,6 @@ size_t lzma(int mode, int fd, const void *buf, size_t size) {
|
||||
case 2:
|
||||
ret = lzma_alone_encoder(&strm, &opt);
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported lzma mode!\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -168,8 +165,6 @@ size_t lz4(int mode, int fd, const void *buf, size_t size) {
|
||||
case 1:
|
||||
ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported lz4 mode!\n");
|
||||
}
|
||||
|
||||
if (LZ4F_isError(ret))
|
||||
@@ -283,8 +278,6 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
|
||||
case 1:
|
||||
ret = BZ2_bzCompressInit(&strm, 9, 0, 0);
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported bzip2 mode!\n");
|
||||
}
|
||||
|
||||
if (ret != BZ_OK)
|
||||
@@ -333,11 +326,10 @@ size_t bzip2(int mode, int fd, const void* buf, size_t size) {
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
|
||||
size_t pos = 0, total = 0;
|
||||
size_t pos = 0;
|
||||
int have;
|
||||
char *out;
|
||||
unsigned block_size, insize;
|
||||
unsigned char block_size_le[4];
|
||||
unsigned block_size, insize, total = 0;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
@@ -350,22 +342,17 @@ size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
|
||||
// Write magic
|
||||
total += xwrite(fd, "\x02\x21\x4c\x18", 4);
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported lz4_legacy mode!\n");
|
||||
}
|
||||
|
||||
do {
|
||||
const char *buff = buf;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
block_size = buff[pos];
|
||||
block_size += (buff[pos + 1]<<8);
|
||||
block_size += (buff[pos + 2]<<16);
|
||||
block_size += ((unsigned)buff[pos + 3])<<24;
|
||||
// Read block size
|
||||
block_size = *(unsigned *)(buf + pos);
|
||||
pos += 4;
|
||||
if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE))
|
||||
LOGE("lz4_legacy block size too large!\n");
|
||||
have = LZ4_decompress_safe((const char*) (buf + pos), out, block_size, LZ4_LEGACY_BLOCKSIZE);
|
||||
goto done;
|
||||
have = LZ4_decompress_safe(buf + pos, out, block_size, LZ4_LEGACY_BLOCKSIZE);
|
||||
if (have < 0)
|
||||
LOGE("Cannot decode lz4_legacy block\n");
|
||||
pos += block_size;
|
||||
@@ -375,21 +362,24 @@ size_t lz4_legacy(int mode, int fd, const void* buf, size_t size) {
|
||||
insize = size - pos;
|
||||
else
|
||||
insize = LZ4_LEGACY_BLOCKSIZE;
|
||||
have = LZ4_compress_default((const char*) (buf + pos), out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE));
|
||||
have = LZ4_compress_HC(buf + pos, out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE), 9);
|
||||
if (have == 0)
|
||||
LOGE("lz4_legacy compression error\n");
|
||||
pos += insize;
|
||||
block_size_le[0] = have & 0xff;
|
||||
block_size_le[1] = (have >> 8) & 0xff;
|
||||
block_size_le[2] = (have >> 16) & 0xff;
|
||||
block_size_le[3] = (have >> 24) & 0xff;
|
||||
total += xwrite(fd, block_size_le, 4);
|
||||
// Write block size
|
||||
total += xwrite(fd, &have, sizeof(have));
|
||||
break;
|
||||
}
|
||||
// Write main data
|
||||
total += xwrite(fd, out, have);
|
||||
} while(pos < size);
|
||||
|
||||
done:
|
||||
if (mode == 1) {
|
||||
// Append original size to output
|
||||
unsigned uncomp = size;
|
||||
xwrite(fd, &uncomp, sizeof(uncomp));
|
||||
}
|
||||
free(out);
|
||||
return total;
|
||||
}
|
||||
|
@@ -5,16 +5,14 @@
|
||||
#include "magiskboot.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Left here for debugging */
|
||||
|
||||
// static void print_subnode(const void *fdt, int parent, int depth) {
|
||||
// int node;
|
||||
// fdt_for_each_subnode(node, fdt, parent) {
|
||||
// for (int i = 0; i < depth; ++i) printf(" ");
|
||||
// printf("%d: %s\n", node, fdt_get_name(fdt, node, NULL));
|
||||
// print_subnode(fdt, node, depth + 1);
|
||||
// }
|
||||
// }
|
||||
static void print_subnode(const void *fdt, int parent, int depth) {
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, parent) {
|
||||
for (int i = 0; i < depth; ++i) printf(" ");
|
||||
printf("%d: %s\n", node, fdt_get_name(fdt, node, NULL));
|
||||
print_subnode(fdt, node, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_fstab(const void *fdt, int parent) {
|
||||
int node, fstab;
|
||||
@@ -28,6 +26,25 @@ static int find_fstab(const void *fdt, int parent) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void dtb_print(const char *file) {
|
||||
size_t size ;
|
||||
void *dtb, *fdt;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
mmap_ro(file, &dtb, &size);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
fdt = dtb + i;
|
||||
fprintf(stderr, "\nPrinting dtb.%04d\n\n", dtb_num++);
|
||||
print_subnode(fdt, 0, 0);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
munmap(dtb, size);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void dtb_patch(const char *file) {
|
||||
size_t size ;
|
||||
void *dtb, *fdt;
|
||||
|
@@ -24,7 +24,7 @@ void hexpatch(const char *image, const char *from, const char *to) {
|
||||
patch = xmalloc(patchsize);
|
||||
hex2byte(from, pattern);
|
||||
hex2byte(to, patch);
|
||||
for (size_t i = 0; i < filesize - patternsize; ++i) {
|
||||
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
|
||||
if (memcmp(file + i, pattern, patternsize) == 0) {
|
||||
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
|
||||
memset(file + i, 0, patternsize);
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define KERNEL_FILE "kernel"
|
||||
#define RAMDISK_FILE "ramdisk.cpio"
|
||||
#define SECOND_FILE "second"
|
||||
#define EXTRA_FILE "extra"
|
||||
#define DTB_FILE "dtb"
|
||||
#define NEW_BOOT "new-boot.img"
|
||||
|
||||
@@ -22,6 +23,7 @@ int parse_img(void *orig, size_t size, boot_img *boot);
|
||||
int cpio_commands(const char *command, int argc, char *argv[]);
|
||||
void comp_file(const char *method, const char *from, const char *to);
|
||||
void decomp_file(char *from, const char *to);
|
||||
void dtb_print(const char *file);
|
||||
void dtb_patch(const char *file);
|
||||
|
||||
// Compressions
|
||||
|
@@ -54,6 +54,9 @@ static void usage(char *arg0) {
|
||||
" -stocksha1\n"
|
||||
" Get stock boot SHA1 recorded within <incpio>\n"
|
||||
"\n"
|
||||
" --dtb-print <dtb>\n"
|
||||
" Print all nodes in <dtb>, for debugging\n"
|
||||
"\n"
|
||||
" --dtb-patch <dtb>\n"
|
||||
" Search for fstab in <dtb> and remove verity checks\n"
|
||||
"\n"
|
||||
@@ -94,6 +97,7 @@ int main(int argc, char *argv[]) {
|
||||
unlink(RAMDISK_FILE ".raw");
|
||||
unlink(SECOND_FILE);
|
||||
unlink(DTB_FILE);
|
||||
unlink(EXTRA_FILE);
|
||||
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
|
||||
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
|
||||
unlink(name);
|
||||
@@ -113,6 +117,8 @@ int main(int argc, char *argv[]) {
|
||||
repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) {
|
||||
decomp_file(argv[2], argc > 3 ? argv[3] : NULL);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--dtb-print") == 0) {
|
||||
dtb_print(argv[2]);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--dtb-patch") == 0) {
|
||||
dtb_patch(argv[2]);
|
||||
} else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) {
|
||||
|
@@ -18,6 +18,8 @@ typedef enum {
|
||||
DTB
|
||||
} file_t;
|
||||
|
||||
#define COMPRESSED(type) (type >= GZIP && type <= LZ4_LEGACY)
|
||||
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define ELF32_MAGIC "\x7f""ELF\x01"
|
||||
#define ELF64_MAGIC "\x7f""ELF\x02"
|
||||
|
@@ -58,9 +58,9 @@ void hide_sensitive_props() {
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_magisk_prop(const char *name) {
|
||||
static void rm_magisk_prop(const char *name, const char *value) {
|
||||
if (strstr(name, "magisk")) {
|
||||
deleteprop(name, 0);
|
||||
deleteprop2(name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,42 +69,6 @@ void clean_magisk_props() {
|
||||
getprop_all(rm_magisk_prop);
|
||||
}
|
||||
|
||||
void relink_sbin() {
|
||||
struct stat st;
|
||||
if (stat("/sbin_orig", &st) == -1 && errno == ENOENT) {
|
||||
// Re-link all binaries and bind mount
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char from[PATH_MAX], to[PATH_MAX];
|
||||
|
||||
LOGI("hide_utils: Re-linking /sbin\n");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xrename("/sbin", "/sbin_orig");
|
||||
xmkdir("/sbin", 0755);
|
||||
xchmod("/sbin", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
xmkdir("/dev/sbin_bind", 0755);
|
||||
xchmod("/dev/sbin_bind", 0755);
|
||||
dir = xopendir("/sbin_orig");
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(from, sizeof(from), "/sbin_orig/%s", entry->d_name);
|
||||
if (entry->d_type == DT_LNK)
|
||||
xreadlink(from, from, sizeof(from));
|
||||
snprintf(to, sizeof(to), "/dev/sbin_bind/%s", entry->d_name);
|
||||
symlink(from, to);
|
||||
lsetfilecon(to, "u:object_r:rootfs:s0");
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
xmount("/dev/sbin_bind", "/sbin", NULL, MS_BIND, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int add_list(char *proc) {
|
||||
if (!hideEnabled) {
|
||||
free(proc);
|
||||
@@ -223,7 +187,6 @@ int destroy_list() {
|
||||
}
|
||||
|
||||
void add_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, add_list(proc));
|
||||
@@ -231,7 +194,6 @@ void add_hide_list(int client) {
|
||||
}
|
||||
|
||||
void rm_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, rm_list(proc));
|
||||
@@ -239,7 +201,6 @@ void rm_hide_list(int client) {
|
||||
}
|
||||
|
||||
void ls_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
if (!hideEnabled) {
|
||||
write_int(client, HIDE_NOT_ENABLED);
|
||||
return;
|
||||
|
@@ -41,9 +41,6 @@ static void usage(char *arg0) {
|
||||
}
|
||||
|
||||
void launch_magiskhide(int client) {
|
||||
// We manually handle crashes
|
||||
err_handler = do_nothing;
|
||||
|
||||
if (hideEnabled) {
|
||||
if (client > 0) {
|
||||
write_int(client, HIDE_IS_ENABLED);
|
||||
@@ -55,7 +52,7 @@ void launch_magiskhide(int client) {
|
||||
hideEnabled = 1;
|
||||
LOGI("* Starting MagiskHide\n");
|
||||
|
||||
deleteprop(MAGISKHIDE_PROP, 1);
|
||||
deleteprop2(MAGISKHIDE_PROP, 1);
|
||||
|
||||
hide_sensitive_props();
|
||||
|
||||
@@ -102,7 +99,7 @@ void stop_magiskhide(int client) {
|
||||
hideEnabled = 0;
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
// Remove without actually removing persist props
|
||||
deleteprop(MAGISKHIDE_PROP, 0);
|
||||
deleteprop2(MAGISKHIDE_PROP, 0);
|
||||
pthread_kill(proc_monitor_thread, SIGUSR1);
|
||||
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
@@ -124,6 +121,8 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
req = RM_HIDELIST;
|
||||
} else if (strcmp(argv[1], "--ls") == 0) {
|
||||
req = LS_HIDELIST;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, req);
|
||||
|
@@ -12,7 +12,6 @@ void proc_monitor();
|
||||
// Utility functions
|
||||
void manage_selinux();
|
||||
void hide_sensitive_props();
|
||||
void relink_sbin();
|
||||
void clean_magisk_props();
|
||||
|
||||
// List managements
|
||||
|
@@ -19,38 +19,25 @@
|
||||
#include "utils.h"
|
||||
#include "magiskhide.h"
|
||||
|
||||
static int zygote_num;
|
||||
static char init_ns[32], zygote_ns[2][32], cache_block[256];
|
||||
static int log_pid, log_fd, target_pid, has_cache = 1;
|
||||
static char *buffer;
|
||||
static int zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
|
||||
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void quit_pthread(int sig) {
|
||||
err_handler = do_nothing;
|
||||
LOGD("proc_monitor: running cleanup\n");
|
||||
destroy_list();
|
||||
free(buffer);
|
||||
hideEnabled = 0;
|
||||
// Kill the logging if needed
|
||||
if (log_pid > 0) {
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
close(log_fd);
|
||||
}
|
||||
// Resume process if possible
|
||||
if (target_pid > 0)
|
||||
kill(target_pid, SIGCONT);
|
||||
// Unregister listener
|
||||
logcat_events[HIDE_EVENT] = -1;
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
pipefd[0] = pipefd[1] = -1;
|
||||
pthread_mutex_destroy(&hide_lock);
|
||||
pthread_mutex_destroy(&file_lock);
|
||||
LOGD("proc_monitor: terminating...\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void proc_monitor_err() {
|
||||
LOGD("proc_monitor: error occured, stopping magiskhide services\n");
|
||||
quit_pthread(SIGUSR1);
|
||||
}
|
||||
|
||||
static int read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
@@ -76,27 +63,19 @@ static void lazy_unmount(const char* mountpoint) {
|
||||
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
static void hide_daemon_err() {
|
||||
LOGD("hide_daemon: error occured, stopping magiskhide services\n");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
static void hide_daemon(int pid) {
|
||||
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
|
||||
// When an error occurs, report its failure to main process
|
||||
err_handler = hide_daemon_err;
|
||||
|
||||
char *line;
|
||||
char *line, buffer[PATH_MAX];
|
||||
struct vector mount_list;
|
||||
|
||||
manage_selinux();
|
||||
relink_sbin();
|
||||
clean_magisk_props();
|
||||
|
||||
if (switch_mnt_ns(pid))
|
||||
return;
|
||||
goto exit;
|
||||
|
||||
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid);
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
@@ -133,7 +112,7 @@ static void hide_daemon(int pid) {
|
||||
vec_destroy(&mount_list);
|
||||
|
||||
// Re-read mount infos
|
||||
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid);
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
@@ -146,8 +125,12 @@ static void hide_daemon(int pid) {
|
||||
free(line);
|
||||
}
|
||||
|
||||
// Free uo memory
|
||||
exit:
|
||||
// Send resume signal
|
||||
kill(pid, SIGCONT);
|
||||
// Free up memory
|
||||
vec_destroy(&mount_list);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
void proc_monitor() {
|
||||
@@ -157,17 +140,12 @@ void proc_monitor() {
|
||||
act.sa_handler = quit_pthread;
|
||||
sigaction(SIGUSR1, &act, NULL);
|
||||
|
||||
// The error handler should stop magiskhide services
|
||||
err_handler = proc_monitor_err;
|
||||
log_pid = target_pid = -1;
|
||||
|
||||
buffer = xmalloc(PATH_MAX);
|
||||
cache_block[0] = '\0';
|
||||
|
||||
// Get the mount namespace of init
|
||||
if (read_namespace(1, init_ns, 32)) {
|
||||
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
|
||||
proc_monitor_err();
|
||||
quit_pthread(SIGUSR1);
|
||||
}
|
||||
LOGI("proc_monitor: init ns=%s\n", init_ns);
|
||||
|
||||
@@ -189,20 +167,15 @@ void proc_monitor() {
|
||||
break;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
// Clear previous logcat buffer
|
||||
exec_command_sync("logcat", "-b", "events", "-c", NULL);
|
||||
// Register our listener to logcat monitor
|
||||
xpipe2(pipefd, O_CLOEXEC);
|
||||
logcat_events[HIDE_EVENT] = pipefd[1];
|
||||
|
||||
// Monitor am_proc_start
|
||||
log_fd = -1;
|
||||
log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-v", "raw", "-s", "am_proc_start", NULL);
|
||||
|
||||
if (log_pid < 0) continue;
|
||||
if (kill(log_pid, 0)) continue;
|
||||
|
||||
while(fdgets(buffer, PATH_MAX, log_fd)) {
|
||||
for (char *log, *line; xxread(pipefd[0], &log, sizeof(log)) > 0; free(log)) {
|
||||
char *ss;
|
||||
if ((ss = strstr(log, "am_proc_start")) && (ss = strchr(ss, '['))) {
|
||||
int pid, ret, comma = 0;
|
||||
char *pos = buffer, *line, processName[256];
|
||||
char *pos = ss, processName[256], ns[32];
|
||||
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
@@ -213,9 +186,9 @@ void proc_monitor() {
|
||||
}
|
||||
|
||||
if (comma == 6)
|
||||
ret = sscanf(buffer, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
else
|
||||
ret = sscanf(buffer, "[%*d %d %*d %256s", &pid, processName);
|
||||
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
@@ -226,12 +199,11 @@ void proc_monitor() {
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
target_pid = pid;
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(target_pid, buffer, 32);
|
||||
if (strcmp(buffer, zygote_ns[i]) == 0) {
|
||||
read_namespace(pid, ns, sizeof(ns));
|
||||
if (strcmp(ns, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
@@ -241,43 +213,21 @@ void proc_monitor() {
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(target_pid, SIGSTOP) == -1) continue;
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, target_pid, buffer);
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
int hide_pid = fork();
|
||||
switch(hide_pid) {
|
||||
case -1:
|
||||
PLOGE("fork");
|
||||
return;
|
||||
case 0:
|
||||
hide_daemon(target_pid);
|
||||
_exit(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (fork_dont_care() == 0)
|
||||
hide_daemon(pid);
|
||||
|
||||
// Wait till the unmount process is done
|
||||
waitpid(hide_pid, &ret, 0);
|
||||
if (WEXITSTATUS(ret))
|
||||
quit_pthread(SIGUSR1);
|
||||
|
||||
// All done, send resume signal
|
||||
kill(target_pid, SIGCONT);
|
||||
target_pid = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
}
|
||||
|
||||
// For some reason it went here, restart logging
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
close(log_fd);
|
||||
}
|
||||
}
|
||||
|
137
jni/magiskinit.c
137
jni/magiskinit.c
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <cil/cil.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
struct cmdline {
|
||||
@@ -46,114 +47,6 @@ extern void mmap_ro(const char *filename, void **buf, size_t *size);
|
||||
extern void mmap_rw(const char *filename, void **buf, size_t *size);
|
||||
extern void *patch_init_rc(char *data, uint32_t *size);
|
||||
|
||||
static void clone_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int srcfd, destfd, newsrc, newdest;
|
||||
struct stat st;
|
||||
char buf[PATH_MAX];
|
||||
ssize_t size;
|
||||
|
||||
dir = fdopendir(src);
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
mkdirat(dest, entry->d_name, st.st_mode & 0777);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
|
||||
// Don't clone recursive if it's /system
|
||||
if (strcmp(entry->d_name, "system") == 0)
|
||||
continue;
|
||||
newsrc = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = openat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
clone_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
break;
|
||||
case DT_REG:
|
||||
destfd = openat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, st.st_mode & 0777);
|
||||
srcfd = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
sendfile(destfd, srcfd, 0, st.st_size);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
|
||||
close(destfd);
|
||||
close(srcfd);
|
||||
break;
|
||||
case DT_LNK:
|
||||
size = readlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
buf[size] = '\0';
|
||||
symlinkat(buf, dest, entry->d_name);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mv_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int newsrc, newdest;
|
||||
struct stat st;
|
||||
char buf[PATH_MAX];
|
||||
ssize_t size;
|
||||
|
||||
dir = fdopendir(src);
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
mkdirat(dest, entry->d_name, st.st_mode & 0777);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
|
||||
newsrc = openat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = openat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
mv_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
break;
|
||||
case DT_REG:
|
||||
renameat(src, entry->d_name, dest, entry->d_name);
|
||||
fchmodat(dest, entry->d_name, st.st_mode & 0777, 0);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, 0);
|
||||
break;
|
||||
case DT_LNK:
|
||||
size = readlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
buf[size] = '\0';
|
||||
symlinkat(buf, dest, entry->d_name);
|
||||
fchownat(dest, entry->d_name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW);
|
||||
break;
|
||||
}
|
||||
unlinkat(src, entry->d_name, AT_REMOVEDIR);
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_rf(int path) {
|
||||
struct dirent *entry;
|
||||
int newfd;
|
||||
DIR *dir = fdopendir(path);
|
||||
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
// Preserve overlay
|
||||
if (strcmp(entry->d_name, "overlay") == 0)
|
||||
continue;
|
||||
newfd = openat(path, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
rm_rf(newfd);
|
||||
close(newfd);
|
||||
unlinkat(path, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
default:
|
||||
unlinkat(path, entry->d_name, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_cmdline(struct cmdline *cmd) {
|
||||
char *tok;
|
||||
char buffer[4096];
|
||||
@@ -197,8 +90,6 @@ static void parse_device(struct device *dev, char *uevent) {
|
||||
|
||||
static int setup_block(struct device *dev, const char *partname) {
|
||||
char buffer[1024], path[128];
|
||||
mkdir("/sys", 0755);
|
||||
mount("sysfs", "/sys", "sysfs", 0, NULL);
|
||||
struct dirent *entry;
|
||||
DIR *dir = opendir("/sys/dev/block");
|
||||
if (dir == NULL)
|
||||
@@ -266,7 +157,8 @@ static void patch_sepolicy() {
|
||||
if (sepolicy == NULL && access("/vendor/etc/selinux/precompiled_sepolicy", R_OK) == 0) {
|
||||
void *sys_sha = NULL, *ven_sha = NULL;
|
||||
size_t sys_size = 0, ven_size = 0;
|
||||
dir = opendir("/vendor/etc/selinux");
|
||||
if ((dir = opendir("/vendor/etc/selinux")) == NULL)
|
||||
goto check_done;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
@@ -277,7 +169,8 @@ static void patch_sepolicy() {
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
dir = opendir("/system/etc/selinux");
|
||||
if ((dir = opendir("/system/etc/selinux")) == NULL)
|
||||
goto check_done;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
@@ -294,6 +187,8 @@ static void patch_sepolicy() {
|
||||
munmap(ven_sha, ven_size);
|
||||
}
|
||||
|
||||
check_done:
|
||||
|
||||
if (sepolicy) {
|
||||
load_policydb(sepolicy);
|
||||
} else {
|
||||
@@ -362,7 +257,13 @@ int main(int argc, char *argv[]) {
|
||||
// Normal boot mode
|
||||
// Clear rootfs
|
||||
int root = open("/", O_RDONLY | O_CLOEXEC);
|
||||
rm_rf(root);
|
||||
|
||||
// Exclude overlay folder
|
||||
excl_list = (char *[]) { "overlay", NULL };
|
||||
frm_rf(root);
|
||||
|
||||
mkdir("/sys", 0755);
|
||||
mount("sysfs", "/sys", "sysfs", 0, NULL);
|
||||
|
||||
char partname[32];
|
||||
snprintf(partname, sizeof(partname), "system%s", cmd.slot);
|
||||
@@ -373,7 +274,11 @@ int main(int argc, char *argv[]) {
|
||||
mkdir("/system_root", 0755);
|
||||
mount(dev.path, "/system_root", "ext4", MS_RDONLY, NULL);
|
||||
int system_root = open("/system_root", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
// Exclude system folder
|
||||
excl_list = (char *[]) { "system", NULL };
|
||||
clone_dir(system_root, root);
|
||||
mkdir("/system", 0755);
|
||||
mount("/system_root/system", "/system", NULL, MS_BIND, NULL);
|
||||
|
||||
int overlay = open("/overlay", O_RDONLY | O_CLOEXEC);
|
||||
@@ -381,8 +286,10 @@ int main(int argc, char *argv[]) {
|
||||
mv_dir(overlay, root);
|
||||
|
||||
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot);
|
||||
setup_block(&dev, partname);
|
||||
mount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
|
||||
// We need to mount independent vendor partition
|
||||
if (setup_block(&dev, partname) == 0)
|
||||
mount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
|
||||
|
||||
patch_ramdisk();
|
||||
patch_sepolicy();
|
||||
|
@@ -56,249 +56,352 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include "_system_properties.h"
|
||||
#include "system_properties.h"
|
||||
|
||||
#include "magisk.h"
|
||||
#include "resetprop.h"
|
||||
extern "C" {
|
||||
#include "vector.h"
|
||||
}
|
||||
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) printf(__VA_ARGS__); }
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) fprintf(stderr, __VA_ARGS__); }
|
||||
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
|
||||
#define PERSISTENT_PROPERTY_DIR "/data/property"
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
static bool is_legal_property_name(const char *name, size_t namelen) {
|
||||
static int check_legal_property_name(const char *name) {
|
||||
int namelen = strlen(name);
|
||||
|
||||
if (namelen < 1) return false;
|
||||
if (name[0] == '.') return false;
|
||||
if (name[namelen - 1] == '.') return false;
|
||||
if (namelen < 1) goto illegal;
|
||||
if (name[0] == '.') goto illegal;
|
||||
if (name[namelen - 1] == '.') goto illegal;
|
||||
|
||||
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
|
||||
/* Don't allow ".." to appear in a property name */
|
||||
for (size_t i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') return false;
|
||||
continue;
|
||||
}
|
||||
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
return false;
|
||||
}
|
||||
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
|
||||
/* Don't allow ".." to appear in a property name */
|
||||
for (size_t i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') goto illegal;
|
||||
continue;
|
||||
}
|
||||
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
goto illegal;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
illegal:
|
||||
PRINT_E("Illegal property name: [%s]\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int usage(char* arg0) {
|
||||
fprintf(stderr,
|
||||
"resetprop v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu & nkk71) - System Props Modification Tool\n\n"
|
||||
"Usage: %s [options] [args...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -v show verbose output\n"
|
||||
" -n only modify property in memory\n"
|
||||
"\n"
|
||||
"%s NAME VALUE set property entry NAME with VALUE\n"
|
||||
"%s --file FILE load props from FILE\n"
|
||||
"%s --delete NAME remove prop entry NAME\n"
|
||||
"\n"
|
||||
fprintf(stderr,
|
||||
"resetprop v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu & nkk71) - System Props Modification Tool\n\n"
|
||||
"Usage: %s [flags] [options...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -h, --help show this message\n"
|
||||
" (no arguments) print all properties\n"
|
||||
" NAME get property\n"
|
||||
" NAME VALUE set property entry NAME with VALUE\n"
|
||||
" --file FILE load props from FILE\n"
|
||||
" --delete NAME delete property\n"
|
||||
"\n"
|
||||
"Flags:\n"
|
||||
" -v print verbose output to stderr\n"
|
||||
" -n set properties without init triggers\n"
|
||||
" only affects setprop\n"
|
||||
" -p access actual persist storage\n"
|
||||
" only affects getprop and deleteprop\n"
|
||||
"\n"
|
||||
|
||||
, arg0, arg0, arg0, arg0);
|
||||
return 1;
|
||||
, arg0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int init_resetprop() {
|
||||
if (__system_properties_init2()) {
|
||||
PRINT_E("resetprop: Initialize error\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
if (__system_properties_init2()) {
|
||||
PRINT_E("resetprop: Initialize error\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prop_exist(const char *name) {
|
||||
if (init_resetprop()) return 0;
|
||||
return __system_property_find2(name) != NULL;
|
||||
if (init_resetprop()) return 0;
|
||||
return __system_property_find2(name) != NULL;
|
||||
}
|
||||
|
||||
static void read_prop_info(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
strcpy((char *) cookie, value);
|
||||
strcpy((char *) cookie, value);
|
||||
}
|
||||
|
||||
|
||||
char *getprop(const char *name) {
|
||||
return getprop2(name, 0);
|
||||
}
|
||||
|
||||
// Get prop by name, return string (should free manually!)
|
||||
char *getprop(const char *name) {
|
||||
if (init_resetprop()) return NULL;
|
||||
const prop_info *pi = __system_property_find2(name);
|
||||
if (pi == NULL) {
|
||||
PRINT_D("resetprop: prop [%s] does not exist\n", name);
|
||||
return NULL;
|
||||
}
|
||||
char value[PROP_VALUE_MAX];
|
||||
__system_property_read_callback2(pi, read_prop_info, value);
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
char *getprop2(const char *name, int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return NULL;
|
||||
char value[PROP_VALUE_MAX];
|
||||
if (init_resetprop()) return NULL;
|
||||
const prop_info *pi = __system_property_find2(name);
|
||||
if (pi == NULL) {
|
||||
if (persist && strncmp(name, "persist.", 8) == 0) {
|
||||
// Try to read from file
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) goto no_prop;
|
||||
PRINT_D("resetprop: read prop from [%s]\n", path);
|
||||
size_t len = read(fd, value, sizeof(value));
|
||||
value[len] = '\0'; // Null terminate the read value
|
||||
} else {
|
||||
no_prop:
|
||||
PRINT_D("resetprop: prop [%s] does not exist\n", name);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
__system_property_read_callback2(pi, read_prop_info, value);
|
||||
}
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
static void (*cb)(const char *);
|
||||
struct wrapper {
|
||||
void (*func)(const char *, const char *);
|
||||
};
|
||||
|
||||
static void run_actual_cb(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
cb(name);
|
||||
static void cb_wrapper(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
((wrapper *) cookie)->func(name, value);
|
||||
}
|
||||
|
||||
static void prop_foreach_cb(const prop_info* pi, void* cookie) {
|
||||
__system_property_read_callback2(pi, run_actual_cb, NULL);
|
||||
__system_property_read_callback2(pi, cb_wrapper, cookie);
|
||||
}
|
||||
|
||||
void getprop_all(void (*cbk)(const char *name)) {
|
||||
if (init_resetprop()) return;
|
||||
cb = cbk;
|
||||
__system_property_foreach2(prop_foreach_cb, NULL);
|
||||
class property {
|
||||
public:
|
||||
property(const char *n, const char *v) {
|
||||
name = strdup(n);
|
||||
value = strdup(v);
|
||||
}
|
||||
~property() {
|
||||
free((void *)name);
|
||||
free((void *)value);
|
||||
}
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
vector prop_list;
|
||||
|
||||
static int prop_cmp(const void *p1, const void *p2) {
|
||||
return strcmp((*((property **) p1))->name, (*((property **) p2))->name);
|
||||
}
|
||||
|
||||
static void print_all_props_cb(const char *name, const char *value) {
|
||||
vec_push_back(&prop_list, new property(name, value));
|
||||
}
|
||||
|
||||
static void print_all_props(int persist) {
|
||||
void *p;
|
||||
vec_init(&prop_list);
|
||||
getprop_all(print_all_props_cb);
|
||||
if (persist) {
|
||||
// Check all persist props in data
|
||||
DIR *dir = opendir(PERSISTENT_PROPERTY_DIR);
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
|
||||
continue;
|
||||
int found = 0;
|
||||
vec_for_each(&prop_list, p) {
|
||||
if (strcmp(((property *) p)->name, entry->d_name) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
vec_push_back(&prop_list, new property(entry->d_name, getprop2(entry->d_name, 1)));
|
||||
}
|
||||
}
|
||||
vec_sort(&prop_list, prop_cmp);
|
||||
vec_for_each(&prop_list, p) {
|
||||
printf("[%s]: [%s]\n", ((property *) p)->name, ((property *) p)->value);
|
||||
delete((property *) p);
|
||||
}
|
||||
vec_destroy(&prop_list);
|
||||
}
|
||||
|
||||
void getprop_all(void (*callback)(const char*, const char*)) {
|
||||
if (init_resetprop()) return;
|
||||
struct wrapper wrap = {
|
||||
.func = callback
|
||||
};
|
||||
__system_property_foreach2(prop_foreach_cb, &wrap);
|
||||
}
|
||||
|
||||
int setprop(const char *name, const char *value) {
|
||||
return setprop2(name, value, 1);
|
||||
return setprop2(name, value, 1);
|
||||
}
|
||||
|
||||
int setprop2(const char *name, const char *value, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
int ret;
|
||||
if (check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
int ret;
|
||||
|
||||
prop_info *pi = (prop_info*) __system_property_find2(name);
|
||||
if (pi != NULL) {
|
||||
if (trigger) {
|
||||
if (!strncmp(name, "ro.", 3)) deleteprop(name, trigger);
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_update2(pi, value, strlen(value));
|
||||
}
|
||||
} else {
|
||||
PRINT_D("resetprop: New prop [%s]\n", name);
|
||||
if (trigger) {
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_add2(name, strlen(name), value, strlen(value));
|
||||
}
|
||||
}
|
||||
prop_info *pi = (prop_info*) __system_property_find2(name);
|
||||
if (pi != NULL) {
|
||||
if (trigger) {
|
||||
if (strncmp(name, "ro.", 3) == 0) deleteprop(name);
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_update2(pi, value, strlen(value));
|
||||
}
|
||||
} else {
|
||||
PRINT_D("resetprop: New prop [%s]\n", name);
|
||||
if (trigger) {
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_add2(name, strlen(name), value, strlen(value));
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
|
||||
trigger ? "property_service" : "modifing prop data structure");
|
||||
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
|
||||
trigger ? "property_service" : "modifing prop data structure");
|
||||
|
||||
if (ret)
|
||||
PRINT_E("resetprop: setprop error\n");
|
||||
if (ret)
|
||||
PRINT_E("resetprop: setprop error\n");
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int deleteprop(const char *name, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: deleteprop [%s]\n", name);
|
||||
if (trigger && strstr(name, "persist.")) {
|
||||
char buffer[PATH_MAX];
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
|
||||
unlink(buffer);
|
||||
}
|
||||
return __system_property_del(name);
|
||||
int deleteprop(const char *name) {
|
||||
return deleteprop2(name, 1);
|
||||
}
|
||||
|
||||
int deleteprop2(const char *name, const int persist) {
|
||||
if (check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
char path[PATH_MAX];
|
||||
path[0] = '\0';
|
||||
PRINT_D("resetprop: deleteprop [%s]\n", name);
|
||||
if (persist && strncmp(name, "persist.", 8) == 0)
|
||||
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
|
||||
return __system_property_del(name) && unlink(path);
|
||||
}
|
||||
|
||||
int read_prop_file(const char* filename, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: Load prop file [%s]\n", filename);
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
PRINT_E("Cannot open [%s]\n", filename);
|
||||
return 1;
|
||||
}
|
||||
char *line = NULL, *pch;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
int comment = 0, i;
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove the trailing newline
|
||||
if (line[read - 1] == '\n') {
|
||||
line[read - 1] = '\0';
|
||||
--read;
|
||||
}
|
||||
comment = 0;
|
||||
for (i = 0; i < read; ++i) {
|
||||
// Ignore starting spaces
|
||||
if (line[i] == ' ') continue;
|
||||
else {
|
||||
// A line starting with # is ignored
|
||||
if (line[i] == '#') comment = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comment) continue;
|
||||
pch = strchr(line, '=');
|
||||
// Ignore ivalid formats
|
||||
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
setprop2(line + i, pch + 1, trigger);
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: Load prop file [%s]\n", filename);
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
PRINT_E("Cannot open [%s]\n", filename);
|
||||
return 1;
|
||||
}
|
||||
char *line = NULL, *pch;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
int comment = 0, i;
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove the trailing newline
|
||||
if (line[read - 1] == '\n') {
|
||||
line[read - 1] = '\0';
|
||||
--read;
|
||||
}
|
||||
comment = 0;
|
||||
for (i = 0; i < read; ++i) {
|
||||
// Ignore starting spaces
|
||||
if (line[i] == ' ') continue;
|
||||
else {
|
||||
// A line starting with # is ignored
|
||||
if (line[i] == '#') comment = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comment) continue;
|
||||
pch = strchr(line, '=');
|
||||
// Ignore ivalid formats
|
||||
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
setprop2(line + i, pch + 1, trigger);
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
int trigger = 1, persist = 0;
|
||||
char *argv0 = argv[0], *prop;
|
||||
|
||||
int del = 0, file = 0, trigger = 1;
|
||||
--argc;
|
||||
++argv;
|
||||
|
||||
int exp_arg = 2;
|
||||
char *name, *value, *filename;
|
||||
// Parse flags and -- options
|
||||
while (argc && argv[0][0] == '-') {
|
||||
for (int idx = 1; 1; ++idx) {
|
||||
switch (argv[0][idx]) {
|
||||
case '-':
|
||||
if (strcmp(argv[0], "--file") == 0 && argc == 2) {
|
||||
return read_prop_file(argv[1], trigger);
|
||||
} else if (strcmp(argv[0], "--delete") == 0 && argc == 2) {
|
||||
return deleteprop2(argv[1], persist);
|
||||
} else if (strcmp(argv[0], "--help") == 0) {
|
||||
goto usage;
|
||||
}
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
continue;
|
||||
case 'p':
|
||||
persist = 1;
|
||||
continue;
|
||||
case 'n':
|
||||
trigger = 0;
|
||||
continue;
|
||||
case '\0':
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage:
|
||||
return usage(argv0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp("-v", argv[i])) {
|
||||
verbose = 1;
|
||||
} else if (!strcmp("-n", argv[i])) {
|
||||
trigger = 0;
|
||||
} else if (!strcmp("--file", argv[i])) {
|
||||
file = 1;
|
||||
exp_arg = 1;
|
||||
} else if (!strcmp("--delete", argv[i])) {
|
||||
del = 1;
|
||||
exp_arg = 1;
|
||||
} else {
|
||||
if (i + exp_arg > argc) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
if (file) {
|
||||
filename = argv[i];
|
||||
break;
|
||||
} else {
|
||||
if(!is_legal_property_name(argv[i], strlen(argv[i]))) {
|
||||
PRINT_E("Illegal property name: [%s]\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
name = argv[i];
|
||||
if (exp_arg > 1) {
|
||||
if (strlen(argv[i + 1]) >= PROP_VALUE_MAX) {
|
||||
PRINT_E("Value too long: [%s]\n", argv[i + 1]);
|
||||
return 1;
|
||||
}
|
||||
value = argv[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
return read_prop_file(filename, trigger);
|
||||
} else if (del) {
|
||||
return deleteprop(name, trigger);
|
||||
} else {
|
||||
return setprop2(name, value, trigger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
switch (argc) {
|
||||
case 0:
|
||||
print_all_props(persist);
|
||||
return 0;
|
||||
case 1:
|
||||
prop = getprop2(argv[0], persist);
|
||||
if (prop == NULL) return 1;
|
||||
printf("%s\n", prop);
|
||||
free(prop);
|
||||
return 0;
|
||||
case 2:
|
||||
return setprop2(argv[0], argv[1], trigger);
|
||||
default:
|
||||
usage(argv0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
2
jni/su
2
jni/su
Submodule jni/su updated: c912c192e0...6de95e0d9b
333
jni/utils/file.c
Normal file
333
jni/utils/file.c
Normal file
@@ -0,0 +1,333 @@
|
||||
/* file.c - Contains all files related utilities
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#ifndef NO_SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
char **excl_list = (char *[]) { NULL };
|
||||
|
||||
static int is_excl(const char *name) {
|
||||
for (int i = 0; excl_list[i]; ++i) {
|
||||
if (strcmp(name, excl_list[i]) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_getpath(int fd, char *path, size_t size) {
|
||||
snprintf(path, size, "/proc/self/fd/%d", fd);
|
||||
if (xreadlink(path, path, size) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdir_p(const char *pathname, mode_t mode) {
|
||||
char *path = strdup(pathname), *p;
|
||||
errno = 0;
|
||||
for (p = path + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
int fd = xopen(path, O_RDONLY | O_CLOEXEC);
|
||||
frm_rf(fd);
|
||||
close(fd);
|
||||
rmdir(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
struct dirent *entry;
|
||||
int newfd;
|
||||
DIR *dir = xfdopendir(dirfd);
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
newfd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
frm_rf(newfd);
|
||||
close(newfd);
|
||||
unlinkat(dirfd, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
default:
|
||||
unlinkat(dirfd, entry->d_name, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This will only on the same file system */
|
||||
void mv_f(const char *source, const char *destination) {
|
||||
struct stat st;
|
||||
xlstat(source, &st);
|
||||
int src, dest;
|
||||
struct file_attr a;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
xmkdir_p(destination, st.st_mode & 0777);
|
||||
src = xopen(source, O_RDONLY | O_CLOEXEC);
|
||||
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
|
||||
fclone_attr(src, dest);
|
||||
mv_dir(src, dest);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else{
|
||||
getattr(source, &a);
|
||||
xrename(source, destination);
|
||||
setattr(destination, &a);
|
||||
}
|
||||
rmdir(source);
|
||||
}
|
||||
|
||||
/* This will only on the same file system */
|
||||
void mv_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int newsrc, newdest;
|
||||
struct file_attr a;
|
||||
|
||||
dir = xfdopendir(src);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
|
||||
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
fsetattr(newdest, &a);
|
||||
mv_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
unlinkat(src, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
case DT_LNK:
|
||||
case DT_REG:
|
||||
renameat(src, entry->d_name, dest, entry->d_name);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cp_afc(const char *source, const char *destination) {
|
||||
int src, dest;
|
||||
struct file_attr a;
|
||||
getattr(source, &a);
|
||||
|
||||
if (S_ISDIR(a.st.st_mode)) {
|
||||
xmkdir_p(destination, a.st.st_mode & 0777);
|
||||
src = xopen(source, O_RDONLY | O_CLOEXEC);
|
||||
dest = xopen(destination, O_RDONLY | O_CLOEXEC);
|
||||
fsetattr(dest, &a);
|
||||
clone_dir(src, dest);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else{
|
||||
unlink(destination);
|
||||
if (S_ISREG(a.st.st_mode)) {
|
||||
src = xopen(source, O_RDONLY);
|
||||
dest = xopen(destination, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
xsendfile(dest, src, NULL, a.st.st_size);
|
||||
fsetattr(src, &a);
|
||||
close(src);
|
||||
close(dest);
|
||||
} else if (S_ISLNK(a.st.st_mode)) {
|
||||
char buf[PATH_MAX];
|
||||
xreadlink(source, buf, sizeof(buf));
|
||||
xsymlink(buf, destination);
|
||||
setattr(destination, &a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clone_dir(int src, int dest) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int srcfd, destfd, newsrc, newdest;
|
||||
char buf[PATH_MAX];
|
||||
struct file_attr a;
|
||||
|
||||
dir = xfdopendir(src);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (is_excl(entry->d_name))
|
||||
continue;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
xmkdirat(dest, entry->d_name, a.st.st_mode & 0777);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
clone_dir(newsrc, newdest);
|
||||
close(newsrc);
|
||||
close(newdest);
|
||||
break;
|
||||
case DT_REG:
|
||||
destfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
|
||||
srcfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
xsendfile(destfd, srcfd, 0, a.st.st_size);
|
||||
fsetattr(destfd, &a);
|
||||
close(destfd);
|
||||
close(srcfd);
|
||||
break;
|
||||
case DT_LNK:
|
||||
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
symlinkat(buf, dest, entry->d_name);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getattr(const char *path, struct file_attr *a) {
|
||||
if (xlstat(path, &a->st) == -1)
|
||||
return -1;
|
||||
char *con = "";
|
||||
#ifndef NO_SELINUX
|
||||
if (lgetfilecon(path, &con) == -1)
|
||||
return -1;
|
||||
strcpy(a->con, con);
|
||||
freecon(con);
|
||||
#else
|
||||
a->con[0] = '\0';
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
int ret = fgetattr(fd, a);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fgetattr(int fd, struct file_attr *a) {
|
||||
#ifndef NO_SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return getattr(path, a);
|
||||
#else
|
||||
if (fstat(fd, &a->st) == -1)
|
||||
return -1;
|
||||
a->con[0] = '\0';
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int setattr(const char *path, struct file_attr *a) {
|
||||
if (chmod(path, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
#ifndef NO_SELINUX
|
||||
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
|
||||
return -1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
|
||||
int fd = xopenat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
int ret = fsetattr(fd, a);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsetattr(int fd, struct file_attr *a) {
|
||||
#ifndef NO_SELINUX
|
||||
char path[PATH_MAX];
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
return setattr(path, a);
|
||||
#else
|
||||
if (fchmod(fd, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void clone_attr(const char *source, const char *target) {
|
||||
struct file_attr a;
|
||||
getattr(source, &a);
|
||||
setattr(target, &a);
|
||||
}
|
||||
|
||||
void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
struct file_attr a;
|
||||
fgetattr(sourcefd, &a);
|
||||
fsetattr(targetfd, &a);
|
||||
}
|
||||
|
||||
#ifndef NO_SELINUX
|
||||
|
||||
#define UNLABEL_CON "u:object_r:unlabeled:s0"
|
||||
#define SYSTEM_CON "u:object_r:system_file:s0"
|
||||
|
||||
void restorecon(int dirfd, int force) {
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
int fd;
|
||||
char path[PATH_MAX], *con;
|
||||
|
||||
fd_getpath(dirfd, path, sizeof(path));
|
||||
lgetfilecon(path, &con);
|
||||
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
|
||||
dir = xfdopendir(dirfd);
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
restorecon(fd, force);
|
||||
} else {
|
||||
fd = xopenat(dirfd, entry->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
fd_getpath(fd, path, sizeof(path));
|
||||
lgetfilecon(path, &con);
|
||||
if (force || strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
|
||||
lsetfilecon(path, SYSTEM_CON);
|
||||
freecon(con);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NO_SELINUX
|
@@ -2,6 +2,7 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
@@ -51,20 +52,13 @@ static char *loopsetup(const char *img) {
|
||||
int create_img(const char *img, int size) {
|
||||
unlink(img);
|
||||
LOGI("Create %s with size %dM\n", img, size);
|
||||
// Create a temp file with the file contexts
|
||||
char file_contexts[] = "/magisk(/.*)? u:object_r:system_file:s0\n";
|
||||
// If not root, attempt to create in current diretory
|
||||
char *filename = getuid() == UID_ROOT ? "/dev/file_contexts_image" : "file_contexts_image";
|
||||
int ret, fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
xwrite(fd, file_contexts, sizeof(file_contexts));
|
||||
close(fd);
|
||||
int ret;
|
||||
|
||||
char buffer[16];
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
ret = exec_command_sync("make_ext4fs", "-l", buffer, "-a", "/magisk", "-S", filename, img, NULL);
|
||||
ret = exec_command_sync("make_ext4fs", "-l", buffer, img, NULL);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
unlink(filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -103,7 +97,7 @@ int resize_img(const char *img, int size) {
|
||||
if (e2fsck(img))
|
||||
return 1;
|
||||
char buffer[128];
|
||||
int pid, status, fd = -1;
|
||||
int pid, fd = -1, used, total;
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
pid = exec_command(1, &fd, NULL, "resize2fs", img, buffer, NULL);
|
||||
if (pid < 0)
|
||||
@@ -111,8 +105,32 @@ int resize_img(const char *img, int size) {
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
close(fd);
|
||||
waitpid(pid, &status, 0);
|
||||
return WEXITSTATUS(status);
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
// Double check our image size
|
||||
get_img_size(img, &used, &total);
|
||||
if (total != size) {
|
||||
// Sammy crap occurs or resize2fs failed, lets create a new image!
|
||||
char *dir = dirname(img);
|
||||
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
|
||||
create_img(buffer, size);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(img, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(buffer, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
cp_afc(SOURCE_TMP, TARGET_TMP);
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
rename(buffer, img);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *mount_image(const char *img, const char *target) {
|
||||
@@ -145,8 +163,9 @@ void umount_image(const char *target, const char *device) {
|
||||
int merge_img(const char *source, const char *target) {
|
||||
if (access(source, F_OK) == -1)
|
||||
return 0;
|
||||
LOGI("* Merging %s -> %s\n", source, target);
|
||||
if (access(target, F_OK) == -1) {
|
||||
rename(source, target);
|
||||
xrename(source, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -157,7 +176,7 @@ int merge_img(const char *source, const char *target) {
|
||||
get_img_size(source, &s_used, &s_total);
|
||||
get_img_size(target, &t_used, &t_total);
|
||||
n_total = round_size(s_used + t_used);
|
||||
if (n_total != t_total)
|
||||
if (n_total > t_total)
|
||||
resize_img(target, n_total);
|
||||
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
@@ -170,7 +189,7 @@ int merge_img(const char *source, const char *target) {
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if (!(dir = opendir(SOURCE_TMP)))
|
||||
if (!(dir = xopendir(SOURCE_TMP)))
|
||||
return 1;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
@@ -183,7 +202,7 @@ int merge_img(const char *source, const char *target) {
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buffer, F_OK) == 0) {
|
||||
LOGI("Upgrade module: %s\n", entry->d_name);
|
||||
exec_command_sync(BBPATH "/rm", "-rf", buffer, NULL);
|
||||
rm_rf(buffer);
|
||||
} else {
|
||||
LOGI("New module: %s\n", entry->d_name);
|
||||
}
|
||||
|
140
jni/utils/misc.c
140
jni/utils/misc.c
@@ -6,19 +6,14 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
@@ -145,12 +140,12 @@ static void proc_name_filter(int pid) {
|
||||
char buf[64];
|
||||
int fd;
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
if (fdgets(buf, sizeof(buf), fd) == 0) {
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
|
||||
close(fd);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
if (access(buf, R_OK) == -1 || (fd = xopen(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
fdgets(buf, sizeof(buf), fd);
|
||||
}
|
||||
@@ -167,32 +162,25 @@ void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
|
||||
ps(proc_name_filter);
|
||||
}
|
||||
|
||||
#define DEV_BLOCK "/dev/block"
|
||||
|
||||
void unlock_blocks() {
|
||||
char path[PATH_MAX];
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int fd, OFF = 0;
|
||||
int fd, dev, OFF = 0;
|
||||
|
||||
if (!(dir = xopendir(DEV_BLOCK)))
|
||||
if ((dev = xopen("/dev/block", O_RDONLY | O_CLOEXEC)) < 0)
|
||||
return;
|
||||
dir = xfdopendir(dev);
|
||||
|
||||
while((entry = readdir(dir))) {
|
||||
if (entry->d_type == DT_BLK &&
|
||||
strstr(entry->d_name, "ram") == NULL &&
|
||||
strstr(entry->d_name, "loop") == NULL) {
|
||||
snprintf(path, sizeof(path), "%s/%s", DEV_BLOCK, entry->d_name);
|
||||
if ((fd = xopen(path, O_RDONLY)) < 0)
|
||||
if (entry->d_type == DT_BLK) {
|
||||
if ((fd = openat(dev, entry->d_name, O_RDONLY)) < 0)
|
||||
continue;
|
||||
|
||||
if (ioctl(fd, BLKROSET, &OFF) == -1)
|
||||
PLOGE("unlock %s", path);
|
||||
PLOGE("unlock %s", entry->d_name);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
close(dev);
|
||||
}
|
||||
|
||||
void setup_sighandlers(void (*handler)(int)) {
|
||||
@@ -243,7 +231,7 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
|
||||
envp = environ;
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
int pid = xfork();
|
||||
if (pid != 0) {
|
||||
if (fd && *fd < 0) {
|
||||
// Give the read end and close write end
|
||||
@@ -255,9 +243,6 @@ static int v_exec_command(int err, int *fd, void (*setupenv)(struct vector*), co
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Don't return to the daemon if anything goes wrong
|
||||
err_handler = exit_proc;
|
||||
|
||||
if (fd) {
|
||||
xdup2(writeEnd, STDOUT_FILENO);
|
||||
if (err) xdup2(writeEnd, STDERR_FILENO);
|
||||
@@ -288,27 +273,6 @@ int exec_command(int err, int *fd, void (*setupenv)(struct vector*), const char
|
||||
return pid;
|
||||
}
|
||||
|
||||
int mkdir_p(const char *pathname, mode_t mode) {
|
||||
char *path = strdup(pathname), *p;
|
||||
errno = 0;
|
||||
for (p = path + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bind_mount(const char *from, const char *to) {
|
||||
int ret = xmount(from, to, NULL, MS_BIND, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
@@ -323,79 +287,6 @@ int open_new(const char *filename) {
|
||||
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
int cp_afc(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
xlstat(source, &buf);
|
||||
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char *s_path, *t_path;
|
||||
|
||||
if (!(dir = xopendir(source)))
|
||||
return 1;
|
||||
|
||||
s_path = xmalloc(PATH_MAX);
|
||||
t_path = xmalloc(PATH_MAX);
|
||||
|
||||
mkdir_p(target, 0755);
|
||||
clone_attr(source, target);
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name);
|
||||
snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name);
|
||||
cp_afc(s_path, t_path);
|
||||
}
|
||||
free(s_path);
|
||||
free(t_path);
|
||||
|
||||
closedir(dir);
|
||||
} else{
|
||||
unlink(target);
|
||||
if (S_ISREG(buf.st_mode)) {
|
||||
int sfd, tfd;
|
||||
sfd = xopen(source, O_RDONLY);
|
||||
tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
xsendfile(tfd, sfd, NULL, buf.st_size);
|
||||
fclone_attr(sfd, tfd);
|
||||
close(sfd);
|
||||
close(tfd);
|
||||
} else if (S_ISLNK(buf.st_mode)) {
|
||||
char buffer[PATH_MAX];
|
||||
xreadlink(source, buffer, sizeof(buffer));
|
||||
xsymlink(buffer, target);
|
||||
clone_attr(source, target);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clone_attr(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
lstat(target, &buf);
|
||||
chmod(target, buf.st_mode & 0777);
|
||||
chown(target, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
lgetfilecon(source, &con);
|
||||
lsetfilecon(target, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
struct stat buf;
|
||||
fstat(sourcefd, &buf);
|
||||
fchmod(targetfd, buf.st_mode & 0777);
|
||||
fchown(targetfd, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
fgetfilecon(sourcefd, &con);
|
||||
fsetfilecon(targetfd, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void get_client_cred(int fd, struct ucred *cred) {
|
||||
socklen_t ucred_length = sizeof(*cred);
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
|
||||
@@ -415,3 +306,14 @@ int switch_mnt_ns(int pid) {
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fork_dont_care() {
|
||||
int pid = xfork();
|
||||
if (pid) {
|
||||
waitpid(pid, NULL, 0);
|
||||
return pid;
|
||||
} else if ((pid = xfork())) {
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/* xwrap.c - wrappers around existing library functions.
|
||||
*
|
||||
* Functions with the x prefix are wrappers that either succeed or kill the
|
||||
* program with an error message, but never return failure. They usually have
|
||||
* the same arguments and return value as the function they wrap.
|
||||
* Functions with the x prefix are wrappers that either succeed or log the
|
||||
* error message. They usually have the same arguments and return value
|
||||
* as the function they wrap.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -56,6 +57,14 @@ int xopen3(const char *pathname, int flags, mode_t mode) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopenat(int dirfd, const char *pathname, int flags) {
|
||||
int fd = openat(dirfd, pathname, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("openat: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count) {
|
||||
int ret = write(fd, buf, count);
|
||||
if (count != ret) {
|
||||
@@ -106,6 +115,14 @@ DIR *xopendir(const char *name) {
|
||||
return d;
|
||||
}
|
||||
|
||||
DIR *xfdopendir(int fd) {
|
||||
DIR *d = fdopendir(fd);
|
||||
if (d == NULL) {
|
||||
PLOGE("fdopendir");
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
struct dirent *xreaddir(DIR *dirp) {
|
||||
errno = 0;
|
||||
struct dirent *e = readdir(dirp);
|
||||
@@ -250,7 +267,16 @@ ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
PLOGE("readlink %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
|
||||
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
if (ret == -1) {
|
||||
PLOGE("readlinkat %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -289,14 +315,6 @@ int xumount2(const char *target, int flags) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xchmod(const char *pathname, mode_t mode) {
|
||||
int ret = chmod(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("chmod %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xrename(const char *oldpath, const char *newpath) {
|
||||
int ret = rename(oldpath, newpath);
|
||||
if (ret == -1) {
|
||||
@@ -313,6 +331,22 @@ int xmkdir(const char *pathname, mode_t mode) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir_p(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir_p(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("mkdir_p %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
|
||||
int ret = mkdirat(dirfd, pathname, mode);
|
||||
if (ret == -1 && errno != EEXIST) {
|
||||
PLOGE("mkdirat %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset) {
|
||||
void *ret = mmap(addr, length, prot, flags, fd, offset);
|
||||
@@ -330,10 +364,10 @@ ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir_p(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir_p(pathname, mode);
|
||||
pid_t xfork() {
|
||||
int ret = fork();
|
||||
if (ret == -1) {
|
||||
PLOGE("mkdir_p %s", pathname);
|
||||
PLOGE("fork");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@@ -66,17 +66,6 @@ cpio_mkdir() {
|
||||
# Initialization
|
||||
##########################################################################################
|
||||
|
||||
BOOTIMAGE="$1"
|
||||
|
||||
[ -e "$BOOTIMAGE" ] || (echo "$BOOTIMAGE does not exist!" && exit 1)
|
||||
|
||||
# Presets
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=false
|
||||
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
|
||||
|
||||
# Detect whether running as root
|
||||
id | grep "uid=0" >/dev/null 2>&1 && ROOT=true || ROOT=false
|
||||
|
||||
if [ -z $SOURCEDMODE ]; then
|
||||
# Switch to the location of the script file
|
||||
cd "`dirname_wrap "${BASH_SOURCE:-$0}"`"
|
||||
@@ -86,6 +75,14 @@ if [ -z $SOURCEDMODE ]; then
|
||||
mount_partitions
|
||||
fi
|
||||
|
||||
BOOTIMAGE="$1"
|
||||
|
||||
[ -e "$BOOTIMAGE" ] || abort "$BOOTIMAGE does not exist!"
|
||||
|
||||
# Presets
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=false
|
||||
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
|
||||
|
||||
chmod -R 755 .
|
||||
|
||||
##########################################################################################
|
||||
@@ -104,6 +101,7 @@ case $? in
|
||||
abort "! Unable to unpack boot image"
|
||||
;;
|
||||
2 )
|
||||
ui_print "- ChromeOS boot image detected"
|
||||
CHROMEOS=true
|
||||
;;
|
||||
3 )
|
||||
@@ -145,7 +143,7 @@ case $? in
|
||||
# Restore failed
|
||||
ui_print "! Cannot restore from internal backup"
|
||||
# If we are root and SHA1 known, we try to find the stock backup
|
||||
if $ROOT && [ ! -z $SHA1 ]; then
|
||||
if [ ! -z $SHA1 ]; then
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
if [ -f ${STOCKDUMP}.gz ]; then
|
||||
ui_print "- Stock boot image backup found"
|
||||
|
@@ -2,20 +2,25 @@
|
||||
|
||||
on post-fs
|
||||
start logd
|
||||
start magisk_daemon
|
||||
wait /dev/.magisk.unblock 5
|
||||
rm /dev/.magisk.unblock
|
||||
start magisk_pfs
|
||||
wait /dev/.magisk.unblock 20
|
||||
wait /dev/.magisk.unblock 10
|
||||
|
||||
on post-fs-data
|
||||
rm /dev/.magisk.unblock
|
||||
load_persist_props
|
||||
rm /dev/.magisk.unblock
|
||||
start magisk_pfsd
|
||||
wait /dev/.magisk.unblock 60
|
||||
|
||||
on property:magisk.restart_pfsd=1
|
||||
trigger post-fs-data
|
||||
wait /dev/.magisk.unblock 10
|
||||
|
||||
# Services
|
||||
|
||||
# Self recoverable service
|
||||
service magisk_daemon /sbin/magisk --daemon
|
||||
user root
|
||||
seclabel u:r:su:s0
|
||||
|
||||
# launch post-fs script
|
||||
service magisk_pfs /sbin/magisk --post-fs
|
||||
user root
|
||||
|
@@ -32,12 +32,7 @@ get_outfd() {
|
||||
}
|
||||
|
||||
ui_print() {
|
||||
if $BOOTMODE; then
|
||||
echo "$1"
|
||||
else
|
||||
echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
|
||||
echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
|
||||
fi
|
||||
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
|
||||
}
|
||||
|
||||
mount_partitions() {
|
||||
@@ -155,8 +150,9 @@ flash_boot_image() {
|
||||
}
|
||||
|
||||
sign_chromeos() {
|
||||
echo > empty
|
||||
ui_print "- Signing ChromeOS boot image"
|
||||
|
||||
echo > empty
|
||||
./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
|
||||
--keyblock ./chromeos/kernel.keyblock --signprivate ./chromeos/kernel_data_key.vbprivk \
|
||||
--version 1 --vmlinuz new-boot.img --config empty --arch arm --bootloader empty --flags 0x1
|
||||
|
7
ziptools/zipsigner/.gitignore
vendored
7
ziptools/zipsigner/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
.idea/
|
||||
/build
|
||||
*.hprof
|
||||
app/.externalNativeBuild/
|
@@ -1,36 +0,0 @@
|
||||
group 'com.topjohnwu'
|
||||
version '1.0.0'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'com.topjohnwu.ZipSigner'
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
classifier = null
|
||||
version = null
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.57'
|
||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.57'
|
||||
}
|
BIN
ziptools/zipsigner/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
ziptools/zipsigner/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
#Thu Aug 24 10:35:40 CST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-bin.zip
|
172
ziptools/zipsigner/gradlew
vendored
172
ziptools/zipsigner/gradlew
vendored
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save ( ) {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
84
ziptools/zipsigner/gradlew.bat
vendored
84
ziptools/zipsigner/gradlew.bat
vendored
@@ -1,84 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@@ -1,4 +0,0 @@
|
||||
rootProject.name = 'zipsigner'
|
||||
include 'apksigner'
|
||||
rootProject.name = 'zipsigner'
|
||||
|
@@ -1,567 +0,0 @@
|
||||
package com.topjohnwu;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DEROutputStream;
|
||||
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||
import org.bouncycastle.cms.CMSException;
|
||||
import org.bouncycastle.cms.CMSProcessableByteArray;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||
import org.bouncycastle.cms.CMSTypedData;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/*
|
||||
* Modified from from AOSP(Marshmallow) SignAPK.java
|
||||
* */
|
||||
|
||||
public class ZipSigner {
|
||||
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
|
||||
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
|
||||
|
||||
private static Provider sBouncyCastleProvider;
|
||||
|
||||
// bitmasks for which hash algorithms we need the manifest to include.
|
||||
private static final int USE_SHA1 = 1;
|
||||
private static final int USE_SHA256 = 2;
|
||||
|
||||
/**
|
||||
* Return one of USE_SHA1 or USE_SHA256 according to the signature
|
||||
* algorithm specified in the cert.
|
||||
*/
|
||||
private static int getDigestAlgorithm(X509Certificate cert) {
|
||||
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
|
||||
if ("SHA1WITHRSA".equals(sigAlg) ||
|
||||
"MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
|
||||
return USE_SHA1;
|
||||
} else if (sigAlg.startsWith("SHA256WITH")) {
|
||||
return USE_SHA256;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
|
||||
"\" in cert [" + cert.getSubjectDN());
|
||||
}
|
||||
}
|
||||
/** Returns the expected signature algorithm for this key type. */
|
||||
private static String getSignatureAlgorithm(X509Certificate cert) {
|
||||
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
|
||||
String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
|
||||
if ("RSA".equalsIgnoreCase(keyType)) {
|
||||
if (getDigestAlgorithm(cert) == USE_SHA256) {
|
||||
return "SHA256withRSA";
|
||||
} else {
|
||||
return "SHA1withRSA";
|
||||
}
|
||||
} else if ("EC".equalsIgnoreCase(keyType)) {
|
||||
return "SHA256withECDSA";
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported key type: " + keyType);
|
||||
}
|
||||
}
|
||||
// Files matching this pattern are not copied to the output.
|
||||
private static Pattern stripPattern =
|
||||
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
|
||||
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
|
||||
private static X509Certificate readPublicKey(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) cf.generateCertificate(input);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a PKCS#8 format private key. */
|
||||
private static PrivateKey readPrivateKey(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
int size = input.read(buffer);
|
||||
byte[] bytes = Arrays.copyOf(buffer, size);
|
||||
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
|
||||
/*
|
||||
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
|
||||
* OID and use that to construct a KeyFactory.
|
||||
*/
|
||||
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
|
||||
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
|
||||
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
|
||||
return KeyFactory.getInstance(algOid).generatePrivate(spec);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add the hash(es) of every file to the manifest, creating it if
|
||||
* necessary.
|
||||
*/
|
||||
private static Manifest addDigestsToManifest(JarFile jar, int hashes)
|
||||
throws IOException, GeneralSecurityException {
|
||||
Manifest input = jar.getManifest();
|
||||
Manifest output = new Manifest();
|
||||
Attributes main = output.getMainAttributes();
|
||||
if (input != null) {
|
||||
main.putAll(input.getMainAttributes());
|
||||
} else {
|
||||
main.putValue("Manifest-Version", "1.0");
|
||||
main.putValue("Created-By", "1.0 (Android SignApk)");
|
||||
}
|
||||
MessageDigest md_sha1 = null;
|
||||
MessageDigest md_sha256 = null;
|
||||
if ((hashes & USE_SHA1) != 0) {
|
||||
md_sha1 = MessageDigest.getInstance("SHA1");
|
||||
}
|
||||
if ((hashes & USE_SHA256) != 0) {
|
||||
md_sha256 = MessageDigest.getInstance("SHA256");
|
||||
}
|
||||
byte[] buffer = new byte[4096];
|
||||
int num;
|
||||
// We sort the input entries by name, and add them to the
|
||||
// output manifest in sorted order. We expect that the output
|
||||
// map will be deterministic.
|
||||
TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();
|
||||
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
|
||||
JarEntry entry = e.nextElement();
|
||||
byName.put(entry.getName(), entry);
|
||||
}
|
||||
for (JarEntry entry: byName.values()) {
|
||||
String name = entry.getName();
|
||||
if (!entry.isDirectory() &&
|
||||
(stripPattern == null || !stripPattern.matcher(name).matches())) {
|
||||
InputStream data = jar.getInputStream(entry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
if (md_sha1 != null) md_sha1.update(buffer, 0, num);
|
||||
if (md_sha256 != null) md_sha256.update(buffer, 0, num);
|
||||
}
|
||||
Attributes attr = null;
|
||||
if (input != null) attr = input.getAttributes(name);
|
||||
attr = attr != null ? new Attributes(attr) : new Attributes();
|
||||
if (md_sha1 != null) {
|
||||
attr.putValue("SHA1-Digest",
|
||||
new String(Base64.encode(md_sha1.digest()), "ASCII"));
|
||||
}
|
||||
if (md_sha256 != null) {
|
||||
attr.putValue("SHA-256-Digest",
|
||||
new String(Base64.encode(md_sha256.digest()), "ASCII"));
|
||||
}
|
||||
output.getEntries().put(name, attr);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/** Write to another stream and track how many bytes have been
|
||||
* written.
|
||||
*/
|
||||
private static class CountOutputStream extends FilterOutputStream {
|
||||
private int mCount;
|
||||
public CountOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
mCount = 0;
|
||||
}
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
super.write(b);
|
||||
mCount++;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
super.write(b, off, len);
|
||||
mCount += len;
|
||||
}
|
||||
public int size() {
|
||||
return mCount;
|
||||
}
|
||||
}
|
||||
/** Write a .SF file with a digest of the specified manifest. */
|
||||
private static void writeSignatureFile(Manifest manifest, OutputStream out,
|
||||
int hash)
|
||||
throws IOException, GeneralSecurityException {
|
||||
Manifest sf = new Manifest();
|
||||
Attributes main = sf.getMainAttributes();
|
||||
main.putValue("Signature-Version", "1.0");
|
||||
main.putValue("Created-By", "1.0 (Android SignApk)");
|
||||
MessageDigest md = MessageDigest.getInstance(
|
||||
hash == USE_SHA256 ? "SHA256" : "SHA1");
|
||||
PrintStream print = new PrintStream(
|
||||
new DigestOutputStream(new ByteArrayOutputStream(), md),
|
||||
true, "UTF-8");
|
||||
// Digest of the entire manifest
|
||||
manifest.write(print);
|
||||
print.flush();
|
||||
main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
|
||||
new String(Base64.encode(md.digest()), "ASCII"));
|
||||
Map<String, Attributes> entries = manifest.getEntries();
|
||||
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
|
||||
// Digest of the manifest stanza for this entry.
|
||||
print.print("Name: " + entry.getKey() + "\r\n");
|
||||
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
|
||||
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
|
||||
}
|
||||
print.print("\r\n");
|
||||
print.flush();
|
||||
Attributes sfAttr = new Attributes();
|
||||
sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest",
|
||||
new String(Base64.encode(md.digest()), "ASCII"));
|
||||
sf.getEntries().put(entry.getKey(), sfAttr);
|
||||
}
|
||||
CountOutputStream cout = new CountOutputStream(out);
|
||||
sf.write(cout);
|
||||
// A bug in the java.util.jar implementation of Android platforms
|
||||
// up to version 1.6 will cause a spurious IOException to be thrown
|
||||
// if the length of the signature file is a multiple of 1024 bytes.
|
||||
// As a workaround, add an extra CRLF in this case.
|
||||
if ((cout.size() % 1024) == 0) {
|
||||
cout.write('\r');
|
||||
cout.write('\n');
|
||||
}
|
||||
}
|
||||
/** Sign data and write the digital signature to 'out'. */
|
||||
private static void writeSignatureBlock(
|
||||
CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
|
||||
OutputStream out)
|
||||
throws IOException,
|
||||
CertificateEncodingException,
|
||||
OperatorCreationException,
|
||||
CMSException {
|
||||
ArrayList<X509Certificate> certList = new ArrayList<>(1);
|
||||
certList.add(publicKey);
|
||||
JcaCertStore certs = new JcaCertStore(certList);
|
||||
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
|
||||
ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
|
||||
.setProvider(sBouncyCastleProvider)
|
||||
.build(privateKey);
|
||||
gen.addSignerInfoGenerator(
|
||||
new JcaSignerInfoGeneratorBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder()
|
||||
.setProvider(sBouncyCastleProvider)
|
||||
.build())
|
||||
.setDirectSignature(true)
|
||||
.build(signer, publicKey));
|
||||
gen.addCertificates(certs);
|
||||
CMSSignedData sigData = gen.generate(data, false);
|
||||
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
|
||||
DEROutputStream dos = new DEROutputStream(out);
|
||||
dos.writeObject(asn1.readObject());
|
||||
}
|
||||
/**
|
||||
* Copy all the files in a manifest from input to output. We set
|
||||
* the modification times in the output to a fixed time, so as to
|
||||
* reduce variation in the output file and make incremental OTAs
|
||||
* more efficient.
|
||||
*/
|
||||
private static void copyFiles(Manifest manifest, JarFile in, JarOutputStream out,
|
||||
long timestamp, int alignment) throws IOException {
|
||||
byte[] buffer = new byte[4096];
|
||||
int num;
|
||||
Map<String, Attributes> entries = manifest.getEntries();
|
||||
ArrayList<String> names = new ArrayList<String>(entries.keySet());
|
||||
Collections.sort(names);
|
||||
boolean firstEntry = true;
|
||||
long offset = 0L;
|
||||
// We do the copy in two passes -- first copying all the
|
||||
// entries that are STORED, then copying all the entries that
|
||||
// have any other compression flag (which in practice means
|
||||
// DEFLATED). This groups all the stored entries together at
|
||||
// the start of the file and makes it easier to do alignment
|
||||
// on them (since only stored entries are aligned).
|
||||
for (String name : names) {
|
||||
JarEntry inEntry = in.getJarEntry(name);
|
||||
JarEntry outEntry = null;
|
||||
if (inEntry.getMethod() != JarEntry.STORED) continue;
|
||||
// Preserve the STORED method of the input entry.
|
||||
outEntry = new JarEntry(inEntry);
|
||||
outEntry.setTime(timestamp);
|
||||
// 'offset' is the offset into the file at which we expect
|
||||
// the file data to begin. This is the value we need to
|
||||
// make a multiple of 'alignement'.
|
||||
offset += JarFile.LOCHDR + outEntry.getName().length();
|
||||
if (firstEntry) {
|
||||
// The first entry in a jar file has an extra field of
|
||||
// four bytes that you can't get rid of; any extra
|
||||
// data you specify in the JarEntry is appended to
|
||||
// these forced four bytes. This is JAR_MAGIC in
|
||||
// JarOutputStream; the bytes are 0xfeca0000.
|
||||
offset += 4;
|
||||
firstEntry = false;
|
||||
}
|
||||
if (alignment > 0 && (offset % alignment != 0)) {
|
||||
// Set the "extra data" of the entry to between 1 and
|
||||
// alignment-1 bytes, to make the file data begin at
|
||||
// an aligned offset.
|
||||
int needed = alignment - (int)(offset % alignment);
|
||||
outEntry.setExtra(new byte[needed]);
|
||||
offset += needed;
|
||||
}
|
||||
out.putNextEntry(outEntry);
|
||||
InputStream data = in.getInputStream(inEntry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, num);
|
||||
offset += num;
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
// Copy all the non-STORED entries. We don't attempt to
|
||||
// maintain the 'offset' variable past this point; we don't do
|
||||
// alignment on these entries.
|
||||
for (String name : names) {
|
||||
JarEntry inEntry = in.getJarEntry(name);
|
||||
JarEntry outEntry = null;
|
||||
if (inEntry.getMethod() == JarEntry.STORED) continue;
|
||||
// Create a new entry so that the compressed len is recomputed.
|
||||
outEntry = new JarEntry(name);
|
||||
outEntry.setTime(timestamp);
|
||||
out.putNextEntry(outEntry);
|
||||
InputStream data = in.getInputStream(inEntry);
|
||||
while ((num = data.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, num);
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
// This class is to provide a file's content, but trimming out the last two bytes
|
||||
// Used for signWholeFile
|
||||
private static class CMSProcessableFile implements CMSTypedData {
|
||||
|
||||
private File file;
|
||||
private ASN1ObjectIdentifier type;
|
||||
private byte[] buffer;
|
||||
int bufferSize = 0;
|
||||
|
||||
CMSProcessableFile(File file) {
|
||||
this.file = file;
|
||||
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
|
||||
buffer = new byte[4096];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASN1ObjectIdentifier getContentType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream out) throws IOException, CMSException {
|
||||
FileInputStream input = new FileInputStream(file);
|
||||
long len = file.length() - 2;
|
||||
while ((bufferSize = input.read(buffer)) > 0) {
|
||||
if (len <= bufferSize) {
|
||||
out.write(buffer, 0, (int) len);
|
||||
break;
|
||||
} else {
|
||||
out.write(buffer, 0, bufferSize);
|
||||
}
|
||||
len -= bufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getContent() {
|
||||
return file;
|
||||
}
|
||||
|
||||
byte[] getTail() {
|
||||
return Arrays.copyOfRange(buffer, 0, bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static void signWholeFile(File input, X509Certificate publicKey,
|
||||
PrivateKey privateKey, OutputStream outputStream)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream temp = new ByteArrayOutputStream();
|
||||
// put a readable message and a null char at the start of the
|
||||
// archive comment, so that tools that display the comment
|
||||
// (hopefully) show something sensible.
|
||||
// TODO: anything more useful we can put in this message?
|
||||
byte[] message = "signed by SignApk".getBytes("UTF-8");
|
||||
temp.write(message);
|
||||
temp.write(0);
|
||||
|
||||
CMSProcessableFile cmsFile = new CMSProcessableFile(input);
|
||||
writeSignatureBlock(cmsFile, publicKey, privateKey, temp);
|
||||
|
||||
// For a zip with no archive comment, the
|
||||
// end-of-central-directory record will be 22 bytes long, so
|
||||
// we expect to find the EOCD marker 22 bytes from the end.
|
||||
byte[] zipData = cmsFile.getTail();
|
||||
if (zipData[zipData.length-22] != 0x50 ||
|
||||
zipData[zipData.length-21] != 0x4b ||
|
||||
zipData[zipData.length-20] != 0x05 ||
|
||||
zipData[zipData.length-19] != 0x06) {
|
||||
throw new IllegalArgumentException("zip data already has an archive comment");
|
||||
}
|
||||
int total_size = temp.size() + 6;
|
||||
if (total_size > 0xffff) {
|
||||
throw new IllegalArgumentException("signature is too big for ZIP file comment");
|
||||
}
|
||||
// signature starts this many bytes from the end of the file
|
||||
int signature_start = total_size - message.length - 1;
|
||||
temp.write(signature_start & 0xff);
|
||||
temp.write((signature_start >> 8) & 0xff);
|
||||
// Why the 0xff bytes? In a zip file with no archive comment,
|
||||
// bytes [-6:-2] of the file are the little-endian offset from
|
||||
// the start of the file to the central directory. So for the
|
||||
// two high bytes to be 0xff 0xff, the archive would have to
|
||||
// be nearly 4GB in size. So it's unlikely that a real
|
||||
// commentless archive would have 0xffs here, and lets us tell
|
||||
// an old signed archive from a new one.
|
||||
temp.write(0xff);
|
||||
temp.write(0xff);
|
||||
temp.write(total_size & 0xff);
|
||||
temp.write((total_size >> 8) & 0xff);
|
||||
temp.flush();
|
||||
// Signature verification checks that the EOCD header is the
|
||||
// last such sequence in the file (to avoid minzip finding a
|
||||
// fake EOCD appended after the signature in its scan). The
|
||||
// odds of producing this sequence by chance are very low, but
|
||||
// let's catch it here if it does.
|
||||
byte[] b = temp.toByteArray();
|
||||
for (int i = 0; i < b.length-3; ++i) {
|
||||
if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
|
||||
throw new IllegalArgumentException("found spurious EOCD header at " + i);
|
||||
}
|
||||
}
|
||||
cmsFile.write(outputStream);
|
||||
outputStream.write(total_size & 0xff);
|
||||
outputStream.write((total_size >> 8) & 0xff);
|
||||
temp.writeTo(outputStream);
|
||||
}
|
||||
private static void signFile(Manifest manifest, JarFile inputJar,
|
||||
X509Certificate publicKey, PrivateKey privateKey,
|
||||
JarOutputStream outputJar)
|
||||
throws Exception {
|
||||
// Assume the certificate is valid for at least an hour.
|
||||
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
|
||||
// MANIFEST.MF
|
||||
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
manifest.write(outputJar);
|
||||
je = new JarEntry(CERT_SF_NAME);
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey));
|
||||
byte[] signedData = baos.toByteArray();
|
||||
outputJar.write(signedData);
|
||||
// CERT.{EC,RSA} / CERT#.{EC,RSA}
|
||||
final String keyType = publicKey.getPublicKey().getAlgorithm();
|
||||
je = new JarEntry(String.format(CERT_SIG_NAME, keyType));
|
||||
je.setTime(timestamp);
|
||||
outputJar.putNextEntry(je);
|
||||
writeSignatureBlock(new CMSProcessableByteArray(signedData),
|
||||
publicKey, privateKey, outputJar);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
boolean minSign = false;
|
||||
int argStart = 0;
|
||||
|
||||
if (args.length < 4) {
|
||||
System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (args[0].equals("-m")) {
|
||||
minSign = true;
|
||||
argStart = 1;
|
||||
}
|
||||
|
||||
sBouncyCastleProvider = new BouncyCastleProvider();
|
||||
Security.insertProviderAt(sBouncyCastleProvider, 1);
|
||||
|
||||
File pubKey = new File(args[argStart]);
|
||||
File privKey = new File(args[argStart + 1]);
|
||||
File input = new File(args[argStart + 2]);
|
||||
File output = new File(args[argStart + 3]);
|
||||
|
||||
int alignment = 4;
|
||||
JarFile inputJar = null;
|
||||
FileOutputStream outputFile = null;
|
||||
int hashes = 0;
|
||||
try {
|
||||
X509Certificate publicKey = readPublicKey(new FileInputStream(pubKey));
|
||||
hashes |= getDigestAlgorithm(publicKey);
|
||||
|
||||
// Set the ZIP file timestamp to the starting valid time
|
||||
// of the 0th certificate plus one hour (to match what
|
||||
// we've historically done).
|
||||
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
|
||||
PrivateKey privateKey = readPrivateKey(new FileInputStream(privKey));
|
||||
|
||||
outputFile = new FileOutputStream(output);
|
||||
if (minSign) {
|
||||
ZipSigner.signWholeFile(input, publicKey, privateKey, outputFile);
|
||||
} else {
|
||||
inputJar = new JarFile(input, false); // Don't verify.
|
||||
JarOutputStream outputJar = new JarOutputStream(outputFile);
|
||||
// For signing .apks, use the maximum compression to make
|
||||
// them as small as possible (since they live forever on
|
||||
// the system partition). For OTA packages, use the
|
||||
// default compression level, which is much much faster
|
||||
// and produces output that is only a tiny bit larger
|
||||
// (~0.1% on full OTA packages I tested).
|
||||
outputJar.setLevel(9);
|
||||
Manifest manifest = addDigestsToManifest(inputJar, hashes);
|
||||
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
||||
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
|
||||
outputJar.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} finally {
|
||||
try {
|
||||
if (inputJar != null) inputJar.close();
|
||||
if (outputFile != null) outputFile.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user