Skip to content

Instantly share code, notes, and snippets.

@svenpeter42
Created May 30, 2021 09:59
Show Gist options
  • Save svenpeter42/d0253c53ac6a8f03211cf1decc38e218 to your computer and use it in GitHub Desktop.
Save svenpeter42/d0253c53ac6a8f03211cf1decc38e218 to your computer and use it in GitHub Desktop.
mini ios.c
/*
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