def Open(self): self.task = ctypes.c_uint32() self.mytask = libc.mach_task_self() ret = libc.task_for_pid(self.mytask, ctypes.c_int(self.pid), ctypes.pointer(self.task)) if ret: if ret == 5: # Most likely this means access denied. This is not perfect # but there is no way to find out. raise process_error.ProcessError( "Access denied (task_for_pid returned 5).") raise process_error.ProcessError( "task_for_pid failed with error code : %s" % ret)
def VirtualQueryEx(self, address): mbi = MEMORY_BASIC_INFORMATION() res = VirtualQueryEx(self.h_process, address, ctypes.byref(mbi), ctypes.sizeof(mbi)) if not res: raise process_error.ProcessError("Error VirtualQueryEx: 0x%08X" % address) return mbi
def __init__(self, pid=None): """Creates a process for reading memory.""" super(Process, self).__init__() if pid is None: raise process_error.ProcessError("No pid given.") self.pid = pid self.task = None self.mytask = None self.Open()
def Is64bit(self): """Returns true if this is a 64 bit process.""" if "64" not in platform.machine(): return False iswow64 = ctypes.c_bool(False) if IsWow64Process is None: return False if not IsWow64Process(self.h_process, ctypes.byref(iswow64)): raise process_error.ProcessError("Error while calling IsWow64Process.") return not iswow64.value
def ReadBytes(self, address, num_bytes): """Reads at most num_bytes starting from offset <address>.""" pdata = ctypes.c_void_p(0) data_cnt = ctypes.c_uint32(0) ret = libc.mach_vm_read(self.task, ctypes.c_ulonglong(address), ctypes.c_longlong(num_bytes), ctypes.pointer(pdata), ctypes.pointer(data_cnt)) if ret: raise process_error.ProcessError("Error in mach_vm_read, ret=%s" % ret) buf = ctypes.string_at(pdata.value, data_cnt.value) libc.vm_deallocate(self.mytask, pdata, data_cnt) return buf
def ReadBytes(self, address, num_bytes): """Reads at most num_bytes starting from offset <address>.""" address = int(address) buf = ctypes.create_string_buffer(num_bytes) bytesread = ctypes.c_size_t(0) res = ReadProcessMemory(self.h_process, address, buf, num_bytes, ctypes.byref(bytesread)) if res == 0: err = wintypes.GetLastError() if err == 299: # Only part of ReadProcessMemory has been done, let's return it. return buf.raw[:bytesread.value] raise process_error.ProcessError("Error in ReadProcessMemory: %d" % err) return buf.raw[:bytesread.value]
def Open(self): """Opens the process for reading.""" self.h_process = kernel32.OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 0, self.pid) if not self.h_process: raise process_error.ProcessError( "Failed to open process (pid %d)." % self.pid) if self.Is64bit(): si = self.GetNativeSystemInfo() self.max_addr = si.lpMaximumApplicationAddress else: si = self.GetSystemInfo() self.max_addr = 2147418111 self.min_addr = si.lpMinimumApplicationAddress
def Regions(self, skip_executable_regions=False, skip_shared_regions=False, skip_readonly_regions=False): """Iterates over the readable regions for this process. We use mach_vm_region_recurse here to get a fine grained view of the process' memory space. The algorithm is that for some regions, the function returns is_submap=True which means that there are actually subregions that we need to examine by increasing the depth and calling the function again. For example, there are two regions, addresses 1000-2000 and 2000-3000 where 1000-2000 has two subregions, 1100-1200 and 1300-1400. In that case we would call: mvrr(address=0, depth=0) -> (1000-2000, is_submap=True) mvrr(address=0, depth=1) -> (1100-1200, is_submap=False) mvrr(address=1200, depth=1) -> (1300-1400, is_submap=False) mvrr(address=1400, depth=1) -> (2000-3000, is_submap=False) At this point, we know we went out of the original submap which ends at 2000. We need to recheck the region at 2000, it could be submap = True at depth 0 so we call mvrr(address=1400, depth=0) -> (2000-3000, is_submap=False) Args: skip_executable_regions: Skips executable sections. skip_shared_regions: Skips shared sections. Includes mapped files. skip_readonly_regions: Skips readonly sections. Yields: Pairs (address, length) for each identified region. """ address = ctypes.c_ulong(0) mapsize = ctypes.c_ulong(0) count = ctypes.c_uint32(submap_info_size) sub_info = vm_region_submap_short_info_data_64() depth = 0 depth_end_addresses = {} while True: c_depth = ctypes.c_uint32(depth) r = libc.mach_vm_region_recurse(self.task, ctypes.pointer(address), ctypes.pointer(mapsize), ctypes.pointer(c_depth), ctypes.pointer(sub_info), ctypes.pointer(count)) # If we get told "invalid address", we have crossed into kernel land... if r == 1: break if r != 0: raise process_error.ProcessError("Error in mach_vm_region, ret=%s" % r) if depth > 0 and address.value >= depth_end_addresses[depth]: del depth_end_addresses[depth] depth -= 1 continue p = sub_info.protection if skip_executable_regions and p & VM_PROT_EXECUTE: address.value += mapsize.value continue if skip_shared_regions and sub_info.share_mode in [ SM_COW, SM_SHARED, SM_TRUESHARED ]: address.value += mapsize.value continue if not p & VM_PROT_READ: address.value += mapsize.value continue writable = p & VM_PROT_WRITE if skip_readonly_regions and not writable: address.value += mapsize.value continue if sub_info.is_submap: depth += 1 depth_end_addresses[depth] = address.value + mapsize.value else: yield address.value, mapsize.value address.value += mapsize.value
def __enter__(self): if self.pid in [101, 106]: raise process_error.ProcessError("Access Denied.") return self