diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..077596f --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Other files/folder +otp.bin +seeprom.bin \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..30a1019 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 NoahAbc12345 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..54a475b --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Coffee Reading + +A command-line parser for Wii U OTP/SEEPROMs. + +Coffee Reading is a tool that can be run through Command Prompt or Terminal to parse Wii U [OTP (one-time programmable)](https://wiiubrew.org/wiki/Hardware/OTP) or [SEEPROM (serial electrically erasable programmable read only memory)](https://wiiubrew.org/wiki/Hardware/SEEPROM) files and present them in an easily-readable format. Please do not share any values obtained from this program! Many are illegal to share due to copyright law and/or unique to your own console! + +## Installation +A Windows executable will be bundled with each release. Linux users will need to use the Python script in the source code in order to run the program. + +## Usage +A Wii U OTP or SEEPROM file is most commonly obtained when following our [hacking guide](https://wiiu.hacks.guide/), with the help of [wiiu-nanddumper](https://github.com/wiiu-env/wiiu-nanddumper-payload/). +* Call the executable file and provide the path to your dumped one-time programmable. By default, only unique values are shown in the program. In order to read the entire file, pass `--unknown` (or `-u`) as an argument. +* The ability to save a report of obtained values is also available. Provide `--save` (or `-s`) as an argument to the program to do so. A text file named with a timestamp will be generated in the same directory as the tool. This can be used to refer to certain values without having to run the program again in the future. + +Example inputs may include: +* `Klefki.exe -s -u otp.bin` +* `./Klefki -s -u otp.bin` \ No newline at end of file diff --git a/source/main.py b/source/main.py new file mode 100644 index 0000000..e6cfbb3 --- /dev/null +++ b/source/main.py @@ -0,0 +1,266 @@ +import construct +import os +import sys +import time + +from datetime import datetime +from itertools import islice + +# Show the corrosponding usage per platform. +def show_example_usage(platform): + if platform == "nt": + print("Usage Example: Coffee_Reading.exe -s -u otp.bin\n") + else: + print("Usage Example: ./Coffee_Reading -s -u otp.bin\n") + + print("-s / --save:") + print("\tOptional argument.") + print("\tSave values that are output to a text file.") + + print("-u / --unknown:") + print("\tOptional argument.") + print("\tShow any unknown, empty, or unused values.\n") + +# Check if all arguments are properly provided. +def validate_arguments(arguments, backup_file): + valid_arguments = ["-s", "-u", "--save", "--unknown"] + + if len(arguments) == 1: + # No argumens were provided. + show_example_usage(os.name) + print("There is no provided file! Did you define a path?\n") + sys.exit() + + try: + if os.stat(backup_file).st_size not in [512, 1024]: + # Neither an OTP or an SEEPROM was given to read. + show_example_usage(os.name) + print("The file provided isn't correct! Is this a valid backup?\n") + sys.exit() + + except FileNotFoundError: + # The file does not exist, so it can't be read. + show_example_usage(os.name) + print("The file provided doesn't exist! Did you enter the right path?\n") + sys.exit() + + if len(arguments) >= 3: + # It's possible there's more arguments than necessary? + for flag in arguments[1:len(arguments) - 1]: + if flag not in valid_arguments or len(arguments) > 4: + show_example_usage(os.name) + print("The provided arguments are invalid! Are they correct?\n") + sys.exit() + +# Read any provided backup data. +def read_backup_data(arguments, backup_data): + unknown_keys = ["Unknown", "Unused", "Empty"] + + for item, value in islice(backup_data.items(), 1, None): + value = str(value) + value = value[11:-2].upper() + + if any(flag in arguments for flag in ["-u", "--unknown"]): + print(f"{item}: {value}") + else: + if not any(word in item for word in unknown_keys): + print(f"{item}: {value}") + +# Write the backup data to an easy-to-read text file. +def write_backup_data(arguments, backup_data, text_file): + with open(text_file, "w") as text_file: + sys.stdout = text_file + # Write a header for the file. + print("Coffee Reading (v1.0.0)") + print("Created by NoahAbc12345.\n") + print("Please do not share any values obtained from this program!") + print("Many are illegal to share due to copyright law and/or unique" + + " to your own console!") + if os.stat(backup_file).st_size == 512: + print("Useful Information: https://wiiubrew.org/wiki/Hardware/SEEPROM\n") + elif os.stat(backup_file).st_size == 1024: + print("Useful Information: https://wiiubrew.org/wiki/Hardware/OTP\n") + print("Generated: " + str(datetime.now())) + + if any(flag in arguments for flag in ["-u", "--unknown"]): + print("Unknown Mode: True\n") + else: + print("Unknown Mode: False\n") + # Read the backup data to the text file. + read_backup_data(arguments, backup_data) + sys.stdout = sys.__stdout__ + +# A construct for the Wii U OTP. +otp_structure = construct.Struct( + "Wii boot1 SHA-1 Hash" / construct.Hex(construct.Bytes(20)), + "Wii Common Key" / construct.Hex(construct.Bytes(16)), + "Wii NG ID" / construct.Hex(construct.Bytes(4)), + "Wii NG Private Key" / construct.Hex(construct.Bytes(28)), + "Wii NAND HMAC" / construct.Hex(construct.Bytes(20)), + "Wii NAND Key" / construct.Hex(construct.Bytes(16)), + "Wii RNG Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 1" / construct.Hex(construct.Bytes(8)), + "Security Level Flag" / construct.Hex(construct.Bytes(4)), + "IOStrength Configuration Flag" / construct.Hex(construct.Bytes(4)), + "Pulse Length for SEEPROM Manual CLK" / construct.Hex(construct.Bytes(4)), + "Possibly a Signature Type" / construct.Hex(construct.Bytes(4)), + "Wii U Starbuck Ancast Key" / construct.Hex(construct.Bytes(16)), + "Wii U SEEPROM Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 2" / construct.Hex(construct.Bytes(16)), + "Unknown Space 3" / construct.Hex(construct.Bytes(16)), + "vWii Common Key" / construct.Hex(construct.Bytes(16)), + "Wii U Common Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 4" / construct.Hex(construct.Bytes(16)), + "Unknown Space 5" / construct.Hex(construct.Bytes(16)), + "Unknown Space 6" / construct.Hex(construct.Bytes(16)), + "SSL RSA Encryption Key" / construct.Hex(construct.Bytes(16)), + "USB Storage Seed Encryption Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 7" / construct.Hex(construct.Bytes(16)), + "Wii U XOR Key" / construct.Hex(construct.Bytes(16)), + "Wii U RNG Key" / construct.Hex(construct.Bytes(16)), + "Wii U SLC Key" / construct.Hex(construct.Bytes(16)), + "Wii U MLC Key" / construct.Hex(construct.Bytes(16)), + "SHDD Encryption Key" / construct.Hex(construct.Bytes(16)), + "DRH WLAN Data Encryption Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 8" / construct.Hex(construct.Bytes(48)), + "Wii U SLC (NAND) HMAC" / construct.Hex(construct.Bytes(20)), + "Unknown Space 9" / construct.Hex(construct.Bytes(12)), + "Unknown Space 10" / construct.Hex(construct.Bytes(16)), + "Unknown Space 11" / construct.Hex(construct.Bytes(12)), + "Wii U NG ID" / construct.Hex(construct.Bytes(4)), + "Wii U NG Private Key" / construct.Hex(construct.Bytes(32)), + "Wii U NSS Device Certificate Key" / construct.Hex(construct.Bytes(32)), + "Wii U OTP RNG Seed" / construct.Hex(construct.Bytes(16)), + "Unknown Space 12" / construct.Hex(construct.Bytes(16)), + "Wii U Root Certificate MS ID" / construct.Hex(construct.Bytes(4)), + "Wii U Root Certificate CA ID" / construct.Hex(construct.Bytes(4)), + "Wii U Root Certificate NG Key ID" / construct.Hex(construct.Bytes(4)), + "Wii U Root Certificate NG Signature" / construct.Hex(construct.Bytes(60)), + "Unknown Space 13" / construct.Hex(construct.Bytes(24)), + "Unknown Space 14" / construct.Hex(construct.Bytes(32)), + "Wii Root Certificate MS ID" / construct.Hex(construct.Bytes(4)), + "Wii Root Certificate CA ID" / construct.Hex(construct.Bytes(4)), + "Wii Root Certificate NG Key ID" / construct.Hex(construct.Bytes(4)), + "Wii Root Certificate NG Signature" / construct.Hex(construct.Bytes(60)), + "Wii Korean Key" / construct.Hex(construct.Bytes(16)), + "Unknown Space 15" / construct.Hex(construct.Bytes(8)), + "Wii NSS Device Certificate Key" / construct.Hex(construct.Bytes(32)), + "Unknown Space 16" / construct.Hex(construct.Bytes(32)), + "Wii U boot1 Key (Locked)" / construct.Hex(construct.Bytes(16)), + "Unknown Space 17" / construct.Hex(construct.Bytes(16)), + "Empty Space 1" / construct.Hex(construct.Bytes(32)), + "Empty Space 2" / construct.Hex(construct.Bytes(4)), + "OTP Version & Revision" / construct.Hex(construct.Bytes(4)), + "OTP Date Code" / construct.Hex(construct.Bytes(8)), + "OTP Version Name String" / construct.Hex(construct.Bytes(8)), + "Empty Space 3" / construct.Hex(construct.Bytes(4)), + "JTAG Status" / construct.Hex(construct.Bytes(4)) +) + +# A construct for the Wii U SEEPROM. +seeprom_structure = construct.Struct( + "Empty Space 1" / construct.Hex(construct.Bytes(18)), + "SEEPROM RNG Seed" / construct.Hex(construct.Bytes(8)), + "Empty Space 2" / construct.Hex(construct.Bytes(6)), + "PPC PVR" / construct.Hex(construct.Bytes(4)), + "SEEPROM Version Name String" / construct.Hex(construct.Bytes(6)), + "SEEPROM Version Code" / construct.Hex(construct.Bytes(2)), + "OTP Version Code" / construct.Hex(construct.Bytes(2)), + "OTP Revision Code" / construct.Hex(construct.Bytes(2)), + "OTP Version Name String" / construct.Hex(construct.Bytes(8)), + "BC Structure CRC32" / construct.Hex(construct.Bytes(4)), + "BC Structure Size" / construct.Hex(construct.Bytes(2)), + "BC library version" / construct.Hex(construct.Bytes(2)), + "BC author" / construct.Hex(construct.Bytes(2)), + "BC boardType" / construct.Hex(construct.Bytes(2)), + "BC boardRevision" / construct.Hex(construct.Bytes(2)), + "BC bootSource" / construct.Hex(construct.Bytes(2)), + "BC ddr3Size" / construct.Hex(construct.Bytes(2)), + "BC ddr3Speed" / construct.Hex(construct.Bytes(2)), + "BC ppcClockMultiplier" / construct.Hex(construct.Bytes(2)), + "BC iopClockMultiplier" / construct.Hex(construct.Bytes(2)), + "BC video1080p" / construct.Hex(construct.Bytes(2)), + "BC ddr3Vendor" / construct.Hex(construct.Bytes(2)), + "BC movPassiveReset" / construct.Hex(construct.Bytes(2)), + "BC sysPllSpeed" / construct.Hex(construct.Bytes(2)), + "BC sataDevice" / construct.Hex(construct.Bytes(2)), + "BC consoleType" / construct.Hex(construct.Bytes(2)), + "BC devicePresense" / construct.Hex(construct.Bytes(4)), + "Empty Space 3" / construct.Hex(construct.Bytes(32)), + "Wii U Drive Key" / construct.Hex(construct.Bytes(16)), + "Wii U Factory Key" / construct.Hex(construct.Bytes(16)), + "Wii U SHDD Key" / construct.Hex(construct.Bytes(16)), + "Wii U SHDD Key Seed" / construct.Hex(construct.Bytes(16)), + "Drive Key's Status Flag" / construct.Hex(construct.Bytes(2)), + "USB Key Seed's Status Flag" / construct.Hex(construct.Bytes(2)), + "SHDD Key's Status Flag" / construct.Hex(construct.Bytes(2)), + "Empty Space 4" / construct.Hex(construct.Bytes(106)), + "Unknown Space 1" / construct.Hex(construct.Bytes(4)), + "Unknown Space 2" / construct.Hex(construct.Bytes(2)), + "Unknown Space 3" / construct.Hex(construct.Bytes(2)), + "Empty Space 5" / construct.Hex(construct.Bytes(8)), + "sys_prod.version" / construct.Hex(construct.Bytes(4)), + "sys_prod.eeprom_version" / construct.Hex(construct.Bytes(4)), + "sys_prod.product_area" / construct.Hex(construct.Bytes(4)), + "sys_prod.game_region" / construct.Hex(construct.Bytes(4)), + "sys_prod.ntsc_pal" / construct.Hex(construct.Bytes(4)), + "sys_prod.5ghz_country_code" / construct.Hex(construct.Bytes(2)), + "sys_prod.5ghz_country_code_revision" / construct.Hex(construct.Bytes(2)), + "sys_prod.code_id" / construct.Hex(construct.Bytes(8)), + "sys_prod.serial_id" / construct.Hex(construct.Bytes(16)), + "sys_prod.model_number" / construct.Hex(construct.Bytes(16)), + "Unknown Space 4" / construct.Hex(construct.Bytes(2)), + "Unknown Space 5" / construct.Hex(construct.Bytes(2)), + "Unknown Space 6" / construct.Hex(construct.Bytes(2)), + "Unknown Space 7" / construct.Hex(construct.Bytes(2)), + "Production Date (Year)" / construct.Hex(construct.Bytes(2)), + "Production Date (Month and Day)" / construct.Hex(construct.Bytes(2)), + "Production Date (Hour and Minute)" / construct.Hex(construct.Bytes(2)), + "CRC32 of the Last 0x0E Bytes" / construct.Hex(construct.Bytes(4)), + "0xAA55 Marker" / construct.Hex(construct.Bytes(2)), + "Unknown Space 8" / construct.Hex(construct.Bytes(2)), + "Unknown Space 9" / construct.Hex(construct.Bytes(2)), + "Unknown Space 10" / construct.Hex(construct.Bytes(2)), + "Empty Space 6" / construct.Hex(construct.Bytes(4)), + "Unknown Space 11" / construct.Hex(construct.Bytes(4)), + "0xBB66 Marker" / construct.Hex(construct.Bytes(2)), + "Unknown Space 12" / construct.Hex(construct.Bytes(2)), + "Unknown Space 13" / construct.Hex(construct.Bytes(2)), + "Unknown Space 14" / construct.Hex(construct.Bytes(8)), + "Unknown Space 15" / construct.Hex(construct.Bytes(2)), + "Unknown Space 16" / construct.Hex(construct.Bytes(2)), + "Empty Space 7" / construct.Hex(construct.Bytes(8)), + "Unknown Space 17" / construct.Hex(construct.Bytes(4)), + "boot0 Parameters 1" / construct.Hex(construct.Bytes(16)), + "boot0 Parameters 2" / construct.Hex(construct.Bytes(16)), + "boot0 Parameters 3" / construct.Hex(construct.Bytes(16)), + "Empty Space 8" / construct.Hex(construct.Bytes(16)), +) + +# Defining necessary arguments. +arguments = sys.argv +backup_file = arguments[-1] +# Print when the program is started. +print("Coffee Reading (v1.0.0)") +print("Created by NoahAbc12345.\n") +print("Please do not share any values obtained from this program!") +print("Many are illegal to share due to copyright law and/or unique " + + "to your own console!") +# Begin to parse through the backup data. +validate_arguments(arguments, backup_file) +if os.stat(backup_file).st_size == 512: + print("Useful Information: https://wiiubrew.org/wiki/Hardware/SEEPROM\n") + backup_data = seeprom_structure.parse_file(backup_file) +elif os.stat(backup_file).st_size == 1024: + print("Useful Information: https://wiiubrew.org/wiki/Hardware/OTP\n") + backup_data = otp_structure.parse_file(backup_file) +# Run the reading function to pull keys. +read_backup_data(arguments, backup_data) +text_file = f"Coffee_Reading_{int(time.time())}.txt" +# If saving is requested, run the writing function. +if any(flag in arguments for flag in ["-s", "--save"]): + write_backup_data(arguments, backup_data, text_file) + print(f"\nThe report has been written to {text_file} successfully.\n") +else: + # Remind the user that saving the keys to a file is possible. + print("\nProvide \"-s\" or \"--save\" as an argument to save a report.\n")