From fdc7085c2394d87ce5dbdfecdf51423e1e7b00a1 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sat, 26 Dec 2020 18:53:35 +0100 Subject: [PATCH] 2020 update - Replace "cryptography" dependency by "pycryptodome", as the former relied on OpenSSL, causing issues on Mac OS - Fix a bug where firmtool build -t didn't alias -S - Fix a bug in the handling of section0 system modules with a 8-character long name --- LICENSE | 2 +- README.md | 16 ++++---- firmtool/__main__.py | 89 +++++++++++++++++++------------------------- setup.py | 6 +-- 4 files changed, 50 insertions(+), 63 deletions(-) diff --git a/LICENSE b/LICENSE index 09d493b..a8ba068 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2017, +Copyright (c) TuxSH 2017-2020 All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 80a8701..80d1c52 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ Compatible with Python >= 3.2 and Python >= 2.7. ## Installation -On Windows, install Python >= 3.4 using the installer provided by the official Python website. Make sure that `pip` is in `PATH` then run `pip install cryptography` as administrator. +On Windows, install Python >= 3.4 using the installer provided by the official Python website. Make sure that `pip` is in `PATH`. -On *ix, install the corresponding packages, they should be named `python`, `python-setuptools`, `python-pip` or similar. If your distribution provides it, install `python-cryptography` as well, otherwise run `pip install cryptography` as `root`. +On *ix, install the corresponding packages, they should be named `python`, `python-setuptools`, `python-pip` or similar. You may need to upgrade `pip`. -On Linux distribution having severly outdated packages such as Debian, run `pip install --upgrade pip setuptools pyparsing`. +The preferred way to install and update firmtool is to run `pip install -U git+https://github.com/TuxSH/firmtool.git` directly (with the appropriate permissions), although `python setup.py install` should work as well. -The preferred way to install firmtool is to run `pip install git+https://github.com/TuxSH/firmtool.git` directly (with the appropriate permissions), although `python setup.py install` should work as well. +`firmtool` depends on `pycryptodome` (either as `Crypto` or `Cryptodome`), old `pycrypto` will not work. ## Usage Showing information about a firmware binary: @@ -33,14 +33,14 @@ cd modules for f in *.cxi do ctrtool -p --exefs=exefs.bin $f - + if [ $f = "Process9.cxi" ] then ctrtool -t exefs --exefsdir=exefs exefs.bin > /dev/null else ctrtool -t exefs --exefsdir=exefs --decompresscode exefs.bin > /dev/null fi - + cp exefs/code.bin $(basename -s .cxi $f).bin rm -rf exefs done @@ -48,13 +48,13 @@ cd .. ``` -Building a firmware binary (for example with two sections, an ARM9 and and ARM11 one, with the entrypoints at the start of the respective sections): +Building a firmware binary (for example with two sections, an Arm9 and and Arm11 one, with the entrypoints at the start of the respective sections): ```bash firmtool build test.firm -n 0x08006800 -e 0x1FF80000 -D arm9.bin arm11.bin -A 0x08006800 0x1FF80000 -C NDMA XDMA ``` -Building a firmware binary from an arm9loaderhax.bin payload which doesn't use the ARM11, with a loader supporting the ARM11 entrypoint being 0: +Building a firmware binary from an arm9loaderhax.bin payload which doesn't use the Arm11, with a loader supporting the Arm11 entrypoint being 0: ```bash firmtool build test.firm -n 0x23F00000 -e 0 -D arm9loaderhax.bin -A 0x23F00000 -C NDMA diff --git a/firmtool/__main__.py b/firmtool/__main__.py index 70fb970..b702f55 100755 --- a/firmtool/__main__.py +++ b/firmtool/__main__.py @@ -1,9 +1,9 @@ #!/usr/bin/env python __author__ = "TuxSH" -__copyright__ = "Copyright (c) 2017 TuxSH" +__copyright__ = "Copyright (c) 2017-2020 TuxSH" __license__ = "BSD" -__version__ = "1.3" +__version__ = "1.4" """ Parses, extracts, and builds 3DS firmware files @@ -16,6 +16,15 @@ import sys import os +# Try to import PyCryptodome +try: + import Crypto # type: ignore +except ImportError: + import Cryptodome as Crypto # type: ignore + +from Crypto.Cipher import AES +from Crypto.Hash import SHA256 + # lenny perfectSignatures = { "firm-nand-retail": ( @@ -141,7 +150,7 @@ def extractElf(elfFile): phentsize, phnum, shentsize, shnum, shstrndx = unpack("<16s2H5I6H", hdr) if machine != 40: - raise ValueError("machine type not ARM") + raise ValueError("machine type not Arm") if version != 1: raise ValueError("invalid ELF version") @@ -182,10 +191,6 @@ def extractElf(elfFile): class FirmSectionHeader(object): def check(self): - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import hashes - - off = self.offset if self.copyMethod == 0 and (1 << 20) > self.size >= 0x800+0xA00 and self.address == 0x08006000: if self.sectionData[0x50 : 0x53] == b"K9L": self.guessedType = self.sectionData[0x50 : 0x54].decode("ascii") @@ -197,19 +202,14 @@ def check(self): if self.sectionData[0x100 : 0x104] == b"NCCH": self.guessedType = "Kernel11 modules" - H = hashes.Hash(hashes.SHA256(), backend=default_backend()) - H.update(self.sectionData) - self.hashIsValid = self.hash == H.finalize() + hash = SHA256.new(self.sectionData).digest() + self.hashIsValid = self.hash == hash def doNtrCrypto(self, encrypt = True): - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - iv = pack("<4I", self.offset, self.address, self.size, self.size) - key = spiCryptoKey.get(self.kind.split('-')[-1], "retail") - cipher = Cipher(algorithms.AES(unhexlify(key)), modes.CBC(iv), backend=default_backend()) - obj = cipher.encryptor() if encrypt else cipher.decryptor() - return obj.update(self.sectionData) + obj.finalize() + key = unhexlify(spiCryptoKey.get(self.kind.split('-')[-1], "retail")) + cipher = AES.new(key, AES.MODE_CBC, iv) + return cipher.encrypt(self.sectionData) if encrypt else cipher.decrypt(self.sectionData) def __init__(self, n, kind = "nand-retail", data = None): self.num = n @@ -225,17 +225,12 @@ def __init__(self, n, kind = "nand-retail", data = None): self.check() def setData(self, data): - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import hashes - self.sectionData = data + b'\xFF' * ((512 - (len(data) % 512)) % 512) self.size = len(self.sectionData) self.guessedType = '' self.hashIsValid = True - H = hashes.Hash(hashes.SHA256(), backend=default_backend()) - H.update(self.sectionData) - self.hash = H.finalize() + self.hash = SHA256.new(self.sectionData).digest() def buildHeader(self): return pack("<4I32s", self.offset, self.address, self.size, self.copyMethod, self.hash) @@ -249,7 +244,9 @@ def export(self, basePath, extractModules = False, secretSector = None): while pos < self.size: size = unpack_from("