EurAsiayour console hacking resource
Select topic
  Create an account Home  ·  Your Account  ·  Online Shop  ·  Forums  ·  Downloads new  ·  Wiki  
Main Menu
· Home
· About Us
· Downloads
· Forums
· Info Pages
· Members List
· Online Shop
· PDA - AvantGo
· Private Messages
· Search Stories
· Statistics
· Stories Archive
· Submit Story
· Top 10
· Topics
· Upload
· Web Links
· Wiki
· Your Account
· Switch to HTTPS!

Online Shop
Credit Card


EurAsia Online Shop

new products
· X360ACE V5
· R4i Gold 3DS Plus
· NS-Atmosphere
· PsNee modchip PSX
· Mars Pro GM-816HD
· EurAsia File Collection 2017
· Matrix Infinity 2.0
· Modbo 5.0
· Screwdriver GC/SNES
· X360ACE V3
· E3 NOR Flasher
· Corona Postfix Adapter V2
· SuperCIC cart key
· SuperCIC SNES kit
· Gateway 3DS
· X360ACE V1
· 3k3y 3KR (SATA)
· Mtx Glitcher v1

complete price list

Tor Hidden Service
Tor Project
EurAsia Onion URL: wrqgfbrcgttkp6pi.onion

Who's Online
There are currently 313 guest(s) and 6 member(s) online.

Checkershome - harryp - hd360 - mabby - moos - tripod731

Welcome honored guest. You can register for free by clicking here.

Site Protection

Hot Wikis
Xbox One Dev Mode
PS4 Exploit using Raspberry Pi
Switch Key List
PS4 firmware updates
3k3y nokeys ISO tutorial
3DS game fw updates
3k3y microSD recovery
PS3 SKU Models
PS3 Metldrpwn
Xk3y microSD recovery
Xbox360 motherboards
Xbox360 Reset Glitch Hack
PS3 Blu-ray Drive
Homemade Sputnik360
PS3 BD drive swap
PSP Crypto Keys
PS3 Hypervisor RE
PS3 Dongle User Guide
PSGroove tutorial
Xecuter LT Fakir
NSMB Modchip Tutorial
PS3 Glitch Hack

RSS Feed
News & Downloads & Wiki


Hosted By


Respected Sites
Home of the Hitmen
English Amiga Board
GXArena OFW Repo
Games and Consoles
Console Wizard
GameCube Linux
Xbox Linux
bunnie's blog





Electronic Frontier Foundation
Amnesty International

Nectarine Radio

Demovibes Radio


Total Page Views
We received
page views since June 2002

Anatomy of a Wii U: The End...?
Posted on Monday, January 15, 2018 @ 04:23:31 GMT

wii u [source: hexkyz @] Welcome to a new write-up! Last time I wrote one of these was months ago, but I had good reasons for that (*cough*Switch*cough*).

Since as early as March, I've been working non-stop on hacking and reverse engineering the Switch alongside extremely talented hackers/developers such as plutoo, derrek, yellows8 and SciresM.
Together we have achieved incredible milestones and I'm really glad all our hard work eventually paid off.

That said, let's move on to the reason for this post: the Wii U.
If you are one of those few enthusiasts that still care about this console, you might recall last year's CCC where derrek, naehrwert and nedwill showcased their progress in hacking the last bits of the 3DS and Wii U.
Back then, they demonstrated their own exploitation path for taking down both the PPC and ARM processors on the Wii U, which was something that had already been publicly achieved using different bugs/exploits. This was also achieved much earlier by the hacking group fail0verflow which showcased their findings during the 30th edition of the CCC (back in 2013).

It was a very cool talk all around, but the main highlight for Wii U hacking fans was something derrek brought up: boot1.
This particular piece of the Wii U's boot chain was never obtained up until derrek and his team (plutoo, yellows8, smealum and naehrwert) successfully launched an hardware based attack that resulted in dumping the boot1 key.
The setup for this attack remained private, but the overall exploitation process was explained during the talk and is also documented here:

That was the last nail in the Wii U's coffin... or was it? Naturally, after obtaining the boot1 binary, derrek and his team began looking for vulnerabilities in it. While most of the usually critical stuff (signature handling, file parsing, etc.) was found to be safely implemented, derrek, plutoo, yellows8, naehrwert and shuffle2 did find one potential bug. However, due to lack of motivation and time, it remained just a theory.

I even wrote a blog post about all this where I mistakenly assumed some things that weren't true. Then I issued another blog post apologizing for said assumptions... Those were confusing times. :P
However, that's what got me to chat with derrek and get to know him better.

After some discussions about boot1, derrek agreed on sharing with me the potential bug that was mentioned during CCC. His team was already getting ready for the Switch release and they had little to no time left to work on trying to exploit this bug, so I offered my help.

A few days later I found a way to exploit this bug and achieved boot1 code execution! Neat, huh?
So, without further ado, I present you a writeup on the mythical boot1hax. :D

NOTE: Obtaining the boot1 binary in the first place is out of scope for the purposes of this writeup.

The Bug

The bug itself is really simple.
After some hours reverse engineering the boot1 binary and using the information derrek had shared with me, finding the bug was straightforward.
However, to understand it, we have to look into a specific IOSU process: IOS-MCP.

Wait, IOS-MCP? That loads AFTER boot1, what could possibly relate them?
Turns out, IOS-MCP manages something that plenty of people have already looked into (and maybe even guessed it's purpose), but couldn't exactly undestand what it does.

There is a range of physical memory mapped in IOSU that appears to serve no clear purpose: 0x10000000 to 0x100FFFFF (see

The typical layout of this region is as follows:
0x10000000: 12 34 56 78 9A BC DE F0 12 34 56 78 9A BC DE F0
0x100003F0: 12 34 56 78 9A BC DE F0 12 34 56 78 9A BC DE F0
0x10000400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005A40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005A50: 00 00 00 00
0x10005A54: PRSH XOR checksum
0x10005A58: "PRSH"      // magic
0x10005A5C: 0x00000001  // version (0 or 1)
0x10005A60: 0x0000259C  // size
0x10005A64: 0x00000001  // unk
0x10005A68: 0x00000020  // max_sections
0x10005A6C: 0x00000007  // num_sections
...                     // PRSH sections
0x10007FF0: PRST XOR checksum
0x10007FF4: 0x0000259C  // size
0x10007FF8: 0x00000001  // unk
0x10007FFC: "PRST"      // magic

Breaking it down, we have a pattern filling the first 0x400 bytes followed by NULL bytes up until this PRSH/PRST structure:
typedef struct {
  char name[0x100];
  void* data;
  u32 size;
  u32 unk;
  u8 hash[0x14];
  u8 padding[0x0C];
} prsh_section;

typedef struct {
  u32 xor_checksum;
  u32 magic;
  u32 version;
  u32 size;
  u32 unk;
  u32 max_sections;
  u32 num_sections;
  prsh_section sections[];
} prsh;

typedef struct {
  u32 xor_checksum;
  u32 size;
  u32 unk;
  u32 magic;
} prst;

This structure is created by IOS-MCP to keep track of the addresses and sizes of several memory regions. It is encapsulated with a header (PRSH) and a footer (PRST) and contains an array of structures describing memory regions.
In the latest firmware version, only 7 regions are registered in this structure. Here's an example parsed from my console:
Name:     "boot_info"
Address:  0x10008000
Size:     0x00000058
UNK:      0x80000000
Name:     "mcp_crash_region"
Address:  0x100F7F60
Size:     0x000080A0
UNK:      0x80000000
Name:     "mcp_syslog_region"
Address:  0x1FF7FFD0
Size:     0x00080030
UNK:      0x80000000
Name:     "mcp_fs_cache_region"
Address:  0x100D7EE0
Size:     0x00020080
UNK:      0x80000000
Name:     "mcp_ramdisk_region"
Address:  0x100B7494
Size:     0x0000006C
UNK:      0x80000000
Name:     "mcp_list_region"
Address:  0x1FE62C40
Size:     0x0011D390
UNK:      0x80000000
Name:     "mcp_launch_region"
Address:  0x100B645C
Size:     0x00001038
UNK:      0x80000000

While most of these regions contain nothing particularly interesting, there is one exception: boot_info.
This region stores data passed along from boot0 and boot1 to IOS-MCP! An example from my console:
0x00000000: 0x00000001	// Always 1 (set by boot1 on coldboot)
0x00000004: 0xA6000000	// Boot flags (0x80 means data is set)
0x00000008: 0x00000000	// Boot state
0x0000000C: 0x00000001	// Boot count (increased by boot1 on reset)
0x00000010: 0x00100000	// Set to 0 by boot1 on coldboot
0x00000014: 0x00000000	// Set to 0 by boot1 on coldboot
0x00000018: 0xFFFFFFFF	// Set to -1 by boot1 on coldboot
0x0000001C: 0xFFFFFFFF	// Set to -1 by boot1 on coldboot
0x00000020: 0xFFFFFFFF	// Set to -1 by boot1 on coldboot
0x00000024: 0xFFFFFFFF	// Set to -1 by boot1 on coldboot
0x00000028: 0xFFFFFFFF	// Set to -1 by boot1 on AHB reset
0x0000002C: 0xFFFFFFFF	// Set to -1 by boot1 on AHB reset
0x00000030: 0x00000000	// Set to 0 by boot1 on AHB reset
0x00000034: 0x00000000	// Set to 0 by boot1 on AHB reset
0x00000038: 0x00369F6B	// boot1_main
0x0000003C: 0x00297268	// boot1_read
0x00000040: 0x0005FCFE	// boot1_verify
0x00000044: 0x00053CE8	// boot1_decrypt
0x00000048: 0x00012030	// boot0_main
0x0000004C: 0x000029D2	// boot0_read
0x00000050: 0x0000D281	// boot0_verify
0x00000054: 0x0000027A	// boot0_decrypt


As an example, the last 8 fields contain the time spent on each boot0/boot1 stage and this data is printed on crash logs.

What derrek and his team found out by looking at the boot1 binary is that this structure is also passed back to boot1!
How? Well, right before a reset is asserted, IOS-MCP encrypts the entire 0x10000400 to 0x10008000 range with the Starbuck ancast key. When the console reboots, RAM contents are not cleared and boot1 will decrypt this range and parse the PRSH/PRST struct looking for the boot_info region.

Even though this might look like a weird way to pass data back and forth across the boot chain, this process is actually properly implemented and boot1's code for parsing the PRSH/PRST structure is sound. But there is one exception...

On coldboot, where the RAM contents are cleared, it's boot1 that creates boot_info for the fist time at the hardcoded address 0x10008000 and inserts this information into the PRSH/PRST structure. However, on a warmboot, boot1 decrypts and parses the already existing PRSH/PRST structure in RAM which might have been changed by IOS-MCP. This means that boot1 actually locates the boot_info section inside the PRSH/PRST structure and uses the pointer stored there to read/write the actual data.

This shouldn't be a problem, but they forgot to validate the pointer to boot_info data, which means that if IOS-MCP changes it, boot1 will attempt to read/write the boot_info data from any address we want (instead of 0x10008000)!

What derrek and his team were able to verify is that changing the pointer for boot_info to an address within boot1's stack region would crash boot1. A plausible exploitation path here would be to take advantage of whatever boot1 writes into boot_info to overwrite some LR address stored in boot1's stack and gain code execution.
Unfortunately, this is very impractical. Turns out, the way boot_info is parsed and modified by boot1 is very, very restricted.

The Exploit

So, we have this weird small structure that boot1 uses to communicate with IOSU (and vice versa) and we can control the pointer for said structure to force boot1 to read it from anywhere we want. The only way to tell if this can be exploited or not is to know exactly why this boot_info structure exists and how boot1 handles it, so I'm going to cheat and jump straight to a breakdown of how it's done:
// Do some boring stuff

// Decrypt PRSH/PRST with Starbuck ancast key
sub_D400320(0x10000400, 0x7C00, iv);
// Parse PRSH/PRST
sub_D40B030(0x10000400, 0x7C00);
// Locate or create new "boot_info"

// RTC SLEEP_EN is raised
if ((rtc_events & 0x01E00001) == 0x00200000)
	*(u32 *)boot_info_08_addr = 0;
	// Read from boot_info + 0x08
	u32 result = sub_D40AB84(boot_info_08_addr);
	// Got boot_info_08
	if (result == 0)
		u32 boot_info_08 = *(u32 *)boot_info_08_addr;
		rtc_events |= (boot_info_08 & 0x101E);
	// Mask boot_info_04 with 0xBFFFFFFF
	// Mask boot_info_04 with 0xF7FFFFFF and set some other fields

// Set boot_info_08

// Do even more boring stuff

// Write to boot_info_38
sub_D40AD2C(0x00, time_boot1);

// Write to boot_info_3C
sub_D40AD2C(0x01, time_boot1_load_fw);

// Write to boot_info_40
sub_D40AD2C(0x02, time_boot1_verify_fw);
// Write to boot_info_44
sub_D40AD2C(0x03, time_boot1_decrypt_fw);

// Write to boot_info_48
sub_D40AD2C(0x04, time_boot0);

// Write to boot_info_4C
sub_D40AD2C(0x05, time_boot0_load_boot1);

// Write to boot_info_50
sub_D40AD2C(0x06, time_boot0_verify_boot1);

// Write to boot_info_54
sub_D40AD2C(0x07, time_boot0_decrypt_boot1);
// Set flag 0x04000000 in boot_info_04
// Increase boot_info_0C by 1

// Run fw.img

As you can see, there's not much going on. The PRSH/PRST structure is decrypted from RAM and boot1 tries to locate boot_info inside it. If it fails, a new boot_info entry is created and inserted into the PRSH/PRST structure, but the pointer for it's data will be hardcoded to 0x10008000, thus causing any subsequent reads/writes to occur in a perfectly safe address range. However, if it does find a preexisting boot_info entry, boot1 will fetch it's data from the pointer stored inside the PRSH/PRST structure and this is what we are interested in causing.

The attack plan is simple:

* Use any exploit we want and escalate to IOS-MCP (or even better, to the IOSU's kernel);
* Craft/modify the PRSH/PRST structure in memory using a modified boot_info pointer;
* Encrypt the PRSH/PRST structure with the Starbuck ancast key and boot_info IV (stored at 0x050677C0 in IOS-MCP);
* Force a reboot.

As expected, we can force boot1 to take a modified boot_info pointer and start reading the data from anywhere we want. Now comes the real challenge: where should we point to?

We must focus on the boot_info fields that are always modified by boot1, but we also need to take into account how boot1 tells if boot_info already exists or not. This happens in sub_D40AF10 and it goes like this:

* Each PRSH/PRST section is parsed and it's name is compared against the string "boot_info";
* If the boot_info section is found, it's size is checked and it must be 0x58;
* Finally, the boot_info_04 field must have bit 0x80 (big endian) set.

This last check is very important since boot1 only accepts a preexisting boot_info structure if the word at offset 0x04 has that specific bit set.

It's now clear that we can achieve a semi-arbitrary write in boot1 by abusing this particular bug. All we need is to change the boot_info pointer inside our crafted PRSH/PRST into something that resembles a valid boot_info structure from boot1's point of view. This would then result in boot1 updating those boot_info fields listed before and, therefore, write data to an arbitrary address.

Let's leave the "boot_info_04 field must have bit 0x80 set" aside for a while and focus on which fields might be useful to write into boot1's address space:

* boot_info_08: this is only modified by boot1 when a specific RTC event has occurred.
* boot_info_38 to boot_info_54: these are used to store the time spent on the various boot0/boot1 stages.
* boot_info_0C: this is increased by 1 each time a warmboot occurs (gets set back to 0 on a coldboot).

To be more precise, there are indeed a few more fields that are modified by boot1, but these are always set to either 0 or -1 in a way that doesn't make it really practical to choose them.

I began by focusing on the time related fields, but these just turned out to be too volatile for a reliable memory corruption. Also, boot_info_0C is not really useful either since it keeps changing on each warmboot.
What about boot_info_08? For this field to be read/written the RTC must have the SLEEP_EN flag set. Luckily, we can just force this flag to be set by calling the system call ios_shutdown(1) (see which also happens to trigger a system reset!

Still, boot_info_08 will be overwritten with rtc_events |= (boot_info_08 & 0x101E). From blind tests, I could tell the final value that got written was always 0x0020XXXX, which means I could write a NULL byte followed by 0x20 and whatever was in the lower bits of boot_info_08. This is far from optimal... :

Took me about 2 afternoons of reading boot1's binary until I finally found the perfect place to corrupt:
0D40AC6C                 MOVS            R0, #0
0D40AC6E                 POP             {R1-R3}
0D40AC70                 MOV             R11, R2
0D40AC72                 MOV             SP, R3
0D40AC74                 BX              R1

This particular snippet is the epilogue of sub_D40AC30, which runs immediately after boot_info_08 is modified. Doesn't look particularly interesting, but let's check the hex:
0D40AC6C  20 00 BC 0E 46 93 46 9D 47 08 00 00

Jackpot! Since we are running way before MMU is set up and all that, we can do unaligned memory reads/writes just fine, so, if I change the boot_info pointer to 0x0D40AC6D, boot1 will see the following structure:

* boot_info_00: 0x00BC0E46
* boot_info_04: 0x93469D47
* boot_info_08: 0x080000XX

Since boot_info_04 has bit 0x80 (big endian) set, boot1 will overwrite boot_info_08 with 0x0020XXXX. Why is this important? Because we now have mutated the instruction BX R1 into BX R0 and R0 is 0.
This means boot1's execution will fall into NULL. Normally this isn't very exciting, but in the Wii U's case the physical address range 0x00000000 to 0x01FFFFFF maps to MEM1 (which is frequently used for graphics).

This memory range is not cleared on a warmboot either so, as long as boot0 and boot1 leave it alone, we can actually plant our payload there and have arbitrary code execution going!

It's known that boot0 doesn't touch any relevant RAM regions, but what about boot1? Well, boot1 actually accesses the three RAM regions (MEM0, MEM1 and MEM2):

* MEM0 is fully cleared by boot1 as soon as it starts;
* MEM2 is left untouched, with the exception of the first 0x400 bytes at address 0x10000000 which are filled with a binary pattern for testing RAM self-refresh;
* MEM1 is also left untouched, with the exception of a single word (0x20008000) being written at address 0x0000000C for unknown reasons.

As long as our payload starts right away with a jump over address 0x0000000C, we are good!

So, I cook up a small payload to copy boot1 from SRAM into some unused MEM2 region, patch the corruption I just caused, jump back to where execution fell to NULL and let boot1 finish. Now I just need to escalate into IOS-MCP or IOSU's kernel and fish out the binary!

As a bonus, this particular method allows me to hijack execution before the 2 mysterious OTP blocks get locked (see so I can easily piggyback on boot1's OTP reading functions and get them too!
Sadly, these blocks are never used and were likely locked out as a preemptive measure so a future update could begin to use them instead of some other key material (especially since the 2 blocks are not per-console).

And there you have it, boot1 code execution from a RAM based attack!
Along with this writeup, I'll be publicly documenting boot1 over at and I'm releasing a patch for my long forgotten project hexFW that gives you the option to dump your console's boot1 and unlocked OTP:

NOTE: This does not include the boot1 AES key, since that one is long gone by the time we are running code in boot1!

What about CFW? Well this attack on it's own doesn't really help you there.
In order to get a custom firmware running straight from boot1 another kind of attack is preferred, specially something that actually survives a coldboot. :
Remember that this only works due to RAM not being cleared on a warmboot so it's impossible to achieve persistence this way.

However... There's one plausible vector that could be used to create a much safer alternative to current methods.
Leveraging this bug from the vWii environment, for example, could grant a nice boot(ish) time CFW by combining some form of contenthax in a way that entering vWii mode would launch the boot1hax payload, reset the console and send you right into a CFW. The total time spent on this would be minimal and it would create a dual-boot environment where you could hold down the "B" button on boot to jump into CFW or do nothing to land on the vanilla OS. That is, of course, if you wouldn't mind sacrificing your vWii channel for a while (it would then be possible to restore it from within the CFW environment, so that's not really an issue).

I've been looking into this for quite some time with derrek, but the Switch has been taking most of our time so I kept postponing this project endlessly. Regardless, I still plan on picking this up one last time during the start of this new year, but derrek and I agreed on sharing this anyway so others can also get the chance to research boot1 and hopefully find some new bugs in the process.

All this has been kept under wraps for quite a while for a very good reason: it's insanely easy to patch. Now that the Wii U reached it's EoL and the Switch is the new kid on the block, it seems appropriate to end (for good) the Wii U cycle while homebrew on the Switch is just beginning to flourish.

Retail plaintext boot1 (v8377) SHA-256: 5013BFABC578CBA08843D9A0F650171942A696CBC54DF12E754D9E0978FCD3B1

I hope you enjoyed reading this as much as I did writing it. :)
Stay safe and have fun!



Member Registration

Related Links
· More about wii u

Most read story about wii u:
Wiike‹ - Its True!

Story Rating
Average Score: 5
Votes: 1


Printer Friendly Page  Printer Friendly Page

"Login" | Login/Create an Account | 0 comments
The comments are owned by the poster. We aren't responsible for their content.

All trademarks and copyrights on this page are owned by their respective owners.
Comments and forum messages are owned by the Poster.