diff --git a/go.mod b/go.mod index 8096ce1..2c9a032 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 1565f10..7a6e04c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index 9e914cb..f577b2f 100644 --- a/main.go +++ b/main.go @@ -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 { diff --git a/modify_dol.go b/modify_dol.go deleted file mode 100644 index 2749870..0000000 --- a/modify_dol.go +++ /dev/null @@ -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) -} diff --git a/patch_base_domain.go b/patch_base_domain.go index b47f7e4..78e6953 100644 --- a/patch_base_domain.go +++ b/patch_base_domain.go @@ -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)...) } diff --git a/patch_custom_ca.go b/patch_custom_ca.go index e02267a..62450fb 100644 --- a/patch_custom_ca.go +++ b/patch_custom_ca.go @@ -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(), + }, }, } } diff --git a/patch_ec_cfg_path.go b/patch_ec_cfg_path.go index 7ec2e1a..01d2e6c 100644 --- a/patch_ec_cfg_path.go +++ b/patch_ec_cfg_path.go @@ -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"), + }, }, } diff --git a/patch_ec_title_check.go b/patch_ec_title_check.go index 08a245b..c681b67 100644 --- a/patch_ec_title_check.go +++ b/patch_ec_title_check.go @@ -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(), + }, }, } diff --git a/patch_overwrite.go b/patch_overwrite.go index 602f440..70cd17f 100644 --- a/patch_overwrite.go +++ b/patch_overwrite.go @@ -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(), + }, }, } diff --git a/powerpc.go b/powerpc.go deleted file mode 100644 index 5a25ba9..0000000 --- a/powerpc.go +++ /dev/null @@ -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) -} diff --git a/powerpc_encoding.go b/powerpc_encoding.go deleted file mode 100644 index aa9ee46..0000000 --- a/powerpc_encoding.go +++ /dev/null @@ -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]} -}