#include #include #include #include #include #include #include #include #include "common.h" #include "lzma.h" #include "elf_abi.h" #define ALIGNOFFSET 32 extern int stub_mini_elf_len; extern char stub_mini_elf[]; extern int stub_mini_debug_elf_len; extern char stub_mini_debug_elf[]; extern int stub_dkf_elf_len; extern char stub_dkf_elf[]; extern int stub_dkf_debug_elf_len; extern char stub_dkf_debug_elf[]; extern int stub_dkfc_elf_len; extern char stub_dkfc_elf[]; extern int stub_dkfc_debug_elf_len; extern char stub_dkfc_debug_elf[]; typedef struct { const char *name; const int *len; const u8 *data; } stub_t; static const stub_t stubs[] = { { "mini", &stub_mini_elf_len, (u8 *) stub_mini_elf }, { "mini_debug", &stub_mini_debug_elf_len, (u8 *) stub_mini_debug_elf }, { "devkitfail", &stub_dkf_elf_len, (u8 *) stub_dkf_elf }, { "devkitfail_debug", &stub_dkf_debug_elf_len, (u8 *) stub_dkf_debug_elf }, { "dkfailchannel", &stub_dkfc_elf_len, (u8 *) stub_dkfc_elf }, { "dkfailchannel_debug", &stub_dkfc_debug_elf_len, (u8 *) stub_dkfc_debug_elf }, { NULL, NULL, NULL } }; typedef struct { u8 *data; u32 len; Elf32_Ehdr *ehdr; Elf32_Phdr *phdrs; Elf32_Shdr *shdrs; } elf_t; typedef struct { u32 dataptr; u32 len_in; u32 len_out; u8 props[LZMA_PROPS_SIZE]; } __attribute__((packed)) payload_t; static inline u8 *phdr_data(const elf_t *elf, const u16 ndx, const u32 off) { return &elf->data[be32(elf->phdrs[ndx].p_offset) + off]; } static elf_t *read_stub(const char *name) { elf_t *elf = (elf_t *) calloc(1, sizeof(elf_t)); if (!elf) die("Error allocating %u bytes", (u32) sizeof(elf_t)); int i = 0; while (stubs[i].name) { if (!strcmp(name, stubs[i].name)) { printf("Using stub '%s'\n", name); elf->len = *(stubs[i].len); elf->data = (u8 *) stubs[i].data; return elf; } ++i; } die("Unknown stub '%s'", name); return NULL; } static elf_t *read_elf(const char *filename) { int fd; struct stat st; elf_t *elf; printf("Reading %s\n", filename); fd = open(filename, O_RDONLY); if (fd < 0) perrordie("Could not open ELF file"); if (fstat(fd, &st)) perrordie("Could not stat ELF file"); if ((u32) st.st_size < sizeof(Elf32_Ehdr)) die("File too short for ELF"); elf = (elf_t *) calloc(1, sizeof(elf_t)); if (!elf) die("Error allocating %u bytes", (u32) sizeof(elf_t)); elf->len = st.st_size; elf->data = (u8 *) malloc(elf->len); if (!elf->data) die("Error allocating %u bytes", elf->len); if (read(fd, elf->data, elf->len) != elf->len) perrordie("Could not read from file"); close(fd); return elf; } static void write_elf(const char *filename, const elf_t *elf) { int fd; fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0755); if (fd < 0) perrordie("Could not open ELF file"); if (write(fd, elf->data, elf->len) != elf->len) perrordie("Could not write ELF file"); close(fd); } static void free_elf(elf_t *elf) { free(elf->data); free(elf); } static void check_elf(elf_t *elf) { if (elf->len < sizeof(Elf32_Phdr)) die("Too short for an ELF"); Elf32_Ehdr *ehdr = (Elf32_Ehdr *) elf->data; if (!IS_ELF(*ehdr)) die("Not an ELF"); if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) die("Invalid ELF class"); if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) die("Invalid ELF byte order"); if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) die("Invalid ELF ident version"); if (be16(ehdr->e_type) != ET_EXEC) die("ELF is not an executable"); if (be16(ehdr->e_machine) != EM_PPC) die("Machine is not PowerPC"); if (be32(ehdr->e_version) != EV_CURRENT) die("Invalid ELF version"); if (!be32(ehdr->e_entry)) die("ELF has no entrypoint"); } static void init_elf(elf_t *elf, int sections) { elf->ehdr = (Elf32_Ehdr *) elf->data; u16 num = be16(elf->ehdr->e_phnum); u32 off = be32(elf->ehdr->e_phoff); if (!num || !off) die("ELF has no program headers"); if (be16(elf->ehdr->e_phentsize) != sizeof(Elf32_Phdr)) die("Invalid program header entry size"); if ((off + num * sizeof(Elf32_Phdr)) > elf->len) die("Program headers out of bounds"); elf->phdrs = (Elf32_Phdr *) &elf->data[off]; num = be16(elf->ehdr->e_shnum); off = be32(elf->ehdr->e_shoff); if (!num || !off) { if (!sections) { elf->shdrs = NULL; return; } die("ELF has no section headers"); } if (be16(elf->ehdr->e_shentsize) != sizeof(Elf32_Shdr)) die("Invalid section header entry size"); if ((off + num * sizeof(Elf32_Shdr)) > elf->len) die("Section headers out of bounds"); elf->shdrs = (Elf32_Shdr *) &elf->data[off]; } static u32 find_payload_offset(const elf_t *elf) { u16 shnum = be16(elf->ehdr->e_shnum); u16 shstrndx = be16(elf->ehdr->e_shstrndx); if (!shstrndx || shstrndx > shnum) die("Invalid .shstrtab index"); u32 off = be32(elf->shdrs[shstrndx].sh_offset); u32 size = be32(elf->shdrs[shstrndx].sh_size); if (off + size > elf->len - 1) die(".shstrtab section out of bounds"); const char *shstr = (const char *) &elf->data[off]; u16 i; for (i = 0; i < shnum; ++i) { off = be32(elf->shdrs[i].sh_name); if (off > size) die("Section #%u name out of .shstrtab bounds", i); if (!strcmp(&shstr[off], ".payload")) { printf(".payload section found: #%u\n", i); return be32(elf->shdrs[i].sh_offset); } } die(".payload section not present"); } static void map_file_offset(const elf_t *elf, const u32 offset, u16 *phdrndx, u32 *phdroff) { u16 phnum = be16(elf->ehdr->e_phnum); u16 i; for (i = 0; i < phnum; ++i) { if (be32(elf->phdrs[i].p_type) != PT_LOAD) continue; if (be32(elf->phdrs[i].p_filesz) < 1) continue; u32 poff = be32(elf->phdrs[i].p_offset); u32 psize = be32(elf->phdrs[i].p_filesz); if (offset >= poff && offset <= poff + psize) { *phdrndx = i; *phdroff = offset - poff; printf("Mapped payload to program header [%u] 0x%06x\n", *phdrndx, *phdroff); return; } } die("File offset 0x%x is not part of any PT_LOAD program header", offset); } static elf_t *strip_elf(const elf_t *elf, u16 *phdrndx) { elf_t *out; u32 pos; u16 count; u16 phnum = be16(elf->ehdr->e_phnum); u16 i; count = 0; pos = round_up(sizeof(Elf32_Ehdr), ALIGNOFFSET); for (i = 0; i < phnum; ++i) { if (be32(elf->phdrs[i].p_type) != PT_LOAD) continue; if (be32(elf->phdrs[i].p_filesz) < 1) continue; if (be32(elf->phdrs[i].p_memsz) < 1) continue; pos += round_up(be32(elf->phdrs[i].p_filesz), ALIGNOFFSET); count++; } pos += round_up(count * sizeof(Elf32_Phdr), ALIGNOFFSET); if (pos > 20 * 1024 * 1024) die("ELF too big, even after stripping (0x%x)", pos); printf("Stripping ELF from 0x%x to 0x%x bytes\n", elf->len, pos); out = (elf_t *) calloc(1, sizeof(elf_t)); if (!out) die("Error allocating %u bytes", (u32) sizeof(elf_t)); out->len = pos; out->data = calloc(1, pos); if (!out->data) die("Error allocating %u bytes", pos); out->ehdr = (Elf32_Ehdr *) out->data; pos = round_up(sizeof(Elf32_Ehdr), ALIGNOFFSET); out->ehdr->e_ident[EI_MAG0] = ELFMAG0; out->ehdr->e_ident[EI_MAG1] = ELFMAG1; out->ehdr->e_ident[EI_MAG2] = ELFMAG2; out->ehdr->e_ident[EI_MAG3] = ELFMAG3; out->ehdr->e_ident[EI_CLASS] = ELFCLASS32; out->ehdr->e_ident[EI_DATA] = ELFDATA2MSB; out->ehdr->e_ident[EI_VERSION] = EV_CURRENT; out->ehdr->e_type = be16(ET_EXEC); out->ehdr->e_machine = be16(EM_PPC); out->ehdr->e_version = be32(EV_CURRENT); out->ehdr->e_entry = elf->ehdr->e_entry; out->ehdr->e_phoff = be32(pos); out->ehdr->e_phentsize = be16(sizeof(Elf32_Phdr)); out->ehdr->e_phnum = be16(count); out->ehdr->e_shentsize = be16(sizeof(Elf32_Shdr)); out->phdrs = (Elf32_Phdr *) &out->data[pos]; pos += round_up(count * sizeof(Elf32_Phdr), ALIGNOFFSET); count = 0; int found = 0; for (i = 0; i < phnum; ++i) { if (be32(elf->phdrs[i].p_type) != PT_LOAD) continue; if (be32(elf->phdrs[i].p_filesz) < 1) continue; if (be32(elf->phdrs[i].p_memsz) < 1) continue; if (phdrndx && i == *phdrndx) { *phdrndx = count; found = 1; } out->phdrs[count].p_type = elf->phdrs[i].p_type; out->phdrs[count].p_offset = be32(pos); out->phdrs[count].p_vaddr = elf->phdrs[i].p_vaddr; out->phdrs[count].p_paddr = elf->phdrs[i].p_paddr; out->phdrs[count].p_filesz = elf->phdrs[i].p_filesz; out->phdrs[count].p_memsz = elf->phdrs[i].p_memsz; out->phdrs[count].p_flags = elf->phdrs[i].p_flags; out->phdrs[count].p_align = elf->phdrs[i].p_align; u32 p_offset = be32(elf->phdrs[i].p_offset); u32 p_filesz = be32(elf->phdrs[i].p_filesz); printf(" PHDR[%u] 0x%08x 0x%06x -> [%u] 0x%06x\n", i, p_offset, p_filesz, count, pos); memcpy(&out->data[pos], &elf->data[p_offset], p_filesz); pos += round_up(p_filesz, ALIGNOFFSET); count++; } if (phdrndx && !found) die("PHDR #%u not part of the stripped ELF", *phdrndx); return out; } static elf_t *inject_elf(elf_t *dst, const u8 *src, const u32 len, u32 *dataaddr, u8 **dataptr) { u16 phdrndx = be16(dst->ehdr->e_phnum) - 1; Elf32_Phdr *phdr = &dst->phdrs[phdrndx]; if (phdr->p_filesz != phdr->p_memsz) die("File size does not match the memory size for the last PHDR"); u32 pos = be32(phdr->p_vaddr) + be32(phdr->p_filesz); u32 pos_a = round_up(pos, ALIGNOFFSET); u32 pos_d = pos_a - pos; u32 size_d = pos_d + round_up(len, ALIGNOFFSET); printf("Injecting payload in PHDR %u, size += 0x%x (0x%x/0x%x/0x%x)\n", phdrndx, size_d, pos_d, len, size_d - pos_d - len); elf_t *elf = (elf_t *) calloc(1, sizeof(elf_t)); if (!elf) die("Error allocating %u bytes", (u32) sizeof(elf_t)); elf->data = calloc(1, dst->len + size_d); elf->len = dst->len + size_d; if (!elf->data) die("Failed to alloc 0x%x bytes", dst->len + size_d); memcpy(elf->data, dst->data, dst->len); init_elf(elf, 0); phdr = &elf->phdrs[phdrndx]; u8 *p = phdr_data(elf, phdrndx, be32(phdr->p_filesz)); memcpy(&p[pos_d], src, len); *dataaddr = pos_a - be32(phdr->p_vaddr) + be32(phdr->p_paddr); *dataaddr |= 0x80000000; phdr->p_filesz = be32(be32(phdr->p_filesz) + size_d); phdr->p_memsz = be32(be32(phdr->p_memsz) + size_d); phdr->p_vaddr = phdr->p_paddr; printf("Payload blob @0x%x at runtime\n", pos_a); *dataptr = &p[pos_d]; return elf; } #define CLOCKS_PER_BYTE 47 #define BUF_SIZE 256 #define ROUNDS 256 static void usage(const char *name) { printf("usage: %s [-s stub] in.elf out.elf\n", name); printf("stubs:"); int i = 0; while (stubs[i].name) { printf(" %s", stubs[i].name); ++i; } printf("\n"); exit(1); } int main(int argc, char *argv[]) { elf_t *elf_tmp, *elf_pl, *elf_stub; u32 offset; u16 phdrndx; u32 phdroff; u32 dataaddr; u8 *dataptr; lzma_t *lzma; printf("wiipax v0.2 (c) 2009 Team Twiizers\n\n"); if (argc < 3) usage(argv[0]); char *stubname = "mini"; char **arg = &argv[1]; argc--; while (argc && *arg[0] == '-') { if (!strcmp(*arg, "-h")) { usage(argv[0]); } else if (!strcmp(*arg, "-s")) { if (argc < 2) usage(argv[0]); arg++; argc--; stubname = *arg; } else if (!strcmp(*arg, "--")) { arg++; argc--; break; } else { die("Unrecognized option %s\n", *arg); usage(argv[0]); } arg++; argc--; } if (argc != 2) usage(argv[0]); elf_tmp = read_stub(stubname); check_elf(elf_tmp); init_elf(elf_tmp, 1); offset = find_payload_offset(elf_tmp); map_file_offset(elf_tmp, offset, &phdrndx, &phdroff); elf_stub = strip_elf(elf_tmp, &phdrndx); free(elf_tmp); elf_tmp = read_elf(arg[0]); check_elf(elf_tmp); init_elf(elf_tmp, 0); elf_pl = strip_elf(elf_tmp, NULL); free_elf(elf_tmp); lzma = lzma_compress(elf_pl->data, elf_pl->len); // test decoding lzma_decode(lzma, elf_pl->data); free_elf(elf_pl); #if 0 lzma_write("x.lzma", lzma); #endif u32 aes_len = (lzma->len_out + 15) & (~15); u8 aiv[16]; memset(aiv, 0, 16); lzma->data = realloc(lzma->data, aes_len); memset(lzma->data + lzma->len_out, 0, aes_len - lzma->len_out); elf_tmp = inject_elf(elf_stub, lzma->data, aes_len, &dataaddr, &dataptr); free_elf(elf_stub); payload_t *pl = (payload_t *) phdr_data(elf_tmp, phdrndx, phdroff); pl->dataptr = be32(dataaddr); pl->len_in = be32(lzma->len_out); pl->len_out = be32(lzma->len_in); memcpy(pl->props, lzma->props, LZMA_PROPS_SIZE); lzma_free(lzma); write_elf(arg[1], elf_tmp); free_elf(elf_tmp); printf("Done.\n"); return 0; }