From f58f05416267b68d64978d8531c223851f1baa28 Mon Sep 17 00:00:00 2001 From: Lillian Skinner <56081713+rvtr@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:19:18 -0400 Subject: [PATCH 1/9] Update main.c --- arm9/src/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arm9/src/main.c b/arm9/src/main.c index 7858c2f..81d6963 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -168,8 +168,10 @@ int main(int argc, char **argv) char path[64]; sprintf(path, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid); unsigned long long tmdSize = getFileSizePath(path); - if (tmdSize > 520) - unlaunchFound = true; + //if (tmdSize > 520) + // unlaunchFound = true; + unlaunchFound = true; + // unlaunch is always true just for testing. //check if launcher patches are enabled const static u32 tidValues[][2] = { @@ -310,4 +312,4 @@ void clearScreen(PrintConsole* screen) { consoleSelect(screen); consoleClear(); -} \ No newline at end of file +} From 907c2fec9bca8b7395f00493a02ed10bc44dfed7 Mon Sep 17 00:00:00 2001 From: rmc Date: Fri, 29 Mar 2024 01:13:08 -0400 Subject: [PATCH 2/9] TAD header reading + file extraction Shockingly it works. --- .gitignore | 1 + Makefile | 6 +- README.md | 3 +- arm9/src/install.c | 11 ++- arm9/src/installmenu.c | 1 + arm9/src/rom.c | 6 ++ arm9/src/tad.c | 200 +++++++++++++++++++++++++++++++++++++++++ arm9/src/tad.h | 9 ++ 8 files changed, 230 insertions(+), 7 deletions(-) create mode 100644 arm9/src/tad.c create mode 100644 arm9/src/tad.h diff --git a/.gitignore b/.gitignore index 05b2a6d..7be604a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.o *.d *.nds +*.tad *.dsi *.elf *.map diff --git a/Makefile b/Makefile index 14231f6..cc75712 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ NITRO_FILES := GAME_TITLE := NAND Title Manager GAME_SUBTITLE1 := JeffRuLz, Pk11 -GAME_CODE := HTMA -GAME_LABEL := NANDTM +GAME_CODE := HTNA +GAME_LABEL := NANDTM_TAD include $(DEVKITARM)/ds_rules @@ -49,7 +49,7 @@ checkarm9: #--------------------------------------------------------------------------------- $(TARGET).dsi : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ - -u "00030004" \ + -u "00030015" \ -g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \ -b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \ $(_ADDFILES) diff --git a/README.md b/README.md index a3e30b7..1518168 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # NAND Title Manager A basic title manager for the Nintendo DSi supporting both hiyaCFW's SDNAND and SysNAND, modified from JeffRuLz's Title Manager for HiyaCFW. +This fork has been modified to allow installing TADs, the DSi's version of a WAD. These are typically signed and encrypted for development units. This makes them impossible to install on a retail unit. My NTM fork should let dev TADs install on retail, retail TADs install on dev, etc. ## WARNING This can modify your internal system NAND! There is *always* a risk of **bricking**, albeit small, when you modify NAND. Please proceed with caution. Having Unlaunch installed is also strongly recommended as it will likely protect you if something manages to go wrong. @@ -30,4 +31,4 @@ This can modify your internal system NAND! There is *always* a risk of **brickin - [Martin Korth (nocash)](https://problemkaputt.de): [GBATEK](https://problemkaputt.de/gbatek.htm) - [JeffRuLz](https://github.com/JeffRuLz): [TMFH](https://github.com/JeffRuLz/TMFH) (what this is a fork of) - [DesperateProgrammer](https://github.com/DesperateProgrammer): [DSi Language Patcher](https://github.com/DesperateProgrammer/DSiLanguagePacher) (working NAND writing code) -- [rvtr](https://github.com/rvtr): Adding support for installing dev titles +- [rvtr](https://github.com/rvtr): Adding support for installing TADs and dev titles diff --git a/arm9/src/install.c b/arm9/src/install.c index e9a9dfd..f106f2c 100644 --- a/arm9/src/install.c +++ b/arm9/src/install.c @@ -8,6 +8,7 @@ #include "nand/ticket0.h" #include "rom.h" #include "storage.h" +#include "tad.h" #include #include #include @@ -383,6 +384,7 @@ bool install(char* fpath, bool systemTitle) if (_patchGameCode(h)) fixHeader = true; + result = decryptTad(fpath); //title id must be one of these if (h->tid_high == 0x00030004 || // DSiWare h->tid_high == 0x00030005 || // "unimportant" system titles @@ -692,10 +694,13 @@ bool install(char* fpath, bool systemTitle) { int result = 0; - if (!romIsCia(fpath)) - result = copyFile(fpath, appPath); - else + if (romIsCia(fpath)) { result = copyFilePart(fpath, 0x3900, fileSize, appPath); + } else if (romIsTad(fpath)) { + result = decryptTad(fpath); + } else { + result = copyFile(fpath, appPath); + } if (result != 0) { diff --git a/arm9/src/installmenu.c b/arm9/src/installmenu.c index 380a12f..5785efa 100644 --- a/arm9/src/installmenu.c +++ b/arm9/src/installmenu.c @@ -191,6 +191,7 @@ static void generateList(Menu* m) strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 || strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 || strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 || + strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0 || strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0) { if (count < m->page * ITEMS_PER_PAGE) diff --git a/arm9/src/rom.c b/arm9/src/rom.c index fab9ddc..2c6e870 100644 --- a/arm9/src/rom.c +++ b/arm9/src/rom.c @@ -286,6 +286,12 @@ bool romIsCia(char const* fpath) return (strstr(fpath, ".cia") != NULL || strstr(fpath, ".CIA") != NULL); } +bool romIsTad(char const* fpath) +{ + if (!fpath) return false; + return (strstr(fpath, ".tad") != NULL || strstr(fpath, ".TAD") != NULL); +} + bool isDsiHeader(tDSiHeader* h) { if (!h) return false; diff --git a/arm9/src/tad.c b/arm9/src/tad.c new file mode 100644 index 0000000..f4a23e1 --- /dev/null +++ b/arm9/src/tad.c @@ -0,0 +1,200 @@ +#include "tad.h" +#include "storage.h" +#include +#include +#include +#include +#include +#include +#include +/* + The common keys for decrypting TADs. + + DEV: Used in most TADs. Anything created with the standard maketad will be dev. + PROD: Used in some TADs for factory tools like PRE_IMPORT and IMPORT. Really uncommon. I only know of 24 prod TADs to have ever been found, and 19 of those haven't ever been released (pleeeeeaaaaase release IMPORT soon). All retail signed and can't be created with any leaked maketads. + DEBUGGER: Used in TwlSystemUpdater TADs. Created with maketad_updater. + + If for whatever reason you want to make TADs, see here: + https://randommeaninglesscharacters.com/dsidev/man/maketad.html +*/ +unsigned char devKey[] = {0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA}; +unsigned char prodKey[] = {0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61}; +unsigned char debuggerKey[] = {0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3}; +typedef struct { + uint32_t hdrSize; + uint16_t tadType; + uint16_t tadVersion; + uint32_t certSize; + uint32_t crlSize; + uint32_t ticketSize; + uint32_t tmdSize; + uint32_t srlSize; + uint32_t metaSize; +} Header; +typedef struct { + uint32_t hdrOffset; + uint32_t certOffset; + uint32_t crlOffset; + uint32_t ticketOffset; + uint32_t tmdOffset; + uint32_t srlOffset; + uint32_t metaOffset; +} Tad; +bool tadSuccess; + +uint32_t swap_endian_u32(uint32_t x) { + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} + +uint16_t swap_endian_u16(uint16_t x) { + return (x >> 8) | (x << 8); +} + +uint32_t round_up( const u32 v, const u32 align ) +{ + u32 r = ((v + align - 1) / align) * align; + return r; +} + +int decryptTad(char const* src) +{ + if (!src) return 1; + + FILE *file = fopen(src, "rb"); + if (file == NULL) { + printf("ERROR: fopen()"); + return 1; + } + + mkdir("sd:/tadtests", 0777); + + // A lot of this is heavily """inspired""" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl) + // This is made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters. + // + // https://github.com/rvtr/TwlIPL/blob/trunk/tools/bin/remaketad.pl + + Header header; + fread(&header, sizeof(Header), 1, file); + iprintf("Parsing TAD header...\n"); + Tad tad; + tad.hdrOffset = 0; + tad.certOffset = round_up(swap_endian_u32(header.hdrSize), 64); + tad.crlOffset = round_up(tad.certOffset + swap_endian_u32(header.certSize), 64); + tad.ticketOffset = round_up(tad.crlOffset + swap_endian_u32(header.crlSize), 64); + tad.tmdOffset = round_up(tad.ticketOffset + swap_endian_u32(header.ticketSize), 64); + tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64); + tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64); + // TODO: Make sure offset calculation and alignment is correct by comparing that to total size + iprintf(" hdrSize: %lu\n", swap_endian_u32(header.hdrSize)); + iprintf(" hdrOffset: %lu\n", tad.hdrOffset); + // 18803 = "Is". This is the standard TAD type. + if (swap_endian_u16(header.tadType) == 18803) { + iprintf(" tadType: 'Is'\n"); + } else { + iprintf(" tadType: UNKNOWN\nERROR: unexpected TAD type\n"); + return 1; + } + iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); + iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); + iprintf(" certOffset: %lu\n", tad.certOffset); + iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize)); + iprintf(" crlOffset: %lu\n", tad.crlOffset); + iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize)); + iprintf(" ticketOffset: %lu\n", tad.ticketOffset); + iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize)); + iprintf(" tmdOffset: %lu\n", tad.tmdOffset); + iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize)); + iprintf(" srlOffset: %lu\n", tad.srlOffset); + iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); + iprintf(" metaOffset: %lu\n", tad.metaOffset); + /* + Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages + that I don't know (python, perl). + + Anyways, the code above is determining the file offsets and sizes within the TAD. + This is done using the 32 byte header. + + Example header from "KART_K04.tad" + + 00000020 49730000 00000E80 00000000 + 000002A4 00000208 000DFC00 00000000 + + Breaking it down... + + Hex | Dec | Meaning + -----------+--------+------------ + 0x00000020 | 32 | Header size + 0x4973 | Is | TAD type + 0x0000 | 0 | TAD version + 0x00000E80 | 3712 | Cert size + 0x00000000 | 0 | Crl size + 0x000002A4 | 676 | Ticket size + 0x00000208 | 520 | TMD size + 0x000DFC00 | 916480 | SRL size + 0x00000000 | 0 | Meta size + + Gee, looks awfully like a WAD header, doesn't it? Turns out TADs are just renamed WADs. Not even changed one bit. + There's literally a commit replacing every instance of WAD with TAD in TwlIPL... + https://github.com/rvtr/TwlIPL/commit/baca65d35d5d62d815c88e6374b895d5b0755277 + */ + + iprintf("================================\nCopying output files...\n"); + // ChatGPT ahh code + // Also more copy pasting because I am a silly idiot! + iprintf(" Copying TMD...\n"); + FILE *tmdFile = fopen("sd:/tadtests/tmd.bin", "wb"); + if (tmdFile == NULL) { + iprintf("ERROR: fopen()\n"); + } + fseek(file, tad.tmdOffset, SEEK_SET); + + for (int i = 0; i < swap_endian_u32(header.tmdSize); i++) { + char ch = fgetc(file); + fputc(ch, tmdFile); + } + fclose(tmdFile); + + iprintf(" Copying ticket...\n"); + FILE *ticketFile = fopen("sd:/tadtests/ticket.bin", "wb"); + if (ticketFile == NULL) { + iprintf("ERROR: fopen()\n"); + } + fseek(file, tad.ticketOffset, SEEK_SET); + + for (int i = 0; i < swap_endian_u32(header.ticketSize); i++) { + char ch = fgetc(file); + fputc(ch, ticketFile); + } + fclose(ticketFile); + + iprintf(" Copying SRL...\n"); + FILE *srlFile = fopen("sd:/tadtests/srl_enc.bin", "wb"); + if (srlFile == NULL) { + iprintf("ERROR: fopen()\n"); + } + fseek(file, tad.srlOffset, SEEK_SET); + + for (int i = 0; i < swap_endian_u32(header.srlSize); i++) { + char ch = fgetc(file); + fputc(ch, srlFile); + } + fclose(srlFile); + fclose(file); + + iprintf("================================\nDone!\n"); + + /* + Try to decrypt the SRL with each key until one works. I don't know a better way to do this (nothing in the TAD would + specify the key needed) so we'll try keys in the order of which ones are more common: + DEV --> DEBUGGER --> PROD + + We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. That region should always be blank. + + https://problemkaputt.de/gbatek.htm#dscartridgeheader + https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 + */ + + //return copyFilePart(src, 0, size, dst); + return 0; + +} \ No newline at end of file diff --git a/arm9/src/tad.h b/arm9/src/tad.h new file mode 100644 index 0000000..aabfb21 --- /dev/null +++ b/arm9/src/tad.h @@ -0,0 +1,9 @@ +#ifndef TAD_H +#define TAD_H + +#include +#include + +int decryptTad(char const* src); + +#endif \ No newline at end of file From 5eab2d4e775f95ffe85d9bbd5ac16d190641ef62 Mon Sep 17 00:00:00 2001 From: rmc Date: Sat, 30 Mar 2024 14:48:19 -0400 Subject: [PATCH 3/9] Get SRL encrypted title key and IV --- arm9/src/tad.c | 182 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 136 insertions(+), 46 deletions(-) diff --git a/arm9/src/tad.c b/arm9/src/tad.c index f4a23e1..86e4acc 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -1,17 +1,22 @@ #include "tad.h" #include "storage.h" +#include "nand/twltool/dsi.h" #include #include #include #include #include #include +#include #include + /* The common keys for decrypting TADs. DEV: Used in most TADs. Anything created with the standard maketad will be dev. - PROD: Used in some TADs for factory tools like PRE_IMPORT and IMPORT. Really uncommon. I only know of 24 prod TADs to have ever been found, and 19 of those haven't ever been released (pleeeeeaaaaase release IMPORT soon). All retail signed and can't be created with any leaked maketads. + PROD: Used in some TADs for factory tools like PRE_IMPORT and IMPORT. Really uncommon. I only know of 24 prod TADs + to have ever been found, and 19 of those haven't ever been released (pleeeeeaaaaase release IMPORT soon). + All retail signed and can't be created with any leaked maketads. DEBUGGER: Used in TwlSystemUpdater TADs. Created with maketad_updater. If for whatever reason you want to make TADs, see here: @@ -56,6 +61,50 @@ uint32_t round_up( const u32 v, const u32 align ) return r; } + /* + This is SRL decryption. Again, just like WADs: + + Common key + title key IV to decrypt title key, title key + content IV to decrypt content + + We have to try each possible common key until we find one that works. I don't know a better way to do this + (nothing in the TAD would specify the key needed) so we'll try keys in the order of which ones are more common: + + DEV --> DEBUGGER --> PROD + + We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. (should always be zerobytes) + + https://problemkaputt.de/gbatek.htm#dscartridgeheader + https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 + */ +void decrypt_title_key(const unsigned char* key, unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, unsigned char* decryptedData) { + aes_context ctx; + unsigned char decryptedBlock[16]; + /* ============================================= */ + iprintf(" Dev common key...\n"); + for (int i = 0; i < sizeof(key); i++) { + iprintf("%02X", key[i]); + } + iprintf("\n"); + iprintf(" Title key IV...\n"); + for (int i = 0; i < sizeof(iv); i++) { + iprintf("%02X", iv[i]); + } + iprintf("\n"); + iprintf(" Enc title key...\n"); + for (int i = 0; i < sizeof(encryptedData); i++) { + iprintf("%02X", encryptedData[i]); + } + iprintf("\n"); + iprintf(" Title key size...\n"); + iprintf("%u", dataSize); + iprintf("\n"); + /* ============================================= */ + aes_setkey_dec(&ctx, key, 256); + aes_crypt_cbc(&ctx, AES_DECRYPT, dataSize, iv, encryptedData, decryptedBlock); + + memcpy(decryptedData, decryptedBlock, dataSize); +} + int decryptTad(char const* src) { if (!src) return 1; @@ -66,18 +115,49 @@ int decryptTad(char const* src) return 1; } - mkdir("sd:/tadtests", 0777); + mkdir("sd:/_nds/tadtests", 0777); - // A lot of this is heavily """inspired""" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl) - // This is made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters. + // A lot of this is heavily "inspired" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl) + // That script was made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters. // // https://github.com/rvtr/TwlIPL/blob/trunk/tools/bin/remaketad.pl + /* + Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages + that I don't know (python, perl). + + Anyways, the code below is determining the file offsets and sizes within the TAD. + This is done using the 32 byte header. + + Example header from "KART_K04.tad" + + 00000020 49730000 00000E80 00000000 + 000002A4 00000208 000DFC00 00000000 + + Breaking it down... + + Hex | Dec | Meaning + -----------+--------+------------ + 0x00000020 | 32 | Header size + 0x4973 | Is | TAD type + 0x0000 | 0 | TAD version + 0x00000E80 | 3712 | Cert size + 0x00000000 | 0 | Crl size + 0x000002A4 | 676 | Ticket size + 0x00000208 | 520 | TMD size + 0x000DFC00 | 916480 | SRL size + 0x00000000 | 0 | Meta size + + Gee, looks awfully like a WAD header, doesn't it? Turns out TADs are just renamed WADs. Not even changed one bit. + There's literally a commit replacing every instance of WAD with TAD in TwlIPL... + https://github.com/rvtr/TwlIPL/commit/baca65d35d5d62d815c88e6374b895d5b0755277 + */ Header header; fread(&header, sizeof(Header), 1, file); - iprintf("Parsing TAD header...\n"); + iprintf("Parsing TAD header:\n"); Tad tad; tad.hdrOffset = 0; + // All offsets in the TAD are aligned to 64 bytes. tad.certOffset = round_up(swap_endian_u32(header.hdrSize), 64); tad.crlOffset = round_up(tad.certOffset + swap_endian_u32(header.certSize), 64); tad.ticketOffset = round_up(tad.crlOffset + swap_endian_u32(header.crlSize), 64); @@ -108,41 +188,18 @@ int decryptTad(char const* src) iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); iprintf(" metaOffset: %lu\n", tad.metaOffset); /* - Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages - that I don't know (python, perl). + Copy the contents of the TAD to the SD card. - Anyways, the code above is determining the file offsets and sizes within the TAD. - This is done using the 32 byte header. + For installing we only need the TMD, ticket, and SRL (obviously lol). - Example header from "KART_K04.tad" - - 00000020 49730000 00000E80 00000000 - 000002A4 00000208 000DFC00 00000000 - - Breaking it down... - - Hex | Dec | Meaning - -----------+--------+------------ - 0x00000020 | 32 | Header size - 0x4973 | Is | TAD type - 0x0000 | 0 | TAD version - 0x00000E80 | 3712 | Cert size - 0x00000000 | 0 | Crl size - 0x000002A4 | 676 | Ticket size - 0x00000208 | 520 | TMD size - 0x000DFC00 | 916480 | SRL size - 0x00000000 | 0 | Meta size - - Gee, looks awfully like a WAD header, doesn't it? Turns out TADs are just renamed WADs. Not even changed one bit. - There's literally a commit replacing every instance of WAD with TAD in TwlIPL... - https://github.com/rvtr/TwlIPL/commit/baca65d35d5d62d815c88e6374b895d5b0755277 + We can skip the cert since that already exists in NAND, and using the TAD's cert could introduce problems like + trying to sign files for dev on a prod console. */ - - iprintf("================================\nCopying output files...\n"); + iprintf("\n--------------------------------\nCopying output files:\n"); // ChatGPT ahh code // Also more copy pasting because I am a silly idiot! iprintf(" Copying TMD...\n"); - FILE *tmdFile = fopen("sd:/tadtests/tmd.bin", "wb"); + FILE *tmdFile = fopen("sd:/_nds/tadtests/tmd.bin", "wb"); if (tmdFile == NULL) { iprintf("ERROR: fopen()\n"); } @@ -155,7 +212,7 @@ int decryptTad(char const* src) fclose(tmdFile); iprintf(" Copying ticket...\n"); - FILE *ticketFile = fopen("sd:/tadtests/ticket.bin", "wb"); + FILE *ticketFile = fopen("sd:/_nds/tadtests/ticket.bin", "wb"); if (ticketFile == NULL) { iprintf("ERROR: fopen()\n"); } @@ -166,9 +223,9 @@ int decryptTad(char const* src) fputc(ch, ticketFile); } fclose(ticketFile); - + /* iprintf(" Copying SRL...\n"); - FILE *srlFile = fopen("sd:/tadtests/srl_enc.bin", "wb"); + FILE *srlFile = fopen("sd:/_nds/tadtests/srl_enc.bin", "wb"); if (srlFile == NULL) { iprintf("ERROR: fopen()\n"); } @@ -179,20 +236,53 @@ int decryptTad(char const* src) fputc(ch, srlFile); } fclose(srlFile); + */ fclose(file); - iprintf("================================\nDone!\n"); + iprintf("\n--------------------------------\n"); /* - Try to decrypt the SRL with each key until one works. I don't know a better way to do this (nothing in the TAD would - specify the key needed) so we'll try keys in the order of which ones are more common: - DEV --> DEBUGGER --> PROD - - We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. That region should always be blank. - - https://problemkaputt.de/gbatek.htm#dscartridgeheader - https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 + Get the title key + IV from the ticket. */ + iprintf("Decrypting SRL:\n\n"); + iprintf(" Finding title key...\n"); + FILE *ticket = fopen("sd:/_nds/tadtests/ticket.bin", "rb"); + unsigned char title_key_enc[16]; + fseek(ticket, 447, SEEK_SET); + fread(title_key_enc, 1, 8, ticket); + iprintf(" Title key found!\n"); + for (int i = 0; i < 16; i++) { + iprintf("%02X", title_key_enc[i]); + } + iprintf("\n"); + iprintf(" Finding title key IV...\n"); + unsigned char title_key_iv[16]; + fseek(ticket, 476, SEEK_SET); + fread(title_key_iv, 1, 8, ticket); + memset(title_key_iv + 8, 0, 8); + iprintf(" Title key IV found!\n"); + for (int i = 0; i < 16; i++) { + iprintf("%02X", title_key_iv[i]); + } + iprintf("\n"); + fclose(ticket); + + iprintf(" Decrypting title key...\n"); + unsigned char decryptedBlock[16]; + /* ============================================= */ + iprintf(" Trying dev common key...\n"); + for (int i = 0; i < sizeof(devKey); i++) { + printf("%02X", devKey[i]); + } + printf("\n"); + /* ============================================= */ + unsigned char title_key_dec[16]; + decrypt_title_key(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), title_key_dec); + printf(" Title key decrypted!\n"); + for (int i = 0; i < sizeof(title_key_dec); i++) { + printf("%02X", title_key_dec[i]); + } + printf("\n"); //return copyFilePart(src, 0, size, dst); return 0; From 7c767e5bcf26f3ac9e46463efafed6f75b304525 Mon Sep 17 00:00:00 2001 From: rmc Date: Sun, 31 Mar 2024 01:02:35 -0400 Subject: [PATCH 4/9] Proper title key decryption but with horrible code I don't care that I have duplicate code and stuff that I'm not using. I just want to commit a working version because this took me a whole fucking day. --- arm9/src/tad.c | 65 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/arm9/src/tad.c b/arm9/src/tad.c index 86e4acc..5ef2b46 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -76,22 +76,22 @@ uint32_t round_up( const u32 v, const u32 align ) https://problemkaputt.de/gbatek.htm#dscartridgeheader https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 */ -void decrypt_title_key(const unsigned char* key, unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, unsigned char* decryptedData) { +void decrypt_title_key(const unsigned char* key, unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, size_t keySize, unsigned char* decryptedData) { aes_context ctx; unsigned char decryptedBlock[16]; /* ============================================= */ iprintf(" Dev common key...\n"); - for (int i = 0; i < sizeof(key); i++) { + for (int i = 0; i < keySize; i++) { iprintf("%02X", key[i]); } iprintf("\n"); iprintf(" Title key IV...\n"); - for (int i = 0; i < sizeof(iv); i++) { + for (int i = 0; i < 16; i++) { iprintf("%02X", iv[i]); } iprintf("\n"); iprintf(" Enc title key...\n"); - for (int i = 0; i < sizeof(encryptedData); i++) { + for (int i = 0; i < dataSize; i++) { iprintf("%02X", encryptedData[i]); } iprintf("\n"); @@ -100,11 +100,60 @@ void decrypt_title_key(const unsigned char* key, unsigned char* iv, const unsign iprintf("\n"); /* ============================================= */ aes_setkey_dec(&ctx, key, 256); - aes_crypt_cbc(&ctx, AES_DECRYPT, dataSize, iv, encryptedData, decryptedBlock); + aes_crypt_cbc(&ctx, AES_DECRYPT, 16, iv, encryptedData, decryptedBlock); memcpy(decryptedData, decryptedBlock, dataSize); + printf(" Title key decrypted!\n"); + for (int i = 0; i < sizeof(decryptedBlock); i++) { + printf("%02X", decryptedBlock[i]); + } + printf("\n"); } +int testroutine() { + const unsigned char iv[16] = { + 0x00, 0x03, 0x00, 0x17, 0x48, 0x4E, 0x41, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + const unsigned char key[16] = { + 0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, + 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA + }; + + const unsigned char encryptedData[16] = { + 0x9D, 0x89, 0x45, 0xB6, 0x12, 0xE9, 0xC1, 0x90, + 0x48, 0x7C, 0x7A, 0x52, 0xED, 0x83, 0xED, 0xEF + }; + + unsigned char decryptedData[16]; + + aes_context ctx; + unsigned char decryptedBlock[16]; + + aes_setkey_dec(&ctx, key, 128); + aes_crypt_cbc(&ctx, AES_DECRYPT, sizeof(encryptedData), iv, encryptedData, decryptedBlock); + + memcpy(decryptedData, decryptedBlock, sizeof(encryptedData)); + + printf("Decrypted Data: "); + for (int i = 0; i < sizeof(decryptedData); i++) { + printf("%02X", decryptedData[i]); + } + printf("\n"); + printf("Decrypted Data: "); + for (int i = 0; i < sizeof(decryptedBlock); i++) { + printf("%02X", decryptedBlock[i]); + } + printf("\n"); + return 0; +} + + + + + + int decryptTad(char const* src) { if (!src) return 1; @@ -249,7 +298,7 @@ int decryptTad(char const* src) FILE *ticket = fopen("sd:/_nds/tadtests/ticket.bin", "rb"); unsigned char title_key_enc[16]; fseek(ticket, 447, SEEK_SET); - fread(title_key_enc, 1, 8, ticket); + fread(title_key_enc, 1, 16, ticket); iprintf(" Title key found!\n"); for (int i = 0; i < 16; i++) { iprintf("%02X", title_key_enc[i]); @@ -277,13 +326,15 @@ int decryptTad(char const* src) printf("\n"); /* ============================================= */ unsigned char title_key_dec[16]; - decrypt_title_key(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), title_key_dec); + + decrypt_title_key(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), sizeof(devKey), title_key_dec); printf(" Title key decrypted!\n"); for (int i = 0; i < sizeof(title_key_dec); i++) { printf("%02X", title_key_dec[i]); } printf("\n"); + testroutine(); //return copyFilePart(src, 0, size, dst); return 0; From aea4371a6a145e119f938302ad55efe17d9edd30 Mon Sep 17 00:00:00 2001 From: rmc Date: Mon, 1 Apr 2024 21:27:36 -0400 Subject: [PATCH 5/9] Full TAD extraction and decryption --- arm9/src/tad.c | 221 ++++++++++++++++++------------------------------- 1 file changed, 80 insertions(+), 141 deletions(-) diff --git a/arm9/src/tad.c b/arm9/src/tad.c index 5ef2b46..1063cd3 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -1,3 +1,10 @@ +/* + A lot of this is heavily "inspired" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl) + That script was made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters. + + https://github.com/rvtr/TwlIPL/blob/trunk/tools/bin/remaketad.pl +*/ + #include "tad.h" #include "storage.h" #include "nand/twltool/dsi.h" @@ -22,9 +29,18 @@ If for whatever reason you want to make TADs, see here: https://randommeaninglesscharacters.com/dsidev/man/maketad.html */ -unsigned char devKey[] = {0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA}; -unsigned char prodKey[] = {0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61}; -unsigned char debuggerKey[] = {0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3}; +const unsigned char devKey[] = { + 0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, + 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA +}; +const unsigned char prodKey[] = { + 0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, + 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61 +}; +const unsigned char debuggerKey[] = { + 0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, + 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3 +}; typedef struct { uint32_t hdrSize; uint16_t tadType; @@ -61,99 +77,12 @@ uint32_t round_up( const u32 v, const u32 align ) return r; } - /* - This is SRL decryption. Again, just like WADs: - - Common key + title key IV to decrypt title key, title key + content IV to decrypt content - - We have to try each possible common key until we find one that works. I don't know a better way to do this - (nothing in the TAD would specify the key needed) so we'll try keys in the order of which ones are more common: - - DEV --> DEBUGGER --> PROD - - We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. (should always be zerobytes) - - https://problemkaputt.de/gbatek.htm#dscartridgeheader - https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 - */ -void decrypt_title_key(const unsigned char* key, unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, size_t keySize, unsigned char* decryptedData) { +void decrypt_cbc(const unsigned char* key, const unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, size_t keySize, unsigned char* decryptedData) { aes_context ctx; - unsigned char decryptedBlock[16]; - /* ============================================= */ - iprintf(" Dev common key...\n"); - for (int i = 0; i < keySize; i++) { - iprintf("%02X", key[i]); - } - iprintf("\n"); - iprintf(" Title key IV...\n"); - for (int i = 0; i < 16; i++) { - iprintf("%02X", iv[i]); - } - iprintf("\n"); - iprintf(" Enc title key...\n"); - for (int i = 0; i < dataSize; i++) { - iprintf("%02X", encryptedData[i]); - } - iprintf("\n"); - iprintf(" Title key size...\n"); - iprintf("%u", dataSize); - iprintf("\n"); - /* ============================================= */ - aes_setkey_dec(&ctx, key, 256); - aes_crypt_cbc(&ctx, AES_DECRYPT, 16, iv, encryptedData, decryptedBlock); - - memcpy(decryptedData, decryptedBlock, dataSize); - printf(" Title key decrypted!\n"); - for (int i = 0; i < sizeof(decryptedBlock); i++) { - printf("%02X", decryptedBlock[i]); - } - printf("\n"); -} - -int testroutine() { - const unsigned char iv[16] = { - 0x00, 0x03, 0x00, 0x17, 0x48, 0x4E, 0x41, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - const unsigned char key[16] = { - 0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, - 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA - }; - - const unsigned char encryptedData[16] = { - 0x9D, 0x89, 0x45, 0xB6, 0x12, 0xE9, 0xC1, 0x90, - 0x48, 0x7C, 0x7A, 0x52, 0xED, 0x83, 0xED, 0xEF - }; - - unsigned char decryptedData[16]; - - aes_context ctx; - unsigned char decryptedBlock[16]; - aes_setkey_dec(&ctx, key, 128); - aes_crypt_cbc(&ctx, AES_DECRYPT, sizeof(encryptedData), iv, encryptedData, decryptedBlock); - - memcpy(decryptedData, decryptedBlock, sizeof(encryptedData)); - - printf("Decrypted Data: "); - for (int i = 0; i < sizeof(decryptedData); i++) { - printf("%02X", decryptedData[i]); - } - printf("\n"); - printf("Decrypted Data: "); - for (int i = 0; i < sizeof(decryptedBlock); i++) { - printf("%02X", decryptedBlock[i]); - } - printf("\n"); - return 0; + aes_crypt_cbc(&ctx, AES_DECRYPT, dataSize, iv, encryptedData, decryptedData); } - - - - - int decryptTad(char const* src) { if (!src) return 1; @@ -164,12 +93,7 @@ int decryptTad(char const* src) return 1; } - mkdir("sd:/_nds/tadtests", 0777); - - // A lot of this is heavily "inspired" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl) - // That script was made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters. - // - // https://github.com/rvtr/TwlIPL/blob/trunk/tools/bin/remaketad.pl + mkdir("sd:/_nds/tadtests/tmp", 0777); /* Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages @@ -201,6 +125,7 @@ int decryptTad(char const* src) There's literally a commit replacing every instance of WAD with TAD in TwlIPL... https://github.com/rvtr/TwlIPL/commit/baca65d35d5d62d815c88e6374b895d5b0755277 */ + Header header; fread(&header, sizeof(Header), 1, file); iprintf("Parsing TAD header:\n"); @@ -236,6 +161,7 @@ int decryptTad(char const* src) iprintf(" srlOffset: %lu\n", tad.srlOffset); iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); iprintf(" metaOffset: %lu\n", tad.metaOffset); + /* Copy the contents of the TAD to the SD card. @@ -244,16 +170,12 @@ int decryptTad(char const* src) We can skip the cert since that already exists in NAND, and using the TAD's cert could introduce problems like trying to sign files for dev on a prod console. */ - iprintf("\n--------------------------------\nCopying output files:\n"); - // ChatGPT ahh code - // Also more copy pasting because I am a silly idiot! - iprintf(" Copying TMD...\n"); - FILE *tmdFile = fopen("sd:/_nds/tadtests/tmd.bin", "wb"); - if (tmdFile == NULL) { - iprintf("ERROR: fopen()\n"); - } - fseek(file, tad.tmdOffset, SEEK_SET); + iprintf("\n--------------------------------\nCopying output files:\n"); + // Sorry for copy pasting, I'll make this a routine later + iprintf(" Copying TMD...\n"); + FILE *tmdFile = fopen("sd:/_nds/tadtests/tmp/tmd.srl", "wb"); + fseek(file, tad.tmdOffset, SEEK_SET); for (int i = 0; i < swap_endian_u32(header.tmdSize); i++) { char ch = fgetc(file); fputc(ch, tmdFile); @@ -261,31 +183,22 @@ int decryptTad(char const* src) fclose(tmdFile); iprintf(" Copying ticket...\n"); - FILE *ticketFile = fopen("sd:/_nds/tadtests/ticket.bin", "wb"); - if (ticketFile == NULL) { - iprintf("ERROR: fopen()\n"); - } + FILE *ticketFile = fopen("sd:/_nds/tadtests/tmp/ticket.srl", "wb"); fseek(file, tad.ticketOffset, SEEK_SET); - for (int i = 0; i < swap_endian_u32(header.ticketSize); i++) { char ch = fgetc(file); fputc(ch, ticketFile); } fclose(ticketFile); - /* + iprintf(" Copying SRL...\n"); - FILE *srlFile = fopen("sd:/_nds/tadtests/srl_enc.bin", "wb"); - if (srlFile == NULL) { - iprintf("ERROR: fopen()\n"); - } + FILE *srlFile = fopen("sd:/_nds/tadtests/tmp/srl_enc.srl", "wb"); fseek(file, tad.srlOffset, SEEK_SET); - for (int i = 0; i < swap_endian_u32(header.srlSize); i++) { char ch = fgetc(file); fputc(ch, srlFile); } fclose(srlFile); - */ fclose(file); iprintf("\n--------------------------------\n"); @@ -293,16 +206,15 @@ int decryptTad(char const* src) /* Get the title key + IV from the ticket. */ + iprintf("Decrypting SRL:\n\n"); iprintf(" Finding title key...\n"); - FILE *ticket = fopen("sd:/_nds/tadtests/ticket.bin", "rb"); + FILE *ticket = fopen("sd:/_nds/tadtests/tmp/ticket.srl", "rb"); unsigned char title_key_enc[16]; fseek(ticket, 447, SEEK_SET); fread(title_key_enc, 1, 16, ticket); iprintf(" Title key found!\n"); - for (int i = 0; i < 16; i++) { - iprintf("%02X", title_key_enc[i]); - } + /* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_enc[i]);} */ iprintf("\n"); iprintf(" Finding title key IV...\n"); unsigned char title_key_iv[16]; @@ -310,31 +222,58 @@ int decryptTad(char const* src) fread(title_key_iv, 1, 8, ticket); memset(title_key_iv + 8, 0, 8); iprintf(" Title key IV found!\n"); - for (int i = 0; i < 16; i++) { - iprintf("%02X", title_key_iv[i]); - } + /* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_iv[i]);} */ iprintf("\n"); fclose(ticket); + /* + This is SRL decryption. AES-CBC. + + Common key + title key IV to decrypt title key, title key + content IV to decrypt content + + We have to try each possible common key until we find one that works. I don't know a better way to do this + (nothing in the TAD would specify the key needed) so we'll try keys in the order of which ones are more common: + + DEV --> DEBUGGER --> PROD + + We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. (should always be zerobytes) + + https://problemkaputt.de/gbatek.htm#dscartridgeheader + https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 + */ + iprintf(" Decrypting title key...\n"); - unsigned char decryptedBlock[16]; - /* ============================================= */ - iprintf(" Trying dev common key...\n"); - for (int i = 0; i < sizeof(devKey); i++) { - printf("%02X", devKey[i]); - } - printf("\n"); - /* ============================================= */ unsigned char title_key_dec[16]; - - decrypt_title_key(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), sizeof(devKey), title_key_dec); + decrypt_cbc(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), sizeof(devKey), title_key_dec); printf(" Title key decrypted!\n"); - for (int i = 0; i < sizeof(title_key_dec); i++) { - printf("%02X", title_key_dec[i]); - } - printf("\n"); + /* for (int i = 0; i < 16; i++) {printf("%02X", title_key_dec[i]);} */ + + iprintf("\n Decrypting SRL chunks...\n"); + unsigned char srl_buffer_enc[16]; + unsigned char srl_buffer_dec[16]; + unsigned char content_iv[] = { + 0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/srl_enc.srl", "rb"); + fseek(srlFile_enc, 0, SEEK_SET); + FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/tad.srl", "wb"); + fseek(srlFile_dec, 0, SEEK_SET); + + for (int i = 0; i < swap_endian_u32(header.srlSize);) { + fread(srl_buffer_enc, 1, 16, srlFile_enc); + /* ===== silly debug ===== */ + iprintf("%ld - %d / %ld", ftell(file), i, swap_endian_u32(header.srlSize)); + /* ===== end of debug ===== */ + + decrypt_cbc(title_key_dec, content_iv, srl_buffer_enc, 16, 16, srl_buffer_dec); + fwrite(srl_buffer_dec, 1, 16, srlFile_dec); + i=i+16; + } + fclose(srlFile_enc); + fclose(srlFile_dec); + iprintf("\nDone everything!"); - testroutine(); //return copyFilePart(src, 0, size, dst); return 0; From 6786ee2ceab7f3fa933cb52c35d24d8543caeea7 Mon Sep 17 00:00:00 2001 From: rmc Date: Tue, 2 Apr 2024 11:39:34 -0400 Subject: [PATCH 6/9] TADs now install! --- Makefile | 8 +- arm9/src/backupmenu.c | 7 +- arm9/src/install.c | 11 +- arm9/src/installmenu.c | 10 +- arm9/src/main.c | 60 ++--------- arm9/src/rom.c | 226 ++++++++++++++++------------------------- arm9/src/rom.h | 3 +- arm9/src/storage.c | 1 + arm9/src/tad.c | 92 +++++++++++++++-- arm9/src/titlemenu.c | 13 +-- icon.bmp | Bin 586 -> 2102 bytes icon.gif | Bin 0 -> 4872 bytes 12 files changed, 198 insertions(+), 233 deletions(-) create mode 100644 icon.gif diff --git a/Makefile b/Makefile index cc75712..673154d 100644 --- a/Makefile +++ b/Makefile @@ -13,11 +13,11 @@ export TOPDIR := $(CURDIR) NITRO_FILES := # These set the information text in the nds file -GAME_TITLE := NAND Title Manager -GAME_SUBTITLE1 := JeffRuLz, Pk11 +GAME_TITLE := TAD Delivery Tool +GAME_SUBTITLE1 := JeffRuLz, Pk11, rmc -GAME_CODE := HTNA -GAME_LABEL := NANDTM_TAD +GAME_CODE := 4TDA +GAME_LABEL := TAD_DELIVERY include $(DEVKITARM)/ds_rules diff --git a/arm9/src/backupmenu.c b/arm9/src/backupmenu.c index d968f5a..6ff6603 100644 --- a/arm9/src/backupmenu.c +++ b/arm9/src/backupmenu.c @@ -157,12 +157,7 @@ static void generateList(Menu* m) } else { - if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0) + if (strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0) { if (count < m->page * ITEMS_PER_PAGE) count += 1; diff --git a/arm9/src/install.c b/arm9/src/install.c index f106f2c..090b49a 100644 --- a/arm9/src/install.c +++ b/arm9/src/install.c @@ -352,7 +352,7 @@ static void _createTicket(tDSiHeader *h, char* ticketPath) } } -bool install(char* fpath, bool systemTitle) +bool install(char* tadPath, bool systemTitle) { bool result = false; @@ -365,6 +365,7 @@ bool install(char* fpath, bool systemTitle) //start installation clearScreen(&bottomScreen); + char* fpath = decryptTad(tadPath); tDSiHeader* h = getRomHeader(fpath); @@ -694,13 +695,7 @@ bool install(char* fpath, bool systemTitle) { int result = 0; - if (romIsCia(fpath)) { - result = copyFilePart(fpath, 0x3900, fileSize, appPath); - } else if (romIsTad(fpath)) { - result = decryptTad(fpath); - } else { - result = copyFile(fpath, appPath); - } + result = copyFile(fpath, appPath); if (result != 0) { diff --git a/arm9/src/installmenu.c b/arm9/src/installmenu.c index 5785efa..059d776 100644 --- a/arm9/src/installmenu.c +++ b/arm9/src/installmenu.c @@ -186,13 +186,7 @@ static void generateList(Menu* m) } else { - if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0 || - strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0) + if (strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0) { if (count < m->page * ITEMS_PER_PAGE) count += 1; @@ -238,7 +232,7 @@ static void printItem(Menu* m) if (m->items[m->cursor].directory) clearScreen(&topScreen); else - printRomInfo(m->items[m->cursor].value); + printTadInfo(m->items[m->cursor].value); } static int subMenu() diff --git a/arm9/src/main.c b/arm9/src/main.c index 81d6963..bc199fa 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -11,8 +11,6 @@ bool programEnd = false; bool sdnandMode = true; bool unlaunchFound = false; bool unlaunchPatches = false; -bool devkpFound = false; -bool launcherDSiFound = false; bool arm7Exiting = false; bool charging = false; u8 batteryLevel = 0; @@ -57,32 +55,30 @@ static int _mainMenu(int cursor) //top screen clearScreen(&topScreen); - iprintf("\t\tNAND Title Manager\n"); + iprintf("\t\tTAD Delivery Tool\n"); iprintf("\t\t\tmodified from\n"); iprintf("\tTitle Manager for HiyaCFW\n"); + iprintf("\tand Nand Title Manager\n"); iprintf("\nversion %s\n", VERSION); iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n"); iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n"); - iprintf("\n\n \x1B[46mgithub.com/Epicpkmn11/NTM/wiki\x1B[47m\n"); + iprintf("\n\n \x1B[46mgithub.com/rvtr/TDT\x1B[47m\n"); iprintf("\x1b[22;0HJeff - 2018-2019"); iprintf("\x1b[23;0HPk11 - 2022-2023"); + iprintf("\x1b[24;0Hrmc - 2024-2024"); //menu Menu* m = newMenu(); setMenuHeader(m, "MAIN MENU"); - char modeStr[32], datamanStr[32], launcherStr[32]; + char modeStr[32]; sprintf(modeStr, "Mode: %s", sdnandMode ? "SDNAND" : "\x1B[41mSysNAND\x1B[47m"); - sprintf(datamanStr, "\x1B[%02omEnable Data Management", devkpFound ? 037 : 047); - sprintf(launcherStr, "\x1B[%02omUninstall region mod", launcherDSiFound ? 047 : 037); addMenuItem(m, modeStr, NULL, 0); addMenuItem(m, "Install", NULL, 0); addMenuItem(m, "Titles", NULL, 0); - addMenuItem(m, "Restore", NULL, 0); + //addMenuItem(m, "Restore", NULL, 0); addMenuItem(m, "Test", NULL, 0); addMenuItem(m, "Fix FAT copy mismatch", NULL, 0); - addMenuItem(m, datamanStr, NULL, 0); - addMenuItem(m, launcherStr, NULL, 0); addMenuItem(m, "\x1B[47mExit", NULL, 0); m->cursor = cursor; @@ -207,19 +203,13 @@ int main(int argc, char **argv) } else if (!unlaunchPatches) { - messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need to\nprovide TMD files or reinstall.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m"); + messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need these\nif you patch any TADs.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m"); } } - //check for dev.kp (Data Management visible) - devkpFound = (access("sd:/sys/dev.kp", F_OK) == 0); - - //check for launcher.dsi (Language patcher) - launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0); - messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n\nIf you have not yet done so,\nyou should make a NAND backup."); - messageBox("If you are following a video\nguide, please stop.\n\nVideo guides for console moddingare often outdated or straight\nup incorrect to begin with.\n\nThe recommended guide for\nmodding your DSi is:\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m\n\nFor more information on using\nNTM, see the official wiki:\n\n\x1B[46mhttps://github.com/Epicpkmn11/\n\t\t\t\t\t\t\t\tNTM/wiki\x1B[47m"); + messageBox("If you are following a video\nguide, please stop.\n\nVideo guides for console moddingare often outdated or straight\nup incorrect to begin with.\n\nThe recommended guide for\nmodding your DSi is:\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m\n\nFor more information on using\nTDT, see the official repo:\n\n\x1B[46mhttps://github.com/rvtr/TDT\x1B[47m"); //main menu int cursor = 0; @@ -231,7 +221,6 @@ int main(int argc, char **argv) { case MAIN_MENU_MODE: sdnandMode = !sdnandMode; - devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0); break; case MAIN_MENU_INSTALL: @@ -241,11 +230,11 @@ int main(int argc, char **argv) case MAIN_MENU_TITLES: titleMenu(); break; - + /* case MAIN_MENU_BACKUP: backupMenu(); break; - + */ case MAIN_MENU_TEST: testMenu(); break; @@ -259,35 +248,6 @@ int main(int argc, char **argv) } break; - case MAIN_MENU_DATA_MANAGEMENT: - if (!devkpFound && (choiceBox("Make Data Management visible\nin System Settings?") == YES) && (sdnandMode || nandio_unlock_writing())) - { - //ensure sys folder exists - if(access(sdnandMode ? "sd:/sys" : "nand:/sys", F_OK) != 0) - mkdir(sdnandMode ? "sd:/sys" : "nand:/sys", 0777); - - //create empty file - FILE *file = fopen(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", "wb"); - fclose(file); - - if(!sdnandMode) - nandio_lock_writing(); - devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0); - messageBox("Data Management is now visible\nin System Settings.\n"); - } - break; - case MAIN_MENU_LANGUAGE_PATCHER: - if (launcherDSiFound && (choiceBox("Uninstall the language patched\nDSi Menu? (launcher.dsi)") == YES) && nandio_unlock_writing()) - { - //delete launcher.dsi - remove("nand:/launcher.dsi"); - - nandio_lock_writing(); - launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0); - messageBox("The language patched DSi Menu\nhas been removed.\n"); - } - break; - case MAIN_MENU_EXIT: programEnd = true; break; diff --git a/arm9/src/rom.c b/arm9/src/rom.c index 2c6e870..5fb99e3 100644 --- a/arm9/src/rom.c +++ b/arm9/src/rom.c @@ -1,6 +1,7 @@ #include "rom.h" #include "main.h" #include "storage.h" +#include "tad.h" #include #include #include @@ -19,11 +20,7 @@ tDSiHeader* getRomHeader(char const* fpath) if (h) { - if (romIsCia(fpath)) - fseek(f, 0x3900, SEEK_SET); - else - fseek(f, 0, SEEK_SET); - + fseek(f, 0, SEEK_SET); fread(h, sizeof(tDSiHeader), 1, f); } @@ -50,11 +47,7 @@ tNDSBanner* getRomBanner(char const* fpath) if (b) { - if (romIsCia(fpath)) - fseek(f, 0x3900, SEEK_SET); - else - fseek(f, 0, SEEK_SET); - + fseek(f, 0, SEEK_SET); fseek(f, h->ndshdr.bannerOffset, SEEK_CUR); fread(b, sizeof(tNDSBanner), 1, f); } @@ -146,106 +139,99 @@ void printRomInfo(char const* fpath) tDSiHeader* h = getRomHeader(fpath); tNDSBanner* b = getRomBanner(fpath); - if (!isDsiHeader(h)) + if (!b) { - iprintf("Could not read dsi header.\n"); + iprintf("Could not read banner.\n"); } else { - if (!b) + //proper title { - iprintf("Could not read banner.\n"); + char gameTitle[128+1]; + getGameTitle(b, gameTitle, true); + + iprintf("%s\n\n", gameTitle); } - else + + //file size { - //proper title - { - char gameTitle[128+1]; - getGameTitle(b, gameTitle, true); - - iprintf("%s\n\n", gameTitle); - } - - //file size - { - iprintf("Size: "); - unsigned long long romSize = getRomSize(fpath); - printBytes(romSize); - //size in blocks, rounded up - iprintf(" (%lld blocks)\n", ((romSize / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK); - } - - iprintf("Label: %.12s\n", h->ndshdr.gameTitle); - iprintf("Game Code: %.4s\n", h->ndshdr.gameCode); - - //system type - { - iprintf("Unit Code: "); - - switch (h->ndshdr.unitCode) - { - case 0: iprintf("NDS"); break; - case 2: iprintf("NDS+DSi"); break; - case 3: iprintf("DSi"); break; - default: iprintf("unknown"); - } - - iprintf("\n"); - } - - //application type - { - iprintf("Program Type: "); - - switch (h->ndshdr.reserved1[7]) - { - case 0x3: iprintf("Normal"); break; - case 0xB: iprintf("Sys"); break; - case 0xF: iprintf("Debug/Sys"); break; - default: iprintf("unknown"); - } - - iprintf("\n"); - } - - //DSi title ids - { - if (h->tid_high == 0x00030004 || - h->tid_high == 0x00030005 || - h->tid_high == 0x00030011 || // TID for software in TWL SDK - h->tid_high == 0x00030015 || - h->tid_high == 0x00030017 || - h->tid_high == 0x00030000) - { - iprintf("Title ID: %08x %08x\n", (unsigned int)h->tid_high, (unsigned int)h->tid_low); - } - } - - //print full file path - iprintf("\n%s\n", fpath); - - //print extra files - int extensionPos = strrchr(fpath, '.') - fpath; - char temp[PATH_MAX]; - strcpy(temp, fpath); - strcpy(temp + extensionPos, ".tmd"); - //DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520 - int tmdSize = getFileSizePath(temp); - if (access(temp, F_OK) == 0) - printf("\t\x1B[%om%s\n\x1B[47m", (tmdSize == 520 || tmdSize == 2312) ? 047 : 041, strrchr(temp, '/') + 1); - - strcpy(temp + extensionPos, ".pub"); - if (access(temp, F_OK) == 0) - printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->public_sav_size) ? 047 : 041, strrchr(temp, '/') + 1); - - strcpy(temp + extensionPos, ".prv"); - if (access(temp, F_OK) == 0) - printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->private_sav_size) ? 047 : 041, strrchr(temp, '/') + 1); - - strcpy(temp + extensionPos, ".bnr"); - if (access(temp, F_OK) == 0) - printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1); + iprintf("Size: "); + unsigned long long romSize = getRomSize(fpath); + printBytes(romSize); + //size in blocks, rounded up + iprintf(" (%lld blocks)\n", ((romSize / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK); } + + iprintf("Label: %.12s\n", h->ndshdr.gameTitle); + iprintf("Game Code: %.4s\n", h->ndshdr.gameCode); + + //system type + { + iprintf("Unit Code: "); + + switch (h->ndshdr.unitCode) + { + case 0: iprintf("NDS"); break; + case 2: iprintf("NDS+DSi"); break; + case 3: iprintf("DSi"); break; + default: iprintf("unknown"); + } + + iprintf("\n"); + } + + //application type + { + iprintf("Program Type: "); + + switch (h->ndshdr.reserved1[7]) + { + case 0x3: iprintf("Normal"); break; + case 0xB: iprintf("Sys"); break; + case 0xF: iprintf("Debug/Sys"); break; + default: iprintf("unknown"); + } + + iprintf("\n"); + } + + //DSi title ids + { + if (h->tid_high == 0x00030004 || + h->tid_high == 0x00030005 || + h->tid_high == 0x00030011 || // TID for software in TWL SDK + h->tid_high == 0x00030015 || + h->tid_high == 0x00030017 || + h->tid_high == 0x00030000) + { + iprintf("Title ID: %08x %08x\n", (unsigned int)h->tid_high, (unsigned int)h->tid_low); + } + } + + //print full file path + iprintf("\n%s\n", fpath); + + //print extra files + int extensionPos = strrchr(fpath, '.') - fpath; + char temp[PATH_MAX]; + strcpy(temp, fpath); + strcpy(temp + extensionPos, ".tmd"); + //DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520 + int tmdSize = getFileSizePath(temp); + if (access(temp, F_OK) == 0) + printf("\t\x1B[%om%s\n\x1B[47m", (tmdSize == 520 || tmdSize == 2312) ? 047 : 041, strrchr(temp, '/') + 1); + + strcpy(temp + extensionPos, ".pub"); + if (access(temp, F_OK) == 0) + printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->public_sav_size) ? 047 : 041, strrchr(temp, '/') + 1); + + strcpy(temp + extensionPos, ".prv"); + if (access(temp, F_OK) == 0) + printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->private_sav_size) ? 047 : 041, strrchr(temp, '/') + 1); + + strcpy(temp + extensionPos, ".bnr"); + if (access(temp, F_OK) == 0) + printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1); } free(b); @@ -261,42 +247,10 @@ unsigned long long getRomSize(char const* fpath) if (f) { - //cia - if (romIsCia(fpath)) - { - unsigned char bytes[4] = { 0 }; - fseek(f, 0x38D0, SEEK_SET); - fread(bytes, 4, 1, f); - size = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; - } - else - { - fseek(f, 0, SEEK_END); - size = ftell(f); - } + fseek(f, 0, SEEK_END); + size = ftell(f); } fclose(f); return size; -} - -bool romIsCia(char const* fpath) -{ - if (!fpath) return false; - return (strstr(fpath, ".cia") != NULL || strstr(fpath, ".CIA") != NULL); -} - -bool romIsTad(char const* fpath) -{ - if (!fpath) return false; - return (strstr(fpath, ".tad") != NULL || strstr(fpath, ".TAD") != NULL); -} - -bool isDsiHeader(tDSiHeader* h) -{ - if (!h) return false; - - u16 crc16 = swiCRC16(0xFFFF, h, 0x15E); - - return h->ndshdr.headerCRC16 == crc16; } \ No newline at end of file diff --git a/arm9/src/rom.h b/arm9/src/rom.h index 9fc07d5..122cdf7 100644 --- a/arm9/src/rom.h +++ b/arm9/src/rom.h @@ -17,7 +17,6 @@ void printRomInfo(char const* fpath); unsigned long long getRomSize(char const* fpath); -bool romIsCia(char const* fpath); -bool isDsiHeader(tDSiHeader* h); +bool romIsSrl(char const* fpath); #endif \ No newline at end of file diff --git a/arm9/src/storage.c b/arm9/src/storage.c index 827c286..7234a1a 100644 --- a/arm9/src/storage.c +++ b/arm9/src/storage.c @@ -440,6 +440,7 @@ int getMenuSlotsFree() const char* dirs[] = { "00030004", "00030005", + "0003000f", "00030015", "00030017" }; diff --git a/arm9/src/tad.c b/arm9/src/tad.c index 1063cd3..d3636af 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -7,6 +7,8 @@ #include "tad.h" #include "storage.h" +#include "rom.h" +#include "main.h" #include "nand/twltool/dsi.h" #include #include @@ -41,6 +43,7 @@ const unsigned char debuggerKey[] = { 0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3 }; + typedef struct { uint32_t hdrSize; uint16_t tadType; @@ -174,7 +177,7 @@ int decryptTad(char const* src) iprintf("\n--------------------------------\nCopying output files:\n"); // Sorry for copy pasting, I'll make this a routine later iprintf(" Copying TMD...\n"); - FILE *tmdFile = fopen("sd:/_nds/tadtests/tmp/tmd.srl", "wb"); + FILE *tmdFile = fopen("sd:/_nds/tadtests/tmp/temp.tmd", "wb"); fseek(file, tad.tmdOffset, SEEK_SET); for (int i = 0; i < swap_endian_u32(header.tmdSize); i++) { char ch = fgetc(file); @@ -183,7 +186,7 @@ int decryptTad(char const* src) fclose(tmdFile); iprintf(" Copying ticket...\n"); - FILE *ticketFile = fopen("sd:/_nds/tadtests/tmp/ticket.srl", "wb"); + FILE *ticketFile = fopen("sd:/_nds/tadtests/tmp/temp.tik", "wb"); fseek(file, tad.ticketOffset, SEEK_SET); for (int i = 0; i < swap_endian_u32(header.ticketSize); i++) { char ch = fgetc(file); @@ -192,7 +195,7 @@ int decryptTad(char const* src) fclose(ticketFile); iprintf(" Copying SRL...\n"); - FILE *srlFile = fopen("sd:/_nds/tadtests/tmp/srl_enc.srl", "wb"); + FILE *srlFile = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "wb"); fseek(file, tad.srlOffset, SEEK_SET); for (int i = 0; i < swap_endian_u32(header.srlSize); i++) { char ch = fgetc(file); @@ -209,7 +212,7 @@ int decryptTad(char const* src) iprintf("Decrypting SRL:\n\n"); iprintf(" Finding title key...\n"); - FILE *ticket = fopen("sd:/_nds/tadtests/tmp/ticket.srl", "rb"); + FILE *ticket = fopen("sd:/_nds/tadtests/tmp/temp.tik", "rb"); unsigned char title_key_enc[16]; fseek(ticket, 447, SEEK_SET); fread(title_key_enc, 1, 16, ticket); @@ -251,19 +254,22 @@ int decryptTad(char const* src) iprintf("\n Decrypting SRL chunks...\n"); unsigned char srl_buffer_enc[16]; unsigned char srl_buffer_dec[16]; + // Should be fine as a hardcoded string. Content IV is based off of the content index. (index # with zerobyte padding) + // All TADs I've only ever a single content. It might be a good idea to add something down the line in case a weird + // TAD pops up, but until then this should do. unsigned char content_iv[] = { 0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/srl_enc.srl", "rb"); + FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "rb"); fseek(srlFile_enc, 0, SEEK_SET); - FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/tad.srl", "wb"); + FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/temp.srl", "wb"); fseek(srlFile_dec, 0, SEEK_SET); for (int i = 0; i < swap_endian_u32(header.srlSize);) { fread(srl_buffer_enc, 1, 16, srlFile_enc); /* ===== silly debug ===== */ - iprintf("%ld - %d / %ld", ftell(file), i, swap_endian_u32(header.srlSize)); + iprintf("%ld - %d / %ld\n", ftell(file), i, swap_endian_u32(header.srlSize)); /* ===== end of debug ===== */ decrypt_cbc(title_key_dec, content_iv, srl_buffer_enc, 16, 16, srl_buffer_dec); @@ -272,9 +278,75 @@ int decryptTad(char const* src) } fclose(srlFile_enc); fclose(srlFile_dec); - iprintf("\nDone everything!"); + iprintf("\nReady to install!"); + return "sd:/_nds/tadtests/tmp/temp.srl"; +} - //return copyFilePart(src, 0, size, dst); - return 0; +void printTadInfo(char const* fpath) +{ + clearScreen(&topScreen); + if (!fpath) return; + + + uint32_t srlCompany; + uint32_t srlTidLow; + uint32_t srlTidHigh; + unsigned char srlVerLow = 0x00; + unsigned char srlVerHigh = 0x00; + + FILE *file = fopen(fpath, "rb"); + Header header; + fread(&header, sizeof(Header), 1, file); + iprintf("Parsing TAD header:\n"); + Tad tad; + tad.hdrOffset = 0; + // All offsets in the TAD are aligned to 64 bytes. + tad.certOffset = round_up(swap_endian_u32(header.hdrSize), 64); + tad.crlOffset = round_up(tad.certOffset + swap_endian_u32(header.certSize), 64); + tad.ticketOffset = round_up(tad.crlOffset + swap_endian_u32(header.crlSize), 64); + tad.tmdOffset = round_up(tad.ticketOffset + swap_endian_u32(header.ticketSize), 64); + tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64); + tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64); + // Get info from TMD. + fseek(file, swap_endian_u32(tad.tmdOffset)+396, SEEK_SET); + fread(srlTidHigh, 1, 4, file); + fread(srlTidLow, 1, 4, file); + fseek(file, swap_endian_u32(tad.tmdOffset)+408, SEEK_SET); + fread(srlCompany, 1, 4, file); + fseek(file, swap_endian_u32(tad.tmdOffset)+476, SEEK_SET); + fread(srlVerHigh, 1, 1, file); + fread(srlVerLow, 1, 1, file); + + iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); + iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); + iprintf(" certOffset: %lu\n", tad.certOffset); + iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize)); + iprintf(" crlOffset: %lu\n", tad.crlOffset); + iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize)); + iprintf(" ticketOffset: %lu\n", tad.ticketOffset); + iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize)); + iprintf(" tmdOffset: %lu\n", tad.tmdOffset); + iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize)); + iprintf(" srlOffset: %lu\n", tad.srlOffset); + iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); + iprintf(" metaOffset: %lu\n", tad.metaOffset); + + //proper title + // TODO! Might not be possible though :(( + + //size in blocks, rounded up + iprintf("Size: (%ld blocks)\n", ((swap_endian_u32(header.srlSize) / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK); + + iprintf("Game Code: %c\n", (char)srlTidLow); + + iprintf("Game Version: %u.%u (NUS: v%u)\n", srlVerHigh * 256, srlVerLow, (srlVerHigh * 256) + srlVerLow); + + iprintf("Company Code: %c (%lu)\n", (char)srlCompany, srlCompany); + + // Print program type based on TID high? + iprintf("Title ID: %08lx %08lx", srlTidHigh, srlTidLow); + + //print full file path + iprintf("\n%s\n", fpath); } \ No newline at end of file diff --git a/arm9/src/titlemenu.c b/arm9/src/titlemenu.c index cb8d18d..1795bde 100644 --- a/arm9/src/titlemenu.c +++ b/arm9/src/titlemenu.c @@ -92,6 +92,7 @@ static void generateList(Menu* m) const char* dirs[] = { "00030004", "00030005", + //"0003000f", "00030015", "00030017" }; @@ -100,17 +101,12 @@ static void generateList(Menu* m) { // 00030004 NULL //nothing blacklisted }, - { // 00030005 - "484e44", // DS Download Play - "484e45", // PictoChat - "484e49", // Nintendo DSi Camera - "484e4a", // Nintendo Zone - "484e4b", // Nintendo DSi Sound + { // 0003000f + "484e43", // WiFi Firmware NULL }, { // 00030015 "484e42", // System Settings - "484e46", // Nintendo DSi Shop NULL }, { // 00030017 @@ -159,8 +155,7 @@ static void generateList(Menu* m) blacklisted = true; // also blacklist specific all-region titles - if ((strcmp("484e4441", ent->d_name) == 0) || // Download Play - (strcmp("484e4541", ent->d_name) == 0) || // PictoChat + if ((strcmp("484e4341", ent->d_name) == 0) || // WiFi Firmware (strcmp("34544e41", ent->d_name) == 0)) // TwlNmenu blacklisted = true; } diff --git a/icon.bmp b/icon.bmp index eba498ddc57b857754817b3437dbea6c2ffc644a..fbdaba4b657147af5b38953b7bc56582a5c2dbc9 100644 GIT binary patch literal 2102 zcmeH|u~Ee^42Gpd_De{|1XLUqul6^cYKn8dh z<=Khy{jwvSeD5wDJ2E>8(G8xi%3vx0?=OcuhyiuLQc5&U6OH3Y%d*hJ;}gyEOm8nt zAFnOlr(61ZzozB$IS1_XUV*&=XHo&)Fr>&$ZO}Rwxb08YHt^-g;BwRVqCZ@?gBxYG zo9EDg2N(|nhahs|2-Yjd1FN9sMvceF7iQSP;;~cyhP*#GLxCyt_4h{@cO6S!-{q_8 zt+_8>t2pxE@OQjLJC1qeSVBBbxvK literal 586 zcmd6kF%H5o3`Jc8E5hOe)DtT*D+AP15eMLQj7VIA1FMQ%zqG{*{o{pT`s0rd1Z-w$+sAL;SB(J%}t5;50R%*-+s7(5X)ShSR=C0hz? zmCbwbgwdnlbPNgarpS8~;aCdBQ*l~|2{o{wV=80r({2g~)lW#=u{Va@?ftGNgjKcR_9hX4Qo diff --git a/icon.gif b/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..548f48edb6bac59ee3643d626bd90d79cea1e331 GIT binary patch literal 4872 zcmd7WcT|(-9tUvVya6)QqbL-VVo(TS$D$^p?F9uvs{&S0P!tqV0Tl#gk3a|`5W?OY zWN!t8ut7$cvchN(8RfF1uG)HUxlf>VUFkaYoaUUI_dPl9Klz;RZ#=Kop@U?S362lP zXUE4Uxr9BYdE%P`>bja=C=p?Z5B&5a5{&^@1@HwA;rKkk_2|)~*9VteTwJbQySB8n zWT1ci;lqbYM-T9x`M(cxWWBpucIBxnCX?dE1jfZg{tJs6G-s=bM=BfC)>`L%w>x@E zO`;XS@`Y8wqlL>X>9v9f#?*2RbNR21y+TM7^<+n4xTCZ zf8Ag8{F<|!h5Id!uZ+DcZ~Hj-QH};s15LfP8KGf85h0=eu~AMj3XxI%1gT8NbkST0 zX`7xYMWTlzQTNL$P`d~vF({?`{0)t$-EfT=p`oL(tGW7OLPwoq-C)b!@c!=Z7W>0( zFn5siQ2T4GG8U@}R@hs$wOE$)5+^5=_urpiZ&)1&Uo;MY0MqXpA;*@1^rwxMfi9@U zYOEJHZ^E)tXfnfm2JdQf)@j)z{6tM*bKrwhRxdRu`@qzc4$hev^lrK)xq8A2KBPzep6p7`Vkk#S(<-v$Z6*F zNU!7JKCu_Wf)ZnyN)eHCIjLONY|%o9-#$B6N;NQrA6;2hjV8;L$HQo1r9ew7dJo)S zF4xl2+Sgw1m(tUu+{EqB8RByLTRWVObw3!NYi4hP3NdjUY4HI&DS?RQo{CBU634Rn2Dn)CP&eHT~zxc$8?1T3573?MvRU>*Kaki7`w5j z)KzT&Ywuv_WN^rYV(MsXsN{3W&%&Ew#WV^I3G}49M|zlt$Hw_a2PGw62y;tMNyy61 zNQLDi(wQ>b%X~^iSrC#^Qnr1MeHaqc+|r66$ut&X6r1^bdNE|UGofdsmpjlA+ST1R z+RHgP-aj?Lp=l3KOmO?BPbM_6+3iI?v`f_KPM}^%dQs1zHg5J`JyJjlvW{BG6MzF) zfDB*(vJZ+6`Ta5$nJrtsgcDtJV3A*3FUNAW9ANzJ~?rz~C$e10k-=E~)>j!w{3 zn_K1w9jKPBwxiKvo9=4lS|G` z#7kC1vBboXp{y#&-O>37B(|fo3%e8F>V{S5;2#{q?n3l2@q^<-li&7+vBwAG2Nt-x zGYbn-LtMIM|Fah(i@I*@$(k__=0N3AhpSl*tf8_?{q$g53#{& zzjbq6EnRWz-LuNIKGE@6Q!3Iy8O}BfBuUphuV?LUd}U<~VZ1R1DCV-xr)a+BmZl6Z z=%S8=UTBzea11rhEWtQ7`BJLAQ+i5NNK%e|rek479uZ$qm`dIz83xo~2CE83j4vg_ z%W6vGRy!=A0}0fW5q;6*6*cS3@c|j2rou+dPEBz=zwBRFm>gL=Jcis!7r7R#Cf!38 zf2M0Y4FI=q7d6$R zHR-zp!~(90GaND!3t%+5ZrfpJQE1hU_V=@~ArrZ{BsC&AL6<-|gj$ECT~)!{yOP}; za-21(3VR=TWD$2k@5r-_xn|xj*7qgd)p9bgosf0B?Bb?HGNl@A0)#$f2f7m@JTS;F z^fD$61G(E>ih<(1jeQMc{0)*aL$Y;l=cg3r2H^`ziUAvy$L7dmyIK5}E$L(5-uG+flLMRkP%gNA7 z&)e6LO!eYLkREa=EFwNK+LWOKVeVickDIZKw2a{7lvKzdi-Zp?I#&YNTph*g;On)3 z%`6zkws&-5l}N26K&^eHN_(RR9}i7@+hY{`7!>I|SEX-ea*}JKVN7~DKR2~sMZGOi zJp^CVu;x>wZSjv4>BrLcA)8IN$@yW$v^$xy<+X_Fpci_OpdjQzwsAk^L8ZC*`k_!> zx2-SvLO~%~u7-g;zZmauTPQIjDTNV%$-_XQB)m^rTTb!Ml0BEfp9f@e-4tTTo z1qnIaoZ5awf%p0?2Y+*pzL^MM9Q+NP?a!h8G$^4wJL*g_b<-8G@-U?8@c6vRHh%sL zrl+3(h+iNj$ki=6Hr$uW!_y++gB_ua!pv*{rUzllF%U(p8ZIl3yIX=ImO%#bzjih= z=G%!7znryRqyB;6$sw-F*v!n_6oSY0`02pNf~Goej)JWDUlsTNrL$H4wax~14=_vL z-TU8NKi(gt?+=8(%4Wt>Goq1gd~a!a2T(=mo(vvgm`_Ah3&Kh00kV}1kQ17F@ftL2+wk`sMoy9_7oQbIs#J-t%)$xw0j%nFVa_aUF zj6dS;D^^Etd%CiIoc>em2M~L=PTzBz`xKq+ZIQm8w)fm3DS*wECYBCgzmo#MMB)UC zith?4k@8U}h%QJdLe|s@DkJV!R94lr*48%WqtUD|c9l$9gSQOS-NQumwR0L(d6c7L zZS{@A-z`p$!uMX3BJfBE`yVU|SxPQ=sykNym74%5*07n0bVvQs#p7MDceTJh@*e}{ z6$bwb#zgSAs;YTh#-ys|>ejk49T9pnONP~3FAI_gb@leLtLyi1hE&VDM{D~D5E?y9 zP%pf94{*uMEf7k@b0ma6mm}!%EjH4=LCLN;tIhPF{&}zuw0K}EC-mjx)Ti%fzm>4x z0rLyvdG*w*boTTBtD71RDmX*6eFTwR#9y3vkSkJK7oYyLC4EUmNU97c`H^FQ2l(UQ zMb8KOEQl4{jaonQGcij#tCBpcCio+Gsq+{|L}ccTiKSkY?6f`VrX=2B>7TMK3M{SQ zG%iKaZl1@`F6}t9>u^j>pzhvWe13=YCXMIEYYol}=9to=Bwr51Ewpw%|Llha&Dk{P zldePy%C&#FdC~M8sJH2!PIN6#2|uQT@@6qh*9{40w8DH}RdLtD`|V%u$;{2oM@Y!$Z%P$e QJ1s1ZNqDk2kLKI^Kf!?rA^-pY literal 0 HcmV?d00001 From 4e84d280f636c98f5082a97e181b7713757472f9 Mon Sep 17 00:00:00 2001 From: rmc Date: Thu, 4 Apr 2024 02:41:00 -0400 Subject: [PATCH 7/9] Fix mismatched TMD app name and installed app name --- .github/workflows/build.yml | 4 +- arm9/src/install.c | 51 ++++----- arm9/src/main.c | 14 +-- arm9/src/tad.c | 204 +++++++++++++++--------------------- 4 files changed, 115 insertions(+), 158 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61f3fff..01cdc66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build NAND Title Manager +name: Build TAD Delivery Tool on: push: @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v1 - name: Setup environment run: git config --global safe.directory '*' - - name: Build NAND Title Manager + - name: Build TAD Delivery Tool run: make - name: Publish build to GH Actions uses: actions/upload-artifact@v2 diff --git a/arm9/src/install.c b/arm9/src/install.c index 090b49a..065d9a4 100644 --- a/arm9/src/install.c +++ b/arm9/src/install.c @@ -388,9 +388,9 @@ bool install(char* tadPath, bool systemTitle) result = decryptTad(fpath); //title id must be one of these if (h->tid_high == 0x00030004 || // DSiWare - h->tid_high == 0x00030005 || // "unimportant" system titles - h->tid_high == 0x00030011 || // SRLs in the TWL SDK - h->tid_high == 0x00030015 || // system titles + h->tid_high == 0x00030005 || // "Unimportant" system titles + h->tid_high == 0x0003000f || // Data titles + h->tid_high == 0x00030015 || // System titles h->tid_high == 0x00030017) // Launcher {} else @@ -403,19 +403,6 @@ bool install(char* tadPath, bool systemTitle) goto error; } - //patch dev titles to system titles on SysNAND. - // - //software released through the TWL SDK usually comes as a TAD and an SRL - //things like NandFiler have a TAD with a TID of 0x00030015 and an SRL with 0x00030011 - //the TAD is the installable version, so I'm assuming that 0x00030015 is what the console wants to see on NAND - //this changes the SRL TID accordingly - //not entirely sure why there's even any difference. I think the installed TAD and SRL the same as each other (minus the TID) - if(!sdnandMode && h->tid_high == 0x00030011) - { - h->tid_high = 0x00030015; - fixHeader = true; - } - //offer to patch system titles to normal DSiWare on SysNAND if(!sdnandMode && h->tid_high != 0x00030004 && h->tid_high != 0x00030017) //do not allow patching home menus to be normal DSiWare! This will trigger "ERROR! - 0x0000000000000008 HWINFO_SECURE" on prototype launchers. May also cause issues on the prod versions. { @@ -453,28 +440,17 @@ bool install(char* tadPath, bool systemTitle) u32 tidLow = (h->tid_low & 0xFFFFFF00); if (!sdnandMode && ( (h->tid_high == 0x00030005 && ( - tidLow == 0x484e4400 || // DS Download Play - tidLow == 0x484e4500 || // PictoChat tidLow == 0x484e4900 || // Nintendo DSi Camera tidLow == 0x484e4a00 || // Nintendo Zone tidLow == 0x484e4b00 // Nintendo DSi Sound - )) || (h->tid_high == 0x00030011 && ( - tidLow == 0x30535500 || // Twl SystemUpdater - tidLow == 0x34544e00 || // TwlNmenu - tidLow == 0x54574c00 // TWL EVA )) || (h->tid_high == 0x00030015 && ( tidLow == 0x484e4200 || // System Settings - tidLow == 0x484e4600 || // Nintendo DSi Shop - tidLow == 0x34544e00 // TwlNmenu + tidLow == 0x484e4600 // Nintendo DSi Shop )) || (h->tid_high == 0x00030017 && ( tidLow == 0x484e4100 // Launcher ))) && ( (h->tid_low & 0xFF) == region || // Only blacklist console region, or the following programs that have all-region codes: - h->tid_low == 0x484e4541 || // PictoChat - h->tid_low == 0x484e4441 || // Download Play - h->tid_low == 0x30535541 || // Twl SystemUpdater (iirc one version fits in NAND) h->tid_low == 0x34544e41 || // TwlNmenu (blocking due to potential to uninstall system titles) - h->tid_low == 0x54574c41 || // TWL EVA region == 0 //if the region check failed somehow, blacklist everything )) { @@ -498,6 +474,9 @@ bool install(char* tadPath, bool systemTitle) { const char system[] = "\x1B[41mWARNING:\x1B[47m This is a system app,\ninstalling it is potentially\nmore risky than regular DSiWare.\n\x1B[33m"; const char areYouSure[] = "Are you sure you want to install\n"; + clearScreen(&topScreen); // Top screen breaks after this for some reason. + printTadInfo(tadPath); + clearScreen(&bottomScreen); char* msg = (char*)malloc(strlen(system) + strlen(areYouSure) + strlen(fpath) + 2); if (sdnandMode || h->tid_high == 0x00030004) sprintf(msg, "%s%s?\n", areYouSure, fpath); @@ -685,11 +664,23 @@ bool install(char* tadPath, bool systemTitle) //create 000000##.app { - iprintf("Creating 000000%02x.app...", appVersion); + // We must get the app name from the TMD (0x1E4-1E8). + // NTM/TMFH did it weirdly before by using a single byte at 0x1E7 called "appVersion"? + // Not sure how that even worked at all. The home menu deleted the incorrectly named apps + // and TwlNmenu showed them as being broken. + // + // This new code should always create valid titles. + FILE *tmd = fopen(tmdPath, "rb"); + unsigned char appName[4]; + fseek(tmd, 484, SEEK_SET); + fread(appName, 1, 4, tmd); + fclose(tmd); + + iprintf("Creating %02x%02x%02x%02x.app...", appName[0], appName[1], appName[2], appName[3]); swiWaitForVBlank(); char appPath[80]; - sprintf(appPath, "%s/000000%02x.app", contentPath, appVersion); + sprintf(appPath, "%s/%02x%02x%02x%02x.app", contentPath, appName[0], appName[1], appName[2], appName[3]); //copy nds file to app { diff --git a/arm9/src/main.c b/arm9/src/main.c index bc199fa..428440c 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -23,11 +23,11 @@ enum { MAIN_MENU_MODE, MAIN_MENU_INSTALL, MAIN_MENU_TITLES, - MAIN_MENU_BACKUP, + //MAIN_MENU_BACKUP, MAIN_MENU_TEST, MAIN_MENU_FIX, - MAIN_MENU_DATA_MANAGEMENT, - MAIN_MENU_LANGUAGE_PATCHER, + //MAIN_MENU_DATA_MANAGEMENT, + //MAIN_MENU_LANGUAGE_PATCHER, MAIN_MENU_EXIT }; @@ -62,10 +62,10 @@ static int _mainMenu(int cursor) iprintf("\nversion %s\n", VERSION); iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n"); iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n"); - iprintf("\n\n \x1B[46mgithub.com/rvtr/TDT\x1B[47m\n"); - iprintf("\x1b[22;0HJeff - 2018-2019"); - iprintf("\x1b[23;0HPk11 - 2022-2023"); - iprintf("\x1b[24;0Hrmc - 2024-2024"); + iprintf("\n\x1B[46mgithub.com/rvtr/TDT\x1B[47m\n"); + iprintf("\x1b[21;0HJeff - 2018-2019"); + iprintf("\x1b[22;0HPk11 - 2022-2023"); + iprintf("\x1b[23;0Hrmc - 2024-2024"); //menu Menu* m = newMenu(); diff --git a/arm9/src/tad.c b/arm9/src/tad.c index d3636af..200af63 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -31,19 +31,24 @@ If for whatever reason you want to make TADs, see here: https://randommeaninglesscharacters.com/dsidev/man/maketad.html */ -const unsigned char devKey[] = { - 0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, - 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA +const unsigned char commonKey[3][16] = { + // DEV + {0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, + 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA}, + // PROD + {0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, + 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61}, + // DEBUGGER + {0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, + 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3} }; -const unsigned char prodKey[] = { - 0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, - 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61 +// Content IV be fine as a hardcoded string. Content IV is based off of the content index. (index # with zerobyte padding) +// All TADs I've seen only ever had a single content. It might be a good idea to add something down the line in case a +// weird TAD pops up, but until then this should do. +unsigned char content_iv[] = { + 0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -const unsigned char debuggerKey[] = { - 0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, - 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3 -}; - typedef struct { uint32_t hdrSize; uint16_t tadType; @@ -96,6 +101,9 @@ int decryptTad(char const* src) return 1; } + // idk how to create folders recursively + mkdir("sd:/_nds", 0777); + mkdir("sd:/_nds/tadtests", 0777); mkdir("sd:/_nds/tadtests/tmp", 0777); /* @@ -131,7 +139,8 @@ int decryptTad(char const* src) Header header; fread(&header, sizeof(Header), 1, file); - iprintf("Parsing TAD header:\n"); + fclose(file); + iprintf("Parsing TAD header...\n"); Tad tad; tad.hdrOffset = 0; // All offsets in the TAD are aligned to 64 bytes. @@ -142,28 +151,21 @@ int decryptTad(char const* src) tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64); tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64); // TODO: Make sure offset calculation and alignment is correct by comparing that to total size - iprintf(" hdrSize: %lu\n", swap_endian_u32(header.hdrSize)); - iprintf(" hdrOffset: %lu\n", tad.hdrOffset); - // 18803 = "Is". This is the standard TAD type. - if (swap_endian_u16(header.tadType) == 18803) { - iprintf(" tadType: 'Is'\n"); - } else { - iprintf(" tadType: UNKNOWN\nERROR: unexpected TAD type\n"); - return 1; - } - iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); - iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); - iprintf(" certOffset: %lu\n", tad.certOffset); - iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize)); - iprintf(" crlOffset: %lu\n", tad.crlOffset); - iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize)); - iprintf(" ticketOffset: %lu\n", tad.ticketOffset); - iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize)); - iprintf(" tmdOffset: %lu\n", tad.tmdOffset); - iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize)); - iprintf(" srlOffset: %lu\n", tad.srlOffset); - iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); - iprintf(" metaOffset: %lu\n", tad.metaOffset); + //iprintf(" hdrSize: %lu\n", swap_endian_u32(header.hdrSize)); + //iprintf(" hdrOffset: %lu\n", tad.hdrOffset); + //iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); + //iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); + //iprintf(" certOffset: %lu\n", tad.certOffset); + //iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize)); + //iprintf(" crlOffset: %lu\n", tad.crlOffset); + //iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize)); + //iprintf(" ticketOffset: %lu\n", tad.ticketOffset); + //iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize)); + //iprintf(" tmdOffset: %lu\n", tad.tmdOffset); + //iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize)); + //iprintf(" srlOffset: %lu\n", tad.srlOffset); + //iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); + //iprintf(" metaOffset: %lu\n", tad.metaOffset); /* Copy the contents of the TAD to the SD card. @@ -174,57 +176,36 @@ int decryptTad(char const* src) trying to sign files for dev on a prod console. */ - iprintf("\n--------------------------------\nCopying output files:\n"); + iprintf("Copying output files...\n"); // Sorry for copy pasting, I'll make this a routine later - iprintf(" Copying TMD...\n"); - FILE *tmdFile = fopen("sd:/_nds/tadtests/tmp/temp.tmd", "wb"); - fseek(file, tad.tmdOffset, SEEK_SET); - for (int i = 0; i < swap_endian_u32(header.tmdSize); i++) { - char ch = fgetc(file); - fputc(ch, tmdFile); - } - fclose(tmdFile); + //iprintf(" Copying TMD...\n"); + copyFilePart(src, tad.tmdOffset, swap_endian_u32(header.tmdSize), "sd:/_nds/tadtests/tmp/temp.tmd"); - iprintf(" Copying ticket...\n"); - FILE *ticketFile = fopen("sd:/_nds/tadtests/tmp/temp.tik", "wb"); - fseek(file, tad.ticketOffset, SEEK_SET); - for (int i = 0; i < swap_endian_u32(header.ticketSize); i++) { - char ch = fgetc(file); - fputc(ch, ticketFile); - } - fclose(ticketFile); + //iprintf(" Copying ticket...\n"); + copyFilePart(src, tad.ticketOffset, swap_endian_u32(header.ticketSize), "sd:/_nds/tadtests/tmp/temp.tik"); - iprintf(" Copying SRL...\n"); - FILE *srlFile = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "wb"); - fseek(file, tad.srlOffset, SEEK_SET); - for (int i = 0; i < swap_endian_u32(header.srlSize); i++) { - char ch = fgetc(file); - fputc(ch, srlFile); - } - fclose(srlFile); - fclose(file); - - iprintf("\n--------------------------------\n"); + //iprintf(" Copying SRL...\n"); + copyFilePart(src, tad.srlOffset, swap_endian_u32(header.srlSize), "sd:/_nds/tadtests/tmp/temp.srl.enc"); /* Get the title key + IV from the ticket. */ - iprintf("Decrypting SRL:\n\n"); - iprintf(" Finding title key...\n"); + iprintf("Decrypting SRL...\n"); + //iprintf(" Finding title key...\n"); FILE *ticket = fopen("sd:/_nds/tadtests/tmp/temp.tik", "rb"); unsigned char title_key_enc[16]; fseek(ticket, 447, SEEK_SET); fread(title_key_enc, 1, 16, ticket); - iprintf(" Title key found!\n"); + //iprintf(" Title key found!\n"); /* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_enc[i]);} */ iprintf("\n"); - iprintf(" Finding title key IV...\n"); + //iprintf(" Finding title key IV...\n"); unsigned char title_key_iv[16]; fseek(ticket, 476, SEEK_SET); fread(title_key_iv, 1, 8, ticket); memset(title_key_iv + 8, 0, 8); - iprintf(" Title key IV found!\n"); + //iprintf(" Title key IV found!\n"); /* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_iv[i]);} */ iprintf("\n"); fclose(ticket); @@ -244,42 +225,31 @@ int decryptTad(char const* src) https://problemkaputt.de/gbatek.htm#dscartridgeheader https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 */ - - iprintf(" Decrypting title key...\n"); + //iprintf(" Decrypting title key...\n"); unsigned char title_key_dec[16]; - decrypt_cbc(devKey, title_key_iv, title_key_enc, sizeof(title_key_enc), sizeof(devKey), title_key_dec); - printf(" Title key decrypted!\n"); + decrypt_cbc(commonKey[0], title_key_iv, title_key_enc, sizeof(title_key_enc), 16, title_key_dec); + //printf(" Title key decrypted!\n"); /* for (int i = 0; i < 16; i++) {printf("%02X", title_key_dec[i]);} */ - iprintf("\n Decrypting SRL chunks...\n"); unsigned char srl_buffer_enc[16]; unsigned char srl_buffer_dec[16]; - // Should be fine as a hardcoded string. Content IV is based off of the content index. (index # with zerobyte padding) - // All TADs I've only ever a single content. It might be a good idea to add something down the line in case a weird - // TAD pops up, but until then this should do. - unsigned char content_iv[] = { - 0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "rb"); fseek(srlFile_enc, 0, SEEK_SET); FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/temp.srl", "wb"); fseek(srlFile_dec, 0, SEEK_SET); + //iprintf("\n Decrypting SRL chunks...\n"); for (int i = 0; i < swap_endian_u32(header.srlSize);) { fread(srl_buffer_enc, 1, 16, srlFile_enc); - /* ===== silly debug ===== */ - iprintf("%ld - %d / %ld\n", ftell(file), i, swap_endian_u32(header.srlSize)); - /* ===== end of debug ===== */ - decrypt_cbc(title_key_dec, content_iv, srl_buffer_enc, 16, 16, srl_buffer_dec); fwrite(srl_buffer_dec, 1, 16, srlFile_dec); + printProgressBar( ((float)i / (float)swap_endian_u32(header.srlSize)) ); i=i+16; } fclose(srlFile_enc); fclose(srlFile_dec); - iprintf("\nReady to install!"); return "sd:/_nds/tadtests/tmp/temp.srl"; + } void printTadInfo(char const* fpath) @@ -288,16 +258,15 @@ void printTadInfo(char const* fpath) if (!fpath) return; - uint32_t srlCompany; - uint32_t srlTidLow; - uint32_t srlTidHigh; - unsigned char srlVerLow = 0x00; - unsigned char srlVerHigh = 0x00; + unsigned char srlCompany[2]; + unsigned char srlTidLow[4]; + unsigned char srlTidHigh[4]; + unsigned char srlVerLow[1]; + unsigned char srlVerHigh[1]; FILE *file = fopen(fpath, "rb"); Header header; fread(&header, sizeof(Header), 1, file); - iprintf("Parsing TAD header:\n"); Tad tad; tad.hdrOffset = 0; // All offsets in the TAD are aligned to 64 bytes. @@ -308,45 +277,42 @@ void printTadInfo(char const* fpath) tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64); tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64); // Get info from TMD. - fseek(file, swap_endian_u32(tad.tmdOffset)+396, SEEK_SET); + fseek(file, tad.tmdOffset+396, SEEK_SET); fread(srlTidHigh, 1, 4, file); fread(srlTidLow, 1, 4, file); - fseek(file, swap_endian_u32(tad.tmdOffset)+408, SEEK_SET); - fread(srlCompany, 1, 4, file); - fseek(file, swap_endian_u32(tad.tmdOffset)+476, SEEK_SET); + fseek(file, tad.tmdOffset+408, SEEK_SET); + fread(srlCompany, 1, 2, file); + fseek(file, tad.tmdOffset+476, SEEK_SET); fread(srlVerHigh, 1, 1, file); fread(srlVerLow, 1, 1, file); - iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); - iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); - iprintf(" certOffset: %lu\n", tad.certOffset); - iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize)); - iprintf(" crlOffset: %lu\n", tad.crlOffset); - iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize)); - iprintf(" ticketOffset: %lu\n", tad.ticketOffset); - iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize)); - iprintf(" tmdOffset: %lu\n", tad.tmdOffset); - iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize)); - iprintf(" srlOffset: %lu\n", tad.srlOffset); - iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); - iprintf(" metaOffset: %lu\n", tad.metaOffset); + // I am so sorry for this mess. + iprintf("\nSize:\n "); + fseek(file, 0, SEEK_END); + unsigned long long romSize = ftell(file); + iprintf("\x1B[42m"); //green + printBytes(romSize); + iprintf("\x1B[47m"); //white + iprintf(" (\x1B[42m%ld blocks\x1B[47m)\n", ((swap_endian_u32(header.srlSize) / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK); - //proper title - // TODO! Might not be possible though :(( + iprintf("Game Code:\n "); + iprintf("\x1B[42m"); //green + for (int i = 0; i < 4; i++) {printf("%c", srlTidLow[i]);} + iprintf("\x1B[47m"); //white - //size in blocks, rounded up - iprintf("Size: (%ld blocks)\n", ((swap_endian_u32(header.srlSize) / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK); - - iprintf("Game Code: %c\n", (char)srlTidLow); - - iprintf("Game Version: %u.%u (NUS: v%u)\n", srlVerHigh * 256, srlVerLow, (srlVerHigh * 256) + srlVerLow); - - iprintf("Company Code: %c (%lu)\n", (char)srlCompany, srlCompany); + iprintf("\nGame Version:\n \x1B[42m%d.%d\x1B[47m (NUS: \x1B[42mv%d\x1B[47m)\n", (int)srlVerHigh[0] * 256, (int)srlVerLow[0], ((int)srlVerHigh[0] * 256) + (int)srlVerLow[0]); + iprintf("Company Code:\n \x1B[42m%c%c\x1B[47m(\x1B[42m%02x%02x\x1B[47m)\n", srlCompany[0], srlCompany[1], srlCompany[0], srlCompany[1]); + // Print program type based on TID high? - iprintf("Title ID: %08lx %08lx", srlTidHigh, srlTidLow); + iprintf("Title ID: \n "); + iprintf("\x1B[42m"); //green + for (int i = 0; i < 4; i++) {printf("%02x", srlTidHigh[i]);} + iprintf(" "); + for (int i = 0; i < 4; i++) {printf("%02x", srlTidLow[i]);} + iprintf("\x1B[47m"); //white //print full file path - iprintf("\n%s\n", fpath); + iprintf("\n\n%s\n", fpath); } \ No newline at end of file From de6ccd9f60f8a92556ade11c9a6176ab095e48de Mon Sep 17 00:00:00 2001 From: rmc Date: Fri, 5 Apr 2024 00:41:23 -0400 Subject: [PATCH 8/9] Add prod and updater (debugger) support --- arm9/src/install.c | 25 +++++++-- arm9/src/main.c | 2 +- arm9/src/tad.c | 129 +++++++++++++++++++++++++++++++-------------- arm9/src/tad.h | 8 ++- 4 files changed, 118 insertions(+), 46 deletions(-) diff --git a/arm9/src/install.c b/arm9/src/install.c index 065d9a4..ff1bbcb 100644 --- a/arm9/src/install.c +++ b/arm9/src/install.c @@ -365,7 +365,7 @@ bool install(char* tadPath, bool systemTitle) //start installation clearScreen(&bottomScreen); - char* fpath = decryptTad(tadPath); + char* fpath = openTad(tadPath); tDSiHeader* h = getRomHeader(fpath); @@ -374,7 +374,7 @@ bool install(char* tadPath, bool systemTitle) iprintf("\x1B[31m"); //red iprintf("Error: "); iprintf("\x1B[33m"); //yellow - iprintf("Could not open file.\n"); + iprintf("Could not decrypt TAD.\n"); iprintf("\x1B[47m"); //white goto error; } @@ -385,7 +385,6 @@ bool install(char* tadPath, bool systemTitle) if (_patchGameCode(h)) fixHeader = true; - result = decryptTad(fpath); //title id must be one of these if (h->tid_high == 0x00030004 || // DSiWare h->tid_high == 0x00030005 || // "Unimportant" system titles @@ -396,14 +395,17 @@ bool install(char* tadPath, bool systemTitle) else { iprintf("\x1B[31m"); //red - iprintf("Error: "); + iprintf("TID Error: "); iprintf("\x1B[33m"); //yellow - iprintf("This is not a DSi rom.\n"); + iprintf("Could not decrypt TAD.\n%s", fpath); iprintf("\x1B[47m"); //white goto error; } + // I am going to remove patching because it results in bad TMDs that the launcher auto deletes. + // Comment these back in if you really want, but know that 99% of dev apps will not install right with patching. //offer to patch system titles to normal DSiWare on SysNAND + /* if(!sdnandMode && h->tid_high != 0x00030004 && h->tid_high != 0x00030017) //do not allow patching home menus to be normal DSiWare! This will trigger "ERROR! - 0x0000000000000008 HWINFO_SECURE" on prototype launchers. May also cause issues on the prod versions. { if(choiceBox("This is set as a system/dev\ntitle, would you like to patch\nit to be a normal DSiWare?\n\nThis is safer, but invalidates\nRSA checks and may not work.\n\nIf the title is homebrew this isstrongly recommended.") == YES) @@ -412,8 +414,10 @@ bool install(char* tadPath, bool systemTitle) fixHeader = true; } } + */ //offer to patch home menus to be system titles on SysNAND + /* if(!sdnandMode && h->tid_high == 0x00030017) { if(choiceBox("This title is a home menu.\nWould you like to patch it to bea system title?\n\nThis is safer and prevents your\nhome menu from being hidden.") == YES) @@ -422,6 +426,7 @@ bool install(char* tadPath, bool systemTitle) fixHeader = true; } } + */ //no system titles without Unlaunch if (!unlaunchFound && h->tid_high != 0x00030004) @@ -538,6 +543,7 @@ bool install(char* tadPath, bool systemTitle) goto error; //system title patch + /* if (systemTitle) { iprintf("System Title Patch..."); @@ -549,8 +555,10 @@ bool install(char* tadPath, bool systemTitle) fixHeader = true; } + */ //check that there's space on nand + /* if (!_checkDsiSpace(installSize, (h->tid_high != 0x00030004))) { if (sdnandMode && choicePrint("Install as system title?")) @@ -563,6 +571,7 @@ bool install(char* tadPath, bool systemTitle) goto error; } } + */ //check for saves char pubPath[PATH_MAX]; @@ -858,5 +867,11 @@ complete: if (!sdnandMode) nandio_lock_writing(); + remove("sd:/_nds/tadtests/tmp/temp.tmd"); + remove("sd:/_nds/tadtests/tmp/temp.tik"); + remove("sd:/_nds/tadtests/tmp/temp.srl.enc"); + remove("sd:/_nds/tadtests/tmp/temp.srl"); + + return result; } \ No newline at end of file diff --git a/arm9/src/main.c b/arm9/src/main.c index 428440c..d7eb077 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -203,7 +203,7 @@ int main(int argc, char **argv) } else if (!unlaunchPatches) { - messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need these\nif you patch any TADs.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m"); + messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need these\nto boot any TADs.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m"); } } diff --git a/arm9/src/tad.c b/arm9/src/tad.c index 200af63..3b4b0fd 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -31,16 +31,17 @@ If for whatever reason you want to make TADs, see here: https://randommeaninglesscharacters.com/dsidev/man/maketad.html */ -const unsigned char commonKey[3][16] = { - // DEV - {0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, - 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA}, - // PROD - {0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, - 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61}, - // DEBUGGER - {0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, - 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3} +const unsigned char devKey[] = { + 0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29, + 0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA +}; +const unsigned char prodKey[] = { + 0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, + 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61 +}; +const unsigned char debuggerKey[] = { + 0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A, + 0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3 }; // Content IV be fine as a hardcoded string. Content IV is based off of the content index. (index # with zerobyte padding) // All TADs I've seen only ever had a single content. It might be a good idea to add something down the line in case a @@ -69,7 +70,11 @@ typedef struct { uint32_t srlOffset; uint32_t metaOffset; } Tad; -bool tadSuccess; +unsigned char srlCompany[2]; +unsigned char srlTidLow[4]; +unsigned char srlTidHigh[4]; +unsigned char srlVerLow[1]; +unsigned char srlVerHigh[1]; uint32_t swap_endian_u32(uint32_t x) { return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); @@ -79,8 +84,7 @@ uint16_t swap_endian_u16(uint16_t x) { return (x >> 8) | (x << 8); } -uint32_t round_up( const u32 v, const u32 align ) -{ +uint32_t round_up( const u32 v, const u32 align ) { u32 r = ((v + align - 1) / align) * align; return r; } @@ -91,8 +95,7 @@ void decrypt_cbc(const unsigned char* key, const unsigned char* iv, const unsign aes_crypt_cbc(&ctx, AES_DECRYPT, dataSize, iv, encryptedData, decryptedData); } -int decryptTad(char const* src) -{ +int openTad(char const* src) { if (!src) return 1; FILE *file = fopen(src, "rb"); @@ -139,7 +142,6 @@ int decryptTad(char const* src) Header header; fread(&header, sizeof(Header), 1, file); - fclose(file); iprintf("Parsing TAD header...\n"); Tad tad; tad.hdrOffset = 0; @@ -153,6 +155,16 @@ int decryptTad(char const* src) // TODO: Make sure offset calculation and alignment is correct by comparing that to total size //iprintf(" hdrSize: %lu\n", swap_endian_u32(header.hdrSize)); //iprintf(" hdrOffset: %lu\n", tad.hdrOffset); + + // Commenting this block out makes the TAD decrypt improperly. Truly a programming moment. + // 18803 = "Is". This is the standard TAD type. + if (swap_endian_u16(header.tadType) == 18803) { + //iprintf(" tadType: 'Is'\n"); + } else { + iprintf(" tadType: UNKNOWN\nERROR: unexpected TAD type\n"); + return 1; + } + //iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion)); //iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize)); //iprintf(" certOffset: %lu\n", tad.certOffset); @@ -167,6 +179,11 @@ int decryptTad(char const* src) //iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize)); //iprintf(" metaOffset: %lu\n", tad.metaOffset); + fseek(file, tad.tmdOffset+396, SEEK_SET); + fread(srlTidHigh, 1, 4, file); + fread(srlTidLow, 1, 4, file); + fclose(file); + /* Copy the contents of the TAD to the SD card. @@ -178,13 +195,13 @@ int decryptTad(char const* src) iprintf("Copying output files...\n"); // Sorry for copy pasting, I'll make this a routine later - //iprintf(" Copying TMD...\n"); + iprintf(" Copying TMD...\n"); copyFilePart(src, tad.tmdOffset, swap_endian_u32(header.tmdSize), "sd:/_nds/tadtests/tmp/temp.tmd"); - //iprintf(" Copying ticket...\n"); + iprintf(" Copying ticket...\n"); copyFilePart(src, tad.ticketOffset, swap_endian_u32(header.ticketSize), "sd:/_nds/tadtests/tmp/temp.tik"); - //iprintf(" Copying SRL...\n"); + iprintf(" Copying SRL...\n"); copyFilePart(src, tad.srlOffset, swap_endian_u32(header.srlSize), "sd:/_nds/tadtests/tmp/temp.srl.enc"); /* @@ -211,7 +228,7 @@ int decryptTad(char const* src) fclose(ticket); /* - This is SRL decryption. AES-CBC. + This is SRL decryption (AES-CBC). Common key + title key IV to decrypt title key, title key + content IV to decrypt content @@ -225,31 +242,72 @@ int decryptTad(char const* src) https://problemkaputt.de/gbatek.htm#dscartridgeheader https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84 */ - //iprintf(" Decrypting title key...\n"); - unsigned char title_key_dec[16]; - decrypt_cbc(commonKey[0], title_key_iv, title_key_enc, sizeof(title_key_enc), 16, title_key_dec); - //printf(" Title key decrypted!\n"); - /* for (int i = 0; i < 16; i++) {printf("%02X", title_key_dec[i]);} */ + bool keyFail; + iprintf("Trying dev common key...\n"); + keyFail = decryptTad(devKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); + if (keyFail == TRUE) { + remove("sd:/_nds/tadtests/tmp/temp.srl"); + iprintf("Key fail!\n\nTrying debugger common key...\n"); + keyFail = decryptTad(debuggerKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); + } + if (keyFail == TRUE) { + remove("sd:/_nds/tadtests/tmp/temp.srl"); + iprintf("Key fail!\n\nTrying prod common key...\n"); + keyFail = decryptTad(prodKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); + } + if (keyFail == TRUE) { + remove("sd:/_nds/tadtests/tmp/temp.srl"); + iprintf("All keys failed!\n"); + return "ERROR"; + } + return "sd:/_nds/tadtests/tmp/temp.srl"; + +} + +bool decryptTad(unsigned char* commonKey, + unsigned char* title_key_iv, + unsigned char* title_key_enc, + unsigned char* content_iv, + int srlSize, + unsigned char* srlTidLow) { + unsigned char title_key_dec[16]; + unsigned char title_key_iv_bak[16]; unsigned char srl_buffer_enc[16]; unsigned char srl_buffer_dec[16]; + + // Backup IV because PolarSSL will overwrite it + memcpy( title_key_iv_bak, title_key_iv, 16 ); + FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "rb"); fseek(srlFile_enc, 0, SEEK_SET); FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/temp.srl", "wb"); fseek(srlFile_dec, 0, SEEK_SET); - //iprintf("\n Decrypting SRL chunks...\n"); - for (int i = 0; i < swap_endian_u32(header.srlSize);) { + iprintf(" Decrypting SRL in chunks..\n"); + decrypt_cbc(commonKey, title_key_iv, title_key_enc, 16, 16, title_key_dec); + int i=0; + bool keyFail = FALSE; + while (i < srlSize && keyFail == FALSE) { fread(srl_buffer_enc, 1, 16, srlFile_enc); decrypt_cbc(title_key_dec, content_iv, srl_buffer_enc, 16, 16, srl_buffer_dec); fwrite(srl_buffer_dec, 1, 16, srlFile_dec); - printProgressBar( ((float)i / (float)swap_endian_u32(header.srlSize)) ); + printProgressBar( ((float)i / (float)srlSize) ); + if (i == 560) { + if (srl_buffer_dec[3] != srlTidLow[0] || + srl_buffer_dec[2] != srlTidLow[1] || + srl_buffer_dec[1] != srlTidLow[2] || + srl_buffer_dec[0] != srlTidLow[3] ) { + keyFail = TRUE; + } + } i=i+16; } - fclose(srlFile_enc); fclose(srlFile_dec); - return "sd:/_nds/tadtests/tmp/temp.srl"; - + fclose(srlFile_enc); + // Restore IV + memcpy( title_key_iv, title_key_iv_bak, 16 ); + return keyFail; } void printTadInfo(char const* fpath) @@ -257,13 +315,6 @@ void printTadInfo(char const* fpath) clearScreen(&topScreen); if (!fpath) return; - - unsigned char srlCompany[2]; - unsigned char srlTidLow[4]; - unsigned char srlTidHigh[4]; - unsigned char srlVerLow[1]; - unsigned char srlVerHigh[1]; - FILE *file = fopen(fpath, "rb"); Header header; fread(&header, sizeof(Header), 1, file); @@ -314,5 +365,5 @@ void printTadInfo(char const* fpath) //print full file path iprintf("\n\n%s\n", fpath); - + fclose(file); } \ No newline at end of file diff --git a/arm9/src/tad.h b/arm9/src/tad.h index aabfb21..5592a51 100644 --- a/arm9/src/tad.h +++ b/arm9/src/tad.h @@ -4,6 +4,12 @@ #include #include -int decryptTad(char const* src); +int openTad(char const* src); +bool decryptTad(unsigned char* commonKey, + unsigned char* title_key_iv, + unsigned char* title_key_enc, + unsigned char* content_iv, + int srlSize, + unsigned char* srlTidLow); #endif \ No newline at end of file From a7a16b59e10b878b31ebe6720c0ebc5d4adb6416 Mon Sep 17 00:00:00 2001 From: rmc Date: Fri, 5 Apr 2024 01:42:41 -0400 Subject: [PATCH 9/9] Fix broken header bug PolarSSL overwrites the IVs and breaks anything past the first install. Here the content IV was overwritten, so the first 16 bytes could not be decrypted properly. Should be the last issue for this. --- arm9/src/install.c | 10 +++++----- arm9/src/tad.c | 31 +++++++++++++++++-------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/arm9/src/install.c b/arm9/src/install.c index ff1bbcb..1835f06 100644 --- a/arm9/src/install.c +++ b/arm9/src/install.c @@ -867,11 +867,11 @@ complete: if (!sdnandMode) nandio_lock_writing(); - remove("sd:/_nds/tadtests/tmp/temp.tmd"); - remove("sd:/_nds/tadtests/tmp/temp.tik"); - remove("sd:/_nds/tadtests/tmp/temp.srl.enc"); - remove("sd:/_nds/tadtests/tmp/temp.srl"); - + remove("sd:/_nds/TADDeliveryTool/tmp/temp.tmd"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.tik"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl"); + rmdir("sd:/_nds/TADDeliveryTool/tmp"); return result; } \ No newline at end of file diff --git a/arm9/src/tad.c b/arm9/src/tad.c index 3b4b0fd..4fea52e 100644 --- a/arm9/src/tad.c +++ b/arm9/src/tad.c @@ -106,8 +106,8 @@ int openTad(char const* src) { // idk how to create folders recursively mkdir("sd:/_nds", 0777); - mkdir("sd:/_nds/tadtests", 0777); - mkdir("sd:/_nds/tadtests/tmp", 0777); + mkdir("sd:/_nds/TADDeliveryTool", 0777); + mkdir("sd:/_nds/TADDeliveryTool/tmp", 0777); /* Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages @@ -196,13 +196,13 @@ int openTad(char const* src) { iprintf("Copying output files...\n"); // Sorry for copy pasting, I'll make this a routine later iprintf(" Copying TMD...\n"); - copyFilePart(src, tad.tmdOffset, swap_endian_u32(header.tmdSize), "sd:/_nds/tadtests/tmp/temp.tmd"); + copyFilePart(src, tad.tmdOffset, swap_endian_u32(header.tmdSize), "sd:/_nds/TADDeliveryTool/tmp/temp.tmd"); iprintf(" Copying ticket...\n"); - copyFilePart(src, tad.ticketOffset, swap_endian_u32(header.ticketSize), "sd:/_nds/tadtests/tmp/temp.tik"); + copyFilePart(src, tad.ticketOffset, swap_endian_u32(header.ticketSize), "sd:/_nds/TADDeliveryTool/tmp/temp.tik"); iprintf(" Copying SRL...\n"); - copyFilePart(src, tad.srlOffset, swap_endian_u32(header.srlSize), "sd:/_nds/tadtests/tmp/temp.srl.enc"); + copyFilePart(src, tad.srlOffset, swap_endian_u32(header.srlSize), "sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc"); /* Get the title key + IV from the ticket. @@ -210,7 +210,7 @@ int openTad(char const* src) { iprintf("Decrypting SRL...\n"); //iprintf(" Finding title key...\n"); - FILE *ticket = fopen("sd:/_nds/tadtests/tmp/temp.tik", "rb"); + FILE *ticket = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.tik", "rb"); unsigned char title_key_enc[16]; fseek(ticket, 447, SEEK_SET); fread(title_key_enc, 1, 16, ticket); @@ -247,21 +247,21 @@ int openTad(char const* src) { keyFail = decryptTad(devKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); if (keyFail == TRUE) { - remove("sd:/_nds/tadtests/tmp/temp.srl"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl"); iprintf("Key fail!\n\nTrying debugger common key...\n"); keyFail = decryptTad(debuggerKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); } if (keyFail == TRUE) { - remove("sd:/_nds/tadtests/tmp/temp.srl"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl"); iprintf("Key fail!\n\nTrying prod common key...\n"); keyFail = decryptTad(prodKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow); } if (keyFail == TRUE) { - remove("sd:/_nds/tadtests/tmp/temp.srl"); + remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl"); iprintf("All keys failed!\n"); return "ERROR"; } - return "sd:/_nds/tadtests/tmp/temp.srl"; + return "sd:/_nds/TADDeliveryTool/tmp/temp.srl"; } @@ -273,15 +273,17 @@ bool decryptTad(unsigned char* commonKey, unsigned char* srlTidLow) { unsigned char title_key_dec[16]; unsigned char title_key_iv_bak[16]; + unsigned char content_iv_bak[16]; unsigned char srl_buffer_enc[16]; unsigned char srl_buffer_dec[16]; - // Backup IV because PolarSSL will overwrite it + // Backup IVs because PolarSSL will overwrite it memcpy( title_key_iv_bak, title_key_iv, 16 ); + memcpy( content_iv_bak, content_iv, 16 ); - FILE *srlFile_enc = fopen("sd:/_nds/tadtests/tmp/temp.srl.enc", "rb"); + FILE *srlFile_enc = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc", "rb"); fseek(srlFile_enc, 0, SEEK_SET); - FILE *srlFile_dec = fopen("sd:/_nds/tadtests/tmp/temp.srl", "wb"); + FILE *srlFile_dec = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.srl", "wb"); fseek(srlFile_dec, 0, SEEK_SET); iprintf(" Decrypting SRL in chunks..\n"); @@ -305,8 +307,9 @@ bool decryptTad(unsigned char* commonKey, } fclose(srlFile_dec); fclose(srlFile_enc); - // Restore IV + // Restore IVs memcpy( title_key_iv, title_key_iv_bak, 16 ); + memcpy( content_iv, content_iv_bak, 16 ); return keyFail; }