From c25148a416f89e430ff490846d92657acd266006 Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Fri, 22 Mar 2024 23:43:32 -0500 Subject: [PATCH 1/6] Korean key detection --- source/mini_seeprom.c | 267 ++++++++++++++++++++++++++++++++++++++++++ source/mini_seeprom.h | 59 ++++++++++ source/otp.c | 70 +++++++++++ source/otp.h | 40 +++++++ source/sys.c | 109 +++++++++++++++++ source/sys.h | 32 +++++ source/wad.c | 152 ++++++++++-------------- 7 files changed, 640 insertions(+), 89 deletions(-) create mode 100644 source/mini_seeprom.c create mode 100644 source/mini_seeprom.h create mode 100644 source/otp.c create mode 100644 source/otp.h diff --git a/source/mini_seeprom.c b/source/mini_seeprom.c new file mode 100644 index 0000000..fa49bef --- /dev/null +++ b/source/mini_seeprom.c @@ -0,0 +1,267 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + SEEPROM support + +Copyright (C) 2008, 2009 Sven Peter +Copyright (C) 2008, 2009 Haxx Enterprises +Copyright (C) 2008, 2009 Hector Martin "marcan" +Copyright (C) 2008, 2009 John Kelley +Copyright (C) 2020 Pablo Curiel "DarkMatterCore" + +# This code is licensed to you under the terms of the GNU GPL, version 2; +# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +*/ + +#include +#include +#include + +#include "mini_seeprom.h" + +#define HW_REG_BASE 0xd800000 +#define HW_GPIO1OUT (HW_REG_BASE + 0x0e0) +#define HW_GPIO1IN (HW_REG_BASE + 0x0e8) + +#define HW_SEEPROM_BLK_SIZE 2 +#define HW_SEEPROM_BLK_CNT (SEEPROM_SIZE / HW_SEEPROM_BLK_SIZE) + +#define eeprom_delay() usleep(5) + +enum { + GP_EEP_CS = 0x000400, + GP_EEP_CLK = 0x000800, + GP_EEP_MOSI = 0x001000, + GP_EEP_MISO = 0x002000 +}; + +static void seeprom_send_bits(u16 value, u8 bits) +{ + if (!bits || bits > 16) return; + + while(bits--) + { + if (value & (1 << bits)) + { + mask32(HW_GPIO1OUT, 0, GP_EEP_MOSI); + } else { + mask32(HW_GPIO1OUT, GP_EEP_MOSI, 0); + } + + eeprom_delay(); + + mask32(HW_GPIO1OUT, 0, GP_EEP_CLK); + eeprom_delay(); + + mask32(HW_GPIO1OUT, GP_EEP_CLK, 0); + eeprom_delay(); + } +} + +static u16 seeprom_recv_bits(u8 bits) +{ + if (!bits || bits > 16) return 0; + + int res = 0; + + while(bits--) + { + res <<= 1; + + mask32(HW_GPIO1OUT, 0, GP_EEP_CLK); + eeprom_delay(); + + mask32(HW_GPIO1OUT, GP_EEP_CLK, 0); + eeprom_delay(); + + res |= !!(read32(HW_GPIO1IN) & GP_EEP_MISO); + } + + return (u16)res; +} + +u16 seeprom_read(void *dst, u16 offset, u16 size) +{ + /* + * WiiUBrew told me that you interact with the SEEPROM the exact same way you do on Wii. + * However the contents are way different. Like there's absolutely no vWii stuff here. + */ + if (read16(0xCD8005A0) == 0xCAFE) return 0; + + if (!dst || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0; + + u16 cur_offset = 0; + + u8 *ptr = (u8*)dst; + u8 val[HW_SEEPROM_BLK_SIZE] = {0}; + + // Calculate block offsets and sizes + u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE); + u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE); + + u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE); + u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE); + + if (!end_addr_size) + { + end_addr--; + end_addr_size = HW_SEEPROM_BLK_SIZE; + } + + if (end_addr == start_addr) end_addr_size -= start_addr_offset; + + mask32(HW_GPIO1OUT, GP_EEP_CLK, 0); + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + for(u16 i = start_addr; i <= end_addr; i++) + { + if (cur_offset >= size) break; + + // Start command cycle + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + + // Send read command + address + seeprom_send_bits(0x600 | i, 11); + + // Receive data + *((u16*)val) = seeprom_recv_bits(16); + + // End of command cycle + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + // Copy read data to destination buffer + if (i == start_addr && start_addr_offset != 0) + { + // Handle unaligned read at start address + memcpy(ptr + cur_offset, val + start_addr_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset); + cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset); + } else + if (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE) + { + // Handle unaligned read at end address + memcpy(ptr + cur_offset, val, end_addr_size); + cur_offset += end_addr_size; + } else { + // Normal read + memcpy(ptr + cur_offset, val, HW_SEEPROM_BLK_SIZE); + cur_offset += HW_SEEPROM_BLK_SIZE; + } + } + + return cur_offset; +} + +#if 0 +u16 seeprom_write(const void *src, u16 offset, u16 size) +{ + if (!src || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0; + + u32 level = 0; + u16 cur_offset = 0; + + const u8 *ptr = (const u8*)src; + u8 val[HW_SEEPROM_BLK_SIZE] = {0}; + + // Calculate block offsets and sizes + u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE); + u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE); + + u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE); + u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE); + + if (!end_addr_size) + { + end_addr--; + end_addr_size = HW_SEEPROM_BLK_SIZE; + } + + if (end_addr == start_addr) end_addr_size -= start_addr_offset; + + // Disable CPU interruptions + _CPU_ISR_Disable(level); + + mask32(HW_GPIO1OUT, GP_EEP_CLK, 0); + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + // EWEN - Enable programming commands + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + seeprom_send_bits(0x4FF, 11); + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + for(u16 i = start_addr; i <= end_addr; i++) + { + if (cur_offset >= size) break; + + // Copy data to write from source buffer + if ((i == start_addr && start_addr_offset != 0) || (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE)) + { + // Read data from SEEPROM to handle unaligned writes + + // Start command cycle + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + + // Send read command + address + seeprom_send_bits(0x600 | i, 11); + + // Receive data + *((u16*)val) = seeprom_recv_bits(16); + + // End of command cycle + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + if (i == start_addr && start_addr_offset != 0) + { + // Handle unaligned write at start address + memcpy(val + start_addr_offset, ptr + cur_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset); + cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset); + } else { + // Handle unaligned write at end address + memcpy(val, ptr + cur_offset, end_addr_size); + cur_offset += end_addr_size; + } + } else { + // Normal write + memcpy(val, ptr + cur_offset, HW_SEEPROM_BLK_SIZE); + cur_offset += HW_SEEPROM_BLK_SIZE; + } + + // Start command cycle + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + + // Send write command + address + seeprom_send_bits(0x500 | i, 11); + + // Send data + seeprom_send_bits(*((u16*)val), 16); + + // End of command cycle + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + // Wait until SEEPROM is ready (write cycle is self-timed so no clocking needed) + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + + do { + eeprom_delay(); + } while(!(read32(HW_GPIO1IN) & GP_EEP_MISO)); + + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + } + + // EWDS - Disable programming commands + mask32(HW_GPIO1OUT, 0, GP_EEP_CS); + seeprom_send_bits(0x400, 11); + mask32(HW_GPIO1OUT, GP_EEP_CS, 0); + eeprom_delay(); + + // Enable CPU interruptions + _CPU_ISR_Restore(level); + + return cur_offset; +} +#endif diff --git a/source/mini_seeprom.h b/source/mini_seeprom.h new file mode 100644 index 0000000..897710b --- /dev/null +++ b/source/mini_seeprom.h @@ -0,0 +1,59 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + SEEPROM support + +Copyright (C) 2008, 2009 Sven Peter + +# This code is licensed to you under the terms of the GNU GPL, version 2; +# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +*/ + +#ifndef __MINI_SEEPROM_H__ +#define __MINI_SEEPROM_H__ + +#define SEEPROM_SIZE 0x100 + +typedef struct +{ + union { + struct { + u8 boot2version; + u8 unknown1; + u8 unknown2; + u8 pad; + u32 update_tag; + }; + u8 data[8]; + }; + u16 checksum; // sum of data[] elements? +} __attribute__((packed)) eep_boot2_ctr_t; + +typedef struct +{ + union { + u32 nand_gen; // matches offset 0x8 in nand SFFS blocks + u8 data[4]; + }; + u16 checksum; // sum of data[] elements? +} __attribute__((packed)) eep_nand_ctr_t; + +struct SEEPROM +{ + u32 ms_id; // 0x00000002 + u32 ca_id; // 0x00000001 + u32 ng_key_id; + u8 ng_sig[60]; + eep_boot2_ctr_t boot2_counters[2]; + eep_nand_ctr_t nand_counters[3]; // current slot rotates on each write + u8 pad0[6]; + u8 korean_key[16]; + u8 pad1[116]; + u16 prng_seed[2]; // u32 with lo word stored first, incremented every time IOS starts. Used with the PRNG key to setup IOS's PRNG (syscalls 73/74 etc.) + u8 pad2[4]; +}; +_Static_assert(sizeof(struct SEEPROM) == SEEPROM_SIZE, "SEEPROM struct size incorrect!"); + +u16 seeprom_read(void *dst, u16 offset, u16 size); +// u16 seeprom_write(const void *src, u16 offset, u16 size); + +#endif /* __MINI_SEEPROM_H__ */ diff --git a/source/otp.c b/source/otp.c new file mode 100644 index 0000000..eadd9c1 --- /dev/null +++ b/source/otp.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include "otp.h" + +#define HW_OTP_COMMAND (*(vu32*)0xCD8001EC) +#define HW_OTP_DATA (*(vu32*)0xCD8001F0) + +#define HW_OTP_BLK_SIZE 4 +#define HW_OTP_BLK_CNT (OTP_BANK_SIZE / HW_OTP_BLK_SIZE) + +u8 otp_read(void *dst, OTPBank bank, u8 offset, u8 size) +{ + if (!dst || bank > 7 || offset >= OTP_BANK_SIZE || !size || (offset + size) > OTP_BANK_SIZE) return 0; + + /* This is not a Wii U. */ + if (bank != 0 && read16(0xCD8005A0) != 0xCAFE) return 0; + + u8 cur_offset = 0; + + u8 *ptr = (u8*)dst; + u8 val[HW_OTP_BLK_SIZE] = {0}; + + // Calculate block offsets and sizes + u8 start_addr = (offset / HW_OTP_BLK_SIZE); + u8 start_addr_offset = (offset % HW_OTP_BLK_SIZE); + + u8 end_addr = ((offset + size) / HW_OTP_BLK_SIZE); + u8 end_addr_size = ((offset + size) % HW_OTP_BLK_SIZE); + + if (!end_addr_size) + { + end_addr--; + end_addr_size = HW_OTP_BLK_SIZE; + } + + if (end_addr == start_addr) end_addr_size -= start_addr_offset; + + for(u8 i = start_addr; i <= end_addr; i++) + { + if (cur_offset >= size) break; + + // Send command + address + HW_OTP_COMMAND = (0x80000000 | bank << 8 | i); + + // Receive data + *((u32*)val) = HW_OTP_DATA; + + // Copy read data to destination buffer + if (i == start_addr && start_addr_offset != 0) + { + // Handle unaligned read at start address + memcpy(ptr + cur_offset, val + start_addr_offset, HW_OTP_BLK_SIZE - start_addr_offset); + cur_offset += (HW_OTP_BLK_SIZE - start_addr_offset); + } else + if (i == end_addr && end_addr_size != HW_OTP_BLK_SIZE) + { + // Handle unaligned read at end address + memcpy(ptr + cur_offset, val, end_addr_size); + cur_offset += end_addr_size; + } else { + // Normal read + memcpy(ptr + cur_offset, val, HW_OTP_BLK_SIZE); + cur_offset += HW_OTP_BLK_SIZE; + } + } + + return cur_offset; +} diff --git a/source/otp.h b/source/otp.h new file mode 100644 index 0000000..a5b35a0 --- /dev/null +++ b/source/otp.h @@ -0,0 +1,40 @@ +#ifndef __OTP_H__ +#define __OTP_H__ + +#include + +#define OTP_BANK_SIZE 0x80 + +// I want to add a structure for Bank 1 but the only thing that matters to me is the vWii common key in there. +#define VWII_COMMON_KEY_OFFSET 0x50 +typedef enum +{ + BANK_WII, + BANK_WIIU, +} OTPBank; + +struct OTP +{ + u8 boot1_hash[20]; + u8 common_key[16]; + u8 ng_id[4]; + union { // first two bytes of nand_hmac overlap last two bytes of ng_priv + struct { + u8 ng_priv[30]; + u8 _wtf1[18]; + }; + struct { + u8 _wtf2[28]; + u8 nand_hmac[20]; + }; + }; + u8 nand_key[16]; + u8 rng_key[16]; + u32 unk1; + u32 unk2; // 0x00000007 +}; +_Static_assert(sizeof(struct OTP) == OTP_BANK_SIZE, "OTP struct size incorrect!"); + +u8 otp_read(void *dst, OTPBank bank, u8 offset, u8 size); + +#endif /* __OTP_H__ */ diff --git a/source/sys.c b/source/sys.c index 8b028d4..42b40fd 100644 --- a/source/sys.c +++ b/source/sys.c @@ -1,8 +1,12 @@ #include #include #include +#include #include "sys.h" +#include "aes.h" +#include "nand.h" +#include "mini_seeprom.h" #include "malloc.h" #include "mload.h" #include "ehcmodule_elf.h" @@ -39,6 +43,25 @@ bool tmdIsStubIOS(tmd* p_tmd) && p_tmd->contents[2].type == 0x8001; } +bool ES_CheckHasKoreanKey(void) +{ + aeskey korean_key; + unsigned char iv[16] = {}; + + __attribute__ ((__aligned__(0x10))) + unsigned char data[16] = {0x56, 0x52, 0x6f, 0x63, 0xa1, 0x2c, 0xd1, 0x32, 0x07, 0x99, 0x82, 0x3b, 0x1b, 0x08, 0x17, 0xd0}; + + if (seeprom_read(korean_key, offsetof(struct SEEPROM, korean_key), sizeof(korean_key)) != sizeof(korean_key)) + return false; + + AES_Init(); + AES_Decrypt(korean_key, 0x10, iv, 0x10, data, data, sizeof(data)); + AES_Close(); + + // return (!strcmp((char*) data, "thepikachugamer")) Just remembered that this is how the Trucha bug came to be + return (!memcmp(data, "thepikachugamer", sizeof(data))); +} + bool isIOSstub(u8 ios_number) { u32 tmd_size = 0; @@ -214,6 +237,92 @@ s32 Sys_GetCerts(signed_blob **certs, u32 *len) return ret; } +s32 Sys_GetSharedContents(SharedContent** out, u32* count) +{ + if (!out || !count) return false; + + int ret = 0; + u32 size; + SharedContent* buf = NANDLoadFile("/shared1/content.map", &size); + + if (!buf) + return (s32)size; + + else if (size % sizeof(SharedContent) != 0) { + free(buf); + return -996; + } + + *out = buf; + *count = size / sizeof(SharedContent); + + return 0; +} + +bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count) +{ + if (!shared || !content || !count) + return false; + + if (!(content->type & 0x8000)) + return false; + + for (SharedContent* s_content = shared; s_content < shared + count; s_content++) + { + if (memcmp(s_content->hash, content->hash, sizeof(sha1)) == 0) + return true; + } + + return false; +} + +bool Sys_GetcIOSInfo(int IOS, cIOSInfo* out) +{ + int ret; + u64 titleID = 0x0000000100000000ULL | IOS; + ATTRIBUTE_ALIGN(0x20) char path[ISFS_MAXPATH]; + u32 size; + cIOSInfo* buf = NULL; + + u32 view_size = 0; + if (ES_GetTMDViewSize(titleID, &view_size) < 0) + return false; + + tmd_view* view = memalign32(view_size); + if (!view) + return false; + + if (ES_GetTMDView(titleID, (u8*)view, view_size) < 0) + goto fail; + + tmd_view_content* content0 = NULL; + + for (tmd_view_content* con = view->contents; con < view->contents + view->num_contents; con++) + { + if (con->index == 0) + content0 = con; + } + + if (!content0) + goto fail; + + sprintf(path, "/title/00000001/%08x/content/%08x.app", IOS, content0->cid); + buf = (cIOSInfo*)NANDLoadFile(path, &size); + + if (!buf || size != 0x40 || buf->hdr_magic != CIOS_INFO_MAGIC || buf->hdr_version != CIOS_INFO_VERSION) + goto fail; + + *out = *buf; + free(view); + free(buf); + return true; + +fail: + free(view); + free(buf); + return false; +} + void SetPRButtons(bool enabled) { gDisablePRButtons = !enabled; diff --git a/source/sys.h b/source/sys.h index 02e80f1..c7990c0 100644 --- a/source/sys.h +++ b/source/sys.h @@ -1,16 +1,48 @@ #ifndef _SYS_H_ #define _SYS_H_ +/* /shared1/content.map entry */ +typedef struct +{ + char filename[8]; + sha1 hash; +} ATTRIBUTE_PACKED SharedContent; + +/* "cIOS build tag" */ +enum +{ + CIOS_INFO_MAGIC = 0x1EE7C105, + CIOS_INFO_VERSION = 1 +}; + +typedef struct +{ + u32 hdr_magic; // 0x1EE7C105 + u32 hdr_version; // 1 + u32 cios_version; // Eg. 11 + u32 ios_base; // Eg. 60 + + char name[16]; + char cios_version_str[16]; + + char _padding[16]; +} cIOSInfo; +_Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?"); + extern u32 boot2version; /* Prototypes */ bool isIOSstub(u8 ios_number); bool tmdIsStubIOS(tmd*); bool loadIOS(int ios); +bool ES_CheckHasKoreanKey(void); void Sys_Init(void); void Sys_Reboot(void); void Sys_Shutdown(void); void Sys_LoadMenu(void); s32 Sys_GetCerts(signed_blob **, u32 *); +bool Sys_GetcIOSInfo(int IOS, cIOSInfo*); +s32 Sys_GetSharedContents(SharedContent** out, u32* count); +bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count); void SetPRButtons(bool enabled); #endif diff --git a/source/wad.c b/source/wad.c index 4a97d7b..6ea03be 100644 --- a/source/wad.c +++ b/source/wad.c @@ -9,6 +9,8 @@ #include "title.h" #include "utils.h" #include "aes.h" +#include "mini_seeprom.h" +#include "otp.h" #include "video.h" #include "wad.h" #include "wpad.h" @@ -554,52 +556,49 @@ out: return ret; } -static const aeskey - WiiCommonKey = { 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7 }, - vWiiCommonKey = { 0x30, 0xbf, 0xc7, 0x6e, 0x7c, 0x19, 0xaf, 0xbb, 0x23, 0x16, 0x33, 0x30, 0xce, 0xd7, 0xc2, 0x8d }; - -void __Wad_FixTicket(signed_blob *s_tik) +bool __Wad_FixTicket(signed_blob *s_tik) { tik* p_tik = SIGNATURE_PAYLOAD(s_tik); u8 *ckey = ((u8*)s_tik) + 0x1F1; - /* - * Alright. I'd hate to pull this off on signed tickets using the vWii common key. - * But this already does it, just without re-crypting the title key. So let's do it. - */ - bool fixKey = *ckey == 2; + bool fixvWiiKey = *ckey == 2; if (*ckey > 1) { /* Set common key */ *ckey = 0; /* Fix tickets using vWii Common Key */ - if (fixKey) + if (fixvWiiKey) { - __attribute__((aligned(0x10))) - static unsigned char keybuf[0x10], iv[0x10]; + __attribute__ ((aligned(0x10))) + aeskey tkeybuf; + aeskey commonkey; + u64 iv[2]; - u8* titlekey = p_tik->cipher_title_key; - u64* titleid = &p_tik->titleid; + memcpy(tkeybuf, p_tik->cipher_title_key, sizeof(aeskey)); + iv[0] = p_tik->titleid; + iv[1] = 0; - memcpy(keybuf, titlekey, sizeof(keybuf)); - memcpy(iv, titleid, sizeof(u64)); - memset(iv + 8, 0, sizeof(iv) - 8); + if (!otp_read(commonkey, BANK_WIIU, VWII_COMMON_KEY_OFFSET, sizeof(commonkey))) + return false; AES_Init(); - AES_Decrypt(vWiiCommonKey, 0x10, iv, 0x10, keybuf, keybuf, sizeof(keybuf)); + AES_Decrypt(commonkey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); - memcpy(iv, titleid, sizeof(u64)); - memset(iv + 8, 0, sizeof(iv) - 8); + otp_read(commonkey, BANK_WII, offsetof(struct OTP, common_key), sizeof(commonkey)); + iv[0] = p_tik->titleid; + iv[1] = 0; - AES_Encrypt(WiiCommonKey, 0x10, iv, 0x10, keybuf, keybuf, sizeof(keybuf)); + AES_Encrypt(commonkey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); - memcpy(titlekey, keybuf, sizeof(keybuf)); + memcpy(p_tik->cipher_title_key, tkeybuf, sizeof(tkeybuf)); AES_Close(); } /* Fakesign ticket */ Title_FakesignTik(s_tik); } + + return true; } bool __Wad_VerifyHeader(wadHeader* header) @@ -610,65 +609,6 @@ bool __Wad_VerifyHeader(wadHeader* header) && header->padding == 0x00; } -/* /shared1/content.map entry */ -typedef struct -{ - char filename[8]; - sha1 hash; -} ATTRIBUTE_PACKED SharedContent; - -bool GetSharedContents(SharedContent** out, u32* count) -{ - if (!out || !count) return false; - - int ret; - SharedContent* buf = NULL; - - int fd = ret = ISFS_Open("/shared1/content.map", ISFS_OPEN_READ); - if (ret < 0) - return false; - - int len = ISFS_Seek(fd, 0, SEEK_END); - ISFS_Seek(fd, 0, SEEK_SET); - if (len <= 0 || len % sizeof(SharedContent)) - goto fail; - - buf = memalign32(len); - if (!buf) - goto fail; - - ret = ISFS_Read(fd, buf, len); - if (ret != len) - goto fail; - - ISFS_Close(fd); - - *out = buf; - *count = len / sizeof(SharedContent); - - return true; - -fail: - ISFS_Close(fd); - free(buf); - return false; -} - -bool SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count) -{ - if (!shared || !content || !count) return false; - - if (!(content->type & 0x8000)) return false; - - for (SharedContent* s_content = shared; s_content < shared + count; s_content++) - { - if (memcmp(s_content->hash, content->hash, sizeof(sha1)) == 0) - return true; - } - - return false; -} - // Some of the safety checks can block region changing // Entering the Konami code turns this true, so it will // skip the problematic checks for region changing. @@ -737,6 +677,14 @@ s32 Wad_Install(FILE *fp) if (ret != 1) goto err; + if (!__Wad_FixTicket(p_tik)) + { + printf("\n This WAD is for the vWii (Wii U) only.\n\n"); + + ret = -999; + goto err; + } + offset += round_up(header->tik_len, 64); /* WAD TMD */ @@ -759,7 +707,7 @@ s32 Wad_Install(FILE *fp) goto err; } - if(get_title_ios(TITLE_ID(1, 2)) == tid) + if(tid == get_title_ios(TITLE_ID(1, 2))) { if (tmdIsStubIOS(tmd_data)) { @@ -767,6 +715,11 @@ s32 Wad_Install(FILE *fp) ret = -999; goto err; } + + else if ((tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80))) + { + /* Check build tag here */ + } } if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K'))) @@ -855,8 +808,30 @@ s32 Wad_Install(FILE *fp) ret = -999; goto err; } + + + skipChecks: - if(tmd_data->title_version < 416) + /* Put this before or after skipChecks? */ + if ((tmd_data->title_version & 0x1F) != 0x6 // 0x6 = KR + && (tmd_data->title_version >> 5) >= 15) // 4.2 or later + { + cIOSInfo ios_info; + + if (!Sys_GetcIOSInfo(TITLE_LOWER(tmd_data->sys_version), &ios_info) || + (ios_info.ios_base != 60 && ES_CheckHasKoreanKey())) + { + printf("\n" + " Installing this System menu will brick your Wii.\n" + " Please remove the Korean key via KoreanKii,\n" + " then try again.\n\n" + ); + + ret = -999; + goto err; + } + } + if (tmd_data->title_version < 416) { if(boot2version == 4) { @@ -923,9 +898,6 @@ skipChecks: cleanupPriiloader = true; } - /* Fix ticket */ - __Wad_FixTicket(p_tik); - printf("\t\t>> Installing ticket..."); fflush(stdout); @@ -945,7 +917,7 @@ skipChecks: goto err; /* Get list of currently installed shared contents */ - GetSharedContents(&sharedContents, &sharedContentsCount); + Sys_GetSharedContents(&sharedContents, &sharedContentsCount); /* Install contents */ for (cnt = 0; cnt < tmd_data->num_contents; cnt++) @@ -958,7 +930,7 @@ skipChecks: /* Encrypted content size */ len = round_up(content->size, 64); - if (SharedContentPresent(content, sharedContents, sharedContentsCount)) + if (Sys_SharedContentPresent(content, sharedContents, sharedContentsCount)) { offset += len; continue; @@ -1258,6 +1230,8 @@ s32 Wad_Uninstall(FILE *fp) Con_ClearLine(); + /* Why don't we do this the other way around? Delete title contents, delete TMD, delete ticket. Seems more natural. */ + printf("\t\t>> Deleting tickets..."); fflush(stdout); From a4b6f965a8b5da9eac7d4c256c9a24ac1f5d87f6 Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Mon, 22 Apr 2024 08:06:01 -0500 Subject: [PATCH 2/6] putting this here for now --- source/otp.c | 11 +- source/otp.h | 19 +-- source/sys.h | 5 +- source/title.c | 27 ++++ source/title.h | 5 + source/wad.c | 352 +++++++++++++++++++++++++------------------------ 6 files changed, 224 insertions(+), 195 deletions(-) diff --git a/source/otp.c b/source/otp.c index eadd9c1..c52d708 100644 --- a/source/otp.c +++ b/source/otp.c @@ -8,14 +8,11 @@ #define HW_OTP_DATA (*(vu32*)0xCD8001F0) #define HW_OTP_BLK_SIZE 4 -#define HW_OTP_BLK_CNT (OTP_BANK_SIZE / HW_OTP_BLK_SIZE) +#define HW_OTP_BLK_CNT (OTP_SIZE / HW_OTP_BLK_SIZE) -u8 otp_read(void *dst, OTPBank bank, u8 offset, u8 size) +u8 otp_read(void *dst, u8 offset, u8 size) { - if (!dst || bank > 7 || offset >= OTP_BANK_SIZE || !size || (offset + size) > OTP_BANK_SIZE) return 0; - - /* This is not a Wii U. */ - if (bank != 0 && read16(0xCD8005A0) != 0xCAFE) return 0; + if (!dst || offset >= OTP_SIZE || !size || (offset + size) > OTP_SIZE) return 0; u8 cur_offset = 0; @@ -42,7 +39,7 @@ u8 otp_read(void *dst, OTPBank bank, u8 offset, u8 size) if (cur_offset >= size) break; // Send command + address - HW_OTP_COMMAND = (0x80000000 | bank << 8 | i); + HW_OTP_COMMAND = (0x80000000 | i); // Receive data *((u32*)val) = HW_OTP_DATA; diff --git a/source/otp.h b/source/otp.h index a5b35a0..d4d9939 100644 --- a/source/otp.h +++ b/source/otp.h @@ -1,19 +1,9 @@ #ifndef __OTP_H__ #define __OTP_H__ -#include +#define OTP_SIZE 0x80 -#define OTP_BANK_SIZE 0x80 - -// I want to add a structure for Bank 1 but the only thing that matters to me is the vWii common key in there. -#define VWII_COMMON_KEY_OFFSET 0x50 -typedef enum -{ - BANK_WII, - BANK_WIIU, -} OTPBank; - -struct OTP +typedef struct { u8 boot1_hash[20]; u8 common_key[16]; @@ -32,9 +22,8 @@ struct OTP u8 rng_key[16]; u32 unk1; u32 unk2; // 0x00000007 -}; -_Static_assert(sizeof(struct OTP) == OTP_BANK_SIZE, "OTP struct size incorrect!"); +} otp_t; -u8 otp_read(void *dst, OTPBank bank, u8 offset, u8 size); +u8 otp_read(void *dst, u8 offset, u8 size); #endif /* __OTP_H__ */ diff --git a/source/sys.h b/source/sys.h index c7990c0..95f5f76 100644 --- a/source/sys.h +++ b/source/sys.h @@ -27,9 +27,12 @@ typedef struct char _padding[16]; } cIOSInfo; -_Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?"); +// _Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?"); + +#define IS_WIIU (*(vu16*)0xCD005A0 == 0xCAFE) extern u32 boot2version; + /* Prototypes */ bool isIOSstub(u8 ios_number); bool tmdIsStubIOS(tmd*); diff --git a/source/title.c b/source/title.c index 19abbc3..44d67de 100644 --- a/source/title.c +++ b/source/title.c @@ -3,9 +3,11 @@ #include #include #include +#include #include "sha1.h" #include "utils.h" +#include "otp.h" #include "malloc.h" s32 Title_ZeroSignature(signed_blob *p_sig) @@ -315,3 +317,28 @@ out: return ret; } + +__attribute__((aligned(0x10))) +aeskey WiiCommonKey, vWiiCommonKey; + +void Title_SetupCommonKeys(void) +{ + static bool keys_ok = false; + if (keys_ok) + return; + + // Grab the Wii common key... + otp_read(WiiCommonKey, offsetof(otp_t, common_key), sizeof(aeskey)); + + // ...and decrypt the vWii common key with it. + static const unsigned char vwii_key_enc_bin[0x10] = { 0x6e, 0x18, 0xdb, 0x23, 0x84, 0x7c, 0xba, 0x6c, 0x19, 0x31, 0xa4, 0x17, 0x9b, 0xaf, 0x8e, 0x09 }; + unsigned char iv[0x10] = {}; + + memcpy(vWiiCommonKey, vwii_key_enc_bin, sizeof(vwii_key_enc_bin)); + AES_Init(); + AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), vWiiCommonKey, vWiiCommonKey, sizeof(aeskey)); + AES_Close(); + + keys_ok = true; + return; +}; \ No newline at end of file diff --git a/source/title.h b/source/title.h index c2c802a..44c04ea 100644 --- a/source/title.h +++ b/source/title.h @@ -1,9 +1,14 @@ #ifndef _TITLE_H_ #define _TITLE_H_ +#include + /* Constants */ #define BLOCK_SIZE 0x4000 +/* Variables */ +extern aeskey WiiCommonKey, vWiiCommonKey; + /* Prototypes */ s32 Title_ZeroSignature(signed_blob *); s32 Title_FakesignTik(signed_blob *); diff --git a/source/wad.c b/source/wad.c index 6ea03be..822295e 100644 --- a/source/wad.c +++ b/source/wad.c @@ -578,17 +578,13 @@ bool __Wad_FixTicket(signed_blob *s_tik) iv[0] = p_tik->titleid; iv[1] = 0; - if (!otp_read(commonkey, BANK_WIIU, VWII_COMMON_KEY_OFFSET, sizeof(commonkey))) - return false; - AES_Init(); - AES_Decrypt(commonkey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); + AES_Decrypt(vWiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); - otp_read(commonkey, BANK_WII, offsetof(struct OTP, common_key), sizeof(commonkey)); iv[0] = p_tik->titleid; iv[1] = 0; - AES_Encrypt(commonkey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); + AES_Encrypt(WiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); memcpy(p_tik->cipher_title_key, tkeybuf, sizeof(tkeybuf)); AES_Close(); @@ -598,7 +594,7 @@ bool __Wad_FixTicket(signed_blob *s_tik) Title_FakesignTik(s_tik); } - return true; + return fixvWiiKey; } bool __Wad_VerifyHeader(wadHeader* header) @@ -677,13 +673,7 @@ s32 Wad_Install(FILE *fp) if (ret != 1) goto err; - if (!__Wad_FixTicket(p_tik)) - { - printf("\n This WAD is for the vWii (Wii U) only.\n\n"); - - ret = -999; - goto err; - } + bool isvWiiTitle = __Wad_FixTicket(p_tik); offset += round_up(header->tik_len, 64); @@ -699,204 +689,222 @@ s32 Wad_Install(FILE *fp) /* Get TMD info */ tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); - - if(TITLE_LOWER(tmd_data->sys_version) != 0 && isIOSstub(TITLE_LOWER(tmd_data->sys_version))) + + if (TITLE_UPPER(tmd_data->sys_version) == 0) // IOS { - printf("\n This Title wants IOS%i but the installed version\n is a stub.\n", TITLE_LOWER(tmd_data->sys_version)); - ret = -1036; - goto err; - } - - if(tid == get_title_ios(TITLE_ID(1, 2))) - { - if (tmdIsStubIOS(tmd_data)) + if (isvWiiTitle && !IS_WIIU) { - printf("\n I won't install a stub System Menu IOS\n"); + printf("\n Cannot install vWii IOS on Wii.\n"); ret = -999; goto err; } - else if ((tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80))) + if(tid == get_title_ios(TITLE_ID(1, 2))) { - /* Check build tag here */ - } - } - - if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K'))) - { - if (tmdIsStubIOS(tmd_data)) - { - printf("\n I won't install a stub EULA IOS\n"); - ret = -999; - goto err; - } - } - - if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'K'))) - { - if (tmdIsStubIOS(tmd_data)) - { - printf("\n I won't install a stub rgnsel IOS\n"); - ret = -999; - goto err; - } - } - if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449))) - { - if (tmdIsStubIOS(tmd_data)) - { - printf("\n Are you sure you wan't to install a stub HBC IOS?\n"); - printf("\n Press A to continue.\n"); - printf(" Press B skip."); - - u32 buttons = WaitButtons(); - - if (!(buttons & WPAD_BUTTON_A)) + if (tmdIsStubIOS(tmd_data)) { - ret = -998; + printf("\n I won't install a stub System Menu IOS\n"); + ret = -999; + goto err; + } + + else if (tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80)) + { + /* Check build tag here */ + } + } + + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K'))) + { + if (tmdIsStubIOS(tmd_data)) + { + printf("\n I won't install a stub EULA IOS\n"); + ret = -999; goto err; } } + + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'E')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'P')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'J')) + || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'K'))) + { + if (tmdIsStubIOS(tmd_data)) + { + printf("\n I won't install a stub rgnsel IOS\n"); + ret = -999; + goto err; + } + } + if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449))) + { + if (tmdIsStubIOS(tmd_data)) + { + printf("\n Are you sure you wan't to install a stub HBC IOS?\n"); + printf("\n Press A to continue.\n"); + printf(" Press B skip."); + + u32 buttons = WaitButtons(); + + if (!(buttons & WPAD_BUTTON_A)) + { + ret = -998; + goto err; + } + } + } } - - if (tid == TITLE_ID(1, 2)) + else // not IOS { - if (skipRegionSafetyCheck || gForcedInstall) - goto skipChecks; - - char region = 0; - u16 version = 0; - - GetSysMenuRegion(&version, ®ion); - - if (tmd_data->vwii_title) + if (isIOSstub(TITLE_LOWER(tmd_data->sys_version))) { - printf("\n I won't install a vWii SM by default.\n\n"); - printf("\n If you're really sure what you're doing, next time\n"); - printf(" select your device using Konami...\n\n"); - - ret = -999; + printf("\n This Title wants IOS%i but the installed version\n is a stub.\n", TITLE_LOWER(tmd_data->sys_version)); + ret = -1036; goto err; } - if(region == 0) + + + if (tid == TITLE_ID(1, 2)) { - printf("\n Unknown System menu region\n Please check the site for updates\n"); + if (skipRegionSafetyCheck || gForcedInstall) + goto skipChecks; + + char region = 0; + u16 version = 0; + + GetSysMenuRegion(&version, ®ion); - ret = -999; - goto err; - } + if (isvWiiTitle || tmd_data->vwii_title) // && !IS_WIIU ? :thinking: + { + printf("\n I won't install a vWii SM by default.\n\n"); + printf("\n If you're really sure what you're doing, next time\n"); + printf(" select your device using Konami...\n\n"); - if (!VersionIsOriginal(tmd_data->title_version)) - { - printf("\n I won't install an unkown SM versions by default.\n\n"); - printf("\n Are you installing a tweaked system menu?\n"); - printf("\n If you're really sure what you're doing, next time\n"); - printf(" select your device using Konami...\n\n"); - - ret = -999; - goto err; - } - - if(region != RegionLookupTable[(tmd_data->title_version & 0x0F)]) - { - printf("\n I won't install the wrong regions SM by default.\n\n"); - printf("\n Are you region changing?\n"); - printf("\n If you're really sure what you're doing, next time\n"); - printf(" select your device using Konami...\n\n"); + ret = -999; + goto err; + } - ret = -999; - goto err; - } + if(region == 0) + { + printf("\n Unknown System menu region\n Please check the site for updates\n"); + + ret = -999; + goto err; + } + if (!VersionIsOriginal(tmd_data->title_version)) + { + printf("\n I won't install an unkown SM versions by default.\n\n"); + printf("\n Are you installing a tweaked system menu?\n"); + printf("\n If you're really sure what you're doing, next time\n"); + printf(" select your device using Konami...\n\n"); + ret = -999; + goto err; + } + + if(region != RegionLookupTable[(tmd_data->title_version & 0x0F)]) + { + printf("\n I won't install the wrong regions SM by default.\n\n"); + printf("\n Are you region changing?\n"); + printf("\n If you're really sure what you're doing, next time\n"); + printf(" select your device using Konami...\n\n"); + + ret = -999; + goto err; + } + + if ((tmd_data->title_version & 0x1F) != 0x6 // 0x6 = KR + && (tmd_data->title_version >> 5) >= 15) // 4.2 or later + { + cIOSInfo ios_info; + + if (!Sys_GetcIOSInfo(TITLE_LOWER(tmd_data->sys_version), &ios_info) || + (ios_info.ios_base != 60 && ES_CheckHasKoreanKey())) + { + printf("\n" + " Installing this System menu will brick your Wii.\n" + " Please remove the Korean key via KoreanKii,\n" + " then try again.\n\n" + ); + + ret = -999; + goto err; + } + } skipChecks: - /* Put this before or after skipChecks? */ - if ((tmd_data->title_version & 0x1F) != 0x6 // 0x6 = KR - && (tmd_data->title_version >> 5) >= 15) // 4.2 or later - { - cIOSInfo ios_info; - - if (!Sys_GetcIOSInfo(TITLE_LOWER(tmd_data->sys_version), &ios_info) || - (ios_info.ios_base != 60 && ES_CheckHasKoreanKey())) + if (tmd_data->title_version < 416) { - printf("\n" - " Installing this System menu will brick your Wii.\n" - " Please remove the Korean key via KoreanKii,\n" - " then try again.\n\n" - ); - - ret = -999; - goto err; - } - } - if (tmd_data->title_version < 416) - { - if(boot2version == 4) - { - printf("\n This version of the System Menu\n is not compatible with your Wii\n"); - ret = -999; - goto err; - } - } - - if (!gForcedInstall && AHBPROT_DISABLED && IsPriiloaderInstalled()) - { - cleanupPriiloader = true; - printf("\n Priiloader is installed next to the system menu.\n\n"); - printf(" It is recommended to retain Priiloader as it can\n"); - printf(" protect your console from being bricked.\n\n"); - printf(" Press A to retain Priiloader or B to remove."); - - u32 buttons = WaitButtons(); - - if ((buttons & WPAD_BUTTON_A)) - { - retainPriiloader = (BackUpPriiloader() && CompareHashes(true)); - if (retainPriiloader) + if(boot2version == 4) { - SetPriiloaderOption(true); - Con_ClearLine(); - printf("\r[+] Priiloader will be retained.\n"); - fflush(stdout); + printf("\n This version of the System Menu\n is not compatible with your Wii\n"); + ret = -999; + goto err; } - else + } + + if (!gForcedInstall && AHBPROT_DISABLED && IsPriiloaderInstalled()) + { + cleanupPriiloader = true; + printf("\n Priiloader is installed next to the system menu.\n\n"); + printf(" It is recommended to retain Priiloader as it can\n"); + printf(" protect your console from being bricked.\n\n"); + printf(" Press A to retain Priiloader or B to remove."); + + u32 buttons = WaitButtons(); + + if ((buttons & WPAD_BUTTON_A)) { - Con_ClearLine(); - printf("\r Couldn't backup Priiloader.\n"); - fflush(stdout); - printf("\n Press A to continue or B to skip"); - - u32 buttons = WaitButtons(); - - if (!(buttons & WPAD_BUTTON_A)) + retainPriiloader = (BackUpPriiloader() && CompareHashes(true)); + if (retainPriiloader) { - ret = -990; - goto err; + SetPriiloaderOption(true); + Con_ClearLine(); + printf("\r[+] Priiloader will be retained.\n"); + fflush(stdout); + } + else + { + Con_ClearLine(); + printf("\r Couldn't backup Priiloader.\n"); + fflush(stdout); + printf("\n Press A to continue or B to skip"); + + u32 buttons = WaitButtons(); + + if (!(buttons & WPAD_BUTTON_A)) + { + ret = -990; + goto err; + } } } - } - if (!retainPriiloader) + if (!retainPriiloader) + { + SetPriiloaderOption(false); + Con_ClearLine(); + printf("\r[+] Priiloader will be removed.\n"); + fflush(stdout); + } + } + else { SetPriiloaderOption(false); - Con_ClearLine(); - printf("\r[+] Priiloader will be removed.\n"); - fflush(stdout); } } - else + + if (gForcedInstall) { - SetPriiloaderOption(false); + gForcedInstall = false; + cleanupPriiloader = true; } } - - if (gForcedInstall) - { - gForcedInstall = false; - cleanupPriiloader = true; - } printf("\t\t>> Installing ticket..."); fflush(stdout); From 920d7fb29595917042ea581a1bfe42d603a049ed Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Mon, 22 Apr 2024 14:43:18 -0500 Subject: [PATCH 3/6] IT WORKS --- source/sys.c | 2 +- source/title.c | 3 ++- source/wad-manager.c | 2 ++ source/wad.c | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/sys.c b/source/sys.c index 42b40fd..524cf7a 100644 --- a/source/sys.c +++ b/source/sys.c @@ -243,7 +243,7 @@ s32 Sys_GetSharedContents(SharedContent** out, u32* count) int ret = 0; u32 size; - SharedContent* buf = NANDLoadFile("/shared1/content.map", &size); + SharedContent* buf = (SharedContent*)NANDLoadFile("/shared1/content.map", &size); if (!buf) return (s32)size; diff --git a/source/title.c b/source/title.c index 44d67de..2f01539 100644 --- a/source/title.c +++ b/source/title.c @@ -6,6 +6,7 @@ #include #include "sha1.h" +#include "aes.h" #include "utils.h" #include "otp.h" #include "malloc.h" @@ -341,4 +342,4 @@ void Title_SetupCommonKeys(void) keys_ok = true; return; -}; \ No newline at end of file +}; diff --git a/source/wad-manager.c b/source/wad-manager.c index 28fb32c..7fbb1fc 100644 --- a/source/wad-manager.c +++ b/source/wad-manager.c @@ -183,6 +183,8 @@ int main(int argc, char **argv) WKB_Initialize(); WIILIGHT_Init(); + Title_SetupCommonKeys(); + /* Print disclaimer */ //Disclaimer(); diff --git a/source/wad.c b/source/wad.c index 822295e..3282051 100644 --- a/source/wad.c +++ b/source/wad.c @@ -571,7 +571,6 @@ bool __Wad_FixTicket(signed_blob *s_tik) { __attribute__ ((aligned(0x10))) aeskey tkeybuf; - aeskey commonkey; u64 iv[2]; memcpy(tkeybuf, p_tik->cipher_title_key, sizeof(aeskey)); From 0895174fbb695d90cd080b094740feb5ae7e2957 Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Tue, 23 Apr 2024 18:41:06 -0500 Subject: [PATCH 4/6] Korean Key detection pt.2 --- source/sys.c | 2 -- source/title.c | 2 -- source/title.h | 1 + source/wad-manager.c | 3 +++ source/wad.c | 54 +++++++++++++++++++++++++++++++++++++++----- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/source/sys.c b/source/sys.c index 524cf7a..59d1253 100644 --- a/source/sys.c +++ b/source/sys.c @@ -54,9 +54,7 @@ bool ES_CheckHasKoreanKey(void) if (seeprom_read(korean_key, offsetof(struct SEEPROM, korean_key), sizeof(korean_key)) != sizeof(korean_key)) return false; - AES_Init(); AES_Decrypt(korean_key, 0x10, iv, 0x10, data, data, sizeof(data)); - AES_Close(); // return (!strcmp((char*) data, "thepikachugamer")) Just remembered that this is how the Trucha bug came to be return (!memcmp(data, "thepikachugamer", sizeof(data))); diff --git a/source/title.c b/source/title.c index 2f01539..f5c4ef8 100644 --- a/source/title.c +++ b/source/title.c @@ -336,9 +336,7 @@ void Title_SetupCommonKeys(void) unsigned char iv[0x10] = {}; memcpy(vWiiCommonKey, vwii_key_enc_bin, sizeof(vwii_key_enc_bin)); - AES_Init(); AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), vWiiCommonKey, vWiiCommonKey, sizeof(aeskey)); - AES_Close(); keys_ok = true; return; diff --git a/source/title.h b/source/title.h index 44c04ea..0b233be 100644 --- a/source/title.h +++ b/source/title.h @@ -20,5 +20,6 @@ s32 Title_GetVersion(u64, u16 *); s32 Title_GetSysVersion(u64, u64 *); s32 Title_GetSize(u64, u32 *); s32 Title_GetIOSVersions(u8 **, u32 *); +void Title_SetupCommonKeys(void); #endif diff --git a/source/wad-manager.c b/source/wad-manager.c index 7fbb1fc..2884931 100644 --- a/source/wad-manager.c +++ b/source/wad-manager.c @@ -8,6 +8,8 @@ #include #include "sys.h" +#include "title.h" +#include "aes.h" #include "gui.h" #include "menu.h" #include "restart.h" @@ -183,6 +185,7 @@ int main(int argc, char **argv) WKB_Initialize(); WIILIGHT_Init(); + AES_Init(); Title_SetupCommonKeys(); /* Print disclaimer */ diff --git a/source/wad.c b/source/wad.c index 3282051..d0611d0 100644 --- a/source/wad.c +++ b/source/wad.c @@ -577,7 +577,6 @@ bool __Wad_FixTicket(signed_blob *s_tik) iv[0] = p_tik->titleid; iv[1] = 0; - AES_Init(); AES_Decrypt(vWiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); iv[0] = p_tik->titleid; @@ -586,7 +585,6 @@ bool __Wad_FixTicket(signed_blob *s_tik) AES_Encrypt(WiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf)); memcpy(p_tik->cipher_title_key, tkeybuf, sizeof(tkeybuf)); - AES_Close(); } /* Fakesign ticket */ @@ -691,9 +689,9 @@ s32 Wad_Install(FILE *fp) if (TITLE_UPPER(tmd_data->sys_version) == 0) // IOS { - if (isvWiiTitle && !IS_WIIU) + if (isvWiiTitle ^ IS_WIIU) // xor is one of my favourite binary operators of all time { - printf("\n Cannot install vWii IOS on Wii.\n"); + printf("\n Cannot install vWii IOS on Wii (and vice versa).\n"); ret = -999; goto err; } @@ -707,9 +705,53 @@ s32 Wad_Install(FILE *fp) goto err; } - else if (tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80)) + // this code feels like a MESS + else if (!IS_WIIU && (tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80))) { - /* Check build tag here */ + tik* ticket = (tik*)SIGNATURE_PAYLOAD(p_tik); + + __aligned(0x10) + aeskey titlekey; + u64 iv[2] = { tid }; + + + memcpy(titlekey, ticket->cipher_title_key, sizeof(aeskey)); + AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), titlekey, titlekey, sizeof(aeskey)); + + u32 content0_offset = offset; + for (tmd_content* con = tmd_data->contents; con < tmd_data->contents + tmd_data->num_contents; con++) + { + if (con->index == 0) break; + content0_offset += round_up(con->size, 0x40); + } + + __aligned(0x20) + cIOSInfo build_tag = {}; + + printf("build_tag@%p\n", &build_tag); + __asm__ volatile ( ".long -1" ); + + ret = FSOPReadOpenFile(fp, (void*)&build_tag, content0_offset, sizeof(cIOSInfo)); + if (ret != 1) + goto err; + + iv[0] = 0; + iv[1] = 0; + AES_Decrypt(titlekey, sizeof(aeskey), iv, sizeof(iv), &build_tag, &build_tag, sizeof(cIOSInfo)); + + if (build_tag.hdr_magic != CIOS_INFO_MAGIC || + build_tag.hdr_version != CIOS_INFO_VERSION || + (build_tag.ios_base != 60 && ES_CheckHasKoreanKey())) + { + printf("\n" + " Installing this System menu IOS will brick your Wii.\n" + " Please remove the Korean key via KoreanKii,\n" + " then try again.\n\n" + ); + + ret = -999; + goto err; + } } } From 3ca05bb55d8dd887ce67b5fe5ee4f4badb55431b Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Wed, 24 Apr 2024 18:38:09 -0500 Subject: [PATCH 5/6] ?? --- source/wad.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/wad.c b/source/wad.c index d0611d0..a92bb1e 100644 --- a/source/wad.c +++ b/source/wad.c @@ -728,9 +728,6 @@ s32 Wad_Install(FILE *fp) __aligned(0x20) cIOSInfo build_tag = {}; - printf("build_tag@%p\n", &build_tag); - __asm__ volatile ( ".long -1" ); - ret = FSOPReadOpenFile(fp, (void*)&build_tag, content0_offset, sizeof(cIOSInfo)); if (ret != 1) goto err; From d11b6381e4ec0b3e190127d30c76d75bab000d95 Mon Sep 17 00:00:00 2001 From: Naim2000 Date: Wed, 24 Apr 2024 18:45:41 -0500 Subject: [PATCH 6/6] typod IS_WIIU --- source/sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/sys.h b/source/sys.h index 95f5f76..5021215 100644 --- a/source/sys.h +++ b/source/sys.h @@ -29,7 +29,7 @@ typedef struct } cIOSInfo; // _Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?"); -#define IS_WIIU (*(vu16*)0xCD005A0 == 0xCAFE) +#define IS_WIIU (*(vu16*)0xCD0005A0 == 0xCAFE) extern u32 boot2version;