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/logrusorgru/aurora/v3 v3.0.0
github.com/wii-tools/GoNUSD v0.2.2 github.com/wii-tools/GoNUSD v0.2.2
github.com/wii-tools/arclib v1.0.0 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 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/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 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms=
github.com/wii-tools/arclib v1.0.0/go.mod h1:uXFan/NSXoQ2pOVPN4ugZ4nJX7esBnjB1QUgVrEzK/4= 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 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8=
github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ= 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/logrusorgru/aurora/v3"
"github.com/wii-tools/GoNUSD" "github.com/wii-tools/GoNUSD"
"github.com/wii-tools/arclib" "github.com/wii-tools/arclib"
"github.com/wii-tools/powerpc"
"github.com/wii-tools/wadlib" "github.com/wii-tools/wadlib"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
@ -137,6 +138,22 @@ func main() {
writeOut("patched.wad", output) 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. // check has an anxiety attack if things go awry.
func check(err error) { func check(err error) {
if err != nil { 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 package main
import "strings" import (
"strings"
. "github.com/wii-tools/powerpc"
)
const ( const (
NintendoBaseDomain = "shop.wii.com" NintendoBaseDomain = "shop.wii.com"
@ -15,38 +19,41 @@ const (
// See docs/patch_base_domain.md for more information. // See docs/patch_base_domain.md for more information.
func PatchBaseDomain() PatchSet { func PatchBaseDomain() PatchSet {
return PatchSet{ return PatchSet{
Patch{ Name: "Change Base Domain",
Name: "Modify /startup domain", Patches: []Patch{
{
Name: "Modify /startup domain",
Before: []byte(ShowManualURL), Before: []byte(ShowManualURL),
After: padReplace(ShowManualURL), After: padReplace(ShowManualURL),
}, },
Patch{ {
Name: "Modify oss-auth URL", Name: "Modify oss-auth URL",
AtOffset: 3180692, AtOffset: 3180692,
Before: []byte(GetLogURL), Before: []byte(GetLogURL),
After: padReplace(GetLogURL), After: padReplace(GetLogURL),
}, },
Patch{ {
Name: "Modify trusted base domain prefix", Name: "Modify trusted base domain prefix",
AtOffset: 3323432, AtOffset: 3323432,
Before: []byte(TrustedDomain), Before: []byte(TrustedDomain),
After: padReplace(TrustedDomain), After: padReplace(TrustedDomain),
}, },
Patch{ {
Name: "Modify ECS SOAP endpoint URL", Name: "Modify ECS SOAP endpoint URL",
AtOffset: 3268896, AtOffset: 3268896,
Before: []byte(ECommerceBaseURL), Before: []byte(ECommerceBaseURL),
After: padReplace(ECommerceBaseURL), After: padReplace(ECommerceBaseURL),
}, },
Patch{ {
Name: "Wildcard replace other instances", Name: "Wildcard replace other instances",
Before: []byte(NintendoBaseDomain), Before: []byte(NintendoBaseDomain),
After: padReplace(baseDomain), After: padReplace(baseDomain),
},
}, },
} }
} }
@ -60,5 +67,5 @@ func padReplace(url string) []byte {
} }
padding := len(url) - len(replaced) 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 package main
import (
. "github.com/wii-tools/powerpc"
)
// LoadCustomCA loads our custom certificate, either generated or loaded, // LoadCustomCA loads our custom certificate, either generated or loaded,
// into the IOS trust store for EC usage. // into the IOS trust store for EC usage.
// It is assumed that rootCertificate has been loaded upon invoking this patchset. // It is assumed that rootCertificate has been loaded upon invoking this patchset.
// See docs/patch_custom_ca_ios.md for more information. // See docs/patch_custom_ca_ios.md for more information.
func LoadCustomCA() PatchSet { func LoadCustomCA() PatchSet {
return PatchSet{ return PatchSet{
Patch{ Name: "Load Custom CA within IOS",
Name: "Insert custom CA into free space", Patches: []Patch{
AtOffset: 3037368, {
Name: "Insert custom CA into free space",
AtOffset: 3037368,
Before: emptyBytes(len(rootCertificate)), Before: EmptyBytes(len(rootCertificate)),
After: rootCertificate, After: rootCertificate,
}, },
Patch{ {
Name: "Modify NHTTPi_SocSSLConnect to load cert", Name: "Modify NHTTPi_SocSSLConnect to load cert",
AtOffset: 644624, AtOffset: 644624,
Before: Instructions{ Before: Instructions{
// Check whether internals->ca_cert is null // Check whether internals->ca_cert is null
LWZ(R4, 0xc0, R28), LWZ(R4, 0xc0, R28),
// cmpwi r4, 0 // cmpwi r4, 0
CMPWI(R4, 0), CMPWI(R4, 0),
// If it is, load the built-in root certificate. // If it is, load the built-in root certificate.
// beq LOAD_BUILTIN_ROOT_CA // beq LOAD_BUILTIN_ROOT_CA
Instruction{0x41, 0x82, 0x00, 0x20}, Instruction{0x41, 0x82, 0x00, 0x20},
// --- // ---
// It seems we are loading a custom certificate. // It seems we are loading a custom certificate.
// r3 -> ssl_fd // r3 -> ssl_fd
// r4 -> ca_cert, loaded previously // r4 -> ca_cert, loaded previously
// r5 -> cert_length // r5 -> cert_length
LWZ(R3, 0xac, R28), LWZ(R3, 0xac, R28),
LWZ(R5, 0xc4, R28), LWZ(R5, 0xc4, R28),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index) // SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c), BL(0x800acae4, 0x800c242c),
// Check if successful // Check if successful
CMPWI(R3, 0), CMPWI(R3, 0),
// beq CONTINUE_CONNECTING // beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28}, Instruction{0x41, 0x82, 0x00, 0x28},
// Return error -1004 if failed // Return error -1004 if failed
LI(R3, 0xfc14), LI(R3, 0xfc14),
// b FUNCTION_PROLOG // b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0), B(0x800acaf4, 0x800acbb0),
// ---- // ----
// It seems we are loading the built-in root CA. // It seems we are loading the built-in root CA.
// r3 -> ssl_fd // r3 -> ssl_fd
// r4 -> cert_length // r4 -> cert_length
LWZ(R3, 0xac, R28), LWZ(R3, 0xac, R28),
LWZ(R4, 0xd8, R28), LWZ(R4, 0xd8, R28),
// SSLSetBuiltinRootCA(ssl_fd, cert_index) // SSLSetBuiltinRootCA(ssl_fd, cert_index)
BL(0x800acb00, 0x800c2574), BL(0x800acb00, 0x800c2574),
// Check if successful // Check if successful
CMPWI(R3, 0), CMPWI(R3, 0),
// beq CONTINUE_CONNECTING // beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x0c}, Instruction{0x41, 0x82, 0x00, 0x0c},
// Return error -1004 if failed // Return error -1004 if failed
LI(R3, 0xfc14), LI(R3, 0xfc14),
// b FUNCTION_PROLOG // b FUNCTION_PROLOG
B(0x800acb10, 0x800acbb0), B(0x800acb10, 0x800acbb0),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
// Our certificate is present at 0x802e97b8. // Our certificate is present at 0x802e97b8.
// r4 is the second parameter of SSLSetRootCA, the ca_cert pointer. // r4 is the second parameter of SSLSetRootCA, the ca_cert pointer.
LIS(R4, 0x802e), LIS(R4, 0x802e),
ORI(R4, R4, 0x97b8), ORI(R4, R4, 0x97b8),
// r5 is the third parameter of SSLSetRootCA, the cert_length field. // r5 is the third parameter of SSLSetRootCA, the cert_length field.
// xor r5, r5, r5 // xor r5, r5, r5
Instruction{0x7c, 0xa5, 0x2a, 0x78}, Instruction{0x7c, 0xa5, 0x2a, 0x78},
ADDI(R5, R5, uint16(len(rootCertificate))), ADDI(R5, R5, uint16(len(rootCertificate))),
// r3 is the first parameter of SSLSetRootCA, the ssl_fd. // r3 is the first parameter of SSLSetRootCA, the ssl_fd.
// We load it exactly as Nintendo does. // We load it exactly as Nintendo does.
LWZ(R3, 0xac, R28), LWZ(R3, 0xac, R28),
// SSLSetRootCA(ssl_fd, ca_cert, cert_index) // SSLSetRootCA(ssl_fd, ca_cert, cert_index)
BL(0x800acae4, 0x800c242c), BL(0x800acae4, 0x800c242c),
// Check for errors // Check for errors
CMPWI(R3, 0), CMPWI(R3, 0),
// beq CONTINUE_CONNECTING // beq CONTINUE_CONNECTING
Instruction{0x41, 0x82, 0x00, 0x28}, Instruction{0x41, 0x82, 0x00, 0x28},
// Return error -1004 if failed // Return error -1004 if failed
LI(R3, 0xfc14), LI(R3, 0xfc14),
// b FUNCTION_PROLOG // b FUNCTION_PROLOG
B(0x800acaf4, 0x800acbb0), B(0x800acaf4, 0x800acbb0),
// NOP the rest in order to allow execution to continue. // NOP the rest in order to allow execution to continue.
NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(),
}.toBytes(), }.Bytes(),
},
}, },
} }
} }

View File

@ -1,11 +1,17 @@
package main package main
var PatchECCfgPath = PatchSet{ import (
Patch{ . "github.com/wii-tools/powerpc"
Name: "Change EC Configuration Path", )
AtOffset: 3319968,
Before: []byte("ec.cfg\x00\x00"), var PatchECCfgPath = PatchSet{
After: []byte("osc.cfg\x00"), 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 package main
import (
. "github.com/wii-tools/powerpc"
)
var NegateECTitle = PatchSet{ var NegateECTitle = PatchSet{
Patch{ Name: "Negate EC Title Check",
Name: "Permit downloading all titles",
AtOffset: 619648,
// Generic function prolog Patches: []Patch{
Before: Instructions{ Patch{
STWU(R1, R1, 0xffe0), Name: "Permit downloading all titles",
MFSPR(), AtOffset: 619648,
}.toBytes(),
// Immediately return true // Generic function prolog
After: Instructions{ Before: Instructions{
LI(R3, 1), STWU(R1, R1, 0xffe0),
BLR(), MFSPR(),
}.toBytes(), }.Bytes(),
},
Patch{
Name: "Mark all titles as managed",
AtOffset: 620656,
Before: Instructions{ // Immediately return true
STWU(R1, R1, 0xfff0), After: Instructions{
MFSPR(), LI(R3, 1),
}.toBytes(), BLR(),
After: Instructions{ }.Bytes(),
LI(R3, 1), },
BLR(), Patch{
}.toBytes(), Name: "Mark all titles as managed",
}, AtOffset: 620656,
Patch{
Name: "Mark all tickets as managed", Before: Instructions{
AtOffset: 619904, STWU(R1, R1, 0xfff0),
Before: Instructions{ MFSPR(),
STWU(R1, R1, 0xfff0), }.Bytes(),
MFSPR(), After: Instructions{
}.toBytes(), LI(R3, 1),
After: Instructions{ BLR(),
LI(R3, 1), }.Bytes(),
BLR(), },
}.toBytes(), Patch{
}, Name: "Mark all tickets as managed",
Patch{ AtOffset: 619904,
Name: "Nullify ec::removeAllTitles", Before: Instructions{
AtOffset: 588368, STWU(R1, R1, 0xfff0),
Before: Instructions{ MFSPR(),
STWU(R1, R1, 0xffc0), }.Bytes(),
MFSPR(), After: Instructions{
}.toBytes(), LI(R3, 1),
After: Instructions{ BLR(),
LI(R3, 0), }.Bytes(),
BLR(), },
}.toBytes(), 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 package main
import (
. "github.com/wii-tools/powerpc"
)
// OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign. // OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign.
// See docs/patch_overwrite_ios.md for more information. // See docs/patch_overwrite_ios.md for more information.
var OverwriteIOSPatch = PatchSet{ var OverwriteIOSPatch = PatchSet{
Patch{ Name: "Overwrite IOS Syscall for ES",
Name: "Clear extraneous functions", Patches: []Patch{
AtOffset: 20272, {
Name: "Clear extraneous functions",
AtOffset: 20272,
Before: Instructions{ Before: Instructions{
// Function: textinput::EventObserver::onOutOfLength // Function: textinput::EventObserver::onOutOfLength
LIS(R3, 0x802f), LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ac4), ADDI(R3, R3, 0x7ac4),
CRXOR(), CRXOR(),
// b printf // b printf
Instruction{0x48, 0x2a, 0x8b, 0x78}, Instruction{0x48, 0x2a, 0x8b, 0x78},
// Function: textinput::EventObserver::onCancel // Function: textinput::EventObserver::onCancel
LIS(R3, 0x802f), LIS(R3, 0x802f),
ADDI(R3, R3, 0x7ab8), ADDI(R3, R3, 0x7ab8),
CRXOR(), CRXOR(),
// b printf // b printf
Instruction{0x48, 0x2a, 0x8b, 0x68}, Instruction{0x48, 0x2a, 0x8b, 0x68},
// Function: textinput::EventObserver::onOK // Function: textinput::EventObserver::onOK
// subi r3, r3, 0x7fe0 // subi r3, r3, 0x7fe0
Instruction{0x38, 0x6d, 0x80, 0x20}, Instruction{0x38, 0x6d, 0x80, 0x20},
CRXOR(), CRXOR(),
// b printf // b printf
Instruction{0x48, 0x2a, 0x8b, 0x5c}, Instruction{0x48, 0x2a, 0x8b, 0x5c},
padding, Padding,
// Function: textinput::EventObserver::onSE // Function: textinput::EventObserver::onSE
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onEvent // Function: textinput::EventObserver::onEvent
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onCommand // Function: textinput::EventObserver::onCommand
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onInput // Function: textinput::EventObserver::onInput
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
}.toBytes(), }.Bytes(),
// We wish to clear extraneous blrs so that our custom overwriteIOSMemory // We wish to clear extraneous blrs so that our custom overwriteIOSMemory
// function does not somehow conflict. // function does not somehow conflict.
// We only preserve onSE, which is this immediate BLR. // We only preserve onSE, which is this immediate BLR.
After: append(Instructions{ After: append(Instructions{
BLR(), BLR(),
}.toBytes(), emptyBytes(108)...), }.Bytes(), 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
}, },
After: []byte{ {
// These are all pointers to our so-called doNothing. Name: "Repair textinput::EventObserver vtable",
0x80, 0x01, 0x43, 0xf0, AtOffset: 3095452,
0x80, 0x01, 0x43, 0xf0,
0x80, 0x01, 0x43, 0xf0, Before: []byte{
0x80, 0x01, 0x43, 0xf0, 0x80, 0x01, 0x44, 0x50, // onSE
0x80, 0x01, 0x43, 0xf0, 0x80, 0x01, 0x44, 0x40, // onEvent
0x80, 0x01, 0x43, 0xf0, 0x80, 0x01, 0x44, 0x30, // onCommand
0x80, 0x01, 0x43, 0xf0, 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",
Name: "Repair ipl::keyboard::EventObserver vtable", AtOffset: 3097888,
AtOffset: 3097888,
Before: []byte{ Before: []byte{
0x80, 0x01, 0x44, 0x50, // textinput::EventObserver::onSE 0x80, 0x01, 0x44, 0x50, // textinput::EventObserver::onSE
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched 0x80, 0x01, 0x84, 0xE0, // onCommand - not patched
0x80, 0x01, 0x44, 0x30, // textinput::EventObserver::onCommand 0x80, 0x01, 0x44, 0x30, // textinput::EventObserver::onCommand
0x80, 0x01, 0x85, 0x20, // onSE - not patching 0x80, 0x01, 0x85, 0x20, // onSE - not patching
0x80, 0x01, 0x87, 0x40, // onOK - not patching 0x80, 0x01, 0x87, 0x40, // onOK - not patching
0x80, 0x01, 0x87, 0x60, // onCancel - not patching 0x80, 0x01, 0x87, 0x60, // onCancel - not patching
0x80, 0x01, 0x43, 0xF0, // textinput::EventObserver::onOutOfLength 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 Name: "Insert patch table",
0x80, 0x01, 0x84, 0xE0, // onCommand - not patched AtOffset: 3205088,
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
0x80, 0x01, 0x85, 0x20, // onSE - not patching Before: EmptyBytes(52),
0x80, 0x01, 0x87, 0x40, // onOK - not patching After: []byte{
0x80, 0x01, 0x87, 0x60, // onCancel - not patching //////////////
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing // 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 overwriteIOSMemory",
Name: "Insert patch table", AtOffset: 20276,
AtOffset: 3205088,
Before: emptyBytes(52), // This area should be cleared in the patch
After: []byte{ // "Clear extraneous functions".
////////////// Before: EmptyBytes(108),
// PATCH #1 // After: Instructions{
////////////// // Our patch table is available at 0x803126e0.
// We want to write to MEM_PROT at 0x0d8b420a. LIS(R8, 0x8031),
// For us, this is mapped to 0xcd8b420a. ORI(R8, R8, 0x26e0),
0xcd, 0x8b, 0x42, 0x0a,
// We are going to write the value 0x2 to unlock everything.
0x00, 0x00, 0x00, 0x02,
////////////// // Load address/value pair for MEM_PROT
// PATCH #2 // LWZ(R9, 0x0, R8),
////////////// LWZ(R10, 0x4, R8),
// We want to write to IOSC_VerifyPublicKeySign at 0x13a73ad4. // Apply lower half
// For us, this is mapped to 0x92a73ad4. STH(R10, 0x0, R9),
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. // Load a better mapping for upper MEM2.
// 0xcd8005a0 is the location of LT_CHIPREVID. LWZ(R9, 0x30, R8),
0xcd, 0x80, 0x05, 0xa0, // mtspr DBAT7U, r9
// We're attempting to compare 0xcafe. Instruction{0x7d, 0x3e, 0x8b, 0xa6},
0x00, 0x00, 0xca, 0xfe,
////////////////////////// // Load address/value pair for IOSC_VerifyPublicKeySign
// PATCH #3 - vWii only // LWZ(R9, 0x8, R8),
////////////////////////// LWZ(R10, 0xc, R8),
// Patch location: // Apply!
// We want to write at 0x20102100, aka "ES_AddTicket". STW(R10, 0x0, R9),
// 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,
////////////////////////// // The remainder of our patches are for a Wii U. We must detect such.
// PATCH #4 - vWii only // // Even in vWii mode, 0x0d8005a0 (LT_CHIPREVID) will have its upper
////////////////////////// // 16 bits set to 0xCAFE. We can compare against this.
// We want to write to 0x20103240, aka "ES_AddTitleStart". // See also: https://wiiubrew.org/wiki/Hardware/Latte_registers
// We use the address mapped to PowerPC. // (However, we must access the cached version at 0xcd8005a0.)
0x93, 0x9f, 0x32, 0x40, LWZ(R9, 0x10, R8),
// The original code has a few conditionals preventing system title usage. LWZ(R9, 0, R9),
// 0xe00846c0 is equivalent in ARM THUMB to: // sync 0
// b +0x8 ; branch past conditionals SYNC(),
// add sp,#0x0 ; recommended THUMB nop
0xe0, 0x08, 0xb0, 0x00,
////////////////////////// // Shift this value 16 bits to the right
// PATCH #5 - vWii only // // in order to compare its higher value.
////////////////////////// // rlwinm r9, r9, 0x10, 0x10, 0x1f
// Lastly, we want to write to 0x20103564, aka "ES_AddContentStart". Instruction{0x55, 0x29, 0x84, 0x3e},
// 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! // Load 0xcafe, our comparison value
// We use this to store our ideal MEM2 mapping. LWZ(R10, 0x14, R8),
0x93, 0x00, 0x01, 0xff,
// 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: "Do not require input for exception handler",
Name: "Insert overwriteIOSMemory", AtOffset: 32032,
AtOffset: 20276, 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 Before: Instructions{
// "Clear extraneous functions". BLR(),
Before: emptyBytes(108), }.Bytes(),
After: Instructions{ After: Instructions{
// Our patch table is available at 0x803126e0. // b overwriteIOSMemory
LIS(R8, 0x8031), Instruction{0x42, 0x80, 0xd2, 0x94},
ORI(R8, R8, 0x26e0), }.Bytes(),
},
// 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(),
}, },
} }

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]}
}