Created
May 30, 2021 09:59
-
-
Save svenpeter42/d0253c53ac6a8f03211cf1decc38e218 to your computer and use it in GitHub Desktop.
mini ios.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
mini - a Free Software replacement for the Nintendo/BroadOn IOS. | |
Copyright (C) 2008, 2009 Sven Peter <sven@svenpeter.dev> | |
Copyright (C) 2009-2010 Andre Heider "dhewg" <dhewg@wiibrew.org> | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, version 2. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
*/ | |
#include "string.h" | |
#include "utils.h" | |
#include "gecko.h" | |
#include "boot2.h" | |
#include "nandfs.h" | |
#include "crypto.h" | |
#include "sha1.h" | |
#include "ios.h" | |
#include "../minimem.h" | |
// biggest ios file seen: | |
// ls -lLSr 00000001000000??/*.dec|grep -v 0000000100000002 | |
// 1623548 0000000100000016/0000000F.dec | |
#define MAX_FILESIZE (2 * 1024 * 1024) | |
// sync with installer/checks.c | |
static u32 __dpki = 0; | |
static struct nandfs_fp __fp; | |
static u8 *__buf_map = (u8 *) 0x10f0f000; | |
static tmd_t *__tmd = (tmd_t *) 0x10e0f000; | |
static u8 *__buf_content = (u8 *) 0x10100000; | |
static u32 __map_entries = 0; | |
static char __filename[NANDFS_MAXPATH_LEN] MEM2_BSS; | |
static struct __patch { | |
const u8 *pattern; | |
const u32 patlen; | |
const u8 *patch; | |
const u32 patchlen; | |
u32 hits; | |
} __patches[] = { | |
{ | |
// strncmp bug | |
(u8 *) "\x20\x07\x4b\x0b", | |
4, | |
(u8 *) "\x00", | |
1, | |
0 | |
}, | |
// .... | |
}; | |
#ifdef NDEBUG | |
#define printhdr(...) | |
#else | |
static void printhdr(ioshdr *hdr) { | |
gecko_printf("ARMBOOT header (@%p):\n",hdr); | |
gecko_printf(" Header size: %08x\n", hdr->hdrsize); | |
gecko_printf(" Loader size: %08x\n", hdr->loadersize); | |
gecko_printf(" ELF size: %08x\n", hdr->elfsize); | |
gecko_printf(" Argument: %08x\n", hdr->argument); | |
gecko_printf(" ELF at %p\n", (u8 *) hdr + hdr->hdrsize + hdr->loadersize); | |
} | |
#endif | |
static s32 __ios_fetch(const char *filename, void *buf) { | |
if (nandfs_open(&__fp, filename) < 0) { | |
gecko_printf("file '%s' not found\n", filename); | |
return -1; | |
} | |
if (__fp.size > MAX_FILESIZE) { | |
gecko_printf("file too big: %u\n", __fp.size); | |
return -1; | |
} | |
if (nandfs_read(buf, __fp.size, &__fp) < 0) { | |
gecko_printf("error reading '%s'\n", filename); | |
return -1; | |
} | |
return 0; | |
} | |
static s32 __ios_get_tmd(u32 version) { | |
sprintf(__filename, "/title/00000001/%08x/content/title.tmd", version); | |
if (__ios_fetch(__filename, __tmd) < 0) | |
return -1; | |
return 0; | |
} | |
static s32 __ios_get_shared_filename(const u8 *hash) { | |
u32 i; | |
u8 *p = __buf_map; | |
u8 *hit = NULL; | |
for (i = 0; i < __map_entries; ++i) { | |
if (!memcmp(p + 8, hash, 20)) { | |
hit = p; | |
break; | |
} | |
p += 28; | |
} | |
if (!hit) { | |
gecko_printf("no entry for hash in map\n"); | |
return -1; | |
} | |
strlcpy(__filename, "/shared1/", NANDFS_MAXPATH_LEN); | |
memcpy(__filename + 9, hit, 8); | |
__filename[17] = 0; | |
strlcat(__filename, ".app", NANDFS_MAXPATH_LEN); | |
return 0; | |
} | |
static s32 __ios_verify(void) { | |
tmd_content_t *contents = &__tmd->contents; | |
u32 i; | |
u8 sha[20]; | |
for (i = 0; i < __tmd->num_contents; ++i) { | |
if (contents[i].type & 0x8000) { | |
if (__ios_get_shared_filename(contents[i].hash) < 0) | |
return -1; | |
} else { | |
sprintf(__filename, "/title/00000001/%08x/content/%08x.app", | |
(u32) __tmd->title_id & 0xff, contents[i].cid); | |
} | |
memset(__buf_content, 0, MAX_FILESIZE); | |
if (__ios_fetch(__filename, __buf_content) < 0) | |
return -1; | |
gecko_printf("verifying hash of %s\n", __filename); | |
memset(sha, 0, 20); | |
SHA1(__buf_content, contents[i].size, sha); | |
if (memcmp(contents[i].hash, sha, 20)) { | |
#ifndef NDEBUG | |
gecko_printf("hash check failed\n"); | |
gecko_printf("is:\n"); | |
hexdump(sha, 20); | |
gecko_printf("should be:\n"); | |
hexdump(contents[i].hash, 20); | |
#endif | |
return -1; | |
} | |
} | |
return 0; | |
} | |
static s32 __ios_patch(u32 size, u32 dryrun) { | |
u32 i, j; | |
u8 *p = __buf_content; | |
u32 offset = 0; | |
if (dryrun) | |
for (i = 0; i < sizeof(__patches) / sizeof(struct __patch); ++i) | |
__patches[i].hits = 0; | |
for (i = 0; i < size; ++i) { | |
for (j = 0; j < sizeof(__patches) / sizeof(struct __patch); ++j) { | |
if (offset + __patches[j].patlen > size) | |
continue; | |
if (!memcmp(p, __patches[j].pattern, __patches[j].patlen)) { | |
gecko_printf("pattern #%u @0x%x\n", j, offset); | |
if (dryrun) | |
__patches[j].hits++; | |
else | |
memcpy(p, __patches[j].patch, __patches[j].patchlen); | |
} | |
} | |
p++; | |
offset++; | |
} | |
if (dryrun) { | |
if (__dpki != 1) | |
if (__patches[0].hits + __patches[1].hits != 1) | |
return -1; | |
if (__patches[2].hits != 1) | |
return -1; | |
return -0; | |
} | |
return 0; | |
} | |
static u32 __ios_load(u32 version) { | |
if (__ios_get_tmd(version) < 0) | |
return 0; | |
gecko_printf("got tmd for ios %u 0x%04x\n", version, __tmd->title_version); | |
if (!(((u32 *) __tmd)[1]) || !(((u32 *) __tmd)[2])) { | |
gecko_printf("ignoring fakesigned tmd\n"); | |
return 0; | |
} | |
if ((__tmd->title_version & 0xff) == 0) { | |
gecko_printf("ignoring stub\n"); | |
return 0; | |
} | |
if (__ios_verify() < 0) | |
return 0; | |
tmd_content_t *contents = &__tmd->contents; | |
if (__ios_get_shared_filename(contents[__tmd->boot_index].hash) < 0) | |
return 0; | |
gecko_printf("shared kernel '%s'\n", __filename); | |
if (__ios_fetch(__filename, __buf_content) < 0) | |
return 0; | |
gecko_printf("got kernel!\n"); | |
return (version << 16) | __tmd->title_version; | |
} | |
s32 ios_init(void) { | |
if ((seeprom.ms_id == 3) && (seeprom.ca_id == 2)) | |
__dpki = 1; | |
if (__ios_fetch("/shared1/content.map", __buf_map) < 0) | |
return -1; | |
__map_entries = __fp.size / 28; | |
return 0; | |
} | |
ioshdr *ios_reload(u32 version, u32 *value3140) { | |
u32 ret = __ios_load(version); | |
if (!ret) | |
return NULL; | |
*value3140 = ret; | |
printhdr((ioshdr *) __buf_content); | |
return (ioshdr *) __buf_content; | |
} | |
// saves time when HBC IOS == installer IOS | |
u32 hbc_loaded_ios = 0; | |
u32 hbc_load_result = 0; | |
int ios_checkhbc(void) { | |
u32 ret; | |
u32 addr = MINI_LIST_HBC; | |
u32 val; | |
while ((val = read32(addr))) { | |
if (val == 0xff) { | |
addr += 4; | |
continue; | |
} | |
gecko_printf("Trying IOS %d for HBC\n", val); | |
ret = __ios_load(val); | |
if (!ret) { | |
gecko_printf("IOS %d is dodgy or broken\n", val); | |
write32(addr, 0xff); | |
addr += 4; | |
continue; | |
} | |
hbc_loaded_ios = val; | |
hbc_load_result = ret; | |
gecko_printf("HBC IOS load result: %x\n", ret); | |
write32(MINI_RESULT_HBC, ret); | |
return 1; | |
} | |
return 0; | |
} | |
ioshdr *ios_load(u32 *value3140) { | |
u32 ret; | |
u32 addr = MINI_LIST_INSTL; | |
u32 val; | |
while ((val = read32(addr))) { | |
if (val == 0xff) { | |
addr += 4; | |
continue; | |
} | |
gecko_printf("Trying IOS %d for loading\n", val); | |
if (hbc_loaded_ios && hbc_load_result && hbc_loaded_ios == val) { | |
ret = hbc_load_result; | |
hbc_loaded_ios = 0; | |
hbc_load_result = 0; | |
} else { | |
ret = __ios_load(val); | |
} | |
if (!ret) { | |
gecko_printf("IOS %d is dodgy or broken\n", val); | |
write32(addr, 0xff); | |
addr += 4; | |
continue; | |
} | |
if (__ios_patch(__fp.size, 1) < 0) { | |
gecko_printf("kernel patch dryrun fail\n"); | |
write32(addr, 0xff); | |
addr += 4; | |
continue; | |
} | |
__ios_patch(__fp.size, 0); | |
*value3140 = ret; | |
printhdr((ioshdr *) __buf_content); | |
return (ioshdr *) __buf_content; | |
} | |
return NULL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment