Code: Select all
/* Copyright 2013 tueidj All Rights Reserved
* This code may not be used in any project
* without explicit permission from the author.
*/
Most people know IOS runs on its own dedicated CPU - an ARM926EJ-S commonly known as Starlet. For the majority of the time, Starlet sits in an idle loop waiting for interrupts - why not give it some real work to do by making IOS run some custom code?
First you need to be able to modify the existing IOS code. This requires the MEM2 protection to be disabled, which needs AHBPROT disabled. HBC can take of AHBPROT and the rest is fairly trivial.
Since the ES module is the only part of IOS that is "allowed" to load a new module, we're going to modify one of its ioctls. Choosing which one is pretty simple; it should be one that isn't used under normal conditions, it should have uniquely identifiable code (so we can find where it is in memory) and there should be plenty of space for us to overwrite with our own code. ES_ImportBoot2 fits these requirements - it's used to upgrade boot2 which no regular application will want to do.
After deciding which ioctl to overwrite all we need to do is write our custom IOS module to the wii's NAND (the /tmp directory is a good choice) and use the following function:
Code: Select all
#define ES_MODULE_START ((u8*)0x939F0000)
#define ES_MODULE_SIZE 0x20000
static int load_module_file(s32 es_fd, const char *filename)
{
// this is the original code for the ES_ImportBoot2 ioctl
const u8 old_es_code[24] = {
0x68, 0x4B, 0x2B, 0x06, 0xD1, 0x0C, 0x68, 0x8B, 0x2B, 0x00,
0xD1, 0x09, 0x68, 0xC8, 0x68, 0x42, 0x23, 0xA9, 0x00, 0x9B,
0x42, 0x9A, 0xD1, 0x03};
// this is the code that will overwrite it
const u16 load_module[12] =
{
0x68CE, // ldr r6, [r1, #0x0c] ; ipcmessage.vec
0x6830, // ldr r0, [r6] ; vec[0].data (filename of module)
0x4778, // thumb->arm (BX PC)
0x46C0, // nop (for alignment, assumes this block starts 4-byte aligned)
0xE600, 0x0B50, // syscall_5a (load_ios_module)
0xE28D, 0xD020, // ADD SP, SP, #0x20
0xE8BD, 0x4070, // LDMFD SP!,{R4-R6,LR}
0xE12F, 0xFF1E, // BX LR
};
u8 *addr;
s32 ret=1;
ioctlv vec;
for (addr=ES_MODULE_START;addr < ES_MODULE_START+ES_MODULE_SIZE-sizeof(old_es_code);addr++) {
if (!memcmp(addr, old_es_code, sizeof(old_es_code))) {
memcpy(addr, load_module, sizeof(load_module));
DCFlushRange((void*)((u32)addr&~0x1F), 32);
vec.data = (void*)filename;
vec.len = strlen(filename)+1;
// call ES_ImportBoot2 with the module filename as a parameter
ret = IOS_Ioctlv(es_fd, 0x1F, 1, 0, &vec);
// restore the old code and flush
memcpy(addr, old_es_code, sizeof(load_module));
DCFlushRange((void*)((u32)addr&~0x1F), 32);
break;
}
}
return !ret;
}