diff --git a/ctrlsrv.py b/ctrlsrv.py
new file mode 100755
index 0000000..cd7fcd3
--- /dev/null
+++ b/ctrlsrv.py
@@ -0,0 +1,327 @@
+#!/usr/bin/env python3
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import pyvncs
+from lib import log
+from lib import common
+from argparse import ArgumentParser
+from threading import Thread
+import time
+import sys
+import socket
+import signal
+import readline
+import traceback
+
+#_debug = log.debug
+_debug = print
+
+if common.isWindows():
+ _debug("Wintendo...")
+ import win32ts
+ import win32security
+ import win32con
+ import win32api
+ import ntsecuritycon
+ import win32process
+ import win32event
+
+def signal_handler(signal, frame):
+ _debug("Exiting on signal %s..." % signal)
+ sys.exit(0)
+
+signal.signal(signal.SIGINT, signal_handler)
+
+config = {
+}
+
+class ControlThread(Thread):
+ def __init__(self, threads):
+ Thread.__init__(self)
+ self.threads = threads
+ self.setDaemon(True)
+
+ def run(self):
+ # elimina los threads muertos
+ while True:
+ time.sleep(1)
+ for t in threads:
+ if not t.isAlive():
+ _debug("ControlThread removing dead", t)
+ threads.remove(t)
+
+class VNCThread(Thread):
+ def __init__(self, port, password):
+ Thread.__init__(self)
+ self.ip = None
+ self.port = port
+ self.sock = None
+ self.password = password
+ self.setDaemon(True)
+
+ def __del__(self):
+ _debug("VNCThread died")
+
+
+ def run(self):
+
+ TCP_IP = '0.0.0.0'
+ _debug("[+] Listen...")
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.bind((TCP_IP, int(self.port)))
+ self.sock.listen(4)
+ (conn, (ip,port)) = self.sock.accept()
+
+ _debug("[+] New server socket started for " + ip + ":" + str(port))
+ #_debug("Thread", self)
+ server = pyvncs.server.VncServer(conn, self.password)
+ #server.CONFIG._8bitdither = CONFIG._8bitdither
+ status = server.init()
+
+ if not status:
+ _debug("Error negotiating client init")
+ return False
+ server.protocol()
+
+
+class ClientThread(Thread):
+ def __init__(self, sock, ip, port, config):
+ Thread.__init__(self)
+ self.ip = ip
+ self.port = port
+ self.sock = sock
+ self.setDaemon(True)
+ self.config = config
+
+ def __del__(self):
+ _debug("ClientThread died")
+
+ def run(self):
+ #_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
+ #_debug("Thread", self)
+ f = self.sock.makefile('rw')
+
+ f.write("AUTH:>")
+ f.flush()
+ passwd = f.readline().strip("\n")
+ if passwd != config["PASSWORD"]:
+ time.sleep(1)
+ f.write("!NO AUTH")
+ f.flush()
+ _debug("NO AUTH '%s' != '%s'" % (passwd, config["PASSWORD"]))
+ self.sock.close()
+ return
+
+ while True:
+ f.write("OK:>")
+ f.flush()
+ try:
+ data = f.readline()
+ cmd = data.strip()
+ if not data: break
+
+ if cmd == "_DEBUG":
+ sys.stdout = sys.stderr = f
+ f.write("OK\n")
+
+ elif cmd == "PING":
+ f.write("PONG\n")
+
+ elif cmd == "QUIT":
+ f.write("BYE\n")
+ self.sock.close()
+ return
+
+ elif cmd.startswith("STARTVNC"):
+ params = cmd.split()
+ if len(params) != 3:
+ f.write("!NOT_PARAMS\n")
+ f.flush()
+ continue
+ _debug("START VNC !!!")
+ newthread = VNCThread(params[1], params[2])
+ newthread.setDaemon(True)
+ newthread.start()
+
+ elif cmd == "_WINSESSIONS" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+
+ elif cmd == "_WINCONSOLE" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+ active = win32ts.WTSGetActiveConsoleSessionId()
+ print(active, file=f)
+ token = win32ts.WTSQueryUserToken(active)
+ print(token, file=f)
+ duplicated = win32security.DuplicateTokenEx(token, win32con.MAXIMUM_ALLOWED, win32con.NULL, win32security.TokenPrimary, win32security.SECURITY_ATTRIBUTES())
+ print("duplicated", token, file=f)
+
+ elif cmd == "_TEST" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+ active = win32ts.WTSGetActiveConsoleSessionId()
+ print(active, file=f)
+ token = win32ts.WTSQueryUserToken(active)
+ print("token", token, file=f)
+ ntoken = win32security.DuplicateTokenEx(token, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
+ print("ntoken", ntoken, file=f)
+ #th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.MAXIMUM_ALLOWED)
+ #print("th", th, file=f)
+ shell_as2(ntoken, True, "cmd")
+
+ elif cmd == "_EVAL":
+ while True:
+ f.write("EV:>")
+ f.flush()
+ data = f.readline()
+ cmd = data.strip()
+ if cmd == "": continue
+ if cmd == "_QUIT": break
+ if not data: break
+
+ try:
+ eval(data)
+ except:
+ print("ERROR:", sys.exc_info()[0], file=f)
+ print(traceback.format_exc(), file=f)
+ f.flush()
+
+ _debug("eval:", cmd.strip())
+ f.flush()
+
+ elif cmd == "_ERROR":
+ a = 1/0
+
+ else:
+ f.write("!NO_CMD %s\n" % cmd)
+
+ _debug("command:", cmd.strip())
+ f.flush()
+ except:
+ print("ERROR:", sys.exc_info()[0], file=f)
+ print(traceback.format_exc(), file=f)
+ f.flush()
+
+def get_all_privs(th):
+ # Try to give ourselves some extra privs (only works if we're admin):
+ # SeBackupPrivilege - so we can read anything
+ # SeDebugPrivilege - so we can find out about other processes (otherwise OpenProcess will fail for some)
+ # SeSecurityPrivilege - ??? what does this do?
+
+ # Problem: Vista+ support "Protected" processes, e.g. audiodg.exe. We can't see info about these.
+ # Interesting post on why Protected Process aren't really secure anyway: http://www.alex-ionescu.com/?p=34
+
+ privs = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
+ for privtuple in privs:
+ privs2 = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
+ newprivs = []
+ for privtuple2 in privs2:
+ if privtuple2[0] == privtuple[0]:
+ newprivs.append((privtuple2[0], 2)) # SE_PRIVILEGE_ENABLED
+ else:
+ newprivs.append((privtuple2[0], privtuple2[1]))
+
+ # Adjust privs
+ privs3 = tuple(newprivs)
+ win32security.AdjustTokenPrivileges(th, False, privs3)
+def shell_as(th, enable_privs = 0):
+ #t = thread(th)
+ #print(t.as_text())
+ new_tokenh = win32security.DuplicateTokenEx(th, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
+ print("new_tokenh: %s" % new_tokenh)
+ print("Impersonating...")
+ if enable_privs:
+ get_all_privs(new_tokenh)
+ commandLine = "cmd"
+ si = win32process.STARTUPINFO()
+ print("pysecdump: Starting shell with required privileges...")
+ (hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
+ new_tokenh,
+ None, # AppName
+ commandLine, # Command line
+ None, # Process Security
+ None, # ThreadSecurity
+ 1, # Inherit Handles?
+ win32process.NORMAL_PRIORITY_CLASS,
+ None, # New environment
+ None, # Current directory
+ si) # startup info.
+ win32event.WaitForSingleObject( hProcess, win32event.INFINITE );
+ print("pysecdump: Quitting")
+
+def shell_as2(new_tokenh, enable_privs = 0, commandLine = "cmd"):
+ print("new_tokenh: %s" % new_tokenh)
+ print("Impersonating...")
+ if enable_privs:
+ get_all_privs(new_tokenh)
+ si = win32process.STARTUPINFO()
+ print("pysecdump: Starting shell with required privileges...")
+ (hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
+ new_tokenh,
+ None, # AppName
+ commandLine, # Command line
+ None, # Process Security
+ None, # ThreadSecurity
+ 1, # Inherit Handles?
+ win32process.NORMAL_PRIORITY_CLASS,
+ None, # New environment
+ None, # Current directory
+ si) # startup info.
+ win32event.WaitForSingleObject( hProcess, win32event.INFINITE )
+ print("pysecdump: Quitting")
+
+def main(argv):
+ global threads, config
+ parser = ArgumentParser()
+ parser.add_argument("-l", "--listen-address", dest="TCP_IP",
+ help="Listen in this address, default: %s" % ("0.0.0.0"), required=False, default='0.0.0.0')
+ parser.add_argument("-p", "--port", dest="TCP_PORT",
+ help="Listen in this port, default: %s" % ("5899"), type=int, required=False, default='5899')
+ parser.add_argument("-P", "--password", help="Sets password", required=True, dest="PASSWORD")
+
+ args = parser.parse_args()
+
+ config["PASSWORD"] = args.PASSWORD
+ config["PORT"] = args.TCP_PORT
+
+ sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sockServer.bind((args.TCP_IP, args.TCP_PORT))
+
+ controlthread = ControlThread(threads)
+ controlthread.start()
+ threads.append(controlthread)
+
+ #_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
+ _debug("Runing on:", sys.platform)
+ while True:
+ sockServer.listen(4)
+ (conn, (ip,port)) = sockServer.accept()
+ newthread = ClientThread(conn, ip, port, config)
+ newthread.setDaemon(True)
+ newthread.start()
+ threads.append(newthread)
+ #print(threads)
+
+
+
+if __name__ == "__main__":
+ threads = []
+ main(sys.argv)
diff --git a/lib/common.py b/lib/common.py
new file mode 100644
index 0000000..6af18f8
--- /dev/null
+++ b/lib/common.py
@@ -0,0 +1,68 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import sys
+from multiprocessing import Process, Value
+import subprocess
+import os
+import time
+import mmap
+import psutil
+
+def isWindows():
+ return sys.platform.startswith("win")
+
+def isOSX():
+ return sys.platform.startswith("darwin")
+
+def isLinux():
+ return sys.platform.startswith("linux")
+
+class proc:
+ def __init__(self):
+ self.pid = Value('i', 0)
+ self._process = None
+
+ def __del__(self):
+ pass
+
+ def _setpid(self, pid):
+ self.pid.value = pid
+
+ def getpid(self):
+ return self.pid.value
+
+ def _newproc(self, cmd):
+ pr = subprocess.Popen(cmd)
+ #print("Launched forkproc Process ID:", str(pr.pid))
+ self._setpid(pr.pid)
+
+ def run(self, *cmd):
+ self._process = Process(target=self._newproc, args=(cmd))
+ self._process.start()
+ self._process.join()
+ return self.pid.value
+
+ def terminate(self):
+ if psutil.pid_exists(self.pid.value):
+ p = psutil.Process(self.pid.value)
+ p.terminate()
+ self._process.terminate()
+
+ def waitproc(self):
+ while psutil.pid_exists(self.pid):
+ time.sleep(.25)
diff --git a/lib/const.py b/lib/const.py
new file mode 100644
index 0000000..235e4c5
--- /dev/null
+++ b/lib/const.py
@@ -0,0 +1,29 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import sys
+
+# constants workaround
+class _const:
+ class ConstError(TypeError): pass
+ def __setattr__(self, name, value):
+ if name in self.__dict__.keys():
+ raise (self.ConstError, "Can't rebind const(%s)" % name)
+ self.__dict__[name]=value
+
+sys.modules[__name__]=_const()
+
diff --git a/lib/encodings/common.py b/lib/encodings/common.py
new file mode 100644
index 0000000..948fa94
--- /dev/null
+++ b/lib/encodings/common.py
@@ -0,0 +1,19 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+class ENCODINGS:
+ pass
\ No newline at end of file
diff --git a/lib/encodings/cursor.py b/lib/encodings/cursor.py
new file mode 100644
index 0000000..37cb736
--- /dev/null
+++ b/lib/encodings/cursor.py
@@ -0,0 +1,20 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+from . import common
+
+common.ENCODINGS.cursor = -239
diff --git a/lib/encodings/raw.py b/lib/encodings/raw.py
new file mode 100644
index 0000000..85bfcc8
--- /dev/null
+++ b/lib/encodings/raw.py
@@ -0,0 +1,32 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+from . import common
+from struct import *
+
+def send_image(x, y, w, h, image):
+ _buff = bytearray()
+ rectangles = 1
+ _buff.extend(pack("!BxH", 0, rectangles))
+ _buff.extend(pack("!HHHH", x, y, w, h))
+ _buff.extend(pack(">i", common.ENCODINGS.raw))
+ _buff.extend( image.tobytes() )
+
+ return _buff
+
+common.ENCODINGS.raw = 0
+common.ENCODINGS.raw_send_image = send_image
\ No newline at end of file
diff --git a/lib/log.py b/lib/log.py
new file mode 100644
index 0000000..5c43b42
--- /dev/null
+++ b/lib/log.py
@@ -0,0 +1,28 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import logging
+logging.basicConfig(level=logging.DEBUG, format='[%(threadName)s] %(message)s')
+logger = logging.getLogger('pyvncs')
+
+
+def debug(*args):
+ str = ""
+ for s in args:
+ str = "%s %s" % (str, s)
+ str = str.strip()
+ logger.debug(str)
diff --git a/pyvncs/__pycache__/server.cpython-36.pyc b/pyvncs/__pycache__/server.cpython-36.pyc
index 96d4937..a87805a 100644
Binary files a/pyvncs/__pycache__/server.cpython-36.pyc and b/pyvncs/__pycache__/server.cpython-36.pyc differ
diff --git a/pyvncs/server.py b/pyvncs/server.py
index da7d137..8c5c215 100644
--- a/pyvncs/server.py
+++ b/pyvncs/server.py
@@ -29,6 +29,7 @@ import zlib
import numpy as np
from lib.encodings import *
+from lib import log
def hexdump(data):
str = ""
@@ -182,7 +183,7 @@ class VncServer():
def __del__(self):
- print("VncServer died")
+ log.debug("VncServer died")
def encrypt(self, key, data):
k = des(key, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
@@ -221,7 +222,7 @@ class VncServer():
data = sock.recv(1024)
except socket.timeout:
data = None
- print("getbuff() timeout")
+ log.debug("getbuff() timeout")
return data
@@ -232,15 +233,15 @@ class VncServer():
# RFB version handshake
data = self.getbuff(30)
- print("init received: '%s'" % data)
+ log.debug("init received: '%s'" % data)
server_version = float(self.RFB_VERSION)
try:
client_version = float(data[4:11])
except:
- print("Error parsing client version")
+ log.debug("Error parsing client version")
return False
- print("client, server:", client_version, server_version)
+ log.debug("client, server:", client_version, server_version)
# security types handshake
sendbuff = pack("B", len(self.sectypes)) # number of security types
@@ -254,13 +255,13 @@ class VncServer():
sectype = None
if sectype not in self.sectypes:
- print("Incompatible security type: %s" % data)
+ log.debug("Incompatible security type: %s" % data)
sock.send(pack("B", 1)) # failed handshake
self.sendmessage("Incompatible security type")
sock.close()
return False
- print("sec type data: %s" % data)
+ log.debug("sec type data: %s" % data)
# VNC Auth
if sectype == 2:
@@ -278,9 +279,9 @@ class VncServer():
if data == crypted:
# Handshake successful
sock.send(pack("I", 0))
- print("Auth OK")
+ log.debug("Auth OK")
else:
- print("Invalid auth")
+ log.debug("Invalid auth")
return False
#unsupported VNC auth type
@@ -289,7 +290,7 @@ class VncServer():
# get ClientInit
data = self.getbuff(30)
- print("Clientinit (shared flag)", repr(data))
+ log.debug("Clientinit (shared flag)", repr(data))
self.ServerInit()
@@ -300,9 +301,9 @@ class VncServer():
sock = self.socket
screen = ImageGrab.grab()
- print("screen", repr(screen))
+ log.debug("screen", repr(screen))
size = screen.size
- print("size", repr(size))
+ log.debug("size", repr(size))
del screen
width = size[0]
@@ -339,8 +340,8 @@ class VncServer():
sendbuff += pack("!I", desktop_name_len)
sendbuff += desktop_name.encode()
- print("width", repr(width))
- print("height", repr(height))
+ log.debug("width", repr(width))
+ log.debug("height", repr(height))
sock.send(sendbuff)
@@ -437,7 +438,7 @@ class VncServer():
kbdmap[0xffe2] = keyboard.Key.shift
while True:
- #print(".", end='', flush=True)
+ #log.debug(".", end='', flush=True)
r,_,_ = select.select([self.socket],[],[],0)
if r == []:
#no data
@@ -448,10 +449,10 @@ class VncServer():
try:
data = sock.recv(1) # read first byte
except socket.timeout:
- #print("timeout")
+ #log.debug("timeout")
continue
except Exception as e:
- print("exception '%s'" % e)
+ log.debug("exception '%s'" % e)
sock.close()
break
@@ -462,40 +463,40 @@ class VncServer():
if data[0] == 0: # client SetPixelFormat
data2 = sock.recv(19, socket.MSG_WAITALL)
- print("Client Message Type: Set Pixel Format (0)")
+ log.debug("Client Message Type: Set Pixel Format (0)")
(self.bpp, self.depth, self.bigendian, self.truecolor, self.red_maximum,
self.green_maximum, self.blue_maximum,
self.red_shift, self.green_shift, self.blue_shift
) = unpack("!xxxBBBBHHHBBBxxx", data2)
- print("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
- print("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
- print("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
+ log.debug("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
+ log.debug("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
+ log.debug("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
if self.red_shift > self.blue_shift:
self.primaryOrder = "rgb"
else:
self.primaryOrder = "bgr"
- print("Using order: ", self.primaryOrder)
+ log.debug("Using order: ", self.primaryOrder)
continue
if data[0] == 2: # SetEncoding
data2 = sock.recv(3)
- print("Client Message Type: SetEncoding (2)")
+ log.debug("Client Message Type: SetEncoding (2)")
(nencodings,) = unpack("!xH", data2)
- print("SetEncoding: total encodings", repr(nencodings))
+ log.debug("SetEncoding: total encodings", repr(nencodings))
data2 = sock.recv(4 * nencodings, socket.MSG_WAITALL)
- #print("len", len(data2))
+ #log.debug("len", len(data2))
self.client_encodings = unpack("!%si" % nencodings, data2)
- #print("data", repr(self.client_encodings), len(self.client_encodings))
+ #log.debug("data", repr(self.client_encodings), len(self.client_encodings))
if hasattr(enc.ENCODINGS, "cursor") and enc.ENCODINGS.cursor in self.client_encodings:
- print("Remote cursor encoding present")
+ log.debug("Remote cursor encoding present")
self.remotecursor = True
self.cursorchanged = True
if hasattr(enc.ENCODINGS, "zlib") and enc.ENCODINGS.zlib in self.client_encodings:
- print("Using zlib encoding")
+ log.debug("Using zlib encoding")
self.encoding = enc.ENCODINGS.zlib
continue
@@ -503,10 +504,10 @@ class VncServer():
if data[0] == 3: # FBUpdateRequest
data2 = sock.recv(9, socket.MSG_WAITALL)
- #print("Client Message Type: FBUpdateRequest (3)")
+ #log.debug("Client Message Type: FBUpdateRequest (3)")
#print(len(data2))
(incremental, x, y, w, h) = unpack("!BHHHH", data2)
- #print("RFBU:", incremental, x, y, w, h)
+ #log.debug("RFBU:", incremental, x, y, w, h)
self.SendRectangles(sock, x, y, w, h, incremental)
if self.remotecursor and self.cursorchanged:
@@ -519,7 +520,7 @@ class VncServer():
data2 = sock.recv(7)
# B = U8, L = U32
(downflag, key) = unpack("!BxxL", data2)
- print("KeyEvent", downflag, hex(key))
+ log.debug("KeyEvent", downflag, hex(key))
# special key
if key in kbdmap:
@@ -531,9 +532,9 @@ class VncServer():
kbdkey = None
try:
- print("KEY:", kbdkey)
+ log.debug("KEY:", kbdkey)
except:
- print("KEY: (unprintable)")
+ log.debug("KEY: (unprintable)")
try:
if downflag:
@@ -541,7 +542,7 @@ class VncServer():
else:
keyboard.Controller().release(kbdkey)
except:
- print("Error sending key")
+ log.debug("Error sending key")
continue
@@ -557,46 +558,46 @@ class VncServer():
mouse.Controller().position = (x, y)
if buttons[0] and not left_pressed:
- print("LEFT PRESSED")
+ log.debug("LEFT PRESSED")
mouse.Controller().press(mouse.Button.left)
left_pressed = 1
elif not buttons[0] and left_pressed:
- print("LEFT RELEASED")
+ log.debug("LEFT RELEASED")
mouse.Controller().release(mouse.Button.left)
left_pressed = 0
if buttons[1] and not middle_pressed:
- print("MIDDLE PRESSED")
+ log.debug("MIDDLE PRESSED")
mouse.Controller().press(mouse.Button.middle)
middle_pressed = 1
elif not buttons[1] and middle_pressed:
- print("MIDDLE RELEASED")
+ log.debug("MIDDLE RELEASED")
mouse.Controller().release(mouse.Button.middle)
middle_pressed = 0
if buttons[2] and not right_pressed:
- print("RIGHT PRESSED")
+ log.debug("RIGHT PRESSED")
mouse.Controller().press(mouse.Button.right)
right_pressed = 1
elif not buttons[2] and right_pressed:
- print("RIGHT RELEASED")
+ log.debug("RIGHT RELEASED")
mouse.Controller().release(mouse.Button.right)
right_pressed = 0
if buttons[3]:
- print("SCROLLUP PRESSED")
+ log.debug("SCROLLUP PRESSED")
mouse.Controller().scroll(0, 2)
if buttons[4]:
- print("SCROLLDOWN PRESSED")
+ log.debug("SCROLLDOWN PRESSED")
mouse.Controller().scroll(0, -2)
- #print("PointerEvent", buttonmask, x, y)
+ #log.debug("PointerEvent", buttonmask, x, y)
continue
else:
data2 = sock.recv(4096)
- print("RAW Server received data:", repr(data[0]) , data+data2)
+ log.debug("RAW Server received data:", repr(data[0]) , data+data2)
def GetRectangle(self, x, y, w, h):
@@ -621,7 +622,7 @@ class VncServer():
def SendRectangles(self, sock, x, y, w, h, incremental=0):
# send FramebufferUpdate to client
- #print("start SendRectangles")
+ #log.debug("start SendRectangles")
rectangle = self.GetRectangle(x, y, w, h)
if not rectangle:
rectangle = Image.new("RGB", [w, h], (0,0,0))
@@ -650,7 +651,7 @@ class VncServer():
(x, y, _, _) = diff.getbbox()
w = rectangle.width
h = rectangle.height
- #print("XYWH:", x,y,w,h, "diff", repr(diff.getbbox()))
+ #log.debug("XYWH:", x,y,w,h, "diff", repr(diff.getbbox()))
stimeout = sock.gettimeout()
sock.settimeout(None)
@@ -677,7 +678,7 @@ class VncServer():
#redMask = ((1 << redBits) - 1) << self.red_shift
#greenMask = ((1 << greenBits) - 1) << self.green_shift
#blueMask = ((1 << blueBits) - 1) << self.blue_shift
- #print("redMask", redMask, greenMask, blueMask)
+ #log.debug("redMask", redMask, greenMask, blueMask)
if self.primaryOrder == "bgr":
self.blue_shift = 0
@@ -757,7 +758,7 @@ class VncServer():
sendbuff.extend(pack("!HHHH", x, y, w, h))
sendbuff.extend(pack(">i", self.encoding))
- print("Compressing...")
+ log.debug("Compressing...")
zlibdata = compress.compress( image.tobytes() )
zlibdata += compress.flush()
l = pack("!I", len(zlibdata) )
@@ -769,7 +770,7 @@ class VncServer():
# send with RAW encoding
sendbuff.extend(enc.ENCODINGS.raw_send_image(x, y, w, h, image))
else:
- print("[!] Unsupported BPP: %s" % self.bpp)
+ log.debug("[!] Unsupported BPP: %s" % self.bpp)
self.framebuffer = lastshot
try:
@@ -778,4 +779,4 @@ class VncServer():
# connection closed?
return False
sock.settimeout(stimeout)
- #print("end SendRectangles")
+ #log.debug("end SendRectangles")
diff --git a/requeriments.txt b/requeriments.txt
index 50504bc..dbfde75 100644
--- a/requeriments.txt
+++ b/requeriments.txt
@@ -1,2 +1,4 @@
-pyuserinput
+pydes
+pynput
+numpy
diff --git a/server.py b/server.py
index 0a6afa2..02a9f95 100755
--- a/server.py
+++ b/server.py
@@ -5,9 +5,19 @@ import pyvncs
from argparse import ArgumentParser
from threading import Thread
from time import sleep
-
import sys
import socket
+import signal
+from lib import log
+
+#_debug = log.debug
+_debug = print
+
+def signal_handler(signal, frame):
+ _debug("Exiting on %s signal..." % signal)
+ sys.exit(0)
+
+signal.signal(signal.SIGINT, signal_handler)
class ControlThread(Thread):
@@ -22,7 +32,7 @@ class ControlThread(Thread):
sleep(1)
for t in threads:
if not t.isAlive():
- print("ControlThread removing dead", t)
+ _debug("ControlThread removing dead", t)
threads.remove(t)
class ClientThread(Thread):
@@ -34,17 +44,17 @@ class ClientThread(Thread):
self.setDaemon(True)
def __del__(self):
- print("ClientThread died")
+ _debug("ClientThread died")
def run(self):
- print("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
- #print("Thread", self)
+ _debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
+ #_debug("Thread", self)
server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD)
server.CONFIG._8bitdither = CONFIG._8bitdither
status = server.init()
if not status:
- print("Error negotiating client init")
+ _debug("Error negotiating client init")
return False
server.protocol()
@@ -61,11 +71,17 @@ def main(argv):
help="Listen in this port, default: %s" % ("5901"), type=int, required=False, default='5901')
parser.add_argument("-P", "--password", help="Sets password", required=True, dest="VNC_PASSWORD")
parser.add_argument("-8", "--8bitdither", help="Enable 8 bit dithering", required=False, action='store_true', dest="dither")
+ parser.add_argument("-O", "--output-file", help="Redirects all debug output to file", required=False, dest="OUTFILE")
+
args = parser.parse_args()
-
+
+ if args.OUTFILE is not None:
+ fsock = open(args.OUTFILE, 'w')
+ sys.stdout = sys.stderr = fsock
+
# Multithreaded Python server
TCP_IP = '0.0.0.0' if not hasattr(args,"TCP_IP") else args.TCP_IP
- TCP_PORT = '0.0.0.0' if not hasattr(args,"TCP_PORT") else args.TCP_PORT
+ TCP_PORT = '5901' if not hasattr(args,"TCP_PORT") else args.TCP_PORT
VNC_PASSWORD = args.VNC_PASSWORD
CONFIG._8bitdither = args.dither
@@ -77,8 +93,8 @@ def main(argv):
controlthread.start()
threads.append(controlthread)
- print("Multithreaded Python server : Waiting for connections from TCP clients...")
- print("Runing on:", sys.platform)
+ _debug("Multithreaded Python server : Waiting for connections from TCP clients...")
+ _debug("Runing on:", sys.platform)
while True:
sockServer.listen(4)
(conn, (ip,port)) = sockServer.accept()
@@ -95,8 +111,8 @@ if __name__ == "__main__":
main(sys.argv)
except KeyboardInterrupt:
# quit
- print("Exiting on ctrl+c...")
+ _debug("Exiting on ctrl+c...")
#for t in threads:
- # print("Killing", t)
+ # _debug("Killing", t)
sys.exit()
\ No newline at end of file
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..52c7033
--- /dev/null
+++ b/test.py
@@ -0,0 +1,16 @@
+from lib import common
+import time
+
+if __name__ == '__main__':
+ #c = ["/bin/ls", "-alh"]
+ #c = ["/bin/sleep", "5"]
+ c = ["./test.sh"]
+ r = common.proc()
+ r.run(c)
+ print("PID", r.getpid())
+ #print("Waiting...")
+ #r.waitproc()
+ time.sleep(1)
+ r.terminate()
+ del r
+ print("DONE")
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..9c56fd9
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+for a in $(seq 1 5); do
+echo "$a $$"
+sleep 1
+done
+
diff --git a/winservice.py b/winservice.py
new file mode 100644
index 0000000..dea87ec
--- /dev/null
+++ b/winservice.py
@@ -0,0 +1,79 @@
+import win32service
+import win32serviceutil
+import win32api
+import win32con
+import win32event
+import win32evtlogutil
+import servicemanager
+import os
+import sys
+from lib import const
+from lib import common
+from win32api import OutputDebugString as ODS
+import traceback
+
+
+const.SERVICENAME = "Test Service"
+const.SERVICEDNAME = "Test Service"
+const.SERVICEDESC = "Test Service Description"
+
+const.CHILD = [
+ "C:\\Program Files\\Python36\\python.exe",
+ "C:\\pyvncs\\ctrlsrv.py",
+ "-P",
+ "kaka80"
+]
+
+class service(win32serviceutil.ServiceFramework):
+
+ _svc_name_ = const.SERVICENAME
+ _svc_display_name_ = const.SERVICEDNAME
+ _svc_description_ = const.SERVICEDESC
+
+ def __init__(self, args):
+ win32serviceutil.ServiceFramework.__init__(self, args)
+ self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
+
+ def SvcStop(self):
+ self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+ win32event.SetEvent(self.hWaitStop)
+
+ def SvcDoRun(self):
+ servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
+ self.timeout = 3000
+
+ servicemanager.LogInfoMsg("%s - is running 1" % const.SERVICENAME)
+ r = common.proc()
+ try:
+ r.run(const.CHILD)
+ except:
+ servicemanager.LogInfoMsg("ERROR: %s" % sys.exc_info()[0])
+ servicemanager.LogInfoMsg(traceback.format_exc())
+ sys.exit(1)
+
+ newpid = r.getpid()
+ servicemanager.LogInfoMsg("%s - started child with pid %s" % (const.SERVICENAME, newpid))
+
+ while True:
+ # Wait for service stop signal, if I timeout, loop again
+ rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
+ # Check to see if self.hWaitStop happened
+ if rc == win32event.WAIT_OBJECT_0:
+ # Stop signal encountered
+ servicemanager.LogInfoMsg("%s - STOPPED" % const.SERVICENAME)
+ r.terminate()
+ break
+ #else:
+ # servicemanager.LogInfoMsg("%s - still running" % const.SERVICENAME)
+
+
+def ctrlHandler(ctrlType):
+ return True
+
+if __name__ == '__main__':
+ ODS("__main__\n")
+ servicemanager.LogInfoMsg("TEST")
+ appdir = os.path.abspath(os.path.dirname(sys.argv[0]))
+ os.chdir(appdir)
+ win32api.SetConsoleCtrlHandler(ctrlHandler, True)
+ win32serviceutil.HandleCommandLine(service)