iosuhax successor (based on SALT iosuhax)
Notable additional features (see also: ios_process/source/config.h
):
- de_Fuse support: Redirects all OTP reads to RAM. Requires minute_minute to patch in the data.
- Decrypted fw.img loading in IOSU.
- IOSU reloads passes through minute to make patching easier
- SEEPROM writes are disabled (for safety)
- redNAND MLC acceleration -- moves MLC cache (SCFM) to SLCCMPT
- Semihosting hooks -- print kprintf and syslogs to pico de_Fuse serial
- Disk drive disable (w/o SEEPROM writing)
USB_SHRINKSHIFT
-- Allows having both the Wii U filesystem and a normal filesystem on a drive by shifting the Wii U portion after the MBRUSB_SEED_SWAP
-- Override the SEEPROM USB key to allow easier system migration
Experimental/Unstable features:
- Loading
kernel.img
from sdcard
Warnings:
- redNAND formatting is incompatible with other implementations! use minute to format.
- If you have existing USB drives, keep
USB_SHRINKSHIFT
set to 0 PRINT_FSAOPEN
is useful, but extremely slow- This is only tested on 5.5.x fw.imgs, I haven't ported to anything before that.
# Needed for plugins
export STROOPWAFEL_ROOT=$(pwd)
# wafel_core
make wafel_core.ipx
# plugin example
make -C wafel_plugin_example
Additional IOSU patches can be added trough plugins. A compiled plugin can be placed next to the wafel_core.ipx and it will be loaded by minute. Stroopwafel then calls two hooks in the plugin:
-
void kern_main()
- runs before everything else in kernel mode. -
void mcp_main()
- runs before MCP's main thread under MCP.
Both functions must return, but mcp_main()
can spawn new threads.
A skeleton can be found here: wafel_plugin_example
A plugin can apply through several macros provided by stroopwafel in wafel/patch.h:
-
U32_PATCH_K(_addr, _val)
overwrites the virtual address the provided 32bit value -
ASM_PATCH_K(_addr, _str)
Takes a string containing assembly, assembles it at compile time and copies the binary at runtime at the given address. You can't do relative reference symbols outside the assembly string. -
ASM_T_PATCH_K(_addr, _str)
same asASM_PATCH_K(_addr, _str)
but for thumb -
BL_TRAMPOLINE_K(_addr, _dst)
generates a relativeBL
at the address to dst. You can't use that to jump to code in your plugin - the branch would be too far. -
BL_T_TRAMPOLINE_K(_addr, _dst)
same asBL_TRAMPOLINE_K(_addr, _dst)
but for thumb -
BRANCH_PATCH_K(_addr, _dst)
generates a relativeB
at the address to dst. You can't use that to jump to code in your plugin - the branch would be too far.
Stroopwafel provides two ways to hook into IOSU to call your C or asm code. The functions and structs are declared in wafel/trampoline.h
-
void trampoline_hook_before(uintptr_t addr, void *target)
Creates a trampoline and aBL
at address to call it. The trampoline will push all GP registers, call your function supplied intarget
, after your function is finished it will restore the registers and execute the orginal instruction that was overwritten. Since aBL
is used to branch to the trampoline, the originalLR
will be overwritten. The orignal instruction is not allowed to usePC
relative addressing, as that would be off. The only exception to this is aBL
, in that case it will branch to the originalBL
target instead. The called function receives a pointer to atrampoline_state
struct, which allows it to read and modify the register state that will be restored after the function finishes. -
void trampoline_t_hook_before(uintptr_t addr, void *target)
Same as the previous, but for thumb. The only difference is that two instructions will be overwritten, since thumb instructions are 2 bytes, but theBL
will take 4. Both instructions will be relocated to the trampoline. The called function receives a pointer to atrampoline_t_state
-
void trampoline_blreplace(uintptr_t addr, void *target)
Replaces aBL
function call. The new target function intarget
receives the first four original arguments (in r0-r3) in their original position, the fith argument is a pointer to the originalBL
target, the sixth is the savedLR
(artifact of how the trampoline works) and after that the other original arguments (on the stack) follow. You can descide if, where and whith what argument's you call the original target function using the supplied pointer. -
void trampoline_t_blreplace(uintptr_t addr, void *target)
Same as before, but for thumb
These trampolines support hooking the same address multiple times. With the blreplace
the replacement function is responsible to call the previous function via the supplied pointer to not break the chain.
A demo of the trampolines can be found here: wafel_trampoline_demo/source/main.c at main · jan-hofmeier/wafel_trampoline_demo · GitHub
stroopwafel operates similar to (and is based on) SaltyNX: Instead of jumping to the reset handler at 0xFFFF0000, code execution is redirected to wafel_core
. wafel_core
patches the IOS kernel, modules, and segments, and then jumps to 0xFFFF0000. The memory for stroopwafel plugins is taken from the end of the 128MiB ramdisk at 0x20000000. Specifics on bootstrapping can be found here.
Plugins are mapped to all IOS modules, to allow for code patching and function replacement. Additional MMU mappings are also added to allow for single-instruction B/BL trampolines. All plugins are PIE and relocate themselves once on startup. Additionally, plugins may resolve symbols from other plugins. wafel_core
provides several base APIs for patching and symbol lookup.
wafel_core
currently provides all of the previously mentioned features (redNAND, semihosting, wupserver, etc).
- smealum for the original iosuhax
- WulfyStylez and Dazzozo for many of the SALT iosuhax patches