From 957d69f118fc8910e1d5082644ae2857ef5276cd Mon Sep 17 00:00:00 2001 From: Spotlight Date: Thu, 30 Dec 2021 21:39:28 -0600 Subject: [PATCH] Add PowerPC instruction encoding With this, we can write assembly directly within patches. This assists for easier tweaks of code, and no need to manually assemble for rapid prototyping. --- patches.go | 78 ++++++++++++-------------- powerpc.go | 52 +++++++++++++++++- powerpc_encoding.go | 130 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 43 deletions(-) create mode 100644 powerpc_encoding.go diff --git a/patches.go b/patches.go index 2e98647..585f498 100644 --- a/patches.go +++ b/patches.go @@ -30,9 +30,11 @@ var OverwriteIOSPatch = PatchSet{ padding, }.toBytes(), - // We wish to clear these so that our custom overwriteIOSMemory - // function does not somehow conflict. - After: emptyBytes(64), + // We wish to clear extraneous blrs so that our custom overwriteIOSMemory + // function does not somehow conflict. We only preserve onSE. + After: append(Instructions{ + BLR(), + }.toBytes(), emptyBytes(60)...), }, Patch{ Name: "Repair textinput::EventObserver vtable", @@ -73,70 +75,62 @@ var OverwriteIOSPatch = PatchSet{ // This area should be cleared. Before: emptyBytes(48), - After: []byte{ + After: Instructions{ // We want r9 to store the location of MEM_PROT at 0x0d8b420a. // For us, this is mapped to 0xcd8b420a. - // lis r9, 0xcd8b - 0x3D, 0x20, 0xCD, 0x8B, - // ori r9, r9, 0x420a - 0x61, 0x29, 0x42, 0x0A, + LIS(R9, 0xcd8b), + ORI(R9, R9, 0x420a), // We want to write 0x2 and unlock everything. - // li r10, 0x02 - 0x39, 0x40, 0x00, 0x02, + LI(R10, 0x02), // Write! - // sth r10, 0x0(r9) - 0xB1, 0x49, 0x00, 0x00, + STH(R10, 0x0, R9), // Flush memory - // eieio - 0x7C, 0x00, 0x06, 0xAC, + EIEIO(), // Location of IOSC_VerifyPublicKeySign - // lis r9, 0xd3a7 - 0x3D, 0x20, 0xD3, 0xA7, - // ori r9, r9, 0x3ad4 - 0x61, 0x29, 0x3A, 0xD4, + LIS(R9, 0xd3a7), + ORI(R9, R9, 0x3ad4), // Write our custom THUMB. // 0x20004770 is equivalent to: // mov r0, #0x0 // bx lr - // lis r10, 0x2000 - 0x3D, 0x40, 0x20, 0x00, - // ori r10, r10, 0x4770 - 0x61, 0x4A, 0x47, 0x70, + LIS(R10, 0x2000), + ORI(R10, R10, 0x4770), // Write! - // stw r10, 0x0(r9) - 0x91, 0x49, 0x00, 0x00, + STW(R10, 0x0, R9), // Possibly clear cache // TODO(spotlightishere): Is this needed? // dcbi 0, r10 - 0x7C, 0x00, 0x53, 0xAC, + Instruction{0x7C, 0x00, 0x53, 0xAC}, // And finish. - // blr - 0x4E, 0x80, 0x00, 0x20, - }, + BLR(), + }.toBytes(), }, Patch{ Name: "Modify ES_InitLib", AtOffset: 2399844, // We inject in the epilog of the function. - Before: []byte{ - 0x80, 0x01, 0x00, 0x14, // lwz r0, local_res4(r1) - 0x7C, 0x08, 0x03, 0xA6, // mtspr LR, r0 - 0x38, 0x21, 0x00, 0x10, // addi r1, r1, 0x10 - 0x4E, 0x80, 0x00, 0x20, // blr - 0x00, 0x00, 0x00, 0x00, // ; empty space following function - }, - After: []byte{ - 0x80, 0x01, 0x00, 0x14, // lwz r0, local_res4(r1) - 0x4B, 0xDB, 0xB1, 0x01, // bl overwriteIOSMemory @ 0x80014428 - 0x7C, 0x08, 0x03, 0xA6, // mtspr LR, r0 - 0x38, 0x21, 0x00, 0x10, // addi r1, r1, 0x10 - 0x4E, 0x80, 0x00, 0x20, // blr - }, + Before: Instructions{ + LWZ(R0, 0x14, R1), + // mtspr LR, r0 + Instruction{0x7C, 0x08, 0x03, 0xA6}, + ADDI(R1, R1, 0x10), + BLR(), + padding, + }.toBytes(), + After: Instructions{ + LWZ(R0, 0x14, R1), + // bl overwriteIOSMemory @ 0x80014428 + Instruction{0x4B, 0xDB, 0xB1, 0x01}, + // mtspr LR, r0 + Instruction{0x7C, 0x08, 0x03, 0xA6}, + ADDI(R1, R1, 0x10), + BLR(), + }.toBytes(), }, } diff --git a/powerpc.go b/powerpc.go index ec7271e..6136dfb 100644 --- a/powerpc.go +++ b/powerpc.go @@ -20,7 +20,57 @@ func (i Instructions) toBytes() []byte { // padding is not an actual instruction - it represents 4 zeros. var padding Instruction = [4]byte{0x00, 0x00, 0x00, 0x00} -// BLR represents the blr PowerPC instruction. +// BLR represents the blr mnemonic on PowerPC. func BLR() Instruction { return [4]byte{0x4E, 0x80, 0x00, 0x20} } + +// 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. +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) +} diff --git a/powerpc_encoding.go b/powerpc_encoding.go new file mode 100644 index 0000000..f924f3e --- /dev/null +++ b/powerpc_encoding.go @@ -0,0 +1,130 @@ +package main + +import ( + "encoding/binary" + "encoding/hex" + "log" +) + +// 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) + + log.Println(hex.EncodeToString([]byte{ + firstInstr, secondInstr, valByte[0], valByte[1], + })) + + return Instruction{firstInstr, secondInstr, valByte[0], valByte[1]} +} + +// 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]} +}