Changes

β†’β€ŽTechnical Details: Better function names and formatting cleanup
Line 79: Line 79:  
Looking closely at the release function in STM, here’s what I found:
 
Looking closely at the release function in STM, here’s what I found:
   βˆ’
release_eventhook
+
release_eventhook
βˆ’
                MOV    R12, SP
+
                  MOV    R12, SP
βˆ’
                STMFD  SP!, {R4-R6,R11,R12,LR,PC}
+
                  STMFD  SP!, {R4-R6,R11,R12,LR,PC}
βˆ’
                LDR    R4, =hook_msg
+
                  LDR    R4, =hook_msg
βˆ’
                MOV    R6, R0
+
                  MOV    R6, R0
βˆ’
                SUB    R11, R12, #4
+
                  SUB    R11, R12, #4
βˆ’
                LDR    R0, =aRelease
+
                  LDR    R0, =aRelease
βˆ’
                BL      printf_disabled
+
                  BL      printf_disabled
βˆ’
                LDR    R3, [R4]
+
                  LDR    R3, [R4]
βˆ’
                MOV    R5, #0
+
                  MOV    R5, #0
βˆ’
                CMP    R3, R5
+
                  CMP    R3, R5
βˆ’
                MOVL    R1, -6
+
                  MOVL    R1, -6
βˆ’
                MOV    R0, R6
+
                  MOV    R0, R6
βˆ’
                BEQ    loc_20300C04
+
                  BEQ    loc_20300C04
βˆ’
 
+
βˆ’
loc_20300BD8
+
loc_20300BD8
βˆ’
                STR    R5, [R4]
+
                  STR    R5, [R4]
βˆ’
                MOV    R0, R3
+
                  MOV    R0, R3
βˆ’
                LDR    R3, [R3,#0x18]
+
                  LDR    R3, [R3,#0x18]
βˆ’
                MOV    R1, R5
+
                  MOV    R1, R5
βˆ’
                STR    R5, [R3]
+
                  STR    R5, [R3]
βˆ’
                BL      AckMessage
+
                  BL      AckMessage
βˆ’
                MOV    R0, R6
+
                  MOV    R0, R6
βˆ’
                MOV    R1, R5
+
                  MOV    R1, R5
βˆ’
                BL      AckMessage
+
                  BL      AckMessage
βˆ’
                LDMFD  SP, {R4-R6,R11,SP,LR}
+
                  LDMFD  SP, {R4-R6,R11,SP,LR}
βˆ’
                BX      LR
+
                  BX      LR
βˆ’
 
+
βˆ’
loc_20300C04
+
loc_20300C04
βˆ’
                BL      AckMessage
+
                  BL      AckMessage
βˆ’
                LDR    R3, [R4]
+
                  LDR    R3, [R4]
βˆ’
                B      loc_20300BD8
+
                  B      loc_20300BD8
 
This translates to the following C code:
 
This translates to the following C code:
   βˆ’
struct ios_message {
+
struct ios_message {
βˆ’
    // this isn't exactly right on the IOS side, but it doesn't matter here
+
    // this isn't exactly right on the IOS side, but it doesn't matter here
βˆ’
    u32 command;      // 0x00 = 6 for ioctl
+
    u32 command;      // 0x00 = 6 for ioctl
βˆ’
    s32 result;      // 0x04
+
    s32 result;      // 0x04
βˆ’
    s32 fd;          // 0x08
+
    s32 fd;          // 0x08
βˆ’
    // arguments for ioctl
+
    // arguments for ioctl
βˆ’
    u32 ioctl_number; // 0x0c
+
    u32 ioctl_number; // 0x0c
βˆ’
    void *buffer_in;  // 0x10
+
    void *buffer_in;  // 0x10
βˆ’
    u32 in_size;      // 0x14
+
    u32 in_size;      // 0x14
βˆ’
    void *buffer_out; // 0x18
+
    void *buffer_out; // 0x18
βˆ’
    u32 out_size;    // 0x1c
+
    u32 out_size;    // 0x1c
βˆ’
};
+
};
βˆ’
 
+
βˆ’
struct ios_message *hook_msg;
+
struct ios_message *hook_msg;
βˆ’
 
+
βˆ’
void release_eventhook(ios_message *imm_msg)
+
void release_eventhook(ios_message *imm_msg)
βˆ’
{
+
{
βˆ’
    struct ios_message *the_hook_msg = hook_msg;
+
    struct ios_message *the_hook_msg = hook_msg;
βˆ’
 
+
βˆ’
    printf_disabled("Release\n");
+
    printf_disabled("Release\n");
βˆ’
    if (!the_hook_msg) {
+
    if (!the_hook_msg) {
βˆ’
        AckMessage(imm_msg, -6);
+
        AckMessage(imm_msg, -6);
βˆ’
    }
+
    }
βˆ’
    hook_msg = NULL;
+
    hook_msg = NULL;
βˆ’
    *(u32*)the_hook_msg->buffer_out = 0;
+
    *(u32*)the_hook_msg->buffer_out = 0;
βˆ’
    AckMessage(the_hook_msg, 0);
+
    AckMessage(the_hook_msg, 0);
βˆ’
    AckMessage(imm_msg, 0);
+
    AckMessage(imm_msg, 0);
βˆ’
}
+
}
    
Nintendo forgot a return; statement right at the end of the if(!the_hook_msg) block! This means that if there is no callback registered, it will try to ack the immediate message twice (which does nothing), it will try to ack a NULL message (which the kernel catches and does nothing), but most importantly, it will dereference a NULL structure, get a pointer from it, and write 0 to the address pointed to by that pointer. In other words, that line of code becomes **(u32**)0x18 = 0;, as 0Γ—18 is the offset of buffer_out inside the structure. And 0Γ—18 is an address in low MEM1 that we completely control from the PowerPC. Whoops.
 
Nintendo forgot a return; statement right at the end of the if(!the_hook_msg) block! This means that if there is no callback registered, it will try to ack the immediate message twice (which does nothing), it will try to ack a NULL message (which the kernel catches and does nothing), but most importantly, it will dereference a NULL structure, get a pointer from it, and write 0 to the address pointed to by that pointer. In other words, that line of code becomes **(u32**)0x18 = 0;, as 0Γ—18 is the offset of buffer_out inside the structure. And 0Γ—18 is an address in low MEM1 that we completely control from the PowerPC. Whoops.
Line 163: Line 163:  
But wait, we need to somehow break into the kernel to disable the signature check. How can we do that? Well, it turns out that Nintendo left behind some useful IOS syscalls (within the Starlet core; different from IPC). They look like this:
 
But wait, we need to somehow break into the kernel to disable the signature check. How can we do that? Well, it turns out that Nintendo left behind some useful IOS syscalls (within the Starlet core; different from IPC). They look like this:
   βˆ’
wtf1
+
get_kernel_flavor
βˆ’
                MOVS    R3, #3
+
                  MOVS    R3, #3
βˆ’
                STR    R3, [R0]
+
                  STR    R3, [R0]
βˆ’
                MOVS    R3, #0
+
                  MOVS    R3, #0
βˆ’
                STRH    R3, [R1]
+
                  STRH    R3, [R1]
βˆ’
                BX      LR
+
                  BX      LR
βˆ’
 
+
βˆ’
wtf2
+
get_unk_flavor
βˆ’
                MOVS    R3, #1
+
                  MOVS    R3, #1
βˆ’
                STR    R3, [R0]
+
                  STR    R3, [R0]
βˆ’
                MOVS    R3, #0
+
                  MOVS    R3, #0
βˆ’
                STRH    R3, [R1]
+
                  STRH    R3, [R1]
βˆ’
                BX      LR
+
                  BX      LR
 
Which translates to:
 
Which translates to:
   βˆ’
void wtf1(u32 *a, u16 *b)
+
void get_kernel_flavor(u32 *type, u16 *unknown)
βˆ’
{
+
{
βˆ’
    *a = 3;
+
    *type = 3;
βˆ’
    *b = 0;
+
    *unknown = 0;
βˆ’
}
+
}
βˆ’
 
+
βˆ’
void wtf2(u32 *a, u16 *b)
+
void get_unk_flavor(u32 *type, u16 *unknown)
βˆ’
{
+
{
βˆ’
    *a = 1;
+
    *type = 1;
βˆ’
    *b = 0;
+
    *unknown = 0;
βˆ’
}
+
}
 
These functions appear to be used as configuration for certain global settings, such as whether IOS is monolithic or modular, so they just return constant values by dereferencing their arguments. In any case, there are no permission checks and these calls happily write to any address that you want, with full kernel permissions. We just pass along an address inside the signature check function that we want patched out, and we win.
 
These functions appear to be used as configuration for certain global settings, such as whether IOS is monolithic or modular, so they just return constant values by dereferencing their arguments. In any case, there are no permission checks and these calls happily write to any address that you want, with full kernel permissions. We just pass along an address inside the signature check function that we want patched out, and we win.
  
5,579

edits