Package lib :: Package core :: Module pipe
[hide private]
[frames] | no frames]

Source Code for Module lib.core.pipe

  1  # Copyright (C) 2010-2013 Claudio Guarnieri. 
  2  # Copyright (C) 2014-2016 Cuckoo Foundation. 
  3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org 
  4  # See the file 'docs/LICENSE' for copying permission. 
  5   
  6  import logging 
  7  import socket 
  8  import threading 
  9   
 10  from ctypes import create_string_buffer, c_uint, byref, sizeof 
 11   
 12  from lib.common.defines import KERNEL32, PIPE_ACCESS_INBOUND, ERROR_MORE_DATA 
 13  from lib.common.defines import PIPE_TYPE_BYTE, PIPE_WAIT, ERROR_PIPE_CONNECTED 
 14  from lib.common.defines import PIPE_UNLIMITED_INSTANCES, INVALID_HANDLE_VALUE 
 15  from lib.common.defines import FILE_FLAG_WRITE_THROUGH, PIPE_READMODE_BYTE 
 16  from lib.common.defines import ERROR_BROKEN_PIPE, PIPE_TYPE_MESSAGE 
 17  from lib.common.defines import PIPE_ACCESS_DUPLEX, PIPE_READMODE_MESSAGE 
 18   
 19  log = logging.getLogger(__name__) 
 20   
 21  BUFSIZE = 0x10000 
 22   
23 -class PipeForwarder(threading.Thread):
24 """The Pipe Forwarder forwards all data received from a local pipe to 25 the Cuckoo server through a socket.""" 26 sockets = {} 27 active = {} 28
29 - def __init__(self, pipe_handle, destination):
30 threading.Thread.__init__(self) 31 self.pipe_handle = pipe_handle 32 self.destination = destination
33
34 - def run(self):
35 buf = create_string_buffer(BUFSIZE) 36 bytes_read = c_uint() 37 pid = c_uint() 38 39 # The first four bytes indicate the process identifier. In case the 40 # pipe handle is closed in an unknown way, reopening one and 41 # specifying the same process identifier will reuse the same socket, 42 # thus making it look like as if it was never closed in the first 43 # place. 44 success = KERNEL32.ReadFile(self.pipe_handle, 45 byref(pid), sizeof(pid), 46 byref(bytes_read), None) 47 48 if not success or bytes_read.value != sizeof(pid): 49 log.warning("Unable to read the process identifier of this " 50 "log pipe instance.") 51 KERNEL32.CloseHandle(self.pipe_handle) 52 return 53 54 if self.active.get(pid.value): 55 log.warning("A second log pipe handler for an active process is " 56 "being requested, denying request.") 57 KERNEL32.CloseHandle(self.pipe_handle) 58 return 59 60 if pid.value not in self.sockets: 61 self.sockets[pid.value] = \ 62 socket.create_connection(self.destination) 63 64 sock = self.sockets[pid.value] 65 self.active[pid.value] = True 66 67 while True: 68 success = KERNEL32.ReadFile(self.pipe_handle, 69 byref(buf), sizeof(buf), 70 byref(bytes_read), None) 71 72 if success or KERNEL32.GetLastError() == ERROR_MORE_DATA: 73 sock.sendall(buf.raw[:bytes_read.value]) 74 # If we get the broken pipe error then this pipe connection has 75 # been terminated for one reason or another. So break from the 76 # loop and make the socket "inactive", that is, another pipe 77 # connection can in theory pick it up. (This will only happen in 78 # cases where malware for some reason broke our pipe connection). 79 elif KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: 80 break 81 else: 82 log.warning("The log pipe handler has failed, last error %d.", 83 KERNEL32.GetLastError()) 84 break 85 86 self.active[pid.value] = False
87
88 -class PipeDispatcher(threading.Thread):
89 """Receives commands through a local pipe, forwards them to the 90 dispatcher, and returns the response."""
91 - def __init__(self, pipe_handle, dispatcher):
92 threading.Thread.__init__(self) 93 self.pipe_handle = pipe_handle 94 self.dispatcher = dispatcher 95 self.do_run = True
96
97 - def _read_message(self, buf):
98 """Reads a message.""" 99 bytes_read = c_uint() 100 ret = "" 101 102 while True: 103 success = KERNEL32.ReadFile(self.pipe_handle, 104 byref(buf), sizeof(buf), 105 byref(bytes_read), None) 106 107 if KERNEL32.GetLastError() == ERROR_MORE_DATA: 108 ret += buf.raw[:bytes_read.value] 109 elif success: 110 return ret + buf.raw[:bytes_read.value] 111 else: 112 return
113
114 - def run(self):
115 """Run the pipe dispatcher.""" 116 buf = create_string_buffer(BUFSIZE) 117 bytes_written = c_uint() 118 119 while self.do_run: 120 message = self._read_message(buf) 121 if not message: 122 break 123 124 response = self.dispatcher.dispatch(message) or "OK" 125 126 KERNEL32.WriteFile(self.pipe_handle, response, len(response), 127 byref(bytes_written), None) 128 129 KERNEL32.CloseHandle(self.pipe_handle)
130
131 -class PipeServer(threading.Thread):
132 """The Pipe Server accepts incoming pipe handlers and initializes 133 them in a new thread."""
134 - def __init__(self, pipe_handler, pipe_name, message=False, **kwargs):
135 threading.Thread.__init__(self) 136 self.pipe_handler = pipe_handler 137 self.pipe_name = pipe_name 138 self.message = message 139 self.kwargs = kwargs 140 self.do_run = True
141
142 - def run(self):
143 while self.do_run: 144 flags = FILE_FLAG_WRITE_THROUGH 145 if self.message: 146 pipe_handle = KERNEL32.CreateNamedPipeA( 147 self.pipe_name, PIPE_ACCESS_DUPLEX | flags, 148 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 149 PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, None) 150 else: 151 pipe_handle = KERNEL32.CreateNamedPipeA( 152 self.pipe_name, PIPE_ACCESS_INBOUND | flags, 153 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 154 PIPE_UNLIMITED_INSTANCES, 0, BUFSIZE, 0, None) 155 156 if pipe_handle == INVALID_HANDLE_VALUE: 157 log.warning("Error opening logging pipe server.") 158 continue 159 160 if KERNEL32.ConnectNamedPipe(pipe_handle, None) or \ 161 KERNEL32.GetLastError() == ERROR_PIPE_CONNECTED: 162 handler = self.pipe_handler(pipe_handle, **self.kwargs) 163 handler.daemon = True 164 handler.start() 165 else: 166 KERNEL32.CloseHandle(pipe_handle)
167
168 - def stop(self):
169 self.do_run = False
170