符 明
(湖南食品藥品職業(yè)學(xué)院圖書館,長沙 410004)
Windows家庭版以上系統(tǒng)自帶遠程桌面服務(wù)。MSTSC遠程桌面服務(wù)被廣泛用于小型局域網(wǎng)的維護工作中。但這個程序需要知道對方計算機的用戶名和密碼,并且只能在同一網(wǎng)段下使用。一般情況下,系統(tǒng)維護操作員會安裝第三方軟件TeamViewer等來翻過NAT路由來代替原有的Windows遠程桌面服務(wù)。
典型的局域網(wǎng)拓撲環(huán)境,計算機1位于NAT1后,計算機2位于NAT2后,計算機1、計算機2均可以直接和服務(wù)器通訊。但是服務(wù)器并不能直接訪問計算機1、計算機2,且計算機1和計算機2之間也無法通訊。
RDP Session是基于RdpEncom.dll的Windows遠 程 桌 面COM控 件[1]。該DLL文 件 位 于System32和sysWOW64目錄下,此COM控件存在于從Windows7至Windows10的系統(tǒng)自帶COM控件庫中。此COM的優(yōu)點是:控制端用RDP連接字符串包含的密鑰,無需輸入被控制端的用戶名及密碼即可接入被控制端計算機的遠程桌面。在同一網(wǎng)段的局域網(wǎng)下,該控件提供了一種優(yōu)異的系統(tǒng)級遠程桌面連接方法,可以輕松實現(xiàn)對遠程計算機進行實時監(jiān)控、控制、發(fā)送文本等功能。缺點是:只能用于同一網(wǎng)段之內(nèi)的計算機遠程桌面服務(wù),無法跨NAT網(wǎng)段連接。以下為一個標準的RDP連接字符串,里面包含了連接密鑰和連接地址與端口號。
<E><A KH="密鑰字符串(略)"KH2="密鑰字符串(略)"
CE="密鑰字符串(略)"ID="連接ID(略)"/><C><T ID="1"SID="0"><L
P="41000"N="fe80::2955:f79a:f465:3c75%2"/><L P="41000"
N="fe80::f997:d009:53b7:50f7%8"/><L P="41000"N="192.168.56.1"/><L
P="41000"N="192.168.0.103"/></T></C></E>
控制端計算機采用本地端口轉(zhuǎn)發(fā),將本地端口映射到服務(wù)器,相當(dāng)于執(zhí)行命令:ssh-L port:localhost:port user@server,通過訪問本地端口交由服務(wù)器轉(zhuǎn)發(fā);被控制端PC采用遠程轉(zhuǎn)發(fā),相當(dāng)于執(zhí)行命令:ssh-R port:localhost:port user@server,將服務(wù)器端口映射到本地端口,訪問服務(wù)器端口轉(zhuǎn)發(fā)至本地端口。通過中間OPENSSH服務(wù)器,兩臺計算機之間建立繞過NAT限制,能直接通訊的加密SSH通道。
提供RDP遠程桌面服務(wù)的計算機創(chuàng)建RDP遠程連接字符串。原始RDP字符串中包含明文的基于本機生成的連接密鑰及本機IP地址、RDP訪問端口等信息??刂贫擞嬎銠C接收到該字符串后,根據(jù)SSH隧道要求,必須要使用修改后的RDP連接字符串,添加本地127.0.0.1及端口信息??刂贫擞嬎銠C就可以利用修改過的RDP連接字符串,通過已經(jīng)建立好的SSH隧道連接到遠程計算機的桌面。
本方法采用NET Framework 4.5環(huán)境,Windows10下Visual Studio 2019 C#社區(qū)版開發(fā),分為控制端、被控制端、服務(wù)器端三個應(yīng)用程序。服務(wù)器安裝OPENSSH服務(wù)環(huán)境??刂贫伺c被控制端均添加引用SSH.NET。
客戶機之間建立SSH隧道,SSH隧道監(jiān)聽的端口就是RDP監(jiān)聽的端口,這個端口可以自定義。
控制端代碼建立SSH本地端口轉(zhuǎn)發(fā),將本地端口映射到服務(wù)器,訪問本地127.0.0.1:Port,即訪問Server:Port。
Renci.SshNet.SshClient sshClient=new Renci.SshNet.SshClient(serverIP,"username","password");
sshClient.Connect();
Renci.SshNet.ForwardedPortLocal localForwardPort=new ForwardedPortLocal("127.0.0.1",(uint)RDP_TCP_Port,"127.0.0.1",(uint)RDP_TCP_Port);
sshClient.AddForwardedPort(localForwardPort);
localForwardPort.Start();
被控制端代碼建立遠程端口轉(zhuǎn)發(fā),將服務(wù)器端口映射到本地,訪問Server:Port,即訪問
local:Port。
Renci.SshNet.SshClient sshClient=new Renci.SshNet.SshClient(serverIP,"username","password");
sshClient.Connect();
Renci.SshNet.ForwardedPortRemote remoteForward-Port=new
ForwardedPortRemote((uint)RDP_TCP_Port,localIP,(uint)RDP_TCP_Port);
sshClient.AddForwardedPort(remoteForwardPort);
remoteForwardPort.Start();
控制端添加ActiveX視窗控件AxRDPCOMAPILib及RdpEncom.dll引用??刂贫藦姆?wù)器通過Socket方式接收RDP連接字符串,并通過此連接字符串進行RDP連接:
//通過Socket形式接收RDP連接字符串過程略
Form frm=new Form();
frm.Text="遠程桌面";
frm.Size=new ystem.Drawing.Size(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
frm.MaximizeBox=false;
frm.MinimizeBox=false;
frm.FormClosing+=new FormClosingEventHandler(frm_FormClosing);
axRDPViewer.Dock=System.Windows.Forms.Dock-Style.Fill;
axRDPViewer.Size=new System.Drawing.Size(frm.Width,frm.Height);
axRDPViewer.OnConnectionTerminated+=AxRDPViewer_OnConnectionTerminated;//定義遠程桌面被控制端斷線以后的事件
((System.ComponentModel.ISupportInitialize)axRDPViewer).BeginInit();
frm.Controls.Add(axRDPViewer);
((System.ComponentModel.ISupportInitialize)axRDPViewer).EndInit();
axRDPViewer.SmartSizing=true;//定義遠程桌面窗口信息,并初始化
axRDPViewer.Connect(rdpConnectString,System.Guid.NewGuid().ToString(),"");//用接收的RDP連接字符串進行連接
frm.ShowDialog();
被控制端添加RdpEncom.dll引用建立RDP連接請求,并將修改后的RDP連接字符串通過Socket方式發(fā)送給服務(wù)器,等待RDP的遠程連接。在RDP連接建立時,可以自定義RDP監(jiān)聽端口:
RDPCOMAPILib.RDPSession rdp=new RDPCOMAPILib.RDPSession();
rdp.OnAttendeeConnected+=new
RDPCOMAPILib._IRDPSessionEvents_OnAttendee ConnectedEventHandler(rdp_OnAttendeeConnected);//定義連接等級
rdp.OnAttendeeDisconnected+=Rdp_OnAttendeeDisconnected;//定義斷開以后的事件
rdp.SetDesktopSharedRect(0,0,System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
rdp.Properties["PortId"]=RDP_TCP_Port;//自 定 義在41000端口上進行RDP監(jiān)聽
rdp.Open();//打開RDP監(jiān)聽
RDPCOMAPILib.IRDPSRAPIInvitation invitation=rdp.Invitations.CreateInvitation(null,System.Guid.NewGuid().ToString(),"",1);
string rdpConnStr=invitation.ConnectionString;//創(chuàng)建RDP連接字符串,同時連接數(shù)=1
using(System.Net.Sockets.Socket skt=objSocket as System.Net.Sockets.Socket)
{
//向服務(wù)器發(fā)送連接字符串,代碼略
}
被控制端定義連接等級:
static void rdp_OnAttendeeConnected(object pAttendee)
{
RDPCOMAPILib.RDPSRAPIAttendee att=pAttendee as RDPCOMAPILib.RDPSRAPIAttendee;
att.ControlLevel=RDPCOMAPILib.CTRL_LEVEL.CTRL_LEVEL_VIEW;//連接等級為僅瀏覽
}
修改RDP字符串,在原字符串上根據(jù)格式直接進行添加:
string insertStr="<L P=""+RDP_TCP_Port+""N="127.0.0.1"/>";
rdpConnectString=rdpConnectString.Insert(rdpConnectString.LastIndexOf("</T>"),insertStr);
控制端計算機和被控制端計算機分別位于D-LINK DIR-629及TPLINK TL-WR886N家用普通路由器后,SSH服務(wù)器在路由器前面,此方案測試通過。
(1)RdpEncom.dll為系統(tǒng)自帶服務(wù),提供了多種連接等級,包括僅瀏覽到全局控制多個控制等級??蔁o視UAC控制窗口對系統(tǒng)進行設(shè)置,并提供系統(tǒng)級遠程桌面服務(wù)。
(2)RdpEncom.dll同時也可以提供Virtual Channel功能,這個功能可以在RDP通訊中提供簡單的文本傳輸服務(wù),即不通過額外的Socket通道實現(xiàn)即時文本通訊。
(3)市場上常見的路由器、防火墻、通訊運營商一般不禁止SSH端口及服務(wù)。這個方案可以用在Internet廣域網(wǎng)中,具有一定的實用性,但需要在Internet上暴露OPENSSH服務(wù)器,因此對此服務(wù)器安全設(shè)置必須十分謹慎。
(4)如果要建立穩(wěn)定的遠程桌面服務(wù),那么必須要提高SSH加密隧道的穩(wěn)定性,這是后續(xù)需要進一步研究的課題。