This is an automated email from the git hooks/post-receive script. evgeni pushed a commit to branch master in repository unshield.
commit e319415d1a38fe82d33703cc0f095c26aba73692 Author: Evgeni Golov <[email protected]> Date: Mon Dec 26 14:27:08 2016 +0100 New upstream version 1.4 --- .gitignore | 34 ++----- .travis.yml | 3 +- CMakeLists.txt | 7 +- README.md | 15 ++- lib/convert_utf/CMakeLists.txt | 2 +- lib/file.c | 81 ++++++++++++--- lib/helper.c | 2 +- lib/internal.h | 2 +- lib/libunshield.c | 10 ++ lib/libunshield.h | 3 + lib/log.h | 8 +- lib/md5/CMakeLists.txt | 2 +- lib/unshield_config.h.in | 3 + man/unshield.1 | 3 + rebuild.sh | 6 +- run-tests.sh | 5 + src/CMakeLists.txt | 2 + src/unshield-deobfuscate.c | 4 +- src/unshield.c | 174 ++++++++++++++++++++++++++++++--- test/v0/avigomanager.md5 | 50 ++++++++++ test/v0/avigomanager.sh | 45 +++++++++ test/v5/CVE-2015-1386/CVE-2015-1386.sh | 35 +++++++ test/v5/CVE-2015-1386/data1.cab | Bin 0 -> 520 bytes test/v5/CVE-2015-1386/data1.hdr | Bin 0 -> 3113 bytes 24 files changed, 427 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index a099270..38bafce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,21 @@ -aclocal.m4 -autom4te.cache/ -config.guess -config.log -config.status -config.sub -configure -configure.ac -depcomp -.deps/ -install-sh +*.a +**/CMakeCache.txt +**/CMakeFiles/ +*cmake_install.cmake +**/CMakeScripts/ +*.core +.gdb_history *.la +lib/libunshield.so* .libs -lib/stamp-h1 -libtool lib/unshield_config.h libunshield.pc *.lo ltmain.sh Makefile -Makefile.in -missing *.o src/unshield src/unshield-deobfuscate -*.core .*.swp -.gdb_history -compile - -# CMake ignore -**/CMakeFiles/ -**/CMakeScripts/ -*cmake_install.cmake -**/CMakeCache.txt +.idea +build diff --git a/.travis.yml b/.travis.yml index ea293c9..ac69139 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,5 @@ env: - USE_OUR_OWN_MD5=1 BUILD_STATIC=1 # cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield . && make && make install -script: cmake -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield -DUSE_OUR_OWN_MD5=$USE_OUR_OWN_MD5 -DBUILD_STATIC=$BUILD_STATIC . && make && make install +script: mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield -DUSE_OUR_OWN_MD5=$USE_OUR_OWN_MD5 -DBUILD_STATIC=$BUILD_STATIC .. && make && make install && ../run-tests.sh + diff --git a/CMakeLists.txt b/CMakeLists.txt index c7a748b..4d43176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(unshield) # Mimic CMP0048 which is avaliable only for cmake 3.0 and later set(PROJECT_VERSION_MAJOR 1) -set(PROJECT_VERSION_MINOR 3) +set(PROJECT_VERSION_MINOR 4) set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") option(BUILD_STATIC "Build static version of libunshield" OFF) @@ -27,6 +27,7 @@ check_include_files(sys/stat.h HAVE_SYS_STAT_H) check_include_files(sys/types.h HAVE_SYS_TYPES_H) check_include_files(unistd.h HAVE_UNISTD_H) check_function_exists(fnmatch HAVE_FNMATCH) +check_function_exists(iconv HAVE_ICONV) set(SIZE_FORMAT "zi") check_c_source_compiles("#include <stdio.h>\nint main(int argc, char **argv) { size_t value = 0; printf(\"%${SIZE_FORMAT}\", value); return 0; }" SIZE_FORMAT_ZI) @@ -58,6 +59,8 @@ message(STATUS "BUILD_STATIC: ${BUILD_STATIC}") add_definitions(-DHAVE_CONFIG_H) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/unshield_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/lib/unshield_config.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libunshield.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libunshield.pc @ONLY) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/lib) # To force position independent code for static libs on Linux x64 if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") @@ -68,4 +71,4 @@ add_subdirectory(lib) add_subdirectory(src) install(FILES man/unshield.1 DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/man/man1) -install(FILES libunshield.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libunshield.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/README.md b/README.md index 4cfc190..a64e96b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -UNSHIELD +Unshield ======== [](https://travis-ci.org/twogood/unshield) -DICTIONARY +Support Unshield development +---------------------------- + +- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SQ7PEFMJK36AU) +- [Pledgie](https://pledgie.com/campaigns/29872) + + +Dictionary ---------- InstallShield (IS): see www.installshield.com @@ -14,7 +21,7 @@ InstallShield Cabinet File (ISCF): A .cab file used by IS. Microsoft Cabinet File (MSCF): A .cab file used by Microsoft. -ABOUT UNSHIELD +About Unshield -------------- To install a Pocket PC application remotely, an installable @@ -66,7 +73,7 @@ implementation are: - Be able to extract files from InstallShield Cabinet Files -LICENSE +License ------- Unshield uses the MIT license. The short version is "do as you diff --git a/lib/convert_utf/CMakeLists.txt b/lib/convert_utf/CMakeLists.txt index eb60fe3..4b956e9 100644 --- a/lib/convert_utf/CMakeLists.txt +++ b/lib/convert_utf/CMakeLists.txt @@ -6,4 +6,4 @@ set(LIBCONVERT_UTF_SOURCES "ConvertUTF.c" ) -add_library(convert_utf ${LIBCONVERT_UTF_HEADES} ${LIBCONVERT_UTF_SOURCES}) \ No newline at end of file +add_library(convert_utf STATIC ${LIBCONVERT_UTF_HEADES} ${LIBCONVERT_UTF_SOURCES}) diff --git a/lib/file.c b/lib/file.c index f08dd7d..2dc7969 100644 --- a/lib/file.c +++ b/lib/file.c @@ -270,11 +270,14 @@ static int unshield_uncompress_old(Byte *dest, uLong *destLen, Byte *source, uLo if (err != Z_OK) return err; - err = inflate(&stream, Z_BLOCK); - if (err != Z_OK) + while (stream.avail_in > 1) { - inflateEnd(&stream); - return err; + err = inflate(&stream, Z_BLOCK); + if (err != Z_OK) + { + inflateEnd(&stream); + return err; + } } *destLen = stream.total_out; @@ -527,6 +530,11 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si unshield_trace("Trying to read 0x%x bytes from offset %08x in volume %i", bytes_to_read, ftell(reader->volume_file), reader->volume); #endif + if (bytes_to_read == 0) + { + unshield_error("bytes_to_read can't be zero"); + goto exit; + } if (bytes_to_read != fread(p, 1, bytes_to_read, reader->volume_file)) { @@ -596,7 +604,7 @@ static UnshieldReader* unshield_reader_create(/*{{{*/ } /* Start with the correct volume for IS5 cabinets */ - if (reader->unshield->header_list->major_version == 5 && + if (reader->unshield->header_list->major_version <= 5 && index > (int)reader->volume_header.last_file_index) { unshield_trace("Trying next volume..."); @@ -637,7 +645,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ FILE* output = NULL; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE+1); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); - int bytes_left; + unsigned int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; @@ -681,7 +689,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ if (filename) { - output = fopen(filename, "w"); + output = fopen(filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -708,14 +716,19 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ if (!unshield_reader_read(reader, &bytes_to_read, sizeof(bytes_to_read))) { -#if VERBOSE - unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", + unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", sizeof(bytes_to_read), index, unshield_file_name(unshield, index), file_descriptor->volume); -#endif goto exit; } bytes_to_read = letoh16(bytes_to_read); + if (bytes_to_read == 0) + { + unshield_error("bytes_to_read can't be zero"); + unshield_error("HINT: Try unshield_file_save_old() or -O command line parameter!"); + goto exit; + } + if (!unshield_reader_read(reader, input_buffer, bytes_to_read)) { #if VERBOSE @@ -734,6 +747,10 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ { unshield_error("Decompression failed with code %i. bytes_to_read=%i, volume_bytes_left=%i, volume=%i, read_bytes=%i", result, bytes_to_read, reader->volume_bytes_left, file_descriptor->volume, read_bytes); + if (result == Z_DATA_ERROR) + { + unshield_error("HINT: Try unshield_file_save_old() or -O command line parameter!"); + } goto exit; } @@ -834,7 +851,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) FILE* output = NULL; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); - int bytes_left; + unsigned int bytes_left; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; @@ -874,7 +891,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) if (filename) { - output = fopen(filename, "w"); + output = fopen(filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -950,7 +967,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) size_t input_buffer_size = BUFFER_SIZE; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); - int bytes_left; + unsigned int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; @@ -991,7 +1008,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) if (filename) { - output = fopen(filename, "w"); + output = fopen(filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -1011,6 +1028,13 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) uLong bytes_to_write = 0; int result; + if (reader->volume_bytes_left == 0 && !unshield_reader_open_volume(reader, reader->volume + 1)) + { + unshield_error("Failed to open volume %i to read %i more bytes", + reader->volume + 1, bytes_left); + goto exit; + } + if (file_descriptor->flags & FILE_COMPRESSED) { static const uint8_t END_OF_CHUNK[4] = { 0x00, 0x00, 0xff, 0xff }; @@ -1053,6 +1077,33 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) chunk_size = match - chunk_buffer; + /* + Detect when the chunk actually contains the end of chunk marker. + + Needed by Qtime.smk from "The Feeble Files - spanish version". + + The first bit of a compressed block is always zero, so we apply this + workaround if it's a one. + + A possibly more proper fix for this would be to have + unshield_uncompress_old eat compressed data and discard chunk + markers inbetween. + */ + while ((chunk_size + sizeof(END_OF_CHUNK)) < input_size && + chunk_buffer[chunk_size + sizeof(END_OF_CHUNK)] & 1) + { + unshield_warning("It seems like we have an end of chunk marker inside of a chunk."); + chunk_size += sizeof(END_OF_CHUNK); + match = find_bytes(chunk_buffer + chunk_size, input_size - chunk_size, END_OF_CHUNK, sizeof(END_OF_CHUNK)); + if (!match) + { + unshield_error("Could not find end of chunk for file %i (%s) from input cabinet file %i", + index, unshield_file_name(unshield, index), file_descriptor->volume); + goto exit; + } + chunk_size = match - chunk_buffer; + } + #if VERBOSE >= 3 unshield_trace("chunk_size = 0x%x", chunk_size); #endif @@ -1066,7 +1117,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) if (Z_OK != result) { - unshield_error("Decompression failed with code %i. input_size=%i, volume_bytes_left=%i, volume=%i, read_bytes=%i", + unshield_error("Decompression failed with code %i. input_size=%i, volume_bytes_left=%i, volume=%i, read_bytes=%i", result, input_size, reader->volume_bytes_left, file_descriptor->volume, read_bytes); goto exit; } diff --git a/lib/helper.c b/lib/helper.c index 77ce512..d23cca5 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -69,7 +69,7 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff #if VERBOSE unshield_trace("Opening file '%s'", filename); #endif - result = fopen(filename, "r"); + result = fopen(filename, "rb"); exit: if (sourcedir) diff --git a/lib/internal.h b/lib/internal.h index 0ea88be..1f5718a 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -3,7 +3,7 @@ #define __internal_h__ #include "libunshield.h" -#include "unshield_config.h" +#include "lib/unshield_config.h" #if HAVE_STDINT_H #include <stdint.h> diff --git a/lib/libunshield.c b/lib/libunshield.c index 749ddac..8b10088 100644 --- a/lib/libunshield.c +++ b/lib/libunshield.c @@ -433,4 +433,14 @@ void unshield_close(Unshield* unshield)/*{{{*/ } }/*}}}*/ +bool unshield_is_unicode(Unshield* unshield) +{ + if (unshield) + { + Header* header = unshield->header_list; + return header->major_version >= 17; + } + else + return false; +} diff --git a/lib/libunshield.h b/lib/libunshield.h index 4a429e2..ee34894 100644 --- a/lib/libunshield.h +++ b/lib/libunshield.h @@ -93,6 +93,9 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) /** Deobfuscate a buffer. Seed is 0 at file start */ void unshield_deobfuscate(unsigned char* buffer, size_t size, unsigned* seed); +/** Is the archive Unicode-capable? */ +bool unshield_is_unicode(Unshield* unshield); + #ifdef __cplusplus } #endif diff --git a/lib/log.h b/lib/log.h index 0ab612a..37995dc 100644 --- a/lib/log.h +++ b/lib/log.h @@ -20,17 +20,17 @@ extern "C" void _unshield_log(int level, const char* file, int line, const char* format, ...); #define unshield_trace(format, args...) \ - _unshield_log(UNSHIELD_LOG_LEVEL_TRACE,__PRETTY_FUNCTION__, __LINE__, format, ##args) + _unshield_log(UNSHIELD_LOG_LEVEL_TRACE,__FUNCTION__, __LINE__, format, ##args) #define unshield_warning(format, args...) \ - _unshield_log(UNSHIELD_LOG_LEVEL_WARNING,__PRETTY_FUNCTION__, __LINE__, format, ##args) + _unshield_log(UNSHIELD_LOG_LEVEL_WARNING,__FUNCTION__, __LINE__, format, ##args) #define unshield_warning_unless(cond, format, args...) \ if (!(cond)) \ - _unshield_log(UNSHIELD_LOG_LEVEL_WARNING,__PRETTY_FUNCTION__, __LINE__, format, ##args) + _unshield_log(UNSHIELD_LOG_LEVEL_WARNING,__FUNCTION__, __LINE__, format, ##args) #define unshield_error(format, args...) \ - _unshield_log(UNSHIELD_LOG_LEVEL_ERROR,__PRETTY_FUNCTION__, __LINE__, format, ##args) + _unshield_log(UNSHIELD_LOG_LEVEL_ERROR,__FUNCTION__, __LINE__, format, ##args) #ifdef __cplusplus } diff --git a/lib/md5/CMakeLists.txt b/lib/md5/CMakeLists.txt index 65a9e3a..e5b66d6 100644 --- a/lib/md5/CMakeLists.txt +++ b/lib/md5/CMakeLists.txt @@ -7,4 +7,4 @@ set(LIBMD5_UTF_SOURCES "md5c.c" ) -add_library(md5 ${LIBMD5_UTF_HEADES} ${LIBMD5_UTF_SOURCES}) \ No newline at end of file +add_library(md5 STATIC ${LIBMD5_UTF_HEADES} ${LIBMD5_UTF_SOURCES}) diff --git a/lib/unshield_config.h.in b/lib/unshield_config.h.in index 846ab9d..13d11c1 100644 --- a/lib/unshield_config.h.in +++ b/lib/unshield_config.h.in @@ -64,6 +64,9 @@ /* Define to 1 if your system has a working POSIX `fnmatch' function. */ #cmakedefine HAVE_FNMATCH 1 +/* Define to 1 if your system has a working POSIX `iconv' function. */ +#cmakedefine HAVE_ICONV 1 + /* Defined if we should use our own MD5 routines. */ #cmakedefine01 USE_OUR_OWN_MD5 diff --git a/man/unshield.1 b/man/unshield.1 index 5bc80d7..3b6abd2 100644 --- a/man/unshield.1 +++ b/man/unshield.1 @@ -45,6 +45,9 @@ Use old compression \fB\-r\fR Save raw data (do not decompress) .TP +\fB\-R\fR +Don't do any conversion to file and directory names when extracting. +.TP \fB\-v\fR Be verbose .TP diff --git a/rebuild.sh b/rebuild.sh index b26e856..869f3aa 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -1,4 +1,8 @@ #!/bin/sh +set -e set -x export CFLAGS="-Wall -Werror -ggdb3" -cmake -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield . && make && make install +cd `dirname $0` +mkdir -p build +cd build +cmake -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield .. && make && make install diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..0e902c3 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash +find `dirname $0`/test/v* -name '*.sh' | while read SCRIPT; do + echo -n "Running test $SCRIPT..." + bash ${SCRIPT} && echo "succeeded" || echo "FAILED with code $?" +done diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 211d4eb..2129ed0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,5 @@ +SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + add_executable(unshield "unshield.c") target_link_libraries(unshield libunshield) diff --git a/src/unshield-deobfuscate.c b/src/unshield-deobfuscate.c index 64e4b2f..a389f98 100644 --- a/src/unshield-deobfuscate.c +++ b/src/unshield-deobfuscate.c @@ -20,7 +20,7 @@ int main(int argc, char** argv) exit(1); } - input = fopen(argv[1], "r"); + input = fopen(argv[1], "rb"); if (!input) { fprintf(stderr, @@ -29,7 +29,7 @@ int main(int argc, char** argv) exit(2); } - output = fopen(argv[2], "w"); + output = fopen(argv[2], "wb"); if (!output) { fprintf(stderr, diff --git a/src/unshield.c b/src/unshield.c index 296abf6..b2299fd 100644 --- a/src/unshield.c +++ b/src/unshield.c @@ -13,11 +13,15 @@ #include <unistd.h> #include "../lib/libunshield.h" #ifdef HAVE_CONFIG_H -#include "../lib/unshield_config.h" +#include "lib/unshield_config.h" #endif #if HAVE_FNMATCH #include <fnmatch.h> #endif +#ifdef HAVE_ICONV +#include <iconv.h> +#include <errno.h> +#endif #ifndef VERSION #define VERSION "Unknown" @@ -25,6 +29,16 @@ #define FREE(ptr) { if (ptr) { free(ptr); ptr = NULL; } } +#ifdef _WIN32 + #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) + #include <direct.h> + #ifndef PATH_MAX + #define PATH_MAX _MAX_PATH + #endif +#else + #include <limits.h> +#endif + typedef enum { OVERWRITE_ASK, @@ -55,6 +69,7 @@ static const char* file_group_name = NULL; static const char* component_name = NULL; static bool junk_paths = false; static bool make_lowercase = false; +static bool raw_filename = false; static bool verbose = false; static ACTION action = ACTION_EXTRACT; static OVERWRITE overwrite = OVERWRITE_ASK; @@ -65,6 +80,10 @@ static int is_version = -1; static const char* cab_file_name = NULL; static char* const* path_names = NULL; static int path_name_count = 0; +#ifdef HAVE_ICONV +static const char* encoding = NULL; +iconv_t encoding_descriptor = (iconv_t)-1; +#endif static bool make_sure_directory_exists(const char* directory)/*{{{*/ { @@ -114,12 +133,48 @@ exit: return success; }/*}}}*/ +#ifdef HAVE_ICONV +static bool convert_encoding(char *buffer, size_t size) +{ + bool success = false; + char *newbuf, *inbuf, *outbuf; + size_t inbytesleft, outbytesleft, newsize; + + if (encoding_descriptor == (iconv_t)-1) + return true; + + inbuf = buffer; + inbytesleft = strlen(buffer); + newbuf = outbuf = malloc(size); + outbytesleft = size - 1; + + if (iconv(encoding_descriptor, + &inbuf, &inbytesleft, + &outbuf, &outbytesleft) == (size_t)-1) + { + fprintf(stderr, "Could not encode text to '%s' error %s\n", + encoding, strerror(errno)); + goto exit; + } + + newsize = (size_t)(outbuf - newbuf); + memcpy(buffer, newbuf, newsize); + buffer[newsize] = '\0'; + + success = true; + +exit: + free(newbuf); + return success; +} +#endif + static void show_usage(const char* name) { fprintf(stderr, "Syntax:\n" "\n" - "\t%s [-c COMPONENT] [-d DIRECTORY] [-D LEVEL] [-g GROUP] [-i VERSION] [-GhlOrV] c|g|l|t|x CABFILE [FILENAME...]\n" + "\t%s [-c COMPONENT] [-d DIRECTORY] [-D LEVEL] [-g GROUP] [-i VERSION] [-e ENCODING] [-GhlOrV] c|g|l|t|x CABFILE [FILENAME...]\n" "\n" "Options:\n" "\t-c COMPONENT Only list/extract this component\n" @@ -132,10 +187,12 @@ static void show_usage(const char* name) "\t-g GROUP Only list/extract this file group\n" "\t-h Show this help message\n" "\t-i VERSION Force InstallShield version number (don't autodetect)\n" + "\t-e ENCODING Convert filename character encoding to local codepage from ENCODING (implicitly sets -R)\n" "\t-j Junk paths (do not make directories)\n" "\t-L Make file and directory names lowercase\n" "\t-O Use old compression\n" "\t-r Save raw data (do not decompress)\n" + "\t-R Don't do any conversion to file and directory names when extracting.\n" "\t-V Print copyright and version information\n" "\n" "Commands:\n" @@ -168,7 +225,7 @@ static bool handle_parameters( { int c; - while ((c = getopt(argc, argv, "c:d:D:g:hi:jLnoOrV")) != -1) + while ((c = getopt(argc, argv, "c:d:D:g:hi:e:jLnoOrRV")) != -1) { switch (c) { @@ -192,6 +249,16 @@ static bool handle_parameters( is_version = atoi(optarg); break; + case 'e': +#ifdef HAVE_ICONV + encoding = optarg; + raw_filename = true; +#else + fprintf(stderr, "This version of Unshield is not built with encoding support.\n"); + return false; +#endif + break; + case 'j': junk_paths = true; break; @@ -199,6 +266,10 @@ static bool handle_parameters( case 'L': make_lowercase = true; break; + + case 'R': + raw_filename = true; + break; case 'n': overwrite = OVERWRITE_NEVER; @@ -292,6 +363,26 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) char filename[256]; char* p; int directory = unshield_file_directory(unshield, index); + long int path_max; + char* real_output_directory; + char* real_filename; + + #ifdef PATH_MAX + path_max = PATH_MAX; + #else + path_max = pathconf(path, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 4096; + #endif + + real_output_directory = malloc(path_max); + real_filename = malloc(path_max); + if (real_output_directory == NULL || real_filename == NULL) + { + fprintf(stderr,"Unable to allocate memory."); + success=false; + goto exit; + } strcpy(dirname, output_directory); strcat(dirname, "/"); @@ -329,14 +420,26 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) break; default: - if (!isprint(*p)) - *p = '_'; - else if (make_lowercase) - *p = tolower(*p); + if (!raw_filename) + { + if (!isprint(*p)) + *p = '_'; + else if (make_lowercase) + *p = tolower(*p); + } break;; } } +#ifdef HAVE_ICONV + if (!convert_encoding(dirname, sizeof(dirname))) + { + success = false; + goto exit; + } +#endif + + #if 0 if (dirname[strlen(dirname)-1] != '/') strcat(dirname, "/"); @@ -349,10 +452,39 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) for (p = filename + strlen(dirname); *p != '\0'; p++) { - if (!isprint(*p)) - *p = '_'; - else if (make_lowercase) - *p = tolower(*p); + if (!raw_filename) + { + if (!isprint(*p)) + *p = '_'; + else if (make_lowercase) + *p = tolower(*p); + } + } + +#ifdef HAVE_ICONV + if (!convert_encoding(filename + strlen(dirname), + sizeof(filename) - strlen(dirname))) + { + success = false; + goto exit; + } +#endif + + /* use GNU extension to return non-existing files to real_output_directory */ + realpath(output_directory, real_output_directory); + realpath(filename, real_filename); + if (real_filename == NULL || strncmp(real_filename, + real_output_directory, + strlen(real_output_directory)) != 0) + { + fprintf(stderr, "\n\nExtraction failed.\n"); + fprintf(stderr, "Possible directory traversal attack for: %s\n", filename); + fprintf(stderr, "To be placed at: %s\n\n", real_filename); + exit_status = 1; + success = false; + free(real_filename); + free(real_output_directory); + return success; } printf(" extracting: %s\n", filename); @@ -369,6 +501,7 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) break; } +exit: if (!success) { fprintf(stderr, "Failed to extract file '%s'.%s\n", @@ -377,7 +510,8 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) unlink(filename); exit_status = 1; } - + free(real_filename); + free(real_output_directory); return success; } @@ -582,6 +716,18 @@ int main(int argc, char* const argv[]) goto exit; } +#ifdef HAVE_ICONV + if (!unshield_is_unicode(unshield) && encoding != NULL) + { + if ((encoding_descriptor = iconv_open("", encoding)) == (iconv_t)-1) + { + fprintf(stderr, "Cannot use encoding '%s' error %s\n", + encoding, strerror(errno)); + goto exit; + } + } +#endif + printf("Cabinet: %s\n", cab_file_name); switch (action) @@ -613,6 +759,10 @@ int main(int argc, char* const argv[]) exit: unshield_close(unshield); +#ifdef HAVE_ICONV + if (encoding_descriptor != (iconv_t)-1) + iconv_close(encoding_descriptor); +#endif if (!success) exit_status = 1; return exit_status; diff --git a/test/v0/avigomanager.md5 b/test/v0/avigomanager.md5 new file mode 100644 index 0000000..e638873 --- /dev/null +++ b/test/v0/avigomanager.md5 @@ -0,0 +1,50 @@ +a943ad8f40479fa5cd68afba5787be4f ./English/Avigo100.pgm +48c56d5db36b20d0f8644a85d1c33dac ./English/AvigoMgr.exe +618341f3e7654c8d5d7e13f17bf433f8 ./English/AvigoToPc.avi +c3f50ecf458c55ac1157b98f74e6ad0f ./English/Comctl32.dll +54584845c6f232a18e4c8f7c59aeca09 ./English/cw3220.dll +87a2adf125be51cdd5d8d3843e0f0b7e ./English/Dao2535.tlb +0aba3f8d3a59754306d75c157e1d2b0a ./English/dao350.dll +0be37395a851b1d7aaa7039605ec12c9 ./English/Deu100.alb +5f854403d1e201a397151610fb9c80dd ./English/DLGDLL.dll +72960b3faf8c1845f37d1aac23996e6c ./English/Download.exe +cb4848abcb77130f48b8f9e9cf6d8977 ./English/Eng100.alb +0e206fc4ef4a922c1de3199465ee5955 ./English/English/AvigoMan.cnt +9095276ff7bdda4dd35b79d76397410c ./English/English/Avigoman.hlp +2b212e81224f2bdd608d98fe579c8c4c ./English/Esp100.alb +abc3474a2219fe49c2a2bf2f3e764a64 ./English/Financial100.app +1f17ba903bc00e2694e6338c0e9bd8c3 ./English/Fra100.alb +df1c526338995596901bcc95ed637f33 ./English/French/AvigoMan.cnt +7265699e1bbae3022376e4f18f2e52b7 ./English/French/AVIGOMAN.HLP +64889479798be3d1a682ebd2e1b50452 ./English/German/AvigoMan.cnt +ec267fd5abb90d373e3b9fe6a5762315 ./English/German/Avigoman.hlp +15f24a05040bb34765c852942480dc0d ./English/io_common.dll +5beb2e7cb566e103480f3f4c3b99f006 ./English/io_error.dll +b83f81c70983c28252c452fcdce906de ./English/io_ircomm.dll +955db4b34095dc7340c82a8e328c4aa3 ./English/io_obex.dll +989c75747c7b24caacf2e4df0330f45c ./English/io_tiobex.dll +e21adacb76cfff3350a3e82c4f883a44 ./English/Ita100.alb +85cf12ce9899c7242233a340dcb10de4 ./English/Italian/AvigoMan.cnt +048fa624c7f7f9ddf76898f1dd5a5418 ./English/Italian/AVIGOMAN.HLP +4aec6b69fd4237a2a0562974e55537ef ./English/language.lng +8a7871c9b80a678813ca668338432456 ./English/msexcl35.dll +e8a31571e9b0f79bc30ad7b8afa75c08 ./English/msjet35.dll +8472c0e32802199891d76d57879bd9d9 ./English/msjint35.dll +5773425a2bb778684b57d042a0cd5247 ./English/msjter35.dll +6252deb3dab5e502fcab24183c642563 ./English/msltus35.dll +d74cc7953be48ec1f3deff4741977887 ./English/msrd2x35.dll +17291135b3146b3c3c9d201b5d65c168 ./English/mstext35.dll +9ba25eab9b071b8ef0799f7b785c4722 ./English/Msvcrt40.dll +81a267f80035cb3a7559be4179700931 ./English/Ole32.dll +b9d04a19150d6799c95045719e6e6913 ./English/Oleaut32.dll +441e965db51513b8e3c22477832641c3 ./English/Olepro32.dll +54e9447a8133042ba0fa293a2440527b ./English/PcToAvigo.avi +5caa91bc875bfdfd4066b1abdfcd6831 ./English/Readme.txt +676dd602591dceb83799e58c032071f1 ./English/Spanish/AvigoMan.cnt +cd8818345ab13e0bf7627035c8a746ad ./English/Spanish/AVIGOMAN.HLP +77abaeafbb2340ec06bd83da4b3c0418 ./English/SyncMovie.avi +149f277034310ea0e204a5ded4502c26 ./English/tops.dll +9cb48c7068f6d9389493b0f1035ab204 ./English/translat.lng +41b7178b258b97248a2e31d6771cf6f3 ./English/update.dll +9f8c2ac5719be020bd5fe898fa01f90f ./English/vbajet32.dll +9d1864ae5f6ff8bbde86a3f5a448110d ./English/vbar332.dll diff --git a/test/v0/avigomanager.sh b/test/v0/avigomanager.sh new file mode 100755 index 0000000..4e66a77 --- /dev/null +++ b/test/v0/avigomanager.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e +cd `dirname $0` +MD5_FILE=`pwd`/`basename $0 .sh`.md5 +UNSHIELD=/var/tmp/unshield/bin/unshield + +if [ \! -x ${UNSHIELD} ]; then + echo "unshield executable not found at $UNSHIELD" >&2 + exit 1 +fi + +DIR=`mktemp -d` +#trap 'rm -rf ${DIR}' TERM INT EXIT +cd ${DIR} + +#URL=https://www.ti.com/organizers/avigo/docs/avigomanager11b22.zip +URL="https://www.dropbox.com/s/8r4b6752swe3nhu/unshield-avigomanager11b22.zip?dl=1" +curl -sSL -o test.zip ${URL} +unzip -q test.zip 'data*' + +set +e +timeout 10 ${UNSHIELD} -d extract1 x data1.cab > log1 2>&1 +CODE=$? +if [ ${CODE} -ne 1 ]; then + cat log1 >&2 + echo "unshield should have failed with error 1 but was $CODE" >&2 + exit 2 +fi + +timeout 10 ${UNSHIELD} -O -d extract2 x data1.cab > log2 2>&1 +CODE=$? +if [ ${CODE} -ne 0 ]; then + cat log2 >&2 + echo "unshield failed with error $CODE" >&2 + exit 3 +fi + +cd extract2 +find . -type f | sort | xargs md5sum > ../md5 +if ! diff ${MD5_FILE} ../md5 >&2 ; then + echo "MD5 sums diff" >&2 + exit 4 +fi + +exit 0 \ No newline at end of file diff --git a/test/v5/CVE-2015-1386/CVE-2015-1386.sh b/test/v5/CVE-2015-1386/CVE-2015-1386.sh new file mode 100755 index 0000000..a04adc2 --- /dev/null +++ b/test/v5/CVE-2015-1386/CVE-2015-1386.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e +cd `dirname $0` +MD5_FILE=`pwd`/`basename $0 .sh`.md5 +CAB_FILE=`pwd`/data1.cab +UNSHIELD=/var/tmp/unshield/bin/unshield + +if [ \! -x ${UNSHIELD} ]; then + echo "unshield executable not found at $UNSHIELD" >&2 + exit 1 +fi + +DIR=`mktemp -d` +#trap 'rm -rf ${DIR}' TERM INT EXIT +cd ${DIR} + +set +e +rm -f /tmp/moo + +timeout 10 ${UNSHIELD} -d extract1 x "$CAB_FILE" > log1 2>&1 +CODE=$? +if [ -e /tmp/moo ]; then + cat log1 >&2 + echo "unshield vulnerable to CVE-2015-1386" >&2 + echo "See https://github.com/twogood/unshield/issues/42" >&2 + exit 2 +fi + +if [ ${CODE} -ne 1 ]; then + cat log1 >&2 + echo "unshield should have failed with error 1 but was $CODE" >&2 + exit 3 +fi + +exit 0 diff --git a/test/v5/CVE-2015-1386/data1.cab b/test/v5/CVE-2015-1386/data1.cab new file mode 100644 index 0000000..50c7ba1 Binary files /dev/null and b/test/v5/CVE-2015-1386/data1.cab differ diff --git a/test/v5/CVE-2015-1386/data1.hdr b/test/v5/CVE-2015-1386/data1.hdr new file mode 100644 index 0000000..0347550 Binary files /dev/null and b/test/v5/CVE-2015-1386/data1.hdr differ -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/unshield.git _______________________________________________ Pkg-games-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

