轉(zhuǎn)載自:http://www./q/484
發(fā)一下以前研究過(guò)的屏幕傳輸算法,,墊墊底。
屏幕監(jiān)控是遠(yuǎn)程控制中的一項(xiàng)主要功能,,有了此功能能使操作遠(yuǎn)程電腦像操作本地電腦一樣方便,。
實(shí)現(xiàn)方法很多,原理就是不斷地把遠(yuǎn)程電腦屏幕的圖像發(fā)送到本地電腦,,本地電腦把圖像顯示出來(lái),。
最早期的實(shí)現(xiàn)方法只是不斷地傳送bmp圖像,這樣做不僅傳輸延時(shí)很大,,且cpu特別是服務(wù)端的cpu占用率很大。
為了解決以上兩個(gè)問(wèn)題,,可以采用傳輸屏幕變化的部分,,傳輸過(guò)程中壓縮解壓縮的方法。
傳輸屏幕變化的部分:應(yīng)用得比較好的有3種方式,,1.驅(qū)動(dòng)級(jí)的Mirror Driver ,、2.GDI+下的算法方式,3.RDP傳輸協(xié)議(遠(yuǎn)程桌面)
Mirror Driver
大名鼎鼎的VNC就是采用這種技術(shù),,屏幕傳輸像看電影一樣非常流暢且cpu占用率為0%~1%,。
傳統(tǒng)截取屏幕采用api hook方式,調(diào)用bitblt截取的是ddb位圖,,要用getbits轉(zhuǎn)換為dib格式的位圖,,不僅增加截屏的時(shí)間消耗,而且會(huì)截取未變化的區(qū)域,,產(chǎn)生冗余數(shù)據(jù),。
而使用Mirror Driver的圖形驅(qū)動(dòng)技術(shù) ,應(yīng)用程序調(diào)用win32 gdi 函數(shù)進(jìn)行圖形輸出請(qǐng)求,,這個(gè)請(qǐng)求通過(guò)核心模式gdi發(fā)送,。核心模式gdi把這些請(qǐng)求發(fā)送到相應(yīng)的圖形驅(qū)動(dòng)程序,。如,顯示器驅(qū)動(dòng)程序,,通信流程圖如下:
使用這種截屏技術(shù)較API Hook截屏方式的優(yōu)越性在于:
1,。驅(qū)動(dòng)截屏技術(shù)是一種標(biāo)準(zhǔn)技術(shù),為微軟公司所推薦,,而API Hook截屏是一種非標(biāo)準(zhǔn)的技術(shù),,不為微軟公司所推薦。
2,。API Hook技術(shù)在實(shí)際截屏?xí)r,,采用API函數(shù)實(shí)現(xiàn),截取DDB位圖,,必須經(jīng)過(guò)一次DDB到DIB的轉(zhuǎn)換,;而驅(qū)動(dòng)技術(shù)直接從其管理的DIB位圖(表面)中將截取區(qū)域的圖形數(shù)據(jù)拷貝到應(yīng)用程序,顯著的降低了一次截屏的時(shí)間消耗,。
3,。如果屏幕圖形小區(qū)域范圍變化較快,屏幕變化區(qū)域矩形坐標(biāo)R1,、R2,、R3……、Rn相繼到達(dá),,由于一次截屏?xí)r間消耗降低,,區(qū)域矩形坐標(biāo)疊加的概率變小,這樣屏幕變化區(qū)域及時(shí)的得到了處理,,不僅增加了連續(xù)性,,而且產(chǎn)生的數(shù)據(jù)量很小。
所以無(wú)論是做遠(yuǎn)程桌面還是屏幕錄制,,基于MirrorDriver的屏幕截取將會(huì)是一個(gè)不錯(cuò)的選擇,,無(wú)論從性能,占用資源的大小來(lái)說(shuō)都要優(yōu)于API Hook的截屏方式,。
Mirror Dirver 技術(shù)涉及核心圖形驅(qū)動(dòng)的編寫(xiě),,實(shí)現(xiàn)上較為復(fù)雜,網(wǎng)絡(luò)上也沒(méi)有開(kāi)源的driver ,,但可以利用免費(fèi)的driver,,如Tight VNC中提供的Mirage Driver,UtralVNC中提供的Winvnc video hook driver,,下載安裝程序安裝即可,。安裝后可以在設(shè)備管理器中看到如下所示的圖:
調(diào)用Mirror Driver 的代碼可以從VNC源代碼中去挖掘,VNC中都封裝好了與Mirror Driver 的調(diào)用關(guān)系,,我們只用關(guān)心VNC中封裝的一個(gè)指向變化矩形的RECT結(jié)構(gòu)和一個(gè)指向變化數(shù)據(jù)的指針,。
在我們的屏幕傳輸模塊中,,服務(wù)端發(fā)送一個(gè)RECT結(jié)構(gòu),發(fā)送一個(gè)由數(shù)據(jù)指針指向的byte[]數(shù)組,,客戶(hù)端接收到數(shù)據(jù),,按RECT結(jié)構(gòu)中的4個(gè)點(diǎn)把byte[]數(shù)組Paint到Picture控件中,然后再重復(fù)上面的操作,,即可發(fā)送屏幕的變化部分,。
雖然使用Mirror Driver 技術(shù)cpu占用率小、網(wǎng)絡(luò)傳輸數(shù)據(jù)量小,,但是需要安裝驅(qū)動(dòng)程序,,每次截屏的時(shí)候屏幕會(huì)出現(xiàn)明顯的刷新,且有時(shí)處理不當(dāng)會(huì)出現(xiàn)BSOD(Blue Screen Of Death),。對(duì)于一個(gè)具有高隱蔽性的木馬來(lái)說(shuō)Mirror Driver技術(shù)來(lái)做屏幕傳輸是不適合的,。
**GDI下 的算法方式**:
算法是程序的靈魂,在這里可以看到算法在優(yōu)化屏幕傳輸上所起的作用,。應(yīng)用得比較好的算法分為分塊異或屏傳,,固定塊隔行掃描,動(dòng)態(tài)分塊隔行掃描,。
分塊異或屏傳:
原理:前后保存兩次bmp位圖,,把屏幕分成若干塊(局域網(wǎng)一般為4~6塊,廣域網(wǎng)一般為8~32塊)并編號(hào),,前后兩副位圖分別按對(duì)應(yīng)編號(hào)塊逐個(gè)像素點(diǎn)做異或(XOR)操作,,若異或后的結(jié)果全是零,證明兩個(gè)被分塊的位圖相等,不為零則兩個(gè)被分塊的位圖不相等,,不相等則把異或的結(jié)果進(jìn)行壓縮,,并發(fā)送。
每個(gè)塊處理完后,,則把后一副圖像記為前一副圖像,再保存一副bmp位圖作為后一副圖像,,再執(zhí)行前面的分塊異或發(fā)送操作,。
注意這里為什么要每個(gè)像素點(diǎn)做異或操作,再壓縮發(fā)送,,而不是每塊圖像做CRC32,,不相同則直接發(fā)送圖像呢?
因?yàn)槠聊簧系淖兓诤芏痰臅r(shí)間內(nèi),,往往都是小范圍的變化,,這就意味著有很多相同的像素點(diǎn),那么兩塊圖像異或的結(jié)果就會(huì)有很多的零,。
這么多的零經(jīng)過(guò)壓縮算法壓縮,,數(shù)據(jù)量會(huì)減少很多,。比如 經(jīng)過(guò)壓縮后,10000001 可以表示為10*61的形式,,當(dāng)然壓縮算法不只是壓縮連續(xù)的0或1,。
經(jīng)過(guò)以上算法優(yōu)化傳輸屏幕變化,實(shí)際的網(wǎng)絡(luò)傳輸量會(huì)變得很小,,屏幕傳輸流暢,,著名的灰鴿子屏幕傳輸就采用此算法。
固定塊隔行掃描,,動(dòng)態(tài)分塊隔行掃描:
Radmin影子遠(yuǎn)程控制系統(tǒng)相信很多人都知道,,用到4899端口,早期系統(tǒng)管理員用來(lái)管理遠(yuǎn)程的計(jì)算機(jī),。速度很快,,特別是屏幕傳輸,可以和3389遠(yuǎn)程桌面媲美,。
Radmin是俄羅斯人編寫(xiě)的收費(fèi)軟件,,沒(méi)有開(kāi)源的代碼,隔行掃描算法最初是受到其反匯編代碼啟發(fā)得來(lái)的,。
原理:前后保存兩次bmp位圖,,把屏幕分成若干塊并編號(hào),前后兩副位圖分別按對(duì)應(yīng)編號(hào)塊對(duì)比,,對(duì)比的方法是,,隔若干行(一般是10行)對(duì)比前后兩幅圖像的一行中的像素點(diǎn)是否相同,若不同則壓縮發(fā)送當(dāng)前塊中的圖像。
因?yàn)槭歉羧舾尚袑?duì)比一行,,所以叫隔行掃描,。只用掃描少數(shù)的行,就可以判斷屏幕變化的部分,,速度是很快的,。
接著,有人提出這樣每次只能掃描那些固定的行,,所以每次重新掃描時(shí)會(huì)對(duì)上次掃描的行編號(hào)+n行,,這樣就避免了總是掃描相同的行,稱(chēng)為“百葉窗”技術(shù),。
隨后算法經(jīng)過(guò)優(yōu)化,,出現(xiàn)了“動(dòng)態(tài)分塊隔行掃描”算法,應(yīng)用此算法,,速度更快,。
原理:在固定塊隔行掃描的基礎(chǔ)上,改“把屏幕分成若干塊”為“由屏幕的變化區(qū)域動(dòng)態(tài)確定要發(fā)送的矩形”,。當(dāng)掃描到有不相同的行時(shí),,由一個(gè)恒定的值確定變化矩形的寬,,然后在這個(gè)寬度范圍內(nèi)向左和向右對(duì)比像素點(diǎn),確定變化矩形的長(zhǎng),,再把矩形的坐標(biāo)點(diǎn)和矩形的圖像(byte[]數(shù)組)保存到發(fā)送隊(duì)列,,接著由之前 掃描行的編號(hào)+矩形的寬度所得的行編號(hào)開(kāi)始掃描,重復(fù)上面的操作,,掃描到屏幕的最后一行為止,,最后服務(wù)端傳輸發(fā)送隊(duì)列的數(shù)據(jù)到客戶(hù)端??蛻?hù)端按矩形的坐標(biāo)和數(shù)據(jù),,把矩形paint到picture控件上。
動(dòng)態(tài)分塊隔行掃描示意圖:
近期,,在動(dòng)態(tài)隔行掃描算法的基礎(chǔ)上,,又發(fā)展起來(lái)“熱點(diǎn)追蹤”的思想,即跟蹤鼠標(biāo)的操作,,因?yàn)檫@是最容易引起變化的地方,,因?yàn)槭髽?biāo)的移動(dòng),鼠標(biāo)的點(diǎn)擊,,屏幕都會(huì)變化,。
在我開(kāi)發(fā)的程序中,使用了“動(dòng)態(tài)分塊隔行掃描+熱點(diǎn)追蹤”的算法,,速度流暢,,cpu占用率小。
總結(jié):在這里可以看到算法的力量了,,為什么會(huì)創(chuàng)造出這么好的算法呢,? 其實(shí)萬(wàn)變不離其中,以上算法都衍生于《計(jì)算機(jī)程序設(shè)計(jì)的藝術(shù)》一書(shū)中所介紹的算法,?!皠?dòng)態(tài)分塊”的思想來(lái)自“動(dòng)態(tài)規(guī)劃”算法,“固定分塊”的 思想來(lái)自“分治法”,,分而治之的思想,。
壓縮解壓縮:在屏幕傳輸中壓縮算法的好壞,直接影響屏幕傳輸?shù)牧鲿扯群蚦pu的占用率,。壓縮圖像的算法很多,JPEG,、Huffman,、RLE(Run Length Encode)、LZW等,,TSP木馬選用的LZW壓縮算法,。