def _query_handle(self, handle, klass, object_info_type): """Gets the object handle info. Parameters ---------- handle: HANDLE handle object klass: int the class of information to query object_info_type: Structure structure type which holds the handle info """ buff = malloc(self._object_buff_size) rlen = ULONG() status = nt_query_object(handle, klass, buff, self._object_buff_size, byref(rlen)) if status >= 0: info = cast(buff, POINTER(object_info_type)) self._buffers.append(buff) return info else: # reallocate the buffer size # and try again buff = realloc(buff, rlen.value) status = nt_query_object(handle, klass, buff, self._object_buff_size, None) if status >= 0: info = cast(buff, POINTER(object_info_type)) self._buffers.append(buff) return info else: free(buff) return None
def read_process_memory(process, chunk, size): """Reads a memory block from the process address space. """ buff = malloc(size) status = _read_process_memory(process, chunk, buff, size, None) if status != ERROR_SUCCESS: return buff else: free(buff)
def read_process_memory(process, chunk, size): """Reads a memory block from the process address space. """ buff = malloc(size) status = _read_process_memory(process, chunk, buff, size, None) if status != ERROR_SUCCESS: return buff else: free(buff)
def _query_handle(self, handle, klass, object_info_type): """Gets the object handle info. Parameters ---------- handle: HANDLE handle object klass: int the class of information to query object_info_type: Structure structure type which holds the handle info """ buff = malloc(self._object_buff_size) rlen = ULONG() status = nt_query_object(handle, klass, buff, self._object_buff_size, byref(rlen)) if status >= 0: info = cast(buff, POINTER(object_info_type)) self._buffers.append(buff) return info else: # reallocate the buffer size # and try again buff = realloc(buff, rlen.value) status = nt_query_object(handle, klass, buff, self._object_buff_size, None) if status >= 0: info = cast(buff, POINTER(object_info_type)) self._buffers.append(buff) return info else: free(buff) return None
def _query_value(self, hkey, subkey, value_name): """Get value content and value type from registry. Parameters ---------- hkey: HKEY handle to registry root key subkey: str path representing the subkey value: the name of the value """ if not hkey: return NA, NA value_type = c_ulong() buff = malloc(MAX_BUFFER_SIZE) buff_size = c_ulong(MAX_BUFFER_SIZE) status = reg_get_value(hkey, c_wchar_p(subkey), c_wchar_p(value_name), RRF_RT_ANY, byref(value_type), buff, byref(buff_size)) if status == ERROR_SUCCESS: value = cast(buff, c_wchar_p).value value_type = value_type.value if value_type in self._reg_value_types: if value_type == ValueType.REG_BINARY.value: value = '<binary>' [value_type] = [v.name for v in ValueType if v.value == value_type] else: value_type = ValueType.REG_NONE.name free(buff) return value, value_type else: free(buff) return NA, NA
def _query_value(self, hkey, subkey, value_name): """Get value content and value type from registry. Parameters ---------- hkey: HKEY handle to registry root key subkey: str path representing the subkey value: the name of the value """ if not hkey: return NA, NA value_type = c_ulong() buff = malloc(MAX_BUFFER_SIZE) buff_size = c_ulong(MAX_BUFFER_SIZE) status = reg_get_value(hkey, c_wchar_p(subkey), c_wchar_p(value_name), RRF_RT_ANY, byref(value_type), buff, byref(buff_size)) if status == ERROR_SUCCESS: value = cast(buff, c_wchar_p).value value_type = value_type.value if value_type in self._reg_value_types: if value_type == ValueType.REG_BINARY.value: value = '<binary>' [value_type ] = [v.name for v in ValueType if v.value == value_type] else: value_type = ValueType.REG_NONE.name free(buff) return value, value_type else: free(buff) return NA, NA
def _query_process_info(self, handle, read_peb=True): """Gets an extended proc info. Parameters ----------- handle: HANDLE handle to process for which the info should be acquired read_peb: boolean true in case the process PEB should be read """ pbi_buff = malloc(sizeof(PROCESS_BASIC_INFORMATION)) status = zw_query_information_process(handle, PROCESS_BASIC_INFO, pbi_buff, sizeof(PROCESS_BASIC_INFORMATION), byref(ULONG())) info = {} if status == STATUS_SUCCESS: pbi = cast(pbi_buff, POINTER(PROCESS_BASIC_INFORMATION)) ppid = pbi.contents.inherited_from_unique_process_id if read_peb: # read the PEB to get the process parameters. # Because the PEB structure resides # in the address space of another process # we must read the memory block in order # to access the structure's fields peb_addr = pbi.contents.peb_base_address peb_buff = read_process_memory(handle, peb_addr, sizeof(PEB)) if peb_buff: peb = cast(peb_buff, POINTER(PEB)) # read the RTL_USER_PROCESS_PARAMETERS struct # which contains the command line and the image # name of the process pp = peb.contents.process_parameters pp_buff = read_process_memory(handle, pp, sizeof(RTL_USER_PROCESS_PARAMETERS)) if pp_buff: pp = cast(pp_buff, POINTER(RTL_USER_PROCESS_PARAMETERS)) comm = pp.contents.command_line.buffer comm_len = pp.contents.command_line.length exe = pp.contents.image_path_name.buffer exe_len = pp.contents.image_path_name.length # these memory reads are required # to copy the command line and image name buffers cb = read_process_memory(handle, comm, comm_len) eb = read_process_memory(handle, exe, exe_len) if cb and eb: # cast the buffers to # UNICODE strings comm = cast(cb, c_wchar_p).value exe = cast(eb, c_wchar_p).value # the image name contains the full path # split the string to get the exec name name = exe[exe.rfind('\\') + 1:] info = ddict(name=name, comm=comm, parent_pid=ppid) free(cb) free(eb) free(pp_buff) free(peb_buff) else: # query only the process image file name exe = ctypes.create_unicode_buffer(MAX_PATH) size = DWORD(MAX_PATH) name = None status = query_full_process_image_name(handle, 0, exe, byref(size)) if status: exe = exe.value name = exe[exe.rfind('\\') + 1:] info = ddict(name=name if name else NA, comm=exe if type(exe) is str else None, parent_pid=ppid) if pbi_buff: free(pbi_buff) return info
def _enum_handles(self, process_id=None): """Enumerates handle information. Enumerates handle info on the start of the kernel capture. Returns a dictionary of handle's information including the handle id, access mask, and the process which owns the handle. """ buff_size = MAX_BUFFER_SIZE size = c_ulong() # allocate the initial buffer buff = malloc(buff_size) handles = {} while True: status = zw_query_system_information( SYSTEM_HANDLE_INFORMATION_CLASS, buff, buff_size, byref(size)) if status == STATUS_INFO_LENGTH_MISMATCH: # the buffer is too small # increment the buffer size and try again buff_size += MAX_BUFFER_SIZE elif status == STATUS_SUCCESS: # cast the buffer to `SYSTEM_HANDLE_INFORMATION` struct # which contains an array of `SYSTEM_HANDLE` structures sys_handle_info = cast(buff, POINTER(SYSTEM_HANDLE_INFORMATION)) sys_handle_info = sys_handle_info.contents handle_count = sys_handle_info.number_of_handles # resize the array size to the # actual number of file handles sys_handles = (SYSTEM_HANDLE * buff_size).from_address( addressof(sys_handle_info.handles)) for i in range(handle_count): sys_handle = sys_handles[i] pid = sys_handle.process_id handle = sys_handle.handle obj = sys_handle.object obj_type_index = sys_handle.object_type_number access_mask = sys_handle.access_mask if process_id and process_id == pid: handles[obj] = ddict(pid=process_id, handle=handle, obj=obj, access_mask=access_mask, obj_type_index=obj_type_index) elif process_id is None: handles[obj] = ddict(pid=pid, handle=handle, obj=obj, access_mask=access_mask, obj_type_index=obj_type_index) break else: raise HandleEnumError(status) # reallocate the buffer buff = realloc(buff, buff_size) # free the buffer memory free(buff) return handles
def _query_process_info(self, handle, read_peb=True): """Gets an extended proc info. Parameters ----------- handle: HANDLE handle to process for which the info should be acquired read_peb: boolean true in case the process PEB should be read """ pbi_buff = malloc(sizeof(PROCESS_BASIC_INFORMATION)) status = zw_query_information_process( handle, PROCESS_BASIC_INFO, pbi_buff, sizeof(PROCESS_BASIC_INFORMATION), byref(ULONG())) info = {} if status == STATUS_SUCCESS: pbi = cast(pbi_buff, POINTER(PROCESS_BASIC_INFORMATION)) ppid = pbi.contents.inherited_from_unique_process_id if read_peb: # read the PEB to get the process parameters. # Because the PEB structure resides # in the address space of another process # we must read the memory block in order # to access the structure's fields peb_addr = pbi.contents.peb_base_address peb_buff = read_process_memory(handle, peb_addr, sizeof(PEB)) if peb_buff: peb = cast(peb_buff, POINTER(PEB)) # read the RTL_USER_PROCESS_PARAMETERS struct # which contains the command line and the image # name of the process pp = peb.contents.process_parameters pp_buff = read_process_memory( handle, pp, sizeof(RTL_USER_PROCESS_PARAMETERS)) if pp_buff: pp = cast(pp_buff, POINTER(RTL_USER_PROCESS_PARAMETERS)) comm = pp.contents.command_line.buffer comm_len = pp.contents.command_line.length exe = pp.contents.image_path_name.buffer exe_len = pp.contents.image_path_name.length # these memory reads are required # to copy the command line and image name buffers cb = read_process_memory(handle, comm, comm_len) eb = read_process_memory(handle, exe, exe_len) if cb and eb: # cast the buffers to # UNICODE strings comm = cast(cb, c_wchar_p).value exe = cast(eb, c_wchar_p).value # the image name contains the full path # split the string to get the exec name name = exe[exe.rfind('\\') + 1:] info = ddict(name=name, comm=comm, parent_pid=ppid) free(cb) free(eb) free(pp_buff) free(peb_buff) else: # query only the process image file name exe = ctypes.create_unicode_buffer(MAX_PATH) size = DWORD(MAX_PATH) name = None status = query_full_process_image_name(handle, 0, exe, byref(size)) if status: exe = exe.value name = exe[exe.rfind('\\') + 1:] info = ddict(name=name if name else NA, comm=exe, parent_pid=ppid) if pbi_buff: free(pbi_buff) return info
def _enum_handles(self, process_id=None): """Enumerates handle information. Enumerates handle info on the start of the kernel capture. Returns a dictionary of handle's information including the handle id, access mask, and the process which owns the handle. """ buff_size = MAX_BUFFER_SIZE size = c_ulong() # allocate the initial buffer buff = malloc(buff_size) handles = {} while True: status = zw_query_system_information(SYSTEM_HANDLE_INFORMATION_CLASS, buff, buff_size, byref(size)) if status == STATUS_INFO_LENGTH_MISMATCH: # the buffer is too small # increment the buffer size and try again buff_size += MAX_BUFFER_SIZE elif status == STATUS_SUCCESS: # cast the buffer to `SYSTEM_HANDLE_INFORMATION` struct # which contains an array of `SYSTEM_HANDLE` structures sys_handle_info = cast(buff, POINTER(SYSTEM_HANDLE_INFORMATION)) sys_handle_info = sys_handle_info.contents handle_count = sys_handle_info.number_of_handles # resize the array size to the # actual number of file handles sys_handles = (SYSTEM_HANDLE * buff_size).from_address(addressof(sys_handle_info.handles)) for i in range(handle_count): sys_handle = sys_handles[i] pid = sys_handle.process_id handle = sys_handle.handle obj = sys_handle.object obj_type_index = sys_handle.object_type_number access_mask = sys_handle.access_mask if process_id and process_id == pid: handles[obj] = ddict(pid=process_id, handle=handle, obj=obj, access_mask=access_mask, obj_type_index=obj_type_index) elif process_id is None: handles[obj] = ddict(pid=pid, handle=handle, obj=obj, access_mask=access_mask, obj_type_index=obj_type_index) break else: raise HandleEnumError(status) # reallocate the buffer buff = realloc(buff, buff_size) # free the buffer memory free(buff) return handles