def read_protected_addr(mbi, address, num_bytes): """read a number of bytes from a given protected memory address. The access flags will be changed temporarily and then be restored afterwards. @param mbi: Memory basic information, obtained by read_addr @type mbi: MEMORY_BASIC_INFORMATION @param address: address to read from @type address: int @param num_bytes: Number of bytes to read from that address @type num_bytes: int @return: bytes read """ mbi = defines.MEMORY_BASIC_INFORMATION() KERNEL_32.VirtualQuery(address, byref(mbi), sizeof(mbi)) base = mbi.BaseAddress size = mbi.RegionSize old_protect = mbi.Protect new_protect = defines.PAGE_EXECUTE_READWRITE old_protect_dummy = defines.DWORD(0) logging.warning("[read_addr] Memory: Protection override!") KERNEL_32.VirtualProtect(base, size, new_protect, byref(old_protect_dummy)) mem_buffer = ctypes.create_string_buffer(num_bytes) ctypes.memmove(ctypes.addressof(mem_buffer), address, num_bytes) KERNEL_32.VirtualProtect(base, size, old_protect, byref(old_protect_dummy)) return mem_buffer.raw
def read_addr(address, num_bytes): """read a number of bytes from a given memory address. The function will try to override existing access rights to read from the specified memory location anyway. @param address: address to read from @type address: int @param num_bytes: Number of bytes to read from that address @type num_bytes: int @return: bytes read """ mbi = defines.MEMORY_BASIC_INFORMATION() if KERNEL_32.VirtualQuery(address, byref(mbi), sizeof(mbi)) != sizeof(mbi): logging.warning("[read_addr] could not read from given address") return "" if (mbi.State != defines.MEM_COMMIT): logging.warning("[read_addr] Memory: State != MEM_COMMIT for addr " \ "0x%08x (is 0x%08x)!", address, mbi.State) return "" elif (mbi.Protect & 0x100) == 0x100: logging.warning("[read_addr] Trying to access a guard page at " \ "address 0x%08x. Skipping memory operation", address) return "" elif (mbi.Protect & 0xe6) == 0: mem = read_protected_addr(mbi, address, num_bytes) return mem else: mem_buffer = ctypes.create_string_buffer(num_bytes) mem_buffer_addr = ctypes.addressof(mem_buffer) ctypes.memmove(mem_buffer_addr, address, num_bytes) return mem_buffer.raw
def set_executable(address, size): """set executable flag for a subsequent number of bytes beginning at given address. @param address: address of first byte to set executable @type address: int @param size: number of bytes to set executable @type size: int """ mbi = defines.MEMORY_BASIC_INFORMATION() if KERNEL_32.VirtualQuery(address, byref(mbi), sizeof(mbi)) != sizeof(mbi): logging.error("VirtualQuery: Failed to get memory info") return False if mbi.Protect >= 0x10: return True #already executable new_protect = mbi.Protect << 4 # add EXEC old_protect = defines.DWORD(0) if not KERNEL_32.VirtualProtect(address, size, new_protect, byref(old_protect)): logging.error("Cannot set address 0x%08x to executable" %\ address) return False return True
def get_region_information(address): """Return start and size of the region in which the address belongs. This consists of a range of pages that are commited together. @param address: Memory address to get the region for @type address: int @return: tuple consisting of (start, size) of region or C{None} if address not committed """ mbi = defines.MEMORY_BASIC_INFORMATION() if ctypes.windll.kernel32.VirtualQuery(address, byref(mbi), sizeof(mbi)) != sizeof(mbi): return None if not (mbi.State & defines.MEM_COMMIT): return None return (mbi.BaseAddress, mbi.RegionSize)
def write_mem(address, data): """Write data to address. Data is either written completely, no error occurs on any of the bytes or not written at all. @param address: Memory address to write data to @type address: int @param data: data to write @type data: str @return: C{True} on success, C{False} on failure """ data_len = len(data) #FIXME: memmove throws exception - no need to check using virtualquery # speeds up but may throw exception in program # -> other exception handler? chk_addr = address chk_len = data_len while chk_len > 0: mbi = defines.MEMORY_BASIC_INFORMATION() if KERNEL_32.VirtualQuery(address, byref(mbi), sizeof(mbi)) != sizeof(mbi): logging.debug("VirtualQuery: Failed to get memory info") return False if (mbi.State != defines.MEM_COMMIT): logging.error("Cannot write to %x - protected with flags %x" % \ (address, mbi.Protect)) return False if mbi.Protect & 0xcc == 0: #logging.warn("Writing to address 0x%08x not allowed - using " \ # "aggressive writing" \ # % address) return write_mem_aggressive(address, data) bytes_in_page = (mbi.BaseAddress + mbi.RegionSize) - chk_addr chk_len -= bytes_in_page chk_addr += bytes_in_page # all data can be written to writeable pages - fine :) temp_buf = ctypes.create_string_buffer(data, len(data)) ctypes.memmove(address, temp_buf, len(data)) return True