摘要:本文介紹了一種通過通訊端網路編程和螢幕捕獲技術實現的對遠端電腦螢幕進行監視的方法。 關鍵詞:通訊端;螢幕捕捉;遠程監視;網路 前言 在實際工程中,經常有施工現場和控制中心不在一起的情況,在這種情況一般多由工程技術人員往返穿梭其間來實現對遠程施工現場的情況瞭解和對控制中心的矯正控制。顯然這種工作方式的效率是很低下的,沒有充分發揮電腦網路的強大優勢,其實通過網路編程完全可以使技術人員在控制中心對位於工程現場的遠端電腦實施監視和控制。雖然互連網上有不少遠程終端控制軟體如"超級間諜"、"冰河"等,但由於其帶有黑軟的性質,不能保證其在編程時沒有留有其他後門,因此從電腦安全的角度出發應當自行開發此類軟體。為避免本文所述技術被用於製造駭客類軟體,本文將不準備對遠程終端的控制部分做進一步的介紹,而將重點放在對遠端電腦螢幕介面的監視上。 1 資料資訊在網路上的傳送 由於本機電腦是通過網路來對遠端電腦實施監控,因此需要對網卡進行編程以實現往來於雙方的資料資訊在網路上的順暢通訊。可供選擇的方案有通訊端、郵槽、具名管道等多種,本文在此選用開發和應用都比較靈活的流式通訊端作為網路通訊的基礎。考慮到實際情況,遠程被監視主機隨時為本地監控主機提供螢幕資訊的服務,因此整個系統可以劃分為兩大模組--伺服器端和客戶機端,分別運行於遠程主機和本地監控主機,由客戶機向伺服器發出串連請求,在建立串連後由伺服器定時發送遠程螢幕資訊給客戶機,客戶機接收到伺服器發來的資料後將其顯示在本地主機。 至於用流式通訊端對網路進行編程的主要過程可用來表示。伺服器方在使用通訊端之前,首先必須擁有一個Socket,可用socket()函數建立之:
sock=socket(AF_INET,SOCK_STREAM,0); |
其中AF_INET 和SOCK_STREAM指定了建立的是採用了TCP/IP地址族的流式通訊端。該通訊端實際上是提供了一個通訊連接埠,通過這個連接埠可與任何一個具有通訊端連接埠的電腦實施通訊。一旦擷取了新的通訊端,應立即通過bind()將該通訊端與本機上的一個連接埠建立關聯。需要預先對一個指向包含有本機IP地址和連接埠資訊的sockaddr_in結構填充一些必要的資訊,如本地連接埠號碼和本地主機地址等,並通過bind()將伺服器處理序在網路上標識出來:
sockin_s.sin_family=AF_INET; sockin_s.sin_addr.s_addr=0; sockin_s.sin_port=htons(PORT); bind(sock,(LPSOCKADDR)&sockin_s,sizeof(sockin_s)); |
在完成接下來的listen()偵聽後,需要用accept()等待接收用戶端的串連,由於該函數在沒有用戶端進行申請串連之前會處於阻塞狀態,因此需要為其單獨開闢一個線程,以免影響到程式整體:
AfxBeginThread(Server,NULL);//建立一個新的線程 …… UINT Server(LPVOID lpVoid) { CSurveillant_ServerView* pView=((CSurveillant_ServerView*)((CFrameWnd*) AfxGetApp()->m_pMainWnd)->GetActiveView()); int nLen=sizeof(SOCKADDR); pView->newskt_s= accept(pView->sock,(LPSOCKADDR)& pView->sockin_s,(LPINT)& nLen); WSAAsyncSelect(pView->newskt_s,pView->m_hWnd,WM_MSG,FD_CLOSE); pView->SetTimer(0,2500,NULL); return 1; } |
在這裡通過WSAAsyncSelect()非同步選擇函數來以非同步形式響應關心的網路事件FD_CLOSE,並在該事件發生時發出自訂WM_MSG訊息,通過響應這個訊息可以得之當前與伺服器聯絡的客戶機程式已關閉退出,由於伺服器部分是運行於遠程工程現場的,為了使控制中心的監控程式(客戶)在下次發出監控請求時能為其提供服務需要在WM_MSG的訊息響應函數裡關閉由accept() 所產生的新的通訊端newskt_s,並重新啟動該線程等待監控程式的再次串連。在accept()函數成功返回後,就可以在定時器響應函數裡用send() 函數與之建立了串連的監控主機定時發送捕獲的遠程螢幕資訊了。 作為客戶的監控程式,其實現過程要比伺服器簡單許多。由於需要接收資料,因此在非同步選擇函數中需要設定待監測的網路事件為FD_CLOSE和FD_READ。在訊息響應函數中可以通過對訊息參數的低位位元組進行判斷而區分出具體發生是何種網路事件,並對其做出響應的反應。下面是監控端程式網路部分的主要代碼:
…… IPaddr=inet_addr(strIP); sock=socket(AF_INET,SOCK_STREAM,0); //建立通訊端 sockin_c.sin_family=AF_INET; sockin_c.sin_addr.S_un.S_addr=IPaddr; sockin_c.sin_port=m_Port; connect(sock,(LPSOCKADDR)&sockin_c,sizeof(sockin_c));//串連伺服器 …… WSAAsyncSelect(sock,m_hWnd,WM_MSG,FD_READ|FD_CLOSE); …… |
通過非同步選擇函數的設定,在有資料到達時會由FD_READ觸發WM_MSG訊息,並在處理函數中通過調用recv ()將遠程主機的螢幕資訊從網路接收到緩衝,並完成在本地機的重顯。通過以上幾步,已經初步具備了在遠程伺服器和本地客戶機之間的網路通訊能力,可以完成螢幕資訊的網路傳送任務。
VC++實現對遠端電腦螢幕的監視 |
|
作者:佚名 文章來源:天極 點擊數:
670 更新時間:2006-1-6 |
對遠端電腦螢幕的捕捉和顯示 前面部分的工作只是為整個監控系統提供一個低層的網路資料通訊的能力,也可以說是為現場主機和監控中心提供一個通訊用通道。至於本文的中心議題--遠程監視工作則需要分別在現場主機和監控中心中完成對螢幕的捕捉和資訊的再現。螢幕的捕捉可以採取先擷取桌面視窗指標並建立一個與之相容的裝置環境,然後建立一個與桌面視窗指標相相容的記憶體位元影像並以位元影像的形式將螢幕映像拷貝到新建立的位元影像之中:
char dot[1572864]; //1024*768*2 CBitmap bmp; //記憶體位元影像 CDC wdc; //裝置環境 CDC* pDC; //指向桌面視窗的裝置環境指標 …… static CWindowDC ddc(GetDesktopWindow()); //引用桌面視窗指標定義對象ddc pDC=&ddc; //將指標pdc指向ddc wdc.CreateCompatibleDC(pDC); //建立與ddc相容的裝置環境 bmp.CreateCompatibleBitmap(pDC,1024,768); //建立與ddc相容的位元影像 wdc.SelectObject(&bmp); //選擇bmp …… wdc.BitBlt(0,0,1024,768,pDC,0,0,SRCCOPY); //把案頭映像複製到wdc的bmp中 |
這時雖以擷取到了螢幕的資訊,並將其複製到記憶體位元影像之中,但此時還不能直接將其發送出去,需要調用CBitmap 類的成員函數GetBitmapBits()來將映像資訊從記憶體位元影像拷貝到緩衝,並通過通訊端的send()函數將緩衝中存放的螢幕資訊通過網路從現場主機發送到控制中心。 現場主機的螢幕資訊在控制中心的再現,基本上是螢幕截取的逆過程:先建立一個同客戶區相關的裝置環境並建立一個與之相容的裝置環境,然後按位元影像格式在記憶體中建立一個與之相容的記憶體位元影像。在從網路接收完一屏資訊後,通過CBitmap的成員函數SetBitmapBits()把緩衝中的螢幕資訊按位元影像格式拷貝到記憶體位元影像,最後完成對記憶體位元影像的顯示。其主要過程如下:
CDC* pDC=GetDC(); //引用使用者視窗指標定義對象pDC wdc.CreateCompatibleDC(pDC); //建立與pDC相容的device context bmp.CreateCompatibleBitmap(pDC,1024,768); //建立與pDC相容的位元影像 wdc.SelectObject(&bmp); …… iReadLen = recv(sock,buffer,60000,0); //從網路接收資料 for(i=0;i{ dot[pointer]=buffer[i]; pointer++; if(pointer==1572864) //判斷接收到的資訊是否已滿一屏 { GetClientRect(&rect); bmp.SetBitmapBits(1572864,(LPVOID)dot); //把記憶體資料複製到bmp中 //把bmp中映像複製到使用者視窗中 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&wdc,0,0,1024,768,SRCCOPY); pointer=0; //接收完一屏後指標複位,準備接收下一屏 } } |
服務程式的自動載入及擴充 從功能上看,服務端程式只負責為遠程客戶提供服務,在全部運行期間根本不需要人為的外來幹預,因此可以隱藏其介面並將其作成後台服務程式:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { …… cs.cx=200; cs.cy=10; cs.style=WS_POPUP; cs.dwExStyle|=WS_EX_TOOLWINDOW; return TRUE; } |
另外,由於現在電腦多具有通過Modem實現遠程喚醒的功能,因此如能使服務程式具備自啟動功能將實現遠程現場主機的無人值守。自啟動有多種方式:在Autoexec.bat、win.ini等檔案中加入啟動命令、在"啟動"菜單裡加入指向程式的捷徑、修改註冊表等。其中由於註冊表通常被人為改動的機會要小的多,因此通過修改註冊表實現自啟動是一種比較安全的方法。本文採取的方法是:先通過API函數CopyFile()將服務程式複製到系統目錄,然後對HKEY_LOCAL_MACHINE 的Software\Microsoft\Windows\CurrentVersion\Run寫入一個字串索引值,該索引值的內容是服務程式在系統目錄下的全路徑:
DWORD type=REG_SZ; DWORD size=MAX_PATH; LPCTSTR Rgspath="Software\\Microsoft\\Windows\\CurrentVersion\\Run" ; …… GetSystemDirectory(SysPath,size); //擷取系統目錄 GetModuleFileName(NULL,CurrentPath,size); //擷取程式路徑 FileCurrentName = CurrentPath; FileNewName = lstrcat(SysPath,"\\Surveillant.exe"); ret = CopyFile(FileCurrentName,FileNewName,TRUE); //拷貝程式到系統目錄 …… //開啟註冊表 ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,Rgspath,0,KEY_WRITE, &hKEY); …… //寫入註冊表 ret=RegSetValueEx(hKEY,"Surveillant",NULL,type, FileNewName,size); …… //關閉註冊表 RegCloseKey(hKEY); |
至於監控中心對現場主機的遠端控制,則主要是通過向對方程式發送用以標識訊息的資料並在遠程主機接收完畢後用SendMessage()向指定視窗發送訊息來完成的,可用CreateProcess();來啟動現場主機的程式以響應訊息。由於該部分技術亦可用來編寫駭客軟體,故本文在此不便作進一步的描述。 小結: 本文主要針對基於流式通訊端的低層網路通訊模組和建立在該模組基礎之上的螢幕截取和複原技術的設計、實現作了較為詳細的介紹。本文所述監控系統在實際應用中取得了較好的效果。使工程技術人員能在控制中心及時瞭解到位於工程現場的電腦螢幕上的指示圖表的動態顯示,並根據監視結果作出及時的決策。本文所述程式在Windows 2000 Professional下,由Microsoft Visual C++編譯通過。 |
|