def hook_IoCreateDriver(ql: Qiling, address: int, params): init_func = params["InitializationFunction"] ret_addr = ql.stack_read(0) # print("\n\n>>> IoCreateDriver at %x, going to execute function at %x, RET = %x\n" %(address, init_func, ret_addr)) # save SP & init_sp sp = ql.reg.sp init_sp = ql.os.init_sp ql.os.fcall = ql.os.fcall_select(STDCALL) ql.os.fcall.writeParams(( (POINTER, ql.loader.driver_object_address), (POINTER, ql.loader.regitry_path_address))) ql.until_addr = ret_addr # now lest emualate InitializationFunction try: ql.run(begin=init_func) except UcError as err: verify_ret(ql, err) # reset SP since emulated function does not cleanup ql.reg.sp = sp ql.os.init_sp = init_sp # ret_addr = ql.stack_read(0) # print("\n\nPC = %x, ret = %x\n" %(ql.pc, ret_addr)) return 0
def ioctl(self, params): def ioctl_code(DeviceType, Function, Method, Access): return (DeviceType << 16) | (Access << 14) | ( Function << 2) | Method alloc_addr = [] def build_mdl(buffer_size, data=None): if self.ql.archtype == QL_ARCH.X8664: mdl = MDL64() else: mdl = MDL32() mapped_address = self.heap.alloc(buffer_size) alloc_addr.append(mapped_address) mdl.MappedSystemVa.value = mapped_address mdl.StartVa.value = mapped_address mdl.ByteOffset = 0 mdl.ByteCount = buffer_size if data: written = data if len( data) <= buffer_size else data[:buffer_size] self.ql.mem.write(mapped_address, written) return mdl # quick simple way to manage all alloc memory if self.ql.ostype == QL_OS.WINDOWS: # print("DeviceControl callback is at 0x%x" %self.loader.driver_object.MajorFunction[IRP_MJ_DEVICE_CONTROL]) if self.ql.loader.driver_object.MajorFunction[ IRP_MJ_DEVICE_CONTROL] == 0: # raise error? return (None, None, None) # create new memory region to store input data _ioctl_code, output_buffer_size, in_buffer = params # extract data transfer method devicetype, function, ctl_method, access = _ioctl_code input_buffer_size = len(in_buffer) input_buffer_addr = self.heap.alloc(input_buffer_size) alloc_addr.append(input_buffer_addr) self.ql.mem.write(input_buffer_addr, bytes(in_buffer)) # create new memory region to store out data output_buffer_addr = self.heap.alloc(output_buffer_size) alloc_addr.append(output_buffer_addr) # allocate memory regions for IRP and IO_STACK_LOCATION if self.ql.archtype == QL_ARCH.X8664: irp_addr = self.heap.alloc(ctypes.sizeof(IRP64)) alloc_addr.append(irp_addr) irpstack_addr = self.heap.alloc( ctypes.sizeof(IO_STACK_LOCATION64)) alloc_addr.append(irpstack_addr) # setup irp stack parameters irpstack = IO_STACK_LOCATION64() # setup IRP structure irp = IRP64() irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(IO_STACK_LOCATION64)) else: irp_addr = self.heap.alloc(ctypes.sizeof(IRP32)) alloc_addr.append(irp_addr) irpstack_addr = self.heap.alloc( ctypes.sizeof(IO_STACK_LOCATION32)) alloc_addr.append(irpstack_addr) # setup irp stack parameters irpstack = IO_STACK_LOCATION32() # setup IRP structure irp = IRP32() irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(IO_STACK_LOCATION32)) #print("32 stack location size = 0x%x" %ctypes.sizeof(IO_STACK_LOCATION32)) #print("32 status block size = 0x%x" %ctypes.sizeof(IO_STATUS_BLOCK32)) #print("32 irp size = 0x%x" %ctypes.sizeof(IRP32)) #print("32 IoStatus offset = 0x%x" %IRP32.IoStatus.offset) #print("32 UserIosb offset = 0x%x" %IRP32.UserIosb.offset) #print("32 UserEvent offset = 0x%x" %IRP32.UserEvent.offset) #print("32 UserBuffer offset = 0x%x" %IRP32.UserBuffer.offset) #print("32 irpstack offset = 0x%x" %IRP32.irpstack.offset) #print("irp at %x, irpstack at %x" %(irp_addr, irpstack_addr)) logging.info("IRP is at 0x%x, IO_STACK_LOCATION is at 0x%x" % (irp_addr, irpstack_addr)) irpstack.Parameters.DeviceIoControl.IoControlCode = ioctl_code( devicetype, function, ctl_method, access) irpstack.Parameters.DeviceIoControl.OutputBufferLength = output_buffer_size irpstack.Parameters.DeviceIoControl.InputBufferLength = input_buffer_size irpstack.Parameters.DeviceIoControl.Type3InputBuffer.value = input_buffer_addr # used by IOCTL_METHOD_NEITHER self.ql.mem.write(irpstack_addr, bytes(irpstack)) if ctl_method == METHOD_NEITHER: irp.UserBuffer.value = output_buffer_addr # used by IOCTL_METHOD_NEITHER # allocate memory for AssociatedIrp.SystemBuffer # used by IOCTL_METHOD_IN_DIRECT, IOCTL_METHOD_OUT_DIRECT and IOCTL_METHOD_BUFFERED system_buffer_size = max(input_buffer_size, output_buffer_size) system_buffer_addr = self.heap.alloc(system_buffer_size) alloc_addr.append(system_buffer_addr) # init data from input buffer self.ql.mem.write(system_buffer_addr, bytes(in_buffer)) irp.AssociatedIrp.SystemBuffer.value = system_buffer_addr if ctl_method in (METHOD_IN_DIRECT, METHOD_OUT_DIRECT): # Create MDL structure for output data # used by both IOCTL_METHOD_IN_DIRECT and IOCTL_METHOD_OUT_DIRECT mdl = build_mdl(output_buffer_size) if self.ql.archtype == QL_ARCH.X8664: mdl_addr = self.heap.alloc(ctypes.sizeof(MDL64)) else: mdl_addr = self.heap.alloc(ctypes.sizeof(MDL32)) alloc_addr.append(mdl_addr) self.ql.mem.write(mdl_addr, bytes(mdl)) irp.MdlAddress.value = mdl_addr # everything is done! Write IRP to memory self.ql.mem.write(irp_addr, bytes(irp)) # set function args logging.info( "Executing IOCTL with DeviceObject = 0x%x, IRP = 0x%x" % (self.ql.loader.driver_object.DeviceObject, irp_addr)) self.set_function_args( (self.ql.loader.driver_object.DeviceObject, irp_addr)) try: # now emulate IOCTL's DeviceControl self.ql.run(self.ql.loader.driver_object. MajorFunction[IRP_MJ_DEVICE_CONTROL]) except UcError as err: verify_ret(self.ql, err) # read current IRP state if self.ql.archtype == QL_ARCH.X8664: irp_buffer = self.ql.mem.read(irp_addr, ctypes.sizeof(IRP64)) irp = IRP64.from_buffer(irp_buffer) else: irp_buffer = self.ql.mem.read(irp_addr, ctypes.sizeof(IRP32)) irp = IRP32.from_buffer(irp_buffer) io_status = irp.IoStatus # read output data output_data = b'' if io_status.Status.Status >= 0: if ctl_method == METHOD_BUFFERED: output_data = self.ql.mem.read(system_buffer_addr, io_status.Information.value) if ctl_method in (METHOD_IN_DIRECT, METHOD_OUT_DIRECT): output_data = self.ql.mem.read(mdl.MappedSystemVa.value, io_status.Information.value) if ctl_method == METHOD_NEITHER: output_data = self.ql.mem.read(output_buffer_addr, io_status.Information.value) # now free all alloc memory for addr in alloc_addr: # print("freeing heap memory at 0x%x" %addr) # FIXME: the output is not deterministic?? self.heap.free(addr) #print("\n") return io_status.Status.Status, io_status.Information.value, output_data else: # TODO: IOCTL for non-Windows. pass
def io_Write(self, in_buffer): if self.ql.ostype == QL_OS.WINDOWS: if self.ql.loader.driver_object.MajorFunction[IRP_MJ_WRITE] == 0: # raise error? return (False, None) if self.ql.archbit == 32: buf = self.ql.mem.read(self.ql.loader.driver_object.DeviceObject, ctypes.sizeof(DEVICE_OBJECT32)) device_object = DEVICE_OBJECT32.from_buffer(buf) else: buf = self.ql.mem.read(self.ql.loader.driver_object.DeviceObject, ctypes.sizeof(DEVICE_OBJECT64)) device_object = DEVICE_OBJECT64.from_buffer(buf) alloc_addr = [] def build_mdl(buffer_size, data=None): if self.ql.archtype == QL_ARCH.X8664: mdl = MDL64() else: mdl = MDL32() mapped_address = self.heap.alloc(buffer_size) alloc_addr.append(mapped_address) mdl.MappedSystemVa.value = mapped_address mdl.StartVa.value = mapped_address mdl.ByteOffset = 0 mdl.ByteCount = buffer_size if data: written = data if len( data) <= buffer_size else data[:buffer_size] self.ql.mem.write(mapped_address, written) return mdl # allocate memory regions for IRP and IO_STACK_LOCATION if self.ql.archtype == QL_ARCH.X8664: irp_addr = self.heap.alloc(ctypes.sizeof(IRP64)) alloc_addr.append(irp_addr) irpstack_addr = self.heap.alloc(ctypes.sizeof(IO_STACK_LOCATION64)) alloc_addr.append(irpstack_addr) # setup irp stack parameters irpstack = IO_STACK_LOCATION64() # setup IRP structure irp = IRP64() irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(IO_STACK_LOCATION64)) else: irp_addr = self.heap.alloc(ctypes.sizeof(IRP32)) alloc_addr.append(irp_addr) irpstack_addr = self.heap.alloc(ctypes.sizeof(IO_STACK_LOCATION32)) alloc_addr.append(irpstack_addr) # setup irp stack parameters irpstack = IO_STACK_LOCATION32() # setup IRP structure irp = IRP32() irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(IO_STACK_LOCATION32)) irpstack.MajorFunction = IRP_MJ_WRITE irpstack.Parameters.Write.Length = len(in_buffer) self.ql.mem.write(irpstack_addr, bytes(irpstack)) if device_object.Flags & DO_BUFFERED_IO: # BUFFERED_IO system_buffer_addr = self.heap.alloc(len(in_buffer)) alloc_addr.append(system_buffer_addr) self.ql.mem.write(system_buffer_addr, bytes(in_buffer)) irp.AssociatedIrp.SystemBuffer.value = system_buffer_addr elif device_object.Flags & DO_DIRECT_IO: # DIRECT_IO mdl = build_mdl(len(in_buffer)) if self.ql.archtype == QL_ARCH.X8664: mdl_addr = self.heap.alloc(ctypes.sizeof(MDL64)) else: mdl_addr = self.heap.alloc(ctypes.sizeof(MDL32)) alloc_addr.append(mdl_addr) self.ql.mem.write(mdl_addr, bytes(mdl)) irp.MdlAddress.value = mdl_addr else: # NEITHER_IO input_buffer_size = len(in_buffer) input_buffer_addr = self.heap.alloc(input_buffer_size) alloc_addr.append(input_buffer_addr) self.ql.mem.write(input_buffer_addr, bytes(in_buffer)) irp.UserBuffer.value = input_buffer_addr # everything is done! Write IRP to memory self.ql.mem.write(irp_addr, bytes(irp)) # set function args self.set_function_args( (self.ql.loader.driver_object.DeviceObject, irp_addr)) try: # now emulate self.ql.run( self.ql.loader.driver_object.MajorFunction[IRP_MJ_WRITE]) except UcError as err: verify_ret(self.ql, err) # read current IRP state if self.ql.archtype == QL_ARCH.X8664: irp_buffer = self.ql.mem.read(irp_addr, ctypes.sizeof(IRP64)) irp = IRP64.from_buffer(irp_buffer) else: irp_buffer = self.ql.mem.read(irp_addr, ctypes.sizeof(IRP32)) irp = IRP32.from_buffer(irp_buffer) io_status = irp.IoStatus # now free all alloc memory for addr in alloc_addr: # print("freeing heap memory at 0x%x" %addr) # FIXME: the output is not deterministic?? self.heap.free(addr) return True, io_status.Information.value
def io_Write(ql: Qiling, in_buffer: bytes): heap = ql.os.heap if ql.loader.driver_object.MajorFunction[IRP_MJ_WRITE] == 0: # raise error? return (False, None) driver_object_cls = ql.loader.driver_object.__class__ buf = ql.mem.read(ql.loader.driver_object.DeviceObject, ctypes.sizeof(driver_object_cls)) device_object = driver_object_cls.from_buffer(buf) alloc_addr = [] def build_mdl(buffer_size: int, data=None): mdl = make_mdl(ql.arch.bits) mapped_address = heap.alloc(buffer_size) alloc_addr.append(mapped_address) mdl.MappedSystemVa.value = mapped_address mdl.StartVa.value = mapped_address mdl.ByteOffset = 0 mdl.ByteCount = buffer_size if data: written = data if len(data) <= buffer_size else data[:buffer_size] ql.mem.write(mapped_address, written) return mdl # allocate memory regions for IRP and IO_STACK_LOCATION irp = make_irp(ql.arch.bits) irpstack_class = irp.irpstack._type_ irp_addr = heap.alloc(ctypes.sizeof(irp)) alloc_addr.append(irp_addr) irpstack_addr = heap.alloc(ctypes.sizeof(irpstack_class)) alloc_addr.append(irpstack_addr) # setup irp stack parameters irpstack = irpstack_class() # setup IRP structure irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(irpstack_class)) irpstack.MajorFunction = IRP_MJ_WRITE irpstack.Parameters.Write.Length = len(in_buffer) ql.mem.write(irpstack_addr, bytes(irpstack)) if device_object.Flags & DO_BUFFERED_IO: # BUFFERED_IO system_buffer_addr = heap.alloc(len(in_buffer)) alloc_addr.append(system_buffer_addr) ql.mem.write(system_buffer_addr, bytes(in_buffer)) irp.AssociatedIrp.SystemBuffer.value = system_buffer_addr elif device_object.Flags & DO_DIRECT_IO: # DIRECT_IO mdl = build_mdl(len(in_buffer)) mdl_addr = heap.alloc(ctypes.sizeof(mdl)) alloc_addr.append(mdl_addr) ql.mem.write(mdl_addr, bytes(mdl)) irp.MdlAddress.value = mdl_addr else: # NEITHER_IO input_buffer_size = len(in_buffer) input_buffer_addr = heap.alloc(input_buffer_size) alloc_addr.append(input_buffer_addr) ql.mem.write(input_buffer_addr, bytes(in_buffer)) irp.UserBuffer.value = input_buffer_addr # everything is done! Write IRP to memory ql.mem.write(irp_addr, bytes(irp)) # set function args # TODO: make sure this is indeed STDCALL ql.os.fcall = ql.os.fcall_select(STDCALL) ql.os.fcall.writeParams( ((POINTER, ql.loader.driver_object.DeviceObject), (POINTER, irp_addr))) try: # now emulate ql.run(ql.loader.driver_object.MajorFunction[IRP_MJ_WRITE]) except UcError as err: verify_ret(ql, err) # read current IRP state irp_buffer = ql.mem.read(irp_addr, ctypes.sizeof(irp)) irp = irp.from_buffer(irp_buffer) io_status = irp.IoStatus # now free all alloc memory for addr in alloc_addr: # print("freeing heap memory at 0x%x" %addr) # FIXME: the output is not deterministic?? heap.free(addr) return True, io_status.Information.value
def ioctl(ql: Qiling, params: Tuple[Tuple, int, bytes]) -> Tuple: allocations = [] def __heap_alloc(size: int) -> int: address = ql.os.heap.alloc(size) allocations.append(address) return address def __free_all(allocations: Iterable[int]) -> None: for address in allocations: ql.os.heap.free(address) def ioctl_code(DeviceType: int, Function: int, Method: int, Access: int) -> int: return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method def build_mdl(buffer_size, data=None): mdl = make_mdl(ql.arch.bits) mapped_address = __heap_alloc(buffer_size) mdl.MappedSystemVa.value = mapped_address mdl.StartVa.value = mapped_address mdl.ByteOffset = 0 mdl.ByteCount = buffer_size if data: written = data if len(data) <= buffer_size else data[:buffer_size] ql.mem.write(mapped_address, written) return mdl if ql.loader.driver_object.MajorFunction[IRP_MJ_DEVICE_CONTROL] == 0: # raise error? return (None, None, None) # create new memory region to store input data _ioctl_code, output_buffer_size, in_buffer = params # extract data transfer method devicetype, function, ctl_method, access = _ioctl_code input_buffer_size = len(in_buffer) input_buffer_addr = __heap_alloc(input_buffer_size) ql.mem.write(input_buffer_addr, bytes(in_buffer)) # create new memory region to store out data output_buffer_addr = __heap_alloc(output_buffer_size) # allocate memory regions for IRP and IO_STACK_LOCATION irp = make_irp(ql.arch.bits) irpstack_class = irp.irpstack._type_ irp_addr = __heap_alloc(ctypes.sizeof(irp)) irpstack_addr = __heap_alloc(ctypes.sizeof(irpstack_class)) # setup irp stack parameters irpstack = irpstack_class() # setup IRP structure irp.irpstack = ctypes.cast(irpstack_addr, ctypes.POINTER(irpstack_class)) ql.log.info("IRP is at 0x%x, IO_STACK_LOCATION is at 0x%x" % (irp_addr, irpstack_addr)) irpstack.Parameters.DeviceIoControl.IoControlCode = ioctl_code( devicetype, function, ctl_method, access) irpstack.Parameters.DeviceIoControl.OutputBufferLength = output_buffer_size irpstack.Parameters.DeviceIoControl.InputBufferLength = input_buffer_size irpstack.Parameters.DeviceIoControl.Type3InputBuffer.value = input_buffer_addr # used by IOCTL_METHOD_NEITHER ql.mem.write(irpstack_addr, bytes(irpstack)) if ctl_method == METHOD_NEITHER: irp.UserBuffer.value = output_buffer_addr # used by IOCTL_METHOD_NEITHER # allocate memory for AssociatedIrp.SystemBuffer # used by IOCTL_METHOD_IN_DIRECT, IOCTL_METHOD_OUT_DIRECT and IOCTL_METHOD_BUFFERED system_buffer_size = max(input_buffer_size, output_buffer_size) system_buffer_addr = __heap_alloc(system_buffer_size) # init data from input buffer ql.mem.write(system_buffer_addr, bytes(in_buffer)) irp.AssociatedIrp.SystemBuffer.value = system_buffer_addr if ctl_method in (METHOD_IN_DIRECT, METHOD_OUT_DIRECT): # Create MDL structure for output data # used by both IOCTL_METHOD_IN_DIRECT and IOCTL_METHOD_OUT_DIRECT mdl = build_mdl(output_buffer_size) mdl_addr = __heap_alloc(ctypes.sizeof(mdl)) ql.mem.write(mdl_addr, bytes(mdl)) irp.MdlAddress.value = mdl_addr # everything is done! Write IRP to memory ql.mem.write(irp_addr, bytes(irp)) # set function args ql.log.info("Executing IOCTL with DeviceObject = 0x%x, IRP = 0x%x" % (ql.loader.driver_object.DeviceObject, irp_addr)) # TODO: make sure this is indeed STDCALL ql.os.fcall = ql.os.fcall_select(STDCALL) ql.os.fcall.writeParams( ((POINTER, ql.loader.driver_object.DeviceObject), (POINTER, irp_addr))) try: ql.log.info( f"Executing from: {ql.loader.driver_object.MajorFunction[IRP_MJ_DEVICE_CONTROL]:#x}" ) # now emulate IOCTL's DeviceControl ql.run(ql.loader.driver_object.MajorFunction[IRP_MJ_DEVICE_CONTROL]) except UcError as err: verify_ret(ql, err) # read current IRP state irp_buffer = ql.mem.read(irp_addr, ctypes.sizeof(irp)) irp = irp.__class__.from_buffer(irp_buffer) io_status = irp.IoStatus # read output data output_data = b'' if io_status.Status.Status >= 0: if ctl_method == METHOD_BUFFERED: output_data = ql.mem.read(system_buffer_addr, io_status.Information.value) if ctl_method in (METHOD_IN_DIRECT, METHOD_OUT_DIRECT): output_data = ql.mem.read(mdl.MappedSystemVa.value, io_status.Information.value) if ctl_method == METHOD_NEITHER: output_data = ql.mem.read(output_buffer_addr, io_status.Information.value) # now free all alloc memory __free_all(allocations) return io_status.Status.Status, io_status.Information.value, output_data