Migrate instruction encoding to external library

The PowerPC instruction encoder and patching logic is now available at https://github.com/wii-tools/powerpc.
This commit is contained in:
Spotlight 2022-04-10 23:20:37 -05:00
parent 4a232c552b
commit e17dbf9ce1
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
11 changed files with 463 additions and 818 deletions

1
go.mod
View File

@ -6,5 +6,6 @@ require (
github.com/logrusorgru/aurora/v3 v3.0.0
github.com/wii-tools/GoNUSD v0.2.2
github.com/wii-tools/arclib v1.0.0
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964
github.com/wii-tools/wadlib v0.3.1
)

2
go.sum
View File

@ -4,5 +4,7 @@ github.com/wii-tools/GoNUSD v0.2.2 h1:vHiSLI9sK9Q4zbCt24jMiq/lo2FISLBuCHoaY3Bd88
github.com/wii-tools/GoNUSD v0.2.2/go.mod h1:Wf0ZMf4jdyQYSaSmNyFsqfQbngFA/cM9mpJBZ63Qlzs=
github.com/wii-tools/arclib v1.0.0 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms=
github.com/wii-tools/arclib v1.0.0/go.mod h1:uXFan/NSXoQ2pOVPN4ugZ4nJX7esBnjB1QUgVrEzK/4=
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964 h1:/ZeMBIoblxzyhKHQJncvBZaxqi6RZz4rm4QvL0QiguA=
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964/go.mod h1:bt/52tMfh1hmEXwFh1i1AOEMNHoe0jSXkcRunOSdFkc=
github.com/wii-tools/wadlib v0.3.1 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8=
github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ=

17
main.go
View File

@ -6,6 +6,7 @@ import (
"github.com/logrusorgru/aurora/v3"
"github.com/wii-tools/GoNUSD"
"github.com/wii-tools/arclib"
"github.com/wii-tools/powerpc"
"github.com/wii-tools/wadlib"
"io/fs"
"io/ioutil"
@ -137,6 +138,22 @@ func main() {
writeOut("patched.wad", output)
}
// applyDefaultPatches applies the default patches to our main DOL.
func applyDefaultPatches() {
var err error
mainDol, err = powerpc.ApplyPatchSet(OverwriteIOSPatch, mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(LoadCustomCA(), mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(PatchBaseDomain(), mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(NegateECTitle, mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(PatchECCfgPath, mainDol)
check(err)
}
// check has an anxiety attack if things go awry.
func check(err error) {
if err != nil {

View File

@ -1,98 +0,0 @@
package main
import (
"bytes"
"errors"
"fmt"
"github.com/logrusorgru/aurora/v3"
)
var (
ErrInconsistentPatch = errors.New("before and after data present within file are not the same size")
ErrPatchOutOfRange = errors.New("patch cannot be applied past binary size")
ErrInvalidPatch = errors.New("before data present within patch did not exist in file")
)
// Patch represents a patch applied to the main binary.
type Patch struct {
// Name is an optional name for this patch.
// If present, its name will be logged upon application.
Name string
// AtOffset is the offset within the file this patch should be applied at.
// If not present, the patch will be recursively applied across the entire file.
// Relying on this behavior is highly discouraged, as it may damage other parts of the binary
// if gone unchecked.
AtOffset int
// Before is an array of the bytes to find for, i.e. present within the original file.
Before []byte
// After is an array of the bytes to replace with.
After []byte
}
// PatchSet represents multiple patches available to be applied.
type PatchSet []Patch
// applyPatch applies the given patch to the main DOL.
func applyPatch(patch Patch) error {
// Print name if present
if patch.Name != "" {
fmt.Println(" + Applying patch", aurora.Cyan(patch.Name))
}
// Ensure consistency
if len(patch.Before) != len(patch.After) {
return ErrInconsistentPatch
}
if patch.AtOffset != 0 && patch.AtOffset > len(mainDol) {
return ErrPatchOutOfRange
}
// Either Before or After should return the same length.
patchLen := len(patch.Before)
// Determine our patching behavior.
if patch.AtOffset != 0 {
// Ensure original bytes are present
originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen]
if !bytes.Equal(originalBytes, patch.Before) {
return ErrInvalidPatch
}
// Apply patch at the specified offset
copy(mainDol[patch.AtOffset:], patch.After)
} else {
// Recursively apply this patch.
// We cannot verify if the original contents are present via this.
mainDol = bytes.ReplaceAll(mainDol, patch.Before, patch.After)
}
return nil
}
// applyPatchSet iterates through all possible patches, noting their name.
func applyPatchSet(setName string, set PatchSet) {
fmt.Printf("Handling patch set \"%s\":\n", aurora.Yellow(setName))
for _, patch := range set {
err := applyPatch(patch)
check(err)
}
}
// emptyBytes returns an empty byte array of the given length.
func emptyBytes(length int) []byte {
return bytes.Repeat([]byte{0x00}, length)
}
// applyDefaultPatches iterates through a list of default patches.
func applyDefaultPatches() {
applyPatchSet("Overwrite IOS Syscall for ES", OverwriteIOSPatch)
applyPatchSet("Load Custom CA within IOS", LoadCustomCA())
applyPatchSet("Change Base Domain", PatchBaseDomain())
applyPatchSet("Negate EC Title Check", NegateECTitle)
applyPatchSet("Change EC Configuration Path", PatchECCfgPath)
}

View File

@ -1,6 +1,10 @@
package main
import "strings"
import (
"strings"
. "github.com/wii-tools/powerpc"
)
const (
NintendoBaseDomain = "shop.wii.com"
@ -15,38 +19,41 @@ const (
// See docs/patch_base_domain.md for more information.
func PatchBaseDomain() PatchSet {
return PatchSet{
Patch{
Name: "Modify /startup domain",
Name: "Change Base Domain",
Patches: []Patch{
{
Name: "Modify /startup domain",
Before: []byte(ShowManualURL),
After: padReplace(ShowManualURL),
},
Patch{
Name: "Modify oss-auth URL",
AtOffset: 3180692,
Before: []byte(ShowManualURL),
After: padReplace(ShowManualURL),
},
{
Name: "Modify oss-auth URL",
AtOffset: 3180692,
Before: []byte(GetLogURL),
After: padReplace(GetLogURL),
},
Patch{
Name: "Modify trusted base domain prefix",
AtOffset: 3323432,
Before: []byte(GetLogURL),
After: padReplace(GetLogURL),
},
{
Name: "Modify trusted base domain prefix",
AtOffset: 3323432,
Before: []byte(TrustedDomain),
After: padReplace(TrustedDomain),
},
Patch{
Name: "Modify ECS SOAP endpoint URL",
AtOffset: 3268896,
Before: []byte(TrustedDomain),
After: padReplace(TrustedDomain),
},
{
Name: "Modify ECS SOAP endpoint URL",
AtOffset: 3268896,
Before: []byte(ECommerceBaseURL),
After: padReplace(ECommerceBaseURL),
},
Patch{
Name: "Wildcard replace other instances",
Before: []byte(ECommerceBaseURL),
After: padReplace(ECommerceBaseURL),
},
{
Name: "Wildcard replace other instances",
Before: []byte(NintendoBaseDomain),
After: padReplace(baseDomain),
Before: []byte(NintendoBaseDomain),
After: padReplace(baseDomain),
},
},
}
}
@ -60,5 +67,5 @@ func padReplace(url string) []byte {
}
padding := len(url) - len(replaced)
return append([]byte(replaced), emptyBytes(padding)...)
return append([]byte(replaced), EmptyBytes(padding)...)
}

View File

@ -1,104 +1,111 @@
package main
import (
. "github.com/wii-tools/powerpc"
)
// LoadCustomCA loads our custom certificate, either generated or loaded,
// into the IOS trust store for EC usage.
// It is assumed that rootCertificate has been loaded upon invoking this patchset.
// See docs/patch_custom_ca_ios.md for more information.
func LoadCustomCA() PatchSet {
return PatchSet{
Patch{
Name: "Insert custom CA into free space",
AtOffset: 3037368,
Name: "Load Custom CA within IOS",
Patches: []Patch{
{
Name: "Insert custom CA into free space",
AtOffset: 3037368,
Before: emptyBytes(len(rootCertificate)),
After: rootCertificate,
},
Patch{
Name: "Modify NHTTPi_SocSSLConnect to load cert",
AtOffset: 644624,
Before: EmptyBytes(len(rootCertificate)),
After: rootCertificate,
},
{
Name: "Modify NHTTPi_SocSSLConnect to load cert",
AtOffset: 644624,
Before: Instructions{
// Check whether internals->ca_cert is null
LWZ(R4, 0xc0, R28),
// cmpwi r4, 0
CMPWI(R4, 0),
Before: Instructions{
// Check whether internals->ca_cert is null
LWZ(R4, 0xc0, R28),
// cmpwi r4, 0
CMPWI(R4, 0),
// If it is, load the built-in root certificate.
// beq LOAD_BUILTIN_ROOT_CA
Instruction{0x41, 0x82, 0x00, 0x20},
// If it is, load the built-in root certificate.
// beq LOAD_BUILTIN_ROOT_CA
Instruction{0x41, 0x82, 0x00, 0x20},
// ---
// ---
// It seems we are loading a custom certificate.
// r3 -> ssl_fd
// r4 -> ca_cert, loaded previously
// r5 -> cert_length
LWZ(R3, 0xac, R28),
LWZ(R5, 0xc4, R28),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c),
// It seems we are loading a custom certificate.
// r3 -> ssl_fd
// r4 -> ca_cert, loaded previously
// r5 -> cert_length
LWZ(R3, 0xac, R28),
LWZ(R5, 0xc4, R28),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c),
// Check if successful
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28},
// Check if successful
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28},
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0),
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0),
// ----
// ----
// It seems we are loading the built-in root CA.
// r3 -> ssl_fd
// r4 -> cert_length
LWZ(R3, 0xac, R28),
LWZ(R4, 0xd8, R28),
// SSLSetBuiltinRootCA(ssl_fd, cert_index)
BL(0x800acb00, 0x800c2574),
// It seems we are loading the built-in root CA.
// r3 -> ssl_fd
// r4 -> cert_length
LWZ(R3, 0xac, R28),
LWZ(R4, 0xd8, R28),
// SSLSetBuiltinRootCA(ssl_fd, cert_index)
BL(0x800acb00, 0x800c2574),
// Check if successful
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x0c},
// Check if successful
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x0c},
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acb10, 0x800acbb0),
}.toBytes(),
After: Instructions{
// Our certificate is present at 0x802e97b8.
// r4 is the second parameter of SSLSetRootCA, the ca_cert pointer.
LIS(R4, 0x802e),
ORI(R4, R4, 0x97b8),
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acb10, 0x800acbb0),
}.Bytes(),
After: Instructions{
// Our certificate is present at 0x802e97b8.
// r4 is the second parameter of SSLSetRootCA, the ca_cert pointer.
LIS(R4, 0x802e),
ORI(R4, R4, 0x97b8),
// r5 is the third parameter of SSLSetRootCA, the cert_length field.
// xor r5, r5, r5
Instruction{0x7c, 0xa5, 0x2a, 0x78},
ADDI(R5, R5, uint16(len(rootCertificate))),
// r5 is the third parameter of SSLSetRootCA, the cert_length field.
// xor r5, r5, r5
Instruction{0x7c, 0xa5, 0x2a, 0x78},
ADDI(R5, R5, uint16(len(rootCertificate))),
// r3 is the first parameter of SSLSetRootCA, the ssl_fd.
// We load it exactly as Nintendo does.
LWZ(R3, 0xac, R28),
// r3 is the first parameter of SSLSetRootCA, the ssl_fd.
// We load it exactly as Nintendo does.
LWZ(R3, 0xac, R28),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c),
// Check for errors
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28},
// Check for errors
CMPWI(R3, 0),
// beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28},
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0),
// Return error -1004 if failed
LI(R3, 0xfc14),
// b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0),
// NOP the rest in order to allow execution to continue.
NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(),
}.toBytes(),
// NOP the rest in order to allow execution to continue.
NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(),
}.Bytes(),
},
},
}
}

View File

@ -1,11 +1,17 @@
package main
var PatchECCfgPath = PatchSet{
Patch{
Name: "Change EC Configuration Path",
AtOffset: 3319968,
import (
. "github.com/wii-tools/powerpc"
)
Before: []byte("ec.cfg\x00\x00"),
After: []byte("osc.cfg\x00"),
var PatchECCfgPath = PatchSet{
Name: "Change EC Configuration Path",
Patches: []Patch{
{
AtOffset: 3319968,
Before: []byte("ec.cfg\x00\x00"),
After: []byte("osc.cfg\x00"),
},
},
}

View File

@ -1,57 +1,65 @@
package main
import (
. "github.com/wii-tools/powerpc"
)
var NegateECTitle = PatchSet{
Patch{
Name: "Permit downloading all titles",
AtOffset: 619648,
Name: "Negate EC Title Check",
// Generic function prolog
Before: Instructions{
STWU(R1, R1, 0xffe0),
MFSPR(),
}.toBytes(),
Patches: []Patch{
Patch{
Name: "Permit downloading all titles",
AtOffset: 619648,
// Immediately return true
After: Instructions{
LI(R3, 1),
BLR(),
}.toBytes(),
},
Patch{
Name: "Mark all titles as managed",
AtOffset: 620656,
// Generic function prolog
Before: Instructions{
STWU(R1, R1, 0xffe0),
MFSPR(),
}.Bytes(),
Before: Instructions{
STWU(R1, R1, 0xfff0),
MFSPR(),
}.toBytes(),
After: Instructions{
LI(R3, 1),
BLR(),
}.toBytes(),
},
Patch{
Name: "Mark all tickets as managed",
AtOffset: 619904,
Before: Instructions{
STWU(R1, R1, 0xfff0),
MFSPR(),
}.toBytes(),
After: Instructions{
LI(R3, 1),
BLR(),
}.toBytes(),
},
Patch{
Name: "Nullify ec::removeAllTitles",
AtOffset: 588368,
Before: Instructions{
STWU(R1, R1, 0xffc0),
MFSPR(),
}.toBytes(),
After: Instructions{
LI(R3, 0),
BLR(),
}.toBytes(),
// Immediately return true
After: Instructions{
LI(R3, 1),
BLR(),
}.Bytes(),
},
Patch{
Name: "Mark all titles as managed",
AtOffset: 620656,
Before: Instructions{
STWU(R1, R1, 0xfff0),
MFSPR(),
}.Bytes(),
After: Instructions{
LI(R3, 1),
BLR(),
}.Bytes(),
},
Patch{
Name: "Mark all tickets as managed",
AtOffset: 619904,
Before: Instructions{
STWU(R1, R1, 0xfff0),
MFSPR(),
}.Bytes(),
After: Instructions{
LI(R3, 1),
BLR(),
}.Bytes(),
},
Patch{
Name: "Nullify ec::removeAllTitles",
AtOffset: 588368,
Before: Instructions{
STWU(R1, R1, 0xffc0),
MFSPR(),
}.Bytes(),
After: Instructions{
LI(R3, 0),
BLR(),
}.Bytes(),
},
},
}

View File

@ -1,275 +1,282 @@
package main
import (
. "github.com/wii-tools/powerpc"
)
// OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign.
// See docs/patch_overwrite_ios.md for more information.
var OverwriteIOSPatch = PatchSet{
Patch{
Name: "Clear extraneous functions",
AtOffset: 20272,
Name: "Overwrite IOS Syscall for ES",
Patches: []Patch{
{
Name: "Clear extraneous functions",
AtOffset: 20272,
Before: Instructions{
// Function: textinput::EventObserver::onOutOfLength
LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ac4),
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x78},
Before: Instructions{
// Function: textinput::EventObserver::onOutOfLength
LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ac4),
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x78},
// Function: textinput::EventObserver::onCancel
LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ab8),
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x68},
// Function: textinput::EventObserver::onCancel
LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ab8),
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x68},
// Function: textinput::EventObserver::onOK
// subi r3, r3, 0x7fe0
Instruction{0x38, 0x6d, 0x80, 0x20},
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x5c},
// Function: textinput::EventObserver::onOK
// subi r3, r3, 0x7fe0
Instruction{0x38, 0x6d, 0x80, 0x20},
CRXOR(),
// b printf
Instruction{0x48, 0x2a, 0x8b, 0x5c},
padding,
Padding,
// Function: textinput::EventObserver::onSE
BLR(),
padding, padding, padding,
// Function: textinput::EventObserver::onEvent
BLR(),
padding, padding, padding,
// Function: textinput::EventObserver::onCommand
BLR(),
padding, padding, padding,
// Function: textinput::EventObserver::onInput
BLR(),
padding, padding, padding,
}.toBytes(),
// Function: textinput::EventObserver::onSE
BLR(),
Padding, Padding, Padding,
// Function: textinput::EventObserver::onEvent
BLR(),
Padding, Padding, Padding,
// Function: textinput::EventObserver::onCommand
BLR(),
Padding, Padding, Padding,
// Function: textinput::EventObserver::onInput
BLR(),
Padding, Padding, Padding,
}.Bytes(),
// We wish to clear extraneous blrs so that our custom overwriteIOSMemory
// function does not somehow conflict.
// We only preserve onSE, which is this immediate BLR.
After: append(Instructions{
BLR(),
}.toBytes(), emptyBytes(108)...),
},
Patch{
Name: "Repair textinput::EventObserver vtable",
AtOffset: 3095452,
Before: []byte{
0x80, 0x01, 0x44, 0x50, // onSE
0x80, 0x01, 0x44, 0x40, // onEvent
0x80, 0x01, 0x44, 0x30, // onCommand
0x80, 0x01, 0x44, 0x20, // onInput
0x80, 0x01, 0x44, 0x10, // onOK
0x80, 0x01, 0x44, 0x00, // onCancel
0x80, 0x01, 0x43, 0xf0, // onOutOfLength
// We wish to clear extraneous blrs so that our custom overwriteIOSMemory
// function does not somehow conflict.
// We only preserve onSE, which is this immediate BLR.
After: append(Instructions{
BLR(),
}.Bytes(), EmptyBytes(108)...),
},
After: []byte{
// These are all pointers to our so-called doNothing.
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
{
Name: "Repair textinput::EventObserver vtable",
AtOffset: 3095452,
Before: []byte{
0x80, 0x01, 0x44, 0x50, // onSE
0x80, 0x01, 0x44, 0x40, // onEvent
0x80, 0x01, 0x44, 0x30, // onCommand
0x80, 0x01, 0x44, 0x20, // onInput
0x80, 0x01, 0x44, 0x10, // onOK
0x80, 0x01, 0x44, 0x00, // onCancel
0x80, 0x01, 0x43, 0xf0, // onOutOfLength
},
After: []byte{
// These are all pointers to our so-called doNothing.
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0,
},
},
},
Patch{
Name: "Repair ipl::keyboard::EventObserver vtable",
AtOffset: 3097888,
{
Name: "Repair ipl::keyboard::EventObserver vtable",
AtOffset: 3097888,
Before: []byte{
0x80, 0x01, 0x44, 0x50, // textinput::EventObserver::onSE
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched
0x80, 0x01, 0x44, 0x30, // textinput::EventObserver::onCommand
0x80, 0x01, 0x85, 0x20, // onSE - not patching
0x80, 0x01, 0x87, 0x40, // onOK - not patching
0x80, 0x01, 0x87, 0x60, // onCancel - not patching
0x80, 0x01, 0x43, 0xF0, // textinput::EventObserver::onOutOfLength
Before: []byte{
0x80, 0x01, 0x44, 0x50, // textinput::EventObserver::onSE
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched
0x80, 0x01, 0x44, 0x30, // textinput::EventObserver::onCommand
0x80, 0x01, 0x85, 0x20, // onSE - not patching
0x80, 0x01, 0x87, 0x40, // onOK - not patching
0x80, 0x01, 0x87, 0x60, // onCancel - not patching
0x80, 0x01, 0x43, 0xF0, // textinput::EventObserver::onOutOfLength
},
After: []byte{
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
0x80, 0x01, 0x85, 0x20, // onSE - not patching
0x80, 0x01, 0x87, 0x40, // onOK - not patching
0x80, 0x01, 0x87, 0x60, // onCancel - not patching
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
},
},
After: []byte{
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
0x80, 0x01, 0x85, 0x20, // onSE - not patching
0x80, 0x01, 0x87, 0x40, // onOK - not patching
0x80, 0x01, 0x87, 0x60, // onCancel - not patching
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
{
Name: "Insert patch table",
AtOffset: 3205088,
Before: EmptyBytes(52),
After: []byte{
//////////////
// PATCH #1 //
//////////////
// We want to write to MEM_PROT at 0x0d8b420a.
// For us, this is mapped to 0xcd8b420a.
0xcd, 0x8b, 0x42, 0x0a,
// We are going to write the value 0x2 to unlock everything.
0x00, 0x00, 0x00, 0x02,
//////////////
// PATCH #2 //
//////////////
// We want to write to IOSC_VerifyPublicKeySign at 0x13a73ad4.
// For us, this is mapped to 0x92a73ad4.
0x93, 0xa7, 0x3a, 0xd4,
// 0x20004770 is equivalent in ARM THUMB to:
// mov r0, #0x0
// bx lr
0x20, 0x00, 0x47, 0x70,
// Not a patch! This is here so we have shorter assembly.
// 0xcd8005a0 is the location of LT_CHIPREVID.
0xcd, 0x80, 0x05, 0xa0,
// We're attempting to compare 0xcafe.
0x00, 0x00, 0xca, 0xfe,
//////////////////////////
// PATCH #3 - vWii only //
//////////////////////////
// Patch location:
// We want to write at 0x20102100, aka "ES_AddTicket".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x21, 0x00,
// The original code has a few conditionals preventing system title usage.
// We simply branch off past these.
// 0x681ae008 is equivalent in ARM THUMB to:
// ldr r2,[r3,#0x0] ; original code we wish to preserve
// ; so we can write 32 bits
// b +0x14 ; branch past conditionals
0x68, 0x1a, 0xe0, 0x08,
//////////////////////////
// PATCH #4 - vWii only //
//////////////////////////
// We want to write to 0x20103240, aka "ES_AddTitleStart".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x32, 0x40,
// The original code has a few conditionals preventing system title usage.
// 0xe00846c0 is equivalent in ARM THUMB to:
// b +0x8 ; branch past conditionals
// add sp,#0x0 ; recommended THUMB nop
0xe0, 0x08, 0xb0, 0x00,
//////////////////////////
// PATCH #5 - vWii only //
//////////////////////////
// Lastly, we want to write to 0x20103564, aka "ES_AddContentStart".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x35, 0x64,
// The original code has a few conditionals preventing system title usage.
// We simply branch off past these.
// 0xe00c46c0 is equivalent in ARM THUMB to:
// b +0xc ; branch past conditionals
// add sp,#0x0 ; recommended THUMB nop
0xe0, 0x0c, 0xb0, 0x00,
// This is additionally not a patch!
// We use this to store our ideal MEM2 mapping.
0x93, 0x00, 0x01, 0xff,
},
},
},
Patch{
Name: "Insert patch table",
AtOffset: 3205088,
{
Name: "Insert overwriteIOSMemory",
AtOffset: 20276,
Before: emptyBytes(52),
After: []byte{
//////////////
// PATCH #1 //
//////////////
// We want to write to MEM_PROT at 0x0d8b420a.
// For us, this is mapped to 0xcd8b420a.
0xcd, 0x8b, 0x42, 0x0a,
// We are going to write the value 0x2 to unlock everything.
0x00, 0x00, 0x00, 0x02,
// This area should be cleared in the patch
// "Clear extraneous functions".
Before: EmptyBytes(108),
After: Instructions{
// Our patch table is available at 0x803126e0.
LIS(R8, 0x8031),
ORI(R8, R8, 0x26e0),
//////////////
// PATCH #2 //
//////////////
// We want to write to IOSC_VerifyPublicKeySign at 0x13a73ad4.
// For us, this is mapped to 0x92a73ad4.
0x93, 0xa7, 0x3a, 0xd4,
// 0x20004770 is equivalent in ARM THUMB to:
// mov r0, #0x0
// bx lr
0x20, 0x00, 0x47, 0x70,
// Load address/value pair for MEM_PROT
LWZ(R9, 0x0, R8),
LWZ(R10, 0x4, R8),
// Apply lower half
STH(R10, 0x0, R9),
// Not a patch! This is here so we have shorter assembly.
// 0xcd8005a0 is the location of LT_CHIPREVID.
0xcd, 0x80, 0x05, 0xa0,
// We're attempting to compare 0xcafe.
0x00, 0x00, 0xca, 0xfe,
// Load a better mapping for upper MEM2.
LWZ(R9, 0x30, R8),
// mtspr DBAT7U, r9
Instruction{0x7d, 0x3e, 0x8b, 0xa6},
//////////////////////////
// PATCH #3 - vWii only //
//////////////////////////
// Patch location:
// We want to write at 0x20102100, aka "ES_AddTicket".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x21, 0x00,
// The original code has a few conditionals preventing system title usage.
// We simply branch off past these.
// 0x681ae008 is equivalent in ARM THUMB to:
// ldr r2,[r3,#0x0] ; original code we wish to preserve
// ; so we can write 32 bits
// b +0x14 ; branch past conditionals
0x68, 0x1a, 0xe0, 0x08,
// Load address/value pair for IOSC_VerifyPublicKeySign
LWZ(R9, 0x8, R8),
LWZ(R10, 0xc, R8),
// Apply!
STW(R10, 0x0, R9),
//////////////////////////
// PATCH #4 - vWii only //
//////////////////////////
// We want to write to 0x20103240, aka "ES_AddTitleStart".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x32, 0x40,
// The original code has a few conditionals preventing system title usage.
// 0xe00846c0 is equivalent in ARM THUMB to:
// b +0x8 ; branch past conditionals
// add sp,#0x0 ; recommended THUMB nop
0xe0, 0x08, 0xb0, 0x00,
// The remainder of our patches are for a Wii U. We must detect such.
// Even in vWii mode, 0x0d8005a0 (LT_CHIPREVID) will have its upper
// 16 bits set to 0xCAFE. We can compare against this.
// See also: https://wiiubrew.org/wiki/Hardware/Latte_registers
// (However, we must access the cached version at 0xcd8005a0.)
LWZ(R9, 0x10, R8),
LWZ(R9, 0, R9),
// sync 0
SYNC(),
//////////////////////////
// PATCH #5 - vWii only //
//////////////////////////
// Lastly, we want to write to 0x20103564, aka "ES_AddContentStart".
// We use the address mapped to PowerPC.
0x93, 0x9f, 0x35, 0x64,
// The original code has a few conditionals preventing system title usage.
// We simply branch off past these.
// 0xe00c46c0 is equivalent in ARM THUMB to:
// b +0xc ; branch past conditionals
// add sp,#0x0 ; recommended THUMB nop
0xe0, 0x0c, 0xb0, 0x00,
// Shift this value 16 bits to the right
// in order to compare its higher value.
// rlwinm r9, r9, 0x10, 0x10, 0x1f
Instruction{0x55, 0x29, 0x84, 0x3e},
// This is additionally not a patch!
// We use this to store our ideal MEM2 mapping.
0x93, 0x00, 0x01, 0xff,
// Load 0xcafe, our comparison value
LWZ(R10, 0x14, R8),
// Compare!
// cmpw r9, r10
Instruction{0x7c, 0x09, 0x50, 0x00},
// If we're not a Wii U, carry on until the end.
// bne (last blr)
Instruction{0x40, 0x82, 0x00, 0x28},
// Apply ES_AddTicket
LWZ(R9, 0x18, R8),
LWZ(R10, 0x1c, R8),
STW(R10, 0x0, R9),
// Apply ES_AddTitleStart
LWZ(R9, 0x20, R8),
LWZ(R10, 0x24, R8),
STW(R10, 0x0, R9),
// Apply ES_AddContentStart
LWZ(R9, 0x28, R8),
LWZ(R10, 0x2c, R8),
STW(R10, 0x0, R9),
// We're finished patching!
BLR(),
}.Bytes(),
},
},
Patch{
Name: "Insert overwriteIOSMemory",
AtOffset: 20276,
{
Name: "Do not require input for exception handler",
AtOffset: 32032,
Before: Instructions{
STWU(R1, R1, 0xFC10),
}.Bytes(),
After: Instructions{
BLR(),
}.Bytes(),
},
{
Name: "Modify ipl::Exception::__ct",
AtOffset: 31904,
// This area should be cleared in the patch
// "Clear extraneous functions".
Before: emptyBytes(108),
After: Instructions{
// Our patch table is available at 0x803126e0.
LIS(R8, 0x8031),
ORI(R8, R8, 0x26e0),
// Load address/value pair for MEM_PROT
LWZ(R9, 0x0, R8),
LWZ(R10, 0x4, R8),
// Apply lower half
STH(R10, 0x0, R9),
// Load a better mapping for upper MEM2.
LWZ(R9, 0x30, R8),
// mtspr DBAT7U, r9
Instruction{0x7d, 0x3e, 0x8b, 0xa6},
// Load address/value pair for IOSC_VerifyPublicKeySign
LWZ(R9, 0x8, R8),
LWZ(R10, 0xc, R8),
// Apply!
STW(R10, 0x0, R9),
// The remainder of our patches are for a Wii U. We must detect such.
// Even in vWii mode, 0x0d8005a0 (LT_CHIPREVID) will have its upper
// 16 bits set to 0xCAFE. We can compare against this.
// See also: https://wiiubrew.org/wiki/Hardware/Latte_registers
// (However, we must access the cached version at 0xcd8005a0.)
LWZ(R9, 0x10, R8),
LWZ(R9, 0, R9),
// sync 0
SYNC(),
// Shift this value 16 bits to the right
// in order to compare its higher value.
// rlwinm r9, r9, 0x10, 0x10, 0x1f
Instruction{0x55, 0x29, 0x84, 0x3e},
// Load 0xcafe, our comparison value
LWZ(R10, 0x14, R8),
// Compare!
// cmpw r9, r10
Instruction{0x7c, 0x09, 0x50, 0x00},
// If we're not a Wii U, carry on until the end.
// bne (last blr)
Instruction{0x40, 0x82, 0x00, 0x28},
// Apply ES_AddTicket
LWZ(R9, 0x18, R8),
LWZ(R10, 0x1c, R8),
STW(R10, 0x0, R9),
// Apply ES_AddTitleStart
LWZ(R9, 0x20, R8),
LWZ(R10, 0x24, R8),
STW(R10, 0x0, R9),
// Apply ES_AddContentStart
LWZ(R9, 0x28, R8),
LWZ(R10, 0x2c, R8),
STW(R10, 0x0, R9),
// We're finished patching!
BLR(),
}.toBytes(),
},
Patch{
Name: "Do not require input for exception handler",
AtOffset: 32032,
Before: Instructions{
STWU(R1, R1, 0xFC10),
}.toBytes(),
After: Instructions{
BLR(),
}.toBytes(),
},
Patch{
Name: "Modify ipl::Exception::__ct",
AtOffset: 31904,
Before: Instructions{
BLR(),
}.toBytes(),
After: Instructions{
// b overwriteIOSMemory
Instruction{0x42, 0x80, 0xd2, 0x94},
}.toBytes(),
Before: Instructions{
BLR(),
}.Bytes(),
After: Instructions{
// b overwriteIOSMemory
Instruction{0x42, 0x80, 0xd2, 0x94},
}.Bytes(),
},
},
}

View File

@ -1,152 +0,0 @@
package main
// uint24 returns
func uint24(num uint32) [3]byte {
if num > 0x00FFFFFF {
panic("invalid uint24 passed")
}
result := fourByte(num)
return [3]byte{result[1], result[2], result[3]}
}
// Instruction represents a 4-byte PowerPC instruction.
type Instruction [4]byte
// Instructions represents a group of PowerPC instructions.
type Instructions []Instruction
// toBytes returns the bytes necessary to represent these instructions.
func (i Instructions) toBytes() []byte {
var contents []byte
for _, instruction := range i {
contents = append(contents, instruction[:]...)
}
return contents
}
// padding is not an actual instruction - it represents 4 zeros.
var padding Instruction = [4]byte{0x00, 0x00, 0x00, 0x00}
// BLR represents the blr mnemonic on PowerPC.
func BLR() Instruction {
return [4]byte{0x4E, 0x80, 0x00, 0x20}
}
// CRXOR represents a common use of CRXOR on PowerPC.
// TODO: actually implement
func CRXOR() Instruction {
return [4]byte{0x4c, 0xc6, 0x31, 0x82}
}
// ADDI represents the addi PowerPC instruction.
func ADDI(rT Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(14, rT, rA, value)
}
// LI represents the li mnemonic on PowerPC.
func LI(rT Register, value uint16) Instruction {
return ADDI(rT, 0, value)
}
// SUBI represents the subi mnemonic on PowerPC.
// TODO: handle negative values properly?
func SUBI(rT Register, rA Register, value uint16) Instruction {
return ADDI(rT, 0, -value)
}
// ADDIS represents the addis PowerPC instruction.
func ADDIS(rT Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(15, rT, rA, value)
}
// LIS represents the lis mnemonic on PowerPC.
func LIS(rT Register, value uint16) Instruction {
return ADDIS(rT, 0, value)
}
// ORI represents the ori PowerPC instruction.
func ORI(rS Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(24, rS, rA, value)
}
// STH represents the sth PowerPC instruction.
func STH(rS Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(44, rS, rA, offset)
}
// EIEIO represents the eieio PowerPC instruction.
func EIEIO() Instruction {
return [4]byte{0x7C, 0x00, 0x06, 0xAC}
}
// STW represents the stw PowerPC instruction.
func STW(rS Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(36, rS, rA, offset)
}
// LWZ represents the lwz PowerPC instruction.
func LWZ(rT Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(32, rT, rA, offset)
}
// NOP represents the nop mnemonic for PowerPC.
func NOP() Instruction {
return ORI(R0, R0, 0)
}
// CMPWI represents the cmpwi mnemonic for PowerPC.
// It does not support any other CR fields asides from 0.
func CMPWI(rA Register, value uint16) Instruction {
return EncodeInstrDForm(11, 0, rA, value)
}
// SYNC is a hack, hardcoding sync 0.
// TODO(spotlightishere): actually encode this
func SYNC() Instruction {
return [4]byte{0x7c, 0x00, 0x04, 0xac}
}
// MTSPR is a hack, hardcoding LR, r0.
// TODO(spotlightishere): actually encode this
func MTSPR() Instruction {
return [4]byte{0x7c, 0x08, 0x03, 0xa6}
}
// MFSPR is a hack, hardcoding r0, LR.
// TODO(spotlightishere): actually encode this
func MFSPR() Instruction {
return [4]byte{0x7c, 0x08, 0x02, 0xa6}
}
// STWU represents the stwu PowerPC instruction.
func STWU(rS Register, rA Register, offset uint16) Instruction {
return EncodeInstrDForm(37, rS, rA, offset)
}
// calcDestination determines the proper offset from a given
// calling address and target address.
func calcDestination(from uint, target uint) [3]byte {
// TODO(spotlightishere): Handle negative offsets properly
offset := target - from
// Sign-extend by two bytes
calc := uint32(offset >> 2)
return uint24(calc)
}
// BL represents the bl PowerPC instruction.
// It calculates the offset from the given current address and the given
// target address, saving the current address in the link register. It then branches.
func BL(current uint, target uint) Instruction {
return EncodeInstrIForm(18, calcDestination(current, target), false, true)
}
// B represents the b PowerPC instruction.
// It calculates the offset from the given current address
// and the given target address, and then branches.
func B(current uint, target uint) Instruction {
return EncodeInstrIForm(18, calcDestination(current, target), false, false)
}

View File

@ -1,160 +0,0 @@
package main
import (
"encoding/binary"
)
// Register represents a value for a PowerPC register.
type Register byte
const (
R0 = iota
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
R16
R17
R18
R19
R20
R21
R22
R23
R24
R25
R26
R27
R28
R29
R30
R31
)
// Bits represents bits for a byte.
// If set, considered as 1. If not, 0.
type Bits [8]bool
// getBits returns a usable array of bits for the given byte.
func getBits(in byte) Bits {
return [8]bool{
(in>>7)&1 == 1,
(in>>6)&1 == 1,
(in>>5)&1 == 1,
(in>>4)&1 == 1,
(in>>3)&1 == 1,
(in>>2)&1 == 1,
(in>>1)&1 == 1,
in&1 == 1,
}
}
// getByte returns the byte represented by these bits.
func (b Bits) getByte() byte {
var result byte
for idx, truthy := range b {
if truthy {
result |= 1 << (7 - idx)
}
}
return result
}
// EncodeInstrDForm handles encoding a given opcode, RT, RA and SI.
// D-form assumes:
// - 6 bits for the opcode
// - 5 for rT
// - 5 for rA
// - 16 for SI
func EncodeInstrDForm(opcode byte, rT Register, rA Register, value uint16) Instruction {
var instr [2]Bits
opBits := getBits(opcode)
rTBits := getBits(byte(rT))
rABits := getBits(byte(rA))
instr[0] = Bits{
// We need the upper six bits for our opcode.
opBits[2],
opBits[3],
opBits[4],
opBits[5],
opBits[6],
opBits[7],
// Next, the lower two bits for rT.
rTBits[3],
rTBits[4],
}
instr[1] = Bits{
// Third, the lower three bits for rT.
rTBits[5],
rTBits[6],
rTBits[7],
// Finally, all five lowest bits for rA.
rABits[3],
rABits[4],
rABits[5],
rABits[6],
rABits[7],
}
firstInstr := instr[0].getByte()
secondInstr := instr[1].getByte()
valByte := twoByte(value)
return Instruction{firstInstr, secondInstr, valByte[0], valByte[1]}
}
// EncodeInstrIForm handles encoding a given opcode, LI, AA and LK.
// I-form assumes:
// - 6 bits for the opcode
// - 24 bits for LI
// - 1 bit for absolute (AA)
// - 1 bit for should store in link register (LK)
func EncodeInstrIForm(opcode byte, LI [3]byte, AA bool, LK bool) Instruction {
opBits := getBits(opcode)
liOne := getBits(LI[0])
liTwo := getBits(LI[1])
liThree := getBits(LI[2])
instr := [4]Bits{
{
// We need the upper six bits for our opcode.
opBits[2], opBits[3], opBits[4], opBits[5], opBits[6], opBits[7],
// Otherwise, copy LI as-is.
liOne[0], liOne[1],
},
{
liOne[2], liOne[3], liOne[4], liOne[5], liOne[6], liOne[7], liTwo[0], liTwo[1],
},
{
liTwo[2], liTwo[3], liTwo[4], liTwo[5], liTwo[6], liTwo[7], liThree[0], liThree[1],
},
{
liThree[2], liThree[3], liThree[4], liThree[5], liThree[6], liThree[7],
// Copy AA and LK as-is.
AA,
LK,
},
}
return Instruction{instr[0].getByte(), instr[1].getByte(), instr[2].getByte(), instr[3].getByte()}
}
// twoByte converts a uint16 to two big-endian bytes.
func twoByte(passed uint16) [2]byte {
result := make([]byte, 2)
binary.BigEndian.PutUint16(result, passed)
return [2]byte{result[0], result[1]}
}