def render(self): """Renders the kevent to standard output stream. Uses the default output format to render the kernel event to standard output stream. The default output format is as follows: id timestamp cpu process (process id) - kevent (parameters) -- --------- --- ------- ----------- ------- ------------ Example: 160 13:27:27.554 0 wmiprvse.exe (1012) - CloseFile (file=C:\\WINDOWS\\SYSTEM32\\RSAENH.DLL, tid=2668) """ self._thread = self.thread if self._thread: kevt = RENDER_FORMAT % (self._id, self._ts.time(), self._cpuid, self._thread.name, self._thread.pid, self._name, self._format_params()) else: # figure out the process id from thread # if the process can't be found in # thread registry pid = NA if self._pid is None: if self._tid: # get the thread handle handle = open_thread(THREAD_QUERY_INFORMATION, False, self._tid) if handle: pid = get_process_id_of_thread(handle) close_handle(handle) else: pid = self._pid kevt = RENDER_FORMAT % (self._id, self._ts.time(), self._cpuid, NA, pid, self._name, self._format_params()) IO.write_console(kevt) self._id += 1
def render(self): """Renders the kevent to standard output stream. Uses the default output format to render the kernel event to standard output stream. The default output format is as follows: id timestamp cpu process (process id) - kevent (parameters) -- --------- --- ------- ----------- ------- ------------ Example: 160 13:27:27.554 0 wmiprvse.exe (1012) - CloseFile (file=C:\\WINDOWS\\SYSTEM32\\RSAENH.DLL, tid=2668) """ self._thread = self.thread if self._thread: kevt = RENDER_FORMAT % (self._id, self._ts.time(), self._cpuid, self._thread.name, self._thread.pid, self._name, self._format_params()) else: # figure out the process id from thread # if the process can't be found in # thread registry pid = NA if self._pid is None: if self._tid: # get the thread handle handle = open_thread(THREAD_QUERY_INFORMATION, False, self._tid) if handle: pid = get_process_id_of_thread(handle) close_handle(handle) else: pid = self._pid kevt = RENDER_FORMAT % (self._id, self._ts.time(), self._cpuid, NA, pid, self._name, self._format_params()) IO.write_console(kevt) self._id += 1
def query_handles(self, pid=None): raw_handles = self._enum_handles(pid) current_ps = HANDLE(get_current_process()) handles = [] # find the object handles for the process for _, handle in raw_handles.items(): ps_handle = open_process(PROCESS_DUP_HANDLE, False, handle.pid) if ps_handle: handle_copy = HANDLE() # to query the object handle # we need to duplicate it in # the address space of the current process status = duplicate_handle(ps_handle, handle.handle, current_ps, byref(handle_copy), 0, 0, 0) if status != ERROR_SUCCESS: # get the object type handle_type = self._query_handle(handle_copy, PUBLIC_OBJECT_TYPE_INFORMATION, OBJECT_TYPE_INFORMATION) if handle_type: handle_type = cast(handle_type.contents.type_name.buffer, c_wchar_p) \ .value \ .upper().replace(' ', '_') # query for object name # (file names, registry keys, # sections, ALPC ports, etc) # check the access mask to make # sure `NtQueryObject` won't hang if handle_type in self._handle_types and \ handle.access_mask not in self._nasty_access_masks: handle_name = self._query_handle(handle_copy, PUBLIC_OBJECT_NAME_INFORMATION, UNICODE_STRING) if handle_name: handle_name = cast(handle_name.contents.buffer, c_wchar_p).value handle_info = HandleInfo(handle.handle, handle.obj, HandleType(HandleType.__getattr__(handle_type)), handle_name, handle.pid) handles.append(handle_info) close_handle(handle_copy) close_handle(ps_handle) return handles
def query_handles(self, pid=None): raw_handles = self._enum_handles(pid) current_ps = HANDLE(get_current_process()) handles = [] # find the object handles for the process for _, handle in raw_handles.items(): ps_handle = open_process(PROCESS_DUP_HANDLE, False, handle.pid) if ps_handle: handle_copy = HANDLE() # to query the object handle # we need to duplicate it in # the address space of the current process status = duplicate_handle(ps_handle, handle.handle, current_ps, byref(handle_copy), 0, 0, 0) if status != ERROR_SUCCESS: # get the object type handle_type = self._query_handle( handle_copy, PUBLIC_OBJECT_TYPE_INFORMATION, OBJECT_TYPE_INFORMATION) if handle_type: handle_type = cast(handle_type.contents.type_name.buffer, c_wchar_p) \ .value \ .upper().replace(' ', '_') # query for object name # (file names, registry keys, # sections, ALPC ports, etc) # check the access mask to make # sure `NtQueryObject` won't hang if handle_type in self._handle_types and \ handle.access_mask not in self._nasty_access_masks: handle_name = self._query_handle( handle_copy, PUBLIC_OBJECT_NAME_INFORMATION, UNICODE_STRING) if handle_name: handle_name = cast(handle_name.contents.buffer, c_wchar_p).value handle_info = HandleInfo( handle.handle, handle.obj, HandleType( HandleType.__getattr__(handle_type)), handle_name, handle.pid) handles.append(handle_info) close_handle(handle_copy) close_handle(ps_handle) return handles
def _get_proc(self, thread_id): handle = open_thread(THREAD_QUERY_INFORMATION, False, thread_id) if handle: # if it was possible to get the process id # which is the parent of the thread, we can # try to get the process name from its pid pid = get_process_id_of_thread(handle) close_handle(handle) handle = open_process(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) if handle: exe = ctypes.create_unicode_buffer(MAX_PATH) status = query_full_process_image_name(handle, 0, exe, DWORD(MAX_PATH)) close_handle(handle) if status: return os.path.basename(exe.value)
def _get_proc(self, thread_id): handle = open_thread(THREAD_QUERY_INFORMATION, False, thread_id) if handle: # if it was possible to get the process id # which is the parent of the thread, we can # try to get the process name from its pid pid = get_process_id_of_thread(handle) close_handle(handle) handle = open_process(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) if handle: exe = ctypes.create_unicode_buffer(MAX_PATH) status = query_full_process_image_name(handle, 0, exe, DWORD(MAX_PATH)) close_handle(handle) if status: return os.path.basename(exe.value)
def get_thread(self): """Gets the thread associated with the kernel event. """ thread = self._find_thread() if thread: return thread.pid, thread.name else: # figure out the process id from thread # if the process can't be found in # the thread registry pid = NA if self._pid is None: if self._tid: # get the thread handle handle = open_thread(THREAD_QUERY_INFORMATION, False, self._tid) if handle: pid = get_process_id_of_thread(handle) close_handle(handle) else: pid = self._pid return pid, NA
def get_thread(self): """Gets the thread associated with the kernel event. """ thread = self._find_thread() if thread: return thread.pid, thread.name else: # figure out the process id from thread # if the process can't be found in # the thread registry pid = NA if self._pid is None: if self._tid: # get the thread handle handle = open_thread(THREAD_QUERY_INFORMATION, False, self._tid) if handle: pid = get_process_id_of_thread(handle) close_handle(handle) else: pid = self._pid return pid, NA
def add_thread(self, ketype, kti): """Adds a new process or thread to thread registry. Parameters ---------- ketype: tuple kernel event type kti: dict event payload as coming from the kernel event stream collector """ if ketype == CREATE_PROCESS or ketype == ENUM_PROCESS: parent_pid = int(kti.parent_id, 16) process_id = int(kti.process_id, 16) # we assume the process id is # equal to thread id (in a single # threaded process) thread_id = process_id name = kti.image_file_name comm = kti.command_line thread = ThreadInfo(process_id, thread_id, parent_pid, name, comm) if ketype == ENUM_PROCESS: thread.handles = [handle for handle in self._handles if handle.pid == process_id] if ketype == CREATE_PROCESS: image_meta = self.image_meta_registry.get_image_meta(thread.exe) if not image_meta: image_meta = self.image_meta_registry.add_image_meta(thread.exe) thread.image_meta = image_meta self._threads[process_id] = thread elif ketype == CREATE_THREAD or ketype == ENUM_THREAD: # new thread created in the # context of the existing process # `procces_id` is the parent # of this thread process_id = int(kti.process_id, 16) parent_pid = process_id thread_id = int(kti.t_thread_id, 16) if parent_pid in self._threads: # copy info from the process # which created this thread pthread = self._threads[parent_pid] # increment the number of threads # for this process pthread.increment_child_count() name = pthread.name comm = pthread.comm thread = ThreadInfo(process_id, thread_id, parent_pid, name, comm) thread.ustack_base = hex(kti.user_stack_base) thread.kstack_base = hex(kti.stack_base) thread.base_priority = kti.base_priority thread.io_priority = kti.io_priority self._threads[thread_id] = thread else: # the parent process has not been found # query the os for process information handle = open_process(PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, False, parent_pid) info = {} if handle: info = self._query_process_info(handle) close_handle(handle) else: if get_last_error() == ERROR_ACCESS_DENIED: if parent_pid == 0: info = ddict(name='idle', comm='idle', parent_id=0) else: # the access to protected / system process # can't be done with PROCESS_VM_READ or PROCESS_QUERY_INFORMATION # flags. Open the process again but with # restricted access rights, so we can get the process image file name handle = open_process(PROCESS_QUERY_LIMITED_INFORMATION, False, parent_pid) if handle: info = self._query_process_info(handle, False) close_handle(handle) # add a new thread and the parent process # we just found to avoid continuous lookup name = info.name if len(info) > 0 and info.name else NA comm = info.comm if len(info) > 0 and info.comm else NA ppid = info.parent_pid if len(info) > 0 and info.parent_pid else NA thread = ThreadInfo(process_id, thread_id, process_id, name, comm) thread.ustack_base = hex(kti.user_stack_base) thread.kstack_base = hex(kti.stack_base) thread.base_priority = kti.base_priority thread.io_priority = kti.io_priority parent = ThreadInfo(process_id, process_id, ppid, name, comm) # enumerate parent handles parent.handles = self.handle_repository.query_handles(process_id) self._threads[thread_id] = thread self._threads[parent_pid] = parent if self.on_thread_added_callback and callable(self.on_thread_added_callback): self.on_thread_added_callback(thread)
def __del__(self): if self._stdout_handle: close_handle(self._stdout_handle)
def __del__(self): if self._stdout_handle: close_handle(self._stdout_handle)
def add_thread(self, ketype, kti): """Adds a new process or thread to thread registry. Parameters ---------- ketype: tuple kernel event type kti: dict event payload as coming from the kernel event stream collector """ if ketype == CREATE_PROCESS or ketype == ENUM_PROCESS: parent_pid = int(kti.parent_id, 16) process_id = int(kti.process_id, 16) # we assume the process id is # equal to thread id (in a single # threaded process) thread_id = process_id name = kti.image_file_name comm = kti.command_line suid = kti.user_sid thread = ThreadInfo(process_id, thread_id, parent_pid, name, comm, suid) if ketype == ENUM_PROCESS: thread.handles = [ handle for handle in self._handles if handle.pid == process_id ] else: thread.handles = self.handle_repository.query_handles( process_id) self._threads[process_id] = thread elif ketype == CREATE_THREAD or ketype == ENUM_THREAD: # new thread created in the # context of the existing process # `procces_id` is the parent # of this thread process_id = int(kti.process_id, 16) parent_pid = process_id thread_id = int(kti.t_thread_id, 16) if parent_pid in self._threads: # copy info from the process # which created this thread pthread = self._threads[parent_pid] # increment the number of threads # for this process pthread.increment_child_count() name = pthread.name comm = pthread.comm suid = pthread.suid thread = ThreadInfo(process_id, thread_id, parent_pid, name, comm, suid) thread.ustack_base = hex(kti.user_stack_base) thread.kstack_base = hex(kti.stack_base) thread.base_priority = kti.base_priority thread.io_priority = kti.io_priority self._threads[thread_id] = thread else: # the parent process has not been found # query the os for process information handle = open_process( PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, False, parent_pid) info = {} if handle: info = self._query_process_info(handle) close_handle(handle) else: if get_last_error() == ERROR_ACCESS_DENIED: if parent_pid == 0: info = ddict(name='idle', comm='idle', parent_id=0) else: # the access to protected / system process # can't be done with PROCESS_VM_READ or PROCESS_QUERY_INFORMATION # flags. Open the process again but with # restricted access rights, so we can get the process image file name handle = open_process( PROCESS_QUERY_LIMITED_INFORMATION, False, parent_pid) if handle: info = self._query_process_info(handle, False) close_handle(handle) # add a new thread and the parent process # we just found to avoid continuous lookup name = info.name if len(info) > 0 and info.name else NA comm = info.comm if len(info) > 0 and info.comm else NA ppid = info.parent_pid if len( info) > 0 and info.parent_pid else NA thread = ThreadInfo(process_id, thread_id, process_id, name, comm, None) thread.ustack_base = hex(kti.user_stack_base) thread.kstack_base = hex(kti.stack_base) thread.base_priority = kti.base_priority thread.io_priority = kti.io_priority parent = ThreadInfo(process_id, process_id, ppid, name, comm, None) # enumerate parent handles parent.handles = self.handle_repository.query_handles( process_id) self._threads[thread_id] = thread self._threads[parent_pid] = parent if self.on_thread_added_callback and callable( self.on_thread_added_callback): self.on_thread_added_callback(thread)