mirror of
https://wiilab.wiimart.org/wiimart/WiiMart-Patcher
synced 2025-09-02 19:41:13 +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/wii-tools/GoNUSD v0.2.2
|
||||
github.com/wii-tools/arclib v1.0.0
|
||||
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964
|
||||
github.com/wii-tools/wadlib v0.3.1
|
||||
)
|
||||
|
2
go.sum
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/arclib v1.0.0 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms=
|
||||
github.com/wii-tools/arclib v1.0.0/go.mod h1:uXFan/NSXoQ2pOVPN4ugZ4nJX7esBnjB1QUgVrEzK/4=
|
||||
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964 h1:/ZeMBIoblxzyhKHQJncvBZaxqi6RZz4rm4QvL0QiguA=
|
||||
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964/go.mod h1:bt/52tMfh1hmEXwFh1i1AOEMNHoe0jSXkcRunOSdFkc=
|
||||
github.com/wii-tools/wadlib v0.3.1 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8=
|
||||
github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ=
|
||||
|
17
main.go
17
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 {
|
||||
|
@ -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
|
||||
|
||||
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)...)
|
||||
}
|
||||
|
@ -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(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
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