├── mechadump ├── knowndumps.hpp ├── sysinfo.hpp ├── dumper.hpp ├── common.hpp ├── configexploit.hpp ├── Makefile ├── sysinfo.cpp ├── knowndumps.cpp ├── configexploit.cpp ├── dumper.cpp └── mechadump.cpp ├── mechacrypto ├── crc32.h ├── crc32.c ├── Makefile ├── cipher.h ├── util.h ├── sha256.cpp ├── keys.hpp ├── sha256.hpp ├── util.c └── cipher.c ├── patch ├── patch.ld ├── Makefile ├── patch-irq-hook.S └── patch-cdprotect-hook.S ├── mechapatchtool ├── Makefile └── mechapatchtool.cpp ├── payload ├── payload.ld ├── payload-crt0.S ├── payload-keystoredump.c ├── Makefile ├── payload-writenvm.c └── payload-fastdump.c ├── Makefile └── README.md /mechadump/knowndumps.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sha256.hpp" 4 | 5 | 6 | bool IsKnownDump(const sha256::digest& dig); 7 | -------------------------------------------------------------------------------- /mechacrypto/crc32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | 11 | uint32_t crc32(const void* data, size_t n_bytes, uint32_t crc); 12 | 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | -------------------------------------------------------------------------------- /mechadump/sysinfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | bool GetMechaconVersion(int& major, int& minor, std::string& refreshDate); 7 | bool DecodeRegionFlags(const uint8_t* eeprom, uint32_t& regionFlags); 8 | std::string GetModelString(); 9 | std::vector MakeEmptyPatchset(); 10 | -------------------------------------------------------------------------------- /patch/patch.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm") 2 | ENTRY(_start) 3 | phys = 0x00010000; 4 | SECTIONS 5 | { 6 | .text phys : AT(phys) { 7 | code = .; 8 | *(.text) 9 | *(.rodata) 10 | . = ALIGN(4); 11 | } 12 | .rodata : AT(phys + (rodata - code)) 13 | { 14 | rodata = .; 15 | *(.rodata) 16 | . = ALIGN(4); 17 | } 18 | .data : AT(phys + (data - code)) 19 | { 20 | data = .; 21 | *(.data) 22 | . = ALIGN(4); 23 | } 24 | .bss : AT(phys + (bss - code)) 25 | { 26 | bss = .; 27 | *(.bss) 28 | . = ALIGN(4); 29 | } 30 | end = .; 31 | } 32 | -------------------------------------------------------------------------------- /mechadump/dumper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef bool DumpMechaconCallback(size_t dumped, size_t total); 7 | 8 | bool IsBackDoorFunctioning(); 9 | std::vector DumpMechaconROMFastWithPayload(const void* payload, size_t payloadSize, 10 | std::function callback, DebugOutput& debug); 11 | std::vector DumpMechaconKeystoreWithPayload(const void* payload, size_t payloadSize, 12 | DebugOutput& debug); 13 | bool RestoreNVMConfigAndPatchData(const void* payload, size_t payloadSize, const uint8_t* configData, 14 | DebugOutput& debug); 15 | bool ResetMechaconAndPowerOff(); 16 | -------------------------------------------------------------------------------- /mechapatchtool/Makefile: -------------------------------------------------------------------------------- 1 | BIN_DIR = bin$(OUT_SUFFIX) 2 | ifndef OUT_DIR 3 | OUT_DIR = $(BIN_DIR) 4 | endif 5 | 6 | CXX_OBJECTS = mechapatchtool.o 7 | TARGET_CXX_OBJECTS = $(foreach obj,$(CXX_OBJECTS),$(BIN_DIR)/$(obj)) 8 | 9 | EXE = $(OUT_DIR)/mechapatchtool 10 | 11 | CC = $(TOOL_PREFIX)gcc 12 | CXX = $(TOOL_PREFIX)g++ 13 | LD = $(TOOL_PREFIX)g++ 14 | 15 | BASEFLAGS = -O2 -Wall $(DEFINES) -I$(INCLUDE_DIR) 16 | CCFLAGS = $(BASEFLAGS) -std=c11 17 | CXXFLAGS = $(BASEFLAGS) -std=c++14 18 | 19 | .PHONY: all clean 20 | 21 | all: $(BIN_DIR) $(OUT_DIR) $(EXE) 22 | 23 | clean: 24 | rm -f $(BIN_DIR)/*.o $(BIN_DIR)/*.a $(EXE) 25 | 26 | $(BIN_DIR): 27 | mkdir $@ 28 | $(OUT_DIR): 29 | mkdir $@ 30 | 31 | $(EXE): $(TARGET_CXX_OBJECTS) | $(OUT_DIR) 32 | $(LD) $^ -L$(LIB_DIR) -lmechacrypto -o $@ 33 | 34 | $(TARGET_CXX_OBJECTS): $(BIN_DIR)/%.o: %.cpp 35 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 36 | -------------------------------------------------------------------------------- /mechacrypto/crc32.c: -------------------------------------------------------------------------------- 1 | /* Simple public domain implementation of the standard CRC32 checksum. 2 | * Outputs the checksum for each file given as a command line argument. 3 | * Invalid file names and files that cause errors are silently skipped. 4 | * The program reads from stdin if it is called with no arguments. */ 5 | 6 | #include "crc32.h" 7 | 8 | static uint32_t crc32_for_byte(uint32_t r) { 9 | for(int j = 0; j < 8; ++j) 10 | r = (r & 1? 0: (uint32_t)0xEDB88320L) ^ r >> 1; 11 | return r ^ (uint32_t)0xFF000000L; 12 | } 13 | 14 | uint32_t crc32(const void *data, size_t n_bytes, uint32_t crc) { 15 | static uint32_t table[0x100]; 16 | if(!*table) 17 | for(size_t i = 0; i < 0x100; ++i) 18 | table[i] = crc32_for_byte(i); 19 | for(size_t i = 0; i < n_bytes; ++i) 20 | crc = table[(uint8_t)crc ^ ((uint8_t*)data)[i]] ^ crc >> 8; 21 | return crc; 22 | } 23 | -------------------------------------------------------------------------------- /payload/payload.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm") 2 | ENTRY(_start) 3 | __phys = 0x00010000; 4 | SECTIONS 5 | { 6 | .text __phys : AT(__phys) { 7 | __code = .; 8 | *(.text.crt0) 9 | *(.text) 10 | . = ALIGN(4); 11 | } 12 | .rodata : AT(__phys + (__rodata - __code)) 13 | { 14 | __rodata = .; 15 | *(.rodata) 16 | . = ALIGN(4); 17 | } 18 | .data : AT(__phys + (__data - __code)) 19 | { 20 | __data = .; 21 | *(.data) 22 | . = ALIGN(4); 23 | } 24 | .got : AT(__phys + (__got - __code)) 25 | { 26 | __got = .; 27 | *(.got) 28 | . = ALIGN(4); 29 | __got_end = .; 30 | } 31 | .got.plt : AT(__phys + (__got_plt - __code)) 32 | { 33 | __got_plt = .; 34 | *(.got.plt) 35 | . = ALIGN(4); 36 | } 37 | .bss : AT(__phys + (__bss - __code)) 38 | { 39 | __bss = .; 40 | *(.bss) 41 | . = ALIGN(4); 42 | __bss_end = .; 43 | } 44 | __end = .; 45 | } 46 | -------------------------------------------------------------------------------- /mechadump/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::int8_t; 8 | using std::int16_t; 9 | using std::int32_t; 10 | using std::int64_t; 11 | using std::uint8_t; 12 | using std::uint16_t; 13 | using std::uint32_t; 14 | using std::uint64_t; 15 | using std::size_t; 16 | using std::ptrdiff_t; 17 | 18 | 19 | enum : uint32_t 20 | { 21 | MECHACON_ROM_START = 0x00000000, 22 | MECHACON_ROM_SIZE = 0x44000, 23 | MECHACON_RAM_START = 0x02000000, 24 | MECHACON_RAM_SIZE = 0x4000, 25 | KEYSTORE_SIZE = 0x400, 26 | }; 27 | 28 | class DebugOutput 29 | { 30 | public: 31 | virtual ~DebugOutput() {} 32 | virtual void VPrintf(const char* format, va_list args) = 0; 33 | 34 | void Printf(const char* format, ...) 35 | { 36 | va_list args; 37 | va_start(args, format); 38 | VPrintf(format, args); 39 | va_end(args); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /mechadump/configexploit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Standard config information (i.e., without us hacking it). 4 | struct ConfigInfo 5 | { 6 | // block 2: 200-26F 7 | uint8_t m_config200[0x70]; 8 | // block 0: 270-2AF 9 | uint8_t m_config270[0x40]; 10 | // block 1: 2B0-31F 11 | uint8_t m_config2B0[0x70]; 12 | 13 | bool HasValidChecksums() const; 14 | }; 15 | 16 | // Read EEPROM at a given arbitrary offset. 17 | // The offset is given in bytes, though note that the EEPROM device is 18 | // natively addressed in 16-bit words. 19 | bool ReadNVM(uint8_t* output, unsigned offset, unsigned length); 20 | 21 | // Read the standard config blocks (EEPROM 0x200-0x31F). 22 | bool ReadAllConfig(ConfigInfo& info); 23 | 24 | // Does the exploit to write the 0xE0 bytes of patch data. 25 | // WARNING: THIS TRASHES RAM AFTER THE LAST WRITE BUFFER. DO NOT OPERATE 26 | // THE LASER WHILE RAM IS TRASHED THIS WAY. INVALID LASER PARAMETERS COULD 27 | // DAMAGE THE MACHINE. 28 | bool WriteConfigExploit(const uint8_t* patchData, int& errorCode); 29 | -------------------------------------------------------------------------------- /mechacrypto/Makefile: -------------------------------------------------------------------------------- 1 | BIN_DIR = bin$(OUT_SUFFIX) 2 | ifndef OUT_DIR 3 | OUT_DIR = $(BIN_DIR) 4 | endif 5 | 6 | C_OBJECTS = cipher.o crc32.o util.o 7 | TARGET_C_OBJECTS = $(foreach obj,$(C_OBJECTS),$(BIN_DIR)/$(obj)) 8 | CXX_OBJECTS = sha256.o 9 | TARGET_CXX_OBJECTS = $(foreach obj,$(CXX_OBJECTS),$(BIN_DIR)/$(obj)) 10 | 11 | LIBRARY = $(OUT_DIR)/libmechacrypto$(OUT_SUFFIX).a 12 | 13 | CC = $(TOOL_PREFIX)gcc 14 | CXX = $(TOOL_PREFIX)g++ 15 | AR = $(TOOL_PREFIX)ar 16 | 17 | BASEFLAGS = -O2 -Wall 18 | CCFLAGS = $(BASEFLAGS) -std=c11 19 | CXXFLAGS = $(BASEFLAGS) -std=c++14 20 | 21 | .PHONY: all clean headers 22 | 23 | all: $(BIN_DIR) $(OUT_DIR) $(LIBRARY) 24 | 25 | clean: 26 | rm -f $(BIN_DIR)/*.o $(BIN_DIR)/*.a $(LIBRARY) 27 | 28 | $(BIN_DIR): 29 | mkdir $@ 30 | $(OUT_DIR): 31 | mkdir $@ 32 | 33 | $(LIBRARY): $(TARGET_C_OBJECTS) $(TARGET_CXX_OBJECTS) | $(OUT_DIR) 34 | rm -f $@ 35 | $(AR) -crs $@ $^ 36 | 37 | $(TARGET_C_OBJECTS): $(BIN_DIR)/%.o: %.c | $(BIN_DIR) headers 38 | $(CC) $(CCFLAGS) -c $^ -o $@ 39 | $(TARGET_CXX_OBJECTS): $(BIN_DIR)/%.o: %.cpp | $(BIN_DIR) headers 40 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 41 | 42 | headers: *.h *.hpp 43 | -------------------------------------------------------------------------------- /patch/Makefile: -------------------------------------------------------------------------------- 1 | ifndef BIN_DIR 2 | BIN_DIR = . 3 | endif 4 | ifndef BIN_DIR 5 | OUT_DIR = . 6 | endif 7 | 8 | FLAGS = -march=armv4t -mfloat-abi=soft -nostdlib 9 | 10 | AS = $(TOOL_PREFIX)gcc 11 | LD = $(TOOL_PREFIX)gcc 12 | OBJCOPY = $(TOOL_PREFIX)objcopy 13 | 14 | ASFLAGS = $(FLAGS) 15 | LDFLAGS = $(FLAGS) 16 | OBJCOPYFLAGS = -O binary 17 | 18 | 19 | all: $(BIN_DIR) $(OUT_DIR) $(OUT_DIR)/patch-irq-hook.unfixed.bin $(OUT_DIR)/patch-cdprotect-hook.unfixed.bin 20 | 21 | $(BIN_DIR): 22 | mkdir $(BIN_DIR) 23 | $(OUT_DIR): 24 | mkdir $(OUT_DIR) 25 | 26 | clean: 27 | rm -f $(OUT_DIR)/*.bin $(BIN_DIR)/*.elf $(BIN_DIR)/*.o 28 | 29 | # patch-irq-hook 30 | $(OUT_DIR)/patch-irq-hook.unfixed.bin: $(BIN_DIR)/patch-irq-hook.elf 31 | $(OBJCOPY) $(OBJCOPYFLAGS) $^ $@ 32 | $(BIN_DIR)/patch-irq-hook.elf: $(BIN_DIR)/patch-irq-hook.o patch.ld 33 | $(LD) $(LDFLAGS) $(BIN_DIR)/patch-irq-hook.o -T patch.ld -o $(BIN_DIR)/patch-irq-hook.elf 34 | $(BIN_DIR)/patch-irq-hook.o: patch-irq-hook.S 35 | $(AS) -c $(ASFLAGS) $^ -o $@ 36 | 37 | # patch-cdprotect-hook 38 | $(OUT_DIR)/patch-cdprotect-hook.unfixed.bin: $(BIN_DIR)/patch-cdprotect-hook.elf 39 | $(OBJCOPY) $(OBJCOPYFLAGS) $^ $@ 40 | $(BIN_DIR)/patch-cdprotect-hook.elf: $(BIN_DIR)/patch-cdprotect-hook.o patch.ld 41 | $(LD) $(LDFLAGS) $(BIN_DIR)/patch-cdprotect-hook.o -T patch.ld -o $(BIN_DIR)/patch-cdprotect-hook.elf 42 | $(BIN_DIR)/patch-cdprotect-hook.o: patch-cdprotect-hook.S 43 | $(AS) -c $(ASFLAGS) $^ -o $@ 44 | -------------------------------------------------------------------------------- /mechacrypto/cipher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef __CIPHER_H__ 21 | #define __CIPHER_H__ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" 29 | { 30 | #endif 31 | 32 | int cipherCbcEncrypt(uint8_t *Result, const uint8_t *Data, size_t Length, 33 | const uint8_t *Keys, int KeyCount, const uint8_t IV[8]); 34 | int cipherCbcDecrypt(uint8_t *Result, const uint8_t *Data, size_t Length, 35 | const uint8_t *Keys, int KeyCount, const uint8_t IV[8]); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /payload/payload-crt0.S: -------------------------------------------------------------------------------- 1 | .section ".text.crt0" 2 | .syntax unified 3 | 4 | 5 | .globl _start 6 | .thumb 7 | .align 2 8 | _start: 9 | push {r0-r3} 10 | 11 | // Find our slide value. 12 | adr r0, .mypool 13 | ldr r1, =.mypool 14 | subs r0, r0, r1 15 | 16 | // Check whether we've relocated already. 17 | ldr r1, =__relocated 18 | adds r1, r1, r0 19 | ldr r2, [r1] 20 | cmp r2, #0 21 | bne .already_relocated 22 | movs r2, #1 23 | str r2, [r1] 24 | 25 | // Zero out the .bss section. 26 | ldr r1, =__bss 27 | adds r1, r1, r0 28 | ldr r2, =__bss_end 29 | adds r2, r2, r0 30 | movs r3, #0 31 | .bss_clear_loop: 32 | cmp r1, r2 33 | bcs .bss_clear_done 34 | str r3, [r1] 35 | adds r1, r1, #4 36 | b .bss_clear_loop 37 | .bss_clear_done: 38 | 39 | // Adjust the pointers in the GOT. 40 | ldr r1, =__got 41 | adds r1, r1, r0 42 | ldr r2, =__got_end 43 | adds r2, r2, r0 44 | .got_fixup_loop: 45 | cmp r1, r2 46 | bcs .got_fixup_done 47 | ldr r3, [r1] 48 | adds r3, r3, r0 49 | str r3, [r1] 50 | adds r1, r1, #4 51 | b .got_fixup_loop 52 | .got_fixup_done: 53 | 54 | .already_relocated: 55 | // We need a register that isn't r0...r3. r12 is expressly designated 56 | // as destroyable by linker-generated thunk code, so use that. 57 | ldr r1, =payload_start+1 58 | adds r1, r1, r0 59 | mov r12, r1 60 | pop {r0-r3} 61 | bx r12 62 | 63 | .align 2 64 | .mypool: 65 | .pool 66 | 67 | 68 | .section ".data" 69 | .align 2 70 | __relocated: 71 | .word 0 72 | -------------------------------------------------------------------------------- /payload/payload-keystoredump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | __attribute__((__target__("thumb"))) 6 | void payload_start(int dummy0, int dummy1, int dummy2, uint32_t parameter) 7 | { 8 | union 9 | { 10 | uint8_t m_bytes[8]; 11 | uint16_t m_halfWords[4]; 12 | } keyData; 13 | 14 | // Unlock the keystore mechanism. 15 | volatile uint8_t* unlockReg1 = (volatile uint8_t*) 0x94255ABC; 16 | volatile uint8_t* unlockReg2 = (volatile uint8_t*) 0x94255EF0; 17 | volatile uint8_t* unlockReg3 = (volatile uint8_t*) 0xFFFFF234; 18 | volatile uint8_t* unlockReg4 = (volatile uint8_t*) 0xFFFFF678; 19 | 20 | *unlockReg1 = 0xC9; 21 | *unlockReg2 = 0xCB; 22 | *unlockReg3 = 0x5D; 23 | *unlockReg4 = 0x58; 24 | 25 | // Select key to use. 26 | volatile uint16_t* keystoreOffset = (volatile uint16_t*) 0x03004F00; 27 | *keystoreOffset = (uint16_t) parameter; 28 | 29 | // Read key out. 30 | volatile uint16_t* keystoreData = (volatile uint16_t*) 0x03004F08; 31 | keyData.m_halfWords[0] = *keystoreData; 32 | keyData.m_halfWords[1] = *keystoreData; 33 | keyData.m_halfWords[2] = *keystoreData; 34 | keyData.m_halfWords[3] = *keystoreData; 35 | 36 | // Lock the keystore mechanism. 37 | *unlockReg1 = 0x9C; 38 | *unlockReg2 = 0xBC; 39 | *unlockReg3 = 0xD5; 40 | *unlockReg4 = 0x85; 41 | 42 | // Return the result. 43 | volatile uint8_t* scmdReply = (volatile uint8_t*) 0x036000D9; 44 | *scmdReply = 0x00; 45 | for (int i = 0; i < sizeof(keyData.m_bytes); ++i) 46 | { 47 | *scmdReply = keyData.m_bytes[i]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mechacrypto/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef __UTIL_H__ 21 | #define __UTIL_H__ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" 29 | { 30 | #endif 31 | 32 | void memrcpy(void *dst, void *src, size_t len); 33 | void memxor(const void *a, const void *b, void *Result, size_t Length); 34 | void write_le_uint16(uint8_t *buf, uint16_t val); 35 | void write_le_uint32(uint8_t *buf, uint32_t val); 36 | void write_le_uint64(uint8_t *buf, uint64_t val); 37 | uint16_t read_le_uint16(const uint8_t *buf); 38 | uint32_t read_le_uint32(const uint8_t *buf); 39 | uint64_t read_le_uint64(const uint8_t *buf); 40 | void write_be_uint16(uint8_t *buf, uint16_t val); 41 | void write_be_uint32(uint8_t *buf, uint32_t val); 42 | void write_be_uint64(uint8_t *buf, uint64_t val); 43 | uint16_t read_be_uint16(const uint8_t *buf); 44 | uint32_t read_be_uint32(const uint8_t *buf); 45 | uint64_t read_be_uint64(const uint8_t *buf); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /mechacrypto/sha256.cpp: -------------------------------------------------------------------------------- 1 | // This implementation is not very fast; it's meant for a simple 2 | // implementation that can be used for doing SHA-256 at compile time, 3 | // but also has a runtime implementation available. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "sha256.hpp" 10 | #include "util.h" 11 | 12 | 13 | void sha256::reset() 14 | { 15 | std::memset(m_buffer, 0, sizeof(m_buffer)); 16 | sha256_internal::sha256_init(m_state); 17 | m_size = 0; 18 | } 19 | 20 | 21 | void sha256::process(const void* data, std::size_t size) 22 | { 23 | const unsigned char* d = static_cast(data); 24 | 25 | // Try to use what's left in the buffer. 26 | std::size_t remaining = 64 - (m_size % 64); 27 | if ((remaining < 64) || (size <= remaining)) 28 | { 29 | std::size_t now = std::min(size, remaining); 30 | std::memcpy(&m_buffer[m_size % 64], d, now); 31 | d += now; 32 | m_size += now; 33 | size -= now; 34 | 35 | if (remaining == now) 36 | { 37 | process_block(m_buffer); 38 | } 39 | } 40 | 41 | // Handle full blocks. 42 | while (size >= 64) 43 | { 44 | process_block(d); 45 | d += 64; 46 | m_size += 64; 47 | size -= 64; 48 | } 49 | 50 | // Put the rest into the buffer. 51 | if (size > 0) 52 | { 53 | std::memcpy(m_buffer, d, size); 54 | d += size; 55 | m_size += size; 56 | size = 0; 57 | } 58 | } 59 | 60 | 61 | void sha256::process_block(const void* block) 62 | { 63 | using namespace sha256_internal; 64 | 65 | const std::uint8_t* b = static_cast(block); 66 | sha256_word input[16]; 67 | 68 | for (int i = 0; i < 16; ++i) 69 | { 70 | input[i] = read_be_uint32(b + (i * 4)); 71 | } 72 | 73 | sha256_transform(m_state, input); 74 | } 75 | 76 | 77 | void sha256::finish(digest& dig) 78 | { 79 | std::uint64_t savedSize = m_size; 80 | 81 | process("\x80", 1); 82 | 83 | while ((m_size % 64) != 64 - sizeof(uint64_t)) 84 | { 85 | process("", 1); 86 | } 87 | 88 | std::uint8_t beSize[sizeof(std::uint64_t)]; 89 | write_be_uint64(beSize, savedSize * 8); 90 | process(beSize, sizeof(beSize)); 91 | assert(m_size % 64 == 0); 92 | 93 | for (int i = 0; i < 8; ++i) 94 | { 95 | write_be_uint32(&dig[i * sizeof(std::uint32_t)], m_state[i]); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KEY_DEFINES = -DMECHA_PATCH_KEY=$(MECHA_PATCH_KEY) -DGLOBAL_FLAGS_KEY=$(GLOBAL_FLAGS_KEY) 2 | 3 | PATCHES = patch-irq-hook patch-cdprotect-hook 4 | PATCHES_UNFIXED = $(foreach obj,$(PATCHES),bin/$(obj).unfixed.bin) 5 | PATCHES_FIXED = $(foreach obj,$(PATCHES),bin/$(obj).fixed.bin) 6 | PATCHES_DECRYPTED = $(foreach obj,$(PATCHES),bin/$(obj).decrypted.bin) 7 | PAYLOADS = bin/payload-fastdump.bin bin/payload-writenvm.bin bin/payload-keystoredump.bin 8 | 9 | # Make subcommands so we can repeat them between clean and all more easily. 10 | MAKECMD_MECHADUMP = $(MAKE) TOOL_PREFIX=$(EE_PREFIX) OUT_DIR=../out LIB_DIR=../mechacrypto/lib-ee \ 11 | INCLUDE_DIR=../mechacrypto PATCH_DIR=../bin DEFINES="$(KEY_DEFINES)" -C mechadump 12 | MAKECMD_MECHAPATCHTOOL = $(MAKE) TOOL_PREFIX=$(HOST_PREFIX) OUT_DIR=../bin BIN_DIR=bin-host \ 13 | LIB_DIR=../mechacrypto/lib-host INCLUDE_DIR=../mechacrypto DEFINES="$(KEY_DEFINES)" -C mechapatchtool 14 | MAKECMD_MECHACRYPTO_HOST = $(MAKE) TOOL_PREFIX=$(HOST_PREFIX) OUT_DIR=lib-host BIN_DIR=bin-host \ 15 | -C mechacrypto 16 | MAKECMD_MECHACRYPTO_EE = $(MAKE) TOOL_PREFIX=$(EE_PREFIX) OUT_DIR=lib-ee BIN_DIR=bin-ee -C mechacrypto 17 | MAKECMD_PATCH = $(MAKE) TOOL_PREFIX=$(ARM_PREFIX) OUT_DIR=../bin BIN_DIR=bin-arm -C patch 18 | MAKECMD_PAYLOAD = $(MAKE) TOOL_PREFIX=$(ARM_PREFIX) OUT_DIR=../bin BIN_DIR=bin-arm -C payload 19 | 20 | 21 | .PHONY: all clean debugdata mechadump mechapatchtool libmechacrypto-host libmechacrypto-ee 22 | 23 | all: mechadump libmechacrypto-ee debugdata 24 | 25 | clean: 26 | rm -f bin/mechapatchtool bin/*.bin 27 | $(MAKECMD_MECHADUMP) clean 28 | $(MAKECMD_MECHAPATCHTOOL) clean 29 | $(MAKECMD_MECHACRYPTO_HOST) clean 30 | $(MAKECMD_MECHACRYPTO_EE) clean 31 | $(MAKECMD_PATCH) clean 32 | $(MAKECMD_PAYLOAD) clean 33 | 34 | bin: 35 | mkdir bin 36 | out: 37 | mkdir out 38 | 39 | debugdata: $(PATCHES_DECRYPTED) 40 | 41 | mechadump: $(PATCHES_FIXED) $(PAYLOADS) libmechacrypto-ee | bin out 42 | $(MAKECMD_MECHADUMP) 43 | 44 | $(PATCHES_DECRYPTED): %.decrypted.bin: %.fixed.bin | bin mechapatchtool 45 | bin/mechapatchtool --decrypt --in="$^" --out="$@" 46 | $(PATCHES_FIXED): %.fixed.bin: %.unfixed.bin | bin mechapatchtool 47 | bin/mechapatchtool --fixup --in="$^" --out="$@" 48 | $(PATCHES_UNFIXED): | bin 49 | $(MAKECMD_PATCH) 50 | 51 | $(PAYLOADS): 52 | $(MAKECMD_PAYLOAD) 53 | 54 | mechapatchtool: libmechacrypto-host | bin 55 | $(MAKECMD_MECHAPATCHTOOL) 56 | 57 | libmechacrypto-host: 58 | $(MAKECMD_MECHACRYPTO_HOST) 59 | libmechacrypto-ee: 60 | $(MAKECMD_MECHACRYPTO_EE) 61 | -------------------------------------------------------------------------------- /payload/Makefile: -------------------------------------------------------------------------------- 1 | ifndef BIN_DIR 2 | BIN_DIR = . 3 | endif 4 | ifndef BIN_DIR 5 | OUT_DIR = . 6 | endif 7 | 8 | CRT0 = $(BIN_DIR)/payload-crt0.o 9 | 10 | FLAGS = -march=armv4t -mfloat-abi=soft -nostdlib 11 | 12 | CC = $(TOOL_PREFIX)gcc 13 | AS = $(TOOL_PREFIX)gcc 14 | LD = $(TOOL_PREFIX)gcc 15 | OBJCOPY = $(TOOL_PREFIX)objcopy 16 | 17 | # -fno-isolate-erroneous-paths-dereference is because 0 is a valid address 18 | CCFLAGS = -O2 -std=c11 -Wall -fPIC -fno-isolate-erroneous-paths-dereference $(FLAGS) 19 | ASFLAGS = $(FLAGS) 20 | LDFLAGS = $(FLAGS) 21 | OBJCOPYFLAGS = -O binary 22 | 23 | 24 | all: $(BIN_DIR) $(OUT_DIR) $(OUT_DIR)/payload-fastdump.bin $(OUT_DIR)/payload-writenvm.bin \ 25 | $(OUT_DIR)/payload-keystoredump.bin 26 | 27 | $(BIN_DIR): 28 | mkdir $(BIN_DIR) 29 | $(OUT_DIR): 30 | mkdir $(OUT_DIR) 31 | 32 | clean: 33 | rm -f $(OUT_DIR)/payload-fastdump.bin $(BIN_DIR)/payload-fastdump.elf $(BIN_DIR)/payload-fastdump.o 34 | rm -f $(OUT_DIR)/payload-writenvm.bin $(BIN_DIR)/payload-writenvm.elf $(BIN_DIR)/payload-writenvm.o 35 | rm -f $(OUT_DIR)/payload-keystoredump.bin $(BIN_DIR)/payload-keystoredump.elf \ 36 | $(BIN_DIR)/payload-keystoredump.o 37 | 38 | $(OUT_DIR)/payload-fastdump.bin: $(BIN_DIR)/payload-fastdump.elf 39 | $(OBJCOPY) $(OBJCOPYFLAGS) $(BIN_DIR)/payload-fastdump.elf $(OUT_DIR)/payload-fastdump.bin 40 | $(BIN_DIR)/payload-fastdump.elf: $(BIN_DIR)/payload-fastdump.o $(CRT0) payload.ld 41 | $(LD) $(LDFLAGS) $(CRT0) $(BIN_DIR)/payload-fastdump.o -T payload.ld -o $(BIN_DIR)/payload-fastdump.elf 42 | $(BIN_DIR)/payload-fastdump.o: payload-fastdump.c 43 | $(CC) -c $(CCFLAGS) $^ -o $@ 44 | 45 | $(OUT_DIR)/payload-writenvm.bin: $(BIN_DIR)/payload-writenvm.elf 46 | $(OBJCOPY) $(OBJCOPYFLAGS) $(BIN_DIR)/payload-writenvm.elf $(OUT_DIR)/payload-writenvm.bin 47 | $(BIN_DIR)/payload-writenvm.elf: $(BIN_DIR)/payload-writenvm.o $(CRT0) payload.ld 48 | $(LD) $(LDFLAGS) $(CRT0) $(BIN_DIR)/payload-writenvm.o -T payload.ld -o $(BIN_DIR)/payload-writenvm.elf 49 | $(BIN_DIR)/payload-writenvm.o: payload-writenvm.c 50 | $(CC) -c $(CCFLAGS) $^ -o $@ 51 | 52 | $(OUT_DIR)/payload-keystoredump.bin: $(BIN_DIR)/payload-keystoredump.elf 53 | $(OBJCOPY) $(OBJCOPYFLAGS) $(BIN_DIR)/payload-keystoredump.elf $(OUT_DIR)/payload-keystoredump.bin 54 | $(BIN_DIR)/payload-keystoredump.elf: $(BIN_DIR)/payload-keystoredump.o $(CRT0) payload.ld 55 | $(LD) $(LDFLAGS) $(CRT0) $(BIN_DIR)/payload-keystoredump.o -T payload.ld -o \ 56 | $(BIN_DIR)/payload-keystoredump.elf 57 | $(BIN_DIR)/payload-keystoredump.o: payload-keystoredump.c 58 | $(CC) -c $(CCFLAGS) $^ -o $@ 59 | 60 | $(CRT0): payload-crt0.S 61 | $(AS) -c $(ASFLAGS) $^ -o $@ 62 | -------------------------------------------------------------------------------- /mechacrypto/keys.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "sha256.hpp" 6 | 7 | 8 | #define STRING_CONCAT_(x, y) x ## y 9 | #define STRING_CONCAT(x, y) STRING_CONCAT_(x, y) 10 | 11 | 12 | template 13 | constexpr std::uint64_t CompileTimeSHA256Part(std::uint64_t data) 14 | { 15 | using namespace sha256_internal; 16 | 17 | sha256_word state[8]{}; 18 | sha256_init(state); 19 | 20 | sha256_word input[16] 21 | { 22 | static_cast(data >> 32), 23 | static_cast(data & 0xFFFFFFFF), 24 | 0x80000000, 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, sizeof(std::uint64_t) * CHAR_BIT 27 | }; 28 | 29 | sha256_transform(state, input); 30 | 31 | return (static_cast(state[Part * 2]) << 32) + state[Part * 2 + 1]; 32 | } 33 | 34 | template 35 | struct CompileTimeSHA256 36 | { 37 | private: 38 | enum : std::uint64_t 39 | { 40 | ab = CompileTimeSHA256Part<0>(Key), 41 | cd = CompileTimeSHA256Part<1>(Key), 42 | ef = CompileTimeSHA256Part<2>(Key), 43 | gh = CompileTimeSHA256Part<3>(Key), 44 | }; 45 | public: 46 | enum : std::uint32_t 47 | { 48 | a = static_cast(ab >> 32), 49 | b = static_cast(ab & 0xFFFFFFFF), 50 | c = static_cast(cd >> 32), 51 | d = static_cast(cd & 0xFFFFFFFF), 52 | e = static_cast(ef >> 32), 53 | f = static_cast(ef & 0xFFFFFFFF), 54 | g = static_cast(gh >> 32), 55 | h = static_cast(gh & 0xFFFFFFFF), 56 | }; 57 | }; 58 | 59 | 60 | constexpr std::uint64_t g_desParityMask = 0x01010101'01010101; 61 | 62 | constexpr std::uint64_t g_mechaPatchKey = STRING_CONCAT(0x, MECHA_PATCH_KEY) & ~g_desParityMask; 63 | constexpr std::uint64_t g_globalFlagsKey = STRING_CONCAT(0x, GLOBAL_FLAGS_KEY) & ~g_desParityMask; 64 | 65 | static_assert( 66 | CompileTimeSHA256::a == 0x2B69BA04 && 67 | CompileTimeSHA256::b == 0x87A716C4 && 68 | CompileTimeSHA256::c == 0xFF66452A && 69 | CompileTimeSHA256::d == 0x816910B7 && 70 | CompileTimeSHA256::e == 0xB6CD2541 && 71 | CompileTimeSHA256::f == 0x898A24C5 && 72 | CompileTimeSHA256::g == 0x9CADB82D && 73 | CompileTimeSHA256::h == 0xF306EF29, 74 | "incorrect MECHA_PATCH_KEY"); 75 | static_assert( 76 | CompileTimeSHA256::a == 0x720CB04F && 77 | CompileTimeSHA256::b == 0x58712834 && 78 | CompileTimeSHA256::c == 0x9053B65E && 79 | CompileTimeSHA256::d == 0xEDAB6607 && 80 | CompileTimeSHA256::e == 0x85F5989D && 81 | CompileTimeSHA256::f == 0x7788F311 && 82 | CompileTimeSHA256::g == 0xE799B490 && 83 | CompileTimeSHA256::h == 0x3053EABA, 84 | "incorrect GLOBAL_FLAGS_KEY"); 85 | 86 | -------------------------------------------------------------------------------- /payload/payload-writenvm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | enum 6 | { 7 | MECHACON_ROM_START = 0x00000000, 8 | MECHACON_ROM_SIZE = 0x44000, 9 | MECHACON_EEPROM_SIZE = 0x400, 10 | }; 11 | 12 | 13 | // Address of the WriteNVM SCMD handler in ROM, saved for efficiency. 14 | int (*scmd_0B_WriteNVM)(unsigned wordOffset, uint16_t value) = (int (*)(unsigned, uint16_t)) -1; 15 | 16 | 17 | // The prolog code of WriteNVM, so we can find it in any version. 18 | static const union 19 | { 20 | uint8_t m_bytes[12]; 21 | uint32_t m_words[3]; 22 | } c_pattern_WriteNVM = { 23 | { 24 | 0xF0, 0xB5, // push {r4-r7,lr} 25 | 0x05, 0x1C, // movs r5, r0 26 | 0x0E, 0x1C, // movs r6, r1 27 | 0x01, 0x27, // movs r7, #1 28 | 0x00, 0x21, // movs r1, #0 29 | 0x01, 0x20, // movs r0, #1 30 | } 31 | }; 32 | 33 | 34 | __attribute__((__target__("thumb"))) 35 | void payload_start(int dummy0, int dummy1, int dummy2, uint32_t parameter) 36 | { 37 | volatile uint8_t* scmdReply = (volatile uint8_t*) 0x036000D9; 38 | 39 | // Search for WriteNVM in ROM if we haven't yet. 40 | if (scmd_0B_WriteNVM == (int (*)(unsigned, uint16_t)) -1) 41 | { 42 | // NOTE: This code needs the -fno-isolate-erroneous-paths-dereference 43 | // GCC command line parameter, because otherwise it will assume that 44 | // reading null will crash and skip this code. 45 | const uint32_t* romEnd = (const uint32_t*) (MECHACON_ROM_START + 46 | MECHACON_ROM_SIZE - sizeof(c_pattern_WriteNVM)); 47 | const uint32_t* search; 48 | uint32_t firstWord = c_pattern_WriteNVM.m_words[0]; 49 | for (search = (const uint32_t*) MECHACON_ROM_START; search <= romEnd; ++search) 50 | { 51 | if (*search == firstWord) 52 | { 53 | if ((search[1] == c_pattern_WriteNVM.m_words[1]) && 54 | (search[2] == c_pattern_WriteNVM.m_words[2])) 55 | { 56 | goto Found; 57 | } 58 | } 59 | } 60 | 61 | *scmdReply = 0x86; 62 | return; 63 | 64 | Found: 65 | // Don't forget to OR with 1 because it's a Thumb function. 66 | scmd_0B_WriteNVM = (int (*)(unsigned, uint16_t)) (((uintptr_t) search) | 1); 67 | } 68 | 69 | // Act the same way as the real WriteNVM: use a big-endian address 70 | // and value. The back door doesn't know that the parameter is bytes 71 | // rather than a uint32_t, so put it back and parse it that way. 72 | union 73 | { 74 | uint32_t m_word; 75 | uint8_t m_bytes[4]; 76 | } converter; 77 | converter.m_word = parameter; 78 | 79 | unsigned wordOffset = (((unsigned) converter.m_bytes[0]) << 8) + converter.m_bytes[1]; 80 | uint16_t value = (uint16_t) ((((unsigned) converter.m_bytes[2]) << 8) + converter.m_bytes[3]); 81 | 82 | // Address out of range? 83 | if (wordOffset > MECHACON_EEPROM_SIZE / sizeof(uint16_t)) 84 | { 85 | *scmdReply = 0x80; 86 | return; 87 | } 88 | 89 | // Execute and return the result. 90 | int result = (*scmd_0B_WriteNVM)(wordOffset, value); 91 | *scmdReply = (uint8_t) result; 92 | } 93 | -------------------------------------------------------------------------------- /mechadump/Makefile: -------------------------------------------------------------------------------- 1 | BIN_DIR = bin$(OUT_SUFFIX) 2 | ifndef OUT_DIR 3 | OUT_DIR = $(BIN_DIR) 4 | endif 5 | 6 | IRXES = fileXio iomanX mcman mcserv mtapman padman sio2man usbd usbhdfsd 7 | IRX_FILES = $(foreach irx,$(IRXES),$(PS2SDK)/iop/irx/$(irx).irx) 8 | IRX_SOURCES = $(foreach irx,$(IRXES),$(BIN_DIR)/$(irx).irx.c) 9 | IRX_OBJECTS = $(foreach irx,$(IRXES),$(BIN_DIR)/$(irx).irx.o) 10 | 11 | PATCHES = patch-irq-hook patch-cdprotect-hook 12 | PAYLOADS = payload-fastdump payload-writenvm payload-keystoredump 13 | PATCH_OBJECTS = $(foreach obj,$(PATCHES),$(BIN_DIR)/$(obj).fixed.o) 14 | PAYLOAD_OBJECTS = $(foreach obj,$(PAYLOADS),$(BIN_DIR)/$(obj).o) 15 | BIN_OBJECTS = $(PATCH_OBJECTS) $(PAYLOAD_OBJECTS) $(IRX_OBJECTS) 16 | 17 | C_OBJECTS = 18 | TARGET_C_OBJECTS = $(foreach obj,$(C_OBJECTS),$(BIN_DIR)/$(obj)) 19 | CXX_OBJECTS = configexploit.o dumper.o knowndumps.o mechadump.o sysinfo.o 20 | TARGET_CXX_OBJECTS = $(foreach obj,$(CXX_OBJECTS),$(BIN_DIR)/$(obj)) 21 | 22 | EE_DEFINES = -D_EE -G0 23 | EE_INCLUDE = -I$(PS2SDK)/ee/include -I$(PS2SDK)/common/include 24 | EE_LDFLAGS = -L$(PS2SDK)/ee/lib -T$(PS2SDK)/ee/startup/linkfile 25 | 26 | EXE = $(OUT_DIR)/mechadump.elf 27 | 28 | LIBS = -lmechacrypto -ldebug -lpadx -lmtap -lmc -lfileXio -lpatches -lc -lcdvd 29 | 30 | CC = $(TOOL_PREFIX)gcc 31 | CXX = $(TOOL_PREFIX)g++ 32 | LD = $(TOOL_PREFIX)g++ 33 | OBJCOPY = $(TOOL_PREFIX)objcopy 34 | 35 | CPPFLAGS = -O2 -Wall $(EE_DEFINES) $(DEFINES) -I$(INCLUDE_DIR) $(EE_INCLUDE) 36 | CFLAGS = $(CPPFLAGS) -std=c11 37 | CXXFLAGS = $(CPPFLAGS) -std=c++14 38 | LDFLAGS = $(EE_LDFLAGS) 39 | 40 | .PHONY: all clean headers 41 | 42 | all: $(BIN_DIR) $(OUT_DIR) $(EXE) 43 | 44 | clean: 45 | rm -f $(BIN_DIR)/*.o $(BIN_DIR)/*.c $(EXE) 46 | 47 | $(BIN_DIR): 48 | mkdir $@ 49 | $(OUT_DIR): 50 | mkdir $@ 51 | 52 | $(EXE): $(TARGET_C_OBJECTS) $(TARGET_CXX_OBJECTS) $(BIN_OBJECTS) $(LIB_DIR)/libmechacrypto.a | $(OUT_DIR) 53 | $(LD) $(LDFLAGS) -L$(LIB_DIR) $^ $(LIBS) -o $@ 54 | 55 | $(TARGET_C_OBJECTS): $(BIN_DIR)/%.o: %.c | headers 56 | $(CC) $(CFLAGS) -c $^ -o $@ 57 | 58 | $(TARGET_CXX_OBJECTS): $(BIN_DIR)/%.o: %.cpp | headers 59 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 60 | 61 | $(IRX_OBJECTS): $(BIN_DIR)/%.irx.o: $(BIN_DIR)/%.irx.c 62 | $(CC) $(CFLAGS) -c $^ -o $@ 63 | $(IRX_SOURCES): $(BIN_DIR)/%.irx.c: $(PS2SDK)/iop/irx/%.irx 64 | bin2c $^ $@ irx_`echo "$^" | sed s/^.*\\\\/// | sed s/\\\\.irx//` 65 | 66 | $(BIN_DIR)/patch-irq-hook.fixed.o: $(BIN_DIR)/patch-irq-hook.fixed.c 67 | $(CC) $(CFLAGS) -c $^ -o $@ 68 | $(BIN_DIR)/patch-irq-hook.fixed.c: $(PATCH_DIR)/patch-irq-hook.fixed.bin 69 | bin2c $^ $@ patch_irq_hook 70 | 71 | $(BIN_DIR)/patch-cdprotect-hook.fixed.o: $(BIN_DIR)/patch-cdprotect-hook.fixed.c 72 | $(CC) $(CFLAGS) -c $^ -o $@ 73 | $(BIN_DIR)/patch-cdprotect-hook.fixed.c: $(PATCH_DIR)/patch-cdprotect-hook.fixed.bin 74 | bin2c $^ $@ patch_cdprotect_hook 75 | 76 | $(BIN_DIR)/payload-fastdump.o: $(BIN_DIR)/payload-fastdump.c 77 | $(CC) $(CFLAGS) -c $^ -o $@ 78 | $(BIN_DIR)/payload-fastdump.c: $(PATCH_DIR)/payload-fastdump.bin 79 | bin2c $^ $@ payload_fastdump 80 | 81 | $(BIN_DIR)/payload-writenvm.o: $(BIN_DIR)/payload-writenvm.c 82 | $(CC) $(CFLAGS) -c $^ -o $@ 83 | $(BIN_DIR)/payload-writenvm.c: $(PATCH_DIR)/payload-writenvm.bin 84 | bin2c $^ $@ payload_writenvm 85 | 86 | $(BIN_DIR)/payload-keystoredump.o: $(BIN_DIR)/payload-keystoredump.c 87 | $(CC) $(CFLAGS) -c $^ -o $@ 88 | $(BIN_DIR)/payload-keystoredump.c: $(PATCH_DIR)/payload-keystoredump.bin 89 | bin2c $^ $@ payload_keystoredump 90 | 91 | headers: *.hpp 92 | -------------------------------------------------------------------------------- /payload/payload-fastdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct PatchRegisterSet_ 5 | { 6 | uint8_t m_enable; 7 | uint8_t m_dummy[3]; 8 | uint32_t m_target; 9 | uint32_t m_value; 10 | uint32_t m_dummy2; 11 | } PatchRegisterSet; 12 | _Static_assert(sizeof(PatchRegisterSet) == 0x10, "bad PatchRegisterSet type"); 13 | _Static_assert(offsetof(PatchRegisterSet, m_enable) == 0x00, "bad PatchRegisterSet type"); 14 | _Static_assert(offsetof(PatchRegisterSet, m_target) == 0x04, "bad PatchRegisterSet type"); 15 | _Static_assert(offsetof(PatchRegisterSet, m_value) == 0x08, "bad PatchRegisterSet type"); 16 | 17 | typedef struct PatchRegisters_ 18 | { 19 | PatchRegisterSet m_sets[4]; 20 | uint32_t m_dummy[4]; 21 | uint16_t m_lockControl; 22 | } PatchRegisters; 23 | _Static_assert(offsetof(PatchRegisters, m_sets) == 0x00, "bad PatchRegisters type"); 24 | _Static_assert(offsetof(PatchRegisters, m_lockControl) == 0x50, "bad PatchRegisters type"); 25 | 26 | 27 | static uint32_t DisableInterrupts(); 28 | static void RestoreInterrupts(uint32_t oldCPSR); 29 | 30 | 31 | __attribute__((__target__("thumb"))) 32 | void payload_start(int dummy0, int dummy1, int dummy2, const uint8_t* address) 33 | { 34 | (void) dummy0; 35 | (void) dummy1; 36 | (void) dummy2; 37 | 38 | // Don't let us messing with the patch registers cause problems. 39 | uint32_t oldCPSR = DisableInterrupts(); 40 | 41 | // Disable patches so that we get a clean dump. 42 | volatile PatchRegisters* patchRegs = (volatile PatchRegisters*) 0x03880000; 43 | patchRegs->m_lockControl = 0xE669; 44 | patchRegs->m_lockControl = 0xCF7E; 45 | 46 | // Save old patch configuration while we disable. 47 | uint8_t patchEnables[4]; 48 | for (int i = 0; i < 4; ++i) 49 | { 50 | patchEnables[i] = patchRegs->m_sets[i].m_enable; 51 | patchRegs->m_sets[i].m_enable = 0x00; 52 | } 53 | 54 | // Copy data to local buffer and checksum. 55 | uint8_t copy_buffer[14]; 56 | unsigned checksum = 0; 57 | for (int i = 0; i < 14; ++i) 58 | { 59 | uint8_t b = address[i]; 60 | copy_buffer[i] = b; 61 | checksum += b; 62 | } 63 | 64 | // The address itself is part of the checksum. 65 | for (int i = 0; i < sizeof(uint32_t); ++i) 66 | { 67 | checksum += (uint8_t) (((uintptr_t) address) >> (i * 8)); 68 | } 69 | 70 | // Send a response code, checksum, and the 14 bytes back. 71 | volatile uint8_t* out_reg = (volatile uint8_t*) 0x036000D9; 72 | *out_reg = 0x69; 73 | *out_reg = (uint8_t) ~checksum; 74 | 75 | for (int i = 0; i < 14; ++i) 76 | { 77 | *out_reg = copy_buffer[i]; 78 | } 79 | 80 | // Restore patch configuration. 81 | for (int i = 0; i < 4; ++i) 82 | { 83 | patchRegs->m_sets[i].m_enable = patchEnables[i]; 84 | } 85 | patchRegs->m_lockControl = 0x78C7; 86 | patchRegs->m_lockControl = 0xF6D7; 87 | 88 | // Restore CPSR before returning. 89 | RestoreInterrupts(oldCPSR); 90 | } 91 | 92 | __attribute__((__noinline__, __target__("arm"))) 93 | static uint32_t DisableInterrupts() 94 | { 95 | uint32_t oldCPSR; 96 | __asm__( 97 | "mrs %0, CPSR\n\t" 98 | : "=r"(oldCPSR)); 99 | 100 | __asm__ volatile( 101 | "msr CPSR_c, %0\n\t" 102 | : 103 | : "r"(oldCPSR | 0x000000C0)); 104 | 105 | return oldCPSR; 106 | } 107 | 108 | __attribute__((__noinline__, __target__("arm"))) 109 | static void RestoreInterrupts(uint32_t oldCPSR) 110 | { 111 | __asm__ volatile( 112 | "msr CPSR_c, %0\n\t" 113 | : 114 | : "r"(oldCPSR)); 115 | } 116 | -------------------------------------------------------------------------------- /mechadump/sysinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "cipher.h" 12 | #include "keys.hpp" 13 | #include "util.h" 14 | 15 | #include "sysinfo.hpp" 16 | 17 | 18 | bool GetMechaconVersion(int& major, int& minor, std::string& refreshDate) 19 | { 20 | uint8_t params[1]; 21 | 22 | uint8_t resultVersion[3]; 23 | params[0] = 0x00; 24 | if (!sceCdApplySCmd(0x03, params, sizeof(params), resultVersion, sizeof(resultVersion))) 25 | return false; 26 | 27 | uint8_t resultDate[6]; 28 | params[0] = 0xFD; 29 | if (!sceCdApplySCmd(0x03, params, sizeof(params), resultDate, sizeof(resultDate))) 30 | return false; 31 | if (resultDate[0] != 0x00) 32 | return false; 33 | 34 | major = resultVersion[1]; 35 | minor = resultVersion[2]; 36 | 37 | char dateString[64]; 38 | std::snprintf(dateString, sizeof(dateString), "20%02x/%02x/%02x %02x:%02x", 39 | resultDate[1], resultDate[2], resultDate[3], resultDate[4], resultDate[5]); 40 | refreshDate = dateString; 41 | 42 | return true; 43 | } 44 | 45 | 46 | bool CheckNVMBlockChecksum(const uint8_t* block, size_t blockSize) 47 | { 48 | assert(blockSize >= 2u); 49 | 50 | unsigned sum = 0; 51 | for (size_t i = 0; i < blockSize - 2; ++i) 52 | sum += block[i]; 53 | 54 | return static_cast(~sum) == block[blockSize - 1]; 55 | } 56 | 57 | 58 | bool DecodeRegionFlags(const uint8_t* eeprom, uint32_t& regionFlags) 59 | { 60 | static constexpr uint8_t c_zeroIV[8] = {}; 61 | 62 | // Validate the EEPROM block checksums. 63 | if (!CheckNVMBlockChecksum(&eeprom[0x1C6], 0x0A)) 64 | return false; 65 | if (!CheckNVMBlockChecksum(&eeprom[0x1D0], 0x0A)) 66 | return false; 67 | 68 | // Derive the key that encrypts the region code. 69 | uint8_t regionKey[8]; 70 | uint8_t globalFlagsKey[8]; 71 | write_be_uint64(globalFlagsKey, g_globalFlagsKey); 72 | cipherCbcEncrypt(regionKey, &eeprom[0x1C6], 8, globalFlagsKey, 1, c_zeroIV); 73 | 74 | // Decrypt the region value. 75 | uint8_t decrypted[8]; 76 | cipherCbcDecrypt(decrypted, &eeprom[0x1D0], 8, regionKey, 1, c_zeroIV); 77 | 78 | // Validate internal checksum on the region. 79 | uint32_t regionCode = read_le_uint32(&decrypted[0]); 80 | uint16_t regionRandom = read_le_uint16(&decrypted[4]); 81 | uint16_t regionChecksum = read_le_uint16(&decrypted[6]); 82 | 83 | unsigned regionCorrect = (0u + regionRandom + (regionCode & 0xFFFF) + (regionCode >> 16)) & 0xFFFF; 84 | if (regionChecksum != regionCorrect) 85 | return false; 86 | 87 | regionFlags = regionCode; 88 | return true; 89 | } 90 | 91 | 92 | std::string GetModelString() 93 | { 94 | char output[17]; 95 | uint8_t params[1]; 96 | uint8_t result[9]; 97 | 98 | params[0] = 0; 99 | if (!sceCdApplySCmd(0x17, params, sizeof(params), result, sizeof(result))) 100 | return std::string(); 101 | if (result[0] != 0x00) 102 | return std::string(); 103 | 104 | std::memcpy(&output[0], &result[1], sizeof(result) - 1); 105 | 106 | params[0] = 8; 107 | if (!sceCdApplySCmd(0x17, params, sizeof(params), result, sizeof(result))) 108 | return std::string(); 109 | if (result[0] != 0x00) 110 | return std::string(); 111 | 112 | std::memcpy(&output[8], &result[1], sizeof(result) - 1); 113 | 114 | output[16] = '\0'; 115 | return output; 116 | } 117 | 118 | 119 | std::vector MakeEmptyPatchset() 120 | { 121 | static constexpr uint8_t c_zeroIV[8] = {}; 122 | 123 | uint8_t encryptedZeros[8]; 124 | 125 | uint8_t mechaPatchKey[8]; 126 | write_be_uint64(mechaPatchKey, g_mechaPatchKey); 127 | cipherCbcEncrypt(encryptedZeros, c_zeroIV, 8, mechaPatchKey, 1, c_zeroIV); 128 | 129 | std::vector result; 130 | result.reserve(0xE0); 131 | result.insert(result.end(), encryptedZeros + 0, encryptedZeros + 8); 132 | result.insert(result.end(), encryptedZeros + 0, encryptedZeros + 8); 133 | for (int i = 0; i < 0xD0; ++i) 134 | result.emplace_back(0xFF); 135 | return result; 136 | } 137 | -------------------------------------------------------------------------------- /mechadump/knowndumps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "knowndumps.hpp" 4 | 5 | 6 | struct KnownDump 7 | { 8 | constexpr KnownDump(const KnownDump&) = default; 9 | constexpr KnownDump& operator =(const KnownDump&) = default; 10 | 11 | constexpr KnownDump(const char* hexString); 12 | 13 | sha256::digest m_digest; 14 | 15 | private: 16 | static constexpr uint8_t ByteFromHexNibble(char ch); 17 | static constexpr uint8_t ByteFromHexString(const char* hexString, size_t index); 18 | }; 19 | 20 | 21 | constexpr uint8_t KnownDump::ByteFromHexNibble(char ch) 22 | { 23 | if ((ch >= '0') && (ch <= '9')) 24 | return static_cast(ch - '0'); 25 | if ((ch >= 'A') && (ch <= 'F')) 26 | return static_cast(ch - 'A' + 10); 27 | if ((ch >= 'a') && (ch <= 'f')) 28 | return static_cast(ch - 'a' + 10); 29 | 30 | // Make constexpr barf 31 | return *reinterpret_cast(ch) * 0; 32 | } 33 | 34 | 35 | constexpr uint8_t KnownDump::ByteFromHexString(const char* hexString, size_t index) 36 | { 37 | return static_cast( 38 | (ByteFromHexNibble(hexString[index * 2]) << 4) + 39 | ByteFromHexNibble(hexString[index * 2 + 1])); 40 | } 41 | 42 | 43 | constexpr KnownDump::KnownDump(const char* hexString) 44 | : m_digest{ 45 | ByteFromHexString(hexString, 0x00), ByteFromHexString(hexString, 0x01), 46 | ByteFromHexString(hexString, 0x02), ByteFromHexString(hexString, 0x03), 47 | ByteFromHexString(hexString, 0x04), ByteFromHexString(hexString, 0x05), 48 | ByteFromHexString(hexString, 0x06), ByteFromHexString(hexString, 0x07), 49 | ByteFromHexString(hexString, 0x08), ByteFromHexString(hexString, 0x09), 50 | ByteFromHexString(hexString, 0x0A), ByteFromHexString(hexString, 0x0B), 51 | ByteFromHexString(hexString, 0x0C), ByteFromHexString(hexString, 0x0D), 52 | ByteFromHexString(hexString, 0x0E), ByteFromHexString(hexString, 0x0F), 53 | ByteFromHexString(hexString, 0x10), ByteFromHexString(hexString, 0x11), 54 | ByteFromHexString(hexString, 0x12), ByteFromHexString(hexString, 0x13), 55 | ByteFromHexString(hexString, 0x14), ByteFromHexString(hexString, 0x15), 56 | ByteFromHexString(hexString, 0x16), ByteFromHexString(hexString, 0x17), 57 | ByteFromHexString(hexString, 0x18), ByteFromHexString(hexString, 0x19), 58 | ByteFromHexString(hexString, 0x1A), ByteFromHexString(hexString, 0x1B), 59 | ByteFromHexString(hexString, 0x1C), ByteFromHexString(hexString, 0x1D), 60 | ByteFromHexString(hexString, 0x1E), ByteFromHexString(hexString, 0x1F) 61 | } 62 | { 63 | } 64 | 65 | 66 | extern const KnownDump g_knownDumps[] = 67 | { 68 | // * Mexican "fat" PS2 SCPH-50011 has a version that calls itself 5.06.0, but has a different date. 69 | // It came after 5.12.0, so it probably should've been numbered 5.16.0. 70 | // * If 5.08 exists, it probably is an early DESR-5000 PSX with CXR706080-701GG. 71 | // * If 6.08 exists, it probably is a late SCPH-750xx or early SCPH-770xx with CXR716080-105GG. 72 | "81347ef6021bfc786139e4af88feac61af4ca07d79eca0ae8df32f4f08e4256f", // 5.00.0 CXR706080-101GG 73 | "0c9edfabfce9e2eaa981c618ada9fd5ffdda7e7a6bad2e62e35404be7954f750", // 5.02.0 CXR706080-102GG 74 | "80d660afffb7c56e1f41c8e7dfe88d301f87cbb98780cbd267370c806d39c544", // 5.04.0 CXR706080-103GG 75 | "6dbb29570ad2c7f9e713eec8f1ca8627245c24afd18ff22e264907e470cb6d09", // 5.06.0 CXR706080-104GG 76 | "37f2f9d900046d54a04803b818f04dde165e2e1984b3389a4cd13e77bac422f2", // 5.06.0 Mexico CXR706080-106GG 77 | "36af871458150ccf14e411b59bded46b4f068158b412b8ebedf997b0fdf6bb47", // 5.10.1 PSX CXR706080-702GG 78 | "271440f19b15501654a9108810950c5aafffc5b1282810edca8a3add56f55797", // 5.12.0 CXR706080-105GG 79 | "6ab6e70d40b6bc17b52ddfc6612a5cf7287a895f101006ebc7aa3d144b85de54", // 5.14.1 PSX CXR706080-703GG 80 | "c4b7cc053da6b17d1ab8a6938c38e2ed16fce811dee4d2fe7507508581ecc6dc", // 6.00.0 CXR716080=101GG 81 | "d4a840191960705cce4aa0a939c8fc387fae40bc3e050ebeeb48e21aec20dd37", // 6.02.0 CXR716080-102GG 82 | "ebe1175fdaaef195cd776d3afa63c6b4c995cad7114d14d14ab17c2c04020ea2", // 6.04.0 CXR716080-103GG 83 | "f435866772a4fb9c4fdb68d3317bb36d72d8999d2efa210f55ae75320bfff616", // 6.06.0 CXR716080-104GG 84 | "68a23e873f6aabdcfd49ace2f9143ebaedf57c37a73ad86e72db3ded1ebc94d4", // 6.10.0 CXR716080-106GG 85 | "18371ba394e6339d861cfe19db7db7c9e44c8067114888421ee4a35feaaeba1b", // 6.12.0 CXR726080-301GB 86 | }; 87 | 88 | 89 | bool IsKnownDump(const sha256::digest& dig) 90 | { 91 | for (const KnownDump& dump : g_knownDumps) 92 | { 93 | if (std::memcmp(dig, dump.m_digest, sizeof(dig)) == 0) 94 | return true; 95 | } 96 | return false; 97 | } 98 | -------------------------------------------------------------------------------- /mechacrypto/sha256.hpp: -------------------------------------------------------------------------------- 1 | // This implementation is not very fast; it's meant for a simple 2 | // implementation that can be used for doing SHA-256 at compile time, 3 | // but also has a runtime implementation available. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | 11 | namespace sha256_internal 12 | { 13 | using sha256_word = std::uint32_t; 14 | 15 | 16 | inline constexpr sha256_word sha256_shift_left(sha256_word value, unsigned shift) 17 | { 18 | if (shift >= 32u) 19 | { 20 | return 0u; 21 | } 22 | 23 | // Mask out the high bits to avoid issues with promotion and 24 | // compile-time overflow warnings. 25 | value &= sha256_word(-1) >> shift; 26 | 27 | return static_cast(value << shift); 28 | } 29 | 30 | inline constexpr sha256_word sha256_shift_right(sha256_word value, unsigned shift) 31 | { 32 | if (shift >= 32u) 33 | { 34 | return 0u; 35 | } 36 | 37 | return static_cast(value >> shift); 38 | } 39 | 40 | inline constexpr sha256_word sha256_rotate_right(sha256_word value, unsigned shift) 41 | { 42 | shift %= 32u; 43 | 44 | return sha256_shift_left(value, 32u - shift) | sha256_shift_right(value, shift); 45 | } 46 | 47 | inline constexpr sha256_word sha256_ch(sha256_word x, sha256_word y, sha256_word z) 48 | { 49 | return (x & y) ^ ((~x) & z); 50 | } 51 | 52 | inline constexpr sha256_word sha256_maj(sha256_word x, sha256_word y, sha256_word z) 53 | { 54 | return (x & y) ^ (x & z) ^ (y & z); 55 | } 56 | 57 | inline constexpr sha256_word sha256_ep0(sha256_word x) 58 | { 59 | return sha256_rotate_right(x, 2) ^ sha256_rotate_right(x, 13) ^ sha256_rotate_right(x, 22); 60 | } 61 | 62 | inline constexpr sha256_word sha256_ep1(sha256_word x) 63 | { 64 | return sha256_rotate_right(x, 6) ^ sha256_rotate_right(x, 11) ^ sha256_rotate_right(x, 25); 65 | } 66 | 67 | inline constexpr sha256_word sha256_sigma0(sha256_word x) 68 | { 69 | return sha256_rotate_right(x, 7) ^ sha256_rotate_right(x, 18) ^ sha256_shift_right(x, 3); 70 | } 71 | 72 | inline constexpr sha256_word sha256_sigma1(sha256_word x) 73 | { 74 | return sha256_rotate_right(x, 17) ^ sha256_rotate_right(x, 19) ^ sha256_shift_right(x, 10); 75 | } 76 | 77 | // The main implementation 78 | inline constexpr void sha256_transform(sha256_word(&state)[8], const sha256_word(&input)[16]) 79 | { 80 | constexpr sha256_word k[64] = 81 | { 82 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 83 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 84 | 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 85 | 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 86 | 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 87 | 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 88 | 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 89 | 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, 90 | }; 91 | 92 | sha256_word a = state[0]; 93 | sha256_word b = state[1]; 94 | sha256_word c = state[2]; 95 | sha256_word d = state[3]; 96 | sha256_word e = state[4]; 97 | sha256_word f = state[5]; 98 | sha256_word g = state[6]; 99 | sha256_word h = state[7]; 100 | 101 | sha256_word w[64]{}; 102 | 103 | for (int i = 0; i < 16; ++i) 104 | { 105 | w[i] = input[i]; 106 | } 107 | 108 | for (int i = 16; i < 64; ++i) 109 | { 110 | w[i] = sha256_sigma1(w[i - 2]) + w[i - 7] + sha256_sigma0(w[i - 15]) + w[i - 16]; 111 | } 112 | 113 | for (int i = 0; i < 64; ++i) 114 | { 115 | sha256_word temp1 = h + sha256_ep1(e) + sha256_ch(e, f, g) + k[i] + w[i]; 116 | sha256_word temp2 = sha256_ep0(a) + sha256_maj(a, b, c); 117 | 118 | h = g; 119 | g = f; 120 | f = e; 121 | e = d + temp1; 122 | d = c; 123 | c = b; 124 | b = a; 125 | a = temp1 + temp2; 126 | } 127 | 128 | state[0] += a; 129 | state[1] += b; 130 | state[2] += c; 131 | state[3] += d; 132 | state[4] += e; 133 | state[5] += f; 134 | state[6] += g; 135 | state[7] += h; 136 | } 137 | 138 | inline constexpr void sha256_init(sha256_word(&state)[8]) 139 | { 140 | state[0] = 0x6A09E667; 141 | state[1] = 0xBB67AE85; 142 | state[2] = 0x3C6EF372; 143 | state[3] = 0xA54FF53A; 144 | state[4] = 0x510E527F; 145 | state[5] = 0x9B05688C; 146 | state[6] = 0x1F83D9AB; 147 | state[7] = 0x5BE0CD19; 148 | } 149 | } 150 | 151 | 152 | // A traditional SHA-256 implementation for non-compile-time. 153 | class sha256 154 | { 155 | public: 156 | using digest = uint8_t[32]; 157 | 158 | sha256() { reset(); } 159 | 160 | sha256(const sha256&) = default; 161 | sha256& operator =(const sha256&) = default; 162 | 163 | void reset(); 164 | void process(const void* data, std::size_t size); 165 | void finish(digest& dig); // can't reuse after 166 | 167 | private: 168 | void process_block(const void* block); 169 | 170 | std::uint8_t m_buffer[64]; 171 | sha256_internal::sha256_word m_state[8]; 172 | std::uint64_t m_size; 173 | }; 174 | -------------------------------------------------------------------------------- /mechacrypto/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "util.h" 21 | 22 | void memrcpy(void *dst, void *src, size_t len) 23 | { 24 | size_t i; 25 | for (i = 0; i < len; i++) { 26 | ((uint8_t *)dst)[i] = ((uint8_t *)src)[len-1-i]; 27 | } 28 | } 29 | 30 | /* 31 | * memxor: perform exclusive-or of memory buffers. 32 | */ 33 | void memxor(const void *a, const void *b, void *Result, size_t Length) 34 | { 35 | size_t i; 36 | for (i = 0; i < Length; i++) { 37 | ((uint8_t *)Result)[i] = ((uint8_t *)a)[i] ^ ((uint8_t *)b)[i]; 38 | } 39 | } 40 | 41 | /* 42 | * write_le_uint16: write an unsigned 16 bits Little Endian 43 | * value to a buffer 44 | */ 45 | void write_le_uint16(uint8_t *buf, uint16_t val) 46 | { 47 | buf[0] = (uint8_t)val; 48 | buf[1] = (uint8_t)(val >> 8); 49 | } 50 | 51 | /* 52 | * write_le_uint32: write an unsigned 32 bits Little Endian 53 | * value to a buffer 54 | */ 55 | void write_le_uint32(uint8_t *buf, uint32_t val) 56 | { 57 | buf[0] = (uint8_t)val; 58 | buf[1] = (uint8_t)(val >> 8); 59 | buf[2] = (uint8_t)(val >> 16); 60 | buf[3] = (uint8_t)(val >> 24); 61 | } 62 | 63 | /* 64 | * write_le_uint64: write an unsigned 64 bits Little Endian 65 | * value to a buffer 66 | */ 67 | void write_le_uint64(uint8_t *buf, uint64_t val) 68 | { 69 | buf[0] = (uint8_t)val; 70 | buf[1] = (uint8_t)(val >> 8); 71 | buf[2] = (uint8_t)(val >> 16); 72 | buf[3] = (uint8_t)(val >> 24); 73 | buf[4] = (uint8_t)(val >> 32); 74 | buf[5] = (uint8_t)(val >> 40); 75 | buf[6] = (uint8_t)(val >> 48); 76 | buf[7] = (uint8_t)(val >> 56); 77 | } 78 | 79 | /* 80 | * read_le_uint16: read an unsigned 16 bits Little Endian 81 | * value from a buffer 82 | */ 83 | uint16_t read_le_uint16(const uint8_t *buf) 84 | { 85 | register uint16_t val; 86 | 87 | val = buf[0]; 88 | val |= ((uint16_t)buf[1] << 8); 89 | 90 | return val; 91 | } 92 | 93 | /* 94 | * read_le_uint32: read an unsigned 32 bits Little Endian 95 | * value from a buffer 96 | */ 97 | uint32_t read_le_uint32(const uint8_t *buf) 98 | { 99 | register uint32_t val; 100 | 101 | val = buf[0]; 102 | val |= ((uint32_t)buf[1] << 8); 103 | val |= ((uint32_t)buf[2] << 16); 104 | val |= ((uint32_t)buf[3] << 24); 105 | 106 | return val; 107 | } 108 | 109 | /* 110 | * read_le_uint64: read an unsigned 64 bits Little Endian 111 | * value from a buffer 112 | */ 113 | uint64_t read_le_uint64(const uint8_t *buf) 114 | { 115 | register uint64_t val; 116 | 117 | val = buf[0]; 118 | val |= ((uint64_t)buf[1] << 8); 119 | val |= ((uint64_t)buf[2] << 16); 120 | val |= ((uint64_t)buf[3] << 24); 121 | val |= ((uint64_t)buf[4] << 32); 122 | val |= ((uint64_t)buf[5] << 40); 123 | val |= ((uint64_t)buf[6] << 48); 124 | val |= ((uint64_t)buf[7] << 56); 125 | 126 | return val; 127 | } 128 | 129 | /* 130 | * write_be_uint16: write an unsigned 16 bits Big Endian 131 | * value to a buffer 132 | */ 133 | void write_be_uint16(uint8_t* buf, uint16_t val) 134 | { 135 | buf[0] = (uint8_t)(val >> 8); 136 | buf[1] = (uint8_t)val; 137 | } 138 | 139 | /* 140 | * write_be_uint32: write an unsigned 32 bits Big Endian 141 | * value to a buffer 142 | */ 143 | void write_be_uint32(uint8_t* buf, uint32_t val) 144 | { 145 | buf[0] = (uint8_t)(val >> 24); 146 | buf[1] = (uint8_t)(val >> 16); 147 | buf[2] = (uint8_t)(val >> 8); 148 | buf[3] = (uint8_t)val; 149 | } 150 | 151 | /* 152 | * write_be_uint64: write an unsigned 64 bits Big Endian 153 | * value to a buffer 154 | */ 155 | void write_be_uint64(uint8_t* buf, uint64_t val) 156 | { 157 | buf[0] = (uint8_t)(val >> 56); 158 | buf[1] = (uint8_t)(val >> 48); 159 | buf[2] = (uint8_t)(val >> 40); 160 | buf[3] = (uint8_t)(val >> 32); 161 | buf[4] = (uint8_t)(val >> 24); 162 | buf[5] = (uint8_t)(val >> 16); 163 | buf[6] = (uint8_t)(val >> 8); 164 | buf[7] = (uint8_t)val; 165 | } 166 | 167 | /* 168 | * read_be_uint16: read an unsigned 16 bits Big Endian 169 | * value from a buffer 170 | */ 171 | uint16_t read_be_uint16(const uint8_t* buf) 172 | { 173 | register uint16_t val; 174 | 175 | val = ((uint16_t)buf[0] << 8); 176 | val |= buf[1]; 177 | 178 | return val; 179 | } 180 | 181 | /* 182 | * read_be_uint32: read an unsigned 32 bits Big Endian 183 | * value from a buffer 184 | */ 185 | uint32_t read_be_uint32(const uint8_t* buf) 186 | { 187 | register uint32_t val; 188 | 189 | val = ((uint32_t)buf[0] << 24); 190 | val |= ((uint32_t)buf[1] << 16); 191 | val |= ((uint32_t)buf[2] << 8); 192 | val |= buf[3]; 193 | 194 | return val; 195 | } 196 | 197 | /* 198 | * read_be_uint64: read an unsigned 64 bits Big Endian 199 | * value from a buffer 200 | */ 201 | uint64_t read_be_uint64(const uint8_t* buf) 202 | { 203 | register uint64_t val; 204 | 205 | val = ((uint64_t)buf[0] << 56); 206 | val |= ((uint64_t)buf[1] << 48); 207 | val |= ((uint64_t)buf[2] << 40); 208 | val |= ((uint64_t)buf[3] << 32); 209 | val |= ((uint64_t)buf[4] << 24); 210 | val |= ((uint64_t)buf[5] << 16); 211 | val |= ((uint64_t)buf[6] << 8); 212 | val |= buf[7]; 213 | 214 | return val; 215 | } 216 | 217 | -------------------------------------------------------------------------------- /patch/patch-irq-hook.S: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | .syntax unified 3 | .align 4 4 | 5 | // Offset 0x00 6 | .thumb 7 | .globl _start 8 | _start: 9 | // Addresses to patch... 10 | .word 0x00000164 11 | .word 0x00000168 12 | .word 0x0000016C 13 | fixup_row_0: 14 | .word 0 15 | 16 | // Offset 0x10 17 | .arm 18 | replacement: 19 | mov r0, #0 20 | ldr r0, [r0, #0x28] 21 | ldr pc, [r0, #0x78] 22 | fixup_row_1: 23 | .word 0xFFFFFFFF 24 | 25 | //---- ROW 2 26 | // Offset 0x20 27 | // This offset is normally the SVC dispatch addresses, but because the initial 28 | // patch set doesn't patch in an SVC, we can put an extra 16 bytes of code. 29 | // 30 | // This function is called from the patch code we placed at 0x00000200. 31 | .arm 32 | initialize_thunk: 33 | adr r0, initialize + 1 34 | bx r0 35 | .thumb 36 | initialize: 37 | push {r2-r3} 38 | 39 | fixup_row_2: 40 | // Safe to destroy r0,r1,r2,r3,flags 41 | .hword (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) 42 | 43 | // Search the ROM for the SCMD 03 dispatch table. 44 | ldr r1, constant_000001A3 45 | ldr r2, constant_000009A4 46 | //---- ROW 3 47 | movs r0, #0 48 | // DEBUGGING ONLY - reduces search size 49 | //ldr r0, constant_00038000 50 | search_loop: 51 | ldmia r0!, {r3} 52 | cmp r3, r1 53 | bne search_loop 54 | ldr r3, [r0, #4] 55 | cmp r3, r2 56 | bne search_loop 57 | 58 | fixup_row_3: 59 | // Safe to destroy r1,r2,r3,flags 60 | .hword (1 << 1) | (1 << 2) | (1 << 3) 61 | //---- ROW 4 62 | fixup_row_4: 63 | // Safe to destroy r1,r2,r3,flags 64 | .hword (1 << 1) | (1 << 2) | (1 << 3) 65 | 66 | // Unlock patch registers. 67 | ldr r2, constant_CF7EE669 68 | ldr r1, constant_03880050 69 | strh r2, [r1] 70 | lsrs r2, r2, #16 71 | strh r2, [r1] 72 | 73 | // Disable patches in patch registers. 74 | subs r1, r1, #0x10 75 | movs r2, #0 76 | //---- ROW 5 77 | disable_patch_loop: 78 | subs r1, r1, #0x10 79 | strb r2, [r1] 80 | lsls r3, r1, #24 81 | bne disable_patch_loop 82 | 83 | fixup_row_5: 84 | // Safe to destroy r2,r3 85 | .hword (1 << 2) | (1 << 3) 86 | 87 | // Hook the SCMD 03:A4 handler. 88 | adr r2, scmd_03_A4_handler 89 | // While we have scmd_03_A4_handler's address, write the address of the 90 | // original handler for scmd_03_A4_handler to use. 91 | ldr r3, [r0] // must this be done before patch register write? 92 | str r3, [r2, #original_scmd_03_A4_handler - scmd_03_A4_handler] 93 | //---- ROW 6 (SHORT ROW - AND NO FIXUP REQUIRED) 94 | // Now we finish the writes to the patch registers. 95 | str r0, [r1, #0x04] 96 | adds r2, r2, #1 97 | str r2, [r1, #0x08] 98 | 99 | // Enable this patch. 100 | movs r0, #1 101 | strb r0, [r1] 102 | 103 | // Lock patch registers. 104 | adds r1, r1, #0x50 105 | ldr r2, constant_F6D778C7 106 | //---- ROW 7 107 | strh r2, [r1] 108 | lsrs r2, r2, #16 109 | strh r2, [r1] 110 | 111 | // Resume with code we patched out. 112 | movs r0, #0x164 / 2 113 | adds r0, r0 114 | fixup_row_7: 115 | .hword (1 << 1) | (1 << 2) | (1 << 3) 116 | // Restore critical registers. 117 | pop {r2-r3} 118 | // Return. 119 | bx r0 120 | 121 | //---- ROW 8 122 | // This halfword is wasted due to alignment, so we might as well use it as 123 | // a branch target for the below code. 124 | execute: 125 | bx r2 126 | 127 | 128 | // *** We just aligned. Cause assembler error if not so we can correct for this. 129 | //.align 2 130 | .thumb 131 | scmd_03_A4_handler: 132 | // Read address of 03:A4 plus 4 written there by initialize. 133 | ldr r0, original_scmd_03_A4_handler 134 | subs r0, r0, #1 135 | // r0 contains the address of the original SCMD 03:A4 handler. 136 | // Get the address of the SCMD parameter buffer. +4 is where the actual 137 | // parameters are located. While we're at it, grab the constant 138 | // 0x036000D0 (SCMD result FIFO, but minus 9). 139 | ldr r1, [r0, #0x78] // 0x036000D0 140 | ldr r0, [r0, #0x70] // SCMD input buffer (+4 = parameters) 141 | 142 | fixup_row_8: 143 | // Safe to destroy r2,r3 144 | .hword (1 << 2) | (1 << 3) 145 | 146 | // 03:A4 takes exactly one parameter, A4, or will fail. However, nothing 147 | // clears the parameter buffer, so rely on that to get extra parameters. 148 | // Check for magic bytes "MCD" after A4. 149 | ldr r2, [r0, #4] 150 | 151 | ldr r3, constant_44434DA4 152 | //---- ROW 9 153 | cmp r2, r3 154 | bne error 155 | 156 | fixup_row_9: 157 | // Safe to destroy r2,r3 158 | .hword (1 << 2) | (1 << 3) 159 | 160 | ldr r2, [r0, #8] 161 | cmp r2, #1 162 | ldr r2, [r0, #12] 163 | ldr r3, [r0, #16] 164 | beq execute // 1 = execute 165 | //---- ROW A 166 | bcs write // 2 = write 167 | ldr r2, [r2] // 0 = load 168 | b done 169 | 170 | fixup_row_A: 171 | // Between code blocks; anything can be here. 172 | .hword 0xFFFF 173 | 174 | write: 175 | str r3, [r2] 176 | 177 | done: 178 | // r0 contains a RAM address (0x0200????), so we can shift it right to use 179 | // to count to 4 instead of initializing it. 180 | movs r3, #0x42 // success code 181 | error: // R3's low byte will be 0xA4 182 | strb r3, [r1, #0x09] 183 | result_loop: 184 | strb r2, [r1, #0x09] // 0x036000D9 (SCMD result FIFO) 185 | //---- ROW B 186 | lsrs r2, r2, #8 187 | lsrs r0, r0, #8 188 | bne result_loop 189 | 190 | fixup_row_B: 191 | // Safe to destroy r0,r1,r2,r3 192 | .hword (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) 193 | 194 | // An ordinary return works just fine. 195 | bx lr 196 | 197 | // We should be aligned here; cause an error if not so we can correct it. 198 | // However, in terms of the checksum system, we're misaligned. 199 | //.align 2 200 | constant_000001A3: 201 | .word 0x000001A3 202 | constant_000009A4: 203 | .word 0x000009A4 204 | //---- ROW C 205 | original_scmd_03_A4_handler: 206 | fixup_row_C: 207 | // Overwritten with address of 03:A4 handler. We can also use this field 208 | // for the checksum brute-force. 209 | .word 0xFFFFFFFF 210 | constant_CF7EE669: 211 | .word 0xCF7EE669 212 | //---- ROW D 213 | constant_03880050: 214 | .word 0x03880050 215 | constant_F6D778C7: 216 | .word 0xF6D778C7 217 | constant_44434DA4: 218 | .word 0x44434DA4 219 | 220 | 221 | // WriteConfig checksum bypass information. 222 | .align 2 223 | fixup_list_start: 224 | // 1 = uint16 (or uint32) that can take any value possible 225 | // 2 = special handling for an unneeded patch address 226 | // 3 = thumb instruction allowed to modify flags and any specified register from uint16 227 | .word 2, fixup_row_0 - _start 228 | .word 1, fixup_row_1 - _start 229 | .word 3, fixup_row_2 - _start 230 | .word 3, fixup_row_3 - _start 231 | .word 3, fixup_row_4 - _start 232 | .word 3, fixup_row_5 - _start 233 | // Row 6 does not require brute force help, because it has the first 0x70-block checksum. 234 | .word 3, fixup_row_7 - _start 235 | .word 3, fixup_row_8 - _start 236 | .word 3, fixup_row_9 - _start 237 | .word 1, fixup_row_A - _start 238 | .word 3, fixup_row_B - _start 239 | .word 1, fixup_row_C - _start 240 | 241 | // The final uint32 is how big the primary payload area is. 242 | .word fixup_list_start - _start 243 | -------------------------------------------------------------------------------- /patch/patch-cdprotect-hook.S: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | .syntax unified 3 | .align 4 4 | 5 | // Offset 0x00 6 | .thumb 7 | .globl _start 8 | _start: 9 | // Addresses to patch... 10 | .word 0x00000200 11 | .word 0x00000204 12 | .word 0x00000208 13 | fixup_row_0: 14 | .word 0 15 | 16 | // Offset 0x10 17 | .thumb 18 | replacement: 19 | movs r2, #0x28 20 | ldr r2, [r2] 21 | adds r2, r2, #0x78 22 | ldr r2, [r2] 23 | adds r2, r2, #1 24 | bx r2 25 | fixup_row_1: 26 | .word 0xFFFFFFFF 27 | 28 | 29 | // Offset 0x20 30 | // This offset is normally the SVC dispatch addresses, but because the initial 31 | // patch set doesn't patch in an SVC, we can put an extra 16 bytes of code. 32 | // 33 | // This function is called from the patch code we placed at 0x00000200. 34 | .thumb 35 | initialize: 36 | push {r0,r1} 37 | 38 | fixup_row_2: 39 | // Safe to destroy r0,r1,r2,r3,lr,flags 40 | .hword (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 14) 41 | 42 | // Search the ROM for the SCMD 03 dispatch table. 43 | ldr r1, constant_000001A3 44 | ldr r2, constant_000009A4 45 | movs r0, #0 46 | // DEBUGGING ONLY - reduces search size 47 | //ldr r0, constant_00038000 48 | search_loop: 49 | ldmia r0!, {r3} 50 | cmp r3, r1 51 | bne search_loop 52 | //---- 53 | ldr r3, [r0, #4] 54 | cmp r3, r2 55 | bne search_loop 56 | 57 | fixup_row_3: 58 | // Safe to destroy r1,r2,r3,lr,flags 59 | .hword (1 << 1) | (1 << 2) | (1 << 3) | (1 << 14) 60 | 61 | // Unlock patch registers. 62 | ldr r2, constant_CF7EE669 63 | ldr r1, constant_03880050 64 | strh r2, [r1] 65 | lsrs r2, r2, #16 66 | //---- 67 | strh r2, [r1] 68 | 69 | // Disable patches in patch registers. 70 | subs r1, r1, #0x10 71 | movs r2, #0 72 | disable_patch_loop: 73 | subs r1, r1, #0x10 74 | strb r2, [r1] 75 | lsls r3, r1, #24 76 | bne disable_patch_loop 77 | 78 | fixup_row_4: 79 | // Safe to destroy r2,r3,lr 80 | .hword (1 << 2) | (1 << 3) | (1 << 14) 81 | //---- 82 | 83 | // Hook the SCMD 03:A4 handler. 84 | adr r2, scmd_03_A4_handler 85 | // While we have scmd_03_A4_handler's address, write the address of the 86 | // original handler for scmd_03_A4_handler to use. 87 | ldr r3, [r0] // must this be done before patch register write? 88 | str r3, [r2, #original_scmd_03_A4_handler - scmd_03_A4_handler] 89 | // Now we finish the writes to the patch registers. 90 | str r0, [r1, #0x04] 91 | adds r2, r2, #1 92 | str r2, [r1, #0x08] 93 | 94 | fixup_row_5: 95 | // Safe to destroy r0,r2,r3,lr 96 | .hword (1 << 0) | (1 << 2) | (1 << 3) | (1 << 14) 97 | 98 | // Enable this patch. 99 | movs r0, #1 100 | //---- 101 | strb r0, [r1] 102 | 103 | // Lock patch registers. 104 | adds r1, r1, #0x50 105 | ldr r2, constant_F6D778C7 106 | strh r2, [r1] 107 | lsrs r2, r2, #16 108 | strh r2, [r1] 109 | 110 | // Resume with code we patched out. (r0 is 1, so << 9 = 0x200.) 111 | lsls r2, r0, #9 112 | //---- *** ROW 6 IS ONLY 7 INSTRUCTIONS AND NO BRUTE FORCE HELP IS REQUIRED *** 113 | // Restore critical registers. 114 | pop {r0,r1} 115 | // Use mov pc instead of bx for convenience, since this is thumb-to-thumb. 116 | mov pc, r2 117 | 118 | 119 | // We're misaligned. Put any uint16 here. 120 | fixup_row_7: 121 | .hword 0xFFFF 122 | 123 | 124 | // *** We just aligned. Cause assembler error if not so we can correct for this. 125 | //.align 2 126 | .thumb 127 | scmd_03_A4_handler: 128 | nop // FIXME: This was an unneeded push instruction before 129 | 130 | // Read address of 03:A4 plus 4 written there by initialize. 131 | ldr r0, original_scmd_03_A4_handler 132 | subs r0, r0, #1 133 | // r0 contains the address of the original SCMD 03:A4 handler. 134 | // Get the address of the SCMD parameter buffer. +4 is where the actual 135 | // parameters are located. While we're at it, grab the constant 136 | // 0x036000D0 (SCMD result FIFO, but minus 9). 137 | ldr r1, [r0, #0x78] // 0x036000D0 138 | ldr r0, [r0, #0x70] // SCMD input buffer (+4 = parameters) 139 | //---- 140 | 141 | // 03:A4 takes exactly one parameter, A4, or will fail. However, nothing 142 | // clears the parameter buffer, so rely on that to get extra parameters. 143 | // Check for magic bytes "MCD" after A4. 144 | ldr r2, [r0, #4] 145 | 146 | fixup_row_8: 147 | // Safe to destroy r3 148 | .hword (1 << 3) 149 | 150 | ldr r3, constant_44434DA4 151 | cmp r2, r3 152 | bne error 153 | 154 | ldr r2, [r0, #8] 155 | cmp r2, #1 156 | ldr r2, [r0, #12] 157 | //---- 158 | ldr r3, [r0, #16] 159 | beq execute // 1 = execute 160 | bcs write // 2 = write 161 | ldr r2, [r2] // 0 = load 162 | b done 163 | 164 | fixup_row_9: 165 | // Between code blocks; anything can be here. 166 | .hword 0xFFFF 167 | 168 | execute: 169 | bx r2 170 | 171 | write: 172 | str r3, [r2] 173 | //---- 174 | 175 | done: 176 | // r0 contains a RAM address (0x0200????), so we can shift it right to use 177 | // to count to 4 instead of initializing it. 178 | movs r3, #0x42 // success code 179 | error: // R3's low byte will be 0xA4 180 | strb r3, [r1, #0x09] 181 | result_loop: 182 | strb r2, [r1, #0x09] // 0x036000D9 (SCMD result FIFO) 183 | lsrs r2, r2, #8 184 | lsrs r0, r0, #8 185 | bne result_loop 186 | 187 | // An ordinary return works just fine. 188 | nop // FIXME: This was an unneeded push instruction before 189 | 190 | fixup_row_A: 191 | // Safe to destroy r0,r1,r2,r3 192 | .hword (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) 193 | //---- 194 | 195 | bx lr 196 | 197 | // We should be aligned here; cause an error if not so we can correct it. 198 | // However, in terms of the checksum system, we're misaligned. 199 | //.align 2 200 | constant_000001A3: 201 | .word 0x000001A3 202 | constant_000009A4: 203 | .word 0x000009A4 204 | original_scmd_03_A4_handler: 205 | fixup_row_B: 206 | // Overwritten with address of 03:A4 handler. We can also use this field 207 | // for the checksum brute-force. 208 | .word 0xFFFFFFFF 209 | //---- 210 | constant_CF7EE669: 211 | .word 0xCF7EE669 212 | constant_03880050: 213 | .word 0x03880050 214 | constant_F6D778C7: 215 | .word 0xF6D778C7 216 | fixup_row_C: 217 | .word 0xFFFFFFFF 218 | constant_44434DA4: 219 | .word 0x44434DA4 220 | 221 | 222 | // WriteConfig checksum bypass information. 223 | .align 2 224 | fixup_list_start: 225 | // 1 = uint16 (or uint32) that can take any value possible 226 | // 2 = special handling for an unneeded patch address 227 | // 3 = thumb instruction allowed to modify flags and any specified register from uint16 228 | .word 2, fixup_row_0 - _start 229 | .word 1, fixup_row_1 - _start 230 | .word 3, fixup_row_2 - _start 231 | .word 3, fixup_row_3 - _start 232 | .word 3, fixup_row_4 - _start 233 | .word 3, fixup_row_5 - _start 234 | // Row 6 does not require brute force help, because it has the first 0x70-block checksum. 235 | .word 1, fixup_row_7 - _start 236 | .word 3, fixup_row_8 - _start 237 | .word 1, fixup_row_9 - _start 238 | .word 3, fixup_row_A - _start 239 | .word 1, fixup_row_B - _start 240 | .word 1, fixup_row_C - _start 241 | 242 | // The final uint32 is how big the primary payload area is. 243 | .word fixup_list_start - _start 244 | -------------------------------------------------------------------------------- /mechadump/configexploit.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "configexploit.hpp" 8 | 9 | // OpenConfig params: 10 | // 0 = must be 00 or 01; becomes bit 1 of config_open_status 11 | // 1 = written to config_open_status+1 12 | // 2 = written to config_open_status+3, and times 16 to config_write_size 13 | // config_open_status+1 selects: 14 | // 0 = config_write_buffer1, offset 0x270 15 | // 1 = config_write_buffer2, offset 0x2B0 16 | // 2 = config_write_buffer3, offset 0x200 17 | // 18 | // ReadConfig: 19 | // no params 20 | // reads 16 bytes at config_write_address + value of config_open_status+2 21 | // subtracts 1 from config_open_status+3 22 | // adds 16 then mods by 256 to config_open_status+2 23 | 24 | enum ConfigBlockID : uint8_t 25 | { 26 | CONFIG_BLOCK_270 = 0x00, 27 | CONFIG_BLOCK_2B0 = 0x01, 28 | CONFIG_BLOCK_200 = 0x02, 29 | }; 30 | 31 | struct ConfigBlockEntry 32 | { 33 | ConfigBlockID m_blockID; 34 | unsigned m_eepromByteAddress; 35 | size_t m_size; 36 | size_t m_configInfoOffset; 37 | }; 38 | const ConfigBlockEntry s_configBlocks[] = 39 | { 40 | { CONFIG_BLOCK_270, 0x270, sizeof(ConfigInfo::m_config270), offsetof(ConfigInfo, m_config270) }, 41 | { CONFIG_BLOCK_2B0, 0x2B0, sizeof(ConfigInfo::m_config2B0), offsetof(ConfigInfo, m_config2B0) }, 42 | { CONFIG_BLOCK_200, 0x200, sizeof(ConfigInfo::m_config200), offsetof(ConfigInfo, m_config200) }, 43 | }; 44 | 45 | // Read EEPROM at a given arbitrary offset. 46 | // The offset is given in bytes, though note that the EEPROM device is 47 | // natively addressed in 16-bit words. 48 | bool ReadNVM(uint8_t* output, unsigned offset, unsigned length) 49 | { 50 | uint8_t result[3]; 51 | 52 | if (offset >= 0x400) 53 | return false; 54 | if (0x400 - offset < length) 55 | return false; 56 | 57 | // ReadNVM takes a big-endian EEPROM offset and returns a big-endian uint16, 58 | // unlike everything else in PS2 and Mechacon. 59 | 60 | // If offset is odd, even it out. 61 | if (offset % 2) 62 | { 63 | uint8_t wordOffset[2] = 64 | { 65 | static_cast((offset / 2) >> 8), 66 | static_cast(offset / 2) 67 | }; 68 | if (!sceCdApplySCmd(0x0A, wordOffset, sizeof(wordOffset), result, sizeof(result))) 69 | return false; 70 | if (result[0] != 0x00) 71 | return false; 72 | 73 | *output++ = result[1]; 74 | ++offset; 75 | --length; 76 | } 77 | 78 | // Read words. 79 | while (length >= 2) 80 | { 81 | uint8_t wordOffset[2] = 82 | { 83 | static_cast((offset / 2) >> 8), 84 | static_cast(offset / 2) 85 | }; 86 | if (!sceCdApplySCmd(0x0A, wordOffset, sizeof(wordOffset), result, sizeof(result))) 87 | return false; 88 | if (result[0] != 0x00) 89 | return false; 90 | 91 | *output++ = result[2]; 92 | *output++ = result[1]; 93 | offset += 2; 94 | length -= 2; 95 | } 96 | 97 | // If there's one byte remaining, handle it. 98 | if (length > 0) 99 | { 100 | uint8_t wordOffset[2] = 101 | { 102 | static_cast((offset / 2) >> 8), 103 | static_cast(offset / 2) 104 | }; 105 | if (!sceCdApplySCmd(0x0A, wordOffset, sizeof(wordOffset), result, sizeof(result))) 106 | return false; 107 | if (result[0] != 0x00) 108 | return false; 109 | 110 | *output++ = result[2]; 111 | ++offset; 112 | --length; 113 | } 114 | 115 | return true; 116 | } 117 | 118 | // Calls OpenConfig with the specified parameters. 119 | bool OpenConfig(bool forWriting, uint8_t whichBlock, uint8_t rowCount) 120 | { 121 | uint8_t request[3] = 122 | { 123 | static_cast(forWriting), 124 | whichBlock, 125 | rowCount 126 | }; 127 | uint8_t reply[1]; 128 | 129 | if (!sceCdApplySCmd(0x40, request, sizeof(request), reply, sizeof(reply))) 130 | return false; 131 | 132 | return reply[0] == 0x00; 133 | } 134 | 135 | // Reads rowCount * 16 bytes to output. 136 | bool ReadConfig(uint8_t* output, uint8_t rowCount) 137 | { 138 | for (unsigned row = 0; row < rowCount; ++row) 139 | { 140 | uint8_t reply[0x10]; 141 | if (!sceCdApplySCmd(0x41, nullptr, 0, reply, sizeof(reply))) 142 | return false; 143 | 144 | std::memcpy(&output[row * 0x10], reply, 0x10); 145 | } 146 | 147 | return true; 148 | } 149 | 150 | // Do the WriteConfig command to write to EEPROM. 151 | bool WriteConfig(const uint8_t* data, uint8_t rowCount) 152 | { 153 | for (unsigned row = 0; row < rowCount; ++row) 154 | { 155 | uint8_t reply[1]; 156 | if (!sceCdApplySCmd(0x42, &data[row * 0x10], 0x10, reply, sizeof(reply))) 157 | return false; 158 | 159 | if (reply[0] != 0x00) 160 | return false; 161 | } 162 | 163 | return true; 164 | } 165 | 166 | uint8_t CloseConfigInternal() 167 | { 168 | uint8_t reply[1]; 169 | if (!sceCdApplySCmd(0x43, nullptr, 0, reply, sizeof(reply))) 170 | return false; 171 | 172 | return reply[0]; 173 | } 174 | 175 | bool CloseConfigAndWait() 176 | { 177 | for (;;) 178 | { 179 | uint8_t result = CloseConfigInternal(); 180 | if (result == 0x00) // success 181 | return true; 182 | else if (result == 0x01) // EEPROM operations not finished 183 | continue; 184 | else // some error (config never opened?) 185 | return false; 186 | } 187 | } 188 | 189 | // Is this 16-byte row valid for WriteConfig? 190 | bool IsValidConfigRowChecksum(const uint8_t* row) 191 | { 192 | unsigned sum = 0; 193 | for (int i = 0; i < 15; ++i) 194 | sum += row[i]; 195 | return row[15] == static_cast(sum); 196 | } 197 | 198 | // Fixes the checksum byte after the 15 data bytes of the row. 199 | void FixConfigRowChecksum(uint8_t* row) 200 | { 201 | unsigned sum = 0; 202 | for (int i = 0; i < 15; ++i) 203 | sum += row[i]; 204 | row[15] = static_cast(sum); 205 | } 206 | 207 | bool ConfigInfo::HasValidChecksums() const 208 | { 209 | const unsigned char* infoBytes = reinterpret_cast(this); 210 | 211 | for (const ConfigBlockEntry& block : s_configBlocks) 212 | { 213 | for (unsigned x = 0; x < block.m_size; x += 0x10) 214 | { 215 | if (!IsValidConfigRowChecksum(infoBytes + x)) 216 | return false; 217 | } 218 | } 219 | 220 | return true; 221 | } 222 | 223 | // Read the standard config blocks (EEPROM 0x200-0x31F). 224 | bool ReadAllConfig(ConfigInfo& info) 225 | { 226 | unsigned char* infoBytes = reinterpret_cast(&info); 227 | 228 | for (const ConfigBlockEntry& block : s_configBlocks) 229 | { 230 | if (!OpenConfig(false, block.m_blockID, block.m_size / 0x10)) 231 | return false; 232 | if (!ReadConfig(infoBytes + block.m_configInfoOffset, block.m_size / 0x10)) 233 | return false; 234 | if (!CloseConfigAndWait()) 235 | return false; 236 | } 237 | 238 | return true; 239 | } 240 | 241 | // Write the standard config blocks (EEPROM 0x200-0x31F). 242 | bool WriteAllConfig(const ConfigInfo& info) 243 | { 244 | const unsigned char* infoBytes = reinterpret_cast(&info); 245 | 246 | for (const ConfigBlockEntry& block : s_configBlocks) 247 | { 248 | if (!OpenConfig(true, block.m_blockID, block.m_size / 0x10)) 249 | return false; 250 | if (!WriteConfig(infoBytes + block.m_configInfoOffset, block.m_size / 0x10)) 251 | return false; 252 | if (!CloseConfigAndWait()) 253 | return false; 254 | } 255 | 256 | return true; 257 | } 258 | 259 | bool WriteConfigExploit(const uint8_t* patchData, int& errorCode) 260 | { 261 | errorCode = 0; 262 | 263 | // Dump the existing config blocks for restoration. 264 | ConfigInfo configBackup; 265 | if (!ReadAllConfig(configBackup)) 266 | { 267 | errorCode = 1; 268 | return false; 269 | } 270 | 271 | // Weird stuff will happen if any rows' checksums are bad. 272 | if (!configBackup.HasValidChecksums()) 273 | { 274 | errorCode = 2; 275 | return false; 276 | } 277 | 278 | // Both the WriteConfig SCMD handler and the underlying code to write to 279 | // EEPROM wrap around the buffer after 0x100 bytes because they store the 280 | // index as uint8. But we can still ask it to write up to 0x1F0, which 281 | // it will do. This will trash config memory, but we just saved it! 282 | // 283 | // Do the exploit by wrapping the 0x270 buffer around. 284 | // Using the 0x270 buffer means that there is 0x70+0x40+0x70 = 0x120 285 | // bytes of memory to hit our target without corrupting critical 286 | // structures that happen to be right after the third buffer. We only 287 | // need 0x100... 288 | // 289 | // The simplest way to do this write is to just imagine that we're 290 | // writing 0x400 - 0x270 = 0x190 bytes. The wrapping buffer just means 291 | // that copies of the patch data end up where config used to be. 292 | if (!OpenConfig(true, CONFIG_BLOCK_270, 0x190 / 0x10)) 293 | { 294 | errorCode = 3; 295 | return false; 296 | } 297 | 298 | // Write zero rows until it's time to send patch data. 299 | uint8_t zeros[0x10]{}; 300 | for (int i = 0; i < 0x190 - 0xE0; i += 0x10) 301 | { 302 | if (!WriteConfig(zeros, 1)) 303 | { 304 | // Attempt to restore config. 305 | CloseConfigAndWait(); 306 | WriteAllConfig(configBackup); 307 | 308 | errorCode = 4; 309 | return false; 310 | } 311 | } 312 | 313 | // Write the 0xE0 bytes of patch data. However, hold off on the last 314 | // row for a safety check. 315 | if (!WriteConfig(patchData, 0xD0 / 0x10)) 316 | { 317 | // Attempt to restore config. 318 | CloseConfigAndWait(); 319 | WriteAllConfig(configBackup); 320 | 321 | errorCode = 5; 322 | return false; 323 | } 324 | 325 | // Write the final row. 326 | if (!WriteConfig(patchData + 0xD0, 0x10 / 0x10)) 327 | { 328 | // Attempt to restore config. 329 | CloseConfigAndWait(); 330 | WriteAllConfig(configBackup); 331 | 332 | errorCode = 6; 333 | return false; 334 | } 335 | 336 | // Need to make sure write finishes... 337 | 338 | sleep(2); 339 | 340 | CloseConfigAndWait(); 341 | 342 | // The write succeeded. Return true no matter what now, but set 343 | // error code if restoring config fails. 344 | if (!WriteAllConfig(configBackup)) 345 | { 346 | errorCode = 7; 347 | return true; 348 | } 349 | 350 | uint8_t checkPatchset[0xE0]; 351 | if (!ReadNVM(checkPatchset, 0x320, 0xE0)) 352 | { 353 | errorCode = 8; 354 | return false; 355 | } 356 | 357 | if (std::memcmp(checkPatchset, patchData, 0xE0) != 0) 358 | { 359 | errorCode = 9; 360 | return false; 361 | } 362 | 363 | return true; 364 | } 365 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MechaDump 2 | MechaDump is a program to dump the firmware from "Dragon"-series PS2 Mechacon chips: SCPH-500xx, all slim systems, and all "PSX" systems. It also dumps the "keystore" of secret keys. 3 | 4 | **WARNING: THIS PROGRAM IS DANGEROUS.** It can _easily_ "brick" your PS2 if something goes wrong, requiring soldering and a Raspberry Pi to fix it. 5 | 6 | **FOLLOW DIRECTIONS. USE AT YOUR OWN RISK.** 7 | 8 | ## Using MechaDump 9 | Please follow these directions. Bad things could happen if something goes wrong. 10 | 11 | 1. Verify that you have a compatible PS2. It needs to be an SCPH-500xx, a "slim", or a "PSX" (the DVR PS2). Earlier consoles are not supported. 12 | 1. Set up a way to run PS2 ELF files. 13 | 1. Free McBoot on most consoles - only some SCPH-900xx systems aren't supported 14 | 1. OpenTuna or Fortuna - not as convenient, but works on FMCB-incompatible SCPH-900xx systems 15 | 1. Copy mechadump.elf to a USB stick (or SD card with USB adaptor) that works with your PS2. 16 | 1. Run mechadump.elf from the USB stick on your PS2. 17 | 1. Accept the warning. 18 | 1. Proceed past the system information screen. 19 | 1. At the main menu, **BACK UP YOUR EEPROM**. 20 | 1. After EEPROM is backed up, select "Install Backdoor". 21 | 1. From here, you have two choices: 22 | 1. "Disc Protect Hook" back door. This option is safer, but requires a legitimate PS2 disc for your PS2's region, and a working PS2 disc drive to read it. 23 | 1. "IRQ Hook" back door. This is more dangerous, but doesn't require a legitimate PS2 disc or a working PS2 disc drive. 24 | 1. Once it is installed, you need to _remove power from the machine_. Turning off the normal way _will not work_. On the "fat" system (SCPH-500xx), you can flip the power 25 | switch in the back. On other systems, you'll need to pull the power plug. 26 | 1. Turn your system back on and return to mechadump.elf. 27 | 1. At the main menu, this time select "Dump Mechacon ROM", which should no longer be grayed out. 28 | 1. If using "Disc Protect Hook", you may be prompted to insert a legitimate disc. Do it when asked. 29 | 1. This will take a bit (~5 minutes). 30 | 1. When it's done, return to the MechaDump main menu and select "Restore EEPROM". 31 | 1. After the restore, your system will automatically turn off, and will be back to normal. You don't need to remove power again. 32 | 1. The dump is done! You will find "mechacon-_version_-_date_.bin" and "mechacon-keystore.bin" on your USB stick. 33 | 34 | **DO NOT** leave your system in the "back door" state. Restore your EEPROM before using your PS2 normally again. Failure to do this could result in reliability issues: 35 | Sony fixed bugs in the Mechacon at the factory, and MechaDump's back door disables these patches. Restoring EEPROM will reinstall the patches. 36 | 37 | ## Building 38 | Compiling MechaDump is easy...once you get the prerequisites, which is the hard part. We need 3 different versions of GCC: one for the machine doing the compile, one for the PS2 39 | Emotion Engine, and one for the Mechacon, which is a 32-bit ARM chip (ARM7TDMI). 40 | 41 | ### Prerequisites 42 | You need: 43 | * A Linux system. 44 | * Ubuntu for Windows works; it's what I use! 45 | * GCC and G++ for your Linux system. 46 | * The homebrew PS2 SDK installed in Linux. 47 | * https://github.com/ps2dev/ps2sdk 48 | * An ARM32 GCC and G++ installed in Linux. 49 | * The Ubuntu package `gcc-arm-none-eabi` will work. 50 | * The 3DS homebrew SDK might work, but I haven't tried. [3DBrew: Setting up Development Environment](https://www.3dbrew.org/wiki/Setting_up_Development_Environment) 51 | * Two secret keys, which aren't provided here, but are easy to find online. 52 | * `MECHA_PATCH_KEY` 53 | * `GLOBAL_FLAGS_KEY` 54 | 55 | ### Make 56 | You need to tell the Makefile the names of your PS2 GCC and your ARM GCC. You do this by setting the Makefile variables `EE_PREFIX` and `ARM_PREFIX` to the part 57 | of the name of GCC for these targets before `gcc`, including any final dashes. For example, if your R5900 (EE) compiler for PS2 is named `meow-ee-gcc`, you use 58 | `EE_PREFIX=meow-ee-`. Likewise for your ARM32 compiler. For a standard PS2SDK setup, `EE_PREFIX` is `mips64r5900el-ps2-elf-`. 59 | 60 | You also need to provide the keys using `MECHA_PATCH_KEY=` and `GLOBAL_FLAGS_KEY=` parameters. There is also an optional `HOST_PREFIX` if you need to use a special `gcc` for 61 | your Linux system itself. (If you're crazy and building from a Raspberry Pi, `ARM_PREFIX=` will work because the host GCC is also ARM32.) 62 | 63 | Here is an example compile string based on my setup, using bogus keys: 64 | 65 | ``` 66 | make EE_PREFIX=mips64r5900el-ps2-elf- ARM_PREFIX=arm-none-eabi- MECHA_PATCH_KEY=0123456789abcdef GLOBAL_FLAGS_KEY=fedcba9876543210 67 | ``` 68 | 69 | ## How It Works 70 | ### Background 71 | It appears that a recurring problem Sony had with pre-50000 Mechacon chips is that they would find problems, and have to make a new mask ROM in order to fix them. That is very expensive and slow. 72 | 73 | In the new Dragon redesign of the Mechacon, Sony added the ability to patch the Mechacon code by writing the patches to EEPROM. Then the factory and repair facilities could update these patches through "test mode" (a serial port connected to Mechacon). 74 | 75 | Naturally, Sony encrypted the patches. But, since this project exists, you can correctly guess that that didn't quite work out as planned. 76 | 77 | The patches were encrypted with DES in ECB mode, with no MAC. The use of ECB meant that any block of 8 zero bytes would always encrypt to the same pattern. When we noticed the repeating pattern, we guessed that they were using ECB mode, and these were encrypted blocks of zeros. That guess turned out correct. We hoped that Sony had used single-DES and not double- or triple-DES, and it turns out they did. Brute-forcing single-DES is actually rather cheap; 256 isn't as big a number as it used to be. 78 | 79 | ### Exploiting 80 | Sony intended that only by connecting to the "test mode" serial port could patches be updated or the region code be changed, so that at the least you'd have to open your system in order to hack it even if the crypto was compromised. But there are bugs in the Mechacon. 81 | 82 | In the Dragon series Mechacons, because there is security-critical data in EEPROM, the "WriteNVM" S-command no longer allows software to write to anywhere within EEPROM. Only certain regions are writable, and patches are not one of them. 83 | 84 | Certain system settings are stored in EEPROM by OSDSYS, like video mode. EEPROM bytes 0x200 through 0x31F are reserved for software-writable configuration in blocks of 16 bytes. Software can write to these areas by using the "OpenConfig" and "WriteConfig" S-commands. The patch data in EEPROM is 0x320-0x3FF, right after the config area. OpenConfig takes a length parameter that wasn't sufficiently validated, so we can write past the end into the patch data - oops. 85 | 86 | Two complications happen. The first is that the WriteConfig buffer wraps every 256 bytes because the index value is a uint8. This just complicates the write exploit a bit. The regular config gets trashed, so we have to restore it after writing the patch data. No big deal. 87 | 88 | The other complication is considerably more annoying: WriteConfig requires that the 16th byte of each block of 16 bytes be a checksum of the previous 15 bytes. And even better, the patches themselves have their own checksum data. We have to simultaneously meet all the checksums...all with DES-encrypted patch data. 89 | 90 | That required some engineering. We made a way for our patch code to have useless instructions that could be brute-forced to match the required checksum bytes. A table in the compiled patch says which bytes are changeable for this purpose. The tool `mechapatchtool` in this project does the brute-forcing to find a useless harmless instruction to put at various places such that when encrypted, it happens to have a valid checksum. 91 | 92 | ### Patching 93 | The patch mechanism has four "channels". Each channel allows replacing a uint32 of ROM with some other value. So, we can change up to 16 bytes of the ROM. The patch EEPROM data is loaded to a RAM address, and typically, the Sony ROM patches trigger jumps to this RAM area. 94 | 95 | These patches are loaded from EEPROM during Mechacon startup. Because Mechacon is the chip that watches for you to press the power button, it's running from the moment you plug in it. This is why you have to unplug the system in order to reload patches. 96 | 97 | Because this tool is specifically for dumping the Mechacon, we have a problem: how do we know what ROM addresses to patch, and how do we know where in RAM our patches have been loaded to? 98 | 99 | This was a difficult problem that took a lot of trial and error until we managed to get our first dump blind. Once we had the first dump, we could analyze the patching mechanism to understand what was happening. Then we got a second dump of a different version. 100 | 101 | Now we could see what would change between versions. It turns out that near the beginning of ROM are two routines that are sufficiently large enough, safe enough to patch, can be triggered reliably, and change locations very little among ROM versions. These two routines are the IRQ handler and one of the functions involved in the disc protection. Hence the two options available. 102 | 103 | The Mechacon has a system call (SVC) mechanism designed to facilitate patching. If an SVC instruction occurs, the SVC handler jumps to an address written to the patch data. Many Sony patches use this by putting an SVC instruction somewhere, then having code that gets executed as a hook on the patched address. 104 | 105 | The SVC handler has a pointer to the RAM area at which patch EEPROM is loaded so it can do this. The SVC handler itself moves among Mechacon versions... but the SVC vector at 0x00000028 has a pointer to the SVC handler. The SVC handler doesn't change at all through the lifetime of the Dragon Mechacon, allowing us to use its pointer to the RAM location to find ourselves. 106 | 107 | The code to find and read the SVC handler then jump to the RAM address is only 6 Thumb instructions, or 12 bytes, which fit in the 16 bytes of ROM patching we can do. (Actually, we can only patch 12 bytes, because one of the patches needs to be a bogus location to match the 16-byte block checksum requirement of WriteConfig.) 108 | 109 | ### Hooking 110 | Now we can execute arbitrary code, but what can we do with it that lets us dump the Mechacon back to PS2 software? 111 | 112 | We search ROM for a certain pattern to find the handler for the S command 0x03 with subcommand 0xA4. 03:A4 is some region-related back door Sony has, and no OS nor game software is going to use this, so we just take it over. From dumps, we know where the patch hardware registers are, so can change the patches at runtime to hook this function once we find it. 113 | 114 | Now we have code that can be executed on demand by PS2 software, receive a request, and send a result. So we make a simple back door that allows reading, writing, and executing arbitrary addresses. 115 | 116 | And all this has to fit in 0xC0 bytes of code, including dummy instructions used to brute-force row checksums. It took quite a bit of size optimization to fit everything needed. 117 | 118 | ## Credits 119 | * AKuHAK 120 | * asmblur 121 | * balika011 122 | * krHACKen 123 | * l\_oliveira 124 | * Mathieulh 125 | * Myria 126 | * V3ditata (for dumping the rare Latin American Mechacon version for us!) 127 | * Others who wish to remain anonymous 128 | -------------------------------------------------------------------------------- /mechadump/dumper.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "dumper.hpp" 12 | #include "util.h" 13 | 14 | 15 | bool IsBackDoorFunctioning() 16 | { 17 | // Issue 03:A4 with a zero response to the challenge. 18 | // The original code will just return a 0; ours will return A4. 19 | uint8_t request[9]{ 0xA4 }; 20 | uint8_t reply[1]; 21 | if (!sceCdApplySCmd(0x03, request, sizeof(request), reply, sizeof(reply))) 22 | return false; 23 | 24 | return reply[0] == 0xA4; 25 | } 26 | 27 | bool BackDoorReadU32(uint32_t *value, uint32_t address) 28 | { 29 | uint32_t params[4]; 30 | params[0] = 0x000000A4; 31 | params[1] = 0; 32 | params[2] = address; 33 | params[3] = 0; 34 | 35 | uint8_t result[5]; 36 | memset(result, 0xCC, sizeof(result)); 37 | if (!sceCdApplySCmd(0x03, params, sizeof(params), result, sizeof(result))) 38 | return false; 39 | if (result[0] != 0x81) 40 | return false; 41 | 42 | params[0] = 0x44434DA4; 43 | memset(result, 0xCC, sizeof(result)); 44 | if (!sceCdApplySCmd(0x03, params, 9, result, sizeof(result))) 45 | return false; 46 | if (result[0] != 0x42) 47 | return false; 48 | 49 | memcpy(value, &result[1], sizeof(*value)); 50 | return true; 51 | } 52 | 53 | bool BackDoorExecuteU32(uint8_t *output16, uint32_t address, uint32_t r3) 54 | { 55 | uint32_t params[4]; 56 | params[0] = 0x000000A4; 57 | params[1] = 1; 58 | params[2] = address; 59 | params[3] = r3; 60 | 61 | uint8_t result[5]; 62 | memset(result, 0xCC, sizeof(result)); 63 | if (!sceCdApplySCmd(0x03, params, sizeof(params), result, sizeof(result))) 64 | return false; 65 | if (result[0] != 0x81) 66 | return false; 67 | 68 | params[0] = 0x44434DA4; 69 | memset(result, 0xCC, sizeof(result)); 70 | if (!sceCdApplySCmd(0x03, params, 9, output16, 16)) 71 | return false; 72 | if (result[0] == 0xA4) 73 | return false; 74 | 75 | return true; 76 | } 77 | 78 | // Dump Mechacon memory slowly using the back door to read 4 bytes at a time. 79 | std::vector DumpMechaconMemory(uint32_t address, uint32_t size, DebugOutput& debug) 80 | { 81 | std::vector emptyVector; 82 | 83 | if (!IsBackDoorFunctioning()) 84 | { 85 | debug.Printf("Back door not found\n"); 86 | return emptyVector; 87 | } 88 | 89 | if ((size | address) % sizeof(uint32_t)) 90 | { 91 | debug.Printf("DumpMechaconMemory: Misaligned address or size\n"); 92 | return emptyVector; 93 | } 94 | 95 | std::vector result; 96 | result.resize(size / sizeof(uint32_t)); 97 | 98 | for (uint32_t index = 0; index < size / sizeof(uint32_t); ++index) 99 | { 100 | uint32_t target = address + (index * sizeof(uint32_t)); 101 | if (!BackDoorReadU32(&result[index], target)) 102 | { 103 | debug.Printf("Failed to read address %08" PRIX32 "\n", target); 104 | return emptyVector; 105 | } 106 | } 107 | 108 | return result; 109 | } 110 | 111 | std::vector DumpMechaconRAM(DebugOutput& debug) 112 | { 113 | return DumpMechaconMemory(MECHACON_RAM_START, MECHACON_RAM_SIZE, debug); 114 | } 115 | 116 | bool UploadFakeMagicGateHeader(const void* data, size_t size, DebugOutput& debug) 117 | { 118 | if (size > 0x7F0) 119 | { 120 | debug.Printf("Size %08X too large for MG header\n", static_cast(size)); 121 | return false; 122 | } 123 | 124 | // Say we're sending a larger header than we actually are, so it doesn't actually start 125 | // processing the header. 126 | uint8_t headerRequest[5] = 127 | { 128 | 0x00, // header code 129 | static_cast((size + 0x10)), 130 | static_cast((size + 0x10) >> 8), 131 | 0x00, // key index? 132 | 0x00, // another key index? 133 | }; 134 | uint8_t reply[1]; 135 | 136 | if (!sceCdApplySCmd(0x90, headerRequest, sizeof(headerRequest), reply, sizeof(reply))) 137 | { 138 | debug.Printf("sceCdApplySCmd failed for command 0x90\n"); 139 | return false; 140 | } 141 | 142 | if (reply[0] != 0x00) 143 | { 144 | debug.Printf("Command 0x90 failed with code %02X\n", reply[0]); 145 | return false; 146 | } 147 | 148 | const uint8_t* ptr = static_cast(data); 149 | while (size > 0) 150 | { 151 | size_t current = (size > 0x10) ? 0x10 : size; 152 | if (!sceCdApplySCmd(0x8D, ptr, current, reply, sizeof(reply))) 153 | { 154 | debug.Printf("sceCdApplySCmd failed for command 0x8D\n"); 155 | return false; 156 | } 157 | 158 | if (reply[0] != 0x00) 159 | { 160 | debug.Printf("Command 0x8D failed with code %02X\n", reply[0]); 161 | return false; 162 | } 163 | 164 | ptr += current; 165 | size -= current; 166 | } 167 | 168 | return true; 169 | } 170 | 171 | bool UploadAndFindCode(const void* data, size_t size, uint32_t& address, DebugOutput& debug) 172 | { 173 | // Memorize the address and reuse it if possible. 174 | static uint32_t s_previousAddress = uint32_t(-1); 175 | 176 | if (size < 16) 177 | { 178 | debug.Printf("Payload is empty\n"); 179 | return false; 180 | } 181 | if (size % sizeof(uint32_t)) 182 | { 183 | debug.Printf("Size not a multiple of uint32\n"); 184 | return false; 185 | } 186 | 187 | // Flush any existing data. 188 | debug.Printf("Flushing MagicGate header buffer...\n"); 189 | 190 | std::vector zeros; 191 | zeros.resize(0x7F0); 192 | 193 | if (!UploadFakeMagicGateHeader(zeros.data(), zeros.size(), debug)) 194 | { 195 | debug.Printf("Flush failed\n"); 196 | return false; 197 | } 198 | 199 | debug.Printf("Uploading payload to find...\n"); 200 | 201 | // Upload real code. 202 | if (!UploadFakeMagicGateHeader(data, size, debug)) 203 | { 204 | debug.Printf("Upload failed\n"); 205 | return false; 206 | } 207 | 208 | // If we already succeeded before, reuse the address if we can. 209 | if (s_previousAddress != uint32_t(-1)) 210 | { 211 | debug.Printf("Attempting to reuse address %08" PRIX32 "...\n", s_previousAddress); 212 | std::vector check = DumpMechaconMemory(s_previousAddress, size, debug); 213 | if ((check.size() * sizeof(uint32_t) == size) && (std::memcmp(check.data(), data, size) == 0)) 214 | { 215 | debug.Printf("Found again at %08" PRIX32 "\n", s_previousAddress); 216 | address = s_previousAddress; 217 | return true; 218 | } 219 | } 220 | 221 | debug.Printf("Dumping RAM...\n"); 222 | 223 | std::vector ram = DumpMechaconRAM(debug); 224 | if (ram.empty()) 225 | { 226 | debug.Printf("RAM dump failed\n"); 227 | return false; 228 | } 229 | 230 | debug.Printf("Searching for payload...\n"); 231 | 232 | // Find our payload. 233 | uint32_t firstUint32; 234 | std::memcpy(&firstUint32, data, sizeof(firstUint32)); 235 | 236 | uint32_t maxPossible = ram.size() - ((size + sizeof(ram[0]) - 1) / sizeof(ram[0])); 237 | 238 | for (size_t index = 0; index <= maxPossible; ++index) 239 | { 240 | if (ram[index] == firstUint32) 241 | { 242 | if (std::memcmp(ram.data() + index, data, size) == 0) 243 | { 244 | address = MECHACON_RAM_START + (index * sizeof(ram[0])); 245 | debug.Printf("Found uploaded code at %08" PRIX32 "\n", address); 246 | s_previousAddress = address; 247 | return true; 248 | } 249 | } 250 | } 251 | 252 | debug.Printf("Could not find code in RAM\n"); 253 | return false; 254 | } 255 | 256 | void DumpPatchRegistersUsingPayload(const void* payload, size_t size, DebugOutput& debug) 257 | { 258 | static const uint32_t s_registers[] 259 | { 260 | (0x00 + 0x03880000) | 0x10000000, 261 | (0x00 + 0x03880004), 262 | (0x00 + 0x03880008), 263 | (0x10 + 0x03880000) | 0x10000000, 264 | (0x10 + 0x03880004), 265 | (0x10 + 0x03880008), 266 | (0x20 + 0x03880000) | 0x10000000, 267 | (0x20 + 0x03880004), 268 | (0x20 + 0x03880008), 269 | (0x30 + 0x03880000) | 0x10000000, 270 | (0x30 + 0x03880004), 271 | (0x30 + 0x03880008), 272 | }; 273 | 274 | uint32_t address; 275 | if (!UploadAndFindCode(payload, size, address, debug)) 276 | { 277 | debug.Printf("Could not upload payload\n"); 278 | return; 279 | } 280 | debug.Printf("Found code at %08" PRIX32 "\n", address); 281 | 282 | for (uint32_t target : s_registers) 283 | { 284 | uint8_t reply[16]; 285 | std::memset(reply, 0xCC, sizeof(reply)); 286 | 287 | if (!BackDoorExecuteU32(reply, address | 1, target)) 288 | { 289 | debug.Printf("Could not execute payload for %08" PRIX32 "\n", target); 290 | return; 291 | } 292 | 293 | if (target & 0x10000000) 294 | { 295 | if (reply[0] != 0x6A) 296 | { 297 | debug.Printf("Unexpected result %02X for %08" PRIX32 "\n", reply[0], target); 298 | return; 299 | } 300 | 301 | debug.Printf("%08" PRIX32 " = %02X\n", target, reply[1]); 302 | } 303 | else 304 | { 305 | if (reply[0] != 0x6B) 306 | { 307 | debug.Printf("Unexpected result %02X for %08" PRIX32 "\n", reply[0], target); 308 | return; 309 | } 310 | 311 | debug.Printf("%08" PRIX32 " = %02X%02X%02X%02X\n", target, 312 | reply[4], reply[3], reply[2], reply[1]); 313 | } 314 | } 315 | } 316 | 317 | // Convenient subroute for dumping ROM. 318 | bool Dump14Bytes(uint8_t* output, uint32_t romAddress, uint32_t payloadAddress, DebugOutput& debug) 319 | { 320 | // Ask for data. 321 | uint8_t reply[16]; 322 | if (!BackDoorExecuteU32(reply, payloadAddress, romAddress)) 323 | { 324 | debug.Printf("Dump14Bytes: BackDoorExecuteU32 failed at %08" PRIX32 "\n", romAddress); 325 | return false; 326 | } 327 | 328 | if (reply[0] != 0x69) 329 | { 330 | debug.Printf("Dump14Bytes: Bad reply %02X from function\n", reply[0]); 331 | return false; 332 | } 333 | 334 | // The checksum includes the ROM address we're reading. 335 | unsigned checksum = 0; 336 | for (unsigned i = 0; i < sizeof(uint32_t); ++i) 337 | { 338 | checksum += static_cast(romAddress >> (i * 8)); 339 | } 340 | 341 | // Checksum the data. 342 | for (unsigned i = 0; i < 14; ++i) 343 | { 344 | checksum += reply[i + 2]; 345 | } 346 | 347 | checksum = static_cast(~checksum); 348 | 349 | // Verify checksum. 350 | if (reply[1] != checksum) 351 | { 352 | debug.Printf("Dump14Bytes: Invalid checksum %02X (correct=%02X) at %08" PRIX32 "\n", 353 | reply[1], checksum, romAddress); 354 | return false; 355 | } 356 | 357 | // Success; copy and return. 358 | std::memcpy(output, &reply[2], 14); 359 | return true; 360 | } 361 | 362 | 363 | std::vector DumpMechaconROMFastWithPayload(const void* payload, size_t payloadSize, 364 | std::function callback, DebugOutput& debug) 365 | { 366 | std::vector emptyVector; 367 | 368 | debug.Printf("Fastdump: Starting payload upload\n"); 369 | 370 | if (!IsBackDoorFunctioning()) 371 | { 372 | debug.Printf("Fastdump: No back door found?\n"); 373 | return emptyVector; 374 | } 375 | 376 | uint32_t payloadAddress; 377 | if (!UploadAndFindCode(payload, payloadSize, payloadAddress, debug)) 378 | { 379 | debug.Printf("Fastdump: UploadAndFindCode failed\n"); 380 | return emptyVector; 381 | } 382 | payloadAddress |= 1; 383 | 384 | std::vector rom; 385 | rom.resize(MECHACON_ROM_SIZE); 386 | static_assert(MECHACON_ROM_SIZE > 14u, "bad MECHACON_ROM_SIZE constant"); 387 | 388 | debug.Printf("Fastdump: Starting ROM dump\n"); 389 | 390 | // Dump 14 bytes at a time. 391 | constexpr uint32_t mechaconEnd = MECHACON_ROM_START + MECHACON_ROM_SIZE; 392 | 393 | uint32_t prevPage = uint32_t(-1); 394 | uint32_t address; 395 | for (address = MECHACON_ROM_START; mechaconEnd - address >= 14; address += 14) 396 | { 397 | uint32_t atPage = address & ~uint32_t(0xFFF); 398 | if (atPage != prevPage) 399 | { 400 | prevPage = atPage; 401 | debug.Printf("Fastdump: Dump at %08" PRIX32 " so far\n", atPage); 402 | if (!callback(atPage - MECHACON_ROM_START, MECHACON_ROM_SIZE)) 403 | { 404 | debug.Printf("Cancelled.\n"); 405 | return emptyVector; 406 | } 407 | } 408 | 409 | if (!Dump14Bytes(rom.data() + address - MECHACON_ROM_START, address, payloadAddress, debug)) 410 | { 411 | debug.Printf("Fastdump: Failed at %08" PRIX32 "\n", address); 412 | return emptyVector; 413 | } 414 | } 415 | 416 | // Handle the last part. 417 | if (mechaconEnd > address) 418 | { 419 | uint8_t lastBlock[14]; 420 | if (!Dump14Bytes(lastBlock, mechaconEnd - 14, payloadAddress, debug)) 421 | { 422 | debug.Printf("Fastdump: Failed dumping last bytes\n"); 423 | return emptyVector; 424 | } 425 | 426 | std::memcpy(rom.data() + address - MECHACON_ROM_START, 427 | &lastBlock[14 - (mechaconEnd - address)], 428 | mechaconEnd - address); 429 | } 430 | 431 | debug.Printf("Fastdump: Success\n"); 432 | callback(MECHACON_ROM_SIZE, MECHACON_ROM_SIZE); 433 | return rom; 434 | } 435 | 436 | 437 | std::vector DumpMechaconKeystoreWithPayload(const void* payload, size_t payloadSize, 438 | DebugOutput& debug) 439 | { 440 | std::vector emptyVector; 441 | 442 | debug.Printf("Keystoredump: Starting payload upload\n"); 443 | 444 | if (!IsBackDoorFunctioning()) 445 | { 446 | debug.Printf("Keystoredump: No back door found?\n"); 447 | return emptyVector; 448 | } 449 | 450 | uint32_t payloadAddress; 451 | if (!UploadAndFindCode(payload, payloadSize, payloadAddress, debug)) 452 | { 453 | debug.Printf("Keystoredump: UploadAndFindCode failed\n"); 454 | return emptyVector; 455 | } 456 | payloadAddress |= 1; 457 | 458 | std::vector keystore; 459 | keystore.resize(KEYSTORE_SIZE); 460 | 461 | debug.Printf("Keystoredump: Starting keystore dump\n"); 462 | 463 | // Dump 8 bytes (one "key") at a time. 464 | for (uint32_t keyHalfwordOffset = 0; keyHalfwordOffset < KEYSTORE_SIZE / 2; keyHalfwordOffset += 4) 465 | { 466 | uint8_t reply[16]; 467 | if (!BackDoorExecuteU32(reply, payloadAddress, keyHalfwordOffset)) 468 | { 469 | debug.Printf("Could not execute payload for %04" PRIX32 "\n", keyHalfwordOffset); 470 | return emptyVector; 471 | } 472 | 473 | if (reply[0] != 0x00) 474 | { 475 | debug.Printf("Keystoredump: Payload failed\n"); 476 | return emptyVector; 477 | } 478 | 479 | std::memcpy(keystore.data() + (keyHalfwordOffset * 2), &reply[1], 8); 480 | } 481 | 482 | debug.Printf("Keystoredump: Success\n"); 483 | return keystore; 484 | } 485 | 486 | 487 | bool RestoreNVMConfigAndPatchData(const void* payload, size_t payloadSize, const uint8_t* configData, 488 | DebugOutput& debug) 489 | { 490 | debug.Printf("RestoreNVMConfigAndPatchData: Starting payload upload\n"); 491 | 492 | if (!IsBackDoorFunctioning()) 493 | { 494 | debug.Printf("RestoreNVMConfigAndPatchData: No back door found?\n"); 495 | return false; 496 | } 497 | 498 | uint32_t payloadAddress; 499 | if (!UploadAndFindCode(payload, payloadSize, payloadAddress, debug)) 500 | { 501 | debug.Printf("RestoreNVMConfigAndPatchData: UploadAndFindCode failed\n"); 502 | return false; 503 | } 504 | payloadAddress |= 1; 505 | 506 | for (uint16_t wordOffset = 0x200 / 2; wordOffset < 0x400 / 2; ++wordOffset) 507 | { 508 | Retry: 509 | uint8_t reply[16]{}; 510 | 511 | // WriteNVM uses big-endian for who knows what reason. 512 | unsigned char command[4]; 513 | write_be_uint16(&command[0], wordOffset); 514 | command[2] = configData[wordOffset * 2 + 1 - 0x200]; 515 | command[3] = configData[wordOffset * 2 + 0 - 0x200]; 516 | uint32_t r3 = read_le_uint32(command); 517 | 518 | if (!BackDoorExecuteU32(reply, payloadAddress, r3)) 519 | { 520 | debug.Printf("Could not execute payload for %04" PRIX16 "\n", wordOffset); 521 | return false; 522 | } 523 | 524 | // Result 1 indicates busy. 525 | if (reply[0] == 0x01) 526 | goto Retry; 527 | 528 | // Other indicates error. 529 | if (reply[0] != 0x00) 530 | { 531 | debug.Printf("RestoreNVMConfigAndPatchData: failed at %04" PRIX16 " with code %02X\n", 532 | wordOffset, reply[0]); 533 | return false; 534 | } 535 | } 536 | 537 | debug.Printf("RestoreNVMConfigAndPatchData: succeeded, in theory?\n"); 538 | return true; 539 | } 540 | 541 | 542 | // Does not return if successful. 543 | bool ResetMechaconAndPowerOff() 544 | { 545 | if (!IsBackDoorFunctioning()) 546 | return false; 547 | 548 | uint8_t reply[16]{}; 549 | if (!BackDoorExecuteU32(reply, 0, 0)) 550 | return false; 551 | 552 | for (;;) 553 | sleep(1); 554 | } 555 | -------------------------------------------------------------------------------- /mechacrypto/cipher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* 21 | * MechaCon cipher. This is a standard-compliant implementation of DES as 22 | * described in FIPS 46-3. The internal format of the key schedule has been 23 | * changed to allow running the cipher more efficiently on large numbers of 24 | * blocks. 25 | */ 26 | 27 | #include "cipher.h" 28 | #include "util.h" 29 | 30 | /* PC-1 */ 31 | static unsigned char PC1_table[56] = { 32 | 57, 49, 41, 33, 25, 17, 9, 33 | 1, 58, 50, 42, 34, 26, 18, 34 | 10, 2, 59, 51, 43, 35, 27, 35 | 19, 11, 3, 60, 52, 44, 36, 36 | 63, 55, 47, 39, 31, 23, 15, 37 | 7, 62, 54, 46, 38, 30, 22, 38 | 14, 6, 61, 53, 45, 37, 29, 39 | 21, 13, 5, 28, 20, 12, 4, 40 | }; 41 | 42 | /* Left-Shift table */ 43 | static unsigned char LS_table[16] = { 44 | 1, 1, 2, 2, 2, 2, 2, 2, 45 | 1, 2, 2, 2, 2, 2, 2, 1, 46 | }; 47 | 48 | /* 49 | * PC-2x. This one resembles PC-2 from FIPS 46-3 very closely; the only 50 | * difference is that (if line counting starts at 1) odd-numbered lines 51 | * appear before even-numbered lines, but relative order is kept. 52 | */ 53 | static unsigned char PC2x_table[48] = { 54 | 14, 17, 11, 24, 1, 5, 55 | 23, 19, 12, 4, 26, 8, 56 | 41, 52, 31, 37, 47, 55, 57 | 44, 49, 39, 56, 34, 53, 58 | 3, 28, 15, 6, 21, 10, 59 | 16, 7, 27, 20, 13, 2, 60 | 30, 40, 51, 45, 33, 48, 61 | 46, 42, 50, 36, 29, 32, 62 | }; 63 | 64 | /* 65 | * cipherKeyScheduleInner: calculate key schedule of MechaCon cipher. 66 | */ 67 | static void cipherKeyScheduleInner(uint64_t RoundKeys[16], uint64_t Key) 68 | { 69 | uint64_t Input = 0; 70 | 71 | int i; 72 | for (i = 0; i < 56; i++) { 73 | if (Key & ((uint64_t)1 << (64 - PC1_table[i]))) { 74 | Input |= (uint64_t)1 << (55 - i); 75 | } 76 | } 77 | 78 | uint32_t C = (uint32_t)(Input >> 28) & 0x0FFFFFFF; 79 | uint32_t D = (uint32_t)Input & 0x0FFFFFFF; 80 | 81 | for (i = 0; i < 16; i++) { 82 | /* Left shift. Up to this point, this is a standard DES key schedule */ 83 | if (LS_table[i] == 1) { 84 | C = (C << 1) | (C >> 27); 85 | D = (D << 1) | (D >> 27); 86 | } else { 87 | C = (C << 2) | (C >> 26); 88 | D = (D << 2) | (D >> 26); 89 | } 90 | 91 | uint64_t CD = ((uint64_t)(C & 0x0FFFFFFF) << 28) | (D & 0x0FFFFFFF); 92 | uint64_t Ki = 0; 93 | int j; 94 | for (j = 0; j < 48; j++) { 95 | if (CD & ((uint64_t)1 << (56 - PC2x_table[j]))) { 96 | Ki |= (uint64_t)1 << (63 - 2 - ((j / 6) * 8 + (j % 6))); 97 | } 98 | } 99 | 100 | RoundKeys[i] = Ki; 101 | } 102 | } 103 | 104 | /* IP. */ 105 | static unsigned char IP_table[64] = { 106 | 58, 50, 42, 34, 26, 18, 10, 2, 107 | 60, 52, 44, 36, 28, 20, 12, 4, 108 | 62, 54, 46, 38, 30, 22, 14, 6, 109 | 64, 56, 48, 40, 32, 24, 16, 8, 110 | 57, 49, 41, 33, 25, 17, 9, 1, 111 | 59, 51, 43, 35, 27, 19, 11, 3, 112 | 61, 53, 45, 37, 29, 21, 13, 5, 113 | 63, 55, 47, 39, 31, 23, 15, 7, 114 | }; 115 | 116 | /* 117 | * cipherIP: apply MechaCon cipher IP to specified value. 118 | */ 119 | static uint64_t cipherIP(uint64_t Value) 120 | { 121 | uint64_t Output = 0; 122 | 123 | int i; 124 | for (i = 0; i < 64; i++) { 125 | if (Value & ((uint64_t)1 << (64 - IP_table[i]))) { 126 | Output |= (uint64_t)1 << (63 - i); 127 | } 128 | } 129 | 130 | return Output; 131 | } 132 | 133 | 134 | /* inverse IP. */ 135 | static unsigned char IPinv_table[64] = { 136 | 40, 8, 48, 16, 56, 24, 64, 32, 137 | 39, 7, 47, 15, 55, 23, 63, 31, 138 | 38, 6, 46, 14, 54, 22, 62, 30, 139 | 37, 5, 45, 13, 53, 21, 61, 29, 140 | 36, 4, 44, 12, 52, 20, 60, 28, 141 | 35, 3, 43, 11, 51, 19, 59, 27, 142 | 34, 2, 42, 10, 50, 18, 58, 26, 143 | 33, 1, 41, 9, 49, 17, 57, 25, 144 | }; 145 | 146 | /* 147 | * cipherIPInverse: apply MechaCon cipher inverse IP to specified value. 148 | */ 149 | static uint64_t cipherIPInverse(uint64_t Value) 150 | { 151 | uint64_t Output = 0; 152 | 153 | int i; 154 | for (i = 0; i < 64; i++) { 155 | if (Value & ((uint64_t)1 << (64 - IPinv_table[i]))) { 156 | Output |= (uint64_t)1 << (63 - i); 157 | } 158 | } 159 | 160 | return Output; 161 | } 162 | 163 | 164 | /* 165 | * DES S+P tables for the fast software implementation. These tables are generated by code in gen.c 166 | * from the official tables described in FIPS 46-3. 167 | * 168 | * All values are reordered to allow simple 6-bit indexing of each table, instead of the bit 169 | * manipulations necessary when using the tables from FIPS 46-3 directly. The values in these 170 | * tables represent the corresponding S box output under the P permutation. To get the output of 171 | * the DES round function, the outputs of all S+P boxes need to be ORed. 172 | */ 173 | 174 | /* S1 */ 175 | static uint32_t SP_box_1[4*16] = { 176 | 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000, 177 | 0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002, 178 | 0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202, 179 | 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000, 180 | 0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200, 181 | 0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202, 182 | 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200, 183 | 0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002, 184 | }; 185 | 186 | /* S2 */ 187 | static uint32_t SP_box_2[4*16] = { 188 | 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010, 189 | 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010, 190 | 0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000, 191 | 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010, 192 | 0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000, 193 | 0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000, 194 | 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010, 195 | 0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000, 196 | }; 197 | 198 | /* S3 */ 199 | static uint32_t SP_box_3[4*16] = { 200 | 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100, 201 | 0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104, 202 | 0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104, 203 | 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000, 204 | 0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000, 205 | 0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004, 206 | 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004, 207 | 0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100, 208 | }; 209 | 210 | /* S4 */ 211 | static uint32_t SP_box_4[4*16] = { 212 | 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000, 213 | 0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000, 214 | 0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040, 215 | 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040, 216 | 0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000, 217 | 0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040, 218 | 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040, 219 | 0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040, 220 | }; 221 | 222 | /* S5 */ 223 | static uint32_t SP_box_5[4*16] = { 224 | 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000, 225 | 0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000, 226 | 0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080, 227 | 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080, 228 | 0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080, 229 | 0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000, 230 | 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000, 231 | 0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080, 232 | }; 233 | 234 | /* S6 */ 235 | static uint32_t SP_box_6[4*16] = { 236 | 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000, 237 | 0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008, 238 | 0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008, 239 | 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000, 240 | 0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008, 241 | 0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000, 242 | 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008, 243 | 0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008, 244 | }; 245 | 246 | /* S7 */ 247 | static uint32_t SP_box_7[4*16] = { 248 | 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400, 249 | 0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401, 250 | 0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001, 251 | 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400, 252 | 0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001, 253 | 0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400, 254 | 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401, 255 | 0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001, 256 | }; 257 | 258 | /* S8 */ 259 | static uint32_t SP_box_8[4*16] = { 260 | 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000, 261 | 0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020, 262 | 0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800, 263 | 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000, 264 | 0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820, 265 | 0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820, 266 | 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000, 267 | 0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800, 268 | }; 269 | 270 | 271 | /* 272 | * cipherForward: run MechaCon cipher in forward direction. 273 | */ 274 | static void cipherForward(uint64_t Value, uint64_t *Result, const uint64_t RoundKeys[16]) 275 | { 276 | uint64_t Current = cipherIP(Value); 277 | uint32_t Low = (uint32_t)Current; 278 | uint32_t High = (uint32_t)(Current >> 32); 279 | 280 | int i; 281 | for (i = 0; i <= 15; i++) { 282 | uint32_t X = (Low << 29) | (Low >> 3); 283 | uint32_t Y = (Low << 1) | (Low >> 31); 284 | 285 | X ^= (uint32_t)(RoundKeys[i] >> 32); 286 | Y ^= (uint32_t)RoundKeys[i]; 287 | 288 | uint32_t Tab0 = SP_box_1[ (X >> 24) & 0x3F ]; 289 | uint32_t Tab1 = SP_box_2[ (Y >> 24) & 0x3F ]; 290 | uint32_t Tab2 = SP_box_3[ (X >> 16) & 0x3F ]; 291 | uint32_t Tab3 = SP_box_4[ (Y >> 16) & 0x3F ]; 292 | uint32_t Tab4 = SP_box_5[ (X >> 8) & 0x3F ]; 293 | uint32_t Tab5 = SP_box_6[ (Y >> 8) & 0x3F ]; 294 | uint32_t Tab6 = SP_box_7[ (X >> 0) & 0x3F ]; 295 | uint32_t Tab7 = SP_box_8[ (Y >> 0) & 0x3F ]; 296 | 297 | uint32_t Tab = Tab0 | Tab1 | Tab2 | Tab3 | Tab4 | Tab5 | Tab6 | Tab7; 298 | 299 | uint32_t NewLow = High ^ Tab; 300 | High = Low; 301 | Low = NewLow; 302 | } 303 | 304 | *Result = cipherIPInverse(((uint64_t)Low << 32) | (uint64_t)High); 305 | } 306 | 307 | /* 308 | * _cipherKeySchedule: calculate key schedule of MechaCon cipher. 309 | */ 310 | static void _cipherKeySchedule(const uint8_t Key[8], uint64_t RoundKeys[16]) 311 | { 312 | uint64_t KeyValue = read_be_uint64(Key); 313 | cipherKeyScheduleInner(RoundKeys, KeyValue); 314 | } 315 | 316 | /* 317 | * _cipherKeyScheduleReverse: calculate key schedule of MechaCon cipher in reverse order. 318 | */ 319 | static void _cipherKeyScheduleReverse(const uint8_t Key[8], uint64_t RoundKeys[16]) 320 | { 321 | uint64_t ForwardRoundKeys[16]; 322 | int i; 323 | uint64_t KeyValue = read_be_uint64(Key); 324 | cipherKeyScheduleInner(ForwardRoundKeys, KeyValue); 325 | 326 | for (i = 0; i < 16; i++) { 327 | RoundKeys[i] = ForwardRoundKeys[15 - i]; 328 | } 329 | } 330 | 331 | /* 332 | * cipherKeySchedule: perform key schedule for multiple invocations of the MechaCon cipher. 333 | * supports up to three keys. 334 | */ 335 | static int cipherKeySchedule(uint64_t *RoundKeys, const uint8_t *Keys, int KeyCount) 336 | { 337 | if (KeyCount != 1 && KeyCount != 2 && KeyCount != 3) { 338 | return -1; 339 | } 340 | 341 | _cipherKeySchedule(Keys + 0, RoundKeys + 0); 342 | if (KeyCount == 1) { 343 | return 1; 344 | } 345 | 346 | _cipherKeyScheduleReverse(Keys + 8, RoundKeys + 16); 347 | if (KeyCount == 2) { 348 | return 2; 349 | } 350 | 351 | _cipherKeySchedule(Keys + 16, RoundKeys + 32); 352 | return 3; 353 | } 354 | 355 | /* 356 | * cipherKeyScheduleReverse: perform key schedule for multiple invocations of the MechaCon cipher 357 | * in reverse order. Supports up to three keys. 358 | */ 359 | static int cipherKeyScheduleReverse(uint64_t *RoundKeys, const uint8_t *Keys, int KeyCount) 360 | { 361 | if (KeyCount != 1 && KeyCount != 2 && KeyCount != 3) { 362 | return -1; 363 | } 364 | 365 | _cipherKeyScheduleReverse(Keys + 0, RoundKeys + 0); 366 | if (KeyCount == 1) { 367 | return 1; 368 | } 369 | 370 | _cipherKeySchedule(Keys + 8, RoundKeys + 16); 371 | if (KeyCount == 2) { 372 | return 2; 373 | } 374 | 375 | memcpy(RoundKeys + 32, RoundKeys, sizeof(RoundKeys[0]) * 16); 376 | 377 | _cipherKeyScheduleReverse(Keys + 16, RoundKeys); 378 | return 3; 379 | } 380 | 381 | /* 382 | * cipherSingleBlock: Invoke the MechaCon cipher multiple times on a single data block. 383 | * If KeyCount is set to one, a single invocation is performed. 384 | * Otherwise, the cipher is called three times; if only two keys 385 | * are provided, the first key is used two times. 386 | */ 387 | static void cipherSingleBlock(uint8_t Result[8], const uint8_t Data[8], 388 | const uint64_t *RoundKeys, int KeyCount) 389 | { 390 | uint64_t Input = read_be_uint64(Data); 391 | 392 | uint64_t Output; 393 | cipherForward(Input, &Output, RoundKeys); 394 | 395 | if (KeyCount != 1) { 396 | cipherForward(Output, &Output, RoundKeys + 16); 397 | 398 | if (KeyCount == 2) { 399 | cipherForward(Output, &Output, RoundKeys); 400 | } else { 401 | cipherForward(Output, &Output, RoundKeys + 32); 402 | } 403 | } 404 | 405 | write_be_uint64(Result, Output); 406 | } 407 | 408 | /* 409 | * cipherCbcEncrypt: encrypt a buffer using multiple invocations of the MechaCon cipher 410 | * in CBC mode. 411 | */ 412 | int cipherCbcEncrypt(uint8_t *Result, const uint8_t *Data, size_t Length, 413 | const uint8_t *Keys, int KeyCount, const uint8_t IV[8]) 414 | { 415 | uint64_t RoundKeys[16*3]; 416 | uint8_t LastBlock[8]; 417 | size_t i, k; 418 | 419 | if (Length <= 0) { 420 | return -2; 421 | } 422 | 423 | KeyCount = cipherKeySchedule(RoundKeys, Keys, KeyCount); 424 | if (KeyCount < 1) { 425 | return -1; 426 | } 427 | 428 | memcpy(LastBlock, IV, sizeof(LastBlock)); 429 | for (i = 0; Length - i*8 >= 8; i++) { 430 | uint8_t InputBlock[8]; 431 | 432 | memxor(LastBlock, Data + i*8, InputBlock, sizeof(InputBlock)); 433 | cipherSingleBlock(LastBlock, InputBlock, RoundKeys, KeyCount); 434 | memcpy(Result + i*8, LastBlock, sizeof(LastBlock)); 435 | } 436 | 437 | if (Length - i*8 > 0) { 438 | uint8_t BaseBlock[8]; 439 | 440 | cipherSingleBlock(BaseBlock, LastBlock, RoundKeys, KeyCount); 441 | for (k = 0; k < Length - i*8; k++) { 442 | Result[i*8 + k] = BaseBlock[k] ^ Data[i*8 + k]; 443 | } 444 | } 445 | 446 | return 0; 447 | } 448 | 449 | /* 450 | * cipherCbcDecrypt: decrypt a buffer using multiple invocations of the MechaCon cipher 451 | * in CBC mode. 452 | */ 453 | int cipherCbcDecrypt(uint8_t *Result, const uint8_t *Data, size_t Length, 454 | const uint8_t *Keys, int KeyCount, const uint8_t IV[8]) 455 | { 456 | uint64_t RoundKeys[16*3]; 457 | uint8_t LastBlock[8], TailBlock[8]; 458 | size_t i, k; 459 | 460 | if (Length <= 0) { 461 | return -2; 462 | } 463 | 464 | KeyCount = cipherKeyScheduleReverse(RoundKeys, Keys, KeyCount); 465 | if (KeyCount < 1) { 466 | return -1; 467 | } 468 | 469 | memcpy(LastBlock, IV, sizeof(LastBlock)); 470 | for (i = 0; Length - i*8 >= 8; i++) { 471 | uint8_t OutputBlock[8]; 472 | 473 | memcpy(TailBlock, Data + i*8, sizeof(TailBlock)); 474 | 475 | cipherSingleBlock(OutputBlock, TailBlock, RoundKeys, KeyCount); 476 | memxor(LastBlock, OutputBlock, OutputBlock, sizeof(OutputBlock)); 477 | 478 | memcpy(LastBlock, TailBlock, sizeof(TailBlock)); 479 | memcpy(Result + i*8, OutputBlock, sizeof(LastBlock)); 480 | } 481 | 482 | if (Length - i*8 > 0) { 483 | uint8_t BaseBlock[8]; 484 | 485 | cipherKeySchedule(RoundKeys, Keys, KeyCount); 486 | cipherSingleBlock(BaseBlock, TailBlock, RoundKeys, KeyCount); 487 | for (k = 0; k < Length - i*8; k++) { 488 | Result[i*8 + k] = BaseBlock[k] ^ Data[i*8 + k]; 489 | } 490 | } 491 | 492 | return 0; 493 | } 494 | 495 | -------------------------------------------------------------------------------- /mechapatchtool/mechapatchtool.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "cipher.h" 16 | #include "keys.hpp" 17 | #include "util.h" 18 | 19 | using std::uint8_t; 20 | using std::uint16_t; 21 | using std::uint32_t; 22 | using std::uint64_t; 23 | using std::size_t; 24 | 25 | 26 | const char* g_patchcryptInFilename = nullptr; 27 | 28 | 29 | static_assert(sizeof(uint8_t) == 1, "unexpected uint8_t size"); 30 | 31 | 32 | // Useful value 33 | static constexpr uint8_t c_zeroIV[8] = {}; 34 | 35 | 36 | [[noreturn]] void FatalErrorWithFilenameV(const char* filename, const char* format, va_list args) 37 | { 38 | std::fprintf(stderr, "patchcrypt: %s: ", filename); 39 | std::vfprintf(stderr, format, args); 40 | std::fprintf(stderr, "\n"); 41 | 42 | std::fflush(stderr); 43 | std::exit(1); 44 | } 45 | 46 | 47 | [[noreturn]] void FatalError(const char* format, ...) 48 | { 49 | va_list args; 50 | va_start(args, format); 51 | FatalErrorWithFilenameV(g_patchcryptInFilename, format, args); 52 | va_end(args); 53 | } 54 | 55 | 56 | std::vector LoadFile(const char* filename) 57 | { 58 | do 59 | { 60 | std::FILE* file = std::fopen(filename, "rb"); 61 | if (!file) 62 | break; 63 | 64 | if (std::fseek(file, 0, SEEK_END)) 65 | break; 66 | 67 | long lengthLong = std::ftell(file); 68 | 69 | if (std::fseek(file, 0, SEEK_SET) || (lengthLong < 0)) 70 | break; 71 | 72 | size_t length = static_cast(lengthLong); 73 | 74 | std::vector data; 75 | data.resize(length); 76 | 77 | if (std::fread(data.data(), 1, length, file) != length) 78 | break; 79 | 80 | std::fclose(file); 81 | return data; 82 | 83 | } while (false); 84 | 85 | FatalError("%s", strerror(errno)); 86 | std::exit(1); 87 | } 88 | 89 | 90 | int SaveFile(const char* filename, const std::vector& data) 91 | { 92 | do 93 | { 94 | std::FILE* file = std::fopen(filename, "wb"); 95 | if (!file) 96 | break; 97 | 98 | if (std::fwrite(data.data(), 1, data.size(), file) != data.size()) 99 | break; 100 | 101 | std::fclose(file); 102 | return 0; 103 | 104 | } while (false); 105 | 106 | FatalError("%s", strerror(errno)); 107 | return 1; 108 | } 109 | 110 | 111 | // List of Thumb replacement instructions for doing fixups. 112 | struct ThumbReplacements 113 | { 114 | ThumbReplacements(); 115 | 116 | std::vector m_universal; 117 | std::vector m_byRegister[16]; 118 | }; 119 | 120 | 121 | // Builds a list of Thumb opcodes that have no side effects other than destroying 122 | // flags and the registers in registerMask. 123 | ThumbReplacements::ThumbReplacements() 124 | { 125 | // "movs low, low" is always allowed because we allow destroying flags. 126 | for (unsigned reg = 0u; reg < 8u; ++reg) 127 | { 128 | unsigned opcode = 0b0001110'000'000000; 129 | opcode |= reg << 3; 130 | opcode |= reg; 131 | m_universal.push_back(static_cast(opcode)); 132 | } 133 | 134 | // "mov high, high" is always allowed for any register except PC. 135 | // Notably, "nop" is actually "mov r8, r8". 136 | for (unsigned reg = 8u; reg < 15u; ++reg) 137 | { 138 | unsigned opcode = 0b01000110'00000000; 139 | opcode |= ((reg & 8) << 4) | (reg & 7); 140 | opcode |= reg << 3; 141 | m_universal.push_back(static_cast(opcode)); 142 | } 143 | 144 | // Stuff for low registers. 145 | for (unsigned reg = 0u; reg < 8u; ++reg) 146 | { 147 | std::vector& list = m_byRegister[reg]; 148 | unsigned opcode00; 149 | 150 | // movs reg, #any8 151 | opcode00 = 0b00100'000'00000000; 152 | opcode00 |= reg << 8; 153 | for (unsigned b = 0; b < 0x100; ++b) 154 | { 155 | list.push_back(static_cast(opcode00 | b)); 156 | } 157 | 158 | // adds reg, #any8 159 | opcode00 = 0b00110'000'00000000; 160 | opcode00 |= reg << 8; 161 | for (unsigned b = 0; b < 0x100; ++b) 162 | { 163 | list.push_back(static_cast(opcode00 | b)); 164 | } 165 | 166 | // subs reg, #any8 167 | opcode00 = 0b00111'000'00000000; 168 | opcode00 |= reg << 8; 169 | for (unsigned b = 0; b < 0x100; ++b) 170 | { 171 | list.push_back(static_cast(opcode00 | b)); 172 | } 173 | 174 | // lsls reg, anyreg, #any5 175 | opcode00 = 0b00000'00000'000'000; 176 | opcode00 |= reg; 177 | for (unsigned shift = 0; shift < 32; ++shift) 178 | { 179 | for (unsigned otherreg = 0; otherreg < 8; ++otherreg) 180 | { 181 | list.push_back(static_cast(opcode00 | (shift << 6) | (otherreg << 3))); 182 | } 183 | } 184 | 185 | // lsrs reg, anyreg, #any5 186 | opcode00 = 0b00001'00000'000'000; 187 | opcode00 |= reg; 188 | for (unsigned shift = 0; shift < 32; ++shift) // shift 0 means 32 for LSRS 189 | { 190 | for (unsigned otherreg = 0; otherreg < 8; ++otherreg) 191 | { 192 | list.push_back(static_cast(opcode00 | (shift << 6) | (otherreg << 3))); 193 | } 194 | } 195 | 196 | // asrs reg, anyreg, #any5 197 | opcode00 = 0b00010'00000'000'000; 198 | opcode00 |= reg; 199 | for (unsigned shift = 0; shift < 32; ++shift) // shift 0 means 32 for ASRS 200 | { 201 | for (unsigned otherreg = 0; otherreg < 8; ++otherreg) 202 | { 203 | list.push_back(static_cast(opcode00 | (shift << 6) | (otherreg << 3))); 204 | } 205 | } 206 | } 207 | 208 | // That was probably overkill. We'll find a match >99% of the time from the above... for one register. 209 | } 210 | 211 | 212 | // Checks whether a "row" (16 bytes) is WriteConfig compatible. 213 | bool IsWriteConfigCompatible(const uint8_t* row) 214 | { 215 | unsigned sum = 0; 216 | for (int i = 0; i < 15; ++i) 217 | { 218 | sum += row[i]; 219 | } 220 | 221 | return row[15] == static_cast(sum); 222 | } 223 | 224 | 225 | void VerifyPatchData(const std::vector& input, bool& isNonzero, bool& writeConfigCompatible, bool& blockChecksumOK, bool& addressChecksumOK) 226 | { 227 | // Check WriteConfig compatibility. 228 | writeConfigCompatible = true; 229 | for (int row = 0; row < 0xE; ++row) 230 | { 231 | if (!IsWriteConfigCompatible(&input[row * 0x10])) 232 | { 233 | writeConfigCompatible = false; 234 | break; 235 | } 236 | } 237 | 238 | // Initialize the key. The DES code we're using always does CBC mode, so turn it into ECB by doing 239 | // a single block decrypt with a zero IV. 240 | uint8_t key[8]; 241 | write_be_uint64(key, g_mechaPatchKey); 242 | 243 | // Check for a null patch list. 244 | uint8_t patchList[16]; 245 | std::memcpy(patchList, input.data(), sizeof(patchList)); 246 | 247 | cipherCbcDecrypt(&patchList[0], &patchList[0], 8, key, 1, c_zeroIV); 248 | cipherCbcDecrypt(&patchList[8], &patchList[8], 8, key, 1, c_zeroIV); 249 | 250 | isNonzero = false; 251 | for (int i = 0; i < 16; ++i) 252 | { 253 | if (patchList[i] != 0) 254 | { 255 | isNonzero = true; 256 | break; 257 | } 258 | } 259 | 260 | // Check the two blocks' checksums. 261 | blockChecksumOK = true; 262 | for (int block = 0; block < 2; ++block) 263 | { 264 | int offset = block * 0x70; 265 | unsigned sum = 0; 266 | for (int i = 0; i < 0x6E; ++i) // byte 0x6E not counted 267 | { 268 | sum += input[offset + i]; 269 | } 270 | 271 | if (input[offset + 0x6F] != static_cast(~sum)) 272 | { 273 | blockChecksumOK = false; 274 | } 275 | } 276 | 277 | // Check the patch address checksum. It's a wrapping checksum. 278 | uint32_t addressSum = 0; 279 | for (int i = 0; i < 4; ++i) 280 | { 281 | addressSum = static_cast(addressSum + read_le_uint32(&patchList[i * sizeof(uint32_t)])); 282 | } 283 | addressSum = static_cast(~addressSum); 284 | uint32_t allegedAddressSum = read_le_uint32(&input[0xDA]); 285 | addressChecksumOK = allegedAddressSum == addressSum; 286 | } 287 | 288 | 289 | std::vector DecryptUnpackPatchData(const std::vector& encrypted) 290 | { 291 | assert(encrypted.size() == 0xE0); 292 | 293 | std::vector decrypted; 294 | decrypted.resize(0xD8); 295 | 296 | // Combine the two blocks. 297 | std::memcpy(decrypted.data(), encrypted.data(), 0x6E); 298 | std::memcpy(decrypted.data() + 0x6E, encrypted.data() + 0x70, 0x6A); 299 | 300 | // Initialize the key. The DES code we're using always does CBC mode, so turn it into ECB by doing 301 | // a single block decrypt with a zero IV. 302 | uint8_t key[8]; 303 | write_be_uint64(key, g_mechaPatchKey); 304 | 305 | // Decrypt the blocks. 306 | for (int i = 0; i < 0xD8; i += 8) 307 | { 308 | cipherCbcDecrypt(decrypted.data() + i, decrypted.data() + i, 8, key, 1, c_zeroIV); 309 | } 310 | 311 | return decrypted; 312 | } 313 | 314 | 315 | std::vector EncryptPackPatchData(const std::vector& decrypted, bool writeConfigFixup6andD) 316 | { 317 | std::vector encrypted; 318 | encrypted.resize(0xE0); 319 | 320 | // Compute the header checksum. 321 | uint32_t addressSum = 0; 322 | for (int i = 0; i < 16; i += 4) 323 | { 324 | addressSum = static_cast(addressSum + read_le_uint32(decrypted.data() + i)); 325 | } 326 | addressSum = static_cast(~addressSum); 327 | 328 | // Copy the decrypted data, padding out to the maximum size. 329 | assert(decrypted.size() <= 0xD8); 330 | std::memcpy(encrypted.data(), decrypted.data(), decrypted.size()); 331 | std::memset(encrypted.data() + decrypted.size(), 0, 0xE0 - decrypted.size()); 332 | 333 | // Initialize the key. The DES code we're using always does CBC mode, so turn it into ECB by doing 334 | // a single block decrypt with a zero IV. 335 | uint8_t key[8]; 336 | write_be_uint64(key, g_mechaPatchKey); 337 | 338 | // Encrypt the blocks. We'll deal with the block split after this.. 339 | for (int i = 0; i < 0xD8; i += 8) 340 | { 341 | cipherCbcEncrypt(encrypted.data() + i, encrypted.data() + i, 8, key, 1, c_zeroIV); 342 | } 343 | 344 | // Split the blocks. 345 | std::memmove(encrypted.data() + 0x70, encrypted.data() + 0x6E, 0x6E); 346 | 347 | // Header checksum. 348 | write_le_uint32(encrypted.data() + 0xDA, addressSum); 349 | 350 | // Block checksums. 351 | for (int block = 0; block < 2; ++block) 352 | { 353 | int offset = block * 0x70; 354 | unsigned sum = 0; 355 | for (int i = 0; i < 0x6E; ++i) // byte 0x6E not counted 356 | { 357 | sum += encrypted[offset + i]; 358 | } 359 | 360 | encrypted[offset + 0x6F] = static_cast(~sum); 361 | 362 | // Unused bytes. If requested, set to the value that makes a WriteConfig checksum pass 363 | // for the final row. 364 | if (writeConfigFixup6andD) 365 | { 366 | unsigned row6Sum = 0x00; 367 | for (int i = 0x60; i < 0x6E; ++i) 368 | { 369 | row6Sum += encrypted[offset + i]; 370 | } 371 | encrypted[offset + 0x6E] = static_cast(encrypted[offset + 0x6F] - row6Sum); 372 | } 373 | else 374 | { 375 | encrypted[offset + 0x6E] = 0x00; 376 | } 377 | } 378 | 379 | return encrypted; 380 | } 381 | 382 | 383 | // Helper routine to extract patch data from an EEPROM dump. 384 | // If it's already extracted, this does nothing. 385 | bool ExtractPatchDataFromEEPROM(std::vector& dump) 386 | { 387 | if (dump.size() == 0xE0) 388 | { 389 | // Already extracted. 390 | return true; 391 | } 392 | else if (dump.size() == 0x400) 393 | { 394 | // Presumably an EEPROM dump. 395 | dump.erase(dump.begin(), dump.begin() + 0x320); 396 | return true; 397 | } 398 | else 399 | { 400 | return false; 401 | } 402 | } 403 | 404 | 405 | // Verifies that a patch file's checksums are valid. 406 | int VerifyPatchFile(const char* inFilename) 407 | { 408 | g_patchcryptInFilename = inFilename; 409 | std::vector input = LoadFile(inFilename); 410 | 411 | if (!ExtractPatchDataFromEEPROM(input)) 412 | { 413 | FatalError("file is not a patch file"); 414 | } 415 | 416 | assert(input.size() == 0xE0); 417 | 418 | bool isNonzero; 419 | bool writeConfigCompatible; 420 | bool blockChecksumOK; 421 | bool addressChecksumOK; 422 | VerifyPatchData(input, isNonzero, writeConfigCompatible, blockChecksumOK, addressChecksumOK); 423 | 424 | std::printf("Patch has data: %s\n", isNonzero ? "YES" : "NO"); 425 | std::printf("WriteConfig exploit compatible: %s\n", writeConfigCompatible ? "YES" : "NO"); 426 | std::printf("Block checksums OK: %s\n", blockChecksumOK ? "YES" : "NO"); 427 | std::printf("Address checksum OK: %s\n", addressChecksumOK ? "YES" : "NO"); 428 | 429 | // isNonzero isn't relevant for success 430 | return (writeConfigCompatible && blockChecksumOK && addressChecksumOK) ? 0 : 1; 431 | } 432 | 433 | 434 | // Decrypts a patch file. 435 | int DecryptPatchFile(const char* inFilename, const char* outFilename) 436 | { 437 | g_patchcryptInFilename = inFilename; 438 | std::vector input = LoadFile(inFilename); 439 | 440 | if (!ExtractPatchDataFromEEPROM(input)) 441 | { 442 | FatalError("file is not a patch file"); 443 | } 444 | 445 | assert(input.size() == 0xE0); 446 | 447 | std::vector output = DecryptUnpackPatchData(input); 448 | 449 | return SaveFile(outFilename, output); 450 | } 451 | 452 | 453 | // Encrypts a patch file. 454 | int EncryptPatchFile(const char* inFilename, const char* outFilename) 455 | { 456 | g_patchcryptInFilename = inFilename; 457 | std::vector input = LoadFile(inFilename); 458 | 459 | assert(input.size() == 0xD8); 460 | 461 | std::vector output = EncryptPackPatchData(input, false); 462 | 463 | return SaveFile(outFilename, output); 464 | } 465 | 466 | 467 | 468 | struct Fixup 469 | { 470 | uint32_t m_type; 471 | uint32_t m_offset; 472 | 473 | bool operator<(const Fixup& other) const 474 | { 475 | return m_offset < other.m_offset; 476 | } 477 | }; 478 | 479 | enum FixupType : uint32_t 480 | { 481 | FixupType_AnyUint8 = 0, 482 | FixupType_AnyUint16 = 1, 483 | FixupType_PatchAddress = 2, 484 | FixupType_ThumbRegDestroy = 3, 485 | FixupType_NumFixupTypes 486 | }; 487 | static const unsigned char s_fixupSizeByType[] = 488 | { 489 | sizeof(uint8_t), // FixupType_AnyUint8 490 | sizeof(uint16_t), // FixupType_AnyUint16 491 | sizeof(uint32_t), // FixupType_PatchAddress 492 | sizeof(uint16_t), // FixupType_ThumbRegDestroy 493 | }; 494 | static_assert(sizeof(s_fixupSizeByType) == FixupType_NumFixupTypes, "s_fixupSizeByType is wrong size"); 495 | 496 | uint32_t FixupAnyUint8_CountPossible(const uint8_t*) 497 | { 498 | return uint32_t(uint8_t(-1)) + 1; 499 | } 500 | void FixupAnyUint8_Apply(uint32_t counter, uint8_t* overwrite, const uint8_t*) 501 | { 502 | assert(counter <= uint8_t(-1)); 503 | *overwrite = static_cast(counter); 504 | } 505 | 506 | uint32_t FixupAnyUint16_CountPossible(const uint8_t*) 507 | { 508 | return uint32_t(uint16_t(-1)) + 1; 509 | } 510 | void FixupAnyUint16_Apply(uint32_t counter, uint8_t* overwrite, const uint8_t*) 511 | { 512 | assert(counter <= uint16_t(-1)); 513 | write_le_uint16(overwrite, static_cast(counter)); 514 | } 515 | 516 | enum : uint32_t 517 | { 518 | // Address range we don't care about messing with in a patch. 519 | PATCHADDRESS_FIXUP_START = 0x44000, // past the end 520 | PATCHADDRESS_FIXUP_SIZE = 0x1000, 521 | }; 522 | uint32_t FixupPatchAddress_CountPossible(const uint8_t*) 523 | { 524 | return PATCHADDRESS_FIXUP_SIZE / 4; 525 | } 526 | void FixupPatchAddress_Apply(uint32_t counter, uint8_t* overwrite, const uint8_t*) 527 | { 528 | assert(counter < (PATCHADDRESS_FIXUP_SIZE / 4)); 529 | write_le_uint32(overwrite, PATCHADDRESS_FIXUP_START + (counter * 4)); 530 | } 531 | 532 | static ThumbReplacements s_thumbReplacements; 533 | uint32_t FixupThumbRegDestroy_CountPossible(const uint8_t* original) 534 | { 535 | unsigned regList = read_le_uint16(original); 536 | size_t count = s_thumbReplacements.m_universal.size(); 537 | for (int i = 0; i < 16; ++i) 538 | { 539 | if (regList & (1u << i)) 540 | { 541 | count += s_thumbReplacements.m_byRegister[i].size(); 542 | } 543 | } 544 | return static_cast(count); 545 | } 546 | void FixupThumbRegDestroy_Apply(uint32_t counter, uint8_t* overwrite, const uint8_t* original) 547 | { 548 | unsigned regList = read_le_uint16(original); 549 | for (int i = -1; i < 16; ++i) 550 | { 551 | const std::vector* current; 552 | if (i < 0) 553 | { 554 | current = &s_thumbReplacements.m_universal; 555 | } 556 | else if (regList & (1u << i)) 557 | { 558 | current = &s_thumbReplacements.m_byRegister[i]; 559 | } 560 | else 561 | { 562 | continue; 563 | } 564 | 565 | if (counter < current->size()) 566 | { 567 | write_le_uint16(overwrite, (*current)[counter]); 568 | return; 569 | } 570 | else 571 | { 572 | counter -= static_cast(current->size()); 573 | } 574 | } 575 | // counter too large 576 | assert(false); 577 | } 578 | 579 | 580 | struct FixupHandler 581 | { 582 | uint32_t(*m_countPossible)(const uint8_t* original); 583 | void(*m_apply)(uint32_t counter, uint8_t* overwrite, const uint8_t* original); 584 | }; 585 | static const FixupHandler s_fixupHandlers[] = 586 | { 587 | { FixupAnyUint8_CountPossible, FixupAnyUint8_Apply }, 588 | { FixupAnyUint16_CountPossible, FixupAnyUint16_Apply }, 589 | { FixupPatchAddress_CountPossible, FixupPatchAddress_Apply }, 590 | { FixupThumbRegDestroy_CountPossible, FixupThumbRegDestroy_Apply }, 591 | }; 592 | static_assert(sizeof(s_fixupHandlers) / sizeof(s_fixupHandlers[0]) == FixupType_NumFixupTypes, "s_fixupHandlers is wrong size"); 593 | 594 | 595 | int FixupPatchFile(const char* inFilename, const char* outFilename) 596 | { 597 | g_patchcryptInFilename = inFilename; 598 | std::vector input = LoadFile(inFilename); 599 | 600 | if (input.size() < sizeof(uint32_t) * 2) 601 | FatalError("file too small"); 602 | 603 | uint32_t payloadLength = read_le_uint32(input.data() + input.size() - sizeof(uint32_t)); 604 | if (payloadLength > input.size() - sizeof(uint32_t)) 605 | FatalError("invalid payload length"); 606 | if (payloadLength < 0x10) 607 | FatalError("payload too short; must be at least 16 bytes"); 608 | if (payloadLength > 0xD8) 609 | FatalError("payload too long; must be 0xD8 bytes or shorter"); 610 | 611 | // Determine number of fixups. 612 | size_t fixupByteLength = input.size() - payloadLength - sizeof(uint32_t); 613 | if (fixupByteLength % (sizeof(uint32_t) * 2) != 0) 614 | FatalError("non-integer number of fixups"); 615 | 616 | size_t fixupLength = fixupByteLength / (sizeof(uint32_t) * 2); 617 | 618 | std::vector fixups; 619 | fixups.reserve(fixupLength); 620 | 621 | // Parse and validate fixup table. 622 | for (size_t i = 0; i < fixupLength; ++i) 623 | { 624 | const uint8_t* p = input.data() + payloadLength + (i * sizeof(uint32_t) * 2); 625 | uint32_t type = read_le_uint32(p); 626 | uint32_t offset = read_le_uint32(p + sizeof(uint32_t)); 627 | uint32_t size = s_fixupSizeByType[type]; 628 | 629 | if (type >= FixupType_NumFixupTypes) 630 | FatalError("bad fixup type %" PRIu32, type); 631 | if (offset >= payloadLength) 632 | FatalError("fixup out of range at offset %02" PRIX32, offset); 633 | if (payloadLength - offset < size) 634 | FatalError("fixup at offset 0x%02" PRIX32 " goes past the end of payload", offset); 635 | if ((offset >= 0x60) && (offset < 0x6E)) 636 | FatalError("fixup at offset 0x%02" PRIX32 " is on row 6", offset); 637 | if ((offset >= 0x6E) && ((offset & 0xF) >= 0xE)) 638 | FatalError("fixup at offset 0x%02" PRIX32 " not allowed at +0xE row offsets after 0x60 for crypto reasons", offset); 639 | if (i > 0) 640 | { 641 | if (offset < fixups[i - 1].m_offset) 642 | FatalError("fixup at offset 0x%02" PRIX32 " is out of order", offset); 643 | if (offset < fixups[i - 1].m_offset + s_fixupSizeByType[fixups[i - 1].m_type]) 644 | FatalError("fixup at offset 0x%02" PRIX32 " overlaps previous fixup", offset); 645 | } 646 | 647 | fixups.emplace_back(Fixup{ type, offset }); 648 | } 649 | 650 | // For any unused bytes, put a single-byte fixup. 651 | for (unsigned i = payloadLength; i < 0xD8; ++i) 652 | { 653 | fixups.emplace_back(Fixup{ FixupType_AnyUint8, i }); 654 | } 655 | 656 | // Buffer for sketching out alternatives. 657 | std::vector sketch = input; 658 | sketch.resize(payloadLength); 659 | sketch.resize(0xD8); 660 | 661 | // Chain of counters for up to 16 fixups. 662 | std::vector rowCounters; 663 | rowCounters.reserve(16); 664 | std::vector rowPossible; 665 | rowPossible.reserve(16); 666 | 667 | // Initialize the key. The DES code we're using always does CBC mode, so turn it into ECB by doing 668 | // a single block decrypt with a zero IV. 669 | uint8_t key[8]; 670 | write_be_uint64(key, g_mechaPatchKey); 671 | 672 | // Handle the rows. 673 | for (unsigned row = 0x0; row <= 0xD; ++row) 674 | { 675 | // Rows 6 and D are handled by EncryptPackPatchData. 676 | if ((row == 0x6) || (row == 0xD)) 677 | continue; 678 | 679 | // For row 7 and later, two of our bytes are part of the previous encryption block, but affect 680 | // our checksum too. So we need to encrypt the previous block to know what our target is. 681 | uint8_t previousBlock[8]{}; 682 | unsigned rowFixupSize = 0x10; 683 | if (row >= 0x7) 684 | { 685 | cipherCbcEncrypt(previousBlock, sketch.data() + (row * 0x10) - 8, 8, key, 1, c_zeroIV); 686 | rowFixupSize = 0xE; 687 | } 688 | 689 | // Find fixups relevant to this row. 690 | auto fixupBegin = std::lower_bound(fixups.begin(), fixups.end(), Fixup{ 0, row * 0x10 }); 691 | auto fixupEnd = std::upper_bound(fixups.begin(), fixups.end(), Fixup{ 0, row * 0x10 + rowFixupSize - 1 }); 692 | 693 | // Fixup loop. The loop is designed so that a null fixup set works if it already matches. 694 | rowCounters.resize(static_cast(fixupEnd - fixupBegin)); 695 | 696 | // Determine number of options for each fixup. 697 | rowPossible.resize(rowCounters.size()); 698 | for (size_t row = 0; row < rowCounters.size(); ++row) 699 | { 700 | rowCounters[row] = 0; 701 | rowPossible[row] = (*s_fixupHandlers[fixupBegin[row].m_type].m_countPossible)(input.data() + fixupBegin[row].m_offset); 702 | if (rowPossible[row] == 0) 703 | FatalError("fixup at offset %02" PRIX32 " has no possible values", fixupBegin[row].m_offset); 704 | } 705 | 706 | for (;;) 707 | { 708 | // Set the data bytes according to their counters. 709 | for (size_t row = 0; row < rowCounters.size(); ++row) 710 | { 711 | (*s_fixupHandlers[fixupBegin[row].m_type].m_apply)(rowCounters[row], sketch.data() + fixupBegin[row].m_offset, 712 | input.data() + fixupBegin[row].m_offset); 713 | } 714 | 715 | // Encrypt the block. 716 | uint8_t encrypted[16]; 717 | cipherCbcEncrypt(&encrypted[0], sketch.data() + (row * 0x10), 8, key, 1, c_zeroIV); 718 | cipherCbcEncrypt(&encrypted[8], sketch.data() + (row * 0x10) + 8, 8, key, 1, c_zeroIV); 719 | 720 | if (row >= 0x7) 721 | { 722 | // The block to check for a match is misaligned. 723 | uint8_t checkBlock[16]; 724 | std::memcpy(&checkBlock[0], &previousBlock[6], 2); 725 | std::memcpy(&checkBlock[2], &encrypted[0], 14); 726 | if (IsWriteConfigCompatible(checkBlock)) 727 | break; 728 | } 729 | else 730 | { 731 | if (IsWriteConfigCompatible(encrypted)) 732 | break; 733 | } 734 | 735 | // Increment counters. 736 | bool overflow = true; 737 | for (size_t row = 0; row < rowCounters.size(); ++row) 738 | { 739 | ++(rowCounters[row]); 740 | if (rowCounters[row] == rowPossible[row]) 741 | { 742 | // Carry 743 | rowCounters[row] = 0; 744 | continue; 745 | } 746 | overflow = false; 747 | break; 748 | } 749 | if (overflow) 750 | FatalError("ran out of possibilities for match on row %X", row); 751 | } 752 | } 753 | 754 | // Encrypt the data, which also handles rows 6 and D. 755 | std::vector encrypted = EncryptPackPatchData(sketch, true); 756 | 757 | bool isNonzero; 758 | bool writeConfigCompatible; 759 | bool blockChecksumOK; 760 | bool addressChecksumOK; 761 | VerifyPatchData(encrypted, isNonzero, writeConfigCompatible, blockChecksumOK, addressChecksumOK); 762 | 763 | std::printf("--- Fixup verification ---\n"); 764 | std::printf("Patch has data: %s\n", isNonzero ? "YES" : "NO"); 765 | std::printf("WriteConfig exploit compatible: %s\n", writeConfigCompatible ? "YES" : "NO"); 766 | std::printf("Block checksums OK: %s\n", blockChecksumOK ? "YES" : "NO"); 767 | std::printf("Address checksum OK: %s\n", addressChecksumOK ? "YES" : "NO"); 768 | 769 | if (!isNonzero || !writeConfigCompatible || !blockChecksumOK || !addressChecksumOK) 770 | FatalError("fixup process failed to verify"); 771 | 772 | return SaveFile(outFilename, encrypted); 773 | } 774 | 775 | 776 | [[noreturn]] void SyntaxError() 777 | { 778 | std::printf( 779 | "Syntax:\n" 780 | " Apply fixups to a compiled patch file:\n" 781 | " mechapatchtool --fixup --in=infile --out=outfile\n" 782 | " Verify the checksums on an encrypted patch file:\n" 783 | " mechapatchtool --verify --in=infile\n" 784 | " Decrypt an encrypted patch dump, either alone or in an EEPROM dump:\n" 785 | " mechapatchtool --decrypt --in=infile --out=outfile\n" 786 | " Encrypt a patch dump:\n" 787 | " mechapatchtool --encrypt --in=infile --out=outfile\n" 788 | "\n" 789 | "Filenames can also be separate parameters (and don't use equal signs).\n"); 790 | std::exit(1); 791 | } 792 | 793 | 794 | int main(int, char** argv) 795 | { 796 | // Parameter declarations. 797 | bool cmdFixup = false; 798 | bool cmdVerify = false; 799 | bool cmdDecrypt = false; 800 | bool cmdEncrypt = false; 801 | const char* inFilename = nullptr; 802 | const char* outFilename = nullptr; 803 | 804 | struct ParamInfo 805 | { 806 | const char* m_name; 807 | bool m_isCommand; 808 | size_t m_strlen; 809 | bool* m_bool; 810 | const char** m_string; 811 | }; 812 | ParamInfo paramInfo[] = 813 | { 814 | { "--fixup", true, 0, &cmdFixup, nullptr }, 815 | { "--verify", true, 0, &cmdVerify, nullptr }, 816 | { "--decrypt", true, 0, &cmdDecrypt, nullptr }, 817 | { "--encrypt", true, 0, &cmdEncrypt, nullptr }, 818 | { "--in", false, 0, nullptr, &inFilename }, 819 | { "--out", false, 0, nullptr, &outFilename }, 820 | }; 821 | for (ParamInfo& param : paramInfo) 822 | { 823 | param.m_strlen = std::strlen(param.m_name); 824 | } 825 | 826 | // Evaluate parameters. 827 | if (!argv[0]) 828 | SyntaxError(); 829 | 830 | bool haveCommand = false; 831 | 832 | for (++argv; *argv; ++argv) 833 | { 834 | const char* arg = *argv; 835 | size_t length = std::strlen(arg); 836 | 837 | for (const ParamInfo& param : paramInfo) 838 | { 839 | if (param.m_bool) 840 | { 841 | if ((length == param.m_strlen) && (std::memcmp(arg, param.m_name, length) == 0)) 842 | { 843 | if (haveCommand && param.m_isCommand) 844 | SyntaxError(); 845 | *param.m_bool = true; 846 | if (param.m_isCommand) 847 | haveCommand = true; 848 | goto found; 849 | } 850 | } 851 | 852 | if (param.m_string) 853 | { 854 | size_t tempLength = length; 855 | const char* equals = static_cast(std::memchr(arg, '=', tempLength)); 856 | if (equals) 857 | tempLength = static_cast(equals - arg); 858 | 859 | if ((tempLength == param.m_strlen) && (std::memcmp(arg, param.m_name, tempLength) == 0)) 860 | { 861 | if (*param.m_string) 862 | SyntaxError(); 863 | *param.m_string = equals ? equals + 1 : *++argv; 864 | if (!*param.m_string) 865 | SyntaxError(); 866 | goto found; 867 | } 868 | } 869 | } 870 | 871 | SyntaxError(); 872 | 873 | found:; 874 | } 875 | 876 | if (!haveCommand) 877 | SyntaxError(); 878 | 879 | if (cmdFixup) 880 | { 881 | if (!inFilename || !outFilename) 882 | SyntaxError(); 883 | 884 | return FixupPatchFile(inFilename, outFilename); 885 | } 886 | else if (cmdVerify) 887 | { 888 | if (!inFilename || outFilename) 889 | SyntaxError(); 890 | 891 | return VerifyPatchFile(inFilename); 892 | } 893 | else if (cmdDecrypt) 894 | { 895 | if (!inFilename || !outFilename) 896 | SyntaxError(); 897 | 898 | return DecryptPatchFile(inFilename, outFilename); 899 | } 900 | else if (cmdEncrypt) 901 | { 902 | if (!inFilename || !outFilename) 903 | SyntaxError(); 904 | 905 | return EncryptPatchFile(inFilename, outFilename); 906 | } 907 | else 908 | { 909 | assert(false); 910 | } 911 | } 912 | -------------------------------------------------------------------------------- /mechadump/mechadump.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "configexploit.hpp" 24 | #include "dumper.hpp" 25 | #include "knowndumps.hpp" 26 | #include "sysinfo.hpp" 27 | 28 | #include "crc32.h" 29 | 30 | 31 | constexpr int c_versionMajor = 1; 32 | constexpr int c_versionMinor = 3; 33 | 34 | 35 | extern "C" unsigned int size_irx_fileXio; 36 | extern "C" unsigned char irx_fileXio[]; 37 | extern "C" unsigned int size_irx_padman; 38 | extern "C" unsigned char irx_padman[]; 39 | extern "C" unsigned int size_irx_sio2man; 40 | extern "C" unsigned char irx_sio2man[]; 41 | extern "C" unsigned int size_irx_iomanX; 42 | extern "C" unsigned char irx_iomanX[]; 43 | extern "C" unsigned int size_irx_mtapman; 44 | extern "C" unsigned char irx_mtapman[]; 45 | extern "C" unsigned int size_irx_mcman; 46 | extern "C" unsigned char irx_mcman[]; 47 | extern "C" unsigned int size_irx_mcserv; 48 | extern "C" unsigned char irx_mcserv[]; 49 | extern "C" unsigned int size_irx_usbd; 50 | extern "C" unsigned char irx_usbd[]; 51 | extern "C" unsigned int size_irx_usbhdfsd; 52 | extern "C" unsigned char irx_usbhdfsd[]; 53 | 54 | extern "C" unsigned int size_patch_irq_hook; 55 | extern "C" unsigned char patch_irq_hook[]; 56 | extern "C" unsigned int size_patch_cdprotect_hook; 57 | extern "C" unsigned char patch_cdprotect_hook[]; 58 | 59 | extern "C" unsigned int size_payload_fastdump; 60 | extern "C" unsigned char payload_fastdump[]; 61 | extern "C" unsigned int size_payload_writenvm; 62 | extern "C" unsigned char payload_writenvm[]; 63 | extern "C" unsigned int size_payload_keystoredump; 64 | extern "C" unsigned char payload_keystoredump[]; 65 | 66 | const std::vector patch_empty_vector = MakeEmptyPatchset(); 67 | 68 | 69 | enum NvmState 70 | { 71 | NVM_STATE_EMPTY, 72 | NVM_STATE_OTHER, 73 | NVM_STATE_IRQ_HOOK, 74 | NVM_STATE_CDPROTECT_HOOK, 75 | }; 76 | 77 | alignas(64) char g_pad0Buffer[256]; 78 | 79 | alignas(16) uint8_t g_eeprom[0x400]; 80 | 81 | int g_mechaconMajor = -1; 82 | int g_mechaconMinor = -1; 83 | bool g_systemIsDEX = false; 84 | std::string g_refreshDate; 85 | std::string g_modelString; 86 | 87 | 88 | class SimpleDebugOutput : public DebugOutput 89 | { 90 | public: 91 | virtual void VPrintf(const char* format, va_list args) 92 | { 93 | std::vprintf(format, args); 94 | std::fflush(stdout); 95 | } 96 | }; 97 | 98 | 99 | // HACK: Hide the silly cursor the debug drawing does. 100 | extern "C" uint8_t msx[]; 101 | void HackToHideCursor() 102 | { 103 | std::memset(&msx[219 * 8], 0, 8); 104 | } 105 | 106 | 107 | void ResetEverything(bool isPS2Link) 108 | { 109 | if (!isPS2Link) 110 | { 111 | // Initialize connection to IOP. 112 | SifInitRpc(0); 113 | 114 | // Reset the IOP with the default ROM modules. 115 | while (!SifIopReset("", 0)) 116 | ; 117 | while (!SifIopSync()) 118 | ; 119 | SifInitRpc(0); 120 | 121 | // Patch the kernel as needed. 122 | sbv_patch_enable_lmb(); 123 | sbv_patch_disable_prefix_check(); 124 | sbv_patch_fileio(); 125 | 126 | // Prepare to load modules. 127 | SifLoadFileInit(); 128 | } 129 | 130 | struct IopModule 131 | { 132 | bool m_loadWhenPS2Link; 133 | const void* m_nameOrPointer; 134 | const unsigned int* m_size; 135 | }; 136 | static const IopModule s_iopModules[] = 137 | { 138 | { false, "rom0:CDVDMAN", nullptr }, 139 | { false, irx_iomanX, &size_irx_iomanX }, 140 | { false, irx_fileXio, &size_irx_fileXio }, 141 | { true, irx_sio2man, &size_irx_sio2man }, 142 | { true, irx_mtapman, &size_irx_mtapman }, 143 | { true, irx_padman, &size_irx_padman }, 144 | { true, irx_mcman, &size_irx_mcman }, 145 | { true, irx_mcserv, &size_irx_mcserv }, 146 | { true, irx_usbd, &size_irx_usbd }, 147 | { true, irx_usbhdfsd, &size_irx_usbhdfsd }, 148 | }; 149 | 150 | for (const IopModule& module : s_iopModules) 151 | { 152 | if (isPS2Link && !module.m_loadWhenPS2Link) 153 | continue; 154 | if (module.m_size) 155 | { 156 | SifExecModuleBuffer(const_cast(module.m_nameOrPointer), *module.m_size, 0, nullptr, 157 | nullptr); 158 | } 159 | else 160 | { 161 | SifLoadModule(static_cast(module.m_nameOrPointer), 0, nullptr); 162 | } 163 | } 164 | } 165 | 166 | 167 | bool GetPressedButtons(int& buttons) 168 | { 169 | int state = padGetState(0, 0); 170 | if (state == PAD_STATE_DISCONN || state == PAD_STATE_EXECCMD || state == PAD_STATE_ERROR) 171 | return false; 172 | 173 | struct padButtonStatus status; 174 | if (!padRead(0, 0, &status)) 175 | return false; 176 | 177 | buttons = status.btns ^ 0xFFFF; 178 | return true; 179 | } 180 | 181 | 182 | // Wait for either X or O. 183 | void WaitForXorOConfirm() 184 | { 185 | int buttons; 186 | for (;;) 187 | { 188 | if (!GetPressedButtons(buttons)) 189 | continue; 190 | if (!(buttons & (PAD_CIRCLE | PAD_CROSS))) 191 | break; 192 | } 193 | 194 | for (;;) 195 | { 196 | if (!GetPressedButtons(buttons)) 197 | continue; 198 | if (buttons & (PAD_CIRCLE | PAD_CROSS)) 199 | break; 200 | } 201 | } 202 | 203 | 204 | void WarningScreen() 205 | { 206 | scr_setbgcolor(0xFF000080); 207 | scr_clear(); 208 | 209 | scr_setXY(0, 5); 210 | scr_printf(" MechaDump v%d.%02d\n", c_versionMajor, c_versionMinor); 211 | scr_printf(" Dumps Dragon-series Mechacon chips.\n"); 212 | scr_printf(" \n"); 213 | scr_printf(" *** WARNING ***\n"); 214 | scr_printf(" THIS PROGRAM IS DANGEROUS, AND HAS A GOOD CHANCE OF\n"); 215 | scr_printf(" BRICKING YOUR PS2, REQUIRING SOLDERING TO RECOVER.\n"); 216 | scr_printf(" \n"); 217 | scr_printf(" USE AT YOUR OWN RISK.\n"); 218 | 219 | sleep(10); 220 | 221 | scr_printf(" \n"); 222 | scr_printf(" Press X or O to continue.\n"); 223 | 224 | WaitForXorOConfirm(); 225 | } 226 | 227 | 228 | std::string RegionFlagsToString(uint32_t regionCode) 229 | { 230 | static const struct 231 | { 232 | int m_bit; 233 | const char* m_string; 234 | } s_bits[] = { 235 | { 20, "Internal" }, 236 | { 19, "Prototype" }, 237 | { 18, "Arcade" }, 238 | { 17, "QA" }, 239 | { 16, "DEX" }, 240 | { 7, "LatinAmerica" }, 241 | { 6, "China" }, 242 | { 5, "Russia" }, 243 | { 4, "Asia" }, 244 | { 3, "AustraliaNZ" }, 245 | { 2, "Europe" }, 246 | { 1, "NorthAmerica" }, 247 | { 0, "Japan" }, 248 | }; 249 | 250 | std::string response; 251 | for (auto&& entry : s_bits) 252 | { 253 | if (regionCode & (uint32_t(1) << entry.m_bit)) 254 | { 255 | if (!response.empty()) 256 | response.append(" "); 257 | response.append(entry.m_string); 258 | } 259 | } 260 | return response; 261 | } 262 | 263 | 264 | NvmState GetNVMState() 265 | { 266 | if (std::memcmp(&g_eeprom[0x320], patch_irq_hook, 0xE0) == 0) 267 | return NVM_STATE_IRQ_HOOK; 268 | else if (std::memcmp(&g_eeprom[0x320], patch_cdprotect_hook, 0xE0) == 0) 269 | return NVM_STATE_CDPROTECT_HOOK; 270 | else if (std::memcmp(&g_eeprom[0x320], patch_empty_vector.data(), 0xE0) == 0) 271 | return NVM_STATE_EMPTY; 272 | else 273 | return NVM_STATE_OTHER; 274 | } 275 | 276 | 277 | bool CheckAndWaitForBackdoor() 278 | { 279 | if (!IsBackDoorFunctioning()) 280 | { 281 | NvmState nvmState = GetNVMState(); 282 | if (nvmState == NVM_STATE_CDPROTECT_HOOK) 283 | { 284 | scr_setbgcolor(0xFF800000); 285 | scr_clear(); 286 | scr_setXY(0, 5); 287 | scr_printf(" Back door not yet active. Insert a legitimate disk\n"); 288 | scr_printf(" for this PS2's region to activate it.\n"); 289 | scr_printf(" Hold Triangle to cancel.\n"); 290 | 291 | for (;;) 292 | { 293 | int buttons = 0; 294 | if (GetPressedButtons(buttons) && (buttons & PAD_TRIANGLE)) 295 | return false; 296 | 297 | if (IsBackDoorFunctioning()) 298 | break; 299 | 300 | sleep(1); 301 | } 302 | 303 | sceCdStop(); 304 | sceCdSync(0); 305 | } 306 | else if (nvmState == NVM_STATE_IRQ_HOOK) 307 | { 308 | scr_setbgcolor(0xFF000080); 309 | scr_clear(); 310 | scr_setXY(0, 5); 311 | scr_printf(" Back door not working for some reason!\n"); 312 | scr_printf(" Press X or O to return to the main menu.\n"); 313 | WaitForXorOConfirm(); 314 | return false; 315 | } 316 | } 317 | return true; 318 | } 319 | 320 | 321 | bool SysinfoScreen() 322 | { 323 | scr_setbgcolor(0xFF800000); 324 | scr_clear(); 325 | scr_setXY(0, 5); 326 | 327 | scr_printf(" Retrieving system information...\n"); 328 | 329 | // Read EEPROM. 330 | if (!ReadNVM(g_eeprom, 0, sizeof(g_eeprom))) 331 | { 332 | scr_setbgcolor(0xFF0000FF); 333 | scr_printf(" Could not dump EEPROM. Cannot proceed.\n"); 334 | return false; 335 | } 336 | 337 | // Model string...simple stuff 338 | g_modelString = GetModelString(); 339 | scr_printf(" Model: %s\n", g_modelString.c_str()); 340 | 341 | // Mechacon version stuff. 342 | if (!GetMechaconVersion(g_mechaconMajor, g_mechaconMinor, g_refreshDate)) 343 | { 344 | scr_printf(" Could not retrieve Mechacon version!\n"); 345 | return false; 346 | } 347 | 348 | g_systemIsDEX = static_cast(g_mechaconMinor & 1); 349 | g_mechaconMinor &= ~1; 350 | 351 | if (g_mechaconMajor < 5) 352 | { 353 | scr_setbgcolor(0xFF000080); 354 | scr_printf(" This PS2 is incompatible with this program.\n"); 355 | scr_printf(" This program is only for consoles with 'Dragon' Mechacons:\n"); 356 | scr_printf(" the 50000, 70000, 75000, 77000, 90000, PSX and Bravia series.\n"); 357 | return false; 358 | } 359 | 360 | scr_printf(" Mechacon version: %d.%02d %s\n", g_mechaconMajor, g_mechaconMinor, 361 | g_systemIsDEX ? "DEX" : "CEX"); 362 | scr_printf(" Mechacon build date: %s\n", g_refreshDate.c_str()); 363 | 364 | // Decode the region. 365 | uint32_t regionFlags; 366 | if (DecodeRegionFlags(g_eeprom, regionFlags)) 367 | scr_printf(" Region code: %08" PRIX32 " %s\n", regionFlags, RegionFlagsToString( 368 | regionFlags).c_str()); 369 | else 370 | scr_printf(" Region code: UNKNOWN\n"); 371 | 372 | // Compute patch data CRC. 373 | uint32_t patchCRC = crc32(&g_eeprom[0x320], 0xE0, 0); 374 | scr_printf(" Patchset CRC32: %08" PRIX32 "\n", patchCRC); 375 | 376 | scr_printf(" \n"); 377 | scr_printf(" This system is compatible.\n"); 378 | scr_printf(" Press X or O to continue.\n"); 379 | 380 | WaitForXorOConfirm(); 381 | 382 | return true; 383 | } 384 | 385 | 386 | std::string MakeDumpFilename() 387 | { 388 | std::string result; 389 | result.reserve(64); 390 | result.append("mechacon-"); 391 | 392 | char versionPart[20]; 393 | std::snprintf(versionPart, sizeof(versionPart), "%d.%02d", g_mechaconMajor, g_mechaconMinor); 394 | result.append(versionPart); 395 | 396 | result.append("-"); 397 | for (char ch : g_refreshDate) 398 | { 399 | if (ch == ' ') 400 | ch = '-'; 401 | if ((ch < '0') || (ch > '9')) 402 | ch = '_'; 403 | result.push_back(ch); 404 | } 405 | 406 | result.append(".bin"); 407 | return result; 408 | } 409 | 410 | 411 | struct MenuOption 412 | { 413 | const char* m_text; 414 | bool m_enabled; 415 | }; 416 | 417 | 418 | void DrawMenuOption(int x, int y, const MenuOption& option, bool highlighted) 419 | { 420 | if (!option.m_enabled) 421 | scr_setbgcolor(0xFF404040); 422 | else if (highlighted) 423 | scr_setbgcolor(0xFF000080); 424 | else 425 | scr_setbgcolor(0xFF800000); 426 | 427 | const char* prefix = " "; 428 | if (highlighted) 429 | prefix = "-> "; 430 | 431 | scr_setXY(x, y); 432 | scr_printf("%s%s", prefix, option.m_text); 433 | } 434 | 435 | 436 | size_t ShowMenu(int x, int y, const std::vector& options) 437 | { 438 | // Find first enabled option. 439 | size_t currentOption = options.size(); 440 | for (size_t i = 0; i < options.size(); ++i) 441 | { 442 | if (options[i].m_enabled) 443 | { 444 | currentOption = i; 445 | break; 446 | } 447 | } 448 | assert(currentOption < options.size()); 449 | 450 | // Any buttons pressed at menu start are ignored until released. 451 | int ignoreButtons = 0; 452 | GetPressedButtons(ignoreButtons); 453 | 454 | // Draw initial menu. 455 | for (size_t i = 0; i < options.size(); ++i) 456 | { 457 | DrawMenuOption(x, y + static_cast(i), options[i], currentOption == i); 458 | } 459 | 460 | // Main loop. 461 | for (;;) 462 | { 463 | // Wait for button presses. 464 | int buttons = 0; 465 | for (;;) 466 | { 467 | if (GetPressedButtons(buttons)) 468 | { 469 | // Un-ignore any buttons that are now released. 470 | ignoreButtons &= buttons; 471 | 472 | // Ignore any buttons that were held at menu start. 473 | buttons &= ~ignoreButtons; 474 | 475 | break; 476 | } 477 | } 478 | 479 | bool changed = false; 480 | 481 | // Ignore up+down together. 482 | if ((buttons & (PAD_UP | PAD_DOWN)) == (PAD_UP | PAD_DOWN)) 483 | buttons &= ~(PAD_UP | PAD_DOWN); 484 | 485 | if (buttons & PAD_UP) 486 | { 487 | for (;;) 488 | { 489 | if (currentOption > 0) 490 | --currentOption; 491 | else 492 | currentOption = options.size() - 1; 493 | if (options[currentOption].m_enabled) 494 | break; 495 | } 496 | ignoreButtons |= PAD_UP; 497 | changed = true; 498 | } 499 | else if (buttons & PAD_DOWN) 500 | { 501 | for (;;) 502 | { 503 | ++currentOption; 504 | if (currentOption >= options.size()) 505 | currentOption = 0; 506 | if (options[currentOption].m_enabled) 507 | break; 508 | } 509 | ignoreButtons |= PAD_DOWN; 510 | changed = true; 511 | } 512 | 513 | // Avoids flicker from constant non-vsync redraw. 514 | if (!changed && !(buttons & (PAD_CIRCLE | PAD_CROSS))) 515 | continue; 516 | 517 | // Update menu. 518 | for (size_t i = 0; i < options.size(); ++i) 519 | { 520 | DrawMenuOption(x, y + static_cast(i), options[i], currentOption == i); 521 | } 522 | 523 | // If selected, return. 524 | if (buttons & (PAD_CIRCLE | PAD_CROSS)) 525 | { 526 | scr_setbgcolor(0xFF800000); 527 | return currentOption; 528 | } 529 | } 530 | } 531 | 532 | 533 | bool Stage_BackupNVM() 534 | { 535 | scr_setbgcolor(0xFF800000); 536 | scr_clear(); 537 | 538 | scr_setXY(10, 5); 539 | scr_printf("EEPROM Backup"); 540 | scr_setXY(10, 7); 541 | scr_printf("Are you sure you want to back up your EEPROM to USB? This will"); 542 | scr_setXY(10, 8); 543 | scr_printf("overwrite any previous backup on your USB stick."); 544 | 545 | std::vector menuOptions; 546 | menuOptions.emplace_back(MenuOption{ "NO", true }); 547 | menuOptions.emplace_back(MenuOption{ "YES", true }); 548 | 549 | if (ShowMenu(10, 11, menuOptions) != 1) 550 | return false; 551 | 552 | scr_setbgcolor(0xFF800000); 553 | scr_clear(); 554 | scr_setXY(10, 5); 555 | scr_printf("Backing up EEPROM to USB stick...\n"); 556 | 557 | bool success = false; 558 | std::FILE* file = std::fopen("mass:/mechadump_eeprom_backup.bin", "wb"); 559 | if (file) 560 | { 561 | success = std::fwrite(g_eeprom, sizeof(g_eeprom), 1, file) == 1; 562 | std::fclose(file); 563 | } 564 | 565 | if (!success) 566 | { 567 | scr_setXY(10, 7); 568 | scr_setbgcolor(0xFF000080); 569 | scr_printf("BACKUP FAILED "); 570 | scr_setbgcolor(0xFF800000); 571 | scr_printf("- no USB stick inserted?\n"); 572 | scr_setXY(10, 8); 573 | scr_printf("Press X or O to continue.\n"); 574 | WaitForXorOConfirm(); 575 | return false; 576 | } 577 | 578 | scr_setXY(10, 7); 579 | scr_printf("Backup successful.\n"); 580 | scr_setXY(10, 8); 581 | scr_printf("Press X or O to continue.\n"); 582 | WaitForXorOConfirm(); 583 | return true; 584 | } 585 | 586 | 587 | // Does not return if successful. 588 | bool Stage_RestoreNVM() 589 | { 590 | SimpleDebugOutput debug; 591 | 592 | if (!CheckAndWaitForBackdoor()) 593 | return false; 594 | 595 | scr_setbgcolor(0xFF800000); 596 | scr_clear(); 597 | 598 | scr_setXY(10, 5); 599 | scr_printf("EEPROM Restore"); 600 | scr_setXY(10, 7); 601 | scr_printf("Are you sure you want to restore your EEPROM from USB?"); 602 | 603 | std::vector menuOptions; 604 | menuOptions.emplace_back(MenuOption{ "NO", true }); 605 | menuOptions.emplace_back(MenuOption{ "YES", true }); 606 | 607 | if (ShowMenu(10, 11, menuOptions) != 1) 608 | return false; 609 | 610 | scr_setXY(10, 5); 611 | scr_printf("Restoring EEPROM patch data from USB stick...\n"); 612 | 613 | uint8_t eepromConfigToRestore[0x200]; 614 | bool success = false; 615 | std::FILE* file = std::fopen("mass:/mechadump_eeprom_backup.bin", "rb"); 616 | if (file) 617 | { 618 | std::fseek(file, 0x200, SEEK_SET); 619 | success = std::fread(eepromConfigToRestore, 0x200, 1, file) == 1; 620 | std::fclose(file); 621 | } 622 | 623 | scr_setbgcolor(0xFF800000); 624 | scr_clear(); 625 | scr_setXY(10, 5); 626 | scr_printf("Restoring EEPROM from backup...\n"); 627 | 628 | if (!success) 629 | { 630 | scr_setXY(10, 7); 631 | scr_setbgcolor(0xFF000080); 632 | scr_printf("RESTORE FAILED "); 633 | scr_setbgcolor(0xFF800000); 634 | scr_printf("- no USB stick inserted?\n"); 635 | scr_setXY(10, 8); 636 | scr_printf("Press X or O to continue.\n"); 637 | WaitForXorOConfirm(); 638 | return false; 639 | } 640 | 641 | if (!RestoreNVMConfigAndPatchData(payload_writenvm, size_payload_writenvm, eepromConfigToRestore, 642 | debug)) 643 | { 644 | scr_setXY(10, 7); 645 | scr_setbgcolor(0xFF000080); 646 | scr_printf("RESTORE FAILED "); 647 | scr_setbgcolor(0xFF800000); 648 | scr_printf("THIS IS REALLY BAD.\n"); 649 | scr_setXY(10, 8); 650 | scr_printf("Press X or O to continue.\n"); 651 | WaitForXorOConfirm(); 652 | return false; 653 | } 654 | 655 | scr_setXY(10, 7); 656 | scr_printf("Restore successful.\n"); 657 | scr_setXY(10, 8); 658 | scr_printf("Press X or O to power off system.\n"); 659 | WaitForXorOConfirm(); 660 | 661 | ResetMechaconAndPowerOff(); 662 | scr_setbgcolor(0xFF800000); 663 | scr_clear(); 664 | scr_setXY(10, 7); 665 | scr_setbgcolor(0xFF000080); 666 | scr_printf("Reboot failed "); 667 | scr_setbgcolor(0xFF800000); 668 | scr_printf("Unplug your system, wait for the red light to turn off, then plug in.\n"); 669 | for (;;) 670 | sleep(1); 671 | } 672 | 673 | 674 | // Does not return if successful. Returns on failure. 675 | void Stage_InstallHackChosen(NvmState which) 676 | { 677 | const unsigned char* patch; 678 | switch (which) 679 | { 680 | case NVM_STATE_IRQ_HOOK: 681 | patch = patch_irq_hook; 682 | break; 683 | case NVM_STATE_CDPROTECT_HOOK: 684 | patch = patch_cdprotect_hook; 685 | break; 686 | default: 687 | assert(false); 688 | ExecOSD(0, nullptr); 689 | for (;;) 690 | sleep(1); 691 | } 692 | 693 | scr_setbgcolor(0xFF800000); 694 | scr_clear(); 695 | 696 | scr_setXY(10, 5); 697 | scr_printf("Installing back door...\n"); 698 | 699 | int errorCode = -1; 700 | bool result = WriteConfigExploit(patch, errorCode); 701 | 702 | if (result) 703 | { 704 | scr_setXY(10, 8); 705 | scr_printf("SUCCESS! Back door installed.\n"); 706 | scr_setXY(10, 10); 707 | scr_printf("Your system is now frozen.\n"); 708 | scr_setXY(10, 11); 709 | if (g_mechaconMajor < 6) 710 | scr_printf("Please UNPLUG or TURN POWER SUPPLY SWITCH OFF,\n"); 711 | else 712 | scr_printf("Please UNPLUG your PS2, then\n"); 713 | scr_setXY(10, 12); 714 | scr_printf("wait for red light to go out, then power on\n"); 715 | scr_setXY(10, 13); 716 | scr_printf("and run this program again.\n"); 717 | for (;;) 718 | sleep(1); 719 | } 720 | 721 | scr_setbgcolor(0xFF000080); 722 | scr_setXY(10, 8); 723 | scr_printf("FAILED TO INSTALL! "); 724 | scr_setXY(10, 9); 725 | scr_printf("Error code: %d ", errorCode); 726 | scr_setXY(10, 11); 727 | scr_printf("Press X or O to return to the menu."); 728 | WaitForXorOConfirm(); 729 | } 730 | 731 | 732 | // Does not return if successful. Returns on failure. 733 | void Stage_InstallHack() 734 | { 735 | scr_setbgcolor(0xFF800000); 736 | scr_clear(); 737 | 738 | scr_setXY(10, 5); 739 | scr_printf("Install Backdoor"); 740 | scr_setXY(10, 7); 741 | scr_printf("Which backdoor payload do you want to use?"); 742 | scr_setXY(10, 8); 743 | 744 | std::vector menuOptions; 745 | menuOptions.emplace_back(MenuOption{ "Cancel", true }); 746 | menuOptions.emplace_back(MenuOption{ "Disc Protect Hook (safer, but requires a legitimate game disc)", 747 | true }); 748 | menuOptions.emplace_back(MenuOption{ "IRQ Hook (riskier, but doesn't need drive at all)", true }); 749 | 750 | size_t choice = ShowMenu(10, 11, menuOptions); 751 | NvmState whichHack = NVM_STATE_EMPTY; 752 | switch (choice) 753 | { 754 | case 0: 755 | return; 756 | case 1: 757 | whichHack = NVM_STATE_CDPROTECT_HOOK; 758 | break; 759 | case 2: 760 | whichHack = NVM_STATE_IRQ_HOOK; 761 | break; 762 | default: 763 | assert(false); 764 | return; 765 | } 766 | 767 | scr_setbgcolor(0xFF800000); 768 | scr_clear(); 769 | 770 | scr_setXY(10, 5); 771 | scr_printf("Install Backdoor"); 772 | scr_setXY(10, 7); 773 | scr_printf("Are you sure you want to install the backdoor?"); 774 | scr_setXY(10, 8); 775 | scr_setbgcolor(0xFF000080); 776 | scr_printf("IT MIGHT BRICK YOUR SYSTEM!"); 777 | scr_setbgcolor(0xFF800000); 778 | scr_printf(" (Recoverable with soldering.)\n"); 779 | 780 | menuOptions.clear(); 781 | menuOptions.emplace_back(MenuOption{ "NO", true }); 782 | menuOptions.emplace_back(MenuOption{ "YES", true }); 783 | 784 | if (ShowMenu(10, 11, menuOptions) != 1) 785 | return; 786 | 787 | Stage_InstallHackChosen(whichHack); 788 | } 789 | 790 | 791 | bool Stage_DumpMechaconROM() 792 | { 793 | SimpleDebugOutput debug; 794 | 795 | scr_setbgcolor(0xFF800000); 796 | scr_clear(); 797 | 798 | scr_setXY(10, 5); 799 | scr_printf("Dump Mechacon ROM"); 800 | scr_setXY(10, 7); 801 | scr_printf("Are you sure you want to dump your Mechacon ROM to USB? This will"); 802 | scr_setXY(10, 8); 803 | scr_printf("overwrite any previous Mechacon ROM dump on your USB stick."); 804 | 805 | std::vector menuOptions; 806 | menuOptions.emplace_back(MenuOption{ "NO", true }); 807 | menuOptions.emplace_back(MenuOption{ "YES", true }); 808 | 809 | if (ShowMenu(10, 11, menuOptions) != 1) 810 | return false; 811 | 812 | if (!IsBackDoorFunctioning()) 813 | { 814 | if (!CheckAndWaitForBackdoor()) 815 | return false; 816 | } 817 | 818 | scr_setbgcolor(0xFF800000); 819 | scr_clear(); 820 | scr_setXY(0, 5); 821 | scr_printf(" Dumping Mechacon... (this can take 15 seconds to start)\n"); 822 | scr_printf("\n"); 823 | scr_printf(" | |\n"); 824 | scr_printf("\n"); 825 | scr_printf(" Hold triangle to cancel.\n"); 826 | 827 | // Dump keystore. This will take 15 seconds if we don't know where the payload gets 828 | // uploaded to. However, once done, we'll remember, so the firmware dump won't wait. 829 | std::vector keystore = DumpMechaconKeystoreWithPayload(payload_keystoredump, 830 | size_payload_keystoredump, debug); 831 | bool cancelled = false; 832 | { 833 | int buttons = 0; 834 | if (GetPressedButtons(buttons) && (buttons & PAD_TRIANGLE)) 835 | { 836 | cancelled = true; 837 | return false; 838 | } 839 | } 840 | 841 | // Dump main firmware. 842 | std::vector rom = DumpMechaconROMFastWithPayload(payload_fastdump, size_payload_fastdump, 843 | [&](size_t current, size_t total) -> bool 844 | { 845 | int buttons = 0; 846 | if (GetPressedButtons(buttons) && (buttons & PAD_TRIANGLE)) 847 | { 848 | cancelled = true; 849 | return false; 850 | } 851 | 852 | float percentage = 100.0f * static_cast(current) / static_cast(total); 853 | int chars = static_cast(percentage) / 2; 854 | if (chars < 0) 855 | chars = 0; 856 | else if (chars > 50) 857 | chars = 50; 858 | // scr_print will print an extra space, so subtract 1. 859 | if (chars >= 1) 860 | { 861 | char spaces[51]; 862 | std::memset(spaces, ' ', 50); 863 | spaces[chars - 1] = '\0'; 864 | 865 | scr_setXY(10 + 1, 7); 866 | scr_setbgcolor(0xFF000080); 867 | scr_printf("%s", spaces); 868 | scr_setbgcolor(0xFF800000); 869 | } 870 | return true; 871 | }, debug); 872 | 873 | if (cancelled) 874 | return false; 875 | 876 | scr_setXY(0, 9); 877 | scr_printf(" Saving... \n"); 878 | 879 | bool success = false; 880 | std::string dumpPath; 881 | if (!keystore.empty() && !rom.empty()) 882 | { 883 | bool successKeystore = false; 884 | std::FILE* keystoreFile = std::fopen("mass:/mechacon-keystore.bin", "wb"); 885 | if (keystoreFile) 886 | { 887 | if (std::fwrite(keystore.data(), 1, keystore.size(), keystoreFile) == keystore.size()) 888 | successKeystore = true; 889 | std::fclose(keystoreFile); 890 | } 891 | 892 | bool successROM = false; 893 | dumpPath = std::string("mass:/") + MakeDumpFilename(); 894 | std::FILE* romFile = std::fopen(dumpPath.c_str(), "wb"); 895 | if (romFile) 896 | { 897 | if (std::fwrite(rom.data(), 1, rom.size(), romFile) == rom.size()) 898 | successROM = true; 899 | std::fclose(romFile); 900 | } 901 | 902 | success = successKeystore && successROM; 903 | } 904 | 905 | if (success) 906 | scr_printf(" Success! File dumped as %s\n", dumpPath.c_str()); 907 | else 908 | scr_printf(" FAILED! Could not write file to USB\n"); 909 | 910 | // Is this a known version? 911 | sha256::digest digest; 912 | sha256 sha256; 913 | sha256.reset(); 914 | sha256.process(rom.data(), rom.size()); 915 | sha256.finish(digest); 916 | debug.Printf("Firmware digest: "); 917 | for (size_t i = 0; i < sizeof(digest); ++i) 918 | { 919 | debug.Printf("%02x", digest[i]); 920 | } 921 | debug.Printf("\n"); 922 | 923 | if (!IsKnownDump(digest)) 924 | { 925 | scr_printf(" "); 926 | scr_setbgcolor(0xFF000080); 927 | scr_printf("Unknown Version!!"); 928 | scr_setbgcolor(0xFF800000); 929 | scr_printf(" Please send it.\n"); 930 | scr_printf("\n"); 931 | } 932 | 933 | scr_printf(" Press X or O to continue.\n"); 934 | WaitForXorOConfirm(); 935 | return success; 936 | } 937 | 938 | 939 | [[noreturn]] void MainMenu() 940 | { 941 | for (;;) 942 | { 943 | NvmState nvmState = GetNVMState(); 944 | 945 | bool allowNVMBackup = false; 946 | bool allowNVMRestore = false; 947 | bool allowInstallHack = false; 948 | bool allowDumpMechacon = false; 949 | 950 | bool backdoorInstalled = false; 951 | bool backdoorFunctioning = IsBackDoorFunctioning(); 952 | 953 | const char* nvmStateString; 954 | switch (nvmState) 955 | { 956 | case NVM_STATE_CDPROTECT_HOOK: 957 | nvmStateString = "Backdoored (Disc Protect)"; 958 | allowNVMBackup = false; 959 | backdoorInstalled = true; 960 | break; 961 | case NVM_STATE_IRQ_HOOK: 962 | nvmStateString = "Backdoored (IRQ Handler)"; 963 | allowNVMBackup = false; 964 | backdoorInstalled = true; 965 | break; 966 | case NVM_STATE_EMPTY: 967 | nvmStateString = "Empty"; 968 | allowNVMBackup = true; 969 | backdoorInstalled = false; 970 | break; 971 | case NVM_STATE_OTHER: 972 | default: 973 | nvmStateString = "Default"; 974 | allowNVMBackup = true; 975 | backdoorInstalled = false; 976 | break; 977 | } 978 | 979 | allowInstallHack = true; 980 | allowNVMRestore = backdoorFunctioning || backdoorInstalled; 981 | allowDumpMechacon = backdoorFunctioning || backdoorInstalled; 982 | 983 | std::FILE* backup = std::fopen("mass:/mechadump_eeprom_backup.bin", "rb"); 984 | if (backup) 985 | std::fclose(backup); 986 | else 987 | allowNVMRestore = false; 988 | 989 | scr_setbgcolor(0xFF800000); 990 | scr_clear(); 991 | 992 | scr_setXY(10, 5); 993 | scr_printf("Patch state: %s", nvmStateString); 994 | scr_setXY(39, 10); 995 | scr_setbgcolor(0xFF800080); 996 | scr_printf(" MAIN MENU"); 997 | 998 | std::vector menuOptions; 999 | menuOptions.emplace_back(MenuOption{ "Back Up EEPROM", allowNVMBackup }); 1000 | menuOptions.emplace_back(MenuOption{ "Restore EEPROM", allowNVMRestore }); 1001 | menuOptions.emplace_back(MenuOption{ "Install Backdoor", allowInstallHack }); 1002 | menuOptions.emplace_back(MenuOption{ "Dump Mechacon ROM", allowDumpMechacon }); 1003 | menuOptions.emplace_back(MenuOption{ "Exit", true }); 1004 | 1005 | size_t choice = ShowMenu(10, 13, menuOptions); 1006 | 1007 | switch (choice) 1008 | { 1009 | case 0: 1010 | if (allowNVMBackup) 1011 | Stage_BackupNVM(); 1012 | break; 1013 | case 1: 1014 | if (allowNVMRestore) 1015 | Stage_RestoreNVM(); 1016 | break; 1017 | case 2: 1018 | if (allowInstallHack) 1019 | Stage_InstallHack(); 1020 | break; 1021 | case 3: 1022 | if (allowDumpMechacon) 1023 | Stage_DumpMechaconROM(); 1024 | break; 1025 | case 4: 1026 | ExecOSD(0, nullptr); 1027 | for (;;) 1028 | sleep(1); 1029 | default: 1030 | break; 1031 | } 1032 | } 1033 | } 1034 | 1035 | 1036 | int main(int argc, char** argv) 1037 | { 1038 | assert(size_patch_irq_hook == 0xE0); 1039 | assert(size_patch_cdprotect_hook == 0xE0); 1040 | 1041 | init_scr(); 1042 | HackToHideCursor(); 1043 | scr_setXY(0, 5); 1044 | scr_printf(" Loading...\n"); 1045 | 1046 | ResetEverything(!argv[0]); 1047 | scr_printf(" Past ResetEverything\n"); 1048 | 1049 | mcInit(MC_TYPE_XMC); 1050 | scr_printf(" Past mcInit\n"); 1051 | mtapInit(); 1052 | scr_printf(" Past mtapInit\n"); 1053 | padInit(0); 1054 | scr_printf(" Past padInit\n"); 1055 | mtapPortOpen(0); 1056 | scr_printf(" Past mtapPortOpen\n"); 1057 | padPortOpen(0, 0, g_pad0Buffer); 1058 | scr_printf(" Past padPortOpen\n"); 1059 | sceCdInit(SCECdINoD); 1060 | scr_printf(" Past sceCdInit\n"); 1061 | 1062 | WarningScreen(); 1063 | 1064 | if (!SysinfoScreen()) 1065 | { 1066 | SleepThread(); 1067 | return 0; 1068 | } 1069 | 1070 | MainMenu(); 1071 | } 1072 | --------------------------------------------------------------------------------