From 57623a6933ac2e2c3fc520cfc4caab235aabf8d1 Mon Sep 17 00:00:00 2001 From: Matias Fernandez Date: Tue, 9 Apr 2019 20:34:12 -0400 Subject: [PATCH 1/2] Testing for windows service --- ctrlsrv.py | 327 +++++++++++++++++++++++ lib/common.py | 68 +++++ lib/const.py | 29 ++ lib/encodings/common.py | 19 ++ lib/encodings/cursor.py | 20 ++ lib/encodings/raw.py | 32 +++ lib/log.py | 28 ++ pyvncs/__pycache__/server.cpython-36.pyc | Bin 15348 -> 15507 bytes pyvncs/server.py | 101 +++---- requeriments.txt | 4 +- server.py | 40 ++- test.py | 16 ++ test.sh | 6 + winservice.py | 79 ++++++ 14 files changed, 706 insertions(+), 63 deletions(-) create mode 100755 ctrlsrv.py create mode 100644 lib/common.py create mode 100644 lib/const.py create mode 100644 lib/encodings/common.py create mode 100644 lib/encodings/cursor.py create mode 100644 lib/encodings/raw.py create mode 100644 lib/log.py create mode 100644 test.py create mode 100755 test.sh create mode 100644 winservice.py 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 96d4937f7aa1c789b6849ac1fdd3d1cd5a87e147..a87805a19fc19b9cdcb77213270f390e2b2407b4 100644 GIT binary patch delta 5530 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 KeEfr*k^cfe1M6@A delta 5449 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 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) From a6327060f9fb2c17d3671e83d7c52324c9111814 Mon Sep 17 00:00:00 2001 From: Matias Fernandez Date: Mon, 29 Jul 2019 22:54:48 -0400 Subject: [PATCH 2/2] Mejoras en general --- .gitignore | 2 + lib/bgr233_palette.py | 260 +++++++++++++ lib/encodings/__init__.py | 4 +- lib/encodings/common.py | 12 +- lib/encodings/cursor.py | 18 +- lib/encodings/raw.py | 34 +- lib/encodings/zlib.py | 54 +++ lib/imagegrab.py | 41 ++ lib/kbdctrl.py | 47 +++ lib/kbdmap.py | 79 ++++ lib/mousectrl.py | 60 +++ lib/oshelpers/windows.py | 31 ++ pyvncs/__pycache__/server.cpython-36.pyc | Bin 15507 -> 0 bytes pyvncs/server.py | 469 +++++------------------ requeriments.txt | 3 +- server.py | 17 +- 16 files changed, 738 insertions(+), 393 deletions(-) create mode 100644 .gitignore create mode 100644 lib/bgr233_palette.py create mode 100644 lib/encodings/zlib.py create mode 100644 lib/imagegrab.py create mode 100644 lib/kbdctrl.py create mode 100644 lib/kbdmap.py create mode 100644 lib/mousectrl.py create mode 100644 lib/oshelpers/windows.py delete mode 100644 pyvncs/__pycache__/server.cpython-36.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43ae0e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.py[cod] diff --git a/lib/bgr233_palette.py b/lib/bgr233_palette.py new file mode 100644 index 0000000..8fa16b2 --- /dev/null +++ b/lib/bgr233_palette.py @@ -0,0 +1,260 @@ +# BGR233 palette + +palette = [ + 0, 0, 0, + 36, 0, 0, + 73, 0, 0, + 109, 0, 0, + 146, 0, 0, + 182, 0, 0, + 219, 0, 0, + 255, 0, 0, + 0, 36, 0, + 36, 36, 0, + 73, 36, 0, + 109, 36, 0, + 146, 36, 0, + 182, 36, 0, + 219, 36, 0, + 255, 36, 0, + 0, 73, 0, + 36, 73, 0, + 73, 73, 0, + 109, 73, 0, + 146, 73, 0, + 182, 73, 0, + 219, 73, 0, + 255, 73, 0, + 0, 109, 0, + 36, 109, 0, + 73, 109, 0, + 109, 109, 0, + 146, 109, 0, + 182, 109, 0, + 219, 109, 0, + 255, 109, 0, + 0, 146, 0, + 36, 146, 0, + 73, 146, 0, + 109, 146, 0, + 146, 146, 0, + 182, 146, 0, + 219, 146, 0, + 255, 146, 0, + 0, 182, 0, + 36, 182, 0, + 73, 182, 0, + 109, 182, 0, + 146, 182, 0, + 182, 182, 0, + 219, 182, 0, + 255, 182, 0, + 0, 219, 0, + 36, 219, 0, + 73, 219, 0, + 109, 219, 0, + 146, 219, 0, + 182, 219, 0, + 219, 219, 0, + 255, 219, 0, + 0, 255, 0, + 36, 255, 0, + 73, 255, 0, + 109, 255, 0, + 146, 255, 0, + 182, 255, 0, + 219, 255, 0, + 255, 255, 0, + 0, 0, 85, + 36, 0, 85, + 73, 0, 85, + 109, 0, 85, + 146, 0, 85, + 182, 0, 85, + 219, 0, 85, + 255, 0, 85, + 0, 36, 85, + 36, 36, 85, + 73, 36, 85, + 109, 36, 85, + 146, 36, 85, + 182, 36, 85, + 219, 36, 85, + 255, 36, 85, + 0, 73, 85, + 36, 73, 85, + 73, 73, 85, + 109, 73, 85, + 146, 73, 85, + 182, 73, 85, + 219, 73, 85, + 255, 73, 85, + 0, 109, 85, + 36, 109, 85, + 73, 109, 85, + 109, 109, 85, + 146, 109, 85, + 182, 109, 85, + 219, 109, 85, + 255, 109, 85, + 0, 146, 85, + 36, 146, 85, + 73, 146, 85, + 109, 146, 85, + 146, 146, 85, + 182, 146, 85, + 219, 146, 85, + 255, 146, 85, + 0, 182, 85, + 36, 182, 85, + 73, 182, 85, + 109, 182, 85, + 146, 182, 85, + 182, 182, 85, + 219, 182, 85, + 255, 182, 85, + 0, 219, 85, + 36, 219, 85, + 73, 219, 85, + 109, 219, 85, + 146, 219, 85, + 182, 219, 85, + 219, 219, 85, + 255, 219, 85, + 0, 255, 85, + 36, 255, 85, + 73, 255, 85, + 109, 255, 85, + 146, 255, 85, + 182, 255, 85, + 219, 255, 85, + 255, 255, 85, + 0, 0, 170, + 36, 0, 170, + 73, 0, 170, + 109, 0, 170, + 146, 0, 170, + 182, 0, 170, + 219, 0, 170, + 255, 0, 170, + 0, 36, 170, + 36, 36, 170, + 73, 36, 170, + 109, 36, 170, + 146, 36, 170, + 182, 36, 170, + 219, 36, 170, + 255, 36, 170, + 0, 73, 170, + 36, 73, 170, + 73, 73, 170, + 109, 73, 170, + 146, 73, 170, + 182, 73, 170, + 219, 73, 170, + 255, 73, 170, + 0, 109, 170, + 36, 109, 170, + 73, 109, 170, + 109, 109, 170, + 146, 109, 170, + 182, 109, 170, + 219, 109, 170, + 255, 109, 170, + 0, 146, 170, + 36, 146, 170, + 73, 146, 170, + 109, 146, 170, + 146, 146, 170, + 182, 146, 170, + 219, 146, 170, + 255, 146, 170, + 0, 182, 170, + 36, 182, 170, + 73, 182, 170, + 109, 182, 170, + 146, 182, 170, + 182, 182, 170, + 219, 182, 170, + 255, 182, 170, + 0, 219, 170, + 36, 219, 170, + 73, 219, 170, + 109, 219, 170, + 146, 219, 170, + 182, 219, 170, + 219, 219, 170, + 255, 219, 170, + 0, 255, 170, + 36, 255, 170, + 73, 255, 170, + 109, 255, 170, + 146, 255, 170, + 182, 255, 170, + 219, 255, 170, + 255, 255, 170, + 0, 0, 255, + 36, 0, 255, + 73, 0, 255, + 109, 0, 255, + 146, 0, 255, + 182, 0, 255, + 219, 0, 255, + 255, 0, 255, + 0, 36, 255, + 36, 36, 255, + 73, 36, 255, + 109, 36, 255, + 146, 36, 255, + 182, 36, 255, + 219, 36, 255, + 255, 36, 255, + 0, 73, 255, + 36, 73, 255, + 73, 73, 255, + 109, 73, 255, + 146, 73, 255, + 182, 73, 255, + 219, 73, 255, + 255, 73, 255, + 0, 109, 255, + 36, 109, 255, + 73, 109, 255, + 109, 109, 255, + 146, 109, 255, + 182, 109, 255, + 219, 109, 255, + 255, 109, 255, + 0, 146, 255, + 36, 146, 255, + 73, 146, 255, + 109, 146, 255, + 146, 146, 255, + 182, 146, 255, + 219, 146, 255, + 255, 146, 255, + 0, 182, 255, + 36, 182, 255, + 73, 182, 255, + 109, 182, 255, + 146, 182, 255, + 182, 182, 255, + 219, 182, 255, + 255, 182, 255, + 0, 219, 255, + 36, 219, 255, + 73, 219, 255, + 109, 219, 255, + 146, 219, 255, + 182, 219, 255, + 219, 219, 255, + 255, 219, 255, + 0, 255, 255, + 36, 255, 255, + 73, 255, 255, + 109, 255, 255, + 146, 255, 255, + 182, 255, 255, + 219, 255, 255, + 255, 255, 255 +] diff --git a/lib/encodings/__init__.py b/lib/encodings/__init__.py index 4d4f35d..2ee2e55 100644 --- a/lib/encodings/__init__.py +++ b/lib/encodings/__init__.py @@ -16,5 +16,7 @@ # along with this program. If not, see . # at least, raw encoding is needed by the rfb protocol -from . import common as enc +from . import common from . import raw +from . import zlib +from . import cursor diff --git a/lib/encodings/common.py b/lib/encodings/common.py index 948fa94..4b421b6 100644 --- a/lib/encodings/common.py +++ b/lib/encodings/common.py @@ -15,5 +15,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +encodings = {} + class ENCODINGS: - pass \ No newline at end of file + raw = 0 + zlib = 6 + # supported pseudo-encodings + cursor = -239 + +encodings_priority = [ + ENCODINGS.zlib, + ENCODINGS.raw +] diff --git a/lib/encodings/cursor.py b/lib/encodings/cursor.py index 37cb736..6fbe3a6 100644 --- a/lib/encodings/cursor.py +++ b/lib/encodings/cursor.py @@ -16,5 +16,21 @@ # along with this program. If not, see . from . import common +from struct import * +from lib import log +import zlib -common.ENCODINGS.cursor = -239 +class Encoding: + name = 'Cursor' + id = -239 + description = 'Cursor pseudo encoding' + enabled = True + pseudoEncoding = True + + cursor_sent = False + + def __init__(self): + log.debug("Initialized", __name__) + +common.encodings[common.ENCODINGS.cursor] = Encoding +log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id)) diff --git a/lib/encodings/raw.py b/lib/encodings/raw.py index 85bfcc8..fbfb36b 100644 --- a/lib/encodings/raw.py +++ b/lib/encodings/raw.py @@ -17,16 +17,30 @@ from . import common from struct import * +from lib import log -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() ) +class Encoding: + _buff = None - return _buff + name = 'raw' + id = 0 + description = 'Raw VNC encoding' + enabled = True + firstUpdateSent = False -common.ENCODINGS.raw = 0 -common.ENCODINGS.raw_send_image = send_image \ No newline at end of file + def __init__(self): + log.debug("Initialized", __name__) + + def send_image(self, x, y, w, h, image): + self._buff = bytearray() + rectangles = 1 + self._buff.extend(pack("!BxH", 0, rectangles)) # message type 0 == FramebufferUpdate + self._buff.extend(pack("!HHHH", x, y, w, h)) + self._buff.extend(pack(">i", self.id)) + self._buff.extend( image.tobytes() ) + + return self._buff + +common.encodings[common.ENCODINGS.raw] = Encoding + +log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id)) diff --git a/lib/encodings/zlib.py b/lib/encodings/zlib.py new file mode 100644 index 0000000..ec25348 --- /dev/null +++ b/lib/encodings/zlib.py @@ -0,0 +1,54 @@ +from . import common +from struct import * +from lib import log +import zlib + + +class Encoding: + name = 'zlib' + id = 6 + description = 'zlib VNC encoding' + enabled = True + firstUpdateSent = False + _compressObj = None + + def __init__(self): + log.debug("Initialized", __name__) + self._compressObj = zlib.compressobj( + zlib.Z_DEFAULT_COMPRESSION, # level: 0-9 + zlib.DEFLATED, # method: must be DEFLATED + zlib.MAX_WBITS, # window size in bits: + # -15..-8: negate, suppress header + # 8..15: normal + # 16..30: subtract 16, gzip header + zlib.DEF_MEM_LEVEL, # mem level: 1..8/9 + zlib.Z_DEFAULT_STRATEGY # strategy: + # 0 = Z_DEFAULT_STRATEGY + # 1 = Z_FILTERED + # 2 = Z_HUFFMAN_ONLY + # 3 = Z_RLE + # 4 = Z_FIXED + ) + + def send_image(self, x, y, w, h, image): + sendbuff = bytearray() + + rectangles = 1 + sendbuff.extend(pack("!BxH", 0, rectangles)) # message type 0 == FramebufferUpdate + sendbuff.extend(pack("!HHHH", x, y, w, h)) + sendbuff.extend(pack(">i", self.id)) + + #log.debug("Compressing...") + zlibdata = self._compressObj.compress( image.tobytes() ) + zlibdata += self._compressObj.flush(zlib.Z_FULL_FLUSH) + #log.debug("LEN", len(zlibdata)) + + l = pack("!I", len(zlibdata) ) + sendbuff.extend( l ) # send length + sendbuff.extend( zlibdata ) # send compressed data + + return sendbuff + +common.encodings[common.ENCODINGS.zlib] = Encoding + +log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id)) diff --git a/lib/imagegrab.py b/lib/imagegrab.py new file mode 100644 index 0000000..6ff18de --- /dev/null +++ b/lib/imagegrab.py @@ -0,0 +1,41 @@ +import sys +from PIL import Image +from lib import log + +if sys.platform == "linux" or sys.platform == "linux2": + log.debug("ImageGrab: running on Linux") + from Xlib import display, X + # take screen images, that's not the best way, so here + # we use directly use xlib to take the screenshot. + class ImageGrab(): + def grab(): + dsp = display.Display() + root = dsp.screen().root + geom = root.get_geometry() + w = geom.width + h = geom.height + raw = root.get_image(0, 0, w ,h, X.ZPixmap, 0xffffffff) + image = Image.frombytes("RGB", (w, h), raw.data, "raw", "BGRX") + return image + +elif sys.platform == "darwin": + log.debug("ImageGrab: running on darwin") + import Quartz.CoreGraphics as CG + class ImageGrab(): + def grab(): + screenshot = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault) + width = CG.CGImageGetWidth(screenshot) + height = CG.CGImageGetHeight(screenshot) + bytesperrow = CG.CGImageGetBytesPerRow(screenshot) + + pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(screenshot)) + + i = Image.frombytes("RGBA", (width, height), pixeldata) + (b, g, r, x) = i.split() + i = Image.merge("RGBX", (r, g, b, x)) + + return i + +else: + log.debug("ImageGrab: running on Unknown!") + from PIL import ImageGrab diff --git a/lib/kbdctrl.py b/lib/kbdctrl.py new file mode 100644 index 0000000..5a9456a --- /dev/null +++ b/lib/kbdctrl.py @@ -0,0 +1,47 @@ +import sys +from struct import pack, unpack +from pynput import keyboard +from lib import log +from lib.kbdmap import * + +class KeyboardController: + kbdmap = kbdmap + kbdkey = '' + downflag = None + key = None + controller = None + kbd = None + + def __init__(self): + self.kbd = keyboard + self.controller = self.kbd.Controller() + + def process_event(self, data): + # B = U8, L = U32 + (self.downflag, self.key) = unpack("!BxxL", data) + log.debug("KeyEvent", self.downflag, hex(self.key)) + + # special key + if self.key in self.kbdmap: + self.kbdkey = self.kbdmap[self.key] + log.debug("SPECIAL KEY", self.kbdkey) + else: # normal key + try: + self.kbdkey = self.kbd.KeyCode.from_char(chr(self.key)) + except: + self.kbdkey = None + + # debug keypress to stdout + try: + log.debug("KEY:", self.kbdkey) + except: + log.debug("KEY: (unprintable)") + + # send the actual keyboard event + try: + if self.downflag: + self.controller.press(self.kbdkey) + else: + self.controller.release(self.kbdkey) + except: + log.debug("Error sending key") diff --git a/lib/kbdmap.py b/lib/kbdmap.py new file mode 100644 index 0000000..e49e30e --- /dev/null +++ b/lib/kbdmap.py @@ -0,0 +1,79 @@ +import sys +from pynput import keyboard + +__all__ = ['kbdmap'] + +kbdmap = { + 0xff08: keyboard.Key.backspace, + 0xff09: keyboard.Key.tab, + 0xff0d: keyboard.Key.enter, + 0xff1b: keyboard.Key.esc, + 0xff63: keyboard.Key.insert if hasattr(keyboard.Key, "insert") else None, + 0xffff: keyboard.Key.delete, + 0xff50: keyboard.Key.home, + 0xff57: keyboard.Key.end, + 0xff55: keyboard.Key.page_up, + 0xff56: keyboard.Key.page_down, + 0xff51: keyboard.Key.left, + 0xff52: keyboard.Key.up, + 0xff53: keyboard.Key.right, + 0xff54: keyboard.Key.down, + 0xffbe: keyboard.Key.f1, + 0xffbf: keyboard.Key.f2, + 0xffc0: keyboard.Key.f3, + 0xffc1: keyboard.Key.f4, + 0xffc2: keyboard.Key.f5, + 0xffc3: keyboard.Key.f6, + 0xffc4: keyboard.Key.f7, + 0xffc5: keyboard.Key.f8, + 0xffc6: keyboard.Key.f9, + 0xffc7: keyboard.Key.f10, + 0xffc8: keyboard.Key.f11, + 0xffc9: keyboard.Key.f12, + 0xffca: keyboard.Key.f13, + 0xffcb: keyboard.Key.f14, + 0xffcc: keyboard.Key.f15, + 0xffcd: keyboard.Key.f16, + 0xffce: keyboard.Key.f17, + 0xffcf: keyboard.Key.f18, + 0xffd0: keyboard.Key.f19, + 0xffd1: keyboard.Key.f20, + 0xffe1: keyboard.Key.shift_l, + 0xffe2: keyboard.Key.shift_r, + 0xffe3: keyboard.Key.ctrl_l, + 0xffe4: keyboard.Key.ctrl_r, + 0xffe7: None, # "KEY_MetaLeft" + 0xffe8: None, # "KEY_MetaRight" + #0xffe9: keyboard.Key.cmd_l, + 0xffe9: keyboard.Key.alt, + 0xffea: keyboard.Key.alt_gr, # "KEY_AltRight" + 0xff14: keyboard.Key.scroll_lock if hasattr(keyboard.Key, "scroll_lock") else None, + 0xff15: keyboard.Key.print_screen if hasattr(keyboard.Key, "print_screen") else None, # "KEY_Sys_Req" + 0xff7f: keyboard.Key.num_lock if hasattr(keyboard.Key, "num_lock") else None, + 0xffe5: keyboard.Key.caps_lock, + 0xff13: keyboard.Key.pause if hasattr(keyboard.Key, "pause") else None, + 0xffeb: keyboard.Key.cmd_r, # "KEY_Super_L" + 0xffec: keyboard.Key.cmd_r, # "KEY_Super_R" + 0xffed: None, # "KEY_Hyper_L" + 0xffee: None, # "KEY_Hyper_R" + 0xffb0: None, # "KEY_KP_0" + 0xffb1: None, # "KEY_KP_1" + 0xffb2: None, # "KEY_KP_2" + 0xffb3: None, # "KEY_KP_3" + 0xffb4: None, # "KEY_KP_4" + 0xffb5: None, # "KEY_KP_5" + 0xffb6: None, # "KEY_KP_6" + 0xffb7: None, # "KEY_KP_7" + 0xffb8: None, # "KEY_KP_8" + 0xffb9: None, # "KEY_KP_9" + 0xff8d: None, # "KEY_KP_Enter" + 0x002f: "/", # KEY_ForwardSlash + 0x005c: "\\", # KEY_BackSlash + 0x0020: keyboard.Key.space, # "KEY_SpaceBar" + 0xff7e: keyboard.Key.alt_gr, # altgr, at least on a mac (?) + #0xfe03: keyboard.Key.alt_l, + 0xfe03: keyboard.Key.cmd_l, +} + +if sys.platform == "darwin": + kbdmap[0xffe2] = keyboard.Key.shift diff --git a/lib/mousectrl.py b/lib/mousectrl.py new file mode 100644 index 0000000..3fc9179 --- /dev/null +++ b/lib/mousectrl.py @@ -0,0 +1,60 @@ +from struct import pack, unpack +from pynput import mouse +from lib import log + +class MouseController(): + def __init__(self): + self.buttonmask = 0 + self.buttons = [0, 0, 0, 0, 0, 0, 0, 0] + self.left_pressed = 0 + self.right_pressed = 0 + self.middle_pressed = 0 + + def process_event(self, data): + (self.buttonmask, x, y) = unpack("!BHH", data) + self.buttons[0] = self.buttonmask & int("0000001", 2) # left button + self.buttons[1] = self.buttonmask & int("0000010", 2) # middle button + self.buttons[2] = self.buttonmask & int("0000100", 2) # right button + self.buttons[3] = self.buttonmask & int("0001000", 2) # scroll up + self.buttons[4] = self.buttonmask & int("0010000", 2) # scroll down + + # set mouse position + mouse.Controller().position = (x, y) + + # process mouse button events + if self.buttons[0] and not self.left_pressed: + log.debug("LEFT PRESSED") + mouse.Controller().press(mouse.Button.left) + self.left_pressed = 1 + elif not self.buttons[0] and self.left_pressed: + log.debug("LEFT RELEASED") + mouse.Controller().release(mouse.Button.left) + self.left_pressed = 0 + + if self.buttons[1] and not self.middle_pressed: + log.debug("MIDDLE PRESSED") + mouse.Controller().press(mouse.Button.middle) + self.middle_pressed = 1 + elif not self.buttons[1] and self.middle_pressed: + log.debug("MIDDLE RELEASED") + mouse.Controller().release(mouse.Button.middle) + self.middle_pressed = 0 + + if self.buttons[2] and not self.right_pressed: + log.debug("RIGHT PRESSED") + mouse.Controller().press(mouse.Button.right) + self.right_pressed = 1 + elif not self.buttons[2] and self.right_pressed: + log.debug("RIGHT RELEASED") + mouse.Controller().release(mouse.Button.right) + self.right_pressed = 0 + + if self.buttons[3]: + log.debug("SCROLLUP PRESSED") + mouse.Controller().scroll(0, 2) + + if self.buttons[4]: + log.debug("SCROLLDOWN PRESSED") + mouse.Controller().scroll(0, -2) + + #log.debug("PointerEvent", buttonmask, x, y) diff --git a/lib/oshelpers/windows.py b/lib/oshelpers/windows.py new file mode 100644 index 0000000..765107c --- /dev/null +++ b/lib/oshelpers/windows.py @@ -0,0 +1,31 @@ +import ctypes +import sys +from elevate import elevate + +def is_admin(): + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + return False + +def run_as_admin_old(argv=None): + shell32 = ctypes.windll.shell32 + if argv is None and shell32.IsUserAnAdmin(): + return True + + if argv is None: + argv = sys.argv + if hasattr(sys, '_MEIPASS'): + # Support pyinstaller wrapped program. + arguments = argv[1:] + else: + arguments = argv + argument_line = u' '.join(arguments) + executable = sys.executable + ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1) + if int(ret) <= 32: + return False + return None + +def run_as_admin(): + elevate() diff --git a/pyvncs/__pycache__/server.cpython-36.pyc b/pyvncs/__pycache__/server.cpython-36.pyc deleted file mode 100644 index a87805a19fc19b9cdcb77213270f390e2b2407b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15507 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 SJvjyaBWLCYL_^4Btp5#KC}8LS diff --git a/pyvncs/server.py b/pyvncs/server.py index 8c5c215..7de6298 100644 --- a/pyvncs/server.py +++ b/pyvncs/server.py @@ -19,17 +19,24 @@ from struct import * from pyDes import * from time import sleep from pynput import mouse, keyboard +from PIL import Image, ImageChops, ImageDraw, ImagePalette import socket import select import os import sys import random -import zlib import numpy as np -from lib.encodings import * +from lib import mousectrl +from lib import kbdctrl +from lib.imagegrab import ImageGrab from lib import log +from lib import bgr233_palette + +# encodings support +import lib.encodings as encs +from lib.encodings.common import ENCODINGS def hexdump(data): str = "" @@ -39,7 +46,7 @@ def hexdump(data): return str def quantizetopalette(silf, palette, dither=False): - """Convert an RGB or L mode image to use a given P image's palette.""" + """Converts an RGB or L mode image to use a given P image's palette.""" silf.load() @@ -60,50 +67,13 @@ def quantizetopalette(silf, palette, dither=False): except AttributeError: return silf._makeself(im) - -from PIL import Image, ImageChops, ImageDraw, ImagePalette -if sys.platform == "linux" or sys.platform == "linux2": - from Xlib import display, X - # take screen images, that's not the best way, so here - # we use directly use xlib to take the screenshot. - class ImageGrab(): - def grab(): - dsp = display.Display() - root = dsp.screen().root - geom = root.get_geometry() - w = geom.width - h = geom.height - raw = root.get_image(0, 0, w ,h, X.ZPixmap, 0xffffffff) - image = Image.frombytes("RGB", (w, h), raw.data, "raw", "BGRX") - return image - -elif sys.platform == "darwin": - import Quartz.CoreGraphics as CG - class ImageGrab(): - def grab(): - screenshot = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault) - width = CG.CGImageGetWidth(screenshot) - height = CG.CGImageGetHeight(screenshot) - bytesperrow = CG.CGImageGetBytesPerRow(screenshot) - - pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(screenshot)) - - i = Image.frombytes("RGBA", (width, height), pixeldata) - (b, g, r, x) = i.split() - i = Image.merge("RGBX", (r, g, b, x)) - - return i - -else: - from PIL import ImageGrab - - - class VncServer(): class CONFIG: _8bitdither = False + encoding_object = None + def __init__(self, socket, password): self.RFB_VERSION = '003.008' self.RFB_SECTYPES = [ @@ -115,73 +85,8 @@ class VncServer(): self.framebuffer = None self.password = password self.sectypes = self.RFB_SECTYPES - self.remotecursor = False - - self.BGR233 = [0, 0, 0, 0, 0, 85, 0, 0, 170, 0, 0, 255, 36, 0, 0, - 36, 0, 85, 36, 0, 170, 36, 0, 255, 73, 0, 0, 73, 0, - 85, 73, 0, 170, 73, 0, 255, 109, 0, 0, 109, 0, 85, - 109, 0, 170, 109, 0, 255, 146, 0, 0, 146, 0, 85, 146, - 0, 170, 146, 0, 255, 182, 0, 0, 182, 0, 85, 182, 0, - 170, 182, 0, 255, 219, 0, 0, 219, 0, 85, 219, 0, 170, - 219, 0, 255, 255, 0, 0, 255, 0, 85, 255, 0, 170, 255, - 0, 255, 0, 36, 0, 0, 36, 85, 0, 36, 170, 0, 36, 255, - 36, 36, 0, 36, 36, 85, 36, 36, 170, 36, 36, 255, 73, - 36, 0, 73, 36, 85, 73, 36, 170, 73, 36, 255, 109, 36, - 0, 109, 36, 85, 109, 36, 170, 109, 36, 255, 146, 36, - 0, 146, 36, 85, 146, 36, 170, 146, 36, 255, 182, 36, - 0, 182, 36, 85, 182, 36, 170, 182, 36, 255, 219, 36, - 0, 219, 36, 85, 219, 36, 170, 219, 36, 255, 255, 36, - 0, 255, 36, 85, 255, 36, 170, 255, 36, 255, 0, 73, 0, - 0, 73, 85, 0, 73, 170, 0, 73, 255, 36, 73, 0, 36, 73, - 85, 36, 73, 170, 36, 73, 255, 73, 73, 0, 73, 73, 85, - 73, 73, 170, 73, 73, 255, 109, 73, 0, 109, 73, 85, - 109, 73, 170, 109, 73, 255, 146, 73, 0, 146, 73, 85, - 146, 73, 170, 146, 73, 255, 182, 73, 0, 182, 73, 85, - 182, 73, 170, 182, 73, 255, 219, 73, 0, 219, 73, 85, - 219, 73, 170, 219, 73, 255, 255, 73, 0, 255, 73, 85, - 255, 73, 170, 255, 73, 255, 0, 109, 0, 0, 109, 85, 0, - 109, 170, 0, 109, 255, 36, 109, 0, 36, 109, 85, 36, - 109, 170, 36, 109, 255, 73, 109, 0, 73, 109, 85, 73, - 109, 170, 73, 109, 255, 109, 109, 0, 109, 109, 85, 109, - 109, 170, 109, 109, 255, 146, 109, 0, 146, 109, 85, - 146, 109, 170, 146, 109, 255, 182, 109, 0, 182, 109, - 85, 182, 109, 170, 182, 109, 255, 219, 109, 0, 219, - 109, 85, 219, 109, 170, 219, 109, 255, 255, 109, 0, - 255, 109, 85, 255, 109, 170, 255, 109, 255, 0, 146, 0, - 0, 146, 85, 0, 146, 170, 0, 146, 255, 36, 146, 0, 36, - 146, 85, 36, 146, 170, 36, 146, 255, 73, 146, 0, 73, - 146, 85, 73, 146, 170, 73, 146, 255, 109, 146, 0, 109, - 146, 85, 109, 146, 170, 109, 146, 255, 146, 146, 0, 146, - 146, 85, 146, 146, 170, 146, 146, 255, 182, 146, 0, 182, - 146, 85, 182, 146, 170, 182, 146, 255, 219, 146, 0, 219, - 146, 85, 219, 146, 170, 219, 146, 255, 255, 146, 0, 255, - 146, 85, 255, 146, 170, 255, 146, 255, 0, 182, 0, 0, - 182, 85, 0, 182, 170, 0, 182, 255, 36, 182, 0, 36, 182, - 85, 36, 182, 170, 36, 182, 255, 73, 182, 0, 73, 182, - 85, 73, 182, 170, 73, 182, 255, 109, 182, 0, 109, 182, - 85, 109, 182, 170, 109, 182, 255, 146, 182, 0, 146, - 182, 85, 146, 182, 170, 146, 182, 255, 182, 182, 0, 182, - 182, 85, 182, 182, 170, 182, 182, 255, 219, 182, 0, 219, - 182, 85, 219, 182, 170, 219, 182, 255, 255, 182, 0, 255, - 182, 85, 255, 182, 170, 255, 182, 255, 0, 219, 0, 20, - 219, 85, 0, 219, 170, 0, 219, 255, 36, 219, 0, 36, 219, - 85, 36, 219, 170, 36, 219, 255, 73, 219, 0, 73, 219, - 85, 73, 219, 170, 73, 219, 255, 109, 219, 0, 109, 219, - 85, 109, 219, 170, 109, 219, 255, 146, 219, 0, 146, 219, - 85, 146, 219, 170, 146, 219, 255, 182, 219, 0, 182, 219, - 85, 182, 219, 170, 182, 219, 255, 219, 219, 0, 219, 219, - 85, 219, 219, 170, 219, 219, 255, 255, 219, 0, 255, 219, - 85, 255, 219, 170, 255, 219, 255, 0, 255, 0, 0, 255, 85, - 0, 255, 170, 0, 255, 255, 36, 255, 0, 36, 255, 85, 36, - 255, 170, 36, 255, 255, 73, 255, 0, 73, 255, 85, 73, - 255, 170, 73, 255, 255, 109, 255, 0, 109, 255, 85, 109, - 255, 170, 109, 255, 255, 146, 255, 0, 146, 255, 85, - 146, 255, 170, 146, 255, 255, 182, 255, 0, 182, 255, 85, - 182, 255, 170, 182, 255, 255, 219, 255, 0, 219, 255, 85, - 219, 255, 170, 219, 255, 255, 255, 255, 0, 255, 255, 85, - 255, 255, 170, 255, 255, 255] - - + self.cursor_support = False + def __del__(self): log.debug("VncServer died") @@ -348,95 +253,19 @@ class VncServer(): def protocol(self): self.socket.settimeout(None) # set nonblocking socket - screen = ImageGrab.grab() size = screen.size width = size[0] height = size[1] del screen + + mousecontroller = mousectrl.MouseController() + kbdcontroller = kbdctrl.KeyboardController() self.primaryOrder = "rgb" - self.encoding = enc.ENCODINGS.raw - buttonmask = 0 - buttons = [0, 0, 0, 0, 0, 0, 0, 0] - left_pressed = 0 - right_pressed = 0 - middle_pressed = 0 + self.encoding = ENCODINGS.raw + self.encoding_object = encs.common.encodings[self.encoding]() - - kbdmap = { - 0xff08: keyboard.Key.backspace, - 0xff09: keyboard.Key.tab, - 0xff0d: keyboard.Key.enter, - 0xff1b: keyboard.Key.esc, - 0xff63: keyboard.Key.insert if hasattr(keyboard.Key, "insert") else None, - 0xffff: keyboard.Key.delete, - 0xff50: keyboard.Key.home, - 0xff57: keyboard.Key.end, - 0xff55: keyboard.Key.page_up, - 0xff56: keyboard.Key.page_down, - 0xff51: keyboard.Key.left, - 0xff52: keyboard.Key.up, - 0xff53: keyboard.Key.right, - 0xff54: keyboard.Key.down, - 0xffbe: keyboard.Key.f1, - 0xffbf: keyboard.Key.f2, - 0xffc0: keyboard.Key.f3, - 0xffc1: keyboard.Key.f4, - 0xffc2: keyboard.Key.f5, - 0xffc3: keyboard.Key.f6, - 0xffc4: keyboard.Key.f7, - 0xffc5: keyboard.Key.f8, - 0xffc6: keyboard.Key.f9, - 0xffc7: keyboard.Key.f10, - 0xffc8: keyboard.Key.f11, - 0xffc9: keyboard.Key.f12, - 0xffca: keyboard.Key.f13, - 0xffcb: keyboard.Key.f14, - 0xffcc: keyboard.Key.f15, - 0xffcd: keyboard.Key.f16, - 0xffce: keyboard.Key.f17, - 0xffcf: keyboard.Key.f18, - 0xffd0: keyboard.Key.f19, - 0xffd1: keyboard.Key.f20, - 0xffe1: keyboard.Key.shift_l, - 0xffe2: keyboard.Key.shift_r, - 0xffe3: keyboard.Key.ctrl_l, - 0xffe4: keyboard.Key.ctrl_r, - 0xffe7: None, # "KEY_MetaLeft" - 0xffe8: None, # "KEY_MetaRight" - 0xffe9: keyboard.Key.cmd_l, - 0xffea: keyboard.Key.alt_gr, # "KEY_AltRight" - 0xff14: keyboard.Key.scroll_lock if hasattr(keyboard.Key, "scroll_lock") else None, - 0xff15: keyboard.Key.print_screen if hasattr(keyboard.Key, "print_screen") else None, # "KEY_Sys_Req" - 0xff7f: keyboard.Key.num_lock if hasattr(keyboard.Key, "num_lock") else None, - 0xffe5: keyboard.Key.caps_lock, - 0xff13: keyboard.Key.pause if hasattr(keyboard.Key, "pause") else None, - 0xffeb: keyboard.Key.cmd_r, # "KEY_Super_L" - 0xffec: keyboard.Key.cmd_r, # "KEY_Super_R" - 0xffed: None, # "KEY_Hyper_L" - 0xffee: None, # "KEY_Hyper_R" - 0xffb0: None, # "KEY_KP_0" - 0xffb1: None, # "KEY_KP_1" - 0xffb2: None, # "KEY_KP_2" - 0xffb3: None, # "KEY_KP_3" - 0xffb4: None, # "KEY_KP_4" - 0xffb5: None, # "KEY_KP_5" - 0xffb6: None, # "KEY_KP_6" - 0xffb7: None, # "KEY_KP_7" - 0xffb8: None, # "KEY_KP_8" - 0xffb9: None, # "KEY_KP_9" - 0xff8d: None, # "KEY_KP_Enter" - 0x002f: "/", # KEY_ForwardSlash - 0x005c: "\\", # KEY_BackSlash - 0x0020: keyboard.Key.space, # "KEY_SpaceBar" - 0xff7e: keyboard.Key.alt_gr, # altgr, at least on a mac (?) - 0xfe03: keyboard.Key.alt_l, - } - - if sys.platform == "darwin": - kbdmap[0xffe2] = keyboard.Key.shift - while True: #log.debug(".", end='', flush=True) r,_,_ = select.select([self.socket],[],[],0) @@ -476,7 +305,7 @@ class VncServer(): self.primaryOrder = "rgb" else: self.primaryOrder = "bgr" - log.debug("Using order: ", self.primaryOrder) + log.debug("Using order:", self.primaryOrder) continue @@ -488,16 +317,25 @@ class VncServer(): data2 = sock.recv(4 * nencodings, socket.MSG_WAITALL) #log.debug("len", len(data2)) self.client_encodings = unpack("!%si" % nencodings, data2) - #log.debug("data", repr(self.client_encodings), len(self.client_encodings)) + log.debug("client_encodings", repr(self.client_encodings), len(self.client_encodings)) - if hasattr(enc.ENCODINGS, "cursor") and enc.ENCODINGS.cursor in self.client_encodings: - 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: - log.debug("Using zlib encoding") - self.encoding = enc.ENCODINGS.zlib + # cursor support? + if ENCODINGS.cursor in self.client_encodings: + log.debug("client cursor support") + self.cursor_support = True + + # which pixel encoding to use? + log.debug("encs.common.encodings_priority", encs.common.encodings_priority) + for e in encs.common.encodings_priority: + log.debug("E", e) + if e in self.client_encodings: + if self.encoding == e: + # don't initialize same encoding again + break + self.encoding = e + log.debug("Using %s encoding" % self.encoding) + self.encoding_object = encs.common.encodings[self.encoding]() + break continue @@ -510,89 +348,14 @@ class VncServer(): #log.debug("RFBU:", incremental, x, y, w, h) self.SendRectangles(sock, x, y, w, h, incremental) - if self.remotecursor and self.cursorchanged: - # FIXME: send cursor to remote client - self.cursorchanged = False continue - if data[0] == 4: - kbdkey = '' - data2 = sock.recv(7) - # B = U8, L = U32 - (downflag, key) = unpack("!BxxL", data2) - log.debug("KeyEvent", downflag, hex(key)) - - # special key - if key in kbdmap: - kbdkey = kbdmap[key] - else: # normal key - try: - kbdkey = keyboard.KeyCode.from_char(chr(key)) - except: - kbdkey = None - - try: - log.debug("KEY:", kbdkey) - except: - log.debug("KEY: (unprintable)") - - try: - if downflag: - keyboard.Controller().press(kbdkey) - else: - keyboard.Controller().release(kbdkey) - except: - log.debug("Error sending key") - + if data[0] == 4: # keyboard event + kbdcontroller.process_event(sock.recv(7)) continue if data[0] == 5: # PointerEvent - data2 = sock.recv(5, socket.MSG_WAITALL) - (buttonmask, x, y) = unpack("!BHH", data2) - buttons[0] = buttonmask & int("0000001", 2) # left button - buttons[1] = buttonmask & int("0000010", 2) # middle button - buttons[2] = buttonmask & int("0000100", 2) # right button - buttons[3] = buttonmask & int("0001000", 2) # scroll up - buttons[4] = buttonmask & int("0010000", 2) # scroll down - - mouse.Controller().position = (x, y) - - if buttons[0] and not left_pressed: - log.debug("LEFT PRESSED") - mouse.Controller().press(mouse.Button.left) - left_pressed = 1 - elif not buttons[0] and left_pressed: - log.debug("LEFT RELEASED") - mouse.Controller().release(mouse.Button.left) - left_pressed = 0 - - if buttons[1] and not middle_pressed: - log.debug("MIDDLE PRESSED") - mouse.Controller().press(mouse.Button.middle) - middle_pressed = 1 - elif not buttons[1] and middle_pressed: - log.debug("MIDDLE RELEASED") - mouse.Controller().release(mouse.Button.middle) - middle_pressed = 0 - - if buttons[2] and not right_pressed: - log.debug("RIGHT PRESSED") - mouse.Controller().press(mouse.Button.right) - right_pressed = 1 - elif not buttons[2] and right_pressed: - log.debug("RIGHT RELEASED") - mouse.Controller().release(mouse.Button.right) - right_pressed = 0 - - if buttons[3]: - log.debug("SCROLLUP PRESSED") - mouse.Controller().scroll(0, 2) - - if buttons[4]: - log.debug("SCROLLDOWN PRESSED") - mouse.Controller().scroll(0, -2) - - #log.debug("PointerEvent", buttonmask, x, y) + mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL)) continue else: @@ -630,19 +393,20 @@ class VncServer(): lastshot = rectangle sendbuff = bytearray() - firstUpdateSent = False + self.encoding_object.firstUpdateSent = False # try to send only the actual changes if self.framebuffer != None and incremental == 1: diff = ImageChops.difference(rectangle, self.framebuffer) if diff.getbbox() is None: + # no changes... rectangles = 0 sendbuff.extend(pack("!BxH", 0, rectangles)) try: sock.sendall(sendbuff) except: return False - sleep(0.3) + sleep(0.1) return if diff.getbbox() is not None: @@ -656,119 +420,70 @@ class VncServer(): stimeout = sock.gettimeout() sock.settimeout(None) - if self.bpp == 32 or self.bpp == 8: - if rectangle.mode is not "RGB": - image = rectangle.convert("RGB") - else: - image = rectangle - - b = np.asarray(image) - a = b.copy() - del b + if self.bpp == 32 or self.bpp == 16 or self.bpp == 8: if self.bpp == 32: redBits = 8 greenBits = 8 blueBits = 8 - elif self.bpp == 8: - redBits = 4 - greenBits = 4 - blueBits = 4 - #redMask = ((1 << redBits) - 1) << self.red_shift - #greenMask = ((1 << greenBits) - 1) << self.green_shift - #blueMask = ((1 << blueBits) - 1) << self.blue_shift - #log.debug("redMask", redMask, greenMask, blueMask) + # image array + a = np.asarray(rectangle).copy() + + if self.primaryOrder == "bgr": # bit shifting + blueMask = (1 << blueBits) - 1 + greenMask = ((1 << greenBits) - 1) << self.green_shift + redMask = ((1 << redBits) - 1) << self.red_shift - if self.primaryOrder == "bgr": - self.blue_shift = 0 - blueMask = (1 << blueBits) - 1 - self.green_shift = blueBits - greenMask = ((1 << greenBits) - 1) << self.green_shift - self.red_shift = self.green_shift + greenBits - redMask = ((1 << redBits) - 1) << self.red_shift + a[..., 0] = ( a[..., 0] ) & blueMask >> self.blue_shift + a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift + a[..., 2] = ( a[..., 2] ) & redMask >> self.red_shift + + else: # RGB + redMask = ((1 << redBits) - 1) << self.red_shift + greenMask = ((1 << greenBits) - 1) << self.green_shift + blueMask = ((1 << blueBits) - 1) << self.blue_shift + a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift + a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift + a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift - else: # RGB - self.red_shift = 0 - redMask = (1 << redBits) - 1 - self.green_shift = redBits - greenMask = ((1 << greenBits) - 1) << self.green_shift - self.blue_shift = self.green_shift + greenBits - blueMask = ((1 << blueBits) - 1) << self.blue_shift - - a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift - a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift - a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift - - image = Image.fromarray(a) - del a - - if self.primaryOrder == "rgb": - (b, g, r) = image.split() - image = Image.merge("RGB", (r, g, b)) - del b,g,r - - if self.bpp == 32: + image = Image.fromarray(a) + if self.primaryOrder == "rgb": + (b, g, r) = image.split() + image = Image.merge("RGB", (r, g, b)) + del b,g,r image = image.convert("RGBX") - elif self.bpp == 8: - #FIXME: improve 8 bit routines up!, image looks awful! - if rectangle.mode is not "RGB": - image = rectangle.convert("RGB") - else: - image = rectangle + if self.bpp == 16: #BGR565 + greenBits = 5 + blueBits = 6 + redBits = 5 - (r, g, b) = image.split() - image = Image.merge("RGB", (g, r, b)) - del b,g,r + image = rectangle + if self.depth == 16: + image = image.convert('BGR;16') + if self.depth == 15: + image = image.convert('BGR;15') + + elif self.bpp == 8: #bgr233 + redBits = 3 + greenBits = 3 + blueBits = 2 + + image = rectangle + + p = Image.new('P',(16,16)) + p.putpalette(bgr233_palette.palette) - p = Image.new("P",(16,16)) - p.putpalette(self.BGR233) image = quantizetopalette(image, p, dither=self.CONFIG._8bitdither) - image.putpalette(self.BGR233) - del p - rectangles = 1 - sendbuff.extend(pack("!BxH", 0, rectangles)) - sendbuff.extend(pack("!HHHH", x, y, w, h)) - sendbuff.extend(pack(">i", self.encoding)) - sendbuff.extend( image.tobytes() ) + + #image = image.convert('RGB', colors=4).quantize(palette=p) + #log.debug(image) - if hasattr(enc.ENCODINGS, "zlib") and self.encoding == enc.ENCODINGS.zlib - 9998: - if not firstUpdateSent: - firstUpdateSent = True - compress = zlib.compressobj( - zlib.Z_DEFAULT_COMPRESSION, # level: 0-9 - zlib.DEFLATED, # method: must be DEFLATED - -15, # window size in bits: - # -15..-8: negate, suppress header - # 8..15: normal - # 16..30: subtract 16, gzip header - 1, # mem level: 1..8/9 - zlib.Z_DEFAULT_STRATEGY # strategy: - # 0 = Z_DEFAULT_STRATEGY - # 1 = Z_FILTERED - # 2 = Z_HUFFMAN_ONLY - # 3 = Z_RLE - # 4 = Z_FIXED - ) - rectangles = 1 - sendbuff.extend(pack("!BxH", 0, rectangles)) - sendbuff.extend(pack("!HHHH", x, y, w, h)) - sendbuff.extend(pack(">i", self.encoding)) - - log.debug("Compressing...") - zlibdata = compress.compress( image.tobytes() ) - zlibdata += compress.flush() - l = pack("!I", len(zlibdata) ) - - sendbuff.extend( l ) # send length - sendbuff.extend( zlibdata ) # send compressed data - - else: - # send with RAW encoding - sendbuff.extend(enc.ENCODINGS.raw_send_image(x, y, w, h, image)) + # send image with client defined encoding + sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image)) else: log.debug("[!] Unsupported BPP: %s" % self.bpp) diff --git a/requeriments.txt b/requeriments.txt index dbfde75..38bbd7e 100644 --- a/requeriments.txt +++ b/requeriments.txt @@ -1,4 +1,5 @@ pydes pynput numpy - +Pillow-PIL +elevate diff --git a/server.py b/server.py index 02a9f95..6101922 100755 --- a/server.py +++ b/server.py @@ -10,8 +10,8 @@ import socket import signal from lib import log -#_debug = log.debug -_debug = print +_debug = log.debug +#_debug = print def signal_handler(signal, frame): _debug("Exiting on %s signal..." % signal) @@ -95,6 +95,19 @@ def main(argv): _debug("Multithreaded Python server : Waiting for connections from TCP clients...") _debug("Runing on:", sys.platform) + if sys.platform in ['win32', 'win64']: + from lib.oshelpers import windows as win32 + if not win32.is_admin(): + ret = win32.run_as_admin() + if ret is None: + log.debug("Respawning with admin rights") + sys.exit(0) + elif ret is True: + # admin rights + log.debug("Running with admin rights!") + else: + print('Error(ret=%d): cannot elevate privilege.' % (ret)) + sys.exit(1) while True: sockServer.listen(4) (conn, (ip,port)) = sockServer.accept()