Merge branch 'experimental' into 'master'
Experimental See merge request radixcl/pyvncs!1
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
327
ctrlsrv.py
Executable file
327
ctrlsrv.py
Executable file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
260
lib/bgr233_palette.py
Normal file
260
lib/bgr233_palette.py
Normal file
@@ -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
|
||||||
|
]
|
||||||
68
lib/common.py
Normal file
68
lib/common.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
29
lib/const.py
Normal file
29
lib/const.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
@@ -16,5 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# at least, raw encoding is needed by the rfb protocol
|
# at least, raw encoding is needed by the rfb protocol
|
||||||
from . import common as enc
|
from . import common
|
||||||
from . import raw
|
from . import raw
|
||||||
|
from . import zlib
|
||||||
|
from . import cursor
|
||||||
|
|||||||
29
lib/encodings/common.py
Normal file
29
lib/encodings/common.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
encodings = {}
|
||||||
|
|
||||||
|
class ENCODINGS:
|
||||||
|
raw = 0
|
||||||
|
zlib = 6
|
||||||
|
# supported pseudo-encodings
|
||||||
|
cursor = -239
|
||||||
|
|
||||||
|
encodings_priority = [
|
||||||
|
ENCODINGS.zlib,
|
||||||
|
ENCODINGS.raw
|
||||||
|
]
|
||||||
36
lib/encodings/cursor.py
Normal file
36
lib/encodings/cursor.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
from struct import *
|
||||||
|
from lib import log
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
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))
|
||||||
46
lib/encodings/raw.py
Normal file
46
lib/encodings/raw.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
from struct import *
|
||||||
|
from lib import log
|
||||||
|
|
||||||
|
class Encoding:
|
||||||
|
_buff = None
|
||||||
|
|
||||||
|
name = 'raw'
|
||||||
|
id = 0
|
||||||
|
description = 'Raw VNC encoding'
|
||||||
|
enabled = True
|
||||||
|
firstUpdateSent = False
|
||||||
|
|
||||||
|
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))
|
||||||
54
lib/encodings/zlib.py
Normal file
54
lib/encodings/zlib.py
Normal file
@@ -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))
|
||||||
41
lib/imagegrab.py
Normal file
41
lib/imagegrab.py
Normal file
@@ -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
|
||||||
47
lib/kbdctrl.py
Normal file
47
lib/kbdctrl.py
Normal file
@@ -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")
|
||||||
79
lib/kbdmap.py
Normal file
79
lib/kbdmap.py
Normal file
@@ -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
|
||||||
28
lib/log.py
Normal file
28
lib/log.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
60
lib/mousectrl.py
Normal file
60
lib/mousectrl.py
Normal file
@@ -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)
|
||||||
31
lib/oshelpers/windows.py
Normal file
31
lib/oshelpers/windows.py
Normal file
@@ -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()
|
||||||
Binary file not shown.
532
pyvncs/server.py
532
pyvncs/server.py
@@ -19,16 +19,24 @@ from struct import *
|
|||||||
from pyDes import *
|
from pyDes import *
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from pynput import mouse, keyboard
|
from pynput import mouse, keyboard
|
||||||
|
from PIL import Image, ImageChops, ImageDraw, ImagePalette
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import select
|
import select
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import random
|
import random
|
||||||
import zlib
|
|
||||||
import numpy as np
|
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):
|
def hexdump(data):
|
||||||
str = ""
|
str = ""
|
||||||
@@ -38,7 +46,7 @@ def hexdump(data):
|
|||||||
return str
|
return str
|
||||||
|
|
||||||
def quantizetopalette(silf, palette, dither=False):
|
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()
|
silf.load()
|
||||||
|
|
||||||
@@ -59,50 +67,13 @@ def quantizetopalette(silf, palette, dither=False):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return silf._makeself(im)
|
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 VncServer():
|
||||||
|
|
||||||
class CONFIG:
|
class CONFIG:
|
||||||
_8bitdither = False
|
_8bitdither = False
|
||||||
|
|
||||||
|
encoding_object = None
|
||||||
|
|
||||||
def __init__(self, socket, password):
|
def __init__(self, socket, password):
|
||||||
self.RFB_VERSION = '003.008'
|
self.RFB_VERSION = '003.008'
|
||||||
self.RFB_SECTYPES = [
|
self.RFB_SECTYPES = [
|
||||||
@@ -114,75 +85,10 @@ class VncServer():
|
|||||||
self.framebuffer = None
|
self.framebuffer = None
|
||||||
self.password = password
|
self.password = password
|
||||||
self.sectypes = self.RFB_SECTYPES
|
self.sectypes = self.RFB_SECTYPES
|
||||||
self.remotecursor = False
|
self.cursor_support = 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]
|
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
print("VncServer died")
|
log.debug("VncServer died")
|
||||||
|
|
||||||
def encrypt(self, key, data):
|
def encrypt(self, key, data):
|
||||||
k = des(key, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
|
k = des(key, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
|
||||||
@@ -221,7 +127,7 @@ class VncServer():
|
|||||||
data = sock.recv(1024)
|
data = sock.recv(1024)
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
data = None
|
data = None
|
||||||
print("getbuff() timeout")
|
log.debug("getbuff() timeout")
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -232,15 +138,15 @@ class VncServer():
|
|||||||
# RFB version handshake
|
# RFB version handshake
|
||||||
data = self.getbuff(30)
|
data = self.getbuff(30)
|
||||||
|
|
||||||
print("init received: '%s'" % data)
|
log.debug("init received: '%s'" % data)
|
||||||
server_version = float(self.RFB_VERSION)
|
server_version = float(self.RFB_VERSION)
|
||||||
try:
|
try:
|
||||||
client_version = float(data[4:11])
|
client_version = float(data[4:11])
|
||||||
except:
|
except:
|
||||||
print("Error parsing client version")
|
log.debug("Error parsing client version")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("client, server:", client_version, server_version)
|
log.debug("client, server:", client_version, server_version)
|
||||||
|
|
||||||
# security types handshake
|
# security types handshake
|
||||||
sendbuff = pack("B", len(self.sectypes)) # number of security types
|
sendbuff = pack("B", len(self.sectypes)) # number of security types
|
||||||
@@ -254,13 +160,13 @@ class VncServer():
|
|||||||
sectype = None
|
sectype = None
|
||||||
|
|
||||||
if sectype not in self.sectypes:
|
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
|
sock.send(pack("B", 1)) # failed handshake
|
||||||
self.sendmessage("Incompatible security type")
|
self.sendmessage("Incompatible security type")
|
||||||
sock.close()
|
sock.close()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("sec type data: %s" % data)
|
log.debug("sec type data: %s" % data)
|
||||||
|
|
||||||
# VNC Auth
|
# VNC Auth
|
||||||
if sectype == 2:
|
if sectype == 2:
|
||||||
@@ -278,9 +184,9 @@ class VncServer():
|
|||||||
if data == crypted:
|
if data == crypted:
|
||||||
# Handshake successful
|
# Handshake successful
|
||||||
sock.send(pack("I", 0))
|
sock.send(pack("I", 0))
|
||||||
print("Auth OK")
|
log.debug("Auth OK")
|
||||||
else:
|
else:
|
||||||
print("Invalid auth")
|
log.debug("Invalid auth")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#unsupported VNC auth type
|
#unsupported VNC auth type
|
||||||
@@ -289,7 +195,7 @@ class VncServer():
|
|||||||
|
|
||||||
# get ClientInit
|
# get ClientInit
|
||||||
data = self.getbuff(30)
|
data = self.getbuff(30)
|
||||||
print("Clientinit (shared flag)", repr(data))
|
log.debug("Clientinit (shared flag)", repr(data))
|
||||||
|
|
||||||
self.ServerInit()
|
self.ServerInit()
|
||||||
|
|
||||||
@@ -300,9 +206,9 @@ class VncServer():
|
|||||||
|
|
||||||
sock = self.socket
|
sock = self.socket
|
||||||
screen = ImageGrab.grab()
|
screen = ImageGrab.grab()
|
||||||
print("screen", repr(screen))
|
log.debug("screen", repr(screen))
|
||||||
size = screen.size
|
size = screen.size
|
||||||
print("size", repr(size))
|
log.debug("size", repr(size))
|
||||||
del screen
|
del screen
|
||||||
|
|
||||||
width = size[0]
|
width = size[0]
|
||||||
@@ -339,105 +245,29 @@ class VncServer():
|
|||||||
sendbuff += pack("!I", desktop_name_len)
|
sendbuff += pack("!I", desktop_name_len)
|
||||||
sendbuff += desktop_name.encode()
|
sendbuff += desktop_name.encode()
|
||||||
|
|
||||||
print("width", repr(width))
|
log.debug("width", repr(width))
|
||||||
print("height", repr(height))
|
log.debug("height", repr(height))
|
||||||
|
|
||||||
sock.send(sendbuff)
|
sock.send(sendbuff)
|
||||||
|
|
||||||
|
|
||||||
def protocol(self):
|
def protocol(self):
|
||||||
self.socket.settimeout(None) # set nonblocking socket
|
self.socket.settimeout(None) # set nonblocking socket
|
||||||
|
|
||||||
screen = ImageGrab.grab()
|
screen = ImageGrab.grab()
|
||||||
size = screen.size
|
size = screen.size
|
||||||
width = size[0]
|
width = size[0]
|
||||||
height = size[1]
|
height = size[1]
|
||||||
del screen
|
del screen
|
||||||
|
|
||||||
|
mousecontroller = mousectrl.MouseController()
|
||||||
|
kbdcontroller = kbdctrl.KeyboardController()
|
||||||
|
|
||||||
self.primaryOrder = "rgb"
|
self.primaryOrder = "rgb"
|
||||||
self.encoding = enc.ENCODINGS.raw
|
self.encoding = ENCODINGS.raw
|
||||||
buttonmask = 0
|
self.encoding_object = encs.common.encodings[self.encoding]()
|
||||||
buttons = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
left_pressed = 0
|
|
||||||
right_pressed = 0
|
|
||||||
middle_pressed = 0
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
while True:
|
||||||
#print(".", end='', flush=True)
|
#log.debug(".", end='', flush=True)
|
||||||
r,_,_ = select.select([self.socket],[],[],0)
|
r,_,_ = select.select([self.socket],[],[],0)
|
||||||
if r == []:
|
if r == []:
|
||||||
#no data
|
#no data
|
||||||
@@ -448,10 +278,10 @@ class VncServer():
|
|||||||
try:
|
try:
|
||||||
data = sock.recv(1) # read first byte
|
data = sock.recv(1) # read first byte
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
#print("timeout")
|
#log.debug("timeout")
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("exception '%s'" % e)
|
log.debug("exception '%s'" % e)
|
||||||
sock.close()
|
sock.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -462,141 +292,75 @@ class VncServer():
|
|||||||
|
|
||||||
if data[0] == 0: # client SetPixelFormat
|
if data[0] == 0: # client SetPixelFormat
|
||||||
data2 = sock.recv(19, socket.MSG_WAITALL)
|
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.bpp, self.depth, self.bigendian, self.truecolor, self.red_maximum,
|
||||||
self.green_maximum, self.blue_maximum,
|
self.green_maximum, self.blue_maximum,
|
||||||
self.red_shift, self.green_shift, self.blue_shift
|
self.red_shift, self.green_shift, self.blue_shift
|
||||||
) = unpack("!xxxBBBBHHHBBBxxx", data2)
|
) = unpack("!xxxBBBBHHHBBBxxx", data2)
|
||||||
print("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
|
log.debug("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
|
||||||
print("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
|
log.debug("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
|
||||||
print("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
|
log.debug("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
|
||||||
|
|
||||||
if self.red_shift > self.blue_shift:
|
if self.red_shift > self.blue_shift:
|
||||||
self.primaryOrder = "rgb"
|
self.primaryOrder = "rgb"
|
||||||
else:
|
else:
|
||||||
self.primaryOrder = "bgr"
|
self.primaryOrder = "bgr"
|
||||||
print("Using order: ", self.primaryOrder)
|
log.debug("Using order:", self.primaryOrder)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data[0] == 2: # SetEncoding
|
if data[0] == 2: # SetEncoding
|
||||||
data2 = sock.recv(3)
|
data2 = sock.recv(3)
|
||||||
print("Client Message Type: SetEncoding (2)")
|
log.debug("Client Message Type: SetEncoding (2)")
|
||||||
(nencodings,) = unpack("!xH", data2)
|
(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)
|
data2 = sock.recv(4 * nencodings, socket.MSG_WAITALL)
|
||||||
#print("len", len(data2))
|
#log.debug("len", len(data2))
|
||||||
self.client_encodings = unpack("!%si" % nencodings, data2)
|
self.client_encodings = unpack("!%si" % nencodings, data2)
|
||||||
#print("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:
|
# cursor support?
|
||||||
print("Remote cursor encoding present")
|
if ENCODINGS.cursor in self.client_encodings:
|
||||||
self.remotecursor = True
|
log.debug("client cursor support")
|
||||||
self.cursorchanged = True
|
self.cursor_support = True
|
||||||
|
|
||||||
if hasattr(enc.ENCODINGS, "zlib") and enc.ENCODINGS.zlib in self.client_encodings:
|
# which pixel encoding to use?
|
||||||
print("Using zlib encoding")
|
log.debug("encs.common.encodings_priority", encs.common.encodings_priority)
|
||||||
self.encoding = enc.ENCODINGS.zlib
|
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
|
continue
|
||||||
|
|
||||||
|
|
||||||
if data[0] == 3: # FBUpdateRequest
|
if data[0] == 3: # FBUpdateRequest
|
||||||
data2 = sock.recv(9, socket.MSG_WAITALL)
|
data2 = sock.recv(9, socket.MSG_WAITALL)
|
||||||
#print("Client Message Type: FBUpdateRequest (3)")
|
#log.debug("Client Message Type: FBUpdateRequest (3)")
|
||||||
#print(len(data2))
|
#print(len(data2))
|
||||||
(incremental, x, y, w, h) = unpack("!BHHHH", 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)
|
self.SendRectangles(sock, x, y, w, h, incremental)
|
||||||
|
|
||||||
if self.remotecursor and self.cursorchanged:
|
|
||||||
# FIXME: send cursor to remote client
|
|
||||||
self.cursorchanged = False
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data[0] == 4:
|
if data[0] == 4: # keyboard event
|
||||||
kbdkey = ''
|
kbdcontroller.process_event(sock.recv(7))
|
||||||
data2 = sock.recv(7)
|
|
||||||
# B = U8, L = U32
|
|
||||||
(downflag, key) = unpack("!BxxL", data2)
|
|
||||||
print("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:
|
|
||||||
print("KEY:", kbdkey)
|
|
||||||
except:
|
|
||||||
print("KEY: (unprintable)")
|
|
||||||
|
|
||||||
try:
|
|
||||||
if downflag:
|
|
||||||
keyboard.Controller().press(kbdkey)
|
|
||||||
else:
|
|
||||||
keyboard.Controller().release(kbdkey)
|
|
||||||
except:
|
|
||||||
print("Error sending key")
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data[0] == 5: # PointerEvent
|
if data[0] == 5: # PointerEvent
|
||||||
data2 = sock.recv(5, socket.MSG_WAITALL)
|
mousecontroller.process_event(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:
|
|
||||||
print("LEFT PRESSED")
|
|
||||||
mouse.Controller().press(mouse.Button.left)
|
|
||||||
left_pressed = 1
|
|
||||||
elif not buttons[0] and left_pressed:
|
|
||||||
print("LEFT RELEASED")
|
|
||||||
mouse.Controller().release(mouse.Button.left)
|
|
||||||
left_pressed = 0
|
|
||||||
|
|
||||||
if buttons[1] and not middle_pressed:
|
|
||||||
print("MIDDLE PRESSED")
|
|
||||||
mouse.Controller().press(mouse.Button.middle)
|
|
||||||
middle_pressed = 1
|
|
||||||
elif not buttons[1] and middle_pressed:
|
|
||||||
print("MIDDLE RELEASED")
|
|
||||||
mouse.Controller().release(mouse.Button.middle)
|
|
||||||
middle_pressed = 0
|
|
||||||
|
|
||||||
if buttons[2] and not right_pressed:
|
|
||||||
print("RIGHT PRESSED")
|
|
||||||
mouse.Controller().press(mouse.Button.right)
|
|
||||||
right_pressed = 1
|
|
||||||
elif not buttons[2] and right_pressed:
|
|
||||||
print("RIGHT RELEASED")
|
|
||||||
mouse.Controller().release(mouse.Button.right)
|
|
||||||
right_pressed = 0
|
|
||||||
|
|
||||||
if buttons[3]:
|
|
||||||
print("SCROLLUP PRESSED")
|
|
||||||
mouse.Controller().scroll(0, 2)
|
|
||||||
|
|
||||||
if buttons[4]:
|
|
||||||
print("SCROLLDOWN PRESSED")
|
|
||||||
mouse.Controller().scroll(0, -2)
|
|
||||||
|
|
||||||
#print("PointerEvent", buttonmask, x, y)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
data2 = sock.recv(4096)
|
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):
|
def GetRectangle(self, x, y, w, h):
|
||||||
@@ -621,7 +385,7 @@ class VncServer():
|
|||||||
def SendRectangles(self, sock, x, y, w, h, incremental=0):
|
def SendRectangles(self, sock, x, y, w, h, incremental=0):
|
||||||
# send FramebufferUpdate to client
|
# send FramebufferUpdate to client
|
||||||
|
|
||||||
#print("start SendRectangles")
|
#log.debug("start SendRectangles")
|
||||||
rectangle = self.GetRectangle(x, y, w, h)
|
rectangle = self.GetRectangle(x, y, w, h)
|
||||||
if not rectangle:
|
if not rectangle:
|
||||||
rectangle = Image.new("RGB", [w, h], (0,0,0))
|
rectangle = Image.new("RGB", [w, h], (0,0,0))
|
||||||
@@ -629,19 +393,20 @@ class VncServer():
|
|||||||
lastshot = rectangle
|
lastshot = rectangle
|
||||||
sendbuff = bytearray()
|
sendbuff = bytearray()
|
||||||
|
|
||||||
firstUpdateSent = False
|
self.encoding_object.firstUpdateSent = False
|
||||||
|
|
||||||
# try to send only the actual changes
|
# try to send only the actual changes
|
||||||
if self.framebuffer != None and incremental == 1:
|
if self.framebuffer != None and incremental == 1:
|
||||||
diff = ImageChops.difference(rectangle, self.framebuffer)
|
diff = ImageChops.difference(rectangle, self.framebuffer)
|
||||||
if diff.getbbox() is None:
|
if diff.getbbox() is None:
|
||||||
|
# no changes...
|
||||||
rectangles = 0
|
rectangles = 0
|
||||||
sendbuff.extend(pack("!BxH", 0, rectangles))
|
sendbuff.extend(pack("!BxH", 0, rectangles))
|
||||||
try:
|
try:
|
||||||
sock.sendall(sendbuff)
|
sock.sendall(sendbuff)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
sleep(0.3)
|
sleep(0.1)
|
||||||
return
|
return
|
||||||
|
|
||||||
if diff.getbbox() is not None:
|
if diff.getbbox() is not None:
|
||||||
@@ -650,126 +415,77 @@ class VncServer():
|
|||||||
(x, y, _, _) = diff.getbbox()
|
(x, y, _, _) = diff.getbbox()
|
||||||
w = rectangle.width
|
w = rectangle.width
|
||||||
h = rectangle.height
|
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()
|
stimeout = sock.gettimeout()
|
||||||
sock.settimeout(None)
|
sock.settimeout(None)
|
||||||
|
|
||||||
if self.bpp == 32 or self.bpp == 8:
|
if self.bpp == 32 or self.bpp == 16 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:
|
if self.bpp == 32:
|
||||||
redBits = 8
|
redBits = 8
|
||||||
greenBits = 8
|
greenBits = 8
|
||||||
blueBits = 8
|
blueBits = 8
|
||||||
elif self.bpp == 8:
|
|
||||||
redBits = 4
|
|
||||||
greenBits = 4
|
|
||||||
blueBits = 4
|
|
||||||
|
|
||||||
#redMask = ((1 << redBits) - 1) << self.red_shift
|
# image array
|
||||||
#greenMask = ((1 << greenBits) - 1) << self.green_shift
|
a = np.asarray(rectangle).copy()
|
||||||
#blueMask = ((1 << blueBits) - 1) << self.blue_shift
|
|
||||||
#print("redMask", redMask, greenMask, blueMask)
|
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":
|
a[..., 0] = ( a[..., 0] ) & blueMask >> self.blue_shift
|
||||||
self.blue_shift = 0
|
a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift
|
||||||
blueMask = (1 << blueBits) - 1
|
a[..., 2] = ( a[..., 2] ) & redMask >> self.red_shift
|
||||||
self.green_shift = blueBits
|
|
||||||
greenMask = ((1 << greenBits) - 1) << self.green_shift
|
else: # RGB
|
||||||
self.red_shift = self.green_shift + greenBits
|
redMask = ((1 << redBits) - 1) << self.red_shift
|
||||||
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
|
image = Image.fromarray(a)
|
||||||
self.red_shift = 0
|
if self.primaryOrder == "rgb":
|
||||||
redMask = (1 << redBits) - 1
|
(b, g, r) = image.split()
|
||||||
self.green_shift = redBits
|
image = Image.merge("RGB", (r, g, b))
|
||||||
greenMask = ((1 << greenBits) - 1) << self.green_shift
|
del b,g,r
|
||||||
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.convert("RGBX")
|
image = image.convert("RGBX")
|
||||||
|
|
||||||
elif self.bpp == 8:
|
if self.bpp == 16: #BGR565
|
||||||
#FIXME: improve 8 bit routines up!, image looks awful!
|
greenBits = 5
|
||||||
if rectangle.mode is not "RGB":
|
blueBits = 6
|
||||||
image = rectangle.convert("RGB")
|
redBits = 5
|
||||||
else:
|
|
||||||
image = rectangle
|
|
||||||
|
|
||||||
(r, g, b) = image.split()
|
image = rectangle
|
||||||
image = Image.merge("RGB", (g, r, b))
|
if self.depth == 16:
|
||||||
del b,g,r
|
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 = quantizetopalette(image, p, dither=self.CONFIG._8bitdither)
|
||||||
image.putpalette(self.BGR233)
|
|
||||||
del p
|
#image = image.convert('RGB', colors=4).quantize(palette=p)
|
||||||
rectangles = 1
|
#log.debug(image)
|
||||||
sendbuff.extend(pack("!BxH", 0, rectangles))
|
|
||||||
sendbuff.extend(pack("!HHHH", x, y, w, h))
|
|
||||||
sendbuff.extend(pack(">i", self.encoding))
|
|
||||||
sendbuff.extend( image.tobytes() )
|
|
||||||
|
|
||||||
|
|
||||||
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
|
# send image with client defined encoding
|
||||||
sendbuff.extend(pack("!BxH", 0, rectangles))
|
sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image))
|
||||||
sendbuff.extend(pack("!HHHH", x, y, w, h))
|
|
||||||
sendbuff.extend(pack(">i", self.encoding))
|
|
||||||
|
|
||||||
print("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))
|
|
||||||
else:
|
else:
|
||||||
print("[!] Unsupported BPP: %s" % self.bpp)
|
log.debug("[!] Unsupported BPP: %s" % self.bpp)
|
||||||
|
|
||||||
self.framebuffer = lastshot
|
self.framebuffer = lastshot
|
||||||
try:
|
try:
|
||||||
@@ -778,4 +494,4 @@ class VncServer():
|
|||||||
# connection closed?
|
# connection closed?
|
||||||
return False
|
return False
|
||||||
sock.settimeout(stimeout)
|
sock.settimeout(stimeout)
|
||||||
#print("end SendRectangles")
|
#log.debug("end SendRectangles")
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
pyuserinput
|
pydes
|
||||||
|
pynput
|
||||||
|
numpy
|
||||||
|
Pillow-PIL
|
||||||
|
elevate
|
||||||
|
|||||||
53
server.py
53
server.py
@@ -5,9 +5,19 @@ import pyvncs
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import socket
|
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):
|
class ControlThread(Thread):
|
||||||
@@ -22,7 +32,7 @@ class ControlThread(Thread):
|
|||||||
sleep(1)
|
sleep(1)
|
||||||
for t in threads:
|
for t in threads:
|
||||||
if not t.isAlive():
|
if not t.isAlive():
|
||||||
print("ControlThread removing dead", t)
|
_debug("ControlThread removing dead", t)
|
||||||
threads.remove(t)
|
threads.remove(t)
|
||||||
|
|
||||||
class ClientThread(Thread):
|
class ClientThread(Thread):
|
||||||
@@ -34,17 +44,17 @@ class ClientThread(Thread):
|
|||||||
self.setDaemon(True)
|
self.setDaemon(True)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
print("ClientThread died")
|
_debug("ClientThread died")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
|
_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
|
||||||
#print("Thread", self)
|
#_debug("Thread", self)
|
||||||
server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD)
|
server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD)
|
||||||
server.CONFIG._8bitdither = CONFIG._8bitdither
|
server.CONFIG._8bitdither = CONFIG._8bitdither
|
||||||
status = server.init()
|
status = server.init()
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
print("Error negotiating client init")
|
_debug("Error negotiating client init")
|
||||||
return False
|
return False
|
||||||
server.protocol()
|
server.protocol()
|
||||||
|
|
||||||
@@ -61,11 +71,17 @@ def main(argv):
|
|||||||
help="Listen in this port, default: %s" % ("5901"), type=int, required=False, default='5901')
|
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("-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("-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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.OUTFILE is not None:
|
||||||
|
fsock = open(args.OUTFILE, 'w')
|
||||||
|
sys.stdout = sys.stderr = fsock
|
||||||
|
|
||||||
# Multithreaded Python server
|
# Multithreaded Python server
|
||||||
TCP_IP = '0.0.0.0' if not hasattr(args,"TCP_IP") else args.TCP_IP
|
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
|
VNC_PASSWORD = args.VNC_PASSWORD
|
||||||
CONFIG._8bitdither = args.dither
|
CONFIG._8bitdither = args.dither
|
||||||
|
|
||||||
@@ -77,8 +93,21 @@ def main(argv):
|
|||||||
controlthread.start()
|
controlthread.start()
|
||||||
threads.append(controlthread)
|
threads.append(controlthread)
|
||||||
|
|
||||||
print("Multithreaded Python server : Waiting for connections from TCP clients...")
|
_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
|
||||||
print("Runing on:", sys.platform)
|
_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:
|
while True:
|
||||||
sockServer.listen(4)
|
sockServer.listen(4)
|
||||||
(conn, (ip,port)) = sockServer.accept()
|
(conn, (ip,port)) = sockServer.accept()
|
||||||
@@ -95,8 +124,8 @@ if __name__ == "__main__":
|
|||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# quit
|
# quit
|
||||||
print("Exiting on ctrl+c...")
|
_debug("Exiting on ctrl+c...")
|
||||||
#for t in threads:
|
#for t in threads:
|
||||||
# print("Killing", t)
|
# _debug("Killing", t)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
16
test.py
Normal file
16
test.py
Normal file
@@ -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")
|
||||||
6
test.sh
Executable file
6
test.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
for a in $(seq 1 5); do
|
||||||
|
echo "$a $$"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
79
winservice.py
Normal file
79
winservice.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user