def testMultiplePipesWithPid0(self): args = pipes.ListNamedPipesFlowArgs() pipe_foo = rdf_client.NamedPipe() pipe_foo.name = "foo-pipe" pipe_foo.client_pid = 0 pipe_bar = rdf_client.NamedPipe() pipe_bar.name = "bar-pipe" pipe_bar.server_pid = 0 proc = rdf_client.Process() proc.pid = 0 proc.exe = r"C:\Windows\system32\notepad.exe" results = self._RunListNamedPipesFlow( args, pipe_results=[pipe_foo, pipe_bar], proc_results=[proc], ) self.assertLen(results, 2) self.assertEqual(results[0].proc.exe, r"C:\Windows\system32\notepad.exe") self.assertEqual(results[1].proc.exe, r"C:\Windows\system32\notepad.exe")
def testPipeEndFilterServerNoMatch(self): args = pipes.ListNamedPipesFlowArgs() args.pipe_end_filter = pipes_pb2.ListNamedPipesFlowArgs.SERVER_END pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.flags = pipes.PIPE_TYPE_MESSAGE | pipes.PIPE_CLIENT_END results = self._RunListNamedPipesFlow(args, pipe_results=[pipe]) self.assertEmpty(results)
def testPipeTypeFilterMessageNoMatch(self): args = pipes.ListNamedPipesFlowArgs() args.pipe_type_filter = pipes_pb2.ListNamedPipesFlowArgs.MESSAGE_TYPE pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.flags = pipes.PIPE_TYPE_BYTE | pipes.PIPE_CLIENT_END results = self._RunListNamedPipesFlow(args, pipe_results=[pipe]) self.assertEmpty(results)
def testMultiplePipes(self): args = pipes.ListNamedPipesFlowArgs() pipe_foo = rdf_client.NamedPipe() pipe_foo.name = "foo" pipe_foo.server_pid = 0xf00 pipe_baz = rdf_client.NamedPipe() pipe_baz.name = "baz" pipe_baz.server_pid = 0xb45 results = self._RunListNamedPipesFlow( args, pipe_results=[pipe_foo, pipe_baz], ) self.assertLen(results, 2) results_by_name = {result.pipe.name: result for result in results} self.assertEqual(results_by_name["foo"].pipe.server_pid, 0xf00) self.assertEqual(results_by_name["baz"].pipe.server_pid, 0xb45)
def testSinglePipe(self): args = pipes.ListNamedPipesFlowArgs() pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.server_pid = 1337 results = self._RunListNamedPipesFlow(args, pipe_results=[pipe]) self.assertLen(results, 1) self.assertEqual(results[0].pipe.name, "foo") self.assertEqual(results[0].pipe.server_pid, 1337)
def testPipeEndFilterClientMatch(self): args = pipes.ListNamedPipesFlowArgs() args.pipe_end_filter = pipes_pb2.ListNamedPipesFlowArgs.CLIENT_END pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.flags = pipes.PIPE_TYPE_MESSAGE | pipes.PIPE_CLIENT_END results = self._RunListNamedPipesFlow(args, pipe_results=[pipe]) self.assertLen(results, 1) self.assertEqual(results[0].pipe.name, "foo")
def testPipeTypeFilterByteMatch(self): args = pipes.ListNamedPipesFlowArgs() args.pipe_type_filter = pipes_pb2.ListNamedPipesFlowArgs.BYTE_TYPE pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.flags = pipes.PIPE_TYPE_BYTE | pipes.PIPE_CLIENT_END results = self._RunListNamedPipesFlow(args, pipe_results=[pipe]) self.assertLen(results, 1) self.assertEqual(results[0].pipe.name, "foo")
def testPipeNameRegex(self): args = pipes.ListNamedPipesFlowArgs() args.pipe_name_regex = "ba." pipe_foo = rdf_client.NamedPipe() pipe_foo.name = "foo" pipe_bar = rdf_client.NamedPipe() pipe_bar.name = "bar" pipe_baz = rdf_client.NamedPipe() pipe_baz.name = "baz" results = self._RunListNamedPipesFlow( args, pipe_results=[pipe_foo, pipe_bar, pipe_baz], ) self.assertLen(results, 2) result_names = {result.pipe.name for result in results} self.assertIn("bar", result_names) self.assertIn("baz", result_names)
def testProcExeRegex(self): args = pipes.ListNamedPipesFlowArgs() args.proc_exe_regex = r"C:\\Windows\\ba.\.exe" pipe_foo = rdf_client.NamedPipe() pipe_foo.name = "foo" pipe_foo.server_pid = 123 proc_foo = rdf_client.Process() proc_foo.pid = 123 proc_foo.exe = r"C:\Windows\foo.exe" pipe_bar = rdf_client.NamedPipe() pipe_bar.name = "bar" pipe_bar.server_pid = 456 proc_bar = rdf_client.Process() proc_bar.pid = 456 proc_bar.exe = r"C:\Windows\bar.exe" pipe_baz = rdf_client.NamedPipe() pipe_baz.name = "baz" pipe_baz.server_pid = 789 proc_baz = rdf_client.Process() proc_baz.pid = 789 proc_baz.exe = r"C:\Windows\baz.exe" results = self._RunListNamedPipesFlow( args, pipe_results=[pipe_foo, pipe_bar, pipe_baz], proc_results=[proc_foo, proc_bar, proc_baz], ) self.assertLen(results, 2) result_names = {result.pipe.name for result in results} self.assertIn("bar", result_names) self.assertIn("baz", result_names)
def testMultiplePipesWithPids(self): args = pipes.ListNamedPipesFlowArgs() pipe_foo = rdf_client.NamedPipe() pipe_foo.name = "foo-pipe" pipe_foo.client_pid = 42 pipe_bar = rdf_client.NamedPipe() pipe_bar.name = "bar-pipe" pipe_bar.server_pid = 1337 pipe_baz = rdf_client.NamedPipe() pipe_baz.name = "baz-pipe" pipe_baz.server_pid = 108 proc_foo = rdf_client.Process() proc_foo.exe = r"C:\Temp\foo.exe" proc_foo.pid = 42 proc_baz = rdf_client.Process() proc_baz.exe = r"C:\Temp\baz.exe" proc_baz.pid = 108 results = self._RunListNamedPipesFlow( args, pipe_results=[pipe_foo, pipe_bar, pipe_baz], proc_results=[proc_foo, proc_baz], ) self.assertLen(results, 3) results_by_name = {result.pipe.name: result for result in results} self.assertEqual(results_by_name["foo-pipe"].proc.exe, r"C:\Temp\foo.exe") self.assertEqual(results_by_name["baz-pipe"].proc.exe, r"C:\Temp\baz.exe") self.assertEmpty(results_by_name["bar-pipe"].proc.exe)
def testSinglePipeWithClientPid(self): args = pipes.ListNamedPipesFlowArgs() pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.client_pid = 1337 proc = rdf_client.Process() proc.pid = 1337 proc.exe = r"C:\Windows\foo.exe" results = self._RunListNamedPipesFlow( args, pipe_results=[pipe], proc_results=[proc], ) self.assertLen(results, 1) self.assertEqual(results[0].pipe.name, "foo") self.assertEqual(results[0].proc.exe, r"C:\Windows\foo.exe")
def testSinglePipeWithNoMatchingPid(self): args = pipes.ListNamedPipesFlowArgs() pipe = rdf_client.NamedPipe() pipe.name = "foo" pipe.server_pid = 1 proc_bar = rdf_client.Process() proc_bar.pid = 2 proc_bar.exe = r"C:\Windows\bar.exe" proc_baz = rdf_client.Process() proc_baz.pid = 3 proc_baz.exe = r"C:\Windows\baz.exe" results = self._RunListNamedPipesFlow( args, pipe_results=[pipe], proc_results=[proc_bar, proc_baz], ) self.assertLen(results, 1) self.assertEqual(results[0].pipe.name, "foo") self.assertEmpty(results[0].proc.exe)
def ListNamedPipes() -> Iterator[rdf_client.NamedPipe]: """Yields all named pipes available in the system.""" if platform.system() != "Windows": raise RuntimeError(f"Unsupported platform: {platform.system()}") # pylint: disable=g-import-not-at-top # pytype: disable=import-error import ctypes import ctypes.wintypes import win32api import win32file import win32pipe import winerror # pytype: enable=import-error # pylint: enable=g-import-not-at-top # The `GetNamedPipeHandleState` function provided by the `win32pipe` module is # broken (calling it results in invalid function exception). Hence, we need to # go to a lower level and use raw Windows API calls to get this information. # # https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipehandlestatew # pytype: disable=module-attr GetNamedPipeHandleStateW = ctypes.windll.kernel32.GetNamedPipeHandleStateW # pylint: disable=invalid-name # pytype: enable=module-attr GetNamedPipeHandleStateW.argtypes = [ ctypes.wintypes.HANDLE, ctypes.wintypes.LPDWORD, ctypes.wintypes.LPDWORD, ctypes.wintypes.LPDWORD, ctypes.wintypes.LPDWORD, ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD, ] GetNamedPipeHandleStateW.restype = ctypes.wintypes.BOOL # For some reason the `GetNamedPipeClientComputerName` function does not exist # in `win32pipe`. Hence, we implement a low-level wrapper for Windows API for # it ourselves. # # https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeclientcomputernamew # pytype: disable=module-attr GetNamedPipeClientComputerNameW = ctypes.windll.kernel32.GetNamedPipeClientComputerNameW # pylint: disable=invalid-name # pytype: enable=module-attr GetNamedPipeClientComputerNameW.argtypes = [ ctypes.wintypes.HANDLE, ctypes.wintypes.LPWSTR, ctypes.wintypes.ULONG, ] GetNamedPipeClientComputerNameW.restype = ctypes.wintypes.BOOL # https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names for name in os.listdir(r"\\.\pipe"): pipe = rdf_client.NamedPipe() pipe.name = name try: handle = win32file.CreateFile(f"\\\\.\\pipe\\{name}", 0, 0, None, win32file.OPEN_EXISTING, 0, None) except win32file.error as error: # There might be some permission issues. We log the error and skip getting # pipe details, but still yield a result with at least the name filled-in. logging.error("Cannot open pipe '%s': %s", name, error) yield pipe continue with contextlib.closing(handle): try: pipe_info = win32pipe.GetNamedPipeInfo(handle) flags, in_buffer_size, out_buffer_size, max_instance_count = pipe_info pipe.flags = flags pipe.in_buffer_size = in_buffer_size pipe.out_buffer_size = out_buffer_size pipe.max_instance_count = max_instance_count except win32pipe.error as error: # Getting the information might fail (for whatever reason), but we don't # want to fail action execution as other probing calls might succeed. logging.error("Failed to get info about pipe '%s': '%s'", name, error) try: pipe.server_pid = win32pipe.GetNamedPipeServerProcessId(handle) except win32pipe.error as error: # See similar comment for `GetNamedPipeInfo` for more information. message = "Failed to get server pid of pipe '%s': '%s'" logging.error(message, name, error) try: pipe.client_pid = win32pipe.GetNamedPipeClientProcessId(handle) except win32pipe.error as error: # See similar comment for `GetNamedPipeInfo` for more information. message = "Failed to get client pid of pipe '%s': '%s'" logging.error(message, name, error) cur_instance_count = ctypes.wintypes.DWORD() status = GetNamedPipeHandleStateW( ctypes.wintypes.HANDLE(int(handle)), None, ctypes.byref(cur_instance_count), None, None, None, 0, ) if status == 0: # See similar comment for `GetNamedPipeInfo` for more information. error = win32api.GetLastError() logging.error("Failed to get state of pipe '%s': %s", name, error) else: pipe.cur_instance_count = cur_instance_count.value client_computer_name = (ctypes.wintypes.WCHAR * _COMPUTER_NAME_MAX_SIZE)() # pytype: disable=not-callable status = GetNamedPipeClientComputerNameW( ctypes.wintypes.HANDLE(int(handle)), client_computer_name, _COMPUTER_NAME_MAX_SIZE, ) if status == 0: # See similar comment for `GetNamedPipeInfo` for more information. error = win32api.GetLastError() # Not being able to get computer name of a local pipe is expected, there # is no need to log errors in such cases. if error != winerror.ERROR_PIPE_LOCAL: logging.error("Failed to get hostname of pipe '%s': %s", name, error) else: pipe.client_computer_name = client_computer_name.value yield pipe