From 4d22ca8acd0144eb73ebdc6121b6bc1d979d584b Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 11:30:43 +0200 Subject: [PATCH 01/12] papawify: hide magic numbers and headers binwalk looks for --- meson.build | 6 +++++- papaw.c | 49 ++++++++++++++++++++++--------------------------- papawify.in | 7 ++++--- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/meson.build b/meson.build index da9cf2b..bbcffdb 100644 --- a/meson.build +++ b/meson.build @@ -65,6 +65,7 @@ compression_sources = [] compression_cflags = ['-DPAPAW_XZ', '-Wno-unused-function'] compression_includes = [include_directories('xz-embedded/userspace'), include_directories('xz-embedded/linux/include/linux')] cfg.set('COMPRESSION_CMD', '"xz", "-c", "--check=none", "--lzma2=preset=9e,dict=512KiB"') +cfg.set('OBFUSCATION', 'obfuscated = b"\\0\\0\\0\\x08\\0" + compressed[5:-2] + b"\\0\\0"') cfg.set('DECOMPRESSION_CMD', '"xz", "-d"') compression = get_option('compression') @@ -72,6 +73,7 @@ if compression == 'lzma' compression_cflags = ['-DPAPAW_LZMA'] compression_includes = [] cfg.set('COMPRESSION_CMD', '"xz", "-c", "--format=lzma", "--lzma1=preset=9e,dict=512KiB"') + cfg.set('OBFUSCATION', 'compressed') elif compression == 'zstd' compression_cflags = ['-DPAPAW_ZSTD'] compression_includes = [] @@ -93,11 +95,13 @@ elif compression == 'zstd' ]) ] cfg.set('COMPRESSION_CMD', '"zstd", "-q", "-c", "-19", "--no-check"') + cfg.set('OBFUSCATION', 'obfuscated = b"\\0\\0\\0\\x08" + compressed[4:]') cfg.set('DECOMPRESSION_CMD', '"zstd", "-d"') elif compression == 'deflate' compression_cflags = ['-DPAPAW_DEFLATE'] compression_includes = [] - cfg.set('COMPRESSION_CMD', '"python3", "-c", "import sys, zlib; sys.stdout.buffer.write(zlib.compress(open(sys.argv[1], \'rb\').read(), 9))"') + cfg.set('COMPRESSION_CMD', '"python3", "-c", "import sys, zlib; o = zlib.compressobj(9, zlib.DEFLATED, -15, 9, zlib.Z_DEFAULT_STRATEGY); o.compress(open(sys.argv[1], \'rb\').read()); sys.stdout.buffer.write(o.flush())"') + cfg.set('OBFUSCATION', 'compressed') cfg.set('DECOMPRESSION_CMD', '"python3", "-c", "import sys, zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read()))"') endif diff --git a/papaw.c b/papaw.c index 550c7ef..72eaa30 100644 --- a/papaw.c +++ b/papaw.c @@ -43,11 +43,7 @@ # include #endif -#ifdef PAPAW_XZ -# define MINIZ_NO_ZLIB_APIS -#endif - -#if defined(PAPAW_XZ) || defined(PAPAW_DEFLATE) +#ifdef PAPAW_DEFLATE # define MINIZ_NO_ARCHIVE_APIS static void *xalloc(size_t); @@ -58,16 +54,17 @@ static void xfree(void *); # define MZ_MALLOC xalloc # define MZ_FREE xfree -# ifdef PAPAW_DEFLATE -# include "miniz/miniz_tinfl.c" -# endif - +# include "miniz/miniz_tinfl.c" # include "miniz/miniz.c" -#endif - -#ifdef PAPAW_XZ +#elif defined(PAPAW_XZ) # define XZ_EXTERN static +# include "xz-embedded/linux/lib/xz/xz_stream.h" +# undef HEADER_MAGIC +# define HEADER_MAGIC "\0\0\0\x08\0" +# undef FOOTER_MAGIC +# define FOOTER_MAGIC "\0\0" + # include "xz-embedded/userspace/xz_config.h" # undef kmalloc # define kmalloc(size, flags) xalloc(size) @@ -78,6 +75,7 @@ static void xfree(void *); # undef vfree # define vfree xfree +# include "xz-embedded/linux/lib/xz/xz_crc32.c" # include "xz-embedded/linux/lib/xz/xz_dec_lzma2.c" # include "xz-embedded/linux/lib/xz/xz_dec_stream.c" #elif defined(PAPAW_LZMA) @@ -93,15 +91,6 @@ static void xfree(void *); # include "zstddeclib.h" #endif -#ifdef PAPAW_XZ - -static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) -{ - return (uint32_t)mz_crc32((mz_ulong)crc, buf, size); -} - -#endif - #if defined(PAPAW_LZMA) || defined(PAPAW_XZ) || defined(PAPAW_DEFLATE) # if defined(PAPAW_XZ) || defined(PAPAW_DEFLATE) @@ -161,8 +150,8 @@ static bool extract(const int out, #elif defined(PAPAW_ZSTD) unsigned char *p; #elif defined(PAPAW_DEFLATE) + mz_stream strm; unsigned char *p; - mz_ulong outlen; #endif void *map; @@ -201,6 +190,8 @@ static bool extract(const int out, return false; } + xz_crc32_init(); + if ((xz_dec_run(xz, &xzbuf) != XZ_STREAM_END) || (xzbuf.out_size != olen)) { xz_dec_end(xz); munmap(xzbuf.out, (size_t)olen); @@ -254,15 +245,19 @@ static bool extract(const int out, return true; #elif defined(PAPAW_DEFLATE) decompress: + if (mz_inflateInit2(&strm, -15) != MZ_OK) + return false; + p = mmap(NULL, (size_t)olen, PROT_WRITE, MAP_SHARED, out, 0); if (p == MAP_FAILED) return false; - outlen = (mz_ulong)olen; - if (mz_uncompress(p, - &outlen, - data, - (mz_ulong)clen) != MZ_OK) { + strm.next_in = data; + strm.avail_in = clen; + strm.next_out = p; + strm.avail_out = olen; + + if (mz_inflate(&strm, MZ_FINISH) != MZ_STREAM_END) { munmap(p, (size_t)olen); return false; } diff --git a/papawify.in b/papawify.in index 0210a03..d9735e4 100755 --- a/papawify.in +++ b/papawify.in @@ -38,12 +38,13 @@ cmd = [@COMPRESSION_CMD@] if args.uncompressed: cmd = ["cat"] -data = subprocess.check_output(cmd + [args.input]) +compressed = subprocess.check_output(cmd + [args.input]) +obfuscated = @OBFUSCATION@ with open(args.stub, "rb") as infp, open(args.output, "wb") as outfp: outfp.write(infp.read()) - outfp.write(data) + outfp.write(obfuscated) outfp.write(struct.pack(">L", os.path.getsize(args.input))) - outfp.write(struct.pack(">L", len(data))) + outfp.write(struct.pack(">L", len(obfuscated))) os.chmod(args.output, 0o755) From aec4d8aab591c83ce52e42b9b370332c94547abd Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 11:46:19 +0200 Subject: [PATCH 02/12] papawify: obfuscate LZMA headers --- meson.build | 2 +- papaw.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index bbcffdb..21da5bf 100644 --- a/meson.build +++ b/meson.build @@ -73,7 +73,7 @@ if compression == 'lzma' compression_cflags = ['-DPAPAW_LZMA'] compression_includes = [] cfg.set('COMPRESSION_CMD', '"xz", "-c", "--format=lzma", "--lzma1=preset=9e,dict=512KiB"') - cfg.set('OBFUSCATION', 'compressed') + cfg.set('OBFUSCATION', 'compressed[:5] + b"\\x08" + compressed[5:]') elif compression == 'zstd' compression_cflags = ['-DPAPAW_ZSTD'] compression_includes = [] diff --git a/papaw.c b/papaw.c index 72eaa30..a8646f9 100644 --- a/papaw.c +++ b/papaw.c @@ -79,6 +79,9 @@ static void xfree(void *); # include "xz-embedded/linux/lib/xz/xz_dec_lzma2.c" # include "xz-embedded/linux/lib/xz/xz_dec_stream.c" #elif defined(PAPAW_LZMA) +# include "lzma/C/LzmaDec.h" +# undef LZMA_PROPS_SIZE +# define LZMA_PROPS_SIZE 6 # include "lzma/C/LzmaDec.c" # define LZMA_HEADER_SIZE LZMA_PROPS_SIZE + 8 #elif defined(PAPAW_ZSTD) From ea8b7bfcc2ce938113d7c4894292453340b3c404 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 11:49:26 +0200 Subject: [PATCH 03/12] ci: make sure binwalk fails --- ci/test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/test.sh b/ci/test.sh index 1151d89..da22ba5 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -97,6 +97,9 @@ meson configure build-$1 -Ddir_prefix=$here ninja -C build-$1 test -n "`strace -qqe mkdir ./build-$1/test_putser 2>&1 | grep $here`" +# make sure binwalk fails to identify the payload format +test `binwalk -M ./build-$1/test_putser | grep 0x | wc -l` -eq 1 + # make sure there are no file descriptor leaks valgrind -q --leak-check=full --error-exitcode=1 --malloc-fill=1 --free-fill=1 --track-fds=yes ./build-$1/test_putser From 208a25f8564a7b01f8f2111086899e8138097af9 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 11:54:04 +0200 Subject: [PATCH 04/12] papaw: fix breakage --- papaw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/papaw.c b/papaw.c index a8646f9..6152d1d 100644 --- a/papaw.c +++ b/papaw.c @@ -46,12 +46,12 @@ #ifdef PAPAW_DEFLATE # define MINIZ_NO_ARCHIVE_APIS +# include "miniz/miniz_common.h" static void *xalloc(size_t); static void xfree(void *); -# include "miniz/miniz_common.h" # undef MZ_MALLOC -# undef MZ_FREE # define MZ_MALLOC xalloc +# undef MZ_FREE # define MZ_FREE xfree # include "miniz/miniz_tinfl.c" @@ -66,6 +66,8 @@ static void xfree(void *); # define FOOTER_MAGIC "\0\0" # include "xz-embedded/userspace/xz_config.h" +static void *xalloc(size_t); +static void xfree(void *); # undef kmalloc # define kmalloc(size, flags) xalloc(size) # undef kfree From 8b811caf69bb7b210ff19e7f38eaa76962751a16 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:21:06 +0200 Subject: [PATCH 05/12] unpapawify: fix breakage --- meson.build | 42 +++++++++++++++++++++++++----------------- papaw.c | 2 +- unpapawify.in | 4 +++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/meson.build b/meson.build index 21da5bf..4b02014 100644 --- a/meson.build +++ b/meson.build @@ -66,6 +66,7 @@ compression_cflags = ['-DPAPAW_XZ', '-Wno-unused-function'] compression_includes = [include_directories('xz-embedded/userspace'), include_directories('xz-embedded/linux/include/linux')] cfg.set('COMPRESSION_CMD', '"xz", "-c", "--check=none", "--lzma2=preset=9e,dict=512KiB"') cfg.set('OBFUSCATION', 'obfuscated = b"\\0\\0\\0\\x08\\0" + compressed[5:-2] + b"\\0\\0"') +cfg.set('DEOBFUSCATION', 'deobfuscated = b"\\3757zXZ" + obfuscated[5:-2] + b"YZ"') cfg.set('DECOMPRESSION_CMD', '"xz", "-d"') compression = get_option('compression') @@ -74,35 +75,42 @@ if compression == 'lzma' compression_includes = [] cfg.set('COMPRESSION_CMD', '"xz", "-c", "--format=lzma", "--lzma1=preset=9e,dict=512KiB"') cfg.set('OBFUSCATION', 'compressed[:5] + b"\\x08" + compressed[5:]') + cfg.set('DEOBFUSCATION', 'deobfuscated = obfuscated[:5] + obfuscated[6:]') elif compression == 'zstd' compression_cflags = ['-DPAPAW_ZSTD'] compression_includes = [] compression_sources += [ - custom_target('zstddeclib', - input: 'zstd/contrib/single_file_decoder/zstddeclib-in.c', - output: 'zstddeclib.h', - command: [ - join_paths(meson.current_source_dir(), 'zstd/contrib/single_file_decoder/combine.sh'), - '-r', - join_paths(meson.current_source_dir(), 'zstd/lib'), - '-r', - join_paths(meson.current_source_dir(), 'zstd/lib/common'), - '-r', - join_paths(meson.current_source_dir(), 'zstd/lib/decompress'), - '-o', - '@OUTPUT@', - '@INPUT@' - ]) + custom_target('zstddeclib_obfuscated', + input: custom_target('zstddeclib', + input: 'zstd/contrib/single_file_decoder/zstddeclib-in.c', + output: 'zstddeclib.h', + command: [ + join_paths(meson.current_source_dir(), 'zstd/contrib/single_file_decoder/combine.sh'), + '-r', + join_paths(meson.current_source_dir(), 'zstd/lib'), + '-r', + join_paths(meson.current_source_dir(), 'zstd/lib/common'), + '-r', + join_paths(meson.current_source_dir(), 'zstd/lib/decompress'), + '-o', + '@OUTPUT@', + '@INPUT@' + ]), + output: 'zstddeclib_obfuscated.h', + command: ['sed', 's/0xFD2FB528/0x00000008/', '@INPUT@'], + capture: true) ] cfg.set('COMPRESSION_CMD', '"zstd", "-q", "-c", "-19", "--no-check"') - cfg.set('OBFUSCATION', 'obfuscated = b"\\0\\0\\0\\x08" + compressed[4:]') + cfg.set('OBFUSCATION', 'obfuscated = b"\\x08\\0\\0\\x00" + compressed[4:]') + cfg.set('DEOBFUSCATION', 'deobfuscated = b"\\xfd\\x2f\\xb5\\x28" + obfuscated[4:]') cfg.set('DECOMPRESSION_CMD', '"zstd", "-d"') elif compression == 'deflate' compression_cflags = ['-DPAPAW_DEFLATE'] compression_includes = [] cfg.set('COMPRESSION_CMD', '"python3", "-c", "import sys, zlib; o = zlib.compressobj(9, zlib.DEFLATED, -15, 9, zlib.Z_DEFAULT_STRATEGY); o.compress(open(sys.argv[1], \'rb\').read()); sys.stdout.buffer.write(o.flush())"') cfg.set('OBFUSCATION', 'compressed') - cfg.set('DECOMPRESSION_CMD', '"python3", "-c", "import sys, zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read()))"') + cfg.set('DEOBFUSCATION', 'deobfuscated = obfuscated') + cfg.set('DECOMPRESSION_CMD', '"python3", "-c", "import sys, zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read(), -15))"') endif papaw = executable('papaw', diff --git a/papaw.c b/papaw.c index 6152d1d..ef24f50 100644 --- a/papaw.c +++ b/papaw.c @@ -93,7 +93,7 @@ static void xfree(void *); # define ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT # define HUF_FORCE_DECOMPRESS_X1 # define MEM_FORCE_MEMORY_ACCESS 0 -# include "zstddeclib.h" +# include "zstddeclib_obfuscated.h" #endif #if defined(PAPAW_LZMA) || defined(PAPAW_XZ) || defined(PAPAW_DEFLATE) diff --git a/unpapawify.in b/unpapawify.in index 95d05b8..73b4eec 100755 --- a/unpapawify.in +++ b/unpapawify.in @@ -37,6 +37,8 @@ with open(sys.argv[1], "rb") as infp: xz = subprocess.Popen([@DECOMPRESSION_CMD@], stdin=subprocess.PIPE, stdout=open(sys.argv[2], "wb")) - xz.stdin.write(data[-(size + 8):-8]) + obfuscated = data[-(size + 8):-8] + @DEOBFUSCATION@ + xz.stdin.write(deobfuscated) xz.stdin.close() xz.wait() From ee7911ff71c1c0c8a40bf61831abf084e32a9940 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:27:50 +0200 Subject: [PATCH 06/12] papaw: remove miniz assertions --- papaw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/papaw.c b/papaw.c index ef24f50..1ff1e93 100644 --- a/papaw.c +++ b/papaw.c @@ -53,6 +53,8 @@ static void xfree(void *); # define MZ_MALLOC xalloc # undef MZ_FREE # define MZ_FREE xfree +# undef MZ_ASSERT +# define MZ_ASSERT(x) do {} while (0) # include "miniz/miniz_tinfl.c" # include "miniz/miniz.c" From acefd5f3e0edf6543194cc445b5bddb028ecff9b Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:30:11 +0200 Subject: [PATCH 07/12] papawify: obfuscate only compressed payloads --- papawify.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/papawify.in b/papawify.in index d9735e4..aff25de 100755 --- a/papawify.in +++ b/papawify.in @@ -39,7 +39,10 @@ if args.uncompressed: cmd = ["cat"] compressed = subprocess.check_output(cmd + [args.input]) -obfuscated = @OBFUSCATION@ +if args.uncompressed: + obfuscated = compressed +else: + obfuscated = @OBFUSCATION@ with open(args.stub, "rb") as infp, open(args.output, "wb") as outfp: outfp.write(infp.read()) From 18457afc4f01ab66ca20d2450d43c1112b882802 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:34:24 +0200 Subject: [PATCH 08/12] ci: make sure there are no compression-related strings --- ci/test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/test.sh b/ci/test.sh index da22ba5..cd20455 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -100,6 +100,9 @@ test -n "`strace -qqe mkdir ./build-$1/test_putser 2>&1 | grep $here`" # make sure binwalk fails to identify the payload format test `binwalk -M ./build-$1/test_putser | grep 0x | wc -l` -eq 1 +# make sure there are no compression-related strings +test -z "`strings -a ./build-$1/test_putser | grep -i -e lz -e xz -e deflate -e miniz -e zlib -e zstandard -e zstd -e huff -e rle -e copy -e license`" + # make sure there are no file descriptor leaks valgrind -q --leak-check=full --error-exitcode=1 --malloc-fill=1 --free-fill=1 --track-fds=yes ./build-$1/test_putser From 73a7fe80281eb7121a88dbf2d1675e36e60c4ba1 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:36:57 +0200 Subject: [PATCH 09/12] unpapawify: fix breakage --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4b02014..8471c5a 100644 --- a/meson.build +++ b/meson.build @@ -102,7 +102,7 @@ elif compression == 'zstd' ] cfg.set('COMPRESSION_CMD', '"zstd", "-q", "-c", "-19", "--no-check"') cfg.set('OBFUSCATION', 'obfuscated = b"\\x08\\0\\0\\x00" + compressed[4:]') - cfg.set('DEOBFUSCATION', 'deobfuscated = b"\\xfd\\x2f\\xb5\\x28" + obfuscated[4:]') + cfg.set('DEOBFUSCATION', 'deobfuscated = b"\\x28\\xb5\\x2f\\xfd" + obfuscated[4:]') cfg.set('DECOMPRESSION_CMD', '"zstd", "-d"') elif compression == 'deflate' compression_cflags = ['-DPAPAW_DEFLATE'] From 910e852c58418398b44634d6d75f43e8769bd415 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:48:40 +0200 Subject: [PATCH 10/12] papaw: fix breakage --- papaw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/papaw.c b/papaw.c index 1ff1e93..54423dd 100644 --- a/papaw.c +++ b/papaw.c @@ -157,7 +157,7 @@ static bool extract(const int out, #elif defined(PAPAW_ZSTD) unsigned char *p; #elif defined(PAPAW_DEFLATE) - mz_stream strm; + mz_stream strm = {0}; unsigned char *p; #endif void *map; @@ -186,9 +186,7 @@ static bool extract(const int out, return false; xzbuf.in = data; - xzbuf.in_pos = 0; xzbuf.in_size = clen; - xzbuf.out_pos = 0; xzbuf.out_size = olen; xz = xz_dec_init(XZ_SINGLE, 0); From 3c404dcb1e33b3736f831a164df4c34e71117f34 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 12:53:16 +0200 Subject: [PATCH 11/12] papaw: fix breakage --- papaw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papaw.c b/papaw.c index 54423dd..84e1300 100644 --- a/papaw.c +++ b/papaw.c @@ -147,7 +147,7 @@ static bool extract(const int out, const uint32_t olen) { #ifdef PAPAW_XZ - struct xz_buf xzbuf; + struct xz_buf xzbuf = {0}; struct xz_dec *xz; #elif defined(PAPAW_LZMA) SizeT inlen = clen - LZMA_HEADER_SIZE, outlen = olen; From fc02489ae93fa84e865df6a02f9e6d3ccd8655a5 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 7 Feb 2020 13:04:26 +0200 Subject: [PATCH 12/12] papawify: fix breakage --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8471c5a..fadef2c 100644 --- a/meson.build +++ b/meson.build @@ -107,7 +107,7 @@ elif compression == 'zstd' elif compression == 'deflate' compression_cflags = ['-DPAPAW_DEFLATE'] compression_includes = [] - cfg.set('COMPRESSION_CMD', '"python3", "-c", "import sys, zlib; o = zlib.compressobj(9, zlib.DEFLATED, -15, 9, zlib.Z_DEFAULT_STRATEGY); o.compress(open(sys.argv[1], \'rb\').read()); sys.stdout.buffer.write(o.flush())"') + cfg.set('COMPRESSION_CMD', '"python3", "-c", "import sys, zlib; o = zlib.compressobj(9, zlib.DEFLATED, -15, 9, zlib.Z_DEFAULT_STRATEGY); sys.stdout.buffer.write(o.compress(open(sys.argv[1], \'rb\').read())); sys.stdout.buffer.write(o.flush())"') cfg.set('OBFUSCATION', 'compressed') cfg.set('DEOBFUSCATION', 'deobfuscated = obfuscated') cfg.set('DECOMPRESSION_CMD', '"python3", "-c", "import sys, zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read(), -15))"')