def perform_manual_getproc_loadlib_32_for_dbg(target, dll_name): dll = "KERNEL32.DLL\x00".encode("utf-16-le") api = "LoadLibraryA\x00" dll_to_load = dll_name + "\x00" RemoteManualLoadLibray = x86.MultipleInstr() code = RemoteManualLoadLibray code += x86.Mov("ECX", x86.mem("[ESP + 4]")) code += x86.Push(x86.mem("[ECX + 4]")) code += x86.Push(x86.mem("[ECX]")) code += x86.Call(":FUNC_GETPROCADDRESS32") code += x86.Push(x86.mem("[ECX + 8]")) code += x86.Call("EAX") # LoadLibrary code += x86.Pop("ECX") code += x86.Pop("ECX") code += x86.Ret() RemoteManualLoadLibray += nativeutils.GetProcAddress32 addr = target.virtual_alloc(0x1000) addr2 = addr + len(dll) addr3 = addr2 + len(api) addr4 = addr3 + len(dll_to_load) target.write_memory(addr, dll) target.write_memory(addr2, api) target.write_memory(addr3, dll_to_load) target.write_qword(addr4, addr) target.write_qword(addr4 + 4, addr2) target.write_qword(addr4 + 0x8, addr3) t = target.execute(RemoteManualLoadLibray.get_code(), addr4) return t
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 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 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_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_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 = x86.mem('[ECX]') INPUT_BUFFER_ALLOC_SIZE = x86.mem('[ECX + 4]') INPUT_BUFFER_ALLOC_TAG = x86.mem('[ECX + 8]') Alloc_IOCTL = x86.MultipleInstr() Alloc_IOCTL += x86.Cmp(self.IO_STACK_INPUT_BUFFER_LEN, 0xc) Alloc_IOCTL += x86.Jnz(':FAIL') Alloc_IOCTL += x86.Mov('ECX', self.IO_STACK_INPUT_BUFFER) Alloc_IOCTL += x86.Mov('EBX', INPUT_BUFFER_ALLOC_TAG) Alloc_IOCTL += x86.Push('EBX') Alloc_IOCTL += x86.Mov('EBX', INPUT_BUFFER_ALLOC_SIZE) Alloc_IOCTL += x86.Push('EBX') Alloc_IOCTL += x86.Mov('EBX', INPUT_BUFFER_ALLOC_TYPE) Alloc_IOCTL += x86.Push('EBX') Alloc_IOCTL += x86.Mov('EAX', ExAllocatePoolWithTag) Alloc_IOCTL += x86.Call('EAX') Alloc_IOCTL += x86.Mov('EDX', self.IRP_OUTPUT_BUFFER) Alloc_IOCTL += x86.Mov(x86.mem('[EDX]'), 'EAX') Alloc_IOCTL += x86.Xor('EAX', 'EAX') Alloc_IOCTL += x86.Ret() Alloc_IOCTL += x86.Label(":FAIL") Alloc_IOCTL += x86.Mov('EAX', 0x0C000000D) Alloc_IOCTL += x86.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_MEMALLOC_IOCTL, Alloc_IOCTL.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 get_peb_addr(self): dest = self.virtual_alloc(0x1000) if self.bitness == 32: store_peb = x86.MultipleInstr() store_peb += x86.Mov('EAX', x86.mem('fs:[0x30]')) store_peb += x86.Mov(x86.create_displacement(disp=dest), 'EAX') store_peb += x86.Ret() get_peb_code = store_peb.get_code() self.write_memory(dest, "\x00" * 4) self.write_memory(dest + 4, get_peb_code) self.create_thread(dest + 4, 0) time.sleep(0.01) peb_addr = struct.unpack("<I", self.read_memory(dest, 4))[0] return peb_addr else: store_peb = x64.MultipleInstr() store_peb += x64.Mov('RAX', x64.mem('gs:[0x60]')) store_peb += x64.Mov(x64.create_displacement(disp=dest), 'RAX') store_peb += x64.Ret() get_peb_code = store_peb.get_code() self.write_memory(dest, "\x00" * 8) self.write_memory(dest + 8, get_peb_code) self.create_thread(dest + 8, 0) time.sleep(0.01) peb_addr = struct.unpack("<Q", self.read_memory(dest, 8))[0] return peb_addr
def test_x86_multiple_instr_add_instr_and_str(): res = x86.MultipleInstr() res += x86.Nop() res += "ret; ret; label :offset_3; ret" res += x86.Nop() res += x86.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_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 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_execute_to_proc32(self, proc32): with proc32.allocated_memory(0x1000) as addr: shellcode = x86.MultipleInstr() shellcode += x86.Mov('EAX', 0x42424242) shellcode += x86.Mov(x86.create_displacement(disp=addr), 'EAX') shellcode += x86.Ret() proc32.execute(shellcode.get_code()) time.sleep(0.1) dword = proc32.read_dword(addr) assert dword == 0x42424242
def test_execute_to_32(self): with Calc32() as calc: data = calc.virtual_alloc(0x1000) shellcode = x86.MultipleInstr() shellcode += x86.Mov('EAX', 0x42424242) shellcode += x86.Mov(x86.create_displacement(disp=data), 'EAX') shellcode += x86.Ret() calc.execute(shellcode.get_code()) time.sleep(0.1) dword = struct.unpack("<I", calc.read_memory(data, 4))[0] self.assertEqual(dword, 0x42424242)
def test_x86_multithread_target(): try: assert x86.Mov("ECX", "SS").get_code() == b"\x8c\xd1" assert x86.Mov("SS", "ECX").get_code() == b"\x8e\xd1" assert x86.Ret().get_code() == b"\xc3" res = x86.MultipleInstr() res += x86.Mov("ECX", "SS") res += x86.Mov("SS", "ECX") res += x86.Ret() assert res.get_code() == b"\x8c\xd1\x8e\xd1\xc3" except Exception as e: threads_error.append(e) raise return True
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 _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())
class CurrentProcess(Process): """The current process""" get_peb = None get_peb_32_code = x86.MultipleInstr() get_peb_32_code += x86.Mov('EAX', x86.mem('fs:[0x30]')) get_peb_32_code += x86.Ret() get_peb_32_code = get_peb_32_code.get_code() get_peb_64_code = x64.MultipleInstr() get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]')) get_peb_64_code += x64.Ret() get_peb_64_code = get_peb_64_code.get_code() allocator = native_exec.native_function.allocator def get_peb_builtin(self): if self.get_peb is not None: return self.get_peb if self.bitness == 32: get_peb = native_exec.create_function(self.get_peb_32_code, [PVOID]) else: get_peb = native_exec.create_function(self.get_peb_64_code, [PVOID]) self.get_peb = get_peb return get_peb def _get_handle(self): return winproxy.GetCurrentProcess() def __del__(self): pass @property def pid(self): """Process ID :type: int """ return os.getpid() # Is there a better way ? @utils.fixedpropety def ppid(self): """Parent Process ID :type: int """ return [p for p in windows.system.processes if p.pid == self.pid][0].ppid @utils.fixedpropety def peb(self): """The Process Environment Block of the current process :type: :class:`PEB` """ return PEB.from_address(self.get_peb_builtin()()) @utils.fixedpropety def bitness(self): """The bitness of the process :returns: int -- 32 or 64""" import platform bits = platform.architecture()[0] return int(bits[:2]) def virtual_alloc(self, size): """Allocate memory in the current process :returns: int """ return winproxy.VirtualAlloc(dwSize=size) def write_memory(self, addr, data): """Write data at addr""" buffertype = (c_char * len(data)).from_address(addr) buffertype[:len(data)] = data return True def read_memory(self, addr, size): """Read size from adddr""" dbgprint('Read CurrentProcess Memory', 'READMEM') buffer = (c_char * size).from_address(addr) return buffer[:] def create_thread(self, lpStartAddress, lpParameter, dwCreationFlags=0): """Create a new thread .. note:: CreateThread https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx """ handle = winproxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags) return WinThread._from_handle(handle) def exit(self, code=0): """Exit the process""" return winproxy.ExitProcess(code)
def test_x86_instr_multiply(): res = x86.MultipleInstr() res += (x86.Nop() * 5) res += x86.Ret() assert res.get_code() == b"\x90\x90\x90\x90\x90\xc3"
def generate_write_at(addr): res = x86.MultipleInstr() res += x86.Mov(x86.deref(addr), "EAX") res += x86.Ret() return res.get_code()
def generate_read_at(addr): res = x86.MultipleInstr() res += x86.Mov("EAX", x86.deref(addr)) res += x86.Ret() return res.get_code()
class CurrentProcess(Process): """The current process""" get_peb = None get_peb_32_code = x86.MultipleInstr() get_peb_32_code += x86.Mov('EAX', x86.mem('fs:[0x30]')) get_peb_32_code += x86.Ret() get_peb_32_code = get_peb_32_code.get_code() get_peb_64_code = x64.MultipleInstr() get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]')) get_peb_64_code += x64.Ret() get_peb_64_code = get_peb_64_code.get_code() allocator = native_exec.native_function.allocator # Use RtlGetCurrentPeb ? def get_peb_builtin(self): if self.get_peb is not None: return self.get_peb if self.bitness == 32: get_peb = native_exec.create_function(self.get_peb_32_code, [PVOID]) else: get_peb = native_exec.create_function(self.get_peb_64_code, [PVOID]) self.get_peb = get_peb return get_peb def _get_handle(self): return winproxy.GetCurrentProcess() def __del__(self): pass @property def pid(self): """Process ID :type: :class:`int` """ return os.getpid() # Is there a better way ? @utils.fixedpropety def ppid(self): """Parent Process ID :type: :class:`int` """ return [p for p in windows.system.processes if p.pid == self.pid][0].ppid @utils.fixedpropety def peb(self): """The Process Environment Block of the current process :type: :class:`PEB` """ return PEB.from_address(self.get_peb_builtin()()) @utils.fixedpropety def bitness(self): """The bitness of the process :type: :class:`int` -- 32 or 64 """ import platform bits = platform.architecture()[0] return int(bits[:2]) def virtual_alloc(self, size, prot=PAGE_EXECUTE_READWRITE): """Allocate memory in the process :return: The address of the allocated memory :rtype: :class:`int` """ return winproxy.VirtualAlloc(dwSize=size, flProtect=prot) def virtual_free(self, addr): """Free memory in the process by virtual_alloc""" return winproxy.VirtualFree(addr) def write_memory(self, addr, data): """Write data at addr""" buffertype = (c_char * len(data)).from_address(addr) buffertype[:len(data)] = data return True def read_memory(self, addr, size): """Read ``size`` from ``addr`` :return: The data read :rtype: :class:`str` """ dbgprint('Read CurrentProcess Memory', 'READMEM') buffer = (c_char * size).from_address(addr) return buffer[:] def create_thread(self, lpStartAddress, lpParameter, dwCreationFlags=0): """Create a new thread :rtype: :class:`WinThread` or :class:`DeadThread` """ handle = winproxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags) return WinThread._from_handle(handle) def execute(self, code, parameter=0): """Execute native code ``code`` in the current thread. :rtype: :class:`int` the return value of the native code""" f = windows.native_exec.create_function(code, [PVOID, PVOID]) return f(parameter) def exit(self, code=0): """Exit the process""" return winproxy.ExitProcess(code) def wait(self, timeout=INFINITE): """Raise :class:`ValueError` to prevent deadlock :D""" raise ValueError("wait() on current thread") @utils.fixedpropety def peb_syswow(self): """The 64bits PEB of a SysWow64 process :type: :class:`PEB` """ if not self.is_wow_64: raise ValueError("Not a syswow process") return windows.syswow64.get_current_process_syswow_peb()
GetProcAddress64 += x64.Pop("RBX") GetProcAddress64 += x64.Ret() GetProcAddress64 += x64.Label(":DLL_NOT_FOUND") GetProcAddress64 += x64.Mov("RAX", 0xfffffffffffffffe) GetProcAddress64 += x64.Jmp(":RETURN") GetProcAddress64 += x64.Label(":API_NOT_FOUND") GetProcAddress64 += x64.Pop("RAX") GetProcAddress64 += x64.Mov("RAX", 0xffffffffffffffff) GetProcAddress64 += x64.Jmp(":RETURN") # Ajout des dependances GetProcAddress64 += StrlenW64 GetProcAddress64 += StrlenA64 ###### 32 bits ####### StrlenW32 = x86.MultipleInstr() StrlenW32 += x86.Label(":FUNC_STRLENW32") StrlenW32 += x86.Push("EDI") StrlenW32 += x86.Mov("EDI", x86.mem("[ESP + 8]")) StrlenW32 += x86.Push("ECX") StrlenW32 += x86.Xor("EAX", "EAX") StrlenW32 += x86.Xor("ECX", "ECX") StrlenW32 += x86.Dec("ECX") StrlenW32 += x86.Repne + x86.ScasW() StrlenW32 += x86.Not("ECX") StrlenW32 += x86.Dec("ECX") StrlenW32 += x86.Mov("EAX", "ECX") StrlenW32 += x86.Pop("ECX") StrlenW32 += x86.Pop("EDI") StrlenW32 += x86.Ret()
def trigger(self, dbg, exc): fault_addr = exc.ExceptionRecord.ExceptionInformation[1] eip = dbg.current_thread.context.pc print("Instruction at <{0:#x}> wrote at <{1:#x}>".format( eip, fault_addr)) dbg.single_step_counter = 4 return dbg.single_step() calc = windows.test.pop_calc_32(dwCreationFlags=DEBUG_PROCESS) d = MyDebugger(calc) code = calc.virtual_alloc(0x1000) data = calc.virtual_alloc(0x1000) injected = x86.MultipleInstr() injected += x86.Mov("EAX", 0) injected += x86.Mov(x86.deref(data), "EAX") injected += x86.Add("EAX", 4) injected += x86.Mov(x86.deref(data + 4), "EAX") injected += x86.Add("EAX", 8) injected += x86.Mov(x86.deref(data + 8), "EAX") injected += x86.Nop() injected += x86.Nop() injected += x86.Ret() calc.write_memory(code, injected.get_code()) d.add_bp(SingleStepOnWrite(data, size=8, events="W")) calc.create_thread(code, 0) d.loop()
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
section_text = binary32.add_section(section_text, PE.SECTION_TYPES.TEXT) section_data = binary32.add_section(section_data, PE.SECTION_TYPES.DATA) binary32.optional_header.addressof_entrypoint = section_text.virtual_address for library in imports.keys(): lib = binary32.add_library(library) for function in imports[library].keys(): lib.add_entry(function) for library in imports.keys(): for function in imports[library].keys(): imports[library][function] = binary32.predict_function_rva( library, function) + binary32.optional_header.imagebase code = x86.MultipleInstr() code += x86.Mov("EBP", "ESP") code += x86.Sub("ESP", 0x100) # GetStdHandle(STD_OUTPUT_HANDLE) code += x86.Push(STD_OUTPUT_HANDLE) code += x86.Call(call_import(imports["kernel32.dll"]["GetStdHandle"])) # WriteFile(eax, welcome, len_welcome, &esp+8, 0) code += x86.Lea("EDI", x86.mem("[ESP + 0x8]")) code += x86.Push(0) code += x86.Push("EDI") code += x86.Push(len(welcome)) code += x86.Push(data[welcome]) code += x86.Push("EAX") # hConsoleOutput code += x86.Call(call_import(imports["kernel32.dll"]["WriteFile"])) # GetStdHandle(STD_INPUT_HANDLE) code += x86.Push(STD_INPUT_HANDLE)
def register_test(self): DOINT3 = x86.MultipleInstr() DOINT3 += x86.Int3() DOINT3 += x86.Ret() self.upgrade_driver_add_new_ioctl_handler(DU_TEST_INT3_IOCTL, DOINT3.get_code())