def register_io_in(self): in_ioctl = x86.MultipleInstr() INPUT_BUFFER_PORT = x86.mem('[ECX + 4]') INPUT_BUFFER_SIZE = x86.mem('[ECX]') in_ioctl += x86.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 8) # size indicator / port in_ioctl += x86.Jnz(":FAIL") in_ioctl += x86.Cmp(self.IO_STACK_OUPUT_BUFFER_LEN, 0x4) in_ioctl += x86.Jnz(":FAIL") in_ioctl += x86.Mov('ECX', self.IO_STACK_INPUT_BUFFER) in_ioctl += x86.Mov('EDX', INPUT_BUFFER_PORT) in_ioctl += x86.Mov('ECX', INPUT_BUFFER_SIZE) in_ioctl += x86.Xor('EAX', 'EAX') in_ioctl += x86.Cmp('ECX', 0x1) in_ioctl += x86.Jnz(":IN_2_OR_4") in_ioctl += x86.In('AL', 'DX') in_ioctl += x86.Jmp(':SUCCESS') in_ioctl += x86.Label(":IN_2_OR_4") in_ioctl += x86.Cmp('ECX', 0x2) in_ioctl += x86.Jnz(":IN_4") in_ioctl += x86.In('AX', 'DX') in_ioctl += x86.Jmp(':SUCCESS') in_ioctl += x86.Label(":IN_4") in_ioctl += x86.In('EAX', 'DX') in_ioctl += x86.Label(":SUCCESS") in_ioctl += x86.Mov('EDX', self.IRP_OUTPUT_BUFFER) in_ioctl += x86.Mov(x86.mem('[EDX]'), 'EAX') in_ioctl += x86.Xor('EAX', 'EAX') in_ioctl += x86.Ret() in_ioctl += x86.Label(":FAIL") in_ioctl += x86.Mov('EAX', 0x0C000000D) in_ioctl += x86.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_IN_IOCTL, in_ioctl.get_code())
def register_io_out(self): out_ioctl = x86.MultipleInstr() INPUT_BUFFER_SIZE = x86.mem('[ECX]') INPUT_BUFFER_PORT = x86.mem('[ECX + 4]') INPUT_BUFFER_VALUE = x86.mem('[ECX + 8]') out_ioctl += x86.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 0xc) # size indicator / port / value out_ioctl += x86.Jnz(":FAIL") out_ioctl += x86.Mov('ECX', self.IO_STACK_INPUT_BUFFER) out_ioctl += x86.Mov('EDX', INPUT_BUFFER_PORT) out_ioctl += x86.Mov('EAX', INPUT_BUFFER_VALUE) out_ioctl += x86.Mov('ECX', INPUT_BUFFER_SIZE) out_ioctl += x86.Cmp('ECX', 0x1) out_ioctl += x86.Jnz(":OUT_2_OR_4") out_ioctl += x86.Out('DX', 'AL') out_ioctl += x86.Jmp(':SUCCESS') out_ioctl += x86.Label(":OUT_2_OR_4") out_ioctl += x86.Cmp('ECX', 0x2) out_ioctl += x86.Jnz(":OUT_4") out_ioctl += x86.Out('DX', 'AX') out_ioctl += x86.Jmp(':SUCCESS') out_ioctl += x86.Label(":OUT_4") out_ioctl += x86.Out('DX', 'EAX') out_ioctl += x86.Label(":SUCCESS") out_ioctl += x86.Xor('EAX', 'EAX') out_ioctl += x86.Ret() out_ioctl += x86.Label(":FAIL") out_ioctl += x86.Mov('EAX', 0x0C000000D) out_ioctl += x86.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_OUT_IOCTL, out_ioctl.get_code())
def register_kernel_call(self): # expect in buffer: the address to call and all dword to push on the stack CCall_IOCTL = x86.MultipleInstr() CCall_IOCTL += x86.Mov('EAX', self.IO_STACK_INPUT_BUFFER_LEN) CCall_IOCTL += x86.Cmp('EAX', 0) CCall_IOCTL += x86.Jz(":FAIL") # Need at least the function to call CCall_IOCTL += x86.Mov('ECX', self.IO_STACK_INPUT_BUFFER) CCall_IOCTL += x86.Label(':PUSH_NEXT_ARG') CCall_IOCTL += x86.Cmp('EAX', 4) CCall_IOCTL += x86.Jz(":DO_CALL") CCall_IOCTL += x86.Sub('EAX', 4) INPUT_BUFFER_NEXT_ARG = x86.create_displacement(base='ECX', index='EAX') CCall_IOCTL += x86.Mov('EBX', INPUT_BUFFER_NEXT_ARG) CCall_IOCTL += x86.Push('EBX') CCall_IOCTL += x86.Jmp(':PUSH_NEXT_ARG') CCall_IOCTL += x86.Label(":DO_CALL") CCall_IOCTL += x86.Mov('EAX', x86.mem('[ECX]')) CCall_IOCTL += x86.Call('EAX') CCall_IOCTL += x86.Mov('EDX', self.IRP_OUTPUT_BUFFER) CCall_IOCTL += x86.Mov(x86.mem('[EDX]'), 'EAX') CCall_IOCTL += x86.Xor('EAX', 'EAX') CCall_IOCTL += x86.Ret() CCall_IOCTL += x86.Label(":FAIL") CCall_IOCTL += x86.Mov('EAX', 0x0C000000D) CCall_IOCTL += x86.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_KCALL_IOCTL, CCall_IOCTL.get_code())
def test_get_context_address_32(self, proc32): code = x86.MultipleInstr() code += x86.Mov("EAX", 0x42424242) code += x86.Label(":LOOP") code += x86.Jmp(":LOOP") t = proc32.execute(code.get_code()) time.sleep(0.5) cont = t.context assert cont.Eax == 0x42424242
def _upgrade_driver_inject_base_upgrade(self): kldbgdrv = self.kldbgdrv upgrade = x86.MultipleInstr() IoControlCode_on_stack = x86.create_displacement(base='EBP', disp=-0x30) IO_STACK_LOCATION_on_stack = x86.create_displacement(base='EBP', disp=-0x34) IoStatus_on_stack = x86.create_displacement(base='EBP', disp=-0x3c) IRP_on_stack = x86.create_displacement(base='EBP', disp=+0x0c) upgrade += x86.Mov('EBX', IoControlCode_on_stack) upgrade += x86.Mov('EAX', x86.create_displacement(disp=kldbgdrv + self.HANDLE_ARRAY_ADDR)) upgrade += x86.Label(":LOOP") upgrade += x86.Mov('ECX', x86.create_displacement('EAX')) upgrade += x86.Cmp('EBX', 'ECX') upgrade += x86.Jnz(':END') upgrade += x86.Mov('EAX', x86.create_displacement('EAX', disp=4)) # ESI -> IO_STACK_LOCATION # EDI -> IRP upgrade += x86.Mov('ESI', IO_STACK_LOCATION_on_stack) upgrade += x86.Mov('EDI', IRP_on_stack) upgrade += x86.Call('EAX') upgrade += x86.Mov(IoStatus_on_stack, 'EAX') upgrade += x86.JmpAt(kldbgdrv + self.normal_end) upgrade += x86.Label(":END") upgrade += x86.Cmp('ECX', 0) upgrade += x86.Jnz(':NEXT') upgrade += x86.JmpAt(kldbgdrv + self.failed_offset) upgrade += x86.Label(":NEXT") upgrade += x86.Add('EAX', 8) upgrade += x86.Jmp(':LOOP') # Write new driver code self.kdbg.write_pfv_memory(kldbgdrv + self.init_function_offset, str(upgrade.get_code())) # Write first array dest self.write_pfv_ptr(kldbgdrv + self.HANDLE_ARRAY_ADDR, kldbgdrv + self.FIRST_ARRAY_ADDR) self.write_pfv_ptr(kldbgdrv + self.FIRST_ARRAY_ADDR, 0) self.write_pfv_ptr(kldbgdrv + self.FIRST_ARRAY_ADDR + 4, 0) # Jump hijack jump_init_function = x86.Jmp(self.init_function_offset - (self.hijack_offset)) self.kdbg.write_pfv_memory(kldbgdrv + self.hijack_offset, str(jump_init_function.get_code())) self.is_upgraded = True self.ioctl_array = kldbgdrv + self.FIRST_ARRAY_ADDR self.ioctl_array_ptr = kldbgdrv + self.HANDLE_ARRAY_ADDR self.next_code_addr = kldbgdrv + self.init_function_offset + len(upgrade.get_code())
def test_get_context_address_32(self): with Calc32() as calc: code = x86.MultipleInstr() code += x86.Mov("EAX", 0x42424242) code += x86.Label(":LOOP") code += x86.Jmp(":LOOP") t = calc.execute(code.get_code()) time.sleep(0.5) cont = t.context self.assertEqual(cont.Eax, 0x42424242)
def test_set_thread_context_32(self, proc32): code = x86.MultipleInstr() code += x86.Label(":LOOP") code += x86.Jmp(":LOOP") data_len = len(code.get_code()) code += x86.Ret() t = proc32.execute(code.get_code()) time.sleep(0.1) assert proc32.is_exit == False t.suspend() ctx = t.context ctx.Eip += data_len ctx.Eax = 0x11223344 t.set_context(ctx) t.resume() time.sleep(0.1) assert t.exit_code == 0x11223344
def hook_ntcreatefile(kdbg, ignore_jump_space_check=False): """Hook NtCreateFile, the hook write the filename to a shared memory page""" nt_create_file = kdbg.resolve_symbol("nt!NtCreateFile") if not ignore_jump_space_check: # Check that function begin with mov edi, edi for the hook short jump if kdbg.read_word(nt_create_file) != 0xff8b: # mov edi, edi print(hex(kdbg.read_word(nt_create_file))) raise ValueError( "Cannot hook fonction that doest not begin with <mov edi,edi> (/f to force if hook already in place)" ) # Check there is 5 bytes free before for the hook long jump if kdbg.read_virtual_memory(nt_create_file - 5, 5) not in ["\x90" * 5, "\xCC" * 5 ]: #NOP * 5 ; INT 3 * 5 print(kdbg.read_virtual_memory(nt_create_file - 5, 5)) raise ValueError( "Cannot hook fonction that is not prefixed with 5 nop/int 3") # Allocate memory for the shared buffer kernel<->user # the format is: # [addr] -> size of size already taken # then: # DWORD string_size # char[string_size] filename data_kernel_addr = kdbg.alloc_memory(0x1000) kdbg.write_pfv_memory(data_kernel_addr, "\x00" * 0x1000) # Map the shared buffer to userland data_user_addr = kdbg.map_page_to_userland(data_kernel_addr, 0x1000) # Allocate memory for the hook shellcode_addr = kdbg.alloc_memory(0x1000) # shellcode shellcode = x86.MultipleInstr() # Save register shellcode += x86.Push('EAX') shellcode += x86.Push('ECX') shellcode += x86.Push('EDI') shellcode += x86.Push('ESI') # Check that there is space remaining, else don't write it shellcode += x86.Cmp(x86.deref(data_kernel_addr), 0x900) shellcode += x86.Jnb(":END") # Get 3rd arg (POBJECT_ATTRIBUTES ObjectAttributes) shellcode += x86.Mov('EAX', x86.mem('[ESP + 0x1c]')) # 0xc + 0x10 for push # Get POBJECT_ATTRIBUTES.ObjectName (PUNICODE_STRING) shellcode += x86.Mov('EAX', x86.mem('[EAX + 0x8]')) shellcode += x86.Xor('ECX', 'ECX') # Get PUNICODE_STRING.Length shellcode += x86.Mov('CX', x86.mem('[EAX + 0]')) # Get PUNICODE_STRING.Buffer shellcode += x86.Mov('ESI', x86.mem('[EAX + 4]')) # Get the next free bytes in shared buffer shellcode += x86.Mov('EDI', data_kernel_addr + 4) shellcode += x86.Add('EDI', x86.deref(data_kernel_addr)) # Write (DWORD string_size) in our 'struct' shellcode += x86.Mov(x86.mem('[EDI]'), 'ECX') # update size taken in shared buffer shellcode += x86.Add(x86.deref(data_kernel_addr), 'ECX') shellcode += x86.Add(x86.deref(data_kernel_addr), 4) # Write (char[string_size] filename) in our 'struct' shellcode += x86.Add('EDI', 4) shellcode += x86.Rep + x86.Movsb() shellcode += x86.Label(":END") # Restore buffer shellcode += x86.Pop('ESI') shellcode += x86.Pop('EDI') shellcode += x86.Pop('ECX') shellcode += x86.Pop('EAX') # Jump to NtCreateFile shellcode += x86.JmpAt(nt_create_file + 2) # Write shellcode kdbg.write_pfv_memory(shellcode_addr, shellcode.get_code()) long_jump = x86.Jmp(shellcode_addr - (nt_create_file - 5)) # Write longjump to shellcode kdbg.write_pfv_memory(nt_create_file - 5, long_jump.get_code()) # Write shortjump NtCreateFile -> longjump short_jmp = x86.Jmp(-5) kdbg.write_pfv_memory(nt_create_file, short_jmp.get_code()) # Return address of shared buffer in userland return data_user_addr
def is_driver_already_upgraded(self): """Check if the driver have already been upgraded by checking if the jump hijack is in place""" jump_hijack = x86.Jmp(self.init_function_offset - (self.hijack_offset)).get_code() mem = self.kdbg.read_virtual_memory(self.kldbgdrv + self.hijack_offset, len(jump_hijack)) return mem == str(jump_hijack)
"EBX", x86.mem("[EAX + 0x18]")) # EBX : first base ! (base of current module) GetProcAddress32 += x86.Cmp("EBX", 0) GetProcAddress32 += x86.Jz(":DLL_NOT_FOUND") GetProcAddress32 += x86.Mov( "ECX", x86.mem("[EAX + 0x30]")) # RCX = NAME (UNICODE_STRING.Buffer) GetProcAddress32 += x86.Push("ECX") GetProcAddress32 += x86.Call(":FUNC_STRLENW32") GetProcAddress32 += x86.Pop("EDI") # Current name GetProcAddress32 += x86.Mov("ECX", "EAX") GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x18]")) GetProcAddress32 += x86.Rep + x86.CmpsW() GetProcAddress32 += x86.Test("ECX", "ECX") GetProcAddress32 += x86.Jz(":DLL_FOUND") GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX]")) GetProcAddress32 += x86.Jmp(":a_dest") GetProcAddress32 += x86.Label(":DLL_FOUND") GetProcAddress32 += x86.Mov("EAX", x86.mem("[EBX + 0x3c]")) # rax = PEBASE RVA GetProcAddress32 += x86.Add("EAX", "EBX") # RAX = PEBASE GetProcAddress32 += x86.Add("EAX", 0x18) # ;OPTIONAL HEADER GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x60]")) # ;ecx = RVA export dir GetProcAddress32 += x86.Add("ECX", "EBX") # ;ecx = export_dir GetProcAddress32 += x86.Mov("EAX", "ECX") GetProcAddress32 += x86.Push("EAX") # Save it # ; EBX = BASE | EAX = EXPORT DIR GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 24] ")) GetProcAddress32 += x86.Mov("EBP", "ECX") # ;EBP = NB names GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 32] ")) # EDX = names array RVA GetProcAddress32 += x86.Add("EDX", "EBX") # RDX = names array