diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 72339ba..32c3c17 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -55,11 +55,10 @@ jobs: ${{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}} + 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 - working-directory: build - run: ctest --output-on-failure + run: ctest --test-dir build --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index d03744d..0f90e1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ endif() message(STATUS "OPENSSL_FOUND: ${OPENSSL_FOUND}") message(STATUS "USE_OUR_OWN_MD5: ${USE_OUR_OWN_MD5}") message(STATUS "BUILD_STATIC: ${BUILD_STATIC}") +message(STATUS "BUILD_TESTING: ${BUILD_TESTING}") add_definitions(-DHAVE_CONFIG_H) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/unshield_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/lib/unshield_config.h) @@ -84,4 +85,6 @@ 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) -add_subdirectory(test) +if (BUILD_TESTING) + add_subdirectory(test) +endif () diff --git a/lib/file.c b/lib/file.c index c385b9f..daf2a32 100644 --- a/lib/file.c +++ b/lib/file.c @@ -314,8 +314,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ #if VERBOSE >= 2 unshield_trace("Open volume %i", volume); #endif - - FCLOSE(reader->volume_file); + + FCLOSE(reader->unshield, reader->volume_file); reader->volume_file = unshield_fopen_for_reading(reader->unshield, volume, CABINET_SUFFIX); if (!reader->volume_file) @@ -328,8 +328,7 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t tmp[COMMON_HEADER_SIZE]; uint8_t* p = tmp; - if (COMMON_HEADER_SIZE != - fread(&tmp, 1, COMMON_HEADER_SIZE, reader->volume_file)) + 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)) @@ -346,8 +345,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t five_header[VOLUME_HEADER_SIZE_V5]; uint8_t* p = five_header; - if (VOLUME_HEADER_SIZE_V5 != - fread(&five_header, 1, VOLUME_HEADER_SIZE_V5, reader->volume_file)) + 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; @@ -383,8 +382,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t six_header[VOLUME_HEADER_SIZE_V6]; uint8_t* p = six_header; - if (VOLUME_HEADER_SIZE_V6 != - fread(&six_header, 1, VOLUME_HEADER_SIZE_V6, reader->volume_file)) + 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; @@ -486,7 +485,7 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ else reader->volume_bytes_left = volume_bytes_left_expanded; - fseek(reader->volume_file, data_offset, SEEK_SET); + unshield_fseek(reader->unshield, reader->volume_file, data_offset, SEEK_SET); reader->volume = volume; success = true; @@ -532,7 +531,7 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si #if VERBOSE >= 3 unshield_trace("Trying to read 0x%x bytes from offset %08x in volume %i", - bytes_to_read, ftell(reader->volume_file), reader->volume); + bytes_to_read, unshield_ftell(reader->unshield, reader->volume_file), reader->volume); #endif if (bytes_to_read == 0) { @@ -540,12 +539,12 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si goto exit; } - if (bytes_to_read != fread(p, 1, bytes_to_read, reader->volume_file)) + 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, - ftell(reader->volume_file)); + unshield_ftell(reader->unshield, reader->volume_file)); goto exit; } @@ -583,14 +582,14 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si return success; }/*}}}*/ -int copy_file(FILE* infile, FILE* outfile) { +int copy_file(Unshield* unshield, FILE* infile, FILE* outfile) { #define SIZE (1024*1024) char buffer[SIZE]; size_t bytes; - while (0 < (bytes = fread(buffer, 1, sizeof(buffer), infile))) - fwrite(buffer, 1, bytes, outfile); + while (0 < (bytes = unshield_fread(unshield, buffer, 1, sizeof(buffer), infile))) + unshield_fwrite(unshield, buffer, 1, bytes, outfile); return 0; } @@ -626,7 +625,7 @@ static UnshieldReader* unshield_reader_create_external(/*{{{*/ } if (file_descriptor->flags & FILE_COMPRESSED) { - long file_size = FSIZE(reader->volume_file); + long file_size = FSIZE(unshield, reader->volume_file); FILE *temporary_file = NULL; /* @@ -639,7 +638,7 @@ static UnshieldReader* unshield_reader_create_external(/*{{{*/ if (diff > 0) { diff = MIN(sizeof(END_OF_CHUNK), diff); temporary_file = tmpfile(); - copy_file(reader->volume_file, temporary_file); + 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); @@ -719,7 +718,7 @@ static void unshield_reader_destroy(UnshieldReader* reader)/*{{{*/ { if (reader) { - FCLOSE(reader->volume_file); + FCLOSE(reader->unshield, reader->volume_file); free(reader); } }/*}}}*/ @@ -777,7 +776,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; @@ -785,7 +784,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ if (filename) { - output = fopen(filename, "wb"); + output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -883,7 +882,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ if (output) { - if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, 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; @@ -925,7 +924,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ md5 = NULL; #endif unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; @@ -992,7 +991,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; @@ -1000,7 +999,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) if (filename) { - output = fopen(filename, "wb"); + output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -1031,7 +1030,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) bytes_left -= bytes_to_write; if (output) { - if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, 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; } @@ -1042,7 +1041,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) exit: unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; @@ -1110,7 +1109,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + 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); @@ -1124,7 +1123,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) if (filename) { - output = fopen(filename, "wb"); + output = unshield_fopen(unshield, filename, "wb"); if (!output) { unshield_error("Failed to open output file '%s'", filename); @@ -1247,7 +1246,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) bytes_left -= bytes_to_write; if (output) { - if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, 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; } @@ -1272,7 +1271,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) bytes_left -= bytes_to_write; if (output) { - if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, 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; @@ -1295,7 +1294,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) exit: unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; diff --git a/lib/helper.c b/lib/helper.c index 3d5d86f..25ed46a 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -11,8 +11,6 @@ #include #ifdef _WIN32 - #define fseek _fseeki64 - #define ftell _ftelli64 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #ifndef PATH_MAX @@ -103,11 +101,12 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff else q=filename; - sourcedir = opendir(dirname); + sourcedir = unshield_opendir(unshield, dirname); /* Search for the File case independent */ if (sourcedir) { - for (dent=readdir(sourcedir);dent;dent=readdir(sourcedir)) + for (dent=unshield_readdir(unshield, sourcedir);dent; + dent=unshield_readdir(unshield, sourcedir)) { if (!(strcasecmp(q, dent->d_name))) { @@ -134,11 +133,11 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff #if VERBOSE unshield_trace("Opening file '%s'", filename); #endif - result = fopen(filename, "rb"); + result = unshield_fopen(unshield, filename, "rb"); exit: if (sourcedir) - closedir(sourcedir); + unshield_closedir(unshield, sourcedir); free(filename); free(dirname); return result; @@ -147,13 +146,13 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff return NULL; } -long long unshield_fsize(FILE* file) +long long unshield_fsize(Unshield* unshield, FILE* file) { long long result; - long long previous = ftell(file); - fseek(file, 0L, SEEK_END); - result = ftell(file); - fseek(file, previous, SEEK_SET); + 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; } diff --git a/lib/internal.h b/lib/internal.h index 7f01d59..9faf414 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -52,6 +52,8 @@ struct _Unshield { Header* header_list; char* filename_pattern; + const UnshieldIoCallbacks* io_callbacks; + void* io_userdata; }; /* @@ -77,13 +79,57 @@ void unshield_file_group_destroy(UnshieldFileGroup* self); 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(FILE* file); +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); +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 @@ -100,8 +146,8 @@ uint8_t* unshield_header_get_buffer(Header* header, uint32_t offset); #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(file) if (file) { fclose(file); (file) = NULL; } -#define FSIZE(file) ((file) ? unshield_fsize(file) : 0) +#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) diff --git a/lib/libunshield.c b/lib/libunshield.c index e562933..e3dd06a 100644 --- a/lib/libunshield.c +++ b/lib/libunshield.c @@ -6,6 +6,82 @@ #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() @@ -239,11 +315,11 @@ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ Header* header = NEW1(Header); header->index = i; - header->size = FSIZE(file); + header->size = FSIZE(unshield, file); if (header->size < 4) { unshield_error("Header file %i too small", i); - FCLOSE(file); + FCLOSE(unshield, file); goto error; } @@ -251,12 +327,12 @@ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ if (!header->data) { unshield_error("Failed to allocate memory for header file %i", i); - FCLOSE(file); + FCLOSE(unshield, file); goto error; } - bytes_read = fread(header->data, 1, header->size, file); - FCLOSE(file); + bytes_read = unshield_fread(unshield, header->data, 1, header->size, file); + FCLOSE(unshield, file); if (bytes_read != header->size) { @@ -346,6 +422,16 @@ Unshield* unshield_open(const char* filename)/*{{{*/ }/*}}}*/ 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) @@ -354,6 +440,9 @@ Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/ 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"); diff --git a/lib/libunshield.h b/lib/libunshield.h index f5940a4..8edaea7 100644 --- a/lib/libunshield.h +++ b/lib/libunshield.h @@ -35,6 +35,22 @@ Unshield* unshield_open(const char* filename); Unshield* unshield_open_force_version(const char* filename, int version); 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* unshield_open2(const char* filename, const UnshieldIoCallbacks* callbacks, void* userdata); +Unshield* unshield_open2_force_version(const char* filename, int version, const UnshieldIoCallbacks* callbacks, void* userdata); + /* Component functions */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3e7c543..2b18c2c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,3 +11,4 @@ if(APPLE) else() add_subdirectory(v5/CVE-2015-1386) endif() +add_subdirectory(v5/stack_memory_file) diff --git a/test/v5/stack_memory_file/CMakeLists.txt b/test/v5/stack_memory_file/CMakeLists.txt new file mode 100644 index 0000000..80b45d2 --- /dev/null +++ b/test/v5/stack_memory_file/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(test_stack_memory_file test_stack_memory_file.c) +target_link_libraries(test_stack_memory_file PUBLIC libunshield) + +add_test( + NAME test_stack_memory_file + COMMAND $ +) diff --git a/test/v5/stack_memory_file/test_stack_memory_file.c b/test/v5/stack_memory_file/test_stack_memory_file.c new file mode 100644 index 0000000..58f2356 --- /dev/null +++ b/test/v5/stack_memory_file/test_stack_memory_file.c @@ -0,0 +1,425 @@ +#include +#include +#include +#include +#include + +#include +#include + +typedef struct +{ + bool opened; + const char* name; + uint32_t index; + struct dirent dirent[2]; +} StackMemoryDirectory_t; + +typedef struct +{ + bool opened; + const char* name; + uint8_t *buf; + uint32_t size; + uint32_t current_pointer; +} StackMemoryFile_t; + +typedef struct +{ + bool opened; + const char* filename; + StackMemoryDirectory_t directory; + StackMemoryFile_t files[3]; +} StackMemoryIoCallbacksUserData_t; + +void *stack_memory_fopen(const char *filename, const char *modes, void *userdata) +{ + (void)modes; + StackMemoryIoCallbacksUserData_t *userdata_stack_memory = (StackMemoryIoCallbacksUserData_t *) userdata; + for (size_t i = 0; i < sizeof(userdata_stack_memory->files) / sizeof(userdata_stack_memory->files[0]); ++i) + { + StackMemoryFile_t* file = &userdata_stack_memory->files[i]; + if (file->name != NULL && strcmp(file->name, filename) == 0) + { + if (file->opened) + return NULL; + userdata_stack_memory->opened = true; + return file; + } + } + return NULL; +} + +int stack_memory_fseek(void *file, long int offset, int whence, void *userdata) +{ + (void)userdata; + StackMemoryFile_t *stack_file = (StackMemoryFile_t *) file; + long int new_offset = (long int)stack_file->current_pointer; + switch (whence) + { + case SEEK_SET: + new_offset = offset; + break; + case SEEK_CUR: + new_offset += offset; + break; + case SEEK_END: + new_offset = (long int)stack_file->size + offset; + break; + default: + return 1; + } + if (new_offset < 0 && new_offset > stack_file->size) + return 1; + stack_file->current_pointer = new_offset; + return 0; +} + +long int stack_memory_ftell(void *file, void *userdata) +{ + (void)userdata; + StackMemoryFile_t *stack_file = (StackMemoryFile_t *) file; + return (long int)stack_file->current_pointer; +} + +size_t stack_memory_fread(void *ptr, size_t size, size_t n, void *file, void *userdata) +{ + (void)userdata; + if (size == 0 || n == 0) + return 0; + uint32_t total_size = size * n; + StackMemoryFile_t *stack_file = (StackMemoryFile_t *) file; + if (total_size > stack_file->current_pointer + stack_file->size) + total_size = stack_file->size - stack_file->current_pointer; + memcpy(ptr, stack_file->buf + stack_file->current_pointer, total_size); + stack_file->current_pointer += total_size; + if (stack_file->current_pointer == stack_file->size) + stack_file->current_pointer = EOF; + return total_size; +} + +size_t stack_memory_fwrite(const void *ptr, size_t size, size_t n, void *file, void *userdata) +{ + (void)userdata; + if (size == 0 || n == 0) + return 0; + StackMemoryFile_t *stack_file = (StackMemoryFile_t *) file; + memcpy(stack_file->buf + stack_file->current_pointer, ptr, size * n); + stack_file->current_pointer += size * n; + return size * n; +} + +int stack_memory_fclose(void *file, void *userdata) +{ + (void)userdata; + StackMemoryFile_t *stack_file = (StackMemoryFile_t *) file; + if (!stack_file->opened) + return EOF; + stack_file->opened = false; + return 0; +} + +void *stack_memory_opendir(const char *name, void *userdata) +{ + StackMemoryIoCallbacksUserData_t *userdata_stack_memory = (StackMemoryIoCallbacksUserData_t *) userdata; + if (strcmp(userdata_stack_memory->directory.name, name) != 0) + return NULL; + if (userdata_stack_memory->directory.opened) + return NULL; + userdata_stack_memory->directory.opened = true; + userdata_stack_memory->directory.index = 0; + return &userdata_stack_memory->directory; +} + +int stack_memory_closedir(void *dir, void *userdata) +{ + StackMemoryIoCallbacksUserData_t *userdata_stack_memory = (StackMemoryIoCallbacksUserData_t *) userdata; + if (dir != &userdata_stack_memory->directory) + return -1; + if (!userdata_stack_memory->directory.opened) + return -1; + userdata_stack_memory->directory.opened = false; + return 0; +} + +struct dirent* stack_memory_readdir(void *dir, void *userdata) +{ + StackMemoryIoCallbacksUserData_t *userdata_stack_memory = (StackMemoryIoCallbacksUserData_t *) userdata; + if (dir != &userdata_stack_memory->directory) + return NULL; + if (userdata_stack_memory->directory.index >= sizeof(userdata_stack_memory->directory.dirent) / sizeof(userdata_stack_memory->directory.dirent[0])) + return NULL; + return &userdata_stack_memory->directory.dirent[userdata_stack_memory->directory.index++]; +} + +static UnshieldIoCallbacks stackMemoryFileIoCallbacks = { + .fopen = stack_memory_fopen, + .fseek = stack_memory_fseek, + .ftell = stack_memory_ftell, + .fread = stack_memory_fread, + .fwrite = stack_memory_fwrite, + .fclose = stack_memory_fclose, + .opendir = stack_memory_opendir, + .closedir = stack_memory_closedir, + .readdir = stack_memory_readdir, +}; + +int test_open_and_save() +{ + uint8_t test1_hdr_data[] = { + 0x49, 0x53, 0x63, 0x28, // signature + 0x00, 0x50, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0x00, // volume_info + 0x10, 0x00, 0x00, 0x00, // cab_descriptor_offset + 0x01, 0x00, 0x00, 0x00, // cab_descriptor_size + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x8 + 0x76, 0x02, 0x00, 0x00, // header->cab.file_table_offset + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // header->cab.file_table_size + 0x00, 0x00, 0x00, 0x00, // header->cab.file_table_size2 + 0x00, 0x00, 0x00, 0x00, // header->cab.directory_count + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x8 + 0x01, 0x00, 0x00, 0x00, // header->cab.file_count + 0x00, 0x00, 0x00, 0x00, // header->cab.cab.file_table_offset2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, // header->file_table[1] + 0x2a, 0x00, 0x00, 0x00, // fd->name_offset + 0x00, 0x00, 0x00, 0x00, // fd->directory_index + 0x00, 0x00, // fd->flags + 0x05, 0x00, 0x00, 0x00, // fd->expanded_size + 0x05, 0x00, 0x00, 0x00, // fd->compressed_size + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x14 + 0x40, 0x00, 0x00, 0x00, // fd->data_offset + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // md5 + 'T', 'E', 'S', 'T', 0x00, // name + }; + uint8_t test1_cab_data[] = { + 0x49, 0x53, 0x63, 0x28, // signature + 0x00, 0x50, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0x00, // volume_info + 0x10, 0x00, 0x00, 0x00, // cab_descriptor_offset + 0x01, 0x00, 0x00, 0x00, // cab_descriptor_size + 0x00, 0x00, 0x00, 0x00, // volume_header.data_offset; + 0x00, 0x00, 0x00, 0x00, // volume_header.unknown; + 0x00, 0x00, 0x00, 0x00, // volume_header.first_file_index; + 0x00, 0x00, 0x00, 0x00, // volume_header.last_file_index; + 0x40, 0x00, 0x00, 0x00, // volume_header.first_file_offset; + 0x05, 0x00, 0x00, 0x00, // volume_header.first_file_size_expanded; + 0x05, 0x00, 0x00, 0x00, // volume_header.first_file_size_compressed; + 0x40, 0x00, 0x00, 0x00, // volume_header.last_file_offset; + 0x05, 0x00, 0x00, 0x00, // volume_header.last_file_size_expanded; + 0x05, 0x00, 0x00, 0x00, // volume_header.last_file_size_compressed; + 0x00, 0x00, 0x00, 0x00, + 'T', 'E', 'S', 'T', 0x00, // name + }; + uint8_t test_output_txt_data[0x10] = { 0, }; + StackMemoryIoCallbacksUserData_t stack_file = { + .opened = false, + .filename = "test.cab", + .directory = { + .opened = false, + .name = ".", + .dirent = { + [0] = { .d_name = "test1.hdr" }, + [1] = { .d_name = "test1.cab" }, + }, + }, + .files = { + [0] = { + .name = "./test1.hdr", + .buf = test1_hdr_data, + .size = sizeof (test1_hdr_data), + }, + [1] = { + .name = "./test1.cab", + .buf = test1_cab_data, + .size = sizeof (test1_cab_data), + }, + [2] = { + .name = "test_output.txt", + .buf = test_output_txt_data, + .size = sizeof (test_output_txt_data), + }, + } + }; + + Unshield *unshield = unshield_open2(stack_file.filename, &stackMemoryFileIoCallbacks, &stack_file); + if (unshield == NULL) + return EXIT_FAILURE; + + if (!stack_file.opened) + return EXIT_FAILURE; + + if (unshield_file_count(unshield) != 1) + { + unshield_close(unshield); + return EXIT_FAILURE; + } + + if (!unshield_file_save(unshield, 0, "test_output.txt")) + { + unshield_close(unshield); + return EXIT_FAILURE; + } + + if (strcmp((char*)test_output_txt_data, "TEST") != 0) + { + unshield_close(unshield); + return EXIT_FAILURE; + } + + unshield_close(unshield); + + return EXIT_SUCCESS; +} + + +int main() +{ + if (test_open_and_save() != EXIT_SUCCESS) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +}