# SPDX-FileCopyrightText: 2022 Andrius Štikonas # SPDX-FileCopyrightText: 2017 Jeremiah Orians # # SPDX-License-Identifier: GPL-3.0-or-later DEFINE add_rax, 4883C0 DEFINE add_rsp, 4883C4 DEFINE add_rbx, 4883C3 DEFINE add_rsi, 4883C6 DEFINE add_rbx,rsi 4801F3 DEFINE add_r15,rbx 4901DF DEFINE add_r15,rdi 4901FF DEFINE add_rbx,[rdi+BYTE] 48035F DEFINE call E8 DEFINE call_[rcx+BYTE] FF51 DEFINE call_[r14+BYTE] 41FF56 DEFINE call_[r14+DWORD] 41FF96 DEFINE cmp_al, 3C DEFINE cmp_rbx,rdx 4839D3 DEFINE inc_rax 48FFC0 DEFINE je8 74 DEFINE jmp8 EB DEFINE jmp E9 DEFINE jne8 75 DEFINE jne 0F85 DEFINE lea_rdx,[rip+DWORD] 488D15 DEFINE lea_r8,[rip+DWORD] 4C8D05 DEFINE mov_dh, B6 DEFINE mov_rbp,rsp 4889E5 DEFINE mov_rbx,rax 4889C3 DEFINE mov_rbx,rcx 4889CB DEFINE mov_rcx,rax 4889C1 DEFINE mov_rcx,rbx 4889D9 DEFINE mov_rcx,rdi 4889F9 DEFINE mov_rcx,r8 4C89C1 DEFINE mov_rcx,r9 4C89C9 DEFINE mov_rcx,r12 4C89E1 DEFINE mov_rcx,r15 4C89F9 DEFINE mov_rdi,rax 4889C7 DEFINE mov_rdx,rbx 4889DA DEFINE mov_rdx,rsp 4889E2 DEFINE mov_rsp,rbp 4889EC DEFINE mov_r8,rax 4989C0 DEFINE mov_r8,rbx 4989D8 DEFINE mov_r8,rdi 4989F8 DEFINE mov_r8,rsp 4989E0 DEFINE mov_r8,r15 4D89F8 DEFINE mov_r9,rax 4989C1 DEFINE mov_r9,rbx 4989D9 DEFINE mov_r9,rdi 4989F9 DEFINE mov_r9,r15 4D89F9 DEFINE mov_r15,rsi 4989F7 DEFINE mov_r15,rax 4989C7 DEFINE mov_[rax], C700 DEFINE mov_[rbx], C603 DEFINE mov_[r8], 49C700 DEFINE mov_[rax],BYTE C600 DEFINE mov_[rax],WORD 66C700 DEFINE mov_[rbx],WORD 66C703 DEFINE mov_[r15],WORD 6641C707 DEFINE mov_al,[rbx] 8A03 DEFINE mov_[rax],r15 4C8938 DEFINE mov_[rbx],ax 668903 DEFINE mov_rax,[rdx+BYTE] 488B42 DEFINE mov_rbx,[rdi+BYTE] 488B5F DEFINE mov_rcx,[rcx+BYTE] 488B49 DEFINE mov_rcx,[rdi+BYTE] 488B4F DEFINE mov_rdx,[rcx+BYTE] 488B51 DEFINE mov_r12,[rsp+BYTE] 4C8B6424 DEFINE mov_r14,[rdx+BYTE] 4C8B72 DEFINE mov_rcx,[rip+DWORD] 488B0D DEFINE mov_rdx,[rip+DWORD] 488B15 DEFINE mov_r8,[rip+DWORD] 4C8B05 DEFINE mov_[rax+BYTE],rbx 488958 DEFINE mov_[rax+BYTE],rcx 488948 DEFINE mov_[rax+BYTE],rsi 488970 DEFINE mov_[rip+DWORD],rcx 48890D DEFINE mov_[rip+DWORD],rax 488905 DEFINE pop_rax 58 DEFINE pop_rcx 59 DEFINE pop_rdi 5F DEFINE pop_rdx 5A DEFINE pop_r8 4158 DEFINE pop_r9 4159 DEFINE push 6A DEFINE push_rax 50 DEFINE push_rbx 53 DEFINE push_rdi 57 DEFINE push_rdx 52 DEFINE push_rsi 56 DEFINE push_rsp 54 DEFINE push_r8 4150 DEFINE ret C3 DEFINE sub_rbx, 4883EB DEFINE sub_rsp, 4883EC DEFINE sub_rbx,rsi 4829F3 DEFINE sub_r15,rdi 4929FF DEFINE test_eax,eax 85C0 DEFINE test_ecx,ecx 85C9 DEFINE test_r8,r8 4D85C0 DEFINE test_r15,r15 4D85FF DEFINE xor_ecx,ecx 31C9 DEFINE xor_edx,edx 31D2 DEFINE xor_esi,esi 31F6 DEFINE xor_r8,r8 4D31C0 DEFINE xor_r9,r9 4D31C9 DEFINE xor_r15,r15 4D31FF # efi_main(void *image_handle, struct efi_system_table *system) :_start mov_rbp,rsp # save stack pointer mov_[rip+DWORD],rcx %image_handle # save image_handle mov_rbx,rcx # save image_handle mov_rax,[rdx+BYTE] !64 # system->out mov_[rip+DWORD],rax %system_out # save system->out mov_r14,[rdx+BYTE] !96 # system->boot xor_ecx,ecx # timeout = 0 xor_edx,edx # watchdog_code = 0 xor_r8,r8 # data_size = 0 xor_r9,r9 # watchdog_data = 0 sub_rsp, !32 # allocate shadow stack space for UEFI function call_[r14+DWORD] %240 # system->boot->set_watchdog_timer # Open Loaded Image protocol mov_r9,rbx # arg4 = image_handle lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL mov_rcx,rbx # arg1 = image_handle call %open_protocol # open protocol mov_rdi,rax # save image mov_[rip+DWORD],rax %image # save image # Get root file system mov_r9,rbx # arg4 = image_handle lea_rdx,[rip+DWORD] %SIMPLE_FS_PROTOCOL # guid = &SIMPLE_FS_PROTOCOL mov_rcx,[rdi+BYTE] !24 # arg1 = root_device = image->device mov_[rip+DWORD],rcx %root_device # save root_device call %open_protocol # open protocol mov_rcx,rax # get rootfs # Get root directory lea_rdx,[rip+DWORD] %rootdir # arg2 = &rootdir push_rax # allocate shadow stack space for UEFI function push_rax # allocate shadow stack space for UEFI function call_[rcx+BYTE] !8 # rootfs->open_volume(rootfs, &rootdir) pop_rax # deallocate stack pop_rax # deallocate stack # Push command line arguments onto stack mov_rbx,[rdi+BYTE] !56 # options = image->load_options mov_rdx,rbx # save beginning of load_options add_rbx,[rdi+BYTE] !48 # go to the end of load_options push !0 # Save end of arguments (NULL) onto stack :loop_options cmp_rbx,rdx # Check if we are done je8 !loop_options_done # We are done sub_rbx, !2 # --options mov_al,[rbx] # *options cmp_al, !0x20 # if *options != ' ' jne8 !loop_options # then continue looping mov_[rbx], !0 # zero it add_rbx, !2 # ++options push_rbx # push another argument onto stack jmp8 !loop_options # next argument :loop_options_done pop_r8 # get input file test_r8,r8 # Check if argument is specified jne8 !arg_done # then use it # Else use default_file lea_r8,[rip+DWORD] %default_file # Use "kaem.amd64" :arg_done # Open file for reading push_rdx # allocate stack for fin mov_rdx,rsp # arg2 = &fin push !1 # arg5 = EFI_FILE_READ_ONLY push !1 # prepare to set arg4 to EFI_FILE_MODE_READ pop_r9 # arg4 = EFI_FILE_MODE_READ # arg3 = script mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir sub_rsp, !32 # allocate shadow stack space for UEFI function call_[rcx+BYTE] !8 # rootdir->open() test_eax,eax # if status != EFI_SUCCESS jne %abort # then exit without closing file mov_r12,[rsp+BYTE] !40 # get fin # Allocate pool for command xor_edx,edx # zero RDX mov_dh, !0x10 # arg2 = 4096 = 0x1000 call %allocate_pool # allocate memory mov_rbx,rax # get command :next_command xor_esi,esi # i = 0 xor_r15,r15 # command_length = 0 :read_command call %read_byte # read another byte c cmp_al, !0xa # if c == '\n' je8 !read_command_done # then we are done with this command cmp_al, !0x20 # if c == ' ' jne8 !read_command_comments test_r15,r15 # and command_length == 0 jne8 !read_command_comments mov_r15,rsi # command_length = i :read_command_comments cmp_al, !0x23 # if c == '#' then process comment jne8 !read_command_store_char # else store char :read_command_skip_comment call %read_byte # get another char cmp_al, !0xa # if c == '\n' jne8 !read_command_skip_comment # continue reading until newline jmp8 !next_command # deal with another line :read_command_store_char add_rbx,rsi # rbx = &command[i] mov_[rbx],ax # command[i] = c sub_rbx,rsi # rbx = &command[0] add_rsi, !2 # location of the next char jmp8 !read_command # continue looping :read_command_done test_r15,r15 # if command_length == 0 je8 !next_command # deal with another line add_rbx,rsi # rbx = &command[i] mov_[rbx],WORD @0 # command[i] = 0 sub_rbx,rsi # rbx = &command[0] add_rsi, !2 # add 2 to get string length with NULL terminator lea_rdx,[rip+DWORD] %prefix # get prefix " +> " call %File_Print # print it mov_rdx,rbx # get command call %File_Print # print it lea_rdx,[rip+DWORD] %suffix # get suffix "\n\r" call %File_Print # print it # Remove command line options add_r15,rbx # go to the space separating command and its options mov_[r15],WORD @0 # zero it to hide command line options # Open executable file for reading push_rdx # allocate stack for fcmd mov_rdx,rsp # arg2 = &fcmd push !1 # arg5 = EFI_FILE_READ_ONLY push !1 # prepare to set arg4 to EFI_FILE_MODE_READ pop_r9 # arg4 = EFI_FILE_MODE_READ mov_r8,rbx # arg3 = command mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir sub_rsp, !32 # allocate shadow stack space for UEFI function call_[rcx+BYTE] !8 # rootdir->open() test_eax,eax # if status != EFI_SUCCESS jne %print_error # then exit add_rsp, !40 # deallocate stack pop_rdi # get fcmd # Restore command line arguments mov_[r15],WORD @0x20 # restore command line options by readding ' ' # Allocate pool for file_info xor_edx,edx # zero RDX mov_dh, !0x10 # arg2 = 4096 = 0x1000 call %allocate_pool # allocate memory mov_r9,rax # get file_info (arg4 for get_info) # Get file info push_rax # save file_info push_rax # allocate stack for file_size mov_r8,rsp # arg3 = &file_size mov_[r8], %0x1000 # file_size = 0x1000 lea_rdx,[rip+DWORD] %FILE_INFO_PROTOCOL # arg2 = &EFI_FILE_INFO_PROTOCOL mov_rcx,rdi # arg1 = fcmd sub_rsp, !32 # allocate shadow stack space for UEFI function call_[rcx+BYTE] !64 # fcmd->get_info(fcmd, &guid, &file_size, file_info) add_rsp, !40 # deallocate stack pop_rcx # restore file_info mov_rdx,[rcx+BYTE] !8 # get file_size # Free file_info pool push_rdx # save file_size onto stack push_rax # allocate shadow stack space for UEFI function call_[r14+BYTE] !72 # system->boot->free_pool(file_info) pop_rax # deallocate stack pop_rdx # restore file_size from stack (arg2 for allocate_pool) # Allocate pool for executable push_rdx # save file_size onto stack call %allocate_pool # allocate memory mov_r15,rax # get executable pop_rdx # restore file_size # Load executable into memory push_rdx # save file_size onto stack mov_r8,r15 # arg3 = executable mov_rdx,rsp # arg2 = &file_size mov_rcx,rdi # arg1 = fcmd push_rax # allocate shadow stack space for UEFI push_rax # allocate shadow stack space for UEFI push_rax # allocate shadow stack space for UEFI call_[rcx+BYTE] !32 # fcmd->read(fcmd, &file_size, executable) pop_rax # deallocate stack pop_rax # deallocate stack pop_rax # deallocate stack # Close fcmd push_rax # allocate stack mov_rcx,rdi # arg1 = fcmd call_[rcx+BYTE] !16 # fcmd->close(fcmd) pop_rax # restore stack pop_rdi # restore file_size # Allocate memory for device_path struct push !28 # 4 + sizeof(struct efi_device_path_protocol) pop_rdx # arg2 = 28 call %allocate_pool # allocate memory mov_r8,rax # get device_path # Initialize struct mov_[rax],BYTE !1 # device_path->type = HARDWARE_DEVICE_PATH inc_rax # next member mov_[rax],BYTE !3 # device_path->subtype = MEMORY_MAPPED inc_rax # next member mov_[rax],WORD @24 # device_path->length = 24 add_rax, !2 # next member mov_[rax], %1 # device_path->memory_type = EFI_LOADER_CODE add_rax, !4 # next member mov_[rax],r15 # device_path->start_address = executable add_rax, !8 # next member add_r15,rdi # end_address = executable + file_size mov_[rax],r15 # device_path->end_address = end_address sub_r15,rdi # restore r15 = executable add_rax, !8 # next member mov_[rax],BYTE !0x7f # device_path[1].type = END_HARDWARE_DEVICE_PATH inc_rax # next member mov_[rax],BYTE !0xff # device_path[1].subtype = END_ENTIRE_DEVICE_PATH inc_rax # next member mov_[rax],WORD @4 # device_path[1].length = 4 # Load image push_r8 # save device_path push_rax # allocate stack for child_ih push_rsp # arg6 = &child_ih push_rdi # arg5 = file size mov_r9,r15 # arg4 = executable # arg3 = device_path mov_rdx,[rip+DWORD] %image_handle # arg2 = image_handle xor_ecx,ecx # arg1 = 0 sub_rsp, !32 # allocate shadow stack space for UEFI function call_[r14+DWORD] %200 # system->boot->load_image() add_rsp, !48 # deallocate stack pop_rdi # save child_ih # Free device_path pool pop_rcx # arg1 = device_path push_rax # allocate shadow stack space for UEFI function call_[r14+BYTE] !72 # system->boot->free_pool(device_path) pop_rax # deallocate stack # Free executable pool mov_rcx,r15 # arg1 = executable push_rax # allocate shadow stack space for UEFI function call_[r14+BYTE] !72 # system->boot->free_pool(executable) pop_rax # deallocate stack # Open Child Image mov_r9,rdi # arg4 = child_ih lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL push_rdx # save &LOADED_IMAGE_PROTOCOL mov_rcx,r9 # arg1 = child_ih call %open_protocol # open protocol mov_[rax+BYTE],rbx !56 # child_image->load_options = command mov_[rax+BYTE],rsi !48 # set child_image->load_options_size mov_rcx,[rip+DWORD] %image # get image mov_rcx,[rcx+BYTE] !24 # image->device mov_[rax+BYTE],rcx !24 # child_image->device = image->device mov_r8,rdi # arg3 = image_handle pop_rdx # arg2 = &LOADED_IMAGE_PROTOCOL mov_rcx,r8 # arg1 = image_handle call %close_protocol # close protocol # Run command xor_r8,r8 # arg3 = 0 (ExitData) xor_edx,edx # arg2 = 0 (ExitData size) mov_rcx,rdi # arg1 = child_ih push_rax # allocate shadow stack space for UEFI push_rax # allocate shadow stack space for UEFI push_rax # allocate shadow stack space for UEFI call_[r14+DWORD] %208 # system->boot->start_image() pop_rcx # deallocate stack pop_rcx # deallocate stack pop_rcx # deallocate stack test_eax,eax # check if return code is 0 jne8 !print_error # print error and exit jmp %next_command # process another line from kaem script :print_error push_rax # save exit code lea_rdx,[rip+DWORD] %subprocess_error # get error message call %File_Print # print it pop_rax # restore exit code # Close script file and exit :terminate # Free pool mov_rcx,rbx # arg1 = command push_rax # save exit code push_rax # allocate shadow stack space for UEFI function call_[r14+BYTE] !72 # system->boot->free_pool(commmand) mov_rcx,r12 # arg1 = fin call_[rcx+BYTE] !16 # fin->close(fin) mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir call_[rcx+BYTE] !16 # rootdir->close(rootdir) pop_rax # deallocate stack pop_rax # restore exit code # Exit without closing script file :terminate_2 mov_r8,[rip+DWORD] %image_handle # arg3 = image_handle push_r8 # save image_handle lea_rdx,[rip+DWORD] %SIMPLE_FS_PROTOCOL # guid = &SIMPLE_FS_PROTOCOL mov_rcx,[rip+DWORD] %root_device # arg1 = root_device call %close_protocol # close protocol pop_r8 # arg3 = image_handle lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL mov_rcx,r8 # arg1 = image_handle call %close_protocol # close protocol :abort mov_rsp,rbp # restore stack ret # return to UEFI # File_Print function # Receives WCHAR* in RDX :File_Print mov_rcx,[rip+DWORD] %system_out # get system_out push_rax # allocate shadow stack space for UEFI function push_rax # allocate shadow stack space for UEFI function call_[rcx+BYTE] !8 # system->out->output_string(system->out, WCHAR*) pop_rax # deallocate stack pop_rax # deallocate stack ret # read_byte function # reads a single character :read_byte mov_rcx,r12 # arg1 = fin push !1 # size = 1 mov_rdx,rsp # arg2 = &size push_rsi # allocate stack mov_r8,rsp # arg3 = &c push_rax # allocate shadow stack space for UEFI function push_rax # allocate shadow stack space for UEFI function push_rax # allocate shadow stack space for UEFI function call_[rcx+BYTE] !32 # fin->read() pop_rax # deallocate stack pop_rax # deallocate stack pop_rax # deallocate stack pop_rax # save c to rax pop_rcx # save size to rcx # If the file ended (0 bytes read) terminate test_ecx,ecx # if size = 0 je8 !terminate # then we are done ret # return # rcx: handle # rdx: &guid # r9: agent_handle # returns interface :open_protocol push_rax # allocate stack for interface mov_r8,rsp # arg3 = &interface push !1 # arg6 = EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL push !0 # arg5 = NULL sub_rsp, !32 # allocate shadow stack space for UEFI function call_[r14+DWORD] %280 # system->boot->open_protocol(handle, &guid, &interface, agent_handle, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL) add_rsp, !48 # deallocate stack pop_rax # get interface ret # rcx: handle # rdx: &guid # r8: agent_handle :close_protocol xor_r9,r9 # arg4 = NULL sub_rsp, !32 # allocate shadow stack space for UEFI function call_[r14+DWORD] %288 # system->boot->close_protocol(handle, &guid, agent_handle, 0) add_rsp, !32 # deallocate stack ret # rdx: number of bytes to allocate # r14: system->boot # returns pointer in rax :allocate_pool push_rdx # allocate stack for pool pointer mov_r8,rsp # arg3 = &pool push !2 pop_rcx # arg1 = EFI_LOADER_DATA sub_rsp, !24 # allocate shadow stack space for UEFI call_[r14+BYTE] !64 # system->boot->allocate_pool(EFI_LOADER_DATA, 2048, &pool) add_rsp, !24 # deallocate stack pop_rax # get pool ret # Protocol GUIDs :LOADED_IMAGE_PROTOCOL %0x5b1b31a1 @0x9562 @0x11d2 !0x8e !0x3f !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b :SIMPLE_FS_PROTOCOL %0x0964e5b22 @0x6459 @0x11d2 !0x8e !0x39 !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b :FILE_INFO_PROTOCOL %0x09576e92 @0x6d3f @0x11d2 !0x8e !0x39 !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b :default_file "k" "a" "e" "m" "." "a" "m" "d" "6" "4" @0 :prefix " " "+" ">" " " @0 :subprocess_error "S" "u" "b" "p" "r" "o" "c" "e" "s" "s" " " "e" "r" "r" "o" "r" :suffix !0xa !0 !0xd !0 @0 # L"\n\r" :image_handle %0 %0 :image %0 %0 :rootdir %0 %0 :root_device %0 %0 :system_out %0 %0 :PE32_end