borntohonk/Switch-Ghidra-Guides

Some Typo's in your keygen scripts.

Closed this issue ยท 9 comments

In both keygen files, you need to change to the following:

import argeparse

change to

import argparse

Also in the file: scripts/erista_keygen.py, you have an error on the path on line 21

subprocess.run(f'hactoolnet --keyset prod.keys -t pk11 0100000000000819/romfs/nx/package1 --outdir 0100000000000819/romfs/nx/pkg1', stdout = subprocess.DEVNULL)

Change to:

subprocess.run(f'hactoolnet --keyset prod.keys -t pk11 0100000000000819/romfs/a/package1 --outdir 0100000000000819/romfs/nx/pkg1', stdout = subprocess.DEVNULL)

Modding both files fixes errors - you could also add a python requirements.txt , If not, users should run - "pip install argparse" to install that module to python.

Thanks for the keygens though, they work well.

Change to:

subprocess.run(f'hactoolnet --keyset prod.keys -t pk11 0100000000000819/romfs/a/package1 --outdir 0100000000000819/romfs/nx/pkg1', stdout = subprocess.DEVNULL)

This actually is not broken.

/a/ is for mariko.

/nx/ is for erista, the reason it's failing is because you do not have the appropriate keys. (they are not identical, but they produce the same master_key at the end)

the keys required are described in the README.md, but the tl:dr really is you need tsec_secret_26 (acl 0x00, https://switchbrew.org/wiki/TSEC#ACLs ) to derive package1_key_%% beyond 6.2.0, and tsec_secret_26 requires hardware glitching of the tsec aes scp key register and bruteforcing. (can be done with rcm payload, though none that do this task are public, and bruteforcing part is done on a pc)*

here's some reading material if you are interested in that, combined its all relevant, but plutoo's gist is what is required:
disclaimer; none of these actually show you what you have to do, or tell you what to do:

https://gist.githubusercontent.com/plutooo/733318dbb57166d203c10d12f6c24e06/raw/15c5b2612ab62998243ce5e7877496466cabb77f/tsec.txt
https://hexkyz.blogspot.com/2021/11/je-ne-sais-quoi-falcons-over-horizon.html
https://www.unknowncheats.me/forum/3141470-post1.html
https://github.com/vbe0201/faucon
https://github.com/CAmadeus/falcon-tools
https://gitlab.com/Nxyoom/tsec-exploration
https://github.com/Thog/ghidra_falcon/releases/tag/master
https://github.com/envytools/envytools
"if you're using hekate you can try to replicate this with something like max77620_regulator_set_voltage(REGULATOR_SD0, 720000 + extra); and synchronize the start and end of the glitch using the falcon mailbox."

something like this:

things to get working:
stage 0:
verify keys in succession are dumped and written on screen/to sd card


stage 1:

if falcon mailbox X
 print content of mailbox to screen/to sd card
 verify screen/to sd card prints
 
stage 2:
stop if other mailbox
verify stop after recieving mailbox

stage 3:
if mailbox
print sor1
if other mailbox
stop

stage 4:
mass spam print sor1 of many csecrets
verify output

stage 5:
repeat with csigenc of 00
verify authhash/csecret 00 is printed to screen/to sd card

stage 6:
use mailbox condition to set voltage
print csigenc
use other mailbox to revert

main.asm (falcon heavysecure entry example for dumping acl 0x03 tsec secrets, built from requiem from https://github.com/CAmadeus/falcon-tools )

.section #ns_code

// Set up the Falcon stack pointer.
mov $r13 #FALCON_HWCFG
iord $r13 I[$r13]
shr b32 $r13 0x9
and $r13 0x1FF
shl b32 $r13 0x8
mov $sp $r13
lcall #main
exit

pushdef(`key_data_addr', `$r5')

main:
    mov $r15 -0x10
    add $sp -0x11C

    mpush $r8

    // Allocate memory for the Key Data table.
    mov $r9 $sp
    add b32 $r9 $r9 0xC4
    and key_data_addr $r9 $r15

    // Copy Key Data into DMEM.
    mov b32 $r10 key_data_addr
    mov $r11 #KEY_TABLE_START
    mov $r12 #KEY_TABLE_SIZE
    lcall #memcpy_i2d

    // Copy the signed microcode portion to DMEM.
    clear b32 $r10
    mov $r11 #HS_PAYLOAD_PHYS_ADDR
    ld b32 $r12 D[key_data_addr + 0x20]
    lcall #memcpy_i2d

    // Remap the signed microcode and tag it as secure.
    mov b32 $r11 $r10
    mov $r10 #HS_PAYLOAD_START
    mov b32 $r13 $r10
    mov $r14 0x1
    lcall #memcpy_d2i

    // Transfer the MAC of the secure payload into crypto register 6.
    clear b32 $r7
    mov b32 $r8 key_data_addr
    sethi $r8 0x60000
    cxset 0x2
    xdst $r7 $r8
    xdwait

    // Transfer the seed for the fake-signing key into crypto register 7.
    clear b32 $r7
    add b32 $r8 key_data_addr 0x10
    sethi $r8 0x70000
    cxset 0x2
    xdst $r7 $r8
    xdwait

    // Load in the cauth details for Heavy Secure mode authentication.
    ld b32 $r9 D[key_data_addr + 0x20]
    shl b32 $r9 0x10
    mov $r15 #HS_PAYLOAD_START
    shr b32 $r15 0x8
    or $r9 $r15
    mov $cauth $r9

    // Jump to Heavy Secure Mode!
    lcall #HS_PAYLOAD_START

    mpopaddret $r8 0x11C

popdef(`key_data_addr')

include(`base_define.asm')
include(`mmio.asm')
include(`memcpy.asm')
include(`memcpy_i2d.asm')
include(`memcpy_d2i.asm')
include(`tsec_wait_dma.asm')
include(`tsec_dma_write.asm')
include(`tsec_set_key.asm')

.align 0x100
.section #ns_data 0x200
.equ #KEY_TABLE_SIZE 0x7C

KEY_TABLE_START:
HS_PAYLOAD_MAC:     .skip 0x10
HS_PAYLOAD_SEED:    .skip 0x10
HS_PAYLOAD_SIZE:    .b32 0x00000000

.align 0x100
HS_PAYLOAD_PHYS_ADDR:
.section #hs_code 0x200
.equ #HS_PAYLOAD_START 0x200

hs_main:
    mov $r11 #FALCON_MAILBOX1
    mov $r12 0xBADC0DED
    iowr I[$r11] $r12

    // load csecret with acl value of 0x03 push it into sor1 with #tsec_set_key to be readable ( https://switchbrew.org/wiki/TSEC#Secrets )
    csecret $c0 0x39 // loads the secret desired
    cxset 0x2 // changes target for next operation to cryto instead of DMA
    xdld $r10 $r10 // load from crypto to $r10, $r10 is equivelant of $c0
    xdwait // waits for xdld with cxset to complete
    lcall #tsec_set_key // this takes what is stored in $r10 and pushes it through sor1

    // Clear the HS signature and return back to NS mode.
    csigclr
    ret

.align 0x100

I am considering pushing a smaller writeup on this, but it is one of my low-priority side-projects.

ideally i'd figure out how to rebase it to mainline hekate as it has features for this and better code to amend. (i currently use a hwinit based launcher, using the gplv2 example from https://gitlab.com/Nxyoom/tsec-exploration/-/tree/main/launcher )

i have pushed this: https://github.com/borntohonk/falcon-tools which is an amended fork of both: https://github.com/CAmadeus/falcon-tools and https://gitlab.com/Nxyoom/tsec-exploration/-/tree/main/launcher

which is preconfigured to dump acl 0x13/0x03 secrets (though build enviroment is very much linux based)

and a sample pushed https://github.com/borntohonk/falcon-tools/releases/tag/dump_readable_secrets (which i have only tested on my own erista console)

edit: if you have years to spare, or want to try a luck-based approach, you also could use this gimmick/joke bruteforcer i made.

from hashlib import sha256
from multiprocessing import Process
import os
from datetime import datetime

list = [
	'7c20cef183f6184f7c5a877040ec63fa44ad42178b1aa6af9932568fc468e426',	'43449338c1bc8ceb1b3232a611f955f9095254f492117a158528589cd16f2930',	'2816295b45e08837846afbe093cd4a3ab5492174798d2e1872fceeccc0463e0f',	'eb06713d87ad94c9832549eb2057f014b5fd34853c0f8ce4108aecd3b23c8a58', 
	'7fafa6babbc8600ec42969ac81e16701320c4611e4cc910b4c51adcf14363212',	'49371c6ccb2cf64c10633164c202a3f7d03a17a0e0098ab7bcd9f84ae9a4805c',	'8745f02b86bbf722654e43b1fef32ac22c740d10aa4432b93d5b2035523c2c94',	'd6ecab46e243d80af83ca5f8bdf440b595459ecb39f2e083a50f793ade04822c', 
	'8ca7cc625a593699870e11056aa52124cc5565df0d934b6431854910314b6c51',	'6836e01fce672b276e3746fac8e7a133a986c7922f2bddebd3c231fcd6a6bac5',	'96240c628444c83b527fb8de96bbc39e3c9ef4c46952286a57f9d7efe0847ae2',	'affa6d401592ca2ac21451064a632b6eecc72bb887d29ac93ce7c0de3e2c9212', 
	'd19495a97b6dd1dac8ee099107c731cdab49c0e1ec5b3cd1b38480d70dbe7003',	'1c081ad4d8c7da9291ec4f5de06e558177fc0faf613fb7d9ff0005ef66f63d61',	'b2429bfb5de59191b825f9675c4320b5e1dfb5cc0e7a8161c5dab64313eb9a63',	'34141a2aa355cfa1d14ec921db288d1cd04c810c3c30c69abb34bb1542a9966f', 
	'678d1b92f9dd7e46bdc9bd96378896f58da01e933d5056c812c9d3a948b709b4',	'7e0aec4bfd4160035d04aec8e2aa0e7668ae769681f8a2c6ba62d31791f072aa',	'641622358b351d50e7f3f2cfee6864a68fa7803a649a2bcade226a99a143918a',	'86899161828e7b3ebb8b90e73261d2e34b8b5314f070f9811cf4173570024665', 
	'735146a321f46b7d130226b0aea05d2042363374b0674e9015d80c4eb17f6e7b',	'9c90367e3b4191706f1018861f1622e233d905445e6f2463bedbdea2f4395205',	'6e607b4265f213530df9d6c9574af4a3a6d5c7282f19214144ac03cda69b68f1',	'fdc6ed08368fc2f19f8f8979fe7545f6f9136897d369045d3afd160756d82c3d', 
	'40c4d1dfb08fb9963ad20076681651a124f325a6065db51c1b88b2efd8799d01',	'eb9b9813c0a08f7c6af56907e09c5df8e53d2d4299914038fa867578ced8b656',	'c62e4708e163252adeac56f749cf025a8921a86f786e2cc396304ebd2e625354',	'0bde3d9cb209d1c132d1c9e80c0ccf595e3feef411be7ee590e181af57421815', 
	'a9dc5a0a27de9214909c8dd933cdd82e6df1cc2d09cb654466406e2cebad0017',	'c58b9370c0c67dbebaf8925f734e29940a3de70d3815fd644f2835f4f0ebb106',	'02667ae7cbe9a608a648eec9876dc66159068aceb872901a085ce6968f5d17a1',	'2885bc4f35d01ad469997b6a36a9bfa2976d62ae5dc48a1f96ecbc73bc770528', 
	'14fc0140daafc49631356da9a6ef5d96ca20b8d45ce63e4227aededbcd0056bf',	'5ad7845f27ea0aa7c717ff56d4cffe5d060a374d86a0e820bdc13fc5f553226b',	'ab1cf064eedfeaa7f71db717bfbcfdbd73b6db7ba356e37cc299d8b731cffe24',	'81fdc5ebcd592f59a063a66155f6b08e48cc89e19c6fb8d3a2756c9ac0590f8f', 
	'c8312de41a98f7c55c4e21184b1f34a7578145c2cbeca78a9556978dd84939e3',	'b20226d3accc9e554278f3ba7157460ebff8a88757e850d57591342b0a275542',	'cefe01c9e3eeef1a73b8c10d742ae386279b7dff30a2fbc0aabd058c1f135833',	'd3ade4766781a5d9862b350867c2572dcb7f513b28c3a812170cd856dfb54f95', 
	'73f4a07cd1f061f81c42b32e3dd1ffa0ac1114d40df92205869e60a1e537d2ac',	'd97b8509b66ae9b33ed6d1e46b37449ed6f7f3e7f4bc03a59004994ff833bb71',	'08a0edf7bf91d7fa685ca77246b8394fa4edd0e06639e53e6fa835436b09560f',	'6d3a215979ae17a947e7c2772d1efec9b0ac9b0063f4e0a64fe93f779fc70188', 
	'20c358eeed4f03cc03ddbad4f9cf9e6f83a86c61fe434ee259789a63ba2178b7',	'07923cbd0e19d3b8c81d3f5d4df8ef58ec667f94e6096897de34c1ebf878b2b0',	'b8706c9d52b7fe020c3c833cfde328dbcda24c290be60c658b3c1784da85340b',	'e0c6273094f499180139a06133e582565cc1cd23478a6180914950a672e3bfaf', 
	'3477d86ed721fd5112c94a566f26b4d30cd7ae78de1b047eb21a709a7934d073',	'd2bf372d3a1b652a31b0fa1264086c8fdce8ab491889dce2cfb4db71eab758b8',	'fc65d00eb14406f76a940368722c4f3b8ab11d1abf44e32499103492cf714af9',	'fa7f4a5cb39ae9205177f3da8f8c2f88ec7f8d14b8c6f75b2dbb661f30ec076d', 
	'4aa215afd1a0ab118ab60db1fbc5ee769907a1b58813ec417e7f1519a5cc4243',	'ae64ed29f1158feee2c3e858b2868197c173b07e6d1d281dbd458449770c492b',	'083bd0a21da79ae6b63c9e01035fad9334983c79a43d555dba5481c6d531b30f',	'9cc2735bc70f0c756279c41b85dd558e00783dd8ec4202f4db0f6c384b43dda2', 
	'90483d58fc3e7c298b353f3d9295d8a81d8bb9f5182bcfcf3c8c60e9b6537aec',	'78a4c4ad790921ab5c6f3224ea394fb53e576110d1fa467b3aa942b5c141cfa8',	'a28b03e2bee0c18640f9607db3cf430af0fc7a9b61b002f3369333e13dec3080',	'eec13d2a63a89e35834d6e1c2ca879ef556e3e970efaf08bee406979f271e9a9', 
	'29b30980914a0201a195dab7c5494d2ca9c94205619c2f91dd74ddeea24d14f0',	'aeff5b69a19c6a1b767dfae9fd57ffcb11ba2f5eb34f0e013a922d9474218d11',	'6b07bc90e01a40ae51fd718e2ef751fb174c14c8cb4f68a00be847f020bdc1a6',	'374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb'
]


def random_hex_string(length=16):
    return os.urandom(length).hex()

def loop():
	for i in range(10000000000000000000000000000):
		key = random_hex_string()
		hash = sha256(bytes.fromhex(key)).hexdigest()
		keyhash = f'key: {key}, hash: {hash}'
		if hash in list:
			match_found = open('./key_match.ini', 'a')
			match_found.write(f'key is found: {key} with hash: {hash}\n')
			match_found.close()
			print(keyhash)



if __name__ =="__main__":
    p1 = Process(target=loop)
    p2 = Process(target=loop)
    p3 = Process(target=loop)
    p4 = Process(target=loop)
    p5 = Process(target=loop)
    p6 = Process(target=loop)
    p7 = Process(target=loop)
    p8 = Process(target=loop)

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()
    p6.start()
    p7.start()
    p8.start()

    p1.join()
    p2.join()
    p3.join()
    p4.join()
    p5.join()
    p6.join()
    p7.join()
    p8.join()

    print("done")
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")
    print("it ended at this time =", current_time)
    ```

Nice, thanks for the info. I've just made a test c# keygen app now and it seems to work fine, so far I have this:

hactoolnet --keyset decrypt.keys -t switchfs Firmware --title 0100000000000819 --romfsdir 0100000000000819/romfs/
hactoolnet --keyset decrypt.keys -t pk11 0100000000000819/romfs/a/package1 --outdir 0100000000000819/romfs/a/pkg1

decrypt.keys at this point only has:
mariko_bek = 6Axxxxx
mariko_kek = 41xxxxx

At this point when we can see the OEM Bootloader Version:

example:
Firmware 15.xx - OEM Bootloader Version: 0F (mariko_master_kek_source_0E)
Firmware 16 - OEM Bootloader Version: 10 (mariko_master_kek_source_0F)
Firmware 17 - OEM Bootloader Version: 11 (mariko_master_kek_source_10)
Firmware 18 - OEM Bootloader Version: 12 (mariko_master_kek_source_11)

Now we can write to our decrypt.keys - mariko_master_kek_source_(OEM Bootloader Version -0x1) = xxx

I wrote the program to get the key from Decrypted.bin automatically and append decrypt.keys, so now we can run hactool to output prod.keys with the new values added.

hactoolnet --keyset "decrypt.keys" -t keygen > prod.keys

I actually wrote a program called IPS Patch creator ages ago that can decrypt the switch firmware if you know the keys, and now I have this info for the keygen I can add this keygen code into it so that it can decrypt the firmware automatically without needing to update the keys manually. So thanks for your scripts are they we very helpfull in understanding how these keys are derived.

I actually wrote a program called IPS Patch creator ages ago that can decrypt the switch firmware if you know the keys, and now I have this info for the keygen I can add this keygen code into it so that it can decrypt the firmware automatically without needing to update the keys manually. So thanks for your scripts are they we very helpfull in understanding how these keys are derived.

yeah, i actually wrote the means to automatically update using mariko bek/kek, because the mariko package is always accessible with keys from a lower iteration because it is required for firmware updates, so initiating keygen while processing firmware files ensures you always have the latest mariko_master_kek_source which also ensures you will always have the latest mariko_master_kek, which in turn will provide you the latest master_key, etc etc

(you can always open 0100000000000819/romfs/a/package1, but not other nca's until you have the latest keyset, but with mariko package1 you obtain the master key encryption key source, which lets you just generate the keys you need)

This is nice to have information. I am so glad you posted those scripts as they make life alot easier without needing to update lockpic_rcm now. Before I was needing to wait until Atmosphere git was updated so I could get the keys to update that but now I don't need to anymore. This makes writing patches much faster now as I can dump FW as soon as it's available and then decrypt it, then use ghidra to dump decrpyted modules into c code - which makes it so much easier to trace the sub's where to patch if that patterns I normally use change. I am pretty chuffed I came across your git yesterday as I have learned alot from it.

Cheers.

no problem mate

PS. I know the Bootloader Version is shown in hactoolnet output, but for speed in code it seems to be at header address 0x150 in /0100000000000819/romfs/a/package1. For your scripts you can read address and use it to write mariko_master_kek_source_xx without having a prod.keys file first and then you can just generate a brand new file from scratch without appending anything.

PS. I know the Bootloader Version is shown in hactoolnet output, but for speed in code it seems to be at header address 0x150 in /0100000000000819/romfs/a/package1. For your scripts you can read address and use it to write mariko_master_kek_source_xx without having a prod.keys file first and then you can just generate a brand new file from scratch without appending anything.

I actually was just about to push a commit with that, as when i first made it it was lazily done.

edit: i realize what you're asking was something different, i guess that can be done also, but that depends on me providing mariko_bek and mariko_kek in plaintext too, which is why it works with an existing key file.

Thanks for updating your scripts, the new updated ones are better and work well. I've been busy all day but I'll have a look in a while and see if I can find anything else that could, but all seems good now on my end - from a noobs perspective :-).