/* * Copyright (c) 2026, Chloe M. * Provided under the BSD-3 clause. * * Description: CUMHOLE extraction module * Author: Chloe M. */ #include #include #include #include #include #include #include #include #define REF_MAGIC "CUMHOLE" #define REF_MAGIC_LEN 8 #define REF_PATH_LEN 256 /* * The offset table is made up of references, this * structure represents a reference. * * @magic: Reference magic number * @path: Path this reference refers to * @size: Size of data being referenced * @data_off: Offset to data being referenced */ struct hole_ref { char magic[REF_MAGIC_LEN]; char path[REF_PATH_LEN]; size_t size; size_t data_off; } __attribute__((packed)); /* Globals */ static char *input_path = NULL; static void mkdir_recurse(const char *path) { char buf[256]; char *p = NULL; size_t len; snprintf(buf, sizeof(buf), "%s", path); len = strlen(buf); /* Strip trailing slash */ if (buf[len - 1] == '/') buf[len - 1] = '\0'; /* Make directory of each component */ for (p = buf; *p != '\0'; ++p) { if (*p == '/') { *p = '\0'; mkdir(buf, S_IRWXU); *p = '/'; } } mkdir(buf, S_IRWXU); } static void help(void) { printf("usage: ./cumxct [flags]\n"); printf("[-h] Display this help menu\n"); printf("[-i] Input CUMHOLE bundle\n"); } static int push_file(const char *path, void *data, size_t size) { int fd; if (path == NULL || data == NULL) { return -1; } fd = open(path, O_RDWR | O_CREAT, 0777); if (fd < 0) { printf("fatal: failed to open '%s'\n", path); perror("open"); return -1; } write(fd, data, size); close(fd); return 0; } static void do_parse(struct hole_ref *ref) { const char *directory_name; struct hole_ref *ref_base = ref; char *path; void *data; if (ref == NULL) { return; } for (;;) { if (memcmp(ref->magic, REF_MAGIC, REF_MAGIC_LEN) != 0) { break; } path = strdup(ref->path); directory_name = dirname(path); mkdir_recurse(directory_name); printf("creating %s\n", ref->path); data = (void *)((char *)ref_base + ref->data_off); if (push_file(ref->path, data, ref->size) < 0) { free(path); return; } free(path); ++ref; } } static void input_parse(void) { int fd; char *data; size_t archive_size; ssize_t nret; fd = open(input_path, O_RDONLY); if (fd < 0) { printf("fatal: could not open input file\n"); perror("open"); return; } /* Obtain the size of the buffer */ archive_size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); /* Allocate the actual buffer */ data = malloc(archive_size); if (data == NULL) { printf("fatal: could not allocate archive buffer\n"); close(fd); return; } /* Populate it with bytes */ nret = read(fd, data, archive_size); if (nret <= 0) { printf("fatal: could not read archive\n"); perror("read"); return; } do_parse((struct hole_ref *)data); close(fd); free(data); } int main(int argc, char **argv) { int opt; if (argc < 2) { printf("fatal: too few arguments\n"); help(); return -1; } while ((opt = getopt(argc, argv, "hi:")) != -1) { switch (opt) { case 'h': help(); return -1; case 'i': input_path = strdup(optarg); if (input_path == NULL) { printf("fatal: out of memory\n"); return -1; } break; } } if (input_path == NULL) { printf("fatal: expected input bundle\n"); help(); return -1; } input_parse(); free(input_path); return 0; }