From eae611c54d736fea18f5c5586e0e1b503303c786 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 2 Aug 2017 18:23:42 +0800 Subject: [PATCH] Add b64xz to handle busybox decode/decompress in scripts --- build.py | 30 +++++++++------ jni/Android.mk | 11 +++++- jni/b64xz.c | 83 ++++++++++++++++++++++++++++++++++++++++ scripts/update_binary.sh | 22 ++++++----- 4 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 jni/b64xz.c diff --git a/build.py b/build.py index 1ef0f13fe..d9d47be37 100755 --- a/build.py +++ b/build.py @@ -35,7 +35,7 @@ import zipfile import datetime import errno import shutil -import gzip +import lzma import base64 def silentremove(file): @@ -164,18 +164,24 @@ def sign_adjust_zip(unsigned, output): def gen_update_binary(): update_bin = [] - bb = os.path.join('libs', 'armeabi-v7a', 'busybox') - if not os.path.exists(bb): - error('{} does not exist! Please build \'binary\' before zipping!'.format(bb)) - with open(bb, 'rb') as busybox: - update_bin.append('#! /sbin/sh\nBB_ARM=') - update_bin.append(base64.b64encode(gzip.compress(busybox.read())).decode('ascii')) - bb = os.path.join('libs', 'x86', 'busybox') - if not os.path.exists(bb): - error('{} does not exist! Please build \'binary\' before zipping!'.format(bb)) - with open(bb, 'rb') as busybox: + binary = os.path.join('libs', 'armeabi-v7a', 'b64xz') + if not os.path.exists(binary): + error('Please build \'binary\' before zipping!') + with open(binary, 'rb') as b64xz: + update_bin.append('#! /sbin/sh\nEX_ARM=') + update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read())) + binary = os.path.join('libs', 'x86', 'b64xz') + with open(binary, 'rb') as b64xz: + update_bin.append('\nEX_X86=') + update_bin.append(''.join("\\\\x{:02X}".format(c) for c in b64xz.read())) + binary = os.path.join('libs', 'armeabi-v7a', 'busybox') + with open(binary, 'rb') as busybox: + update_bin.append('\nBB_ARM=') + update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii')) + binary = os.path.join('libs', 'x86', 'busybox') + with open(binary, 'rb') as busybox: update_bin.append('\nBB_X86=') - update_bin.append(base64.b64encode(gzip.compress(busybox.read())).decode('ascii')) + update_bin.append(base64.b64encode(lzma.compress(busybox.read())).decode('ascii')) update_bin.append('\n') with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script: update_bin.append(script.read()) diff --git a/jni/Android.mk b/jni/Android.mk index 152e00147..fcd85108a 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -74,9 +74,18 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -DZLIB_CONST include $(BUILD_EXECUTABLE) -# busybox (32-bit only) +# 32-bit static binaries ifneq ($(TARGET_ARCH_ABI), x86_64) ifneq ($(TARGET_ARCH_ABI), arm64-v8a) +# b64xz +include $(CLEAR_VARS) +LOCAL_MODULE := b64xz +LOCAL_STATIC_LIBRARIES := liblzma +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ndk-compression/xz/src/liblzma/api +LOCAL_SRC_FILES := b64xz.c +LOCAL_LDFLAGS := -static +include $(BUILD_EXECUTABLE) +# Busybox include jni/busybox/Android.mk endif endif diff --git a/jni/b64xz.c b/jni/b64xz.c new file mode 100644 index 000000000..4c4fac3de --- /dev/null +++ b/jni/b64xz.c @@ -0,0 +1,83 @@ +/* b64xz.c - Base64-XZ Extractor + * + * This program expects data from stdin. The data should be compressed with xz and + * then encoded into base64 format. What b64xz does is basically the reverse of the + * mentioned process: decode base64 to uint8_ts, decompress xz, then dump to stdout + * + * The compiled binary will be hex-dumped into update-binary + * Busybox will be xz-compressed, base64 encoded and dumped into update-binary + * This program is to recover busybox for Magisk installation environment + * + * I intentionally removed stdio. This will result in a smaller binary size because + * all I/O are handled by system calls (read/write) instead of libc wrappers + */ + +#include +#include + +#define BUFSIZE 8192 + +static const char trans_tbl[] = + "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +static void decodeblock(uint8_t* in, uint8_t* out) { + out[0] = (uint8_t)(in[0] << 2 | in[1] >> 4); + out[1] = (uint8_t)(in[1] << 4 | in[2] >> 2); + out[2] = (uint8_t)(((in[2] << 6) & 0xc0) | in[3]); +} + +static int unxz(lzma_stream *strm, void *buf, size_t size) { + lzma_ret ret = 0; + uint8_t out[BUFSIZE]; + strm->next_in = buf; + strm->avail_in = size; + do { + strm->next_out = out; + strm->avail_out = sizeof(out); + ret = lzma_code(strm, LZMA_RUN); + write(STDOUT_FILENO, out, sizeof(out) - strm->avail_out); + } while (strm->avail_out == 0 && ret == LZMA_OK); + + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + write(STDERR_FILENO, "LZMA error!\n", 13); + return ret; +} + +int main(int argc, char const* argv[]) { + if (argc > 1) + return 0; + + uint8_t in[4], buf[BUFSIZE]; + int len = 0, pos = 0; + char c; + + // Setup lzma stream + lzma_stream strm = LZMA_STREAM_INIT; + if (lzma_auto_decoder(&strm, UINT64_MAX, 0) != LZMA_OK) { + write(STDERR_FILENO, "Unable to init lzma stream\n", 28); + return 1; + } + + while (read(STDIN_FILENO, &c, sizeof(c)) == 1) { + c = ((c < 43 || c > 122) ? -1 : (trans_tbl[c - 43] == '$' ? -1 : trans_tbl[c - 43] - 62)); + if (c >= 0) + in[len++] = c; + if (len < 4) + continue; + len = 0; + decodeblock(in, buf + pos); + pos += 3; + if (pos > sizeof(buf) - 3) { + // Buffer is full, unxz + if (unxz(&strm, buf, pos)) + return 1; + pos = 0; + } + } + if (pos) { + if (unxz(&strm, buf, pos)) + return 1; + } + lzma_end(&strm); + return 0; +} diff --git a/scripts/update_binary.sh b/scripts/update_binary.sh index ea4fa3855..938b0295a 100644 --- a/scripts/update_binary.sh +++ b/scripts/update_binary.sh @@ -1,14 +1,16 @@ -# BB_ARM and BB_X86 should be generated in build.py -TMPDIR=/dev/tmp -INSTALLER=$TMPDIR/install -BBDIR=$TMPDIR/bin -BBBIN=$BBDIR/busybox -rm -rf $TMPDIR 2>/dev/null; mkdir -p $BBDIR; touch $BBBIN; chmod 755 $BBBIN -echo $BB_ARM | base64 -d | gzip -d > $BBBIN -if ! $BBBIN --install -s $TMPDIR/bin >/dev/null 2>&1; then - echo $BB_X86 | base64 -d | gzip -d > $BBBIN - $BBBIN --install -s $TMPDIR/bin >/dev/null 2>&1 || exit 1 +# EX_ARM, EX_X86, BB_ARM, and BB_X86 should be generated in build.py +TMPDIR=/dev/tmp; INSTALLER=$TMPDIR/install; BBDIR=$TMPDIR/bin +EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox +rm -rf $TMPDIR 2>/dev/null; mkdir -p $BBDIR +touch $EXBIN $BBBIN; chmod 755 $EXBIN $BBBIN +echo -ne $EX_ARM > $EXBIN +if $EXBIN --test 2>/dev/null; then + echo $BB_ARM | $EXBIN > $BBBIN +else + echo -ne $EX_x86 > $EXBIN + echo $BB_x86 | $EXBIN > $BBBIN fi +$BBBIN --install -s $TMPDIR/bin export PATH=$BBDIR:$PATH mkdir -p $INSTALLER unzip -o "$3" -d $INSTALLER