/* | |
* SEP firmware split tool | |
* | |
* Copyright (c) 2017 xerub | |
*/ | |
#include <fcntl.h> | |
#include <stddef.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <unistd.h> | |
#include <mach-o/loader.h> | |
#define IS64(image) (*(uint8_t *)(image) & 1) | |
#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) | |
static const struct sepapp_t { | |
uint64_t phys; | |
uint32_t virt; | |
uint32_t size; | |
uint32_t entry; | |
char name[12]; | |
/*char hash[16];*/ | |
} *apps; | |
static size_t sizeof_sepapp = sizeof(struct sepapp_t); | |
#define UCHAR_MAX 255 | |
static unsigned char * | |
boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, | |
const unsigned char* needle, size_t nlen) | |
{ | |
size_t last, scan = 0; | |
size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: | |
* bad character shift */ | |
/* Sanity checks on the parameters */ | |
if (nlen <= 0 || !haystack || !needle) | |
return NULL; | |
/* ---- Preprocess ---- */ | |
/* Initialize the table to default value */ | |
/* When a character is encountered that does not occur | |
* in the needle, we can safely skip ahead for the whole | |
* length of the needle. | |
*/ | |
for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) | |
bad_char_skip[scan] = nlen; | |
/* C arrays have the first byte at [0], therefore: | |
* [nlen - 1] is the last byte of the array. */ | |
last = nlen - 1; | |
/* Then populate it with the analysis of the needle */ | |
for (scan = 0; scan < last; scan = scan + 1) | |
bad_char_skip[needle[scan]] = last - scan; | |
/* ---- Do the matching ---- */ | |
/* Search the haystack, while the needle can still be within it. */ | |
while (hlen >= nlen) | |
{ | |
/* scan from the end of the needle */ | |
for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) | |
if (scan == 0) /* If the first byte matches, we've found it. */ | |
return (void *)haystack; | |
/* otherwise, we need to skip some bytes and start again. | |
Note that here we are getting the skip value based on the last byte | |
of needle, no matter where we didn't match. So if needle is: "abcd" | |
then we are skipping based on 'd' and that value will be 4, and | |
for "abcdd" we again skip on 'd' but the value will be only 1. | |
The alternative of pretending that the mismatched character was | |
the last character is slower in the normal case (E.g. finding | |
"abcd" in "...azcd..." gives 4 by using 'd' but only | |
4-2==2 using 'z'. */ | |
hlen -= bad_char_skip[haystack[last]]; | |
haystack += bad_char_skip[haystack[last]]; | |
} | |
return NULL; | |
} | |
static size_t | |
restore_linkedit(uint8_t *p, size_t size) | |
{ | |
unsigned i; | |
struct mach_header *hdr = (struct mach_header *)p; | |
uint64_t min = -1; | |
uint64_t delta = 0; | |
int is64 = 0; | |
uint8_t *q; | |
if (size < 1024) { | |
return -1; | |
} | |
if (!MACHO(p)) { | |
return -1; | |
} | |
if (IS64(p)) { | |
is64 = 4; | |
} | |
q = p + sizeof(struct mach_header) + is64; | |
for (i = 0; i < hdr->ncmds; i++) { | |
const struct load_command *cmd = (struct load_command *)q; | |
if (cmd->cmd == LC_SEGMENT) { | |
const struct segment_command *seg = (struct segment_command *)q; | |
if (strcmp(seg->segname, "__PAGEZERO") && min > seg->vmaddr) { | |
min = seg->vmaddr; | |
} | |
} | |
if (cmd->cmd == LC_SEGMENT_64) { | |
const struct segment_command_64 *seg = (struct segment_command_64 *)q; | |
if (strcmp(seg->segname, "__PAGEZERO") && min > seg->vmaddr) { | |
min = seg->vmaddr; | |
} | |
} | |
q = q + cmd->cmdsize; | |
} | |
q = p + sizeof(struct mach_header) + is64; | |
for (i = 0; i < hdr->ncmds; i++) { | |
const struct load_command *cmd = (struct load_command *)q; | |
if (cmd->cmd == LC_SEGMENT) { | |
struct segment_command *seg = (struct segment_command *)q; | |
if (!strcmp(seg->segname, "__LINKEDIT")) { | |
delta = seg->vmaddr - min - seg->fileoff; | |
seg->fileoff += delta; | |
} | |
} | |
if (cmd->cmd == LC_SEGMENT_64) { | |
struct segment_command_64 *seg = (struct segment_command_64 *)q; | |
if (!strcmp(seg->segname, "__LINKEDIT")) { | |
delta = seg->vmaddr - min - seg->fileoff; | |
seg->fileoff += delta; | |
} | |
} | |
if (cmd->cmd == LC_SYMTAB) { | |
struct symtab_command *sym = (struct symtab_command *)q; | |
if (sym->stroff) sym->stroff += delta; | |
if (sym->symoff) sym->symoff += delta; | |
} | |
q = q + cmd->cmdsize; | |
} | |
return 0; | |
} | |
static size_t | |
calc_size(const uint8_t *p, size_t size) | |
{ | |
unsigned i; | |
const struct mach_header *hdr = (struct mach_header *)p; | |
const uint8_t *q = p + sizeof(struct mach_header); | |
size_t end, tsize = 0; | |
if (size < 1024) { | |
return 0; | |
} | |
if (!MACHO(p)) { | |
return 0; | |
} | |
if (IS64(p)) { | |
q += 4; | |
} | |
for (i = 0; i < hdr->ncmds; i++) { | |
const struct load_command *cmd = (struct load_command *)q; | |
if (cmd->cmd == LC_SEGMENT) { | |
const struct segment_command *seg = (struct segment_command *)q; | |
end = seg->fileoff + seg->filesize; | |
if (tsize < end) { | |
tsize = end; | |
} | |
} | |
if (cmd->cmd == LC_SEGMENT_64) { | |
const struct segment_command_64 *seg = (struct segment_command_64 *)q; | |
end = seg->fileoff + seg->filesize; | |
if (tsize < end) { | |
tsize = end; | |
} | |
} | |
q = q + cmd->cmdsize; | |
} | |
return tsize; | |
} | |
uint8_t *kernel = MAP_FAILED; | |
size_t kernel_size = 0; | |
static int kernel_fd = -1; | |
static int | |
init_kernel(const char *filename) | |
{ | |
kernel_fd = open(filename, O_RDONLY); | |
if (kernel_fd < 0) { | |
return -1; | |
} | |
kernel_size = lseek(kernel_fd, 0, SEEK_END); | |
kernel = mmap(NULL, kernel_size, PROT_READ, MAP_PRIVATE, kernel_fd, 0); | |
if (kernel == MAP_FAILED) { | |
close(kernel_fd); | |
kernel_fd = -1; | |
return -1; | |
} | |
return 0; | |
} | |
static void | |
term_kernel(void) | |
{ | |
munmap(kernel, kernel_size); | |
close(kernel_fd); | |
} | |
static int | |
write_file(const char *name, const void *buf, size_t size) | |
{ | |
int fd; | |
size_t sz; | |
fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0644); | |
if (fd < 0) { | |
return -1; | |
} | |
sz = write(fd, buf, size); | |
close(fd); | |
return (sz == size) ? 0 : -1; | |
} | |
static int | |
restore_file(unsigned index, const unsigned char *buf, size_t size, int restore) | |
{ | |
int rv; | |
void *tmp; | |
char name[256]; | |
char tail[12 + 1]; | |
if (index == 1 && size > 4096) { | |
unsigned char *toc = boyermoore_horspool_memmem(buf + size - 4096, 4096, (unsigned char *)"SEPOS ", 12); | |
if (toc) { | |
unsigned char *p = boyermoore_horspool_memmem(toc + 1, 64, (unsigned char *)"SEP", 3); | |
if (p) { | |
sizeof_sepapp = p - toc; | |
} | |
apps = (struct sepapp_t *)(toc - offsetof(struct sepapp_t, name)); | |
} | |
} | |
if (apps && buf > (unsigned char *)apps) { | |
char *p; | |
memcpy(tail, apps->name, 12); | |
for (p = tail + 12; p > tail && p[-1] == ' '; p--) { | |
continue; | |
} | |
*p = '\0'; | |
printf("%-12s phys 0x%llx, virt 0x%x, size 0x%x, entry 0x%x\n", tail, apps->phys, apps->virt, apps->size, apps->entry); | |
apps = (struct sepapp_t *)((char *)apps + sizeof_sepapp); | |
} else { | |
if (index == 0) { | |
strcpy(tail, "boot"); | |
printf("%s\n", tail); | |
} else if (index == 1) { | |
strcpy(tail, "kernel"); | |
printf("%s\n", tail); | |
} else { | |
*tail = '\0'; | |
printf("macho%d\n", index); | |
} | |
} | |
snprintf(name, sizeof(name), "sepdump%02u_%s", index, tail); | |
if (!restore) { | |
return write_file(name, buf, size); | |
} | |
tmp = malloc(size); | |
if (!tmp) { | |
return -1; | |
} | |
memcpy(tmp, buf, size); | |
restore_linkedit(tmp, size); | |
rv = write_file(name, tmp, size); | |
free(tmp); | |
return rv; | |
} | |
static int | |
split(int restore) | |
{ | |
size_t i; | |
unsigned j = 0; | |
size_t last = 0; | |
for (i = 0; i < kernel_size; i += 4) { | |
size_t sz = calc_size(kernel + i, kernel_size - i); | |
if (sz) { | |
restore_file(j++, kernel + last, i - last, restore); | |
last = i; | |
i += sz - 4; | |
} | |
} | |
restore_file(j, kernel + last, i - last, restore); | |
return 0; | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
int rv; | |
const char *krnl = (argc > 1) ? argv[1] : "sep"; | |
rv = init_kernel(krnl); | |
if (rv) { | |
fprintf(stderr, "[e] cannot read kernel\n"); | |
return -1; | |
} | |
rv = split(1); | |
term_kernel(); | |
return 0; | |
} |
This comment has been minimized.
This comment has been minimized.
Noob question, how to use img4lib & sepsplit.c to decrypt SEP-firmware ? Actually i want to try to decrypt n53ap sep. Thank you! |
This comment has been minimized.
This comment has been minimized.
$ ./sepsplit sep-firmware.n51.RELEASE.im4p.decrypt Anyone get this problem?(iOS 10.2 firmware) |
This comment has been minimized.
This comment has been minimized.
@Daemonceltics it is not decryted |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Thanks!