├── CMakeLists.txt ├── main.cpp ├── OTP.h ├── README.md └── OTP.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8.2) 2 | project(SHOW_MII_VWII_KEYS) 3 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY Binary) 4 | 5 | add_library(OTP OTP.cpp OTP.h) 6 | add_executable(ShowMiiVWiiKeys main.cpp) 7 | target_link_libraries (ShowMiiVWiiKeys PUBLIC OTP) -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "OTP.h" 2 | using namespace std; 3 | 4 | // 5 | // I plan to make use of the args feature later. 6 | // 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | cout << "ShowMiiVWiiKeys " << "0.01 by CuriousTommy (Thomas A.)" << endl << endl; 11 | cout << "The program is currently hardcoded to expect a otp.bin file in the " 12 | << "same location at the application. It will also create a keys.bin and " 13 | << "keys.txt file. Please don't share these files as they contain personal " 14 | << "information about your Wii U." << endl << endl << endl; 15 | 16 | try 17 | { 18 | OTP mainOperation; 19 | 20 | mainOperation.printData(cout, OTP::BK0 | OTP::BK6); 21 | mainOperation.genFile(OTP::BootMii); 22 | mainOperation.genFile(OTP::Text); 23 | } 24 | 25 | catch (const char *exception) 26 | { 27 | cout << exception << endl; 28 | } 29 | } -------------------------------------------------------------------------------- /OTP.h: -------------------------------------------------------------------------------- 1 | #ifndef OTP_H 2 | #define OTP_H 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class OTP 10 | { 11 | private: // data 12 | 13 | // Information about the Wii U's OTP was taken from here: 14 | // http://wiiubrew.org/wiki/Hardware/OTP 15 | 16 | struct 17 | { 18 | struct 19 | { 20 | char bootSHA1[0x14]; // 0x00, Size: 0x14 21 | char commonKey[0x10]; // 0x14, Size: 0x10 22 | char NG_ID[0x4]; // 0x24, Size: 0x4 23 | char privNGKey[0x1C]; // 0x28, Size: 0x1C 24 | char NAND_HMAC[0x14]; // 0x44, Size: 0x14 25 | char keyNAND[0x10]; // 0x58, Size: 0x10 26 | char keyRNG[0x10]; // 0x68, Size: 0x10 27 | char unknown[0x08]; // 0x78, Size: 0x08 28 | } BK0; // Wii Bank 29 | 30 | char BK1[0x80]; // Wii U Bank (Part 1) 31 | char BK2[0x80]; // Wii U Bank (Part 2) 32 | char BK3[0x80]; // Wii U Bank (Part 3) 33 | char BK4[0x80]; // Wii U NG Bank 34 | char BK5[0x80]; // Wii U Certificate Bank 35 | 36 | struct 37 | { 38 | char rootMS_ID[0x4]; // 0x300, Size: 0x4 39 | char rootCA_ID[0x4]; // 0x304, Size: 0x4 40 | char rootKeyNG_ID[0x4]; // 0x308, Size: 0x4 41 | char rootSignNG[0x3C]; // 0x30C, Size: 0x3C 42 | char keyKorean[0x10]; // 0x348, Size: 0x10 43 | char Unknown[0x8]; // 0x358, Size: 0x8 44 | char privKeyDeviceNSS[0x20]; // 0x360, Size: 0x20 45 | } BK6; // Wii Certificate Bank 46 | 47 | char BK7[0x80]; // Misc Bank 48 | 49 | } otpBin; 50 | 51 | private: // functions 52 | string hexToHexString(char request[], uint8_t size, bool includeSpace = true); 53 | void formatData(ostream &streamOutput, int setWidth, string filename, string hexString); 54 | 55 | public: // enumerator 56 | enum bankType {BK0 = 1, BK1 = 2, BK2 = 4, 57 | BK3 = 8, BK4 = 16, BK5 = 32, 58 | BK6 = 64, BK7 = 128}; 59 | enum fileType {BootMii = 0, Text = 1}; 60 | 61 | public: // functions 62 | OTP(string filename = "otp.bin"); 63 | 64 | void genFile(int fileType = 0, string filename = ""); 65 | void printData(ostream &streamOutput, uint8_t requestedBanks); 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShowMiiVWiiKeys 2 | An Application That Analyzes A Wii U OTP File For vWii Information. It can generate a text file that can hold the vWii key and a BootMii keys.bin file that can be used with Dolphin (read the disclaimer). 3 | 4 | **Please do NOT share the otp.bin, generated keys.bin, or generated text file as they not only contain copyrighted keys but also personal information about you Wii U** (Note: If you are doing a recording of this application, be sure to censor what is shown on the cmd/terminal window). 5 | 6 | ## Why create this program when we can run Dump Mii Nand, XYZZY, etc. 7 | When you try to run those programs on a vWii, they do not, for some reason, provide the entire information needed to run the Wii Shop Channel on Dolphin. 8 | 9 | ## How to obtain the otp.bin file 10 | You will need a Wii U with the ability run the [Homebrew Launcher](https://github.com/dimok789/homebrew_launcher/releases) and run the IOSU exploit. Download dimok's [OTP2SD application](https://github.com/dimok789/otp2sd_dumper/releases) and place the .elf file on your SD card like so: 11 | 12 | ``` 13 | [root of SD card]/wiiu/apps/otp2sd/otp2sd.elf 14 | ``` 15 | If the program ran successfully on you Wii U, you should see an otp.bin file in the root of your SD card. Copy that file and paste it next to the ShowMiiVWiiKeys application. 16 | 17 | ## How to use the program 18 | Currently, the program is hard-coded to check if the otp.bin file in the same folder as the executable/application. If the file is there, the program will generate both a keys.bin and keys.txt file. 19 | 20 | To run the program, just double click on the executable/application (or run it on a cmd/terminal interface). 21 | 22 | ## How to access the vWii Shop Account on Dolphin. 23 | **Disclaimer: This is considered unofficial since the vWii is not a real Wii. There may be features on Dolphin that are impossible to do with the generated keys.bin. Please do not bother the Dolphin team if you have any issues with accessing the Wii Shop Channel or using any of the Wii NAND features** 24 | 25 | Keep in mind that vWii NAND dumps are not compatible to with Dolphin, so you will need a manually create a Wii filesystem. You will need the latest development version of Dolphin or at least the version of Dolphin that [supports doing Wii update without needing a Wii NAND](https://github.com/dolphin-emu/dolphin/pull/5610). 26 | 27 | If you haven't already, dump the otp.bin file and use my program to generate a keys.bin file. Before we start with Dolphin, we will also need to go to the Wii U's `System Settings` app. 28 | 1. On the top-right corner, you will see the current version of your Wii U. Make a note of the letter that is next to the number. You will need this to download the correct Wii OS files. 29 | 2. Go to `Console Information` (it has a picture of the Wii U with the tablet), then select `Set Country of Residence`. Make a note of the country that is selected on the Wii U. This is needed to access the Wii Shop on Dolphin. 30 | 31 | 32 | Now let open up Dolphin! Click on the `Tools` tab and hover over `Perform Online System Update`. Select the region that corresponds to the letter you saw next to the version number. For example, my console's version is `5.5.1 U`. Therefore, I need to pick `United States`. If you are unsure what option to pick refer to the table below. 33 | 34 | Letter Region | Region Option on Dolphin 35 | --- | --- 36 | U | United States 37 | E | Europe 38 | J | Japan 39 | 40 | Once that is done, we are going to close Dolphin. We will need to paste the generated keys.bin file into the Wii folder. By default, Dolphin has the Wii folder located in these locations: 41 | 42 | Operating System | Folder Location 43 | --- | --- 44 | Windows | `%userprofile%\My Documents\Dolphin Emulator\Wii` 45 | Mac | `~/Library/Application Support/Dolphin/Wii` 46 | Linux | `~/.local/share/dolphin-emu/Wii/` 47 | 48 | After doing that, reopen Dolphin. Click on `Tools` again and click on `Load Wii System...`. go through the setup procedure, as you would on a real Wii until you get to the `Select your country of residence` section. Once you are in this section, put the country that you set on your Wii U. Then finish the rest of the setup. 49 | 50 | Before we can actually use the Wii Shop Channel, we need to agree to the user agreements. On the Wii menu, click on the `Wii` circle button. We then traverse to `Wii Settings` --> `Internet` --> `User Agreements`. Once you agreed to the terms of condition, you will be returned to the Wii Menu. 51 | 52 | Now you can finally use the Wii Shop Channel! Go ahead and launch the Wii Shop Channel! If you see a 205906 error code, don't worry about it. It is normal, just click `OK`. If everything worked fine, you should now have access to the Wii Shop Channel! 53 | 54 | ## Current Bugs 55 | 56 | Sometimes Dolphin gives an error when trying to access the Wii Shop Channel as if you never supplied a keys.bin file. 57 | 58 | I don't have a proper solution for this problem. One method I tried that worked was to use my normal Wii's keys.bin and then swap it with my Wii u version. This bug seems to happen randomly, but once you get access to the shop channel, it seems to go away. -------------------------------------------------------------------------------- /OTP.cpp: -------------------------------------------------------------------------------- 1 | #include "OTP.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | 14 | 15 | 16 | OTP::OTP(string filename) 17 | { 18 | ifstream input; 19 | input.open(filename, ios::binary); 20 | if (!input) throw "The Wii U OTP file could not be found!"; 21 | 22 | 23 | input.read(reinterpret_cast(&otpBin), sizeof(otpBin)); 24 | input.close(); 25 | } 26 | 27 | 28 | 29 | 30 | void OTP::genFile(int fileType, string filename) 31 | { 32 | ofstream output; 33 | 34 | switch (fileType) 35 | { 36 | case BootMii: 37 | { 38 | string stringConsoleID; 39 | if (filename == "") filename = "keys.bin"; 40 | output.open(filename, ios::binary); 41 | if (!output) throw "Could not create BootMii file"; 42 | 43 | 44 | // Information of the keys.bin file are taken from: 45 | // https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Core/ec_wii.h 46 | // https://wiibrew.org/wiki/Bootmii/NAND_dump_format 47 | // http://wiibrew.org/wiki/BootMii#Console_Keys_and_keys.bin 48 | // http://wiibrew.org/wiki/Hardware/OTP 49 | // http://wiibrew.org/wiki/Hardware/SEEPROM 50 | 51 | struct 52 | { 53 | char humanInfo[0x100]; // 0x0 54 | 55 | // ec_wii.h states that privNGKey should be 0x1E 56 | // but remember that privNGKey and NAND_HMAC overlaps 57 | // each other by two bytes. So this should be okay. 58 | struct 59 | { 60 | char bootSHA1[0x14]; // 0x100 61 | char commonKey[0x10]; // 0x114 62 | char consoleID[0x4]; // 0x124 63 | char privNGKey[0x1C]; // 0x128 64 | char NAND_HMAC[0x14]; // 0x144 65 | char keyNAND[0x10]; // 0x158 66 | char keyRNG[0x10]; // 0x168 67 | char Unknown[0x08]; // 0x178 68 | 69 | } OTP; 70 | 71 | char postPadOTP[0x80]; // 0x180 72 | 73 | // Note, there is a structure for both counters, 74 | // but since the Wii U's OTP does not provide info 75 | // for these counter, I am going to be lazy and have 76 | // a char array represent the whole counter. 77 | struct 78 | { 79 | char rootMS_ID[0x4]; // 0x200 80 | char rootCA_ID[0x4]; // 0x204 81 | char rootKeyNG_ID[0x4]; // 0x208 82 | char rootSignNG[0x3C]; // 0x20C 83 | char boot2Counter[0x14]; // 0x248 84 | char nandCounter[0x18]; // 0x25c 85 | char keyKorean[0x10]; // 0x274 86 | char postKorPad[0x74]; // 0x284 87 | char PRNG_Seed[0x4]; // 0x2F8 88 | char postPRNG_Pad[0x4]; // 0x2FC 89 | } SEEPROM; 90 | 91 | char postPadSEEPROM[0x100]; 92 | } bootMiiKeys; 93 | 94 | cout << "Generating " << filename << endl; 95 | 96 | // Lets fill the entire structure with zero 97 | fill_n(reinterpret_cast(&bootMiiKeys), sizeof(bootMiiKeys), 0); 98 | 99 | // Fill the humanInfo section 100 | strcpy(bootMiiKeys.humanInfo, "BackupMii v1, ConsoleID: "); 101 | stringConsoleID = hexToHexString(otpBin.BK0.NG_ID, sizeof(otpBin.BK0.NG_ID), false); 102 | strcpy(bootMiiKeys.humanInfo + 0x19, stringConsoleID.c_str()); // 0x19 == sizeof("BackupMii ...") 103 | bootMiiKeys.humanInfo[0x21] = 0x0A; 104 | 105 | // Fill the OTP section 106 | memcpy(&bootMiiKeys.OTP, &otpBin.BK0, sizeof(bootMiiKeys.OTP)); 107 | 108 | // Fill the SEEPROM section 109 | memcpy(&bootMiiKeys.SEEPROM, &otpBin.BK6, 0x4 + 0x4 + 0x4 + 0x3C); // rootMS_ID + rootCA_ID + rootKeyNG_ID + rootSignNG 110 | memcpy(&bootMiiKeys.SEEPROM.keyKorean, &otpBin.BK6.keyKorean, sizeof(bootMiiKeys.SEEPROM.keyKorean)); 111 | 112 | // Write the entire the structure in file 113 | output.write(reinterpret_cast(&bootMiiKeys), sizeof(bootMiiKeys)); 114 | output.close(); 115 | 116 | cout << filename << " has be successfully created" << endl; 117 | cout << endl; 118 | 119 | break; 120 | } 121 | 122 | 123 | case Text: 124 | { 125 | if (filename == "") filename = "keys.txt"; 126 | output.open(filename); 127 | if (!output) throw "Could not create text file"; 128 | 129 | cout << "Generating " << filename << endl; 130 | 131 | // The stream from printData will be stored into output. 132 | printData(output, BK0 | BK6); 133 | output.close(); 134 | 135 | cout << filename << " has be successfully created" << endl; 136 | cout << endl; 137 | 138 | break; 139 | } 140 | 141 | 142 | default: 143 | throw "Invalid Request"; 144 | } 145 | } 146 | 147 | 148 | 149 | 150 | void OTP::printData(ostream &streamOutput, uint8_t requestedBanks) 151 | { 152 | if (requestedBanks & BK0) 153 | { 154 | int size = 24; 155 | 156 | streamOutput << "BANK 0:" << endl; 157 | formatData(streamOutput, size, "Wii Boot1 SHA-1 Hash", 158 | hexToHexString(otpBin.BK0.bootSHA1, sizeof(otpBin.BK0.bootSHA1))); 159 | 160 | formatData(streamOutput, size, "Wii Common Key", 161 | hexToHexString(otpBin.BK0.commonKey, sizeof(otpBin.BK0.commonKey))); 162 | 163 | formatData(streamOutput, size, "Wii NG ID", 164 | hexToHexString(otpBin.BK0.NG_ID, sizeof(otpBin.BK0.NG_ID))); 165 | 166 | formatData(streamOutput, size, "Wii NG Private Key", 167 | hexToHexString(otpBin.BK0.privNGKey, sizeof(otpBin.BK0.privNGKey))); 168 | 169 | formatData(streamOutput, size, "Wii NAND HMAC", 170 | hexToHexString(otpBin.BK0.NAND_HMAC, sizeof(otpBin.BK0.NAND_HMAC))); 171 | 172 | formatData(streamOutput, size, "Wii NAND key", 173 | hexToHexString(otpBin.BK0.keyNAND, sizeof(otpBin.BK0.keyNAND))); 174 | 175 | formatData(streamOutput, size, "Wii RNG key", 176 | hexToHexString(otpBin.BK0.keyRNG, sizeof(otpBin.BK0.keyRNG))); 177 | streamOutput << endl; 178 | } 179 | 180 | if (requestedBanks & BK6) 181 | { 182 | int size = 38; 183 | 184 | streamOutput << "BANK 6:" << endl; 185 | formatData(streamOutput, size, "Wii Root Certificate MS ID", 186 | hexToHexString(otpBin.BK6.rootMS_ID, sizeof(otpBin.BK6.rootMS_ID))); 187 | 188 | formatData(streamOutput, size, "Wii Root Certificate CA ID", 189 | hexToHexString(otpBin.BK6.rootCA_ID, sizeof(otpBin.BK6.rootCA_ID))); 190 | 191 | formatData(streamOutput, size, "Wii Root Certificate NG Key ID", 192 | hexToHexString(otpBin.BK6.rootKeyNG_ID, sizeof(otpBin.BK6.rootKeyNG_ID))); 193 | 194 | formatData(streamOutput, size, "Wii Root Certificate NG Singature", 195 | hexToHexString(otpBin.BK6.rootSignNG, sizeof(otpBin.BK6.rootSignNG))); 196 | 197 | formatData(streamOutput, size, "Wii Korean Key", 198 | hexToHexString(otpBin.BK6.keyKorean, sizeof(otpBin.BK6.keyKorean))); 199 | 200 | formatData(streamOutput, size, "Unknown (unused)", 201 | hexToHexString(otpBin.BK6.Unknown, sizeof(otpBin.BK6.Unknown))); 202 | 203 | formatData(streamOutput, size, "NSS Device Certificate Private Key", 204 | hexToHexString(otpBin.BK6.privKeyDeviceNSS, sizeof(otpBin.BK6.privKeyDeviceNSS))); 205 | streamOutput << endl; 206 | } 207 | } 208 | 209 | 210 | 211 | 212 | string OTP::hexToHexString(char request[], uint8_t size, bool includeSpace) 213 | { 214 | stringstream value; 215 | 216 | for (int i = 0; i < size; i++) 217 | { 218 | value << setw(2) << setfill('0'); 219 | value << hex << static_cast((static_cast(request[i]))); 220 | includeSpace ? (value << " ") : (value << ""); 221 | } 222 | 223 | return value.str(); 224 | } 225 | 226 | 227 | 228 | 229 | void OTP::formatData(ostream &streamOutput, int setWidth, string filename, string hexString) 230 | { 231 | // For the sake of convenience, I will be using stringstream 232 | // to store the hex string. hexSize is used to state the max 233 | // amount of hexes each row can hold. 234 | stringstream value(hexString); 235 | string eightBitHex; 236 | int hexSize = 20; 237 | 238 | 239 | // This is the very first row, the name of the key will be 240 | // printed follow by first 20 hex values. 241 | // For Example: Key ~~~setw(space)~~~ : AA BB CC DD EE FF 242 | streamOutput << left << setw(setWidth - 3) << filename; 243 | streamOutput << right << " : "; 244 | for (int i = 0; i < hexSize && value; i++) 245 | { 246 | if (value >> eightBitHex) streamOutput << eightBitHex << " "; 247 | else break; 248 | } 249 | streamOutput << endl; 250 | 251 | 252 | // If there happens to be additional hex values they will be printed 253 | // in a new line. 254 | while (value) 255 | { 256 | streamOutput << setw(setWidth) << " "; 257 | for (int i = 0; i < hexSize && value; i++) 258 | { 259 | if (value >> eightBitHex) streamOutput << eightBitHex << " "; 260 | else break; 261 | } 262 | streamOutput << endl; 263 | } 264 | } --------------------------------------------------------------------------------