diff --git a/.gitmodules b/.gitmodules index f2b25b1..93db729 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "zstd"] path = zstd url = https://github.com/facebook/zstd +[submodule "minlzma"] + path = minlzma + url = https://github.com/dimkr/minlzma diff --git a/README.md b/README.md index da8a7ab..b2a4951 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,11 @@ papaw consists of a small executable (~15-40K) containing a decompressor. It ext The payload executable is extracted to a temporary file. When running as root, this is done by mounting a tmpfs file system and lazily unmounting it before the extraction. -## Supported Compression Algorithms +## Supported Compression Algorithms and Implementations * LZMA2, using [XZ Embedded](https://tukaani.org/xz/embedded.html) (the default) * LZMA1, using the [LZMA SDK](https://www.7-zip.org/sdk.html) decompressor +* LZMA2, using [Minimal LZMA](https://github.com/ionescu007/minlzma) * Zstandard, using the [zstd](https://github.com/facebook/zstd) decompressor * Deflate, using [miniz](https://github.com/richgel999/miniz) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2aefb58..143125b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,6 +37,10 @@ steps: displayName: 'LAMA1 Test' - script: docker run --privileged -w /root/papaw -v `pwd`:/root/papaw $(imageName) ./ci/build.sh lzma displayName: 'LAMA1 Build' +- script: docker run --privileged -w /root/papaw -v `pwd`:/root/papaw $(imageName) ./ci/test.sh minlzma + displayName: 'Minimal LAMA Test' +- script: docker run --privileged -w /root/papaw -v `pwd`:/root/papaw $(imageName) ./ci/build.sh minlzma + displayName: 'Minimal LAMA Build' - script: docker run --privileged -w /root/papaw -v `pwd`:/root/papaw $(imageName) ./ci/test.sh zstd displayName: 'Zstandard Test' - script: docker run --privileged -w /root/papaw -v `pwd`:/root/papaw $(imageName) ./ci/build.sh zstd diff --git a/ci/test.sh b/ci/test.sh index 81fac6c..44c05f6 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -92,7 +92,11 @@ test x`./build-$1/test_crasher` = x test -z "`ls /tmp/core* 2>/dev/null`" # make sure there are no compression-related strings -test -z "`strings -a ./build-$1/test_putser | grep -i -e $1 -e miniz -e zlib -e zstandard -e zstd -e huff -e rle -e copy -e license -e papaw`" +DESTDIR=out ninja -C build-$1 install +for i in ./build-$1/out/usr/local/bin/papaw ./build-$1/test_putser +do + test -z "`strings -a $i | grep -i -e $1 -e miniz -e zlib -e zstandard -e zstd -e huff -e rle -e copy -e license -e papaw`" +done # the payload should be extracted to dir_prefix here=`pwd` diff --git a/meson.build b/meson.build index 2c3944a..f173c65 100644 --- a/meson.build +++ b/meson.build @@ -76,6 +76,8 @@ if compression == 'lzma' 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 == 'minlzma' + compression_cflags = ['-DPAPAW_MINLZMA'] elif compression == 'zstd' compression_cflags = ['-DPAPAW_ZSTD'] compression_includes = [] diff --git a/meson_options.txt b/meson_options.txt index bf9b65c..905ab17 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,4 +2,4 @@ option('dir_prefix', type: 'string', value: '/tmp') option('allow_coredumps', type: 'boolean') option('allow_ptrace', type: 'boolean') option('ci', type: 'boolean', value: false) -option('compression', type: 'combo', choices: ['xz', 'lzma', 'zstd', 'deflate']) +option('compression', type: 'combo', choices: ['xz', 'lzma', 'minlzma', 'zstd', 'deflate']) diff --git a/minlzma b/minlzma new file mode 160000 index 0000000..3081dc7 --- /dev/null +++ b/minlzma @@ -0,0 +1 @@ +Subproject commit 3081dc7bcce14a0c856ad58c5b39347ebac8eac1 diff --git a/papaw.c b/papaw.c index 845b146..c4a98aa 100644 --- a/papaw.c +++ b/papaw.c @@ -86,6 +86,13 @@ static void xfree(void *); # define LZMA_PROPS_SIZE 6 # include "lzma/C/LzmaDec.c" # define LZMA_HEADER_SIZE LZMA_PROPS_SIZE + 8 +#elif defined(PAPAW_MINLZMA) +# include "minlzma/minlzlib/dictbuf.c" +# include "minlzma/minlzlib/inputbuf.c" +# include "minlzma/minlzlib/lzma2dec.c" +# include "minlzma/minlzlib/xzstream.c" +# include "minlzma/minlzlib/lzmadec.c" +# include "minlzma/minlzlib/rangedec.c" #elif defined(PAPAW_ZSTD) # define DYNAMIC_BMI2 0 # define ZSTD_NO_INLINE @@ -152,6 +159,9 @@ static bool extract(const int out, unsigned char *p; ELzmaStatus status; const ISzAlloc alloc = {xalloc, xfree}; +#elif defined(PAPAW_MINLZMA) + unsigned char *p; + uint32_t dlen; #elif defined(PAPAW_ZSTD) unsigned char *p; #elif defined(PAPAW_DEFLATE) @@ -163,7 +173,7 @@ static bool extract(const int out, if (ftruncate(out, (off_t)olen) < 0) return false; -#if defined(PAPAW_XZ) || defined(PAPAW_LZMA) || defined(PAPAW_ZSTD) || defined(PAPAW_DEFLATE) +#if defined(PAPAW_XZ) || defined(PAPAW_LZMA) || defined(PAPAW_MINLZMA) || defined(PAPAW_ZSTD) || defined(PAPAW_DEFLATE) if (clen != olen) goto decompress; #endif @@ -228,6 +238,20 @@ static bool extract(const int out, return false; } + munmap(p, (size_t)olen); + return true; +#elif defined(PAPAW_MINLZMA) +decompress: + p = mmap(NULL, (size_t)olen, PROT_WRITE, MAP_SHARED, out, 0); + if (p == MAP_FAILED) + return false; + + dlen = olen; + if (!XzDecode((uint8_t *)data, clen, p, &dlen) || (dlen != olen)) { + munmap(p, (size_t)olen); + return false; + } + munmap(p, (size_t)olen); return true; #elif defined(PAPAW_ZSTD)