Quasar (.NET3.5)通訊建立
quasar 是一個知名的遠控軟體,這裡解析它在.net3.5的版本。
3.5 可以解決windows的相容性問題,
即:經過一些方法編譯為特定的計算機程式碼,可以在windows7-windows11 的記憶體中載入執行
Client 端
初始化函式 Initialize()
包含一些系統引數的獲取,以及初始化客戶端的網路配置,這裡重點關注網路初始化的配置!
位置:Program.cs => Main =>Initialize()
初始化網路配置 InitializeClient()
位置:Initialize()=>InitializeClient()
private static void InitializeClient()
{
ConnectClient = new Client();
//這裡將所有的包型別都 封裝了 Type[] 陣列中,以便於後續解析網路包的時候,透過反射進行操作
ConnectClient.AddTypesToSerializer(new Type[]
{
typeof (Core.Packets.ServerPackets.GetAuthentication),
typeof (Core.Packets.ServerPackets.DoClientDisconnect),
typeof (Core.Packets.ServerPackets.DoClientReconnect),
typeof (Core.Packets.ServerPackets.DoClientUninstall),
typeof (Core.Packets.ServerPackets.DoDownloadAndExecute),
typeof (Core.Packets.ServerPackets.DoUploadAndExecute),
typeof (Core.Packets.ServerPackets.GetDesktop),
typeof (Core.Packets.ServerPackets.GetProcesses),
typeof (Core.Packets.ServerPackets.DoProcessKill),
typeof (Core.Packets.ServerPackets.DoProcessStart),
typeof (Core.Packets.ServerPackets.GetDrives),
typeof (Core.Packets.ServerPackets.GetDirectory),
typeof (Core.Packets.ServerPackets.DoDownloadFile),
typeof (Core.Packets.ServerPackets.DoMouseEvent),
typeof (Core.Packets.ServerPackets.DoKeyboardEvent),
typeof (Core.Packets.ServerPackets.GetSystemInfo),
typeof (Core.Packets.ServerPackets.DoVisitWebsite),
typeof (Core.Packets.ServerPackets.DoShowMessageBox),
typeof (Core.Packets.ServerPackets.DoClientUpdate),
typeof (Core.Packets.ServerPackets.GetMonitors),
typeof (Core.Packets.ServerPackets.DoShellExecute),
typeof (Core.Packets.ServerPackets.DoPathRename),
typeof (Core.Packets.ServerPackets.DoPathDelete),
typeof (Core.Packets.ServerPackets.DoShutdownAction),
typeof (Core.Packets.ServerPackets.GetStartupItems),
typeof (Core.Packets.ServerPackets.DoStartupItemAdd),
typeof (Core.Packets.ServerPackets.DoStartupItemRemove),
typeof (Core.Packets.ServerPackets.DoDownloadFileCancel),
typeof (Core.Packets.ServerPackets.GetKeyloggerLogs),
typeof (Core.Packets.ServerPackets.DoUploadFile),
typeof (Core.Packets.ServerPackets.GetPasswords),
typeof (Core.Packets.ClientPackets.GetAuthenticationResponse),
typeof (Core.Packets.ClientPackets.SetStatus),
typeof (Core.Packets.ClientPackets.SetStatusFileManager),
typeof (Core.Packets.ClientPackets.SetUserStatus),
typeof (Core.Packets.ClientPackets.GetDesktopResponse),
typeof (Core.Packets.ClientPackets.GetProcessesResponse),
typeof (Core.Packets.ClientPackets.GetDrivesResponse),
typeof (Core.Packets.ClientPackets.GetDirectoryResponse),
typeof (Core.Packets.ClientPackets.DoDownloadFileResponse),
typeof (Core.Packets.ClientPackets.GetSystemInfoResponse),
typeof (Core.Packets.ClientPackets.GetMonitorsResponse),
typeof (Core.Packets.ClientPackets.DoShellExecuteResponse),
typeof (Core.Packets.ClientPackets.GetStartupItemsResponse),
typeof (Core.Packets.ClientPackets.GetKeyloggerLogsResponse),
typeof (Core.Packets.ClientPackets.GetPasswordsResponse),
typeof (Core.ReverseProxy.Packets.ReverseProxyConnect),
typeof (Core.ReverseProxy.Packets.ReverseProxyConnectResponse),
typeof (Core.ReverseProxy.Packets.ReverseProxyData),
typeof (Core.ReverseProxy.Packets.ReverseProxyDisconnect)
});
// 註冊事件,後面將重點關注 ClientRead
ConnectClient.ClientState += ClientState;
ConnectClient.ClientRead += ClientRead;
ConnectClient.ClientFail += ClientFail;
}
註冊ConnectClient 相關的事件
網路連線 1 Connect()
位置:Main=>Connect()
/// <summary>
/// 連線有四種狀態:
/// 1,2. 重連 連線失敗
/// 3.是否已經連線
/// 4.是否需要斷連
/// </summary>
private static void Connect()
{
// 需要重連 && 連線失敗
while (_reconnect && !SystemCore.Disconnect)
{
if (!_connected)
{
Thread.Sleep(100 + new Random().Next(0, 250));
// 輪詢所有的服務端ip池
Host host = _hosts.GetNextHost();
// ConnectClient 物件的 連線操作 稍後詳細說明
ConnectClient.Connect(host.Hostname, host.Port);
Thread.Sleep(200);
// 耗時操作是這裡需要輪詢 ip 池,
// Application.DoEvents()讓程式不會假死
Application.DoEvents();
}
while (_connected) // hold client open
{
Application.DoEvents();
//1000ms = 1s
//2500ms = 2.5s
Thread.Sleep(2500);
}
// 觀測連線關閉操作位:退出連線
if (SystemCore.Disconnect)
{
ConnectClient.Disconnect();
return;
}
Thread.Sleep(Settings.RECONNECTDELAY + new Random().Next(250, 750));
}
}
網路連線 2 Client.Connect(string host, ushort port)
位置:Main=>Connect()=>ConnectClient.Connect(host.Hostname, host.Port)
public void Connect(string host, ushort port)
{
if (_serializer == null) throw new Exception("Serializer not initialized");
try
{
Disconnect();
Initialize();
// socket 的 tcp 協議配置
_handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
_readBuffer = new byte[BUFFER_SIZE];
_tempHeader = new byte[HEADER_SIZE];
// ip 埠配置
_handle.Connect(host, port);
if (_handle.Connected)
{
// flag 解析
//None:不使用任何標誌。
//OutOfBand:處理帶外資料。
//Peek:窺視(Peek)接收緩衝區中的訊息,但不將其從緩衝區中刪除。
//DontRoute:傳送資料時不使用路由表。
//MaxIOVectorLength:用於指定傳送和接收資料時使用的 WSABUF 結構的數量的標準值。
//Truncated:訊息太大,無法放入指定的緩衝區,並被截斷。
//ControlDataTruncated:控制資料無法放入內部 64 KB 緩衝區,並被截斷。
//Broadcast:指示廣播資料包。
//Multicast:指示組播資料包。
//Partial:部分傳送或接收訊息。
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
OnClientState(true);
}
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
重點關注 _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null)
講解下 BeginReceive 這個函式的作用:
- 這個函式是非同步的
- _readBuffer :接受資料的容器
- 0: 讀取的起始位置
- _readBuffer.Length: 接受資料包的長度
- AsyncReceive:回撥方法
現在我們需要往 回撥方法走,看看到底做了什麼
BeginReceive 的回撥函式 AsyncReceive(IAsyncResult result)
private void AsyncReceive(IAsyncResult result)
{
try
{
int bytesTransferred;
try
{
bytesTransferred = _handle.EndReceive(result);
if (bytesTransferred <= 0)
{
OnClientState(false);
return;
}
}
catch (Exception)
{
OnClientState(false);
return;
}
byte[] received = new byte[bytesTransferred];
Array.Copy(_readBuffer, received, received.Length);
lock (_readBuffers)
{
_readBuffers.Enqueue(received);
}
lock (_readingPacketsLock)
{
if (!_readingPackets)
{
_readingPackets = true;
ThreadPool.QueueUserWorkItem(AsyncReceive);
}
}
}
catch
{
}
try
{
// 遞迴非同步模式
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
這裡將接收到的buffer 放到佇列裡面,然後呼叫非同步方法去做操作,下一步我們將去探究下
這裡用了遞迴非同步模式,高效能收包模式
解析包頭的函式 AsyncReceive(object state)
位置:AsyncReceive(IAsyncResult result)=>AsyncReceive(object state)
private void AsyncReceive(object state)
{
while (true)
{
byte[] readBuffer;
lock (_readBuffers)
{
if (_readBuffers.Count == 0)
{
// 位元組佇列裡面沒有東西,關閉讀取引數
lock (_readingPacketsLock)
{
_readingPackets = false;
}
return;
}
readBuffer = _readBuffers.Dequeue();
}
_readableDataLen += readBuffer.Length;
bool process = true;
while (process)
{
// 這裡開始迴圈解析,包頭+包體
switch (_receiveState)
{
case ReceiveType.Header:
{
// 計算出 payloadLen的值
if (_readableDataLen >= HEADER_SIZE)
{ // we can read the header
int headerLength = (_appendHeader)
? HEADER_SIZE - _tempHeaderOffset
: HEADER_SIZE;
try
{
if (_appendHeader)
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset,
headerLength);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
// 小端序,位元組轉整數 ,這是自定義的
// 一般tcp的解析都是大端序
_payloadLen = BitConverter.ToInt32(_tempHeader, 0);
_tempHeaderOffset = 0;
_appendHeader = false;
}
else
{
_payloadLen = BitConverter.ToInt32(readBuffer, _readOffset);
}
if (_payloadLen <= 0 || _payloadLen > MAX_PACKET_SIZE)
throw new Exception("invalid header");
}
catch (Exception)
{
process = false;
Disconnect();
break;
}
_readableDataLen -= headerLength;
_readOffset += headerLength;
_receiveState = ReceiveType.Payload;
}
else // _parentServer.HEADER_SIZE < _readableDataLen
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_tempHeaderOffset += _readableDataLen;
_appendHeader = true;
process = false;
}
break;
}
case ReceiveType.Payload:
{
if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen)
_payloadBuffer = new byte[_payloadLen];
int length = (_writeOffset + _readableDataLen >= _payloadLen)
? _payloadLen - _writeOffset
: _readableDataLen;
try
{
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_writeOffset += length;
_readOffset += length;
_readableDataLen -= length;
if (_writeOffset == _payloadLen)
{
// 壓縮啟用控制符
if (encryptionEnabled)
// 解密
_payloadBuffer = AES.Decrypt(_payloadBuffer);
bool isError = _payloadBuffer.Length == 0; // check if payload decryption failed
if (_payloadBuffer.Length > 0)
{
if (compressionEnabled)
// 這裡要有一層解壓
_payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer);
isError = _payloadBuffer.Length == 0; // check if payload decompression failed
}
if (isError)
{
process = false;
Disconnect();
break;
}
using (MemoryStream deserialized = new MemoryStream(_payloadBuffer))
{
IPacket packet = (IPacket)_serializer.Deserialize(deserialized);
// 這開啟記憶體觸發了讀包
OnClientRead(packet);
}
_receiveState = ReceiveType.Header;
_payloadBuffer = null;
_payloadLen = 0;
_writeOffset = 0;
}
if (_readableDataLen == 0)
process = false;
break;
}
}
}
if (_receiveState == ReceiveType.Header)
{
_writeOffset = 0; // prepare for next packet
}
_readOffset = 0;
_readableDataLen = 0;
}
}
swtich 裡面先根據 header 拿到 payload的長度
這裡再根據payload的實際值,開闢記憶體,作為引數,呼叫OnClientRead(packet);
回到InitializeClient,關注ClientRead(Client client, IPacket packet) 處理
private static void ClientRead(Client client, IPacket packet)
{
// 包 全部交由這個函式去處理
PacketHandler.HandlePacket(client, packet);
}
public static class PacketHandler
{
public static void HandlePacket(Client client, IPacket packet)
{
var type = packet.GetType();
if (type == typeof(ServerPackets.GetAuthentication))
{
CommandHandler.HandleGetAuthentication((ServerPackets.GetAuthentication)packet, client);
}
else if (type == typeof(ServerPackets.DoDownloadAndExecute))
{
CommandHandler.HandleDoDownloadAndExecute((ServerPackets.DoDownloadAndExecute)packet,
client);
}
else if (type == typeof(ServerPackets.DoUploadAndExecute))
{
CommandHandler.HandleDoUploadAndExecute((ServerPackets.DoUploadAndExecute)packet, client);
}
else if (type == typeof(ServerPackets.DoClientDisconnect))
{
Program.Disconnect();
}
else if (type == typeof(ServerPackets.DoClientReconnect))
{
Program.Disconnect(true);
}
else if (type == typeof(ServerPackets.DoClientUninstall))
{
CommandHandler.HandleDoClientUninstall((ServerPackets.DoClientUninstall)packet, client);
}
else if (type == typeof(ServerPackets.GetDesktop))
{
CommandHandler.HandleGetDesktop((ServerPackets.GetDesktop)packet, client);
}
else if (type == typeof(ServerPackets.GetProcesses))
{
CommandHandler.HandleGetProcesses((ServerPackets.GetProcesses)packet, client);
}
else if (type == typeof(ServerPackets.DoProcessKill))
{
CommandHandler.HandleDoProcessKill((ServerPackets.DoProcessKill)packet, client);
}
else if (type == typeof(ServerPackets.DoProcessStart))
{
CommandHandler.HandleDoProcessStart((ServerPackets.DoProcessStart)packet, client);
}
else if (type == typeof(ServerPackets.GetDrives))
{
CommandHandler.HandleGetDrives((ServerPackets.GetDrives)packet, client);
}
else if (type == typeof(ServerPackets.GetDirectory))
{
CommandHandler.HandleGetDirectory((ServerPackets.GetDirectory)packet, client);
}
else if (type == typeof(ServerPackets.DoDownloadFile))
{
CommandHandler.HandleDoDownloadFile((ServerPackets.DoDownloadFile)packet, client);
}
else if (type == typeof(ServerPackets.DoUploadFile))
{
CommandHandler.HandleDoUploadFile((ServerPackets.DoUploadFile)packet, client);
}
else if (type == typeof(ServerPackets.DoMouseEvent))
{
CommandHandler.HandleDoMouseEvent((ServerPackets.DoMouseEvent)packet, client);
}
else if (type == typeof(ServerPackets.DoKeyboardEvent))
{
CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent)packet, client);
}
else if (type == typeof(ServerPackets.GetSystemInfo))
{
CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo)packet, client);
}
else if (type == typeof(ServerPackets.DoVisitWebsite))
{
CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite)packet, client);
}
else if (type == typeof(ServerPackets.DoShowMessageBox))
{
CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox)packet, client);
}
else if (type == typeof(ServerPackets.DoClientUpdate))
{
CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate)packet, client);
}
else if (type == typeof(ServerPackets.GetMonitors))
{
CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors)packet, client);
}
else if (type == typeof(ServerPackets.DoShellExecute))
{
CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute)packet, client);
}
else if (type == typeof(ServerPackets.DoPathRename))
{
CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename)packet, client);
}
else if (type == typeof(ServerPackets.DoPathDelete))
{
CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete)packet, client);
}
else if (type == typeof(ServerPackets.DoShutdownAction))
{
CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction)packet, client);
}
else if (type == typeof(ServerPackets.GetStartupItems))
{
CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems)packet, client);
}
else if (type == typeof(ServerPackets.DoStartupItemAdd))
{
CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd)packet, client);
}
else if (type == typeof(ServerPackets.DoStartupItemRemove))
{
CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove)packet, client);
}
else if (type == typeof(ServerPackets.DoDownloadFileCancel))
{
CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel)packet,
client);
}
else if (type == typeof(ServerPackets.GetKeyloggerLogs))
{
CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs)packet, client);
}
else if (type == typeof(ServerPackets.GetPasswords))
{
CommandHandler.HandleGetPasswords((ServerPackets.GetPasswords)packet, client);
}
else if (type == typeof(ReverseProxy.Packets.ReverseProxyConnect) ||
type == typeof(ReverseProxy.Packets.ReverseProxyConnectResponse) ||
type == typeof(ReverseProxy.Packets.ReverseProxyData) ||
type == typeof(ReverseProxy.Packets.ReverseProxyDisconnect))
{
ReverseProxyCommandHandler.HandleCommand(client, packet);
}
}
}
斷點除錯發現,服務端首先傳送的是認證包,即走的這個判斷
if (type == typeof(ServerPackets.GetAuthentication))
{
CommandHandler.HandleGetAuthentication((ServerPackets.GetAuthentication)packet, client);
}
將系統資訊發給客戶端做判斷,至此client 端 的網路請求便走通了
Server端
關注FrmMain_Load 的載入內容
private void FrmMain_Load(object sender, EventArgs e)
{
// 初始化
InitializeServer();
// 監聽
AutostartListeningP();
}
初始化ConnectionHandler
private void InitializeServer()
{
ConServer = new ConnectionHandler();
ConServer.ServerState += ServerState;
ConServer.ClientConnected += ClientConnected;
ConServer.ClientDisconnected += ClientDisconnected;
}
開啟埠監聽
private void AutostartListeningP()
{
// 自動監聽 不懂
// upnp 主要是為了埠對映
if (Settings.AutoListen && Settings.UseUPnP)
{
UPnP.Initialize(Settings.ListenPort);
ConServer.Listen(Settings.ListenPort);
}
else if (Settings.AutoListen)
{
UPnP.Initialize();
ConServer.Listen(Settings.ListenPort);
}
else
{
UPnP.Initialize();
}
if (Settings.EnableNoIPUpdater)
{
NoIpUpdater.Start();
}
}