Changes

469 bytes added ,  20:49, 25 February 2022
m
recategorized
Line 4: Line 4:  
This bug was discovered by accident, and in fact it is a real honest-to-goodness software bug that is not only exploitable, but a nuisance during regular use. To understand it, you need to understand how STM works.
 
This bug was discovered by accident, and in fact it is a real honest-to-goodness software bug that is not only exploitable, but a nuisance during regular use. To understand it, you need to understand how STM works.
   −
[[IOS#STM|STM]] is the [[IOS]] module in responsible for some hardware functions like handling the fan, “idle” ([[WiiConnect24]]) mode, the front slot LED (including the blink patterns), and the buttons. It has been to referred to as “State-TM” a few times on the Wii. A main function of STM is to provide a way for PowerPC software to get notifications when either the Reset or the Power buttons are pressed. It’s worth noting that it is unknown why they did this –the PowerPC already knows about Reset via the legacy GameCube interface, and can be given direct access to Power including IRQ via the shared GPIO system, and IOS doesn’t use these buttons at all– but they did. It works like this: STM creates two devices, an “immediate” device, and an “event” device. The immediate device is used to issue commands to STM that take effect immediately, while the event device is the callback mechanism. The PowerPC code issues an IOS_IoctlAsync() call on the “event” device, and this call blocks (asynchronously) until there is an event (such as a button press). When this happens, the call returns with the event code, and the PowerPC code reissues it to listen for further events.
+
[[:/dev/stm|STM]] (State Transition Manager) is the [[IOS]] module in responsible for some hardware functions like handling the fan, “idle” ([[WiiConnect24]]) mode, the front slot LED (including the blink patterns), and the front-panel buttons. A main function of STM is to provide a way for PowerPC software to get notifications when either the Reset or the Power buttons are pressed. It’s worth noting that it is unknown why they did this –the PowerPC already knows about Reset via the legacy GameCube interface, and can be given direct access to Power including IRQ via the shared GPIO system, and IOS doesn’t use these buttons at all– but they did. It works like this: STM creates two devices, an “immediate” device, and an “event” device. The immediate device is used to issue commands to STM that take effect immediately, while the event device is the callback mechanism. The PowerPC code issues an IOS_IoctlAsync() call on the “event” device, and this call blocks (asynchronously) until there is an event (such as a button press). When this happens, the call returns with the event code, and the PowerPC code reissues it to listen for further events.
    
One problem with this approach is that the PowerPC needs a way to shut down the event callback. The IOS [[Hardware/IPC|IPC]] mechanism doesn’t provide a way for the PowerPC to cancel an ongoing request; it must wait until its completion. When PowerPC code needs to hand off execution, it needs to clean up all references and file descriptors to IOS, so it needs a way to get rid of the event call. STM implements this by having a call on the immediate interface that forces the event call to return with a zero event code. So far so good. If you’re interested, check out stm.c on libogc (particularly the functions with EventHook in the name).
 
One problem with this approach is that the PowerPC needs a way to shut down the event callback. The IOS [[Hardware/IPC|IPC]] mechanism doesn’t provide a way for the PowerPC to cancel an ongoing request; it must wait until its completion. When PowerPC code needs to hand off execution, it needs to clean up all references and file descriptors to IOS, so it needs a way to get rid of the event call. STM implements this by having a call on the immediate interface that forces the event call to return with a zero event code. So far so good. If you’re interested, check out stm.c on libogc (particularly the functions with EventHook in the name).
Line 10: Line 10:  
In order to better understand the mechanism, it’s worth looking at the individual messages as they are exchanged with IOS. Here’s what it might look like:
 
In order to better understand the mechanism, it’s worth looking at the individual messages as they are exchanged with IOS. Here’s what it might look like:
   −
PowerPCIOS
+
{| class="wikitable"
Initializing STM
+
|-
open(path=/dev/stm/immediate”)
+
! PowerPC
open() fd = 1
+
! IOS
open(path=/dev/stm/eventhook”)
+
|-
open() fd = 2
+
|  colspan="2" style="text-align: center;" | Initializing STM
ioctl(fd=2, num=EVENTHOOK, evbuf=0×12345600)
+
|-
Time passes, user presses button
+
| open(path="[[:/dev/stm/immediate]]")
Write event code to 0×12345600
+
|
ioctl(fd=2) result = 0
+
|-
Read event code from 0×12345600
+
|
ioctl(fd=2, num=EVENTHOOK, evbuf=0×12345600)
+
| open() fd = 1
Time passes, software decides to shut down STM
+
|-
ioctl(fd=1, num=RELEASE)
+
| open(path="[[:/dev/stm/eventhook]]")
Write 0 event code to 0×12345600
+
|
ioctl(fd=2) result = 0
+
|-
ioctl(fd=1) result = 0
+
|
close(2)
+
|open() fd = 2
close(2) result = 0
+
|-
close(1)
+
| ioctl(fd=2, num=EVENTHOOK, evbuf=0×12345600)
close(1) result = 0
+
|
 +
|-
 +
| colspan="2" style="text-align: center;" | Time passes, user presses button
 +
|-
 +
|
 +
| Write event code to 0×12345600  
 +
|-
 +
|
 +
| ioctl(fd=2) result = 0
 +
|-
 +
| Read event code from 0×12345600
 +
|
 +
|-
 +
| ioctl(fd=2, num=EVENTHOOK, evbuf=0×12345600)
 +
|
 +
|-
 +
| colspan="2" style="text-align: center;" | Time passes, software decides to shut down STM
 +
|-
 +
| ioctl(fd=1, num=RELEASE)
 +
|
 +
|-
 +
| Write 0 event code to 0×12345600
 +
|
 +
|-
 +
|
 +
| ioctl(fd=2) result = 0
 +
|-
 +
|
 +
| ioctl(fd=1) result = 0
 +
|-
 +
| close(2)
 +
|
 +
|-
 +
|
 +
| close(2) result = 0
 +
|-
 +
| close(1)
 +
|
 +
|-
 +
|
 +
| close(1) result = 0
 +
|}
    
Things didn’t work well when using the [[Twilight Hack]] because Zelda’s STM eventhook was still active, and STM won’t let you register a new one. So an STM eventhook release was added to the Twilight Hack. One slight issue is that we can’t know if there was an old eventhook or not, depending on what the state of the machine was (since the Twilight Hack can be relaunched from software, as an SD loader of sorts, and this was popular in the early days), so we just make it attempt to release the eventhook always. This is fine, as the release function will return an error if there is no eventhook active.
 
Things didn’t work well when using the [[Twilight Hack]] because Zelda’s STM eventhook was still active, and STM won’t let you register a new one. So an STM eventhook release was added to the Twilight Hack. One slight issue is that we can’t know if there was an old eventhook or not, depending on what the state of the machine was (since the Twilight Hack can be relaunched from software, as an SD loader of sorts, and this was popular in the early days), so we just make it attempt to release the eventhook always. This is fine, as the release function will return an error if there is no eventhook active.
Line 38: 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 120: Line 161:  
The exploit is quite simple: we simply find the address of the stack location that contains the return address for the function (LR), and write it to 0×18. Then we release the STM callback twice. The second time around, STM zeroes out the return address and the function returns to execute code at address 0. We place our own code there, and clean up afterwards by jumping to the real return location, so STM keeps on running happily.
 
The exploit is quite simple: we simply find the address of the stack location that contains the return address for the function (LR), and write it to 0×18. Then we release the STM callback twice. The second time around, STM zeroes out the return address and the function returns to execute code at address 0. We place our own code there, and clean up afterwards by jumping to the real return location, so STM keeps on running happily.
   −
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. 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.
   Line 163: Line 204:  
On October 23rd, 2008, Nintendo released an update that fixed the [[signing bug]] in every publicly-known IOS, disabling any direct methods to install unofficial content on all updated Wiis. Team Twiizers decided to take the opportunity to use one of their stockpiled IOS exploits to get the [[Homebrew Channel]] working with this update. These stockpiled exploits differ from fakesigning in that they directly exploit the IOS runtime, injecting code that lets us take control and disable signatures altogether. Therefore, this was the first released IOS code execution exploit.
 
On October 23rd, 2008, Nintendo released an update that fixed the [[signing bug]] in every publicly-known IOS, disabling any direct methods to install unofficial content on all updated Wiis. Team Twiizers decided to take the opportunity to use one of their stockpiled IOS exploits to get the [[Homebrew Channel]] working with this update. These stockpiled exploits differ from fakesigning in that they directly exploit the IOS runtime, injecting code that lets us take control and disable signatures altogether. Therefore, this was the first released IOS code execution exploit.
   −
In order to hinder Nintendo’s attempts at fixing it, and to avoid misuse by warez kiddies, sven and I had a lot of fun obfuscating the exploit over a couple afternoons. We decided not to release information about it, hoping it would last long enough to be useful for future installers and BootMii.
+
In order to hinder Nintendo’s attempts at fixing it, and to avoid misuse by warez kiddies, sven and marcan had a lot of fun obfuscating the exploit over a couple afternoons. We decided not to release information about it, hoping it would last long enough to be useful for future installers and BootMii.
   −
The [[System Menu 4.0]] update eliminated the STM Release Exploit, but it was still left undocumented. Team Twiizers eventually proposed a challenge: the exploit would be documented when someone “broke” the obfuscation and figured out how the exploit works. The intent was to promote reverse engineering and also see just how long it would take people to crack it. An anonymous hacker successfully reverse engineered our layers of obfuscation.
+
The [[System Menu 4.0]] update eliminated the STM Release Exploit, but it was still left undocumented. Team Twiizers eventually proposed a challenge: the exploit would be documented when someone “broke” the obfuscation and figured out how the exploit works. The intent was to promote reverse engineering and also see just how long it would take people to crack it. An anonymous hacker successfully reverse engineered their layers of obfuscation.
    
==Sources==
 
==Sources==
[http://hackmii.com/2010/01/the-stm-release-exploit/The STM Release Exploit - HackMii]
+
[http://hackmii.com/2010/01/the-stm-release-exploit/ The STM Release Exploit - HackMii]
   −
[[Category:Exploits]]
+
[[Category:IOS exploits]]
[[Category:IOS Exploits]]
 
5,579

edits