mirror of
https://wiilab.wiimart.org/wiimart/WiiMart-Patcher
synced 2025-09-03 20:11:19 +02:00
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:
parent
4a232c552b
commit
e17dbf9ce1
1
go.mod
1
go.mod
@ -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
2
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/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
17
main.go
@ -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 {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -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)...)
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
152
powerpc.go
152
powerpc.go
@ -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)
|
|
||||||
}
|
|
@ -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]}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user