pax_global_header00006660000000000000000000000064147734522040014522gustar00rootroot0000000000000052 comment=51de441ba6893f11026d4671ccef9e8e2a4634fa twogood-unshield-51de441/000077500000000000000000000000001477345220400153465ustar00rootroot00000000000000twogood-unshield-51de441/.editorconfig000066400000000000000000000004521477345220400200240ustar00rootroot00000000000000# top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] charset = utf-8 trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true # Tab indentation (no size specified) [Makefile] indent_style = tab [*.{c,h,cpp,hpp}] indent_size = 2 twogood-unshield-51de441/.github/000077500000000000000000000000001477345220400167065ustar00rootroot00000000000000twogood-unshield-51de441/.github/workflows/000077500000000000000000000000001477345220400207435ustar00rootroot00000000000000twogood-unshield-51de441/.github/workflows/cmake.yml000066400000000000000000000041501477345220400225460ustar00rootroot00000000000000name: CMake on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ${{matrix.platform.os}} defaults: run: shell: ${{ matrix.platform.shell }} strategy: fail-fast: false matrix: platform: - { name: macOS, os: macos-latest, shell: sh } - { name: Ubuntu, os: ubuntu-latest, shell: sh } - { name: Windows - mingw32, os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686 } - { name: Windows - mingw64, os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64 } build-type: [Release] build-static: [ON] steps: - uses: actions/checkout@v4 - name: Setup GNU/Linux dependencies if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install \ libssl-dev ninja-build zlib1g-dev - name: Setup macOS dependencies if: runner.os == 'macOS' run: | brew install \ ninja zlib echo "CMAKE_PREFIX_PATH=$(brew --prefix openssl):$(brew --prefix zlib)" >> $GITHUB_ENV - name: Setup Windows dependencies if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.platform.msystem}} install: >- ${{matrix.platform.msys-env}}-gcc ${{matrix.platform.msys-env}}-cmake ${{matrix.platform.msys-env}}-ninja ${{matrix.platform.msys-env}}-openssl ${{matrix.platform.msys-env}}-zlib - name: Configure CMake run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build-type}} -DBUILD_STATIC=${{matrix.build-static}} -DBUILD_TESTING=ON - name: Build run: cmake --build build - name: Test run: ctest --test-dir build --output-on-failure - name: Install run: cmake --install build --strip --prefix install - name: Upload uses: actions/upload-artifact@v4 with: name: unshield-${{github.sha}} (${{matrix.platform.name}}) path: install if-no-files-found: error twogood-unshield-51de441/.github/workflows/codeql-analysis.yml000066400000000000000000000030521477345220400245560ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. name: "CodeQL" on: push: branches: [main] pull_request: # The branches below must be a subset of the branches above branches: [main] schedule: - cron: '0 1 * * 3' jobs: analyze: # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest runs-on: ubuntu-latest permissions: # required for all workflows security-events: write steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: cpp # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below). - name: Autobuild uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following # three lines and modify them (or add more) to build your code if your # project uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 twogood-unshield-51de441/.gitignore000066400000000000000000000007231477345220400173400ustar00rootroot00000000000000*.a **/CMakeCache.txt **/CMakeFiles/ *cmake_install.cmake **/CMakeScripts/ *.core .gdb_history *.la lib/libunshield.so* .libs lib/unshield_config.h libunshield.pc *.lo ltmain.sh Makefile *.o src/unshield src/unshield-deobfuscate .*.swp .idea build /cmake-build-debug/ .vs *.sln *.vcxproj *.vcxproj.filters *.vcxproj.user DartConfiguration.tcl CTestTestfile.cmake *.tlog *.obj *.FileListAbsolute.txt *.lastbuildstate *.log *.pdb *.lib *.recipe *.exp *.ilk *.exe *.dll twogood-unshield-51de441/CMakeLists.txt000066400000000000000000000072251477345220400201140ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.21) project(unshield C) # Mimic CMP0048 which is avaliable only for cmake 3.0 and later set(PROJECT_VERSION_MAJOR 1) set(PROJECT_VERSION_MINOR 6) set(PROJECT_VERSION_PATCH 2) set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") option(BUILD_STATIC "Build static version of libunshield" OFF) include(CheckIncludeFiles) include(CheckSymbolExists) include(CheckCSourceCompiles) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test") include(CTest) endif() include(GNUInstallDirs) check_include_files(dlfcn.h HAVE_DLFCN_H) check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(memory.h HAVE_MEMORY_H) check_include_files(stdbool.h HAVE_STDBOOL_H) check_include_files(stdint.h HAVE_STDINT_H) check_include_files(stdlib.h HAVE_STDLIB_H) check_include_files(strings.h HAVE_STRINGS_H) check_include_files(string.h HAVE_STRING_H) 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_symbol_exists(fnmatch fnmatch.h HAVE_FNMATCH) check_symbol_exists(iconv iconv.h HAVE_ICONV) set(SIZE_FORMAT "zi") check_c_source_compiles("#include \nint main(int argc, char **argv) { size_t value = 0; printf(\"%${SIZE_FORMAT}\", value); return 0; }" SIZE_FORMAT_ZI) if(NOT SIZE_FORMAT_ZI) set(SIZE_FORMAT "i") check_c_source_compiles("#include \nint main(int argc, char **argv) { size_t value = 0; printf(\"%${SIZE_FORMAT}\", value); return 0; }" SIZE_FORMAT_I) if(NOT SIZE_FORMAT_I) set(SIZE_FORMAT "li") check_c_source_compiles("#include \nint main(int argc, char **argv) { size_t value = 0; printf(\"%${SIZE_FORMAT}\", value); return 0; }" SIZE_FORMAT_LI) if(NOT SIZE_FORMAT_LI) message(FATAL_ERROR "You must be using a really weird platform!") endif() endif() endif() find_package(ZLIB REQUIRED) find_package(OpenSSL) if(${OPENSSL_FOUND}) option(USE_OUR_OWN_MD5 "Build using own md5 implementation" OFF) else() option(USE_OUR_OWN_MD5 "Build using own md5 implementation" ON) endif() message(STATUS "OPENSSL_FOUND: ${OPENSSL_FOUND}") message(STATUS "USE_OUR_OWN_MD5: ${USE_OUR_OWN_MD5}") message(STATUS "BUILD_STATIC: ${BUILD_STATIC}") if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test") message(STATUS "BUILD_TESTING: ${BUILD_TESTING}") else() set(BUILD_TESTING OFF) endif() 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) # Function to change MSVC runtime linkage to static function(set_msvc_runtime_static) foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag_var} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endif() endforeach() endfunction() if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32_msvc) endif () add_subdirectory(lib) add_subdirectory(src) install(FILES man/unshield.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libunshield.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install(EXPORT unshieldConfig NAMESPACE unshield:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/unshield) if(BUILD_TESTING) add_subdirectory(test) endif() twogood-unshield-51de441/ChangeLog000066400000000000000000000013311477345220400171160ustar00rootroot000000000000002009-07-16 Mark Ellis * 0.6 release 2009-06-18 Mark Ellis * src/unshield.c - fix use of -L (lowercase) option with -d 2008-05-06 Jonny Lamb * .cvsignore: * cvsclean: * lib/.cvsignore: * lib/md5/.cvsignore: * src/.cvsignore: Removed useless CVS ignore and clean files. * bootstrap: * unshield.spec.in: Removed spec file. 2008-05-06 Jonny Lamb * VERSION: Upped version number to 0.5.1. 2008-02-15 Mark Ellis * added man page for unshield 2008-01-09 Mark Ellis * started changelog * added LICENSE to EXTRA_DIST * added cabfile.h to libunshield_la_SOURCES twogood-unshield-51de441/LICENSE000066400000000000000000000021031477345220400163470ustar00rootroot00000000000000Copyright (c) 2003 David Eriksson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. twogood-unshield-51de441/README.md000066400000000000000000000056561477345220400166410ustar00rootroot00000000000000Unshield ======== [![Packaging status](https://repology.org/badge/tiny-repos/unshield.svg)](https://repology.org/project/unshield/versions) [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/unshield.svg)](https://repology.org/project/unshield/versions) Support Unshield development ---------------------------- - [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SQ7PEFMJK36AU) Dictionary ---------- InstallShield (IS): see www.installshield.com InstallShield Cabinet File (ISCF): A .cab file used by IS. Microsoft Cabinet File (MSCF): A .cab file used by Microsoft. About Unshield -------------- To install a Pocket PC application remotely, an installable Microsoft Cabinet File is copied to the /Windows/AppMgr/Install directory on the PDA and then the wceload.exe is executed to perform the actual install. That is a very simple procedure. Unfortunately, many applications for Pocket PC are distributed as InstallShield installers for Microsoft Windows, and not as individual Microsoft Cabinet Files. That is very impractical for users of other operating systems, such as Linux or FreeBSD. An installer created by the InstallShield software stores the files it will install inside of InstallShield Cabinet Files. It would thus be desirable to be able to extract the Microsoft Cabinet Files from the InstallShield Cabinet Files in order to be able to install the applications without access to Microsoft Windows. The format of InstallShield Cabinet Files is not officially documented but there are two tools available for Microsoft Windows that extracts files from InstallShield installers, and they are distributed with source code included. These tools are named "i5comp" and "i6comp" and can be downloaded from the Internet. One major drawback with these tools are that for the actual decompression of the files stored in the InstallShield Cabinet Files they require the use of code written by InstallShield that is not available as source code. Luckily, by examining this code with the 'strings' tool, I discovered that they were using the open source zlib library (www.gzip.org/zlib) for decompression. I could have modified i5comp and i6comp to run on other operating systems than Microsoft Windows, but I preferred to use them as a reference for this implementation. The goals of this implementation are: - Use a well known open source license (MIT) - Work on both little-endian and big-endian systems - Separate the implementation in a tool and a library - Support InstallShield versions 5 and later - Be able to list contents of InstallShield Cabinet Files - Be able to extract files from InstallShield Cabinet Files License ------- Unshield uses the MIT license. The short version is "do as you like, but don't blame me if anything goes wrong". See the file LICENSE for details. Build From Source ----------------- Just use the standard CMake build process: ``` sh cmake . make make install ``` twogood-unshield-51de441/lib/000077500000000000000000000000001477345220400161145ustar00rootroot00000000000000twogood-unshield-51de441/lib/CMakeLists.txt000066400000000000000000000027171477345220400206630ustar00rootroot00000000000000if(USE_OUR_OWN_MD5) add_subdirectory(md5) endif() set(LIBUNSHIELD_HEADERS internal.h libunshield.h log.h cabfile.h converter.h ) set(LIBUNSHIELD_SOURCES component.c converter.c directory.c file.c file_group.c helper.c libunshield.c log.c ) if(BUILD_STATIC) add_library(libunshield STATIC ${LIBUNSHIELD_HEADERS} ${LIBUNSHIELD_SOURCES}) else() add_library(libunshield SHARED ${LIBUNSHIELD_HEADERS} ${LIBUNSHIELD_SOURCES}) target_compile_definitions(libunshield PUBLIC UNSHIELD_DYNAMIC_LIBRARY) endif() target_include_directories(libunshield PUBLIC $ $) target_link_libraries(libunshield PUBLIC ZLIB::ZLIB) target_compile_definitions(libunshield PRIVATE UNSHIELD_EXPORT) set_target_properties(libunshield PROPERTIES OUTPUT_NAME unshield) set_target_properties(libunshield PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) if(USE_OUR_OWN_MD5) target_link_libraries(libunshield PRIVATE $) else() target_link_libraries(libunshield PRIVATE OpenSSL::Crypto) endif() install(TARGETS libunshield EXPORT unshieldConfig RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES libunshield.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") twogood-unshield-51de441/lib/cabfile.h000066400000000000000000000042021477345220400176500ustar00rootroot00000000000000#ifndef __cabfile_h__ #define __cabfile_h__ #define OFFSET_COUNT 0x47 #define CAB_SIGNATURE 0x28635349 #define MSCF_SIGNATURE 0x4643534d #define COMMON_HEADER_SIZE 20 #define VOLUME_HEADER_SIZE_V5 40 #define VOLUME_HEADER_SIZE_V6 64 #define MAX_FILE_GROUP_COUNT 71 #define MAX_COMPONENT_COUNT 71 typedef struct { uint32_t signature; /* 00 */ uint32_t version; uint32_t volume_info; uint32_t cab_descriptor_offset; uint32_t cab_descriptor_size; /* 10 */ } CommonHeader; typedef struct { uint32_t data_offset; uint32_t data_offset_high; uint32_t first_file_index; uint32_t last_file_index; uint32_t first_file_offset; uint32_t first_file_offset_high; uint32_t first_file_size_expanded; uint32_t first_file_size_expanded_high; uint32_t first_file_size_compressed; uint32_t first_file_size_compressed_high; uint32_t last_file_offset; uint32_t last_file_offset_high; uint32_t last_file_size_expanded; uint32_t last_file_size_expanded_high; uint32_t last_file_size_compressed; uint32_t last_file_size_compressed_high; } VolumeHeader; typedef struct { uint32_t file_table_offset; /* c */ uint32_t file_table_size; /* 14 */ uint32_t file_table_size2; /* 18 */ uint32_t directory_count; /* 1c */ uint32_t file_count; /* 28 */ uint32_t file_table_offset2; /* 2c */ uint32_t file_group_offsets[MAX_FILE_GROUP_COUNT]; /* 0x3e */ uint32_t component_offsets [MAX_COMPONENT_COUNT]; /* 0x15a */ } CabDescriptor; #define FILE_SPLIT 1U #define FILE_OBFUSCATED 2U #define FILE_COMPRESSED 4U #define FILE_INVALID 8U #define LINK_NONE 0 #define LINK_PREV 1 #define LINK_NEXT 2 #define LINK_BOTH 3 typedef struct { uint32_t name_offset; uint32_t directory_index; uint16_t flags; uint64_t expanded_size; uint64_t compressed_size; uint64_t data_offset; uint8_t md5[16]; uint16_t volume; uint32_t link_previous; uint32_t link_next; uint8_t link_flags; } FileDescriptor; typedef struct { uint32_t name_offset; uint32_t descriptor_offset; uint32_t next_offset; } OffsetList; #endif twogood-unshield-51de441/lib/component.c000066400000000000000000000031171477345220400202640ustar00rootroot00000000000000#include "internal.h" #include "log.h" #include #include int unshield_component_count(Unshield* unshield) { Header* header = unshield->header_list; return header->component_count; } const char* unshield_component_name(Unshield* unshield, int index) { Header* header = unshield->header_list; if (index >= 0 && index < header->component_count) return header->components[index]->name; else return NULL; } UnshieldComponent* unshield_component_new(Header* header, uint32_t offset) { UnshieldComponent* self = NEW1(UnshieldComponent); uint8_t* p = unshield_header_get_buffer(header, offset); uint32_t file_group_table_offset; unsigned i; self->name = unshield_header_get_string(header, READ_UINT32(p)); p += 4; switch (header->major_version) { case 0: case 5: p += 0x6c; break; case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: default: p += 0x6b; break; } self->file_group_count = READ_UINT16(p); p += 2; if (self->file_group_count > MAX_FILE_GROUP_COUNT) abort(); self->file_group_names = NEW(const char*, self->file_group_count); file_group_table_offset = READ_UINT32(p); p += 4; p = unshield_header_get_buffer(header, file_group_table_offset); for (i = 0; i < self->file_group_count; i++) { self->file_group_names[i] = unshield_header_get_string(header, READ_UINT32(p)); p += 4; } return self; } void unshield_component_destroy(UnshieldComponent* self) { if (self) { FREE(self->file_group_names); free(self); } } twogood-unshield-51de441/lib/converter.c000066400000000000000000000257701477345220400203020ustar00rootroot00000000000000#include #include // The type of a single Unicode codepoint typedef uint32_t codepoint_t; // The last codepoint of the Basic Multilingual Plane, which is the part of Unicode that // UTF-16 can encode without surrogates #define BMP_END 0xFFFF // The highest valid Unicode codepoint #define UNICODE_MAX 0x10FFFF // The codepoint that is used to replace invalid encodings #define INVALID_CODEPOINT 0xFFFD // If a character, masked with GENERIC_SURROGATE_MASK, matches this value, it is a surrogate. #define GENERIC_SURROGATE_VALUE 0xD800 // The mask to apply to a character before testing it against GENERIC_SURROGATE_VALUE #define GENERIC_SURROGATE_MASK 0xF800 // If a character, masked with SURROGATE_MASK, matches this value, it is a high surrogate. #define HIGH_SURROGATE_VALUE 0xD800 // If a character, masked with SURROGATE_MASK, matches this value, it is a low surrogate. #define LOW_SURROGATE_VALUE 0xDC00 // The mask to apply to a character before testing it against HIGH_SURROGATE_VALUE or LOW_SURROGATE_VALUE #define SURROGATE_MASK 0xFC00 // The value that is subtracted from a codepoint before encoding it in a surrogate pair #define SURROGATE_CODEPOINT_OFFSET 0x10000 // A mask that can be applied to a surrogate to extract the codepoint value contained in it #define SURROGATE_CODEPOINT_MASK 0x03FF // The number of bits of SURROGATE_CODEPOINT_MASK #define SURROGATE_CODEPOINT_BITS 10 // The highest codepoint that can be encoded with 1 byte in UTF-8 #define UTF8_1_MAX 0x7F // The highest codepoint that can be encoded with 2 bytes in UTF-8 #define UTF8_2_MAX 0x7FF // The highest codepoint that can be encoded with 3 bytes in UTF-8 #define UTF8_3_MAX 0xFFFF // The highest codepoint that can be encoded with 4 bytes in UTF-8 #define UTF8_4_MAX 0x10FFFF // If a character, masked with UTF8_CONTINUATION_MASK, matches this value, it is a UTF-8 continuation byte #define UTF8_CONTINUATION_VALUE 0x80 // The mask to a apply to a character before testing it against UTF8_CONTINUATION_VALUE #define UTF8_CONTINUATION_MASK 0xC0 // The number of bits of a codepoint that are contained in a UTF-8 continuation byte #define UTF8_CONTINUATION_CODEPOINT_BITS 6 // Represents a UTF-8 bit pattern that can be set or verified typedef struct { // The mask that should be applied to the character before testing it utf8_t mask; // The value that the character should be tested against after applying the mask utf8_t value; } utf8_pattern; // The patterns for leading bytes of a UTF-8 codepoint encoding // Each pattern represents the leading byte for a character encoded with N UTF-8 bytes, // where N is the index + 1 static const utf8_pattern utf8_leading_bytes[] = { { 0x80, 0x00 }, // 0xxxxxxx { 0xE0, 0xC0 }, // 110xxxxx { 0xF0, 0xE0 }, // 1110xxxx { 0xF8, 0xF0 } // 11110xxx }; // The number of elements in utf8_leading_bytes #define UTF8_LEADING_BYTES_LEN 4 // Gets a codepoint from a UTF-16 string // utf16: The UTF-16 string // len: The length of the UTF-16 string, in UTF-16 characters // index: // A pointer to the current index on the string. // When the function returns, this will be left at the index of the last character // that composes the returned codepoint. // For surrogate pairs, this means the index will be left at the low surrogate. static codepoint_t decode_utf16(utf16_t const* utf16, size_t len, size_t* index) { utf16_t high = utf16[*index]; // BMP character if ((high & GENERIC_SURROGATE_MASK) != GENERIC_SURROGATE_VALUE) return high; // Unmatched low surrogate, invalid if ((high & SURROGATE_MASK) != HIGH_SURROGATE_VALUE) return INVALID_CODEPOINT; // String ended with an unmatched high surrogate, invalid if (*index == len - 1) return INVALID_CODEPOINT; utf16_t low = utf16[*index + 1]; // Unmatched high surrogate, invalid if ((low & SURROGATE_MASK) != LOW_SURROGATE_VALUE) return INVALID_CODEPOINT; // Two correctly matched surrogates, increase index to indicate we've consumed // two characters (*index)++; // The high bits of the codepoint are the value bits of the high surrogate // The low bits of the codepoint are the value bits of the low surrogate codepoint_t result = high & SURROGATE_CODEPOINT_MASK; result <<= SURROGATE_CODEPOINT_BITS; result |= low & SURROGATE_CODEPOINT_MASK; result += SURROGATE_CODEPOINT_OFFSET; // And if all else fails, it's valid return result; } // Calculates the number of UTF-8 characters it would take to encode a codepoint // The codepoint won't be checked for validity, that should be done beforehand. static int calculate_utf8_len(codepoint_t codepoint) { // An array with the max values would be more elegant, but a bit too heavy // for this common function if (codepoint <= UTF8_1_MAX) return 1; if (codepoint <= UTF8_2_MAX) return 2; if (codepoint <= UTF8_3_MAX) return 3; return 4; } // Encodes a codepoint in a UTF-8 string. // The codepoint won't be checked for validity, that should be done beforehand. // // codepoint: The codepoint to be encoded. // utf8: The UTF-8 string // len: The length of the UTF-8 string, in UTF-8 characters // index: The first empty index on the string. // // return: The number of characters written to the string. static size_t encode_utf8(codepoint_t codepoint, utf8_t* utf8, size_t len, size_t index) { int size = calculate_utf8_len(codepoint); // Not enough space left on the string if (index + size > len) return 0; // Write the continuation bytes in reverse order first for (int cont_index = size - 1; cont_index > 0; cont_index--) { utf8_t cont = codepoint & ~UTF8_CONTINUATION_MASK; cont |= UTF8_CONTINUATION_VALUE; utf8[index + cont_index] = cont; codepoint >>= UTF8_CONTINUATION_CODEPOINT_BITS; } // Write the leading byte utf8_pattern pattern = utf8_leading_bytes[size - 1]; utf8_t lead = codepoint & ~(pattern.mask); lead |= pattern.value; utf8[index] = lead; return size; } size_t utf16_to_utf8(utf16_t const* utf16, size_t utf16_len, utf8_t* utf8, size_t utf8_len) { // The next codepoint that will be written in the UTF-8 string // or the size of the required buffer if utf8 is NULL size_t utf8_index = 0; for (size_t utf16_index = 0; utf16_index < utf16_len; utf16_index++) { codepoint_t codepoint = decode_utf16(utf16, utf16_len, &utf16_index); if (utf8 == NULL) utf8_index += calculate_utf8_len(codepoint); else utf8_index += encode_utf8(codepoint, utf8, utf8_len, utf8_index); } return utf8_index; } // Gets a codepoint from a UTF-8 string // utf8: The UTF-8 string // len: The length of the UTF-8 string, in UTF-8 characters // index: // A pointer to the current index on the string. // When the function returns, this will be left at the index of the last character // that composes the returned codepoint. // For example, for a 3-byte codepoint, the index will be left at the third character. static codepoint_t decode_utf8(utf8_t const* utf8, size_t len, size_t* index) { utf8_t leading = utf8[*index]; // The number of bytes that are used to encode the codepoint int encoding_len = 0; // The pattern of the leading byte utf8_pattern leading_pattern; // If the leading byte matches the current leading pattern bool matches = false; do { encoding_len++; leading_pattern = utf8_leading_bytes[encoding_len - 1]; matches = (leading & leading_pattern.mask) == leading_pattern.value; } while (!matches && encoding_len < UTF8_LEADING_BYTES_LEN); // Leading byte doesn't match any known pattern, consider it invalid if (!matches) return INVALID_CODEPOINT; codepoint_t codepoint = leading & ~leading_pattern.mask; for (int i = 0; i < encoding_len - 1; i++) { // String ended before all continuation bytes were found // Invalid encoding if (*index + 1 >= len) return INVALID_CODEPOINT; utf8_t continuation = utf8[*index + 1]; // Number of continuation bytes not the same as advertised on the leading byte // Invalid encoding if ((continuation & UTF8_CONTINUATION_MASK) != UTF8_CONTINUATION_VALUE) return INVALID_CODEPOINT; codepoint <<= UTF8_CONTINUATION_CODEPOINT_BITS; codepoint |= continuation & ~UTF8_CONTINUATION_MASK; (*index)++; } int proper_len = calculate_utf8_len(codepoint); // Overlong encoding: too many bytes were used to encode a short codepoint // Invalid encoding if (proper_len != encoding_len) return INVALID_CODEPOINT; // Surrogates are invalid Unicode codepoints, and should only be used in UTF-16 // Invalid encoding if (codepoint < BMP_END && (codepoint & GENERIC_SURROGATE_MASK) == GENERIC_SURROGATE_VALUE) return INVALID_CODEPOINT; // UTF-8 can encode codepoints larger than the Unicode standard allows // Invalid encoding if (codepoint > UNICODE_MAX) return INVALID_CODEPOINT; return codepoint; } // Calculates the number of UTF-16 characters it would take to encode a codepoint // The codepoint won't be checked for validity, that should be done beforehand. static int calculate_utf16_len(codepoint_t codepoint) { if (codepoint <= BMP_END) return 1; return 2; } // Encodes a codepoint in a UTF-16 string. // The codepoint won't be checked for validity, that should be done beforehand. // // codepoint: The codepoint to be encoded. // utf16: The UTF-16 string // len: The length of the UTF-16 string, in UTF-16 characters // index: The first empty index on the string. // // return: The number of characters written to the string. static size_t encode_utf16(codepoint_t codepoint, utf16_t* utf16, size_t len, size_t index) { // Not enough space on the string if (index >= len) return 0; if (codepoint <= BMP_END) { utf16[index] = codepoint; return 1; } // Not enough space on the string for two surrogates if (index + 1 >= len) return 0; codepoint -= SURROGATE_CODEPOINT_OFFSET; utf16_t low = LOW_SURROGATE_VALUE; low |= codepoint & SURROGATE_CODEPOINT_MASK; codepoint >>= SURROGATE_CODEPOINT_BITS; utf16_t high = HIGH_SURROGATE_VALUE; high |= codepoint & SURROGATE_CODEPOINT_MASK; utf16[index] = high; utf16[index + 1] = low; return 2; } size_t utf8_to_utf16(utf8_t const* utf8, size_t utf8_len, utf16_t* utf16, size_t utf16_len) { // The next codepoint that will be written in the UTF-16 string // or the size of the required buffer if utf16 is NULL size_t utf16_index = 0; for (size_t utf8_index = 0; utf8_index < utf8_len; utf8_index++) { codepoint_t codepoint = decode_utf8(utf8, utf8_len, &utf8_index); if (utf16 == NULL) utf16_index += calculate_utf16_len(codepoint); else utf16_index += encode_utf16(codepoint, utf16, utf16_len, utf16_index); } return utf16_index; } twogood-unshield-51de441/lib/converter.h000066400000000000000000000033461477345220400203020ustar00rootroot00000000000000#pragma once #include #include typedef uint8_t utf8_t; // The type of a single UTF-8 character typedef uint16_t utf16_t; // The type of a single UTF-16 character /* * Converts a UTF-16 string to a UTF-8 string. * * utf16: * The UTF-16 string, not null-terminated. * * utf16_len: * The length of the UTF-16 string, in 16-bit characters. * * utf8: * The buffer where the resulting UTF-8 string will be stored. * If set to NULL, indicates that the function should just calculate * the required buffer size and not actually perform any conversions. * * utf8_len: * The length of the UTF-8 buffer, in 8-bit characters. * Ignored if utf8 is NULL. * * return: * If utf8 is NULL, the size of the required UTF-8 buffer. * Otherwise, the number of characters written to the utf8 buffer. * */ size_t utf16_to_utf8( utf16_t const* utf16, size_t utf16_len, utf8_t* utf8, size_t utf8_len ); /* * Converts a UTF-8 string to a UTF-16 string. * * utf8: * The UTF-8 string, not null-terminated. * * utf8_len: * The length of the UTF-8 string, in 8-bit characters. * * utf16: * The buffer where the resulting UTF-16 string will be stored. * If set to NULL, indicates that the function should just calculate * the required buffer size and not actually perform any conversions. * * utf16_len: * The length of the UTF-16 buffer, in 16-bit characters. * Ignored if utf16 is NULL. * * return: * If utf16 is NULL, the size of the required UTF-16 buffer, * in 16-bit characters. * Otherwise, the number of characters written to the utf8 buffer, in * 16-bit characters. * */ size_t utf8_to_utf16( utf8_t const* utf8, size_t utf8_len, utf16_t* utf16, size_t utf16_len ); twogood-unshield-51de441/lib/directory.c000066400000000000000000000014231477345220400202640ustar00rootroot00000000000000#include "internal.h" #include "log.h" int unshield_directory_count(Unshield* unshield) { if (unshield) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; return header->cab.directory_count; } else return -1; } const char* unshield_directory_name(Unshield* unshield, int index) { if (unshield && index >= 0) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; if (index < (int)header->cab.directory_count) return unshield_get_utf8_string(header, header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->file_table[index]); } unshield_warning("Failed to get directory name %i", index); return NULL; } twogood-unshield-51de441/lib/file.c000066400000000000000000001101471477345220400172030ustar00rootroot00000000000000#include "internal.h" #if USE_OUR_OWN_MD5 #include "md5/global.h" #include "md5/md5.h" #else #include #endif #include "cabfile.h" #include "log.h" #include #include #include #if !defined(_MSC_VER) #include /* for MIN(a,b) */ #endif #ifdef _WIN32 #define fseek _fseeki64 #define ftell _ftelli64 #endif #ifndef MIN /* missing in some platforms */ #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #include #define VERBOSE 3 #define ror8(x,n) (((x) >> ((int)(n))) | ((x) << (8 - (int)(n)))) #define rol8(x,n) (((x) << ((int)(n))) | ((x) >> (8 - (int)(n)))) static const uint8_t END_OF_CHUNK[4] = { 0x00, 0x00, 0xff, 0xff }; static FileDescriptor* unshield_read_file_descriptor(Unshield* unshield, int index) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; uint8_t* p = NULL; uint8_t* saved_p = NULL; FileDescriptor* fd = NEW1(FileDescriptor); switch (header->major_version) { case 0: case 5: saved_p = p = header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->file_table[header->cab.directory_count + index]; #if VERBOSE unshield_trace("File descriptor offset %i: %08x", index, p - header->data); #endif fd->volume = header->index; fd->name_offset = READ_UINT32(p); p += 4; fd->directory_index = READ_UINT32(p); p += 4; fd->flags = READ_UINT16(p); p += 2; fd->expanded_size = READ_UINT32(p); p += 4; fd->compressed_size = READ_UINT32(p); p += 4; p += 0x14; fd->data_offset = READ_UINT32(p); p += 4; #if VERBOSE >= 2 unshield_trace("Name offset: %08x", fd->name_offset); unshield_trace("Directory index: %08x", fd->directory_index); unshield_trace("Flags: %04x", fd->flags); unshield_trace("Expanded size: %08x", fd->expanded_size); unshield_trace("Compressed size: %08x", fd->compressed_size); unshield_trace("Data offset: %08x", fd->data_offset); #endif if (header->major_version == 5) { memcpy(fd->md5, p, 0x10); p += 0x10; assert((p - saved_p) == 0x3a); } break; case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: default: saved_p = p = header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->cab.file_table_offset2 + index * 0x57; #if VERBOSE unshield_trace("File descriptor offset: %08x", p - header->data); #endif fd->flags = READ_UINT16(p); p += 2; fd->expanded_size = READ_UINT64(p); p += 8; fd->compressed_size = READ_UINT64(p); p += 8; fd->data_offset = READ_UINT64(p); p += 8; memcpy(fd->md5, p, 0x10); p += 0x10; p += 0x10; fd->name_offset = READ_UINT32(p); p += 4; fd->directory_index = READ_UINT16(p); p += 2; assert((p - saved_p) == 0x40); p += 0xc; fd->link_previous = READ_UINT32(p); p += 4; fd->link_next = READ_UINT32(p); p += 4; fd->link_flags = *p; p ++; #if VERBOSE if (fd->link_flags != LINK_NONE) { unshield_trace("Link: previous=%i, next=%i, flags=%i", fd->link_previous, fd->link_next, fd->link_flags); } #endif fd->volume = READ_UINT16(p); p += 2; assert((p - saved_p) == 0x57); break; } if (!(fd->flags & FILE_COMPRESSED) && fd->compressed_size != fd->expanded_size) { unshield_warning("File is not compressed but compressed size is %08x and expanded size is %08x", fd->compressed_size, fd->expanded_size); } return fd; } static FileDescriptor* unshield_get_file_descriptor(Unshield* unshield, int index) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; if (index < 0 || index >= (int)header->cab.file_count) { unshield_error("Invalid index"); return NULL; } if (!header->file_descriptors) header->file_descriptors = calloc(header->cab.file_count, sizeof(FileDescriptor*)); if (!header->file_descriptors[index]) header->file_descriptors[index] = unshield_read_file_descriptor(unshield, index); return header->file_descriptors[index]; } int unshield_file_count (Unshield* unshield)/*{{{*/ { if (unshield) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; return header->cab.file_count; } else return -1; }/*}}}*/ const char* unshield_file_name (Unshield* unshield, int index)/*{{{*/ { FileDescriptor* fd = unshield_get_file_descriptor(unshield, index); if (fd) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; return unshield_get_utf8_string(header, header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + fd->name_offset); } unshield_warning("Failed to get file descriptor %i", index); return NULL; }/*}}}*/ bool unshield_file_is_valid(Unshield* unshield, int index) { bool is_valid = false; FileDescriptor* fd; if (index < 0 || index >= unshield_file_count(unshield)) goto exit; if (!(fd = unshield_get_file_descriptor(unshield, index))) goto exit; if (fd->flags & FILE_INVALID) goto exit; if (!fd->name_offset) goto exit; if (!fd->data_offset) goto exit; is_valid = true; exit: return is_valid; } static int unshield_uncompress (Byte *dest, uLong* destLen, Byte *source, uLong *sourceLen)/*{{{*/ { z_stream stream; int err; stream.next_in = source; stream.avail_in = (uInt)*sourceLen; stream.next_out = dest; stream.avail_out = (uInt)*destLen; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; /* make second parameter negative to disable checksum verification */ err = inflateInit2(&stream, -MAX_WBITS); if (err != Z_OK) return err; err = inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { inflateEnd(&stream); return err; } *destLen = stream.total_out; *sourceLen = stream.total_in; err = inflateEnd(&stream); return err; }/*}}}*/ static int unshield_uncompress_old(Byte *dest, uLong *destLen, Byte *source, uLong *sourceLen)/*{{{*/ { z_stream stream; int err; stream.next_in = source; stream.avail_in = (uInt)*sourceLen; stream.next_out = dest; stream.avail_out = (uInt)*destLen; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; *destLen = 0; *sourceLen = 0; /* make second parameter negative to disable checksum verification */ err = inflateInit2(&stream, -MAX_WBITS); if (err != Z_OK) return err; while (stream.avail_in > 1) { err = inflate(&stream, Z_BLOCK); if (err != Z_OK) { inflateEnd(&stream); return err; } } *destLen = stream.total_out; *sourceLen = stream.total_in; err = inflateEnd(&stream); return err; }/*}}}*/ typedef struct { Unshield* unshield; unsigned index; FileDescriptor* file_descriptor; int volume; FILE* volume_file; VolumeHeader volume_header; unsigned volume_bytes_left; unsigned obfuscation_offset; } UnshieldReader; static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{*/ { bool success = false; uint64_t data_offset = 0; uint64_t volume_bytes_left_compressed; uint64_t volume_bytes_left_expanded; CommonHeader common_header; #if VERBOSE >= 2 unshield_trace("Open volume %i", volume); #endif FCLOSE(reader->unshield, reader->volume_file); reader->volume_file = unshield_fopen_for_reading(reader->unshield, volume, CABINET_SUFFIX); if (!reader->volume_file) { unshield_error("Failed to open input cabinet file %i", volume); goto exit; } { uint8_t tmp[COMMON_HEADER_SIZE]; uint8_t* p = tmp; if (COMMON_HEADER_SIZE != unshield_fread(reader->unshield, &tmp, 1, COMMON_HEADER_SIZE, reader->volume_file)) goto exit; if (!unshield_read_common_header(&p, &common_header)) goto exit; } memset(&reader->volume_header, 0, sizeof(VolumeHeader)); switch (reader->unshield->header_list->major_version) { case 0: case 5: { uint8_t five_header[VOLUME_HEADER_SIZE_V5]; uint8_t* p = five_header; if (VOLUME_HEADER_SIZE_V5 != unshield_fread(reader->unshield, &five_header, 1, VOLUME_HEADER_SIZE_V5, reader->volume_file)) goto exit; reader->volume_header.data_offset = READ_UINT32(p); p += 4; #if VERBOSE if (READ_UINT32(p)) unshield_trace("Unknown = %08x", READ_UINT32(p)); #endif /* unknown */ p += 4; reader->volume_header.first_file_index = READ_UINT32(p); p += 4; reader->volume_header.last_file_index = READ_UINT32(p); p += 4; reader->volume_header.first_file_offset = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_expanded = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_compressed = READ_UINT32(p); p += 4; reader->volume_header.last_file_offset = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_expanded = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_compressed = READ_UINT32(p); p += 4; if (reader->volume_header.last_file_offset == 0) reader->volume_header.last_file_offset = INT32_MAX; } break; case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: default: { uint8_t six_header[VOLUME_HEADER_SIZE_V6]; uint8_t* p = six_header; if (VOLUME_HEADER_SIZE_V6 != unshield_fread(reader->unshield, &six_header, 1, VOLUME_HEADER_SIZE_V6, reader->volume_file)) goto exit; reader->volume_header.data_offset = READ_UINT32(p); p += 4; reader->volume_header.data_offset_high = READ_UINT32(p); p += 4; reader->volume_header.first_file_index = READ_UINT32(p); p += 4; reader->volume_header.last_file_index = READ_UINT32(p); p += 4; reader->volume_header.first_file_offset = READ_UINT32(p); p += 4; reader->volume_header.first_file_offset_high = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_expanded = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_expanded_high = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_compressed = READ_UINT32(p); p += 4; reader->volume_header.first_file_size_compressed_high = READ_UINT32(p); p += 4; reader->volume_header.last_file_offset = READ_UINT32(p); p += 4; reader->volume_header.last_file_offset_high = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_expanded = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_expanded_high = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_compressed = READ_UINT32(p); p += 4; reader->volume_header.last_file_size_compressed_high = READ_UINT32(p); p += 4; } break; } #if VERBOSE >= 2 unshield_trace("First file index = %i, last file index = %i", reader->volume_header.first_file_index, reader->volume_header.last_file_index); unshield_trace("First file offset = %08x, last file offset = %08x", reader->volume_header.first_file_offset, reader->volume_header.last_file_offset); #endif /* enable support for split archives for IS5 */ if (reader->unshield->header_list->major_version == 5) { if (reader->index < (reader->unshield->header_list->cab.file_count - 1) && reader->index == reader->volume_header.last_file_index && reader->volume_header.last_file_size_compressed != reader->file_descriptor->compressed_size) { unshield_trace("IS5 split file last in volume"); reader->file_descriptor->flags |= FILE_SPLIT; } else if (reader->index > 0 && reader->index == reader->volume_header.first_file_index && reader->volume_header.first_file_size_compressed != reader->file_descriptor->compressed_size) { unshield_trace("IS5 split file first in volume"); reader->file_descriptor->flags |= FILE_SPLIT; } } if (reader->file_descriptor->flags & FILE_SPLIT) { #if VERBOSE unshield_trace(/*"Total bytes left = 0x08%x, "*/"previous data offset = 0x08%x", /*total_bytes_left, */data_offset); #endif if (reader->index == reader->volume_header.last_file_index && reader->volume_header.last_file_offset != 0x7FFFFFFF) { /* can be first file too... */ #if VERBOSE unshield_trace("Index %i is last file in cabinet file %i", reader->index, volume); #endif data_offset = reader->volume_header.last_file_offset; volume_bytes_left_expanded = reader->volume_header.last_file_size_expanded; volume_bytes_left_compressed = reader->volume_header.last_file_size_compressed; } else if (reader->index == reader->volume_header.first_file_index) { #if VERBOSE unshield_trace("Index %i is first file in cabinet file %i", reader->index, volume); #endif data_offset = reader->volume_header.first_file_offset; volume_bytes_left_expanded = reader->volume_header.first_file_size_expanded; volume_bytes_left_compressed = reader->volume_header.first_file_size_compressed; } else { success = true; goto exit; } #if VERBOSE unshield_trace("Will read 0x%08x bytes from offset 0x%08x", volume_bytes_left_compressed, data_offset); #endif } else { data_offset = reader->file_descriptor->data_offset; volume_bytes_left_expanded = reader->file_descriptor->expanded_size; volume_bytes_left_compressed = reader->file_descriptor->compressed_size; } if (reader->file_descriptor->flags & FILE_COMPRESSED) reader->volume_bytes_left = volume_bytes_left_compressed; else reader->volume_bytes_left = volume_bytes_left_expanded; unshield_fseek(reader->unshield, reader->volume_file, data_offset, SEEK_SET); reader->volume = volume; success = true; exit: return success; }/*}}}*/ void unshield_deobfuscate(unsigned char* buffer, size_t size, unsigned* seed) { unsigned tmp_seed = *seed; for (; size > 0; size--, buffer++, tmp_seed++) { *buffer = ror8(*buffer ^ 0xd5, 2) - (tmp_seed % 0x47); } *seed = tmp_seed; } static void unshield_reader_deobfuscate(UnshieldReader* reader, uint8_t* buffer, size_t size) { unshield_deobfuscate(buffer, size, &reader->obfuscation_offset); } static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t size)/*{{{*/ { bool success = false; uint8_t* p = buffer; size_t bytes_left = size; #if VERBOSE >= 3 unshield_trace("unshield_reader_read start: bytes_left = 0x%x, volume_bytes_left = 0x%x", bytes_left, reader->volume_bytes_left); #endif for (;;) { /* Read as much as possible from this volume */ size_t bytes_to_read = MIN(bytes_left, reader->volume_bytes_left); #if VERBOSE >= 3 unshield_trace("Trying to read 0x%x bytes from offset %08x in volume %i", bytes_to_read, unshield_ftell(reader->unshield, 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 != unshield_fread(reader->unshield, p, 1, bytes_to_read, reader->volume_file)) { unshield_error("Failed to read 0x%08x bytes of file %i (%s) from volume %i. Current offset = 0x%08x", bytes_to_read, reader->index, unshield_file_name(reader->unshield, reader->index), reader->volume, unshield_ftell(reader->unshield, reader->volume_file)); goto exit; } bytes_left -= bytes_to_read; reader->volume_bytes_left -= bytes_to_read; #if VERBOSE >= 3 unshield_trace("bytes_left = %i, volume_bytes_left = %i", bytes_left, reader->volume_bytes_left); #endif if (!bytes_left) break; p += bytes_to_read; /* Open next volume */ if (!unshield_reader_open_volume(reader, reader->volume + 1)) { unshield_error("Failed to open volume %i to read %i more bytes", reader->volume + 1, bytes_to_read); goto exit; } } if (reader->file_descriptor->flags & FILE_OBFUSCATED) unshield_reader_deobfuscate(reader, buffer, size); success = true; exit: return success; }/*}}}*/ int copy_file(Unshield* unshield, FILE* infile, FILE* outfile) { #define SIZE (1024*1024) char buffer[SIZE]; size_t bytes; while (0 < (bytes = unshield_fread(unshield, buffer, 1, sizeof(buffer), infile))) unshield_fwrite(unshield, buffer, 1, bytes, outfile); return 0; } static UnshieldReader* unshield_reader_create_external(/*{{{*/ Unshield* unshield, int index, FileDescriptor* file_descriptor) { bool success = false; const char* file_name = unshield_file_name(unshield, index); const char* directory_name = unshield_directory_name(unshield, file_descriptor->directory_index); char* base_directory_name = unshield_get_base_directory_name(unshield); long int path_max = unshield_get_path_max(unshield); char* directory_and_filename = malloc(path_max); UnshieldReader* reader = NEW1(UnshieldReader); if (!reader) goto exit; reader->unshield = unshield; reader->index = index; reader->file_descriptor = file_descriptor; snprintf(directory_and_filename, path_max, "%s/%s/%s", base_directory_name, directory_name, file_name); reader->volume_file = fopen(directory_and_filename, "rb"); if (!reader->volume_file) { unshield_error("Failed to open input file %s", directory_and_filename); goto exit; } if (file_descriptor->flags & FILE_COMPRESSED) { long file_size = FSIZE(unshield, reader->volume_file); FILE *temporary_file = NULL; /* * Normally the compressed data is nicely terminated with end of chunk marker 00 00 ff ff but not always * This seem to happen for small files where the compressed size and expanded size are almost the same. * Workaround: Create a temporary file with the correct end of chunk. */ long diff = file_descriptor->compressed_size - file_size; if (diff > 0) { diff = MIN(sizeof(END_OF_CHUNK), diff); temporary_file = tmpfile(); copy_file(reader->unshield, reader->volume_file, temporary_file); fwrite(END_OF_CHUNK + sizeof(END_OF_CHUNK) - diff, 1, diff, temporary_file); fseek(temporary_file, 0, SEEK_SET); fclose(reader->volume_file); reader->volume_file = temporary_file; reader->volume_bytes_left = file_size + diff; } else { reader->volume_bytes_left = file_descriptor->compressed_size; } } else { reader->volume_bytes_left = file_descriptor->expanded_size; } success = true; exit: FREE(base_directory_name); FREE(directory_and_filename); if (success) return reader; FREE(reader); return NULL; } static UnshieldReader* unshield_reader_create(/*{{{*/ Unshield* unshield, int index, FileDescriptor* file_descriptor) { bool success = false; UnshieldReader* reader = NEW1(UnshieldReader); if (!reader) return NULL; reader->unshield = unshield; reader->index = index; reader->file_descriptor = file_descriptor; for (;;) { if (!unshield_reader_open_volume(reader, file_descriptor->volume)) { unshield_error("Failed to open volume %i", file_descriptor->volume); goto exit; } /* Start with the correct volume for IS5 cabinets */ if (reader->unshield->header_list->major_version <= 5 && index > (int)reader->volume_header.last_file_index) { unshield_trace("Trying next volume..."); file_descriptor->volume++; continue; } break; }; success = true; exit: if (success) return reader; FREE(reader); return NULL; }/*}}}*/ static void unshield_reader_destroy(UnshieldReader* reader)/*{{{*/ { if (reader) { FCLOSE(reader->unshield, reader->volume_file); free(reader); } }/*}}}*/ #define BUFFER_SIZE (64*1024) /* * If filename is NULL, just throw away the result */ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{{{*/ { bool success = false; FILE* output = NULL; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE+1); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; #if USE_OUR_OWN_MD5 MD5_CTX md5; MD5Init(&md5); #else EVP_MD_CTX *md5 = EVP_MD_CTX_new(); EVP_MD_CTX_init(md5); EVP_DigestInit_ex(md5, EVP_md5(), NULL); #endif if (!unshield) goto exit; if (!(file_descriptor = unshield_get_file_descriptor(unshield, index))) { unshield_error("Failed to get file descriptor for file %i", index); goto exit; } if ((file_descriptor->flags & FILE_INVALID) || 0 == file_descriptor->data_offset) { /* invalid file */ goto exit; } if (file_descriptor->link_flags & LINK_PREV) { success = unshield_file_save(unshield, file_descriptor->link_previous, filename); goto exit; } reader = unshield_reader_create(unshield, index, file_descriptor); if (!reader) { unshield_error("Failed to create data reader for file %i", index); goto exit; } if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; } if (filename) { output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); goto exit; } } if (file_descriptor->flags & FILE_COMPRESSED) bytes_left = file_descriptor->compressed_size; else bytes_left = file_descriptor->expanded_size; /*unshield_trace("Bytes to read: %i", bytes_left);*/ while (bytes_left > 0) { uLong bytes_to_write = BUFFER_SIZE; int result; if (file_descriptor->flags & FILE_COMPRESSED) { uLong read_bytes; uint16_t bytes_to_read = 0; uint8_t bytes_to_read_bytes[2]; if (!unshield_reader_read(reader, bytes_to_read_bytes, sizeof(bytes_to_read_bytes))) { unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", sizeof(bytes_to_read_bytes), index, unshield_file_name(unshield, index), file_descriptor->volume); goto exit; } bytes_to_read = READ_UINT16(bytes_to_read_bytes); 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 unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", bytes_to_read, index, unshield_file_name(unshield, index), file_descriptor->volume); #endif goto exit; } /* add a null byte to make inflate happy */ input_buffer[bytes_to_read] = 0; read_bytes = bytes_to_read+1; result = unshield_uncompress(output_buffer, &bytes_to_write, input_buffer, &read_bytes); if (Z_OK != result) { 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; } #if VERBOSE >= 3 unshield_trace("read_bytes = %i", read_bytes); #endif bytes_left -= 2; bytes_left -= bytes_to_read; } else { bytes_to_write = MIN(bytes_left, BUFFER_SIZE); if (!unshield_reader_read(reader, output_buffer, bytes_to_write)) { #if VERBOSE unshield_error("Failed to read %i bytes from input cabinet file %i", bytes_to_write, file_descriptor->volume); #endif goto exit; } bytes_left -= bytes_to_write; } #if USE_OUR_OWN_MD5 MD5Update(&md5, output_buffer, bytes_to_write); #else EVP_DigestUpdate(md5, output_buffer, bytes_to_write); #endif if (output) { if (bytes_to_write != unshield_fwrite(unshield, output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } } total_written += bytes_to_write; } if (file_descriptor->expanded_size != total_written) { unshield_error("Expanded size expected to be %i, but was %i", file_descriptor->expanded_size, total_written); goto exit; } if (unshield->header_list->major_version >= 6) { unsigned char md5result[16]; #if USE_OUR_OWN_MD5 MD5Final(md5result, &md5); #else EVP_DigestFinal_ex(md5, md5result, NULL); #endif if (0 != memcmp(md5result, file_descriptor->md5, 16)) { unshield_error("MD5 checksum failure for file %i (%s)", index, unshield_file_name(unshield, index)); goto exit; } } success = true; exit: #ifndef USE_OUR_OWN_MD5 EVP_MD_CTX_free(md5); md5 = NULL; #endif unshield_reader_destroy(reader); FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; }/*}}}*/ int unshield_file_directory(Unshield* unshield, int index)/*{{{*/ { FileDescriptor* fd = unshield_get_file_descriptor(unshield, index); if (fd) { return fd->directory_index; } else return -1; }/*}}}*/ size_t unshield_file_size(Unshield* unshield, int index)/*{{{*/ { FileDescriptor* fd = unshield_get_file_descriptor(unshield, index); if (fd) { return fd->expanded_size; } else return 0; }/*}}}*/ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) { /* XXX: Thou Shalt Not Cut & Paste... */ bool success = false; FILE* output = NULL; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned int bytes_left; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; if (!unshield) goto exit; if (!(file_descriptor = unshield_get_file_descriptor(unshield, index))) { unshield_error("Failed to get file descriptor for file %i", index); goto exit; } if ((file_descriptor->flags & FILE_INVALID) || 0 == file_descriptor->data_offset) { /* invalid file */ goto exit; } if (file_descriptor->link_flags & LINK_PREV) { success = unshield_file_save_raw(unshield, file_descriptor->link_previous, filename); goto exit; } reader = unshield_reader_create(unshield, index, file_descriptor); if (!reader) { unshield_error("Failed to create data reader for file %i", index); goto exit; } if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; } if (filename) { output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); goto exit; } } if (file_descriptor->flags & FILE_COMPRESSED) bytes_left = file_descriptor->compressed_size; else bytes_left = file_descriptor->expanded_size; /*unshield_trace("Bytes to read: %i", bytes_left);*/ while (bytes_left > 0) { uLong bytes_to_write = MIN(bytes_left, BUFFER_SIZE); if (!unshield_reader_read(reader, output_buffer, bytes_to_write)) { #if VERBOSE unshield_error("Failed to read %i bytes from input cabinet file %i", bytes_to_write, file_descriptor->volume); #endif goto exit; } bytes_left -= bytes_to_write; if (output) { if (bytes_to_write != unshield_fwrite(unshield, output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } } } success = true; exit: unshield_reader_destroy(reader); FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; } static uint8_t* find_bytes( const uint8_t* buffer, size_t bufferSize, const uint8_t* pattern, size_t patternSize) { const unsigned char *p = buffer; size_t buffer_left = bufferSize; while ((p = memchr(p, pattern[0], buffer_left)) != NULL) { if (patternSize > buffer_left) break; if (memcmp(p, pattern, patternSize) == 0) return (uint8_t*)p; ++p; --buffer_left; } return NULL; } bool unshield_file_save_old(Unshield* unshield, int index, const char* filename)/*{{{*/ { /* XXX: Thou Shalt Not Cut & Paste... */ bool success = false; FILE* output = NULL; 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); unsigned int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; if (!unshield) goto exit; if (!(file_descriptor = unshield_get_file_descriptor(unshield, index))) { unshield_error("Failed to get file descriptor for file %i", index); goto exit; } if ((file_descriptor->flags & FILE_INVALID) || 0 == file_descriptor->data_offset) { /* invalid file */ goto exit; } if (file_descriptor->link_flags & LINK_PREV) { success = unshield_file_save_old(unshield, file_descriptor->link_previous, filename); goto exit; } reader = unshield_reader_create(unshield, index, file_descriptor); if (!reader) { unshield_error("Failed to create data reader for file %i", index); goto exit; } if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet. Trying external file!", index); unshield_reader_destroy(reader); reader = unshield_reader_create_external(unshield, index, file_descriptor); if (!reader) { unshield_error("Failed to create data reader for file %i", index); goto exit; } } if (filename) { output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); goto exit; } } bytes_left = file_descriptor->expanded_size; #if VERBOSE >= 4 unshield_trace("Bytes to write: %i", bytes_left); #endif while (bytes_left > 0) { 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) { uLong read_bytes; size_t input_size = reader->volume_bytes_left; uint8_t* chunk_buffer; while (input_size > input_buffer_size) { input_buffer_size *= 2; #if VERBOSE >= 3 unshield_trace("increased input_buffer_size to 0x%x", input_buffer_size); #endif input_buffer = realloc(input_buffer, input_buffer_size); assert(input_buffer); } if (!unshield_reader_read(reader, input_buffer, input_size)) { #if VERBOSE unshield_error("Failed to read 0x%x bytes of file %i (%s) from input cabinet file %i", input_size, index, unshield_file_name(unshield, index), file_descriptor->volume); #endif goto exit; } for (chunk_buffer = input_buffer; input_size && bytes_left; ) { size_t chunk_size; uint8_t* match = find_bytes(chunk_buffer, input_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; /* 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 /* add a null byte to make inflate happy */ chunk_buffer[chunk_size] = 0; bytes_to_write = BUFFER_SIZE; read_bytes = chunk_size; result = unshield_uncompress_old(output_buffer, &bytes_to_write, chunk_buffer, &read_bytes); if (Z_OK != result) { 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; } #if VERBOSE >= 3 unshield_trace("read_bytes = 0x%x", read_bytes); #endif chunk_buffer += chunk_size; chunk_buffer += sizeof(END_OF_CHUNK); input_size -= chunk_size; input_size -= sizeof(END_OF_CHUNK); bytes_left -= bytes_to_write; if (output) { if (bytes_to_write != unshield_fwrite(unshield, output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } } total_written += bytes_to_write; } } else { bytes_to_write = MIN(bytes_left, BUFFER_SIZE); if (!unshield_reader_read(reader, output_buffer, bytes_to_write)) { #if VERBOSE unshield_error("Failed to read %i bytes from input cabinet file %i", bytes_to_write, file_descriptor->volume); #endif goto exit; } bytes_left -= bytes_to_write; if (output) { if (bytes_to_write != unshield_fwrite(unshield, output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } } total_written += bytes_to_write; } } if (file_descriptor->expanded_size != total_written) { unshield_error("Expanded size expected to be %i, but was %i", file_descriptor->expanded_size, total_written); goto exit; } success = true; exit: unshield_reader_destroy(reader); FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; }/*}}}*/ twogood-unshield-51de441/lib/file_group.c000066400000000000000000000034251477345220400204170ustar00rootroot00000000000000#include "internal.h" #include "log.h" #include #include #define VERBOSE 1 UnshieldFileGroup* unshield_file_group_new(Header* header, uint32_t offset)/*{{{*/ { UnshieldFileGroup* self = NEW1(UnshieldFileGroup); uint8_t* p = unshield_header_get_buffer(header, offset); #if VERBOSE unshield_trace("File group descriptor offset: %08x", offset); #endif self->name = unshield_header_get_string(header, READ_UINT32(p)); p += 4; if (header->major_version <= 5) p += 0x48; else p += 0x12; self->first_file = READ_UINT32(p); p += 4; self->last_file = READ_UINT32(p); p += 4; #if VERBOSE unshield_trace("File group %08x first file = %i, last file = %i", offset, self->first_file, self->last_file); #endif return self; }/*}}}*/ void unshield_file_group_destroy(UnshieldFileGroup* self)/*{{{*/ { FREE(self); }/*}}}*/ int unshield_file_group_count(Unshield* unshield)/*{{{*/ { Header* header = unshield->header_list; return header->file_group_count; }/*}}}*/ UnshieldFileGroup* unshield_file_group_get(Unshield* unshield, int index) { Header* header = unshield->header_list; if (index >= 0 && index < header->file_group_count) return header->file_groups[index]; else return NULL; } UnshieldFileGroup* unshield_file_group_find(Unshield* unshield, const char* name) { Header* header = unshield->header_list; int i; for (i = 0; i < header->file_group_count; i++) { if (STREQ(header->file_groups[i]->name, name)) return header->file_groups[i]; } return NULL; } const char* unshield_file_group_name(Unshield* unshield, int index)/*{{{*/ { Header* header = unshield->header_list; if (index >= 0 && index < header->file_group_count) return header->file_groups[index]->name; else return NULL; }/*}}}*/ twogood-unshield-51de441/lib/helper.c000066400000000000000000000150151477345220400175410ustar00rootroot00000000000000#define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include "internal.h" #include "converter.h" #include "log.h" #include #include #include #include #include #include #ifdef _WIN32 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #ifndef PATH_MAX #define PATH_MAX _MAX_PATH #endif #else #include #endif #define VERBOSE 0 #if defined(_MSC_VER) #define snprintf _snprintf #define vsnprintf _vsnprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif long int unshield_get_path_max(Unshield* unshield) { #ifdef PATH_MAX return PATH_MAX; #else long int path_max = pathconf(unshield->filename_pattern, _PC_PATH_MAX); if (path_max <= 0) path_max = 4096; return path_max; #endif } char *unshield_get_base_directory_name(Unshield *unshield) { long int path_max = unshield_get_path_max(unshield); char *p = unshield_get_last_path_separator(unshield->filename_pattern); char *dirname = malloc(path_max); if (p) { strncpy(dirname, unshield->filename_pattern, path_max); if ((unsigned int) (p - unshield->filename_pattern) > path_max) { dirname[path_max - 1] = 0; } else dirname[(p - unshield->filename_pattern)] = 0; } else strcpy(dirname, "."); return dirname; } static char* get_filename(Unshield* unshield, int index, const char* suffix) { if (unshield && unshield->filename_pattern) { long path_max = unshield_get_path_max(unshield); char* filename = malloc(path_max); if (filename == NULL) { unshield_error("Unable to allocate memory.\n"); goto exit; } if (snprintf(filename, path_max, unshield->filename_pattern, index, suffix) >= path_max) { unshield_error("Pathname exceeds system limits.\n"); goto exit; } exit: return filename; } return NULL; } FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suffix) { if (unshield && unshield->filename_pattern) { FILE* result = NULL; char* filename = get_filename(unshield, index, suffix); char* dirname = unshield_get_base_directory_name(unshield); const char *q; struct dirent *dent = NULL; DIR *sourcedir = NULL; long int path_max = unshield_get_path_max(unshield); q=unshield_get_last_path_separator(filename); if (q) q++; else q=filename; sourcedir = unshield_opendir(unshield, dirname); /* Search for the File case independent */ if (sourcedir) { for (dent=unshield_readdir(unshield, sourcedir);dent; dent=unshield_readdir(unshield, sourcedir)) { if (!(strcasecmp(q, dent->d_name))) { /*unshield_trace("Found match %s\n",dent->d_name);*/ break; } } if (dent == NULL) { unshield_trace("File %s not found even case insensitive\n",filename); goto exit; } else if(snprintf(filename, path_max, "%s/%s", dirname, dent->d_name)>=path_max) { unshield_error("Pathname exceeds system limits.\n"); goto exit; } } else unshield_trace("Could not open directory %s error %s\n", dirname, strerror(errno)); #if VERBOSE unshield_trace("Opening file '%s'", filename); #endif result = unshield_fopen(unshield, filename, "rb"); exit: if (sourcedir) unshield_closedir(unshield, sourcedir); free(filename); free(dirname); return result; } return NULL; } long long unshield_fsize(Unshield* unshield, FILE* file) { long long result; long long previous = unshield_ftell(unshield, file); unshield_fseek(unshield, file, 0L, SEEK_END); result = unshield_ftell(unshield, file); unshield_fseek(unshield, file, previous, SEEK_SET); return result; } bool unshield_read_common_header(uint8_t** buffer, CommonHeader* common) { uint8_t* p = *buffer; common->signature = READ_UINT32(p); p += 4; if (CAB_SIGNATURE != common->signature) { unshield_error("Invalid file signature"); if (MSCF_SIGNATURE == common->signature) unshield_warning("Found Microsoft Cabinet header. Use cabextract (https://www.cabextract.org.uk/) to unpack this file."); return false; } common->version = READ_UINT32(p); p += 4; common->volume_info = READ_UINT32(p); p += 4; common->cab_descriptor_offset = READ_UINT32(p); p += 4; common->cab_descriptor_size = READ_UINT32(p); p += 4; #if VERBOSE unshield_trace("Common header: %08x %08x %08x %08x", common->version, common->volume_info, common->cab_descriptor_offset, common->cab_descriptor_size); #endif *buffer = p; return true; } /** Get pointer at cab descriptor + offset */ uint8_t* unshield_header_get_buffer(Header* header, uint32_t offset) { if (offset) return header->data + header->common.cab_descriptor_offset + offset; else return NULL; } /** Returns the last path separator in a filesystem path */ char *unshield_get_last_path_separator(char *path) { char *p = strrchr(path, '/'); #ifdef WIN32 char *pbs = strrchr(path, '\\'); if (NULL != pbs && (NULL == p || pbs > p)) return pbs; #endif return p; } static int unshield_strlen_utf16(const uint16_t* utf16) { const uint16_t* current = utf16; while (*current++) ; return current - utf16; } static StringBuffer* unshield_add_string_buffer(Header* header) { StringBuffer* result = NEW1(StringBuffer); result->next = header->string_buffer; return header->string_buffer = result; } static const char* unshield_utf16_to_utf8(Header* header, const uint16_t* utf16) { StringBuffer* string_buffer = unshield_add_string_buffer(header); int length = unshield_strlen_utf16(utf16); int buffer_size = 3 * length + 1; char* target = string_buffer->string = NEW(char, buffer_size); size_t result = utf16_to_utf8( utf16, length + 1, target, buffer_size); return string_buffer->string; } const char* unshield_get_utf8_string(Header* header, const void* buffer) { if (header->major_version >= 17 && buffer != NULL) { return unshield_utf16_to_utf8(header, (const uint16_t*)buffer); } else { return (const char*)buffer; } } /** Get string at cab descriptor offset + string offset */ const char* unshield_header_get_string(Header* header, uint32_t offset) { return unshield_get_utf8_string(header, unshield_header_get_buffer(header, offset)); } twogood-unshield-51de441/lib/internal.h000066400000000000000000000110241477345220400200770ustar00rootroot00000000000000#ifndef __internal_h__ #define __internal_h__ #include "libunshield.h" #include "lib/unshield_config.h" #if HAVE_STDINT_H #include #elif HAVE_INTTYPES_H #include #endif #include #include /* for FILE */ #include "cabfile.h" typedef struct _StringBuffer StringBuffer; struct _StringBuffer { StringBuffer* next; char* string; }; typedef struct _Header Header; struct _Header { Header* next; int index; uint8_t* data; size_t size; int major_version; /* shortcuts */ CommonHeader common; CabDescriptor cab; uint32_t* file_table; FileDescriptor** file_descriptors; int component_count; UnshieldComponent** components; int file_group_count; UnshieldFileGroup** file_groups; StringBuffer* string_buffer; }; struct _Unshield { Header* header_list; char* filename_pattern; const UnshieldIoCallbacks* io_callbacks; void* io_userdata; }; /* Internal component functions */ UnshieldComponent* unshield_component_new(Header* header, uint32_t offset); void unshield_component_destroy(UnshieldComponent* self); /* Internal file group functions */ UnshieldFileGroup* unshield_file_group_new(Header* header, uint32_t offset); void unshield_file_group_destroy(UnshieldFileGroup* self); /* Helpers */ char *unshield_get_base_directory_name(Unshield *unshield); long int unshield_get_path_max(Unshield* unshield); FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suffix); long long unshield_fsize(Unshield* unshield, FILE* file); bool unshield_read_common_header(uint8_t** buffer, CommonHeader* common); const char* unshield_get_utf8_string(Header* header, const void* buffer); const char* unshield_header_get_string(Header* header, uint32_t offset); uint8_t* unshield_header_get_buffer(Header* header, uint32_t offset); char *unshield_get_last_path_separator(char *path); static inline void* unshield_fopen(Unshield* unshield, const char *filename, const char *modes) { return unshield->io_callbacks->fopen(filename, modes, unshield->io_userdata); } static inline int unshield_fseek(Unshield* unshield, void *file, long int offset, int whence) { return unshield->io_callbacks->fseek(file, offset, whence, unshield->io_userdata); } static inline long int unshield_ftell(Unshield* unshield, void *file) { return unshield->io_callbacks->ftell(file, unshield->io_userdata); } static inline size_t unshield_fread(Unshield* unshield, void *ptr, size_t size, size_t n, void *file) { return unshield->io_callbacks->fread(ptr, size, n, file, unshield->io_userdata); } static inline size_t unshield_fwrite(Unshield* unshield, const void *ptr, size_t size, size_t n, void *file) { return unshield->io_callbacks->fwrite(ptr, size, n, file, unshield->io_userdata); } static inline int unshield_fclose(Unshield* unshield, void *ptr) { return unshield->io_callbacks->fclose(ptr, unshield->io_userdata); } static inline void * unshield_opendir(Unshield* unshield, const char *name) { return unshield->io_callbacks->opendir(name, unshield->io_userdata); } static inline int unshield_closedir(Unshield* unshield, void *dir) { return unshield->io_callbacks->closedir(dir, unshield->io_userdata); } static inline struct dirent* unshield_readdir(Unshield* unshield, void *dir) { return unshield->io_callbacks->readdir(dir, unshield->io_userdata); } /* Constants */ #define HEADER_SUFFIX "hdr" #define CABINET_SUFFIX "cab" /* Macros for safer development */ #define FREE(ptr) { if (ptr) { free(ptr); (ptr) = NULL; } } #define STRDUP(str) ((str) ? strdup(str) : NULL) #define NEW(type, count) ((type*)calloc(count, sizeof(type))) #define NEW1(type) ((type*)calloc(1, sizeof(type))) #define FCLOSE(unshield, file) if (file) { unshield_fclose(unshield, file); (file) = NULL; } #define FSIZE(unshield, file) ((file) ? unshield_fsize(unshield, file) : 0) #define STREQ(s1,s2) (0 == strcmp(s1,s2)) static inline uint16_t get_unaligned_le16(const uint8_t *p) { return p[0] | p[1] << 8; } static inline uint32_t get_unaligned_le32(const uint8_t *p) { return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; } static inline uint64_t get_unaligned_le64(const uint8_t *p) { return (uint64_t)get_unaligned_le32(p + 4) << 32 | get_unaligned_le32(p); } #define READ_UINT16(p) get_unaligned_le16(p) #define READ_UINT32(p) get_unaligned_le32(p) #define READ_UINT64(p) get_unaligned_le64(p) #define READ_INT16(p) ((int16_t)READ_UINT16(p)) #define READ_INT32(p) ((int32_t)READ_UINT32(p)) #endif twogood-unshield-51de441/lib/libunshield.c000066400000000000000000000311641477345220400205670ustar00rootroot00000000000000#define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include "internal.h" #include "log.h" #include #include #include #include #include #ifdef _WIN32 #define unshield_native_fseek _fseeki64 #define unshield_native_ftell _ftelli64 #else #define unshield_native_fseek fseek #define unshield_native_ftell ftell #endif void *unshield_default_fopen(const char *filename, const char *modes, void *userdata) { (void)userdata; return fopen(filename, modes); } int unshield_default_fseek(void *file, long int offset, int whence, void *userdata) { (void)userdata; return unshield_native_fseek(file, offset, whence); } long int unshield_default_ftell(void *file, void *userdata) { (void)userdata; return unshield_native_ftell(file); } size_t unshield_default_fread(void *ptr, size_t size, size_t n, void *file, void *userdata) { (void)userdata; return fread(ptr, size, n, file); } size_t unshield_default_fwrite(const void *ptr, size_t size, size_t n, void *file, void *userdata) { (void)userdata; return fwrite(ptr, size, n, file); } int unshield_default_fclose(void *file, void *userdata) { (void)userdata; return fclose(file); } void *unshield_default_opendir(const char *name, void *userdata) { (void)userdata; return opendir(name); } int unshield_default_closedir(void *dir, void *userdata) { (void)userdata; return closedir(dir); } struct dirent* unshield_default_readdir(void *dir, void *userdata) { (void)userdata; return readdir(dir); } static UnshieldIoCallbacks unshield_default_io_callbacks = { .fopen = unshield_default_fopen, .fseek = unshield_default_fseek, .ftell = unshield_default_ftell, .fread = unshield_default_fread, .fwrite = unshield_default_fwrite, .fclose = unshield_default_fclose, .opendir = unshield_default_opendir, .closedir = unshield_default_closedir, .readdir = unshield_default_readdir, }; /** Create filename pattern used by unshield_fopen_for_reading() */ static bool unshield_create_filename_pattern(Unshield* unshield, const char* filename)/*{{{*/ { /* TODO Correct his function so that it handles filenames with more than one dot ('.')! */ if (unshield && filename) { char pattern[256]; char* prefix = strdup(filename); char* p = unshield_get_last_path_separator(prefix); if (!p) p = prefix; for (; *p != '\0'; p++) { if ('.' == *p || isdigit(*p)) { *p = '\0'; break; } } snprintf(pattern, sizeof(pattern), "%s%%i.%%s", prefix); free(prefix); FREE(unshield->filename_pattern); unshield->filename_pattern = strdup(pattern); return true; } else return false; }/*}}}*/ static bool unshield_get_common_header(Header* header) { uint8_t* p = header->data; return unshield_read_common_header(&p, &header->common); } static bool unshield_get_cab_descriptor(Header* header) { if (header->common.cab_descriptor_size) { uint8_t* p = header->data + header->common.cab_descriptor_offset; int i; p += 0xc; header->cab.file_table_offset = READ_UINT32(p); p += 4; p += 4; header->cab.file_table_size = READ_UINT32(p); p += 4; header->cab.file_table_size2 = READ_UINT32(p); p += 4; header->cab.directory_count = READ_UINT32(p); p += 4; p += 8; header->cab.file_count = READ_UINT32(p); p += 4; header->cab.file_table_offset2 = READ_UINT32(p); p += 4; assert((p - (header->data + header->common.cab_descriptor_offset)) == 0x30); if (header->cab.file_table_size != header->cab.file_table_size2) unshield_warning("File table sizes do not match"); unshield_trace("Cabinet descriptor: %08x %08x %08x %08x", header->cab.file_table_offset, header->cab.file_table_size, header->cab.file_table_size2, header->cab.file_table_offset2 ); unshield_trace("Directory count: %i", header->cab.directory_count); unshield_trace("File count: %i", header->cab.file_count); p += 0xe; for (i = 0; i < MAX_FILE_GROUP_COUNT; i++) { header->cab.file_group_offsets[i] = READ_UINT32(p); p += 4; } for (i = 0; i < MAX_COMPONENT_COUNT; i++) { header->cab.component_offsets[i] = READ_UINT32(p); p += 4; } return true; } else { unshield_error("No CAB descriptor available!"); return false; } } static bool unshield_get_file_table(Header* header) { uint8_t* p = header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset; int count = header->cab.directory_count + header->cab.file_count; int i; header->file_table = calloc(count, sizeof(uint32_t)); for (i = 0; i < count; i++) { header->file_table[i] = READ_UINT32(p); p += 4; } return true; } static bool unshield_header_get_components(Header* header)/*{{{*/ { int count = 0; int i; int available = 16; header->components = malloc(available * sizeof(UnshieldComponent*)); for (i = 0; i < MAX_COMPONENT_COUNT; i++) { if (header->cab.component_offsets[i]) { OffsetList list; list.next_offset = header->cab.component_offsets[i]; while (list.next_offset) { uint8_t* p = unshield_header_get_buffer(header, list.next_offset); list.name_offset = READ_UINT32(p); p += 4; list.descriptor_offset = READ_UINT32(p); p += 4; list.next_offset = READ_UINT32(p); p += 4; if (count == available) { available <<= 1; header->components = realloc(header->components, available * sizeof(UnshieldComponent*)); } header->components[count++] = unshield_component_new(header, list.descriptor_offset); } } } header->component_count = count; return true; } /*}}}*/ static bool unshield_header_get_file_groups(Header* header)/*{{{*/ { int count = 0; int i; int available = 16; header->file_groups = malloc(available * sizeof(UnshieldFileGroup*)); for (i = 0; i < MAX_FILE_GROUP_COUNT; i++) { if (header->cab.file_group_offsets[i]) { OffsetList list; list.next_offset = header->cab.file_group_offsets[i]; while (list.next_offset) { uint8_t* p = unshield_header_get_buffer(header, list.next_offset); list.name_offset = READ_UINT32(p); p += 4; list.descriptor_offset = READ_UINT32(p); p += 4; list.next_offset = READ_UINT32(p); p += 4; if (count == available) { available <<= 1; header->file_groups = realloc(header->file_groups, available * sizeof(UnshieldFileGroup*)); } header->file_groups[count++] = unshield_file_group_new(header, list.descriptor_offset); } } } header->file_group_count = count; return true; } /*}}}*/ /** Read all header files */ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ { int i; bool iterate = true; Header* previous = NULL; if (unshield->header_list) { unshield_warning("Already have a header list"); return true; } for (i = 1; iterate; i++) { FILE* file = unshield_fopen_for_reading(unshield, i, HEADER_SUFFIX); if (file) { unshield_trace("Reading header from .hdr file %i.", i); iterate = false; } else { unshield_trace("Could not open .hdr file %i. Reading header from .cab file %i instead.", i, i); file = unshield_fopen_for_reading(unshield, i, CABINET_SUFFIX); } if (file) { size_t bytes_read; Header* header = NEW1(Header); header->index = i; header->size = FSIZE(unshield, file); if (header->size < 4) { unshield_error("Header file %i too small", i); FCLOSE(unshield, file); goto error; } header->data = malloc(header->size); if (!header->data) { unshield_error("Failed to allocate memory for header file %i", i); FCLOSE(unshield, file); goto error; } bytes_read = unshield_fread(unshield, header->data, 1, header->size, file); FCLOSE(unshield, file); if (bytes_read != header->size) { unshield_error("Failed to read from header file %i. Expected = %i, read = %i", i, header->size, bytes_read); goto error; } if (!unshield_get_common_header(header)) { unshield_error("Failed to read common header from header file %i", i); goto error; } if (version != -1) { header->major_version = version; } else if (header->common.version >> 24 == 1) { header->major_version = (header->common.version >> 12) & 0xf; } else if (header->common.version >> 24 == 2 || header->common.version >> 24 == 4) { header->major_version = (header->common.version & 0xffff); if (header->major_version != 0) header->major_version = header->major_version / 100; } #if 0 if (header->major_version < 5) header->major_version = 5; #endif unshield_trace("Version 0x%08x handled as major version %i", header->common.version, header->major_version); if (!unshield_get_cab_descriptor(header)) { unshield_error("Failed to read CAB descriptor from header file %i", i); goto error; } if (!unshield_get_file_table(header)) { unshield_error("Failed to read file table from header file %i", i); goto error; } if (!unshield_header_get_components(header)) { unshield_error("Failed to read components from header file %i", i); goto error; } if (!unshield_header_get_file_groups(header)) { unshield_error("Failed to read file groups from header file %i", i); goto error; } if (previous) previous->next = header; else previous = unshield->header_list = header; continue; error: if (header) FREE(header->data); FREE(header); iterate = false; } else iterate = false; } return (unshield->header_list != NULL); }/*}}}*/ Unshield* unshield_open(const char* filename)/*{{{*/ { return unshield_open_force_version(filename, -1); }/*}}}*/ Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/ { return unshield_open2_force_version(filename, version, NULL, NULL); }/*}}}*/ Unshield* unshield_open2(const char* filename, const UnshieldIoCallbacks* callbacks, void* userdata)/*{{{*/ { return unshield_open2_force_version(filename, -1, callbacks, userdata); }/*}}}*/ Unshield* unshield_open2_force_version(const char* filename, int version, const UnshieldIoCallbacks* callbacks, void* userdata)/*{{{*/ { Unshield* unshield = NEW1(Unshield); if (!unshield) { unshield_error("Failed to allocate memory for Unshield structure"); goto error; } unshield->io_callbacks = callbacks == NULL ? &unshield_default_io_callbacks : callbacks; unshield->io_userdata = userdata; if (!unshield_create_filename_pattern(unshield, filename)) { unshield_error("Failed to create filename pattern"); goto error; } if (!unshield_read_headers(unshield, version)) { unshield_error("Failed to read header files"); goto error; } return unshield; error: unshield_close(unshield); return NULL; }/*}}}*/ static void unshield_free_string_buffers(Header* header) { StringBuffer* current = header->string_buffer; header->string_buffer = NULL; while (current != NULL) { StringBuffer* next = current->next; FREE(current->string); FREE(current); current = next; } } void unshield_close(Unshield* unshield)/*{{{*/ { if (unshield) { Header* header; for(header = unshield->header_list; header; ) { Header* next = header->next; int i; unshield_free_string_buffers(header); if (header->components) { for (i = 0; i < header->component_count; i++) unshield_component_destroy(header->components[i]); free(header->components); } if (header->file_groups) { for (i = 0; i < header->file_group_count; i++) unshield_file_group_destroy(header->file_groups[i]); free(header->file_groups); } if (header->file_descriptors) { for (i = 0; i < (int)header->cab.file_count; i++) FREE(header->file_descriptors[i]); free(header->file_descriptors); } FREE(header->file_table); FREE(header->data); FREE(header); header = next; } FREE(unshield->filename_pattern); free(unshield); } }/*}}}*/ bool unshield_is_unicode(Unshield* unshield) { if (unshield) { Header* header = unshield->header_list; return header->major_version >= 17; } else return false; } twogood-unshield-51de441/lib/libunshield.h000066400000000000000000000075201477345220400205730ustar00rootroot00000000000000#ifndef __unshield_h__ #define __unshield_h__ #include #include #define UNSHIELD_LOG_LEVEL_LOWEST 0 #define UNSHIELD_LOG_LEVEL_ERROR 1 #define UNSHIELD_LOG_LEVEL_WARNING 2 #define UNSHIELD_LOG_LEVEL_TRACE 3 #define UNSHIELD_LOG_LEVEL_HIGHEST 4 #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) && defined(UNSHIELD_DYNAMIC_LIBRARY) # if defined(UNSHIELD_EXPORTS) # define UNSHIELD_API __declspec(dllexport) # else # define UNSHIELD_API __declspec(dllimport) # endif #else # define UNSHIELD_API #endif typedef struct _Unshield Unshield; /* Logging */ UNSHIELD_API void unshield_set_log_level(int level); /* Open/close functions */ UNSHIELD_API Unshield* unshield_open(const char* filename); UNSHIELD_API Unshield* unshield_open_force_version(const char* filename, int version); UNSHIELD_API void unshield_close(Unshield* unshield); typedef struct { void *(*fopen)(const char *filename, const char *modes, void *userdata); int (*fseek)(void *file, long int offset, int whence, void *userdata); long int (*ftell)(void *file, void *userdata); size_t (*fread)(void *ptr, size_t size, size_t n, void *file, void *userdata); size_t (*fwrite)(const void *ptr, size_t size, size_t n, void *file, void *userdata); int (*fclose)(void *file, void *userdata); void *(*opendir)(const char *name, void *userdata); int (*closedir)(void *dir, void *userdata); struct dirent* (*readdir)(void *dir, void *userdata); } UnshieldIoCallbacks; UNSHIELD_API Unshield* unshield_open2(const char* filename, const UnshieldIoCallbacks* callbacks, void* userdata); UNSHIELD_API Unshield* unshield_open2_force_version(const char* filename, int version, const UnshieldIoCallbacks* callbacks, void* userdata); /* Component functions */ typedef struct { const char* name; unsigned file_group_count; const char** file_group_names; } UnshieldComponent; UNSHIELD_API int unshield_component_count (Unshield* unshield); UNSHIELD_API const char* unshield_component_name (Unshield* unshield, int index); /* File group functions */ typedef struct { const char* name; unsigned first_file; unsigned last_file; } UnshieldFileGroup; UNSHIELD_API int unshield_file_group_count (Unshield* unshield); UNSHIELD_API UnshieldFileGroup* unshield_file_group_get (Unshield* unshield, int index); UNSHIELD_API UnshieldFileGroup* unshield_file_group_find (Unshield* unshield, const char* name); UNSHIELD_API const char* unshield_file_group_name (Unshield* unshield, int index); /* Directory functions */ UNSHIELD_API int unshield_directory_count (Unshield* unshield); UNSHIELD_API const char* unshield_directory_name (Unshield* unshield, int index); /* File functions */ UNSHIELD_API int unshield_file_count (Unshield* unshield); UNSHIELD_API const char* unshield_file_name (Unshield* unshield, int index); UNSHIELD_API bool unshield_file_is_valid (Unshield* unshield, int index); UNSHIELD_API bool unshield_file_save (Unshield* unshield, int index, const char* filename); UNSHIELD_API int unshield_file_directory (Unshield* unshield, int index); UNSHIELD_API size_t unshield_file_size (Unshield* unshield, int index); /** For investigation of compressed data */ UNSHIELD_API bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename); /** Maybe it's just gzip without size? */ UNSHIELD_API bool unshield_file_save_old(Unshield* unshield, int index, const char* filename); /** Deobfuscate a buffer. Seed is 0 at file start */ UNSHIELD_API void unshield_deobfuscate(unsigned char* buffer, size_t size, unsigned* seed); /** Is the archive Unicode-capable? */ UNSHIELD_API bool unshield_is_unicode(Unshield* unshield); #ifdef __cplusplus } #endif #endif twogood-unshield-51de441/lib/log.c000066400000000000000000000007561477345220400170510ustar00rootroot00000000000000#include "log.h" #include #include /* evil static data */ static int current_log_level = UNSHIELD_LOG_LEVEL_HIGHEST; void unshield_set_log_level(int level) { current_log_level = level; } void _unshield_log(int level, const char* file, int line, const char* format, ...) { va_list ap; if (level > current_log_level) return; fprintf(stderr, "[%s:%i] ", file, line); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); } twogood-unshield-51de441/lib/log.h000066400000000000000000000014431477345220400170500ustar00rootroot00000000000000#ifndef __log_h__ #define __log_h__ #include "internal.h" #define UNSHIELD_LOG_LEVEL_LOWEST 0 #define UNSHIELD_LOG_LEVEL_ERROR 1 #define UNSHIELD_LOG_LEVEL_WARNING 2 #define UNSHIELD_LOG_LEVEL_TRACE 3 #define UNSHIELD_LOG_LEVEL_HIGHEST 4 #ifdef __cplusplus extern "C" { #endif void _unshield_log(int level, const char* file, int line, const char* format, ...); #define unshield_trace(format, ...) \ _unshield_log(UNSHIELD_LOG_LEVEL_TRACE,__FUNCTION__, __LINE__, format, ##__VA_ARGS__) #define unshield_warning(format, ...) \ _unshield_log(UNSHIELD_LOG_LEVEL_WARNING,__FUNCTION__, __LINE__, format, ##__VA_ARGS__) #define unshield_error(format, ...) \ _unshield_log(UNSHIELD_LOG_LEVEL_ERROR,__FUNCTION__, __LINE__, format, ##__VA_ARGS__) #ifdef __cplusplus } #endif #endif twogood-unshield-51de441/lib/md5/000077500000000000000000000000001477345220400166015ustar00rootroot00000000000000twogood-unshield-51de441/lib/md5/CMakeLists.txt000066400000000000000000000003131477345220400213360ustar00rootroot00000000000000set(LIBMD5_HEADERS global.h md5.h ) set(LIBMD5_SOURCES md5c.c ) if(BUILD_STATIC AND MSVC) set_msvc_runtime_static() endif() add_library(md5 OBJECT ${LIBMD5_HEADERS} ${LIBMD5_SOURCES}) twogood-unshield-51de441/lib/md5/global.h000066400000000000000000000014101477345220400202060ustar00rootroot00000000000000/* GLOBAL.H - RSAREF types and constants */ /* PROTOTYPES should be set to one if and only if the compiler supports function argument prototyping. The following makes PROTOTYPES default to 0 if it has not already been defined with C compiler flags. */ #ifndef PROTOTYPES #define PROTOTYPES 0 #endif /* POINTER defines a generic pointer type */ typedef unsigned char *POINTER; /* UINT2 defines a two byte word */ typedef unsigned short int UINT2; /* UINT4 defines a four byte word */ typedef unsigned int UINT4; /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it returns an empty list. */ #if PROTOTYPES #define PROTO_LIST(list) list #else #define PROTO_LIST(list) () #endif twogood-unshield-51de441/lib/md5/md5.h000066400000000000000000000025061477345220400174420ustar00rootroot00000000000000/* MD5.H - header file for MD5C.C */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* MD5 context. */ typedef struct { UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init PROTO_LIST ((MD5_CTX *)); void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); twogood-unshield-51de441/lib/md5/md5c.c000066400000000000000000000243071477345220400176030ustar00rootroot00000000000000/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ #include "global.h" #include "md5.h" /* Constants for MD5Transform routine. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); static void Encode PROTO_LIST ((unsigned char *, UINT4 *, unsigned int)); static void Decode PROTO_LIST ((UINT4 *, unsigned char *, unsigned int)); static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) ((((x) << (n)) & 0xffffffffU) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* MD5 initialization. Begins an MD5 operation, writing a new context. */ void MD5Init (context) MD5_CTX *context; /* context */ { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. */ void MD5Update (context, input, inputLen) MD5_CTX *context; /* context */ unsigned char *input; /* input block */ unsigned int inputLen; /* length of input block */ { unsigned int i, index, partLen; /* Compute number of bytes mod 64 */ index = (unsigned int)((context->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) context->count[1]++; context->count[1] += ((UINT4)inputLen >> 29); partLen = 64 - index; /* Transform as many times as possible. */ if (inputLen >= partLen) { MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); } /* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context. */ void MD5Final (digest, context) unsigned char digest[16]; /* message digest */ MD5_CTX *context; /* context */ { unsigned char bits[8]; unsigned int index, padLen; /* Save number of bits */ Encode (bits, context->count, 8); /* Pad out to 56 mod 64. */ index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update (context, PADDING, padLen); /* Append length (before padding) */ MD5Update (context, bits, 8); /* Store state in digest */ Encode (digest, context->state, 16); /* Zeroize sensitive information. */ MD5_memset ((POINTER)context, 0, sizeof (*context)); } /* MD5 basic transformation. Transforms state based on block. */ static void MD5Transform (state, block) UINT4 state[4]; unsigned char block[64]; { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ MD5_memset ((POINTER)x, 0, sizeof (x)); } /* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ static void Encode (output, input, len) unsigned char *output; UINT4 *input; unsigned int len; { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } } /* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ static void Decode (output, input, len) UINT4 *output; unsigned char *input; unsigned int len; { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); } /* Note: Replace "for loop" with standard memcpy if possible. */ static void MD5_memcpy (output, input, len) POINTER output; POINTER input; unsigned int len; { unsigned int i; for (i = 0; i < len; i++) output[i] = input[i]; } /* Note: Replace "for loop" with standard memset if possible. */ static void MD5_memset (output, value, len) POINTER output; int value; unsigned int len; { unsigned int i; for (i = 0; i < len; i++) ((char *)output)[i] = (char)value; } twogood-unshield-51de441/lib/unshield_config.h.in000066400000000000000000000041301477345220400220300ustar00rootroot00000000000000/* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDBOOL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Name of package */ #define PACKAGE "@PROJECT_NAME@" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "@PROJECT_NAME@" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "@PROJECT_NAME@ @PROJECT_VERSION@" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "@PROJECT_NAME@" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "@PROJECT_VERSION@" /* printf format that works with size_t values */ #cmakedefine SIZE_FORMAT "@SIZE_FORMAT@" /* 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 /* Version number of package */ #define VERSION "@PROJECT_VERSION@" /* Enable GNU style printf formatters */ #define __USE_MINGW_ANSI_STDIO 1 twogood-unshield-51de441/libunshield.pc.in000066400000000000000000000004771477345220400206110ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_FULL_BINDIR@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: libunshield Description: Library to extract CAB files from InstallShield installers Version: @PROJECT_VERSION@ Libs: -L${libdir} -lunshield Cflags: -I${includedir} twogood-unshield-51de441/man/000077500000000000000000000000001477345220400161215ustar00rootroot00000000000000twogood-unshield-51de441/man/unshield.1000066400000000000000000000035251477345220400200230ustar00rootroot00000000000000.TH UNSHIELD "1" "November 2023" "The Unshield project" "https://github.com/twogood/unshield" .SH NAME unshield \- extract CAB files from an InstallShield installer archive .SH SYNOPSIS unshield [\-c COMPONENT] [\-d DIRECTORY] [\-D LEVEL] [\-g GROUP] [\-h] [\-i VERSION] [\-e ENCODING] [\-j] [\-L] [\-O] [\-r] [\-R] [\-V] c|g|l|t|x CABFILE [FILENAME...] .SH DESCRIPTION Unshield extracts CAB files from InstallShield installers, used to install software on Microsoft Windows based machines. .SH OPTIONS .TP \fB\-c\fR COMPONENT Only list/extract this component .TP \fB\-d\fR DIRECTORY Extract files to DIRECTORY .TP \fB\-D\fR LEVEL Set debug log level 0 \- No logging (default) 1 \- Errors only 2 \- Errors and warnings 3 \- Errors, warnings and debug messages .TP \fB\-g\fR GROUP Only list/extract this file group .TP \fB\-h\fR Show help message .TP \fB\-i\fR VERSION Force InstallShield version number (don't autodetect) .br Use this option if you have a very old archive (generated with InstallShield <= 4) and / or the automatic detection fails .TP \fB\-e\fR ENCODING Convert filename character encoding to local codepage from ENCODING (implicitly sets -R) .TP \fB\-j\fR Junk paths (do not make directories) .TP \fB\-L\fR Make file and directory names lowercase .TP \fB\-O\fR Use old compression .TP \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, \fB\-\-version\fR Print version information .SS "Commands:" .TP c List components .TP g List file groups .TP l List files .TP t Test files .TP x Extract files .SS Other: .TP CABFILE The file to list or extract contents of .TP FILENAME Optionally specify names of specific files to extract (wildcards are supported) .SH AUTHOR This manual page was adapted by Mark Ellis , from the skeleton generated by help2man twogood-unshield-51de441/rebuild.sh000077500000000000000000000002641477345220400173350ustar00rootroot00000000000000#!/bin/sh set -e set -x export CFLAGS="-Wall -Werror -ggdb3" cd `dirname $0` mkdir -p build cd build cmake -DCMAKE_INSTALL_PREFIX:PATH=/var/tmp/unshield .. && make && make install twogood-unshield-51de441/src/000077500000000000000000000000001477345220400161355ustar00rootroot00000000000000twogood-unshield-51de441/src/CMakeLists.txt000066400000000000000000000015751477345220400207050ustar00rootroot00000000000000list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif() add_executable(unshield unshield.c) target_link_libraries(unshield libunshield) if(WIN32) if(HAVE_ICONV) target_link_libraries(unshield iconv) endif() target_link_libraries(unshield ${ZLIB_LIBRARIES}) endif() add_executable(unshield-deobfuscate unshield-deobfuscate.c) target_link_libraries(unshield-deobfuscate libunshield) if(WIN32) target_link_libraries(unshield-deobfuscate ${ZLIB_LIBRARIES}) endif() if(BUILD_STATIC AND MSVC) set_msvc_runtime_static() endif() install(TARGETS unshield EXPORT unshieldConfig RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) twogood-unshield-51de441/src/unshield-deobfuscate.c000066400000000000000000000017721477345220400224050ustar00rootroot00000000000000#include #include #include "../lib/libunshield.h" int main(int argc, char** argv) { unsigned seed = 0; FILE* input = NULL; FILE* output = NULL; size_t size; unsigned char buffer[16384]; if (argc != 3) { fprintf(stderr, "Syntax:\n" " %s INPUT-FILE OUTPUT-FILE\n", argv[0]); exit(1); } input = fopen(argv[1], "rb"); if (!input) { fprintf(stderr, "Failed to open %s for reading\n", argv[1]); exit(2); } output = fopen(argv[2], "wb"); if (!output) { fprintf(stderr, "Failed to open %s for writing\n", argv[2]); exit(3); } while ((size = fread(buffer, 1, sizeof(buffer), input)) != 0) { unshield_deobfuscate(buffer, size, &seed); if (fwrite(buffer, 1, size, output) != size) { fprintf(stderr, "Failed to write %lu bytes to %s\n", (unsigned long)size, argv[2]); exit(4); } } fclose(input); fclose(output); return 0; } twogood-unshield-51de441/src/unshield.c000066400000000000000000000447021477345220400201230ustar00rootroot00000000000000#ifdef __linux__ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #define _POSIX_C_SOURCE 2 #endif #include #include #include #include #include #include #include #include #include #include "../lib/libunshield.h" #ifdef HAVE_CONFIG_H #include "lib/unshield_config.h" #endif #if HAVE_FNMATCH #include #endif #ifdef HAVE_ICONV #include #include #endif #ifndef VERSION #define VERSION "Unknown" #endif #define FREE(ptr) { if (ptr) { free(ptr); ptr = NULL; } } #ifdef _WIN32 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #ifndef PATH_MAX #define PATH_MAX _MAX_PATH #endif #else #include #endif #ifndef NAME_MAX #define NAME_MAX FILENAME_MAX #endif typedef enum { ACTION_EXTRACT, ACTION_LIST_COMPONENTS, ACTION_LIST_FILE_GROUPS, ACTION_LIST_FILES, ACTION_TEST } ACTION; typedef enum { FORMAT_NEW, FORMAT_OLD, FORMAT_RAW } FORMAT; #define DEFAULT_OUTPUT_DIRECTORY "." static const char* output_directory = DEFAULT_OUTPUT_DIRECTORY; 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 ACTION action = ACTION_EXTRACT; static int log_level = UNSHIELD_LOG_LEVEL_LOWEST; static int exit_status = 0; static FORMAT format = FORMAT_NEW; 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)/*{{{*/ { struct stat dir_stat; const char* p = directory; bool success = false; char* current = NULL; while (p && *p) { if ('/' == *p) p++; else if (0 == strncmp(p, "./", 2)) p+=2; else if (0 == strncmp(p, "../", 3)) p+=3; #ifdef WIN32 if ('\\' == *p) p++; else if (0 == strncmp(p, ".\\", 2)) p += 2; else if (0 == strncmp(p, "..\\", 3)) p += 3; #endif else { int is_win_root = 0; const char* slash = strchr(p, '/'); #ifdef WIN32 const char* backslash = strchr(p, '\\'); if (NULL != backslash && (NULL == slash || backslash < slash)) slash = backslash; #endif current = strdup(directory); if (slash) current[slash-directory] = '\0'; #ifdef WIN32 if (slash - directory == 2 && current[1] == ':') is_win_root = 1; #endif if (!is_win_root && stat(current, &dir_stat) < 0) { #if defined (__MINGW32__) || defined (_WIN32) if (_mkdir(current) < 0) #else if (mkdir(current, 0700) < 0) #endif { fprintf(stderr, "Failed to create directory %s\n", directory); if(strlen(directory)>NAME_MAX) fprintf(stderr, "Directory name must be less than %i characters\n", NAME_MAX+1); goto exit; } } p = slash; FREE(current); } } success = true; exit: FREE(current); 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] [-h] [-i VERSION] [-e ENCODING] [-j] [-L] [-O] [-r] [-R] [-V] c|g|l|t|x CABFILE [FILENAME...]\n" "\n" "Options:\n" "\t-c COMPONENT Only list/extract this component\n" "\t-d DIRECTORY Extract files to DIRECTORY\n" "\t-D LEVEL Set debug log level\n" "\t 0 - No logging (default)\n" "\t 1 - Errors only\n" "\t 2 - Errors and warnings\n" "\t 3 - Errors, warnings and debug messages\n" "\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 --version Print copyright and version information\n" "\n" "Commands:\n" "\tc List components\n" "\tg List file groups\n" "\tl List files\n" "\tt Test files\n" "\tx Extract files\n" "\n" "Other:\n" "\tCABFILE The file to list or extract contents of\n" "\tFILENAME... Optionally specify names of specific files to extract" #if HAVE_FNMATCH " (wildcards are supported)" #endif "\n" , name); } static bool handle_parameters( int argc, char* const argv[]) { int c; static struct option long_options[] = { { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; while ((c = getopt_long(argc, argv, "c:d:D:g:hi:e:jLOrRV", long_options, NULL)) != -1) { switch (c) { case 'c': component_name = optarg; break; case 'd': output_directory = optarg; break; case 'D': log_level = atoi(optarg); break; case 'g': file_group_name = optarg; break; case 'i': 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; case 'L': make_lowercase = true; break; case 'R': raw_filename = true; break; case 'O': format = FORMAT_OLD; break; case 'r': format = FORMAT_RAW; break; case 'V': printf("Unshield version " VERSION ". MIT License. (C) 2003-2023 David Eriksson.\n"); exit(0); break; case 'h': default: show_usage(argv[0]); return false; } } unshield_set_log_level(log_level); if (optind == argc || !argv[optind]) { fprintf(stderr, "No action provided on command line.\n\n"); show_usage(argv[0]); return false; } char action_char = argv[optind++][0]; switch (action_char) { case 'c': action = ACTION_LIST_COMPONENTS; break; case 'g': action = ACTION_LIST_FILE_GROUPS; break; case 'l': action = ACTION_LIST_FILES; break; case 't': action = ACTION_TEST; break; case 'x': action = ACTION_EXTRACT; break; default: fprintf(stderr, "Unknown action '%c' on command line.\n\n", action_char); show_usage(argv[0]); return false; } cab_file_name = argv[optind++]; if (cab_file_name == NULL) { fprintf(stderr, "No InstallShield Cabinet File name provided on command line.\n\n"); show_usage(argv[0]); return false; } path_name_count = argc - optind; path_names = &argv[optind]; return true; } static bool extract_file(Unshield* unshield, const char* prefix, int index) { bool success; char* dirname; char* filename; 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(prefix, _PC_PATH_MAX); if (path_max <= 0) path_max = 4096; #endif real_output_directory = malloc(path_max); real_filename = malloc(path_max); dirname = malloc(path_max); filename = malloc(path_max); if (real_output_directory == NULL || real_filename == NULL) { fprintf(stderr,"Unable to allocate memory."); success=false; goto exit; } if(strlen(output_directory) < path_max-1) { strncpy(dirname, output_directory,path_max-1); if (path_max > 0) dirname[path_max - 1]= '\0'; strcat(dirname, "/"); } else { fprintf(stderr, "\nOutput directory exceeds maximum path length.\n"); success = false; goto exit; } if (prefix && prefix[0]) { if(strlen(dirname)+strlen(prefix) < path_max-1) { strcat(dirname, prefix); strcat(dirname, "/"); } else { fprintf(stderr, "\nOutput directory exceeds maximum path length.\n"); success = false; goto exit; } } if (!junk_paths && directory >= 0) { const char* tmp = unshield_directory_name(unshield, directory); if (tmp && tmp[0]) { if(strlen(dirname)+strlen(tmp) < path_max-1) { strcat(dirname, tmp); strcat(dirname, "/"); } else { fprintf(stderr, "\nOutput directory exceeds maximum path length.\n"); success = false; goto exit; } } } for (p = dirname + strlen(output_directory); *p != '\0'; p++) { switch (*p) { case '\\': *p = '/'; break; case ' ': case '<': case '>': case '[': case ']': *p = '_'; break; default: if (!raw_filename) { if (!isprint(*p)) *p = '_'; else if (make_lowercase) *p = tolower(*p); } break;; } } #ifdef HAVE_ICONV if (!convert_encoding(dirname, path_max)) { success = false; goto exit; } #endif #if 0 if (dirname[strlen(dirname)-1] != '/') strcat(dirname, "/"); #endif make_sure_directory_exists(dirname); snprintf(filename, path_max, "%s%s", dirname, unshield_file_name(unshield, index)); for (p = filename + strlen(dirname); *p != '\0'; p++) { if (!raw_filename) { if (!isprint(*p)) *p = '_'; else if (make_lowercase) *p = tolower(*p); } } #ifdef HAVE_ICONV if (!convert_encoding(filename + strlen(dirname), path_max - strlen(dirname))) { success = false; goto exit; } #endif #ifdef __GLIBC__ /* 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, "Error: %s (%d).\n", strerror(errno), errno); fprintf(stderr, "Possible directory traversal attack for: %s\n", filename); fprintf(stderr, "To be placed at: %s\n\n", real_filename); success = false; goto exit; } #endif printf(" extracting: %s\n", filename); switch (format) { case FORMAT_NEW: success = unshield_file_save(unshield, index, filename); break; case FORMAT_OLD: success = unshield_file_save_old(unshield, index, filename); break; case FORMAT_RAW: success = unshield_file_save_raw(unshield, index, filename); break; } exit: if (!success) { fprintf(stderr, "Failed to extract file '%s'.%s\n", unshield_file_name(unshield, index), (log_level < 3) ? "Run unshield again with -D 3 for more information." : ""); unlink(filename); exit_status = 1; } free(real_filename); free(real_output_directory); free(dirname); free(filename); return success; } static bool should_process_file(Unshield* unshield, int index) { int i; if (path_name_count == 0) return true; for (i = 0; i < path_name_count; i++) { #if HAVE_FNMATCH if (fnmatch(path_names[i], unshield_file_name(unshield, index), 0) == 0) return true; #else if (strcmp(path_names[i], unshield_file_name(unshield, index)) == 0) return true; #endif } return false; } static int extract_helper(Unshield* unshield, const char* prefix, int first, int last)/*{{{*/ { int i; int count = 0; for (i = first; i <= last; i++) { if (unshield_file_is_valid(unshield, i) && should_process_file(unshield, i) && extract_file(unshield, prefix, i)) count++; } return count; }/*}}}*/ static bool test_file(Unshield* unshield, int index) { bool success; printf(" testing: %s\n", unshield_file_name(unshield, index)); switch (format) { case FORMAT_NEW: success = unshield_file_save(unshield, index, NULL); break; case FORMAT_OLD: success = unshield_file_save_old(unshield, index, NULL); break; case FORMAT_RAW: success = unshield_file_save_raw(unshield, index, NULL); break; } if (!success) { fprintf(stderr, "Failed to extract file '%s'.%s\n", unshield_file_name(unshield, index), (log_level < 3) ? "Run unshield again with -D 3 for more information." : ""); exit_status = 1; } return success; } static int test_helper(Unshield* unshield, const char* prefix, int first, int last)/*{{{*/ { int i; int count = 0; for (i = first; i <= last; i++) { if (unshield_file_is_valid(unshield, i) && test_file(unshield, i)) count++; } return count; }/*}}}*/ static bool list_components(Unshield* unshield) { int i; int count = unshield_component_count(unshield); if (count < 0) return false; for (i = 0; i < count; i++) { printf("%s\n", unshield_component_name(unshield, i)); } printf("-------\n%i components\n", count); return true; } static bool list_file_groups(Unshield* unshield) { int i; int count = unshield_file_group_count(unshield); if (count < 0) return false; for (i = 0; i < count; i++) { printf("%s\n", unshield_file_group_name(unshield, i)); } printf("-------\n%i file groups\n", count); return true; } static int list_files_helper(Unshield* unshield, const char* prefix, int first, int last)/*{{{*/ { int i; int valid_count = 0; for (i = first; i <= last; i++) { char dirname[4096]; if (unshield_file_is_valid(unshield, i) && should_process_file(unshield, i)) { valid_count++; if (prefix && prefix[0]) { strcpy(dirname, prefix); strcat(dirname, "\\"); } else dirname[0] = '\0'; strcat(dirname, unshield_directory_name(unshield, unshield_file_directory(unshield, i))); #if 0 for (p = dirname + strlen(output_directory); *p != '\0'; p++) if ('\\' == *p) *p = '/'; #endif if (dirname[strlen(dirname)-1] != '\\') strcat(dirname, "\\"); printf(" %8" SIZE_FORMAT " %s%s\n", unshield_file_size(unshield, i), dirname, unshield_file_name(unshield, i)); } } return valid_count; }/*}}}*/ typedef int (*ActionHelper)(Unshield* unshield, const char* prefix, int first, int last); static bool do_action(Unshield* unshield, ActionHelper helper) { int count = 0; if (component_name) { fprintf(stderr, "This action is not implemented for components, sorry! Patch welcome!\n"); return false; } else if (file_group_name) { UnshieldFileGroup* file_group = unshield_file_group_find(unshield, file_group_name); printf("File group: %s\n", file_group_name); if (file_group) count = helper(unshield, file_group_name, file_group->first_file, file_group->last_file); } else { int i; for (i = 0; i < unshield_file_group_count(unshield); i++) { UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i); if (file_group) count += helper(unshield, file_group->name, file_group->first_file, file_group->last_file); } } printf(" -------- -------\n %i files\n", count); return true; } int main(int argc, char* const argv[]) { bool success = false; Unshield* unshield = NULL; setlocale(LC_ALL, ""); if (!handle_parameters(argc, argv)) goto exit; unshield = unshield_open_force_version(cab_file_name, is_version); if (!unshield) { fprintf(stderr, "Failed to open %s as an InstallShield Cabinet File\n", cab_file_name); 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) { case ACTION_EXTRACT: success = do_action(unshield, extract_helper); break; case ACTION_LIST_COMPONENTS: success = list_components(unshield); break; case ACTION_LIST_FILE_GROUPS: success = list_file_groups(unshield); break; case ACTION_LIST_FILES: success = do_action(unshield, list_files_helper); break; case ACTION_TEST: if (strcmp(output_directory, DEFAULT_OUTPUT_DIRECTORY) != 0) fprintf(stderr, "Output directory (-d) option has no effect with test (t) command.\n"); if (make_lowercase) fprintf(stderr, "Make lowercase (-L) option has no effect with test (t) command.\n"); success = do_action(unshield, test_helper); break; } 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; } twogood-unshield-51de441/valgrind.sh000077500000000000000000000001511477345220400175100ustar00rootroot00000000000000#!/bin/sh libtool --mode=execute valgrind --num-callers=10 --leak-check=yes `dirname $0`/src/unshield $@ twogood-unshield-51de441/win32_msvc/000077500000000000000000000000001477345220400173405ustar00rootroot00000000000000twogood-unshield-51de441/win32_msvc/dirent.h000066400000000000000000000531541477345220400210060ustar00rootroot00000000000000/* * Dirent interface for Microsoft Visual Studio * Version 1.21 * * Copyright (C) 2006-2012 Toni Ronkko * This file is part of dirent. Dirent may be freely distributed * under the MIT license. For all details and documentation, see * https://github.com/tronkko/dirent */ #ifndef DIRENT_H #define DIRENT_H /* * Include windows.h without Windows Sockets 1.1 to prevent conflicts with * Windows Sockets 2.0. */ #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include #include #include /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat(), general mask */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT #endif /* Directory bit */ #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR #endif /* Character device bit */ #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR #endif /* Pipe bit */ #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO #endif /* Regular file bit */ #if !defined(S_IFREG) # define S_IFREG _S_IFREG #endif /* Read permission */ #if !defined(S_IREAD) # define S_IREAD _S_IREAD #endif /* Write permission */ #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE #endif /* Execute permission */ #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC #endif /* Pipe */ #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO #endif /* Block device */ #if !defined(S_IFBLK) # define S_IFBLK 0 #endif /* Link */ #if !defined(S_IFLNK) # define S_IFLNK 0 #endif /* Socket */ #if !defined(S_IFSOCK) # define S_IFSOCK 0 #endif /* Read user permission */ #if !defined(S_IRUSR) # define S_IRUSR S_IREAD #endif /* Write user permission */ #if !defined(S_IWUSR) # define S_IWUSR S_IWRITE #endif /* Execute user permission */ #if !defined(S_IXUSR) # define S_IXUSR 0 #endif /* Read group permission */ #if !defined(S_IRGRP) # define S_IRGRP 0 #endif /* Write group permission */ #if !defined(S_IWGRP) # define S_IWGRP 0 #endif /* Execute group permission */ #if !defined(S_IXGRP) # define S_IXGRP 0 #endif /* Read others permission */ #if !defined(S_IROTH) # define S_IROTH 0 #endif /* Write others permission */ #if !defined(S_IWOTH) # define S_IWOTH 0 #endif /* Execute others permission */ #if !defined(S_IXOTH) # define S_IXOTH 0 #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK #define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #if !defined(S_ISFIFO) # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #endif #if !defined(S_ISDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #if !defined(S_ISREG) # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #if !defined(S_ISLNK) # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #if !defined(S_ISSOCK) # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif #if !defined(S_ISCHR) # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #endif #if !defined(S_ISBLK) # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #endif /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { /* Always zero */ long d_ino; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ wchar_t d_name[PATH_MAX]; }; typedef struct _wdirent _wdirent; struct _WDIR { /* Current directory entry */ struct _wdirent ent; /* Private file data */ WIN32_FIND_DATAW data; /* True if data is valid */ int cached; /* Win32 search handle */ HANDLE handle; /* Initial directory name */ wchar_t *patt; }; typedef struct _WDIR _WDIR; static _WDIR *_wopendir (const wchar_t *dirname); static struct _wdirent *_wreaddir (_WDIR *dirp); static int _wclosedir (_WDIR *dirp); static void _wrewinddir (_WDIR* dirp); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Multi-byte character versions */ struct dirent { /* Always zero */ long d_ino; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ char d_name[PATH_MAX]; }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; static DIR *opendir (const char *dirname); static struct dirent *readdir (DIR *dirp); static int closedir (DIR *dirp); static void rewinddir (DIR* dirp); /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno (int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume its an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) n = wcslen(dirname); # else n = GetFullPathNameW (dirname, 0, NULL, NULL); # endif /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume its an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) wcsncpy_s(dirp->patt, n+1, dirname, n); # else n = GetFullPathNameW (dirname, n, dirp->patt, NULL); # endif if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/; break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first (dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno (ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno (ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. The directory entry is returned in dirent * structure in the d_name field. Individual directory entries returned by * this function include regular files, sub-directories, pseudo-directories * "." and ".." as well as volume labels, hidden files and system files. */ static struct _wdirent* _wreaddir( _WDIR *dirp) { WIN32_FIND_DATAW *datap; struct _wdirent *entp; /* Read next directory entry */ datap = dirent_next (dirp); if (datap) { size_t n; DWORD attr; /* Pointer to directory entry to return */ entp = &dirp->ent; /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } dirp->ent.d_name[n] = 0; /* Length of file name excluding zero terminator */ entp->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct _wdirent); } else { /* Last directory entry read */ entp = NULL; } return entp; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir( _WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free (dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free (dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir( _WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } /* Open new search handle */ dirent_first (dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileExW( dirp->patt, FindExInfoStandard, &dirp->data, FindExSearchNameMatch, NULL, 0); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* Get next directory entry (internal) */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occured */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir( const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * When working with text consoles, please note that file names returned by * readdir() are represented in the default ANSI code page while any output to * console is typically formatted on another code page. Thus, non-ASCII * characters in file names will not usually display correctly on console. The * problem can be fixed in two ways: (1) change the character set of console * to 1252 using chcp utility and use Lucida Console font, or (2) use * _cprintf function when writing to console. The _cprinf() will re-encode * ANSI strings to the console code page so many non-ASCII characters will * display correcly. */ static struct dirent* readdir( DIR *dirp) { WIN32_FIND_DATAW *datap; struct dirent *entp; /* Read next directory entry */ datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cAlternateFileName, PATH_MAX); } if (!error) { DWORD attr; /* Initialize directory entry for return */ entp = &dirp->ent; /* Length of file name excluding zero terminator */ entp->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an errornous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entp = &dirp->ent; entp->d_name[0] = '?'; entp->d_name[1] = '\0'; entp->d_namlen = 1; entp->d_type = DT_UNKNOWN; entp->d_ino = 0; entp->d_reclen = 0; } } else { /* No more directory entries */ entp = NULL; } return entp; } /* * Close directory stream. */ static int closedir( DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir (dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free (dirp); } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir (dirp->wdirp); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to wide-character string (or count characters) */ n = mbstowcs (wcstr, mbstr, sizeInWords); if (!wcstr || n < count) { /* Zero-terminate output buffer */ if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } wcstr[n] = 0; } /* Length of resuting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Could not convert string */ error = 1; } #endif return error; } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, /* max size of mbstr */ const wchar_t *wcstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to multi-byte string (or count the number of bytes needed) */ n = wcstombs (mbstr, wcstr, sizeInBytes); if (!mbstr || n < count) { /* Zero-terminate output buffer */ if (mbstr && sizeInBytes) { if (n >= sizeInBytes) { n = sizeInBytes - 1; } mbstr[n] = '\0'; } /* Length of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Cannot convert string */ error = 1; } #endif return error; } /* Set errno variable */ static void dirent_set_errno( int error) { #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 and later */ _set_errno (error); #else /* Non-Microsoft compiler or older Microsoft compiler */ errno = error; #endif } #ifdef __cplusplus } #endif #endif /*DIRENT_H*/ twogood-unshield-51de441/win32_msvc/getopt.h000066400000000000000000000265431477345220400210250ustar00rootroot00000000000000/* -*- indent-tabs-mode: nil -*- * * ya_getopt - Yet another getopt * https://github.com/kubo/ya_getopt * * Copyright 2015 Kubo Takehiro * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of the authors. * */ #ifndef YA_GETOPT_H #define YA_GETOPT_H 1 #if defined(__cplusplus) extern "C" { #endif #include #include #include #include #define ya_no_argument 0 #define ya_required_argument 1 #define ya_optional_argument 2 struct option { const char *name; int has_arg; int *flag; int val; }; // error: ‘ya_getopt’ defined but not used [-Werror=unused-function] ///static int ya_getopt(int argc, char * const argv[], const char *optstring); static int ya_getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); ///static int ya_getopt_long_only(int argc, char * const argv[], const char *optstring, /// const struct option *longopts, int *longindex); #ifndef YA_GETOPT_NO_COMPAT_MACRO ///#define getopt ya_getopt #define getopt_long ya_getopt_long ///#define getopt_long_only ya_getopt_long_only #define optarg ya_optarg #define optind ya_optind #define opterr ya_opterr #define optopt ya_optopt #define no_argument ya_no_argument #define required_argument ya_required_argument #define optional_argument ya_optional_argument #endif // =============================================================================== char *ya_optarg = NULL; int ya_optind = 1; int ya_opterr = 1; int ya_optopt = '?'; static char *ya_optnext = NULL; static int posixly_correct = -1; static int handle_nonopt_argv = 0; static void ya_getopt_error(const char *optstring, const char *format, ...); static void check_gnu_extension(const char *optstring); static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only); static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only); static int ya_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct option *longopts, int *longindex, int *long_only_flag); static void ya_getopt_error(const char *optstring, const char *format, ...) { if (ya_opterr && optstring[0] != ':') { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } } static void check_gnu_extension(const char *optstring) { if (optstring[0] == '+' || getenv("POSIXLY_CORRECT") != NULL) { posixly_correct = 1; } else { posixly_correct = 0; } if (optstring[0] == '-') { handle_nonopt_argv = 1; } else { handle_nonopt_argv = 0; } } static int is_option(const char *arg) { return arg[0] == '-' && arg[1] != '\0'; } // ================================================================== ///static int ya_getopt(int argc, char * const argv[], const char *optstring) ///{ /// return ya_getopt_internal(argc, argv, optstring, NULL, NULL, 0); ///} static int ya_getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex) { return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 0); } ///static int ya_getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex) ///{ /// return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 1); ///} // ================================================================== static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only) { static int start, end; if (ya_optopt == '?') { ya_optopt = 0; } if (posixly_correct == -1) { check_gnu_extension(optstring); } if (ya_optind == 0) { check_gnu_extension(optstring); ya_optind = 1; ya_optnext = NULL; } switch (optstring[0]) { case '+': case '-': optstring++; } if (ya_optnext == NULL && start != 0) { int last_pos = ya_optind - 1; ya_optind -= end - start; if (ya_optind <= 0) { ya_optind = 1; } while (start < end--) { int i; char *arg = argv[end]; for (i = end; i < last_pos; i++) { ((char **)argv)[i] = argv[i + 1]; } ((char const **)argv)[i] = arg; last_pos--; } start = 0; } if (ya_optind >= argc) { ya_optarg = NULL; return -1; } if (ya_optnext == NULL) { const char *arg = argv[ya_optind]; if (!is_option(arg)) { if (handle_nonopt_argv) { ya_optarg = argv[ya_optind++]; start = 0; return 1; } else if (posixly_correct) { ya_optarg = NULL; return -1; } else { int i; start = ya_optind; for (i = ya_optind + 1; i < argc; i++) { if (is_option(argv[i])) { end = i; break; } } if (i == argc) { ya_optarg = NULL; return -1; } ya_optind = i; arg = argv[ya_optind]; } } if (strcmp(arg, "--") == 0) { ya_optind++; return -1; } if (longopts != NULL && arg[1] == '-') { return ya_getopt_longopts(argc, argv, argv[ya_optind] + 2, optstring, longopts, longindex, NULL); } } if (ya_optnext == NULL) { ya_optnext = argv[ya_optind] + 1; } if (long_only) { int long_only_flag = 0; int rv = ya_getopt_longopts(argc, argv, ya_optnext, optstring, longopts, longindex, &long_only_flag); if (!long_only_flag) { ya_optnext = NULL; return rv; } } return ya_getopt_shortopts(argc, argv, optstring, long_only); } static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only) { int opt = *ya_optnext; const char *os = strchr(optstring, opt); if (os == NULL) { ya_optarg = NULL; if (long_only) { ya_getopt_error(optstring, "%s: unrecognized option '-%s'\n", argv[0], ya_optnext); ya_optind++; ya_optnext = NULL; } else { ya_optopt = opt; ya_getopt_error(optstring, "%s: invalid option -- '%c'\n", argv[0], opt); if (*(++ya_optnext) == 0) { ya_optind++; ya_optnext = NULL; } } return '?'; } if (os[1] == ':') { if (ya_optnext[1] == 0) { ya_optind++; ya_optnext = NULL; if (os[2] == ':') { /* optional argument */ ya_optarg = NULL; } else { if (ya_optind == argc) { ya_optarg = NULL; ya_optopt = opt; ya_getopt_error(optstring, "%s: option requires an argument -- '%c'\n", argv[0], opt); if (optstring[0] == ':') { return ':'; } else { return '?'; } } ya_optarg = argv[ya_optind]; ya_optind++; } } else { ya_optarg = ya_optnext + 1; ya_optind++; } ya_optnext = NULL; } else { ya_optarg = NULL; if (ya_optnext[1] == 0) { ya_optnext = NULL; ya_optind++; } else { ya_optnext++; } } return opt; } static int ya_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct option *longopts, int *longindex, int *long_only_flag) { char *val = NULL; const struct option *opt; size_t namelen; int idx; for (idx = 0; longopts[idx].name != NULL; idx++) { opt = &longopts[idx]; namelen = strlen(opt->name); if (strncmp(arg, opt->name, namelen) == 0) { switch (arg[namelen]) { case '\0': switch (opt->has_arg) { case ya_required_argument: ya_optind++; if (ya_optind == argc) { ya_optarg = NULL; ya_optopt = opt->val; ya_getopt_error(optstring, "%s: option '--%s' requires an argument\n", argv[0], opt->name); if (optstring[0] == ':') { return ':'; } else { return '?'; } } val = argv[ya_optind]; break; } goto found; case '=': if (opt->has_arg == ya_no_argument) { const char *hyphens = (argv[ya_optind][1] == '-') ? "--" : "-"; ya_optind++; ya_optarg = NULL; ya_optopt = opt->val; ya_getopt_error(optstring, "%s: option '%s%s' doesn't allow an argument\n", argv[0], hyphens, opt->name); return '?'; } val = arg + namelen + 1; goto found; } } } if (long_only_flag) { *long_only_flag = 1; } else { ya_getopt_error(optstring, "%s: unrecognized option '%s'\n", argv[0], argv[ya_optind]); ya_optind++; } return '?'; found: ya_optarg = val; ya_optind++; if (opt->flag) { *opt->flag = opt->val; } if (longindex) { *longindex = idx; } return opt->flag ? 0 : opt->val; } #if defined(__cplusplus) } #endif #endif twogood-unshield-51de441/win32_msvc/unistd.h000066400000000000000000000023751477345220400210260ustar00rootroot00000000000000#ifndef _UNISTD_H #define _UNISTD_H 1 /* This file intended to serve as a drop-in replacement for * unistd.h on Windows * Please add functionality as neeeded */ #include #include #include /* for getpid() and the exec..() family */ #include /* for _getcwd() and _chdir() */ #define srandom srand #define random rand /* Values for the second argument to access. These may be OR'd together. */ #define R_OK 4 /* Test for read permission. */ #define W_OK 2 /* Test for write permission. */ //#define X_OK 1 /* execute permission - unsupported in windows*/ #define F_OK 0 /* Test for existence. */ #define access _access #define dup2 _dup2 #define execve _execve #define ftruncate _chsize #define unlink _unlink #define fileno _fileno #define getcwd _getcwd #define chdir _chdir #define isatty _isatty #define lseek _lseek /* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */ #define ssize_t int #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif /* unistd.h */