diff --git a/modify_arc.go b/modify_arc.go index 5cc9f32..f965a69 100644 --- a/modify_arc.go +++ b/modify_arc.go @@ -54,7 +54,7 @@ func generateTag(tag Tag, tagContents []byte) []byte { byte(tag), } // Tag length - contents = append(contents, toLength(len(tagContents))...) + contents = append(contents, fourByte(uint32(len(tagContents)))...) // Tag contents contents = append(contents, tagContents...) @@ -120,9 +120,9 @@ func generateOperaCertStore() { file.Write(append(header, caCertTag...)) } -// toLength returns 4 bytes, suitable for the given length. -func toLength(value int) []byte { +// fourByte returns 4 bytes, suitable for the given length. +func fourByte(value uint32) []byte { holder := make([]byte, 4) - binary.BigEndian.PutUint32(holder, uint32(value)) + binary.BigEndian.PutUint32(holder, value) return holder } diff --git a/patch_custom_ca.go b/patch_custom_ca.go index fa47a14..e02267a 100644 --- a/patch_custom_ca.go +++ b/patch_custom_ca.go @@ -36,7 +36,7 @@ func LoadCustomCA() PatchSet { LWZ(R3, 0xac, R28), LWZ(R5, 0xc4, R28), // SSLSetRootCA(ssl_fd, ca_cert, cert_index) - Instruction{0x48, 0x01, 0x59, 0x49}, + BL(0x800acae4, 0x800c242c), // Check if successful CMPWI(R3, 0), @@ -46,7 +46,7 @@ func LoadCustomCA() PatchSet { // Return error -1004 if failed LI(R3, 0xfc14), // b FUNCTION_PROLOG - Instruction{0x48, 0x00, 0x00, 0xbc}, + B(0x800acaf4, 0x800acbb0), // ---- @@ -56,7 +56,7 @@ func LoadCustomCA() PatchSet { LWZ(R3, 0xac, R28), LWZ(R4, 0xd8, R28), // SSLSetBuiltinRootCA(ssl_fd, cert_index) - Instruction{0x48, 0x01, 0x5a, 0x75}, + BL(0x800acb00, 0x800c2574), // Check if successful CMPWI(R3, 0), @@ -66,7 +66,7 @@ func LoadCustomCA() PatchSet { // Return error -1004 if failed LI(R3, 0xfc14), // b FUNCTION_PROLOG - Instruction{0x48, 0x00, 0x00, 0xa0}, + B(0x800acb10, 0x800acbb0), }.toBytes(), After: Instructions{ // Our certificate is present at 0x802e97b8. @@ -84,7 +84,7 @@ func LoadCustomCA() PatchSet { LWZ(R3, 0xac, R28), // SSLSetRootCA(ssl_fd, ca_cert, cert_index) - Instruction{0x48, 0x01, 0x59, 0x49}, + BL(0x800acae4, 0x800c242c), // Check for errors CMPWI(R3, 0), @@ -94,7 +94,7 @@ func LoadCustomCA() PatchSet { // Return error -1004 if failed LI(R3, 0xfc14), // b FUNCTION_PROLOG - Instruction{0x48, 0x00, 0x00, 0xbc}, + B(0x800acaf4, 0x800acbb0), // NOP the rest in order to allow execution to continue. NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), diff --git a/powerpc.go b/powerpc.go index 3838677..172edd5 100644 --- a/powerpc.go +++ b/powerpc.go @@ -1,5 +1,15 @@ 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 @@ -102,3 +112,28 @@ func MFSPR() Instruction { func STWU(rS Register, rA Register, offset uint16) Instruction { return EncodeInstrDForm(37, rS, rA, offset) } + +// calcDestination determines the proper offset from a given +// calling address and target address. +func calcDestination(from uint, target uint) [3]byte { + // TODO(spotlightishere): Handle negative offsets properly + offset := target - from + + // Sign-extend by two bytes + calc := uint32(offset >> 2) + return uint24(calc) +} + +// BL represents the bl PowerPC instruction. +// It calculates the offset from the given current address and the given +// target address, saving the current address in the link register. It then branches. +func BL(current uint, target uint) Instruction { + return EncodeInstrIForm(18, calcDestination(current, target), false, true) +} + +// B represents the b PowerPC instruction. +// It calculates the offset from the given current address +// and the given target address, and then branches. +func B(current uint, target uint) Instruction { + return EncodeInstrIForm(18, calcDestination(current, target), false, false) +} diff --git a/powerpc_encoding.go b/powerpc_encoding.go index 6c7d8a1..aa9ee46 100644 --- a/powerpc_encoding.go +++ b/powerpc_encoding.go @@ -116,6 +116,42 @@ func EncodeInstrDForm(opcode byte, rT Register, rA Register, value uint16) Instr 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)