#include #include #include #include #include "utils.h" #include "f_xy.h" #include "dsi.h" #include #include "sha1.h" u32 tadsrl_keyX[4] = {0x4E00004A, 0x4A00004E, 0, 0}; u8 tadsrl_keyY[16] = {0xcc, 0xfc, 0xa7, 0x03, 0x20, 0x61, 0xbe, 0x84, 0xd3, 0xeb, 0xa4, 0x26, 0xb8, 0x6d, 0xbe, 0xc2}; u8 sd_key[16] = {0x3d, 0xa3, 0xea, 0x33, 0x4c, 0x86, 0xa6, 0xb0, 0x2a, 0xae, 0xdb, 0x51, 0x16, 0xea, 0x92, 0x62}; char modcrypt_shared_key[8] = {'N','i','n','t','e','n','d','o'}; // middle two words are 'NINTENDO' big endian u32 emmc_keyX_3DS[4] = {0x00000000, 0x544E494E, 0x4F444E45, 0x00000000}; u32 emmc_keyY[4] = {0x0AB9DC76,0xBD4DC4D3,0x202DDD1D,0xE1A00005}; u8 block[0x10]; int i; static const char* s_PubkeyNtrboot = "cc987b417d9d7c800285db10998cd4a7867aefda43a96d0b55b010736799c2d9f072d6fb1da6179a778987e7e8c8c08636bf5d2cd75a1feae04f66c783a833f493739e3a9770525e3d9d0eed504fd65fc4367191c41681cacf924ead1c3270dc58e58989a36a094390f3f18e404fb7f1687f30f76d008a9cd487254a98c9662f" , * s_PubkeyNand = "f1f51aff66f9b3694dcb78deaf311b783c072aac943011114a1cf6fe62b091b5ef0eba3aa9ec3ea01c5df666653e18df22533bd5e8d6ff58970b24e886fa878b62669924a8fa87f274004fea2ff623e1f2907ca4671fca283e86b6cac546a79c75c80feb32882c3d1df7d5dc1a1998e9f626d4fc76cb231358cb43a9b3cba3c5" , * s_PubkeySpi = "b85f0fdb262c1afcf86e37a7cdd211d455203ca8fb106edec7036ed7d6363b6633f4d6275865b632b9277a74ce1d41dbd96a0486ee7271b38673a03b8348627b22a20b7738dd027376c4f8523dea5c6f5df262e45f7a901c16a18cff0588862e1c826276c8ac7987f0a1d57d9399d5cd5d48554d30315dc1b15a324511f911cb" ; typedef struct __attribute__((__packed__)) { u8 status; u8 start_chs[3]; u8 partition_type; u8 end_chs[3]; u32 start_sector; u32 num_sectors; } mbr_partition; // laaaaazy! typedef struct { u8 code[446]; mbr_partition partition[4]; u8 signature[2]; } mbr; void decrypt_modcrypt_area(dsi_context* ctx, u8 *buffer, unsigned int size) { uint32_t len = size / 0x10; while(len>0) { memset(block, 0, 0x10); dsi_crypt_ctr_block(ctx, buffer, block); memcpy(buffer, block, 0x10); buffer+=0x10; len--; } } // From dsi_srl_extract int decryptsrl(u8 *srl) { u8 *keyX_ptr = NULL, *keyY_ptr = NULL; uint32_t offset, size; int verbose=0; u8 *header, *buffer; u8 key_x[16]; u8 key_y[16]; u8 key[16]; dsi_context ctx; header = srl; memcpy(key_x, modcrypt_shared_key, 8); memcpy(&key_x[8], &header[0x0c], 4); key_x[12 + 0] = header[0x0c + 3]; key_x[12 + 1] = header[0x0c + 2]; key_x[12 + 2] = header[0x0c + 1]; key_x[12 + 3] = header[0x0c + 0]; memcpy(key_y, &header[0x350], 16); if((header[0x1c] & 4) || (header[0x1bf] & 0x80)) { printf("Crypting dev modcrypt.\n"); } else { printf("Crypting retail modcrypt.\n"); keyX_ptr = key_x; keyY_ptr = key_y; } memcpy(key, header, 16); printf("Crypting...\n"); if(keyX_ptr) { F_XY((uint32_t*)key, (uint32_t*)key_x, (uint32_t*)key_y); } dsi_set_key(&ctx, key); memcpy(&offset, &header[0x220], 4); memcpy(&size, &header[0x224], 4); dsi_set_ctr(&ctx, &header[0x300]); if(offset!=0) { printf("Modcrypt area 0: offset %x size %x\n", offset, size); buffer = srl + offset; decrypt_modcrypt_area(&ctx, buffer, size); } else { printf("Modcrypt area 0 is unused.\n"); } memcpy(&offset, &header[0x228], 4); memcpy(&size, &header[0x22c], 4); dsi_set_ctr(&ctx, &header[0x314]); if(offset!=0) { printf("Modcrypt area 1: offset %x size %x\n", offset, size); buffer = srl + offset; decrypt_modcrypt_area(&ctx, buffer, size); } else { printf("Modcrypt area 1 is unused.\n"); } printf("Done.\n"); return 0; } void decrypt_boot2_section(u8* data, u32 len, bool is3DS, bool isNtrboot, u8* keyY) { u8 normalkey[16] = {0}; u8 keyX_TWLFIRM[16] = {0xE1, 0xEB, 0xDF, 0x44, 0xAB, 0x1D, 0x81, 0xE3, 0x93, 0x9A, 0x4A, 0xB5, 0x36, 0xFC, 0x3A, 0x0E}; u8 keyX_DSi[16] = {0x4E, 0x69, 0x6E, 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x20, 0x44, 0x53, 0x00, 0x01, 0x23, 0x21, 0x00}; u8 keyY_3DS[16] = {0xAB, 0x4E, 0x18, 0xA8, 0x51, 0x16, 0x90, 0x7E, 0x9F, 0x65, 0xF0, 0xCE, 0x21, 0x7C, 0x3A, 0x70}; u8 keyY_DSi[16] = {0xEC, 0x07, 0x00, 0x00, 0x34, 0xE2, 0x94, 0x7C, 0xC3, 0x0E, 0x81, 0x7C, 0xEC, 0x07, 0x00, 0x00}; u32 ctr[4]; memset(ctr, 0, 16); int i; dsi_context dsictx; if (keyY != NULL) memcpy(keyY_DSi, keyY, 16); if(is3DS == true) F_XY((u32*) normalkey, (u32*)keyX_TWLFIRM, (u32*)keyY_3DS); else F_XY((u32*) normalkey, (u32*)keyX_DSi, (u32*)keyY_DSi); ctr[0] = len; ctr[1] = -len; ctr[2] = ~len; printf("CTR:\n"); hexdump(ctr,16); printf("Normalkey:\n"); hexdump(normalkey,16); dsi_set_key(&dsictx, normalkey); dsi_set_ctr(&dsictx, (u8*)ctr); // auto-increments ctr for(i = 0; i < len; i+= 0x10) dsi_crypt_ctr_block(&dsictx, data+i, data+i); } void decrypt_boot2(char* in, bool is3DS, bool isNtrboot) { u32 offset; u32 len; u8* data; //u8 signedData[ FILE* f_in = fopen(in,"rb"); FILE* f_out; u32 arm9_offset = isNtrboot ? 0x20 : 0x220; u32 arm7_offset = isNtrboot ? 0x30 : 0x230; if(f_in == NULL) { printf("Input filename invalid!\n"); return; } // get the keyY from the signature u8 signature[0x80]; u8 message[0x80]; const char* pubkey = NULL; u8* keyY = NULL; if (!is3DS) { static const u8 s_MessagePadding[] = { 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 }; if (isNtrboot) { pubkey = s_PubkeyNtrboot; fseek(f_in, 0x100, SEEK_SET); fread(signature, 1, sizeof(signature), f_in); dsi_rsa_signature_decrypt(pubkey, signature, message); if (memcmp(message, s_MessagePadding, sizeof(s_MessagePadding)) != 0) { printf("Signature invalid!\n"); return; } } else { pubkey = s_PubkeyNand; fseek(f_in, 0x300, SEEK_SET); fread(signature, 1, sizeof(signature), f_in); dsi_rsa_signature_decrypt(pubkey, signature, message); if (memcmp(message, s_MessagePadding, sizeof(s_MessagePadding)) != 0) { pubkey = s_PubkeySpi; dsi_rsa_signature_decrypt(pubkey, signature, message); if (memcmp(message, s_MessagePadding, sizeof(s_MessagePadding)) != 0) { printf("Signature invalid!\n"); return; } } } keyY = &message[sizeof(s_MessagePadding)]; printf("KeyY:\n"); hexdump(keyY,16); } // decrypt and write ARM9 f_out = fopen("arm9.bin","wb"); fseek(f_in, arm9_offset, SEEK_SET); fread(&offset, 1, sizeof(offset), f_in); fseek(f_in, arm9_offset + 0xC, SEEK_SET); fread(&len, 1, sizeof(len), f_in); printf("arm9: offset=%x, len=%x\n", offset, len); fseek(f_in, offset, SEEK_SET); data = malloc(len); fread(data, 1, len, f_in); decrypt_boot2_section(data, len, is3DS, isNtrboot, keyY); fwrite(data, 1, len, f_out); free(data); fclose(f_out); // decrypt and write ARM7 f_out = fopen("arm7.bin","wb"); fseek(f_in, arm7_offset, SEEK_SET); fread(&offset, 1, sizeof(offset), f_in); fseek(f_in, arm7_offset + 0xC, SEEK_SET); fread(&len, 1, sizeof(len), f_in); printf("arm7: offset=%x, len=%x\n", offset, len); fseek(f_in, offset, SEEK_SET); data = malloc(len); fread(data, 1, len, f_in); decrypt_boot2_section(data, len, is3DS, isNtrboot, keyY); fwrite(data, 1, len, f_out); fclose(f_out); fclose(f_in); free(data); } void decrypt_srl(char* in, char* out) { FILE* f_in = fopen(in,"r+b"); FILE* f_out; if(!strcmp(in,out)) f_out = fopen(out,"r+b"); else f_out = fopen(out,"wb"); if(f_in == NULL) { printf("Input filename invalid!"); return; } fseek(f_in, 0L, SEEK_END); u32 fsize = ftell(f_in); fseek(f_in, 0L, SEEK_SET); u8* srl = malloc(fsize); fread(srl, 1, fsize, f_in); decryptsrl(srl); fwrite(srl, 1, fsize, f_out); free(srl); fclose(f_in); fclose(f_out); } void cid_brute_3ds(u32* consoleID, u8* emmc_cid, u8* test_data, char* cidfile, bool isN3DS) { dsi_context ctx; int i, diff; struct timeb start, end; u8 emmc_normalkey[16]; u8 emmc_cid_hash[20]; u8 CTR[16]; u8* consoleID8 = (u8*)consoleID; if(isN3DS == true) consoleID[1] = 0x00000002; else consoleID[1] = 0x00000000; emmc_keyX_3DS[3] = consoleID[1] ^ 0x08C267B7; u8 target_bytes[16] = {0}; sha1(emmc_cid_hash, emmc_cid, 16); memcpy(CTR, emmc_cid_hash, 16); // store our target ctr so we won't have to copy + increment it every iteration dsi_set_ctr(&ctx, (u8*)CTR); dsi_add_ctr(&ctx, 0x1E); memcpy(CTR, ctx.ctr, 16); ftime(&start); // first bit is always set, we only need to brute the bottom 7 bits for(i = 0x00000000; i < 0x7FFFFFFF; i++) { consoleID[0] = i; memcpy(ctx.ctr, CTR, 16); emmc_keyX_3DS[0] = (consoleID[0] ^ 0xB358A6AF) | 0x80000000; F_XY((u32*)emmc_normalkey, (u32*) emmc_keyX_3DS, (u32*) emmc_keyY); dsi_set_key(&ctx, emmc_normalkey); dsi_crypt_ctr_block(&ctx, test_data, block); // if this block decrypts to all zero, we've got the right consoleID. if(!memcmp(target_bytes, block, sizeof(target_bytes))){ // print this as-is without endian flipping! printf("Got it!! ConsoleID is "); for(i = 0; i < 8; i++) printf("%02X", consoleID8[i]); printf("\n"); if(cidfile) { FILE* f = fopen(cidfile, "w+b"); if(!f) { printf("Failed to write CID to %s! Continuing...\n", cidfile); return; } fwrite(consoleID, 1, sizeof(consoleID), f); fclose(f); } break; } if(!(i % 0x200000)) printf("CID 0x%08X of 0x7FFFFFFF\n",i); } ftime(&end); diff = (int) (1000.0 * (end.time - start.time) + (end.millitm - start.millitm)); printf("Bruteforce took %u milliseconds\n", diff); } void nand_decrypt_3ds(u8 *emmc_cid, u32 *consoleID, char *in, char *out, bool brute_cid, char* cidfile, bool isN3DS) { dsi_context ctx; u32 i; u8 emmc_normalkey[16]; u8 emmc_cid_hash[20]; u8 CTR[16]; u8 brute_buf[16]; if(brute_cid == true) { FILE* f_in = fopen(in,"r+b"); fseek(f_in, 0x1E0, SEEK_SET); fread(brute_buf, 1, sizeof(brute_buf), f_in); fclose(f_in); cid_brute_3ds(consoleID, emmc_cid, brute_buf, cidfile, isN3DS); } // Prepare CTR by SHA1-hashing eMMC CID sha1(emmc_cid_hash, emmc_cid, 16); memcpy(CTR, emmc_cid_hash, 16); dsi_set_ctr(&ctx, (u8*)CTR); // Generate AES normalkey from consoleID emmc_keyX_3DS[0] = (consoleID[0] ^ 0xB358A6AF) | 0x80000000; emmc_keyX_3DS[3] = consoleID[1] ^ 0x08C267B7; F_XY((u32*) emmc_normalkey, (u32*) emmc_keyX_3DS, (u32*) emmc_keyY); dsi_set_key(&ctx, emmc_normalkey); FILE* f_in = fopen(in,"r+b"); FILE* f_out; if(!strcmp(in,out)) f_out = fopen(out,"r+b"); else f_out = fopen(out,"wb"); if(f_in == NULL) { printf("Input filename invalid!"); return; } for(i = 0; i < 0x0B100000; i += 0x10) { fread(block, 1, 0x10, f_in); dsi_crypt_ctr_block(&ctx, block, block); fwrite(block, 1, 0x10, f_out); if(i % 0x1000000 == 0) printf("%.2f %% complete.\n",(100.0 * i / 0x0B100000)); } fclose(f_in); fclose(f_out); printf("Crypt complete!"); } void file_copy_append(FILE* f_in, FILE* f_out, dsi_context* ctx, u8 disp_progress, u32 start_addr, u32 end_addr) { u32 cur_size; const u32 buf_size = 0x100000; void* buf = malloc(buf_size); if(!buf) { printf("Failed to allocate %d byte buf for file operation!", buf_size); exit(EXIT_FAILURE); } fseek(f_in, start_addr, SEEK_SET); for(i = start_addr; i < end_addr; i += buf_size) { cur_size = (end_addr - i) >= buf_size ? buf_size : end_addr - i; fread(buf, 1, cur_size, f_in); // do CTR crypto if a ctx is supplied if(ctx) dsi_crypt_ctr(ctx, buf, buf, cur_size); fwrite(buf, 1, cur_size, f_out); //update progress every 16MB if(disp_progress && (((i - start_addr) / buf_size) % 25) == 0) printf("%.2f %% complete.\n",100.0 * (i - start_addr) / (end_addr - start_addr)); } if(disp_progress) printf("100.00%% complete.\n"); free(buf); } void nand_decrypt_dsi(u8 *emmc_cid, u32 *consoleID, char *in, char *out) { dsi_context ctx; u32 i; u32 emmc_keyX[4]; u8 emmc_normalkey[16]; u8 emmc_cid_hash[20]; u8 base_ctr[16]; mbr mbr; bool have_nocashinfo = false; FILE* f_in = fopen(in,"r+b"); FILE* f_out; if(!strcmp(in,out)) f_out = fopen(out,"r+b"); else f_out = fopen(out,"wb"); if(f_in == NULL) { printf("Input filename invalid!"); return; } // Endian-swap the input ConsoleID (provided from tad footer) u32 tmp = getbe32((u8*)&consoleID[0]); consoleID[0] = getbe32((u8*)&consoleID[1]); consoleID[1] = tmp; char nocashinfo[64]; fseek(f_in, -64, SEEK_END); fread(nocashinfo,1,64,f_in); fseek(f_in, 0, SEEK_SET); if (memcmp(nocashinfo,"DSi eMMC CID/CPU",16) == 0 ) { printf("reading consoleid/CID from nocashinfo block\n"); memcpy(consoleID,&nocashinfo[32],8); memcpy(emmc_cid,&nocashinfo[16],16); have_nocashinfo = true; } // Prepare AES CTR by SHA1-hashing eMMC CID sha1(emmc_cid_hash, emmc_cid, 16); memcpy(base_ctr, emmc_cid_hash, 16); dsi_set_ctr(&ctx, (u8*)base_ctr); // Generate AES normalkey from consoleID (which comes in reverse word order) emmc_keyX[0] = consoleID[0]; emmc_keyX[1] = consoleID[0] ^ 0x24EE6906; emmc_keyX[2] = consoleID[1] ^ 0xE65B601D; emmc_keyX[3] = consoleID[1]; F_XY((u32*) emmc_normalkey, (u32*) emmc_keyX, (u32*) emmc_keyY); dsi_set_key(&ctx, emmc_normalkey); // get MBR from encrypted or decrypted NAND fread(&mbr, 1, 0x200, f_in); if(mbr.signature[0] != 0x55 || mbr.signature[1] != 0xAA) { dsi_crypt_ctr(&ctx, &mbr, &mbr, 0x200); if(mbr.signature[0] != 0x55 || mbr.signature[1] != 0xAA) { printf("MBR verification failed! Make sure your CID and consoleID are correct."); fclose(f_in); fclose(f_out); return; } } // process NAND parts (encrypted and otherwise) rewind(f_in); // process MBR dsi_set_ctr(&ctx, (u8*)base_ctr); file_copy_append(f_in, f_out, &ctx, 0, 0, 0x200); // process space before partition 1, including stage 2 bootloader etc file_copy_append(f_in, f_out, NULL, 0, 0x200, 0x10EE00); // process twln printf("Processing twln...\n"); dsi_set_ctr(&ctx, (u8*)base_ctr); dsi_add_ctr(&ctx, 0x10EE00 / 0x10); file_copy_append(f_in, f_out, &ctx, 1, 0x10EE00, 0x0CF00000); // process space before partition 2 file_copy_append(f_in, f_out, NULL, 0, 0x0CF00000, 0x0CF09A00); // process twlp printf("Processing twlp...\n"); dsi_set_ctr(&ctx, (u8*)base_ctr); dsi_add_ctr(&ctx, 0x0CF09A00 / 0x10); file_copy_append(f_in, f_out, &ctx, 1, 0x0CF09A00, 0x0EFC0000); // process the rest, including unused (and unencrypted) third partition) file_copy_append(f_in, f_out, NULL, 0, 0x0EFC0000, 0x0F000000); if (have_nocashinfo) fwrite(nocashinfo,1,64,f_out); fclose(f_in); fclose(f_out); printf("Crypt complete!"); } /** * crypt system files (tickets, dev.kp) with ES Block crypto */ void es_crypt_file(char* in, char* out, u32 consoleID[2], bool encrypt, bool is3DS) { u8 es_system_keyY[16] = {0xE5, 0xCC, 0x5A, 0x8B, 0x56, 0xD0, 0xC9, 0x72, 0x9C, 0x17, 0xE8, 0xDC, 0x39, 0x12, 0x36, 0xA9}; u32 in_size; u32 write_size = 0; int ret = 0; u32 normalkey[4]; dsi_es_context ctx; FILE* f_in = fopen(in,"r+b"); FILE* f_out; if(!strcmp(in,out)) f_out = fopen(out,"r+b"); else f_out = fopen(out,"wb"); if(f_in == NULL) { printf("Input filename invalid! %s", in); return; } if(f_out == NULL) { printf("Output filename invalid! %s", out); return; } fseek(f_in, 0, SEEK_END); in_size = ftell(f_in); rewind(f_in); void* in_data = malloc(in_size); if(!in_data) { printf("Failed to allocate input file buf!"); return; } fread(in_data, 1, in_size, f_in); if(is3DS == false) { u32 tmp = getbe32((u8*)&consoleID[0]); consoleID[0] = getbe32((u8*)&consoleID[1]); consoleID[1] = tmp; } // set up keys for crypto (consoleid is in reverse word order) tadsrl_keyX[2] = consoleID[1] ^ 0xC80C4B72; tadsrl_keyX[3] = consoleID[0]; F_XY(normalkey, tadsrl_keyX, (u32*)es_system_keyY); dsi_es_init(&ctx, (u8*)normalkey); if(encrypt == false) { // decrypt! write_size = in_size - 0x20; ret = dsi_es_decrypt(&ctx, in_data, in_data + write_size, write_size); if(ret == -1) { printf("ES magic check failed! Is your consoleID correct?"); return; } else if(ret == -2) { printf("Decrypted file size is incorrect!"); return; } else if(ret == -3) { printf("MAC mismatch! Continuing..."); return; } fwrite(in_data, 1, write_size, f_out); printf("ES decrypt success!"); } else { // encrypt! u8 metablock[32]; dsi_es_encrypt(&ctx, in_data, metablock, in_size); fwrite(in_data, 1, in_size, f_out); fwrite(metablock, 1, 0x20, f_out); printf("ES encrypt complete!"); } fclose(f_in); fclose(f_out); free(in_data); } /** * crypt system files (verdata DER files) with ES Block crypto and provided normalkey */ void es_crypt_file_normalkey(char* in, char* out, u32 normalkey[4], bool encrypt) { u32 in_size; u32 write_size = 0; int ret = 0; dsi_es_context ctx; FILE* f_in = fopen(in,"r+b"); FILE* f_out; if(!strcmp(in,out)) f_out = fopen(out,"r+b"); else f_out = fopen(out,"wb"); if(f_in == NULL) { printf("Input filename invalid! %s", in); return; } if(f_out == NULL) { printf("Output filename invalid! %s", out); return; } fseek(f_in, 0, SEEK_END); in_size = ftell(f_in); rewind(f_in); void* in_data = malloc(in_size); if(!in_data) { printf("Failed to allocate input file buf!"); return; } fread(in_data, 1, in_size, f_in); dsi_es_init(&ctx, (u8*)normalkey); if(encrypt == false) { // decrypt! write_size = in_size - 0x20; ret = dsi_es_decrypt(&ctx, in_data, in_data + write_size, write_size); if(ret == -1) { printf("ES magic check failed! Is your consoleID correct?"); return; } else if(ret == -2) { printf("Decrypted file size is incorrect!"); return; } else if(ret == -3) { printf("MAC mismatch! Continuing..."); return; } fwrite(in_data, 1, write_size, f_out); printf("ES decrypt success!"); } else { // encrypt! u8 metablock[32]; dsi_es_encrypt(&ctx, in_data, metablock, in_size); fwrite(in_data, 1, in_size, f_out); fwrite(metablock, 1, 0x20, f_out); printf("ES encrypt complete!"); } fclose(f_in); fclose(f_out); free(in_data); } /* * Read a string and get a byte array or contents of a file from it * returns 0 on success */ int read_hex_file_string(char* str, u8* buf, int len) { FILE* f = fopen(str, "rb"); if(f) { fseek(f, 0L, SEEK_END); u32 fsize = ftell(f); rewind(f); if(fsize == len) { fread(buf, 1, len, f); fclose(f); return 0; } else { printf("Invalid file size for %s! Expected 0x%x, got 0x%x\n", str, len, fsize); return 1; } } if(hex2bytes(str, strlen(str), buf, len)) return 1; return 0; } void display_help() { printf("Usage: twltool [args]\n"); printf("Commands:\n"); printf(" nandcrypt\n"); printf(" modcrypt\n"); printf(" boot2\n"); printf(" syscrypt\n"); printf("nandcrypt: (de)crypt DSi NAND\n"); printf(" --cid [file/hex CID] eMMC CID\n"); printf(" --consoleid [file/hex ID] DSi ConsoleID\n"); printf(" --in [infile] Input image\n"); printf(" --out [outfile] Output file (optional)\n"); printf(" --3ds Crypt 3DS TWLNAND\n"); printf(" --3dsbrute Bruteforce 3DS ConsoleID\n"); printf(" --cidfile [outfile] Output name for bruteforced CID (optional)\n"); printf(" --n3ds New3DS bruteforce (use with --3ds)\n"); printf("modcrypt: (de)crypt SRL modcrypt sections\n"); printf(" --in [infile] Input SRL\n"); printf(" --out [outfile] Output file (optional)\n"); printf("boot2: decrypt boot2 image to arm7.bin and arm9.bin\n"); printf(" --in [infile] Input image\n"); printf(" --debug Crypt debug boot2 (devkits, TWL_FIRM, ...)\n"); printf(" --ntrboot Crypt ntrboot image\n"); printf("syscrypt: crypt system files with ES block crypto (dev.kp, tickets, ...)\n"); printf(" --in [infile] Input SRL\n"); printf(" --out [outfile] Output file (optional)\n"); printf(" --consoleid [file/hex ID] DSi ConsoleID\n"); printf(" --normalkey [file/hex ID] Normalkey for ES encryption (overrides consoleID)\n"); printf(" --encrypt Encrypt file\n"); printf(" --3ds Using 3DS ConsoleID\n"); } int main(int argc, char* argv[]) { u8 consoleID[8] = {0}; u8 normalkey[16] = {0}; u8 cid[16] = {0}; char in[400] = {0}; char out[400] = {0}; char cidfile[400] = {0}; bool is3DS = false, isNtrboot = false; bool brute_cid = false; bool isN3DS = false; bool encrypt = false; bool use_normalkey = false; printf("TWLTool v1.7\n"); printf(" by WulfyStylez\n"); printf(" Special thanks to CaitSith2\n\n"); if(argc <= 1) display_help(); else { if(!strcmp(argv[1], "nandcrypt")) { if(argc < 4) { printf("Invalid options!\n"); display_help(); exit(EXIT_FAILURE); } for(i = 0; i < argc; i++) { if(!strcmp(argv[i],"--consoleid")) { if(read_hex_file_string(argv[i+1], consoleID, 8)) exit(EXIT_FAILURE); } if(!strcmp(argv[i],"--cid")) { if(read_hex_file_string(argv[i+1], cid, 16)) exit(EXIT_FAILURE); } if(!strcmp(argv[i],"--3ds")) is3DS = true; if(!strcmp(argv[i],"--3dsbrute")) brute_cid = true; if(!strcmp(argv[i],"--n3ds")) isN3DS = true; if(!strcmp(argv[i],"--in")) { strcpy(in, argv[i+1]); } if(!strcmp(argv[i],"--out")) { strcpy(out, argv[i+1]); } if(!strcmp(argv[i],"--cidfile")) { strcpy(cidfile, argv[i+1]); } } if(in[0] == 0) { printf("Invalid filename!\n"); display_help(); exit(EXIT_FAILURE); } if(out[0] == 0) strcpy(out,in); if(is3DS) nand_decrypt_3ds(cid, (u32*)consoleID, in, out, brute_cid, cidfile, isN3DS); else nand_decrypt_dsi(cid, (u32*)consoleID, in, out); } else if(!strcmp(argv[1], "modcrypt")) { if(argc < 4) { printf("Invalid options!\n"); display_help(); exit(EXIT_FAILURE); } for(i = 0; i < argc; i++) { if(!strcmp(argv[i],"--in")) { strcpy(in, argv[i+1]); } if(!strcmp(argv[i],"--out")) { strcpy(out, argv[i+1]); } } if(in[0] == 0) { printf("Invalid filename!\n"); display_help(); exit(EXIT_FAILURE); } if(out[0] == 0) strcpy(out,in); decrypt_srl(in, out); } else if(!strcmp(argv[1], "boot2")) { if(argc < 4) { printf("Invalid options!\n"); display_help(); exit(EXIT_FAILURE); } for(i = 0; i < argc; i++) { if(!strcmp(argv[i],"--in")) { strcpy(in, argv[i+1]); } if(!strcmp(argv[i],"--debug")) { is3DS = 1; } if(!strcmp(argv[i],"--ntrboot")) { isNtrboot = 1; } } if(in[0] == 0) { printf("Invalid filename!\n"); display_help(); exit(EXIT_FAILURE); } decrypt_boot2(in, is3DS, isNtrboot); } else if(!strcmp(argv[1], "syscrypt")) { if(argc < 6) { printf("Invalid options!\n"); display_help(); exit(EXIT_FAILURE); } for(i = 0; i < argc; i++) { if(!strcmp(argv[i],"--in")) { strcpy(in, argv[i+1]); } if(!strcmp(argv[i],"--out")) { strcpy(out, argv[i+1]); } if(!strcmp(argv[i],"--consoleid")) { if(read_hex_file_string(argv[i+1], consoleID, 8)) exit(EXIT_FAILURE); } if(!strcmp(argv[i],"--normalkey")) { if(read_hex_file_string(argv[i+1], normalkey, 16)) exit(EXIT_FAILURE); use_normalkey = true; } if(!strcmp(argv[i],"--encrypt")) { encrypt = true; } if(!strcmp(argv[i],"--3ds")) is3DS = true; } if(in[0] == 0) { printf("Invalid input filename! %s\n", in); display_help(); exit(EXIT_FAILURE); } if(out[0] == 0) strcpy(out,in); if(use_normalkey) { es_crypt_file_normalkey(in, out, (u32*)normalkey, encrypt); } else { es_crypt_file(in, out, (u32*)consoleID, encrypt, is3DS); } } else { printf("Invalid command!\n"); display_help(); exit(EXIT_FAILURE); } } return 0; }