def register_io_in(self): in_ioctl = x64.MultipleInstr() INPUT_BUFFER_SIZE = x64.mem('[RCX]') INPUT_BUFFER_PORT = x64.mem('[RCX + 8]') in_ioctl += x64.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 0x10) # size indicator / port in_ioctl += x64.Jnz(":FAIL") in_ioctl += x64.Mov('RCX', self.IO_STACK_INPUT_BUFFER) in_ioctl += x64.Mov('RDX', INPUT_BUFFER_PORT) in_ioctl += x64.Mov('RCX', INPUT_BUFFER_SIZE) in_ioctl += x64.Cmp('RCX', 0x1) in_ioctl += x64.Jnz(":OUT_2_OR_4") in_ioctl += x64.In('AL', 'DX') in_ioctl += x64.Jmp(':SUCCESS') in_ioctl += x64.Label(":OUT_2_OR_4") in_ioctl += x64.Cmp('RCX', 0x2) in_ioctl += x64.Jnz(":OUT_4") in_ioctl += x64.In('AX', 'DX') in_ioctl += x64.Jmp(':SUCCESS') in_ioctl += x64.Label(":OUT_4") in_ioctl += x64.In('EAX', 'DX') in_ioctl += x64.Label(":SUCCESS") in_ioctl += x64.Mov('RDX', self.IRP_OUTPUT_BUFFER) in_ioctl += x64.Mov(x64.mem('[RDX]'), 'RAX') in_ioctl += x64.Xor('RAX', 'RAX') in_ioctl += x64.Ret() in_ioctl += x64.Label(":FAIL") in_ioctl += x64.Mov('RAX', 0x0C000000D) in_ioctl += x64.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_IN_IOCTL, in_ioctl.get_code())
def register_io_out(self): out_ioctl = x64.MultipleInstr() INPUT_BUFFER_SIZE = x64.mem('[RCX]') INPUT_BUFFER_PORT = x64.mem('[RCX + 8]') INPUT_BUFFER_VALUE = x64.mem('[RCX + 0x10]') out_ioctl += x64.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 0x18) # size indicator / port / value out_ioctl += x64.Jnz(":FAIL") out_ioctl += x64.Mov('RCX', self.IO_STACK_INPUT_BUFFER) out_ioctl += x64.Mov('RDX', INPUT_BUFFER_PORT) out_ioctl += x64.Mov('RAX', INPUT_BUFFER_VALUE) out_ioctl += x64.Mov('RCX', INPUT_BUFFER_SIZE) out_ioctl += x64.Cmp('RCX', 0x1) out_ioctl += x64.Jnz(":OUT_2_OR_4") out_ioctl += x64.Out('DX', 'AL') out_ioctl += x64.Jmp(':SUCCESS') out_ioctl += x64.Label(":OUT_2_OR_4") out_ioctl += x64.Cmp('RCX', 0x2) out_ioctl += x64.Jnz(":OUT_4") out_ioctl += x64.Out('DX', 'AX') out_ioctl += x64.Jmp(':SUCCESS') out_ioctl += x64.Label(":OUT_4") out_ioctl += x64.Out('DX', 'EAX') out_ioctl += x64.Label(":SUCCESS") out_ioctl += x64.Xor('RAX', 'RAX') out_ioctl += x64.Ret() out_ioctl += x64.Label(":FAIL") out_ioctl += x64.Mov('RAX', 0x0C000000D) out_ioctl += x64.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_OUT_IOCTL, out_ioctl.get_code())
def sc_64_LoadLibrary(dll_path): dll = bytes("KERNEL32.DLL\x00".encode("utf-16-le")) api = b"LoadLibraryA\x00" if PY3 and isinstance(dll_path, str): dll_path = bytes(dll_path.encode()) LoadLibrary64_sc = x64.MultipleInstr() LoadLibrary64_sc += shellcraft.amd64.pushstr(dll) LoadLibrary64_sc += x64.Mov("RCX", "RSP") LoadLibrary64_sc += shellcraft.amd64.pushstr(api) LoadLibrary64_sc += x64.Mov("RDX", "RSP") LoadLibrary64_sc += x64.Call(":FUNC_GETPROCADDRESS64") LoadLibrary64_sc += x64.Mov("R10", "RAX") LoadLibrary64_sc += shellcraft.amd64.pushstr(dll_path) LoadLibrary64_sc += x64.Mov("RCX", "RSP") LoadLibrary64_sc += x64.Sub("RSP", 0x30) LoadLibrary64_sc += x64.And("RSP", -32) LoadLibrary64_sc += x64.Call("R10") LoadLibrary64_sc += x64.Label(":HERE") LoadLibrary64_sc += x64.Jmp(":HERE") LoadLibrary64_sc += windows.native_exec.nativeutils.GetProcAddress64 return LoadLibrary64_sc.get_code()
def register_alloc_memory(self): ExAllocatePoolWithTag = self.kdbg.get_symbol_offset("nt!ExAllocatePoolWithTag") if ExAllocatePoolWithTag is None: raise ValueError("Could not resolve <ExAllocatePoolWithTag>") INPUT_BUFFER_ALLOC_TYPE = x64.mem('[RCX]') INPUT_BUFFER_ALLOC_SIZE = x64.mem('[RCX + 0x8]') INPUT_BUFFER_ALLOC_TAG = x64.mem('[RCX + 0x10]') Alloc_IOCTL = x64.MultipleInstr() Alloc_IOCTL += x64.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 0x18) Alloc_IOCTL += x64.Jnz(':FAIL') Alloc_IOCTL += x64.Mov('RCX', self.IO_STACK_INPUT_BUFFER) Alloc_IOCTL += x64.Mov('R8', INPUT_BUFFER_ALLOC_TAG) Alloc_IOCTL += x64.Mov('RDX', INPUT_BUFFER_ALLOC_SIZE) Alloc_IOCTL += x64.Mov('RCX', INPUT_BUFFER_ALLOC_TYPE) Alloc_IOCTL += x64.Mov('RAX', ExAllocatePoolWithTag) Alloc_IOCTL += x64.Call('RAX') Alloc_IOCTL += x64.Mov('RBX', self.IRP_OUTPUT_BUFFER) Alloc_IOCTL += x64.Mov(x64.mem('[RBX]'), 'RAX') Alloc_IOCTL += x64.Xor('RAX', 'RAX') Alloc_IOCTL += x64.Ret() Alloc_IOCTL += x64.Label(":FAIL") Alloc_IOCTL += x64.Mov('RAX', 0x0C000000D) Alloc_IOCTL += x64.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_MEMALLOC_IOCTL, Alloc_IOCTL.get_code())
def sc_64_WinExec(exe): dll = bytes("KERNEL32.DLL\x00".encode("utf-16-le")) api = b"WinExec\x00" if PY3 and isinstance(exe, str): exe = bytes(exe.encode()) WinExec64_sc = x64.MultipleInstr() WinExec64_sc += shellcraft.amd64.pushstr(dll) WinExec64_sc += x64.Mov("RCX", "RSP") WinExec64_sc += shellcraft.amd64.pushstr(api) WinExec64_sc += x64.Mov("RDX", "RSP") WinExec64_sc += x64.Call(":FUNC_GETPROCADDRESS64") WinExec64_sc += x64.Mov("R10", "RAX") WinExec64_sc += shellcraft.amd64.pushstr(exe) WinExec64_sc += x64.Mov("RCX", "RSP") WinExec64_sc += x64.Sub("RSP", 0x30) WinExec64_sc += x64.And("RSP", -32) WinExec64_sc += x64.Call("R10") WinExec64_sc += x64.Label(":HERE") WinExec64_sc += x64.Jmp(":HERE") WinExec64_sc += windows.native_exec.nativeutils.GetProcAddress64 # Dirty infinite loop #WinExec64_sc +=# x64.Ret(), return WinExec64_sc.get_code()
def generate_64bits_execution_stub_from_syswow(x64shellcode): """shellcode must NOT end by a ret""" current_process = windows.current_process if not current_process.is_wow_64: raise ValueError( "Calling generate_64bits_execution_stub_from_syswow from non-syswow process" ) transition64 = x64.MultipleInstr() transition64 += x64.Call(":TOEXEC") transition64 += x64.Mov("RDX", "RAX") transition64 += x64.Shr("RDX", 32) transition64 += x64.Retf32() # 32 bits return addr transition64 += x64.Label(":TOEXEC") x64shellcodeaddr = windows.current_process.allocator.write_code( transition64.get_code() + x64shellcode) transition = x86.MultipleInstr() transition += x86.Call(CS_64bits, x64shellcodeaddr) transition += x86.Ret() stubaddr = windows.current_process.allocator.write_code( transition.get_code()) exec_stub = ctypes.CFUNCTYPE(ULONG64)(stubaddr) return exec_stub
def generate_64bits_execution_stub_from_syswow(x64shellcode): """shellcode must NOT end by a ret""" current_process = windows.current_process if not current_process.is_wow_64: raise ValueError( "Calling generate_64bits_execution_stub_from_syswow from non-syswow process" ) transition64 = x64.MultipleInstr() transition64 += x64.Call(":TOEXEC") transition64 += x64.Mov("RDX", "RAX") transition64 += x64.Shr("RDX", 32) transition64 += x64.Retf32() # 32 bits return addr transition64 += x64.Label(":TOEXEC") x64shellcodeaddr = thread_state.allocator.write_code( transition64.get_code() + x64shellcode) transition = x86.MultipleInstr() transition += x86.Call(CS_64bits, x64shellcodeaddr) # Reset the SS segment selector. # We need to do that due to a bug in AMD CPUs with RETF & SS # https://github.com/hakril/PythonForWindows/issues/10 # http://blog.rewolf.pl/blog/?p=1484 transition += x86.Mov("ECX", "SS") transition += x86.Mov("SS", "ECX") transition += x86.Ret() stubaddr = thread_state.allocator.write_code(transition.get_code()) exec_stub = ctypes.CFUNCTYPE(ULONG64)(stubaddr) return exec_stub
def test_x64_multiple_instr_add_instr_and_str(): res = x64.MultipleInstr() res += x64.Nop() res += "ret; ret; label :offset_3; ret" res += x64.Nop() res += x64.Label(":offset_5") assert res.get_code() == b"\x90\xc3\xc3\xc3\x90" assert res.labels == {":offset_3": 3, ":offset_5": 5}
def test_get_context_address_64(self, proc64): code = x64.MultipleInstr() code += x64.Mov("RAX", 0x4242424243434343) code += x64.Label(":LOOP") code += x64.Jmp(":LOOP") t = proc64.execute(code.get_code()) time.sleep(0.5) cont = t.context assert cont.Rax == 0x4242424243434343
def register_kernel_call(self): # expect in buffer: the address to call and all dword to push on the stack CCall_IOCTL = x64.MultipleInstr() CCall_IOCTL += x64.Mov('RAX', self.IO_STACK_INPUT_BUFFER_LEN) CCall_IOCTL += x64.Cmp('RAX', 0) CCall_IOCTL += x64.Jz(":FAIL") # Need at least the function to call CCall_IOCTL += x64.Mov('R15', 4 * 8) # Size to pop on the stack at the end (4 * push RDI) CCall_IOCTL += x64.Mov('R10', self.IO_STACK_INPUT_BUFFER) CCall_IOCTL += x64.Label(':PUSH_NEXT_ARG') CCall_IOCTL += x64.Cmp('RAX', (8 * 5)) CCall_IOCTL += x64.Jbe(":SETUP_REG_ARGS") CCall_IOCTL += x64.Sub('RAX', 8) INPUT_BUFFER_NEXT_ARG = x64.create_displacement(base='R10', index='RAX') CCall_IOCTL += x64.Mov('RBX', INPUT_BUFFER_NEXT_ARG) CCall_IOCTL += x64.Push('RBX') CCall_IOCTL += x64.Add('R15', 8) # Add at Size to pop on the stack at the end CCall_IOCTL += x64.Jmp(':PUSH_NEXT_ARG') CCall_IOCTL += x64.Label(":SETUP_REG_ARGS") # Could be done in a loop # But do I really want to generate x86 in a loop.. CCall_IOCTL += x64.Cmp('RAX', (8 * 5)) CCall_IOCTL += x64.Jz(":SETUP_4_ARGS") CCall_IOCTL += x64.Cmp('RAX', (8 * 4)) CCall_IOCTL += x64.Jz(":SETUP_3_ARGS") CCall_IOCTL += x64.Cmp('RAX', (8 * 3)) CCall_IOCTL += x64.Jz(":SETUP_2_ARGS") CCall_IOCTL += x64.Cmp('RAX', (8 * 2)) CCall_IOCTL += x64.Jz(":SETUP_1_ARGS") CCall_IOCTL += x64.Jmp(":SETUP_0_ARGS") CCall_IOCTL += x64.Label(":SETUP_4_ARGS") CCall_IOCTL += x64.Mov('R9', x64.mem('[R10 + 0x20]')) CCall_IOCTL += x64.Label(":SETUP_3_ARGS") CCall_IOCTL += x64.Mov('R8', x64.mem('[R10 + 0x18]')) CCall_IOCTL += x64.Label(":SETUP_2_ARGS") CCall_IOCTL += x64.Mov('RDX', x64.mem('[R10 + 0x10]')) CCall_IOCTL += x64.Label(":SETUP_1_ARGS") CCall_IOCTL += x64.Mov('RCX', x64.mem('[R10 + 8]')) CCall_IOCTL += x64.Label(":SETUP_0_ARGS") CCall_IOCTL += x64.Mov('RAX', x64.mem('[R10]')) # Fix Reserve space (calling convention) CCall_IOCTL += x64.Push('RDI') CCall_IOCTL += x64.Push('RDI') CCall_IOCTL += x64.Push('RDI') CCall_IOCTL += x64.Push('RDI') CCall_IOCTL += x64.Call('RAX') CCall_IOCTL += x64.Mov('RDX', self.IRP_OUTPUT_BUFFER) CCall_IOCTL += x64.Mov(x64.mem('[RDX]'), 'RAX') CCall_IOCTL += x64.Xor('RAX', 'RAX') CCall_IOCTL += x64.Add('RSP', 'R15') CCall_IOCTL += x64.Ret() CCall_IOCTL += x64.Label(":FAIL") CCall_IOCTL += x64.Mov('RAX', 0x0C000000D) CCall_IOCTL += x64.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_KCALL_IOCTL, CCall_IOCTL.get_code())
def test_get_context_address_64(self): with Calc64() as calc: code = x64.MultipleInstr() code += x64.Mov("RAX", 0x4242424243434343) code += x64.Label(":LOOP") code += x64.Jmp(":LOOP") t = calc.execute(code.get_code()) time.sleep(0.5) cont = t.context self.assertEqual(cont.Rax, 0x4242424243434343)
def _upgrade_driver_inject_base_upgrade(self): kldbgdrv = self.kldbgdrv upgrade = x64.MultipleInstr() # R14 : IOCODE # RSI -> IO_STACK_LOCATION # RDI -> IRP upgrade = x64.MultipleInstr() upgrade += x64.Cmp('R14', self.NORMAL_IO_CODE) upgrade += x64.Jz(self.normal_io_offset - (self.init_driver_offset + len(upgrade.get_code()))) upgrade += x64.Mov('Rax', x64.create_displacement(disp=kldbgdrv + self.HANDLE_ARRAY_ADDR)) upgrade += x64.Label(":LOOP") upgrade += x64.Mov('RCX', x64.create_displacement('RAX')) upgrade += x64.Cmp('R14', 'RCX') upgrade += x64.Jnz(':END') upgrade += x64.Mov('RAX', x64.create_displacement('RAX', disp=8)) upgrade += x64.Call('RAX') upgrade += x64.Mov('RBX', 'RAX') upgrade += x64.JmpAt(kldbgdrv + self.normal_end_offset) upgrade += x64.Label(":END") upgrade += x64.Cmp('RCX', 0) upgrade += x64.Jnz(':NEXT') upgrade += x64.JmpAt(kldbgdrv + self.fail_offset) upgrade += x64.Label(":NEXT") upgrade += x64.Add('RAX', 0x10) upgrade += x64.Jmp(':LOOP') self.kdbg.write_pfv_memory(kldbgdrv + self.init_driver_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 + 8, 0) # Jump hijack jump_init_function = x64.Jmp(self.init_driver_offset - (self.hijack_offset)) self.kdbg.write_pfv_memory(kldbgdrv + self.hijack_offset, str(jump_init_function.get_code())) self.ioctl_array = kldbgdrv + self.FIRST_ARRAY_ADDR self.ioctl_array_ptr = kldbgdrv + self.HANDLE_ARRAY_ADDR self.next_code_addr = kldbgdrv + self.init_driver_offset + len(upgrade.get_code()) self.is_upgraded = True
def test_set_thread_context_64(self, proc64): code = x64.MultipleInstr() code += x64.Label(":LOOP") code += x64.Jmp(":LOOP") data_len = len(code.get_code()) code += x64.Ret() t = proc64.execute(code.get_code()) time.sleep(0.1) assert proc64.is_exit == False t.suspend() ctx = t.context ctx.Rip += data_len ctx.Rax = 0x11223344 t.set_context(ctx) t.resume() time.sleep(0.1) assert t.exit_code == 0x11223344
def sc_64_LoadLibrary(dll_path): dll = "KERNEL32.DLL\x00".encode("utf-16-le") api = "LoadLibraryA\x00" LoadLibrary64_sc = x64.MultipleInstr() map(LoadLibrary64_sc.__iadd__, [ shellcraft.amd64.pushstr(dll), x64.Mov("RCX", "RSP"), shellcraft.amd64.pushstr(api), x64.Mov("RDX", "RSP"), x64.Call(":FUNC_GETPROCADDRESS64"), x64.Mov("R10", "RAX"), shellcraft.amd64.pushstr(dll_path), x64.Mov("RCX", "RSP"), x64.Sub("RSP", 0x30), x64.And("RSP", -32), x64.Call("R10"), x64.Label(":HERE"), x64.Jmp(":HERE"), # Dirty infinite loop # x64.Ret(), windows.native_exec.nativeutils.GetProcAddress64, ]) return LoadLibrary64_sc.get_code()
import windows import windows.native_exec.simple_x64 as x64 import windows.native_exec.simple_x86 as x86 from windows.generated_def.winstructs import * StrlenW64 = x64.MultipleInstr() StrlenW64 += x64.Label(":FUNC_STRLENW64") StrlenW64 += x64.Push("RCX") StrlenW64 += x64.Push("RDI") StrlenW64 += x64.Mov("RDI", "RCX") StrlenW64 += x64.Xor("RAX", "RAX") StrlenW64 += x64.Xor("RCX", "RCX") StrlenW64 += x64.Dec("RCX") StrlenW64 += x64.Repne + x64.ScasW() StrlenW64 += x64.Not("RCX") StrlenW64 += x64.Dec("RCX") StrlenW64 += x64.Mov("RAX", "RCX") StrlenW64 += x64.Pop("RDI") StrlenW64 += x64.Pop("RCX") StrlenW64 += x64.Ret() StrlenA64 = x64.MultipleInstr() StrlenA64 += x64.Label(":FUNC_STRLENA64") StrlenA64 += x64.Push("RCX") StrlenA64 += x64.Push("RDI") StrlenA64 += x64.Mov("RDI", "RCX") StrlenA64 += x64.Xor("RAX", "RAX") StrlenA64 += x64.Xor("RCX", "RCX") StrlenA64 += x64.Dec("RCX") StrlenA64 += x64.Repne + x64.ScasB()