2 InstallShield Z file format
3 Copyright 2015 <hacker@iam.tj>
4 Licenced on the terms of then GNU General Public Licence version 3
6 Read and extract InstallShield .Z archive files
10 #include <sys/types.h>
19 #include "installshield_z.h"
21 static const size_t BUFFER_SIZE = 0x4000;
22 static bool debug = 0;
23 static bool extract = 0;
26 usage(const char *name)
28 printf("usage: %s %s\n\n", name,
35 headers_decode(int z_fd)
38 struct archive_header *header = NULL;
42 if (z_fd != -1 && (header = calloc(BUFFER_SIZE, 1))) {
43 offset = lseek(z_fd, offset, SEEK_SET);
44 if ((qty = read(z_fd, header, Z_FILE_HEADER_BYTES))< Z_FILE_HEADER_BYTES)
45 fprintf(stderr, "Warning: Tried to read %ld header bytes but only got %ld. Will do my best\n", Z_FILE_HEADER_BYTES, qty);
46 printf("Magic %08x\n", header->magic);
47 printf("CRC32 %08x\n", header->__crc32);
48 printf("File size %08x\n", header->file_bytes);
49 printf("Payload size %08x\n", header->payload_bytes);
50 printf("Directories offset %08x\n", header->directories_offset);
51 printf("Directories size %08x\n", header->directories_bytes);
52 printf("Directories entries %04x\n", header->directories_entries);
53 printf("Files offset %08x\n", header->files_offset);
54 printf("Files size %08x\n", header->files_bytes);
55 printf("Files entries %04x\n", header->files_entries);
57 offset = lseek(z_fd, header->directories_offset, SEEK_SET);
58 struct dirent_prefix *dirent, *p;
60 if (offset == header->directories_offset && ((dirent = p = calloc(header->directories_bytes, 1)))) {
62 if ((qty = read(z_fd, dirent, header->directories_bytes)) != header->directories_bytes)
63 fprintf(stderr, "Warning: Tried to read %d directory bytes but only got %ld. Will do my best\n", header->directories_bytes, qty);
65 printf("Directory entries: %d ( %08x bytes @ 0x%08x)\n", header->directories_entries, header->directories_bytes, header->directories_offset);
67 struct ll_dirents dirents[header->directories_entries];
68 for (int i = 0; i < header->directories_entries; ++i) {
69 printf(" Entry %4d Size %x @ %08x \"%s\"\n", i, p->entry_bytes, header->directories_offset + (uint32_t)((void *)p - (void *)dirent), p->name);
71 p = (void *)p + p->entry_bytes;
74 offset = lseek(z_fd, header->files_offset, SEEK_SET);
75 struct fileent_prefix *fileent, *q;
76 if (offset == header->files_offset && ((fileent = q = calloc(header->files_bytes, 1)))) {
77 if ((qty = read(z_fd, fileent, header->files_bytes)) != header->files_bytes)
78 fprintf(stderr, "Warning: Tried to read %d file entry bytes but only got %ld. Will do my best\n", header->files_bytes, qty);
80 printf("File entries: %d ( %08x bytes @ 0x%08x)\n", header->files_entries, header->files_bytes, header->files_offset);
82 for (int i = 0; i < header->files_entries; ++i) {
83 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);
86 printf(" next @ %08x", q->payload_offset + q->payload_bytes);
87 for (int r = 0; r < 0x1E; ++r)
88 printf(" %02x", *(unsigned char *)(r + (void *)q));
91 printf(" \"%s\\%s\"\n", dirents[q->dir_index].entry->name, q->name);
93 int dir_fd = AT_FDCWD;
94 int dir_fd_last = dir_fd;
96 char *dir = dirents[q->dir_index].entry->name;
98 for (char *sep = dir;; ++sep) {
99 if (*sep == '\\' || *sep == 0) {
103 if (strlen(dir) > 0) {
107 printf("Creating directory '%s': ", dir);
109 if (!dirents[q->dir_index].created && mkdirat(dir_fd, dir, 0755) != 0) {
110 if (errno != EEXIST) {
112 fprintf(stderr, "Error %d: cannot create directory %s\n", errno, dir);
114 goto dir_create_abort;
117 printf("Directory already exists: %s\n", dir);
120 dir_fd_last = dir_fd;
122 printf("Opening directory '%s'\n", dir);
123 if ((dir_fd = openat(dir_fd_last, dir, O_DIRECTORY)) == -1) {
125 fprintf(stderr, "Error %d: cannot open directory %s\n", errno, dir);
127 goto dir_create_abort;
133 if (dir_fd_last != AT_FDCWD) close(dir_fd_last);
139 if (*sep == 0) break;
143 dirents[q->dir_index].created = true;
144 goto dir_create_done;
149 if (dir_fd_last != AT_FDCWD) {
151 dir_fd_last = AT_FDCWD;
153 if (dir_fd != AT_FDCWD) {
161 char *dir_name = strdup(dirents[q->dir_index].entry->name);
162 for (char *p = dir_name; *p; ++p)
163 if (*p == '\\') *p = '/';
165 if (dir_fd == AT_FDCWD && (dir_fd = openat(AT_FDCWD, dir_name, O_DIRECTORY)) != -1) {
167 unsigned int z_filename_len = strlen(q->name) + 4;
168 char z_filename[z_filename_len];
169 strncpy(z_filename, q->name, z_filename_len);
170 strncat(z_filename, ".z", 2);
173 printf(" Creating payload file \"%s/%s\"\n", dir_name, z_filename);
175 if ((offset = lseek(z_fd, q->payload_offset, SEEK_SET)) == q->payload_offset) {
177 if ((buffer = calloc(q->payload_bytes, 1)) != 0) {
179 if ((pay_fd = openat(dir_fd, z_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
180 ssize_t z_qty, pay_qty;
181 if ((z_qty = read(z_fd, buffer, q->payload_bytes)) == q->payload_bytes) {
182 if ((pay_qty = write(pay_fd, buffer, q->payload_bytes)) != q->payload_bytes)
183 fprintf(stderr, "Error: whilst writing payload expected %d bytes but wrote %ld\n", q->payload_bytes, pay_qty);
185 fprintf(stderr, "Error: whilst reading payload expected %d bytes but read %ld\n", q->payload_bytes, z_qty);
191 fprintf(stderr, "Error %d: failed to create %s\n", errno, z_filename);
194 fprintf(stderr, "Error %d: cannot open directory at %d %s\n", errno, dir_fd, dir_name);
198 q = (void *)q + q->entry_bytes;
204 fprintf(stderr, "Error: %s\n", "failed to seek or couldn't allocate memory for directories");
213 main(int argc, char **argv, char **env)
223 for (char **e = env; *e; ++e) {
224 if (strncmp("DEBUG", *e, 5) == 0)
227 for (char **a = argv; *a; ++a) {
228 if (debug) printf("Arg: %s\n", *a);
229 if (strncmp("-x", *a, 2) == 0 || strncmp("--extract", *a, 9) == 0)
233 if ((fd = open(argv[1], O_RDONLY)) != -1) {
234 result = headers_decode(fd);
237 fprintf(stderr, "Error: could not open %s\n", argv[0]);