InstallShield Z Archive extractor, v0.5
authorTJ <hacker@iam.tj>
Fri, 13 Nov 2015 05:34:31 +0000 (05:34 +0000)
committerTJ <hacker@iam.tj>
Fri, 13 Nov 2015 05:34:31 +0000 (05:34 +0000)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
installshield_main.c [new file with mode: 0644]
installshield_z.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..73f8eaa
--- /dev/null
@@ -0,0 +1,5 @@
+COMMON
+PDDOC
+PDMAIN
+
+*.swp
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..4946167
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+EXE=isz
+DATA=DATA.Z
+SRC=installshield_main.c
+CFLAGS=$(CLFAGS) -g
+
+build:
+       gcc $(CFLAGS) -o $(EXE) $(SRC)
+
+run: build
+       ./$(EXE) $(DATA) -x
+
+debug: build
+       DEBUG=1 ./$(EXE) $(DATA) -x
+
+strace: build
+       DEBU=1 strace ./$(EXE) $(DATA) -x
+
+clean:
+       rm -f $(EXE) *.o
+       rm -rf COMMON PDMAIN PDDOC
+
+PHONY: clean
diff --git a/installshield_main.c b/installshield_main.c
new file mode 100644 (file)
index 0000000..40bb743
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+  InstallShield Z file format
+  Copyright 2015 <hacker@iam.tj>
+  Licenced on the terms of then GNU General Public Licence version 3
+
+  Read and extract InstallShield .Z archive files
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "installshield_z.h"
+
+static const size_t BUFFER_SIZE = 0x4000;
+static bool debug = 0;
+static bool extract = 0;
+
+void
+usage(const char *name)
+{
+       printf("usage: %s %s\n\n", name,
+               " FILENAME\n"
+               " [-x|--extract]"
+       );
+}
+
+int
+headers_decode(int z_fd)
+{
+       int result = -1;
+       struct archive_header *header = NULL;
+       off_t offset = 0;
+       ssize_t qty = 0;
+
+       if (z_fd != -1 && (header = calloc(BUFFER_SIZE, 1))) {
+               offset = lseek(z_fd, offset, SEEK_SET);
+               if ((qty = read(z_fd, header, Z_FILE_HEADER_BYTES))< Z_FILE_HEADER_BYTES)
+                       fprintf(stderr, "Warning: Tried to read %ld header bytes but only got %ld. Will do my best\n", Z_FILE_HEADER_BYTES, qty);
+               printf("Magic               %08x\n", header->magic);
+               printf("CRC32               %08x\n", header->__crc32);
+               printf("File size           %08x\n", header->file_bytes);
+               printf("Payload size        %08x\n", header->payload_bytes);
+        printf("Directories offset  %08x\n", header->directories_offset);
+               printf("Directories size    %08x\n", header->directories_bytes);
+               printf("Directories entries     %04x\n", header->directories_entries);
+               printf("Files offset        %08x\n", header->files_offset);
+               printf("Files size          %08x\n", header->files_bytes);
+               printf("Files entries           %04x\n", header->files_entries);
+
+               offset = lseek(z_fd, header->directories_offset, SEEK_SET);
+               struct dirent_prefix *dirent, *p;
+
+               if (offset == header->directories_offset && ((dirent = p = calloc(header->directories_bytes, 1)))) {
+
+                       if ((qty = read(z_fd, dirent, header->directories_bytes)) != header->directories_bytes)
+                               fprintf(stderr, "Warning: Tried to read %d directory bytes but only got %ld. Will do my best\n", header->directories_bytes, qty);
+
+                       printf("Directory entries: %d ( %08x bytes @ 0x%08x)\n", header->directories_entries, header->directories_bytes, header->directories_offset);
+
+                       struct ll_dirents dirents[header->directories_entries];
+                       for (int i = 0; i < header->directories_entries; ++i) {
+                               printf(" Entry %4d Size %x @ %08x    \"%s\"\n", i, p->entry_bytes, header->directories_offset + (uint32_t)((void *)p - (void *)dirent), p->name);
+                               dirents[i].entry = p;
+                               p = (void *)p + p->entry_bytes;
+                       }
+
+                       offset = lseek(z_fd, header->files_offset, SEEK_SET);
+                       struct fileent_prefix *fileent, *q;
+                       if (offset == header->files_offset && ((fileent = q = calloc(header->files_bytes, 1)))) {
+                               if ((qty = read(z_fd, fileent, header->files_bytes)) != header->files_bytes)
+                                       fprintf(stderr, "Warning: Tried to read %d file entry bytes but only got %ld. Will do my best\n", header->files_bytes, qty);
+
+                               printf("File entries: %d ( %08x bytes @ 0x%08x)\n", header->files_entries, header->files_bytes, header->files_offset);
+
+                               for (int i = 0; i < header->files_entries; ++i) {
+                                       printf(" Entry %4d Size %06x @ %08x payload size %06x @ %08x", i, q->entry_bytes, header->files_offset +(uint32_t)((void *)q - (void *)fileent), q->payload_bytes, q->payload_offset);
+
+                                       if (debug) {
+                                               printf(" next @ %08x", q->payload_offset + q->payload_bytes);
+                                               for (int r = 0; r < 0x1E; ++r)
+                                               printf(" %02x", *(unsigned char *)(r + (void *)q));
+                                       }
+
+                                       printf(" \"%s\\%s\"\n", dirents[q->dir_index].entry->name, q->name);
+
+                                       int dir_fd = AT_FDCWD;
+                                       int dir_fd_last = dir_fd;
+                                       if (extract) {
+                                               char *dir = dirents[q->dir_index].entry->name;
+
+                                               for (char *sep = dir;; ++sep) {
+                                                       if (*sep == '\\' || *sep == 0) {
+                                                               char temp = *sep;
+                                                               *sep = 0;
+
+                                                               if (strlen(dir) > 0) {
+                                                                       int ret;
+
+                                                                       if (debug)
+                                                                               printf("Creating directory '%s': ", dir);
+
+                                                                       if (!dirents[q->dir_index].created && mkdirat(dir_fd, dir, 0755) != 0) {
+                                                                               if (errno != EEXIST) {
+                                                                                       *sep = temp;
+                                                                                       fprintf(stderr, "Error %d: cannot create directory %s\n", errno,  dir);
+                                                                                       perror(NULL);
+                                                                                       goto dir_create_abort;
+                                                                               }
+                                                                               if (debug)
+                                                                                       printf("Directory already exists: %s\n", dir);
+                                                                       }
+
+                                                                       dir_fd_last = dir_fd;
+                                                                       if (debug)
+                                                                               printf("Opening directory '%s'\n", dir);
+                                                                       if ((dir_fd = openat(dir_fd_last, dir, O_DIRECTORY)) == -1) {
+                                                                               *sep = temp;
+                                                                               fprintf(stderr, "Error %d: cannot open directory %s\n", errno, dir);
+                                                                               perror(NULL);
+                                                                               goto dir_create_abort;
+                                                                       }
+
+                                                                       if (debug)
+                                                                               printf("%s\n", "");
+
+                                                                       if (dir_fd_last != AT_FDCWD) close(dir_fd_last);
+dir_exists:
+                                                                       *sep = temp;
+                                                                       dir = sep + 1;
+                                                               }
+                                                       }
+                                                       if (*sep == 0) break;
+                                               }
+                                               close(dir_fd);
+                                               dir_fd = AT_FDCWD;
+                                               dirents[q->dir_index].created = true;
+                                               goto dir_create_done;
+
+dir_create_abort:
+                                               if (debug)
+                                                       printf("%s", "\n");
+                                               if (dir_fd_last != AT_FDCWD) {
+                                                       close(dir_fd_last);
+                                                       dir_fd_last = AT_FDCWD;
+                                               }
+                                               if (dir_fd != AT_FDCWD) {
+                                                       close(dir_fd);
+                                                       dir_fd = AT_FDCWD;
+                                               }
+
+                                       }
+dir_create_done:
+                                       if (NULL);
+                                       char *dir_name = strdup(dirents[q->dir_index].entry->name);
+                                       for (char *p = dir_name; *p; ++p)
+                                               if (*p == '\\') *p = '/';
+
+                                       if (dir_fd == AT_FDCWD && (dir_fd = openat(AT_FDCWD, dir_name, O_DIRECTORY)) != -1) {
+
+                                               unsigned int z_filename_len = strlen(q->name) + 4;
+                                               char z_filename[z_filename_len];
+                                               strncpy(z_filename, q->name, z_filename_len);
+                                               strncat(z_filename, ".z", 2);
+
+                                               if (debug)
+                                                       printf(" Creating payload file \"%s/%s\"\n", dir_name,  z_filename);
+
+                                               if ((offset = lseek(z_fd, q->payload_offset, SEEK_SET)) == q->payload_offset) {
+                                                       void *buffer;
+                                                       if ((buffer = calloc(q->payload_bytes, 1)) != 0) {
+                                                               int pay_fd;
+                                                               if ((pay_fd = openat(dir_fd, z_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
+                                                                       ssize_t z_qty, pay_qty;
+                                                                       if ((z_qty = read(z_fd, buffer, q->payload_bytes)) == q->payload_bytes) {
+                                                                               if ((pay_qty = write(pay_fd, buffer, q->payload_bytes)) != q->payload_bytes)
+                                                                                       fprintf(stderr, "Error: whilst writing payload expected %d bytes but wrote %ld\n", q->payload_bytes, pay_qty);
+                                                                       } else
+                                                                               fprintf(stderr, "Error: whilst reading payload expected %d bytes but read %ld\n", q->payload_bytes, z_qty);
+                                                                       close(pay_fd);
+                                                               }
+                                                               free(buffer);
+                                                       }
+                                               } else
+                                                       fprintf(stderr, "Error %d: failed to create %s\n", errno, z_filename);
+                                               close(dir_fd);
+                                       } else {
+                                               fprintf(stderr, "Error %d: cannot open directory at %d %s\n", errno, dir_fd, dir_name);
+                                               perror(NULL);
+                                       }
+
+                                       q = (void *)q + q->entry_bytes;
+                               }
+                               free(fileent);
+                       }
+                       free(dirent);
+               } else 
+                       fprintf(stderr, "Error: %s\n", "failed to seek or couldn't allocate memory for directories");
+               free(header);
+               result = 0;
+       }
+       return result;
+}
+
+int
+main(int argc, char **argv, char **env)
+{
+       int result = -1;
+       int fd = 0;
+
+       if (argc < 2 ) {
+               usage(argv[0]);
+               return 0;
+       }
+
+       for (char **e = env; *e; ++e) {
+               if (strncmp("DEBUG", *e, 5) == 0)
+                       debug = true;
+       }
+       for (char **a = argv; *a; ++a) {
+               if (debug) printf("Arg: %s\n", *a);
+               if (strncmp("-x", *a, 2) == 0 || strncmp("--extract", *a, 9) == 0)
+                       extract = true;
+       }
+
+       if ((fd = open(argv[1], O_RDONLY)) != -1) {
+               result = headers_decode(fd);
+               close(fd);
+       } else {
+               fprintf(stderr, "Error: could not open %s\n", argv[0]);
+               return errno;
+       }
+
+
+       return result;
+}
+
diff --git a/installshield_z.h b/installshield_z.h
new file mode 100644 (file)
index 0000000..c3b8375
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+  InstallShield Z file format
+  Copyright 2015 <hacker@iam.tj>
+  Licenced on the terms of then GNU General Public Licence version 3
+
+  Data structures used in InstallShield .Z archive files
+ */
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+const unsigned int file_magic = 0x8C655D12;
+const ssize_t Z_FILE_HEADER_BYTES = 0x100;
+
+/* __prefixed field purpose is not confirmed */
+struct __attribute__((packed)) archive_header {
+       uint32_t                magic;                                          // 0x00
+       uint16_t                __version;                                      // 0x04
+       uint16_t                __flags;                                        // 0x06
+       uint32_t                __unknown08;                            // 0x08
+       uint16_t                files_entries;                          // 0x0C
+       uint32_t                __crc32;                                        // 0x0E
+       uint32_t                file_bytes;                                     // 0x12
+       uint32_t                payload_bytes;                          // 0x16
+       uint8_t                 __known1A;                                      // 0x1A
+       uint16_t                __unknown1B;                            // 0x1B
+       uint32_t                __unknown1D;                            // 0x1D
+       uint32_t                __unknown21;                            // 0x21
+       uint32_t                __unknown25;                            // 0x25
+       uint32_t                directories_offset;                     // 0x29
+       uint32_t                directories_bytes;                      // 0x2D
+       uint16_t                directories_entries;            // 0x31
+       uint32_t                files_offset;                           // 0x33
+       uint32_t                files_bytes;                            // 0x37
+       unsigned char   unknown3B[0x100 - 0x3B];        // 0x3B to 0xFF
+};
+
+struct __attribute__((packed)) dirent_prefix {
+       uint16_t                        __flags;                        // 0x00
+       uint16_t                        entry_bytes;            // 0x02
+       uint16_t                        name_bytes;                     // 0x04
+       unsigned char           name[];                         // 0x06
+};
+
+struct __attribute__((packed)) dirent_suffix {
+       unsigned char           __eos;                                  // end of string NUL
+       uint32_t                        __unknown00;                    // 0x01 after end of directory_entry_prefix
+};
+
+struct __attribute__((packed)) fileent_prefix {
+       uint8_t                         __unknown00;
+       uint16_t                        dir_index;
+       uint32_t                        __unknown03;
+       uint32_t                        payload_bytes;
+       uint32_t                        payload_offset;
+       uint32_t                        __crc32;
+       uint32_t                        __unknown13;
+       uint16_t                        entry_bytes;
+       uint32_t                        __unknown01;
+       uint8_t                         name_bytes;
+       unsigned char           name[];
+};
+
+struct __attribute__((packed)) fileent_suffix {
+       uint32_t                        __unkown00;
+       uint32_t                        __unkown04;
+       uint32_t                        __unkown08;
+};
+
+struct ll_dirents {
+       struct dirent_prefix *entry;
+       struct ll_dirents       *next;
+       bool                            created;
+};
+
+struct ll_fileents {
+       struct fileent_prefix *entry;
+       struct ll_fileents *next;
+};