WINVNC源码阅读(七)

VNC协议分析 

简介 
VNC(Virtual Network Computing)是基于RFB(Remote Frame Buffer)协议进行通信的,是一个基于平台无关的简单显示协议的超级瘦客户系统,
由Cambridge的AT&T实验室设计开发的。
vnc的缺省端口是main:5900(C/S)和http:5800(B/S)端口。
RFB (远程帧缓存) 是一个远程图形用户的简单协议,因为它工作在帧缓存级别上,所以它可以应用于所有的窗口系统,例如:X11,Windows和Mac系统。
远程终端用户使用机器(比如显示器、键盘、鼠标)的叫做RFB客户端,提供帧缓存变化的被称为RFB服务器。
RFB是基于tcp的一个应用层协议。
   WINVNC源码阅读(七)_第1张图片
RFB 是真正意义上的“瘦客机”协议。RFB协议设计的重点在于减少对客户端的硬件需求。这样客户端就可以运行在许多不同的硬件上,客户机的任务实现上就会尽量的简单。
RFB协议对于客户端是无状态的。也就是说:如果客户端从服务器端断开,那么如果它重新连接相同的服务器,客户端的状态会被保存。甚至,一个不同的客户端可以用来连接相同的RFB服务器。而在新的客户端已经能够获得与前一个客户端相同的用户状态。因此,用户的应用接口变的非常便捷。
只要合适的网络连接存在,那么用户就可以使用自己的应用程序,并且这些应用会一直保存,即使在不同的接入点也不会变化。这样无论在哪,系统都会给用户提供一个熟悉、独特的计算环境。
显示协议 
显示协议是建立在“把像素数据放在一个由x,y 定位的方框内”这单一图形基础之上的。
乍一看上去,把这么多的用户接口组件绘制出来是非常低效的方法。但是,允许不同的像素数据编码方式,使得我们在处理不同的参数(如:网络带宽,客户端的绘制速度,服务器处理速度)有了很大程度的灵活性。
通过矩形的序列来完成帧缓存的更新。一次更新代表着从一个可用帧缓存状态转换到另一个可用,因此有点和视频的桢类似。尽管矩形的更新一般是分开的,但是并不是必须的。
显示协议的更新部分是由客户端通过命令驱动的。也就是说,更新只是在服务器端响应客户端的请求时发生的。这样就让协议更新质量是可变的。客户端/网络越慢,更新速度也就越慢。对于一些应用来说,相同区域的更新是连续不断的。如果用一个慢的客户端,那么帧缓存的缓存状态是可以被忽略的。这样也可以减少对客户端网络速度和绘制速度的要求。
输入协议 
输入协议是基于标准工作站的键盘和鼠标等设备的连接协议。
输入事件就是通过把客户端的输入发送到服务器端。
这些输入事件也可以通过非标准的I /O 设备来综合。
例如,手写笔引擎可能产生一个键盘事件。
像素数据的表示 
初始的交互涉及到RFB客户端和服务器之间传输像素数据格式和编码方式的协调。这种协调被设计的让客户端的工作尽量简单。而设计的底线是:服务器必须按照客户端的要求格式来提供像素数据。如果客户端可以同样的处理多种数据格式或编码格式,那么一般会选择服务器端易于生成的格式。
像素格式涉及如何通过像素值来实现不同颜色的重现。最常用的一般像素格式是24 位或16 位的“真彩色”,它通过位来直接实现像素值到红、绿、蓝亮度的转换。8 位“颜色映射”可以任意映射像素值到RGB亮度的转换。
编码指一个矩形的像素数据如何通过网线传输。每个像素数据的矩形都加上了一个头,给定矩形在屏幕上的X、Y坐标、矩形的宽和高,以及指定的编码类型。而后数据本身就是采用这种特定的编码方式。
数据本身遵循特定的编码。目前的编码方式主要有Raw、CopyRect、RRE、Hextile 和ZRLE.在实际应用中我们一般使用ZRLE、Hextile 和CopyRect,因为它们提供了典型桌面的最好压缩。其他可能的编码方式还包括,用于静态图片的JPEG和用于动态图像有效传输的MPEG。
协议可以通过增加新的编码方式来进行扩展。
协议扩展 
协议可以通过以下方式进行扩展:
新的编码方式
一种新的协议可以通过与现存的客户端和服务端进行相关兼容的添加。因为现存的服务器将会忽略它们所不支持的新编码方式。所以客户端通过新的编码方式进行请求也就不会有结果返回。
伪编码方式
除了真正的编码方式,客户端也可以请求“伪编码”通告服务器,它支持某一协议的扩展。服务器如果不支持这种扩展,那么它将忽略。值得注意的是:客户端必须先假设服务器端不支持这种扩展,直到它获得服务器端支持的确认。
新的安全方式
添加一个新型的安全方式会带来无限的灵活性,它通过修改协议的一些行为,但是并没有牺牲现存客户端和服务器端的兼容性。客户端和服务器端可以通过协议好的安全方式进行交流,当然并不一定与RFB协议类似。
无论如何你都不应使用不同的版本号。
RFB协议的版本是由RealVNC公司来制定的。如果你使用一个不同的协议版本可能与RFB/VNC不兼容,要保证协议的兼容性,请联系RealVNC公司。这样会减少在编码方式和安全类型上的冲突。
协议消息 
RFB协议可以进行可靠的传输,如字节流或基于消息的。和大多数协议一样,它也是通过TCP /IP协议簇连接。协议由三步完成连接。首先是握手报文,目的是对协议版本和加密方式进行协商。第二步是初始化报文,主要用于客户和服务器的初始化消息。最后就是正常协议的交互,客户端可以按需发送消息,然后可以获得服务器的回复。
所有的消息以消息类型开始,接下来是特定的消息数据。
协议消息描述的基本类型有:U8、U16、U32、S8、S16、S32。
U表示无符号整数,S表示有符号整数。
所有字节整数(除了像素值本身)遵从big endian顺序。
big endian或者little endian跟cpu有关,从而影响整数在内存中的排列顺序。big endian是高字节在前,little endian是低字节在前,网络字节序一般是big-endian。
PIXEL代表一个像素值bytesPerPixel字节,8XbytesPerPixel = bits-per-pixel。
协议流程 
   WINVNC源码阅读(七)_第2张图片
消息说明 
握手消息 
1、vnc服务器发送所能够支持的最高RFB协议版本号给客户端,比如:“RFB 003.006\n”,即版本号为3.6,版本号固定格式为×××.×××,不足部分前面补零。
 
2、客户端回复将要使用的版本号,格式如上。客户端的版本号必须小于或等于服务器版本号。这样服务器可以实现向后兼容。
3、目前发布的协议版本主要有3.3、3.7、3.8(3.5版本被报告存在问题),最高版本号为4.0。
协商安全类型 
(一)v3.7以上版本安全类型
服务器发送所支持的安全类型列表
如果客户端能支持服务器的某一安全类型,那么客户端就会发送一个字节来确认连接
的安全类型:
 
如果安全类型数是0,那么连接失败(例如服务器不支持客户请求版本号),这样就
会有字符串来描述失败原因:
 
服务器在发送原因字串后,就会关闭连接。
(二)3.7以下版本(以vnc认证为例)
1、服务器发送一个无符号的32位整数标识一个安全类型(与认证有关)。
 
安全类型:
 
其他认证类型:
 
说明:
①0,连接失败(例如服务器不支持客户请求版本号),这样就会有字符串来描述失败原因:
 
服务器发送完reason-string就关闭连接。
②NONE,不需要认证(不要输密码),协议数据将被使用明文发送。
V3.8 以上版本, 还会带有安全结果的消息。
V3.3 和 3.7 协议直接进入初始报文.
③VNC认证,协议数据将采用明文发送,服务器发送一个16 字节的随机数。
 
客户端使用DES对验证进行加密,使用用户密码作为密钥,把16 字节的回复返回到服务器。
 
随之而来的就是安全结果消息。
2、服务器发送16位随机数。
3、客户端使用DES对验证进行加密,使用用户密码作为密钥,把加密后的16字节返回给服务器。
4、服务器对安全认证进行确认,返回值为无符号32位整数,如果为0则表示成功,1表示失败。如果不成功,服务器直接关闭连接。
 
V3.8 以上版本 如果不成功,就会有字符串来描述失败原因,并关闭连接。
对于V3.8以下,如果不成功,服务器直接关闭连接。
初始化消息 
1、客户端发送一个字节的初始化消息。
 
如果允许服务器其他客户继续连接,那么共享标志应该是非零(真)。否则,服务器将
断开其他客户的连接。
2、服务器发送初始化消息,主要告知客户端服务器的帧缓存的高、宽、象素格式和桌面相关的名称。
 
这个跟实现有关,有些实现是先发送24个字节,然后再发送桌面名字字符串。名称字符串格式如:sh-yinghua -1 ( 192.168.70.69 )。
帧缓存宽度一般为水平分辨率的大小,帧缓存高度一般是垂直分辨率的大小,比如1024×768等。
象素格式主要包括以下段:
 
服务器象素定义服务器本来的象素格式,这种象素格式会被一直使用,除非客户端使
用设置象素格式消息来请求另一种象素格式。bits-per-pixel是表示每个像素值需要的位数。这个数字必须大于等于depth,而depth用来表示像素值中有用的位数。目前位每象素必须是8,16 或32——小于8 位象素不被支持。如果多字节象素被看做big-endian,那么Big-endian 标志非零。当然了,这对8 位每象素没有任何意义。
如果真彩标志非零,那么最后6 项规定如何按照象素值来确定红、绿、蓝的亮度。红的最
大值是红色的最大值(=2 ^n - 1, n 表示用在红色上的位数)。注意这个值一般在big endian
的顺序中。红色-替换表示要得到最低明显bit 所需要的替换个数。绿色最大值、绿色-替换和蓝色最大值、蓝色-替换和红色类似。要在0—红色最大值之间找一个红色值,按照以下步骤进行:
? 遵循big-endian 标志进行象素值。(例如:如果big-endian 标志为0,主机的字节顺序是big endian,然后交换)。
? 使用红色—替换将右边替换。
? 和红色最大值进行逻辑与(按照主机字节顺序)。
如果真彩标志是零,那么服务器使用的象素值不是直接由红、绿、蓝的亮度组成,但是
服务为索引到颜色图中去。颜色图中的项目是由服务器使用“设置颜色面板条目” (FixColourMapEntries)消息进行设置的。
说明:位/象素一般为显示设置的颜色质量位数。
目前的任何服务器都还不能支持FixColourMapEntries消息,只有基于X的服务器才能支持颜色映射。实际上,为了能够完全支持颜色映射,客户端大概需要能够指定特殊的、服务器不会使用的像素值。这可能会加在未来的协议版本里。
客户端到服务器的消息 
所有客户端到服务器的消息第一个字节都为消息类型,数据类型U8。
客户到服务器的消息在本文中有如下定义:
 
其余的注册消息类型有:
 
值得注意的是:如果要发送未在本文中定义的消息,那么必须得到服务器端的消息确认。
设置象素格式消息
“帧缓存更新”消息中设置什么格式的象素值如何设置。
如果客户端没有发送“设置象素格式”消息,那么服务器发送的象素值将遵循在服务器初始化消息中所包括的象素格式。
如果真彩标志是零,那么意味着使用“颜色面板”,只要客户端发送颜色面板空的消息,或者是面板项被服务器端重设,服务器可以使用设置颜色面板项目进行颜色面板的设置。
 
注:其中的象素格式如在上文中的描述。
设置编码格式
设置编码方式可以来确定服务器发送象素数据的类型。消息中编码方式的顺序是客户端
按照优先级来排列(第一个拥有最高的优先级)。服务器可能选择这种顺序,也可能不选择。
象素数据也可以使用“原始编码”如果没有具体说明。
除了基本的编码方式,客户端也可以请求“伪编码”通告服务器它支持某一种扩展协议。如果服务器不支持这种扩展,它就会忽略这种伪编码。注意:这意味着客户端在得到服务器的确认之前都要假设服务器并不支持它的扩展。
 
接下来就是编码数目个编码类型的重复
 
由编码类型决定编码格式。
请求帧缓存更新
通知服务器,客户对帧缓冲区中的某个区域感兴趣,这个区域由x坐标、y坐标、宽度和高度几个参数限定。
服务器通常对FramebufferUpdateRequest消息的响应,是发送一条FramebufferUpdate消息。
注意,可以发送一条FramebufferUpdate消息用来回复几条FramebufferUpdateRequest消息。
服务器假定客户保留了帧服务器中它感兴趣的所有部分的副本。这意味着,服务器通常只需要向客户发送增量部分的更新。
但是,如果由于某种原因,客户丢失了它所需要的一个特定区域的内容,就发送一条FramebufferUpdateRequest消息,把消息中的incremental置为0(false)。这要求服务器把指定区域的全部内容尽可能快地发送过来。这个区域的更新不会使用copy rectangle编码方式。
如果客户没有丢失它感兴趣区域的任何内容,就发送一条FramebufferUpdateRequest消息,把消息中的incremental设为非零(true)。当帧缓冲区中的指定区域发生变化时,服务器会发送一条FramebufferUpdate消息。
注意,在FramebufferUpdateRequest和FramebufferUpdate之间可能会有一段不确定长的间隔。
对于速度快的客户,它可能希望以固定频率发送增量的FramebufferUpdateRequests消息,以避免占用网络资源。
 
增量标志为0时,表示必须发送完整内容过来。
按键事件
某一个键的按下与释放。如果某一个键被按下,那么按下标志非零。释放的时候变为零。
在X Window 系统中键本身被赋值为“keysym”。
 
对于大多数键来说,“keysym”与ASCII码相对应,具体参考《The Xlib ReferenceManual》或者参考。在Linux上为/usr/include/X11/keysymdef.h。
对于大多数普通键,“keysym”和ASCII码的值是一致的(前面3个字节为0,最后一个字节为ASCII码)。其他的命令键为:
 
鼠标(指针)事件
检测指针移动或者某一个键的按下或释放。指针目前在(x坐标、y 坐标),鼠标按钮的各键采用1到8位掩码标识,0 表示松开,1 表示按下。
拿普通鼠标来说,全零表示鼠标移动,第1,2,3 分别对应左、中、右键。对于滑轮鼠标来说,滚轮向上对应第4位,滚轮向下对应第5位。
拖动操作是不断的发送左键按下的消息,并变换鼠标的坐标。
 
客户端文本剪切
客户端有新的ISO8859 - 1(Latin - 1) 文本在它的剪切缓存里,行的末尾通过新行字符(值为10)来表示。 需要无回车(值为13)。目前还没有找到传输非Latin - 1 字符集的方法。
 
服务器到客户消息 
服务器到客户消息在本文中定义如下:
 
其余注册的消息类型:
 
注意在服务器发送消息之前必须确认客户端支持相关扩展,通常在请求“伪编码”的
时候使用。
帧缓存更新
帧缓存更新是由一系列像素数据矩形而组成,这些矩形会被客户端送入它的帧缓存中。
它是对客户端帧缓存更新请求的响应。而在请求和响应之间有可能存在不确定时期。
 
随着像素数据矩形的个数,每个矩形包括以下内容:
 
后面就是特定编码的数据。
设置颜色面板条目
当像素格式使用“颜色面板”时,消息告诉客户端对应像素值如何映射为RGB亮度。
 
下面就是重复具体的色彩
 
目前对颜色映射的支持还很少甚至没有。这方面已经做了一些初步的工作,但是还没有完成。
目前,只有基于X 的服务器能够完全支持颜色映射。
响铃
如果有响铃事件,就在客户端上响铃。
 
服务器剪切文本
如果服务器的剪切板有新内容,服务器主动发送该消息给客户端
 
编码格式 
本文的编码类型
 
其他编码类型
 
原始编码( Raw 编码)
即采用原始的像素数据,而不进行任何的加工处理。在这种情况下,对于一个宽度乘以高度(即面积)为N的矩形,数据就由N个像素值组成,这些值表示按照扫描线顺序从左到右排列的每个像素。很明显,这种编码方式是最简单的,也是效率最低的。
RFB要求所有的客户都必须能够处理这种原始编码的数据,并且在客户没有特别指定需要某种编码方式的时候,RFB服务器就默认生成原始编码。
 
复制矩形编码( CopyRect编码)
CopyRect 编码方式对于客户端在某些已经有了相同的象素数据的时候是非常简单和有效的。这种编码方式在网络中表现为x,y 坐标。让客户端知道去拷贝那一个矩形的象素数据。它可以应用于很多种情况。最明显的就是当用户在屏幕上移动某一个窗口的时候,还有在窗口内容滚动的时候。在优化画的时候不是很明显,一个比较智能的服务器可能只会发送一次,因为它知道在客户端的帧缓存里已经存在了。
复制矩形编码并不是完全独立地发送所有的数据矩形,而是对于像素值完全相同的一组矩形,只发送第一个矩形全部数据,随后的矩形则只需要发送左上角X、Y坐标。实际上,复制矩形编码主要指的就是随后的这一系列X、Y坐标,而对于第一个矩形具体采用何种编码类型并没有限制,仅仅需要知道第一个矩形在帧缓冲区中的位置,以便于完成复制操作。因此,往往是把复制矩形编码和其它针对某一个矩形的编码类型结合使用。
接下来使用CopyRect 编码方式发送相同的式样。
 
二维行程编码( rise-and-run-length,RRE)
RRE表示提升和运行长度,正如它名字暗示的那样,它实质上表示二维向量的运行长度编码。RRE把矩形编码成可以被客户机的图形引擎翻译的格式。RRE不适合复杂的桌面,但在一些情况下比较有用。
RRE的思想就是把像素矩形的数据分成一些子区域,和一些压缩原始区域的单元。最近最佳的分区方式一般是比较容易计算的。
编码是由像素值组成的,Vb(基本上是在矩形中最常用的像素值)和一个计数N,紧接着是N的子矩形列表,这些里面由数组组成,(x,y)是对应子矩形的坐标,表示子矩形上-左的坐标值,(w,h) 则表示子矩形的宽高。客户端可以通过绘制使用背景像素数据值,然后再根据子矩形来绘制原始矩形。
二维行程编码本质上是对行程编码的一个二维模拟,而其压缩度可以保证与行程编码相同甚至更好。而且更重要的是,采用RRE编码的矩形被传送到客户端以后,可以立即有效地被最简单的图形引擎所还原。
在传输中,数据以下面的头开始描述:
 
后面跟随重复的子矩形结构:
 
CoRRE 编码
CoRRE是RRE的变体,它把发送的最大矩形限制在255×255个像素以内,用一个字节就能表示子矩形的维度。如果服务器想要发送一个超出限制的矩形,则只要把它划分成几个更小的RFB矩形即可。“对于通常的桌面,这样的方式具有比RRE更好的压缩度”。
实际上,如果进一步限制矩形的大小,就能够获得最好的压缩度。“矩形的最大值越小,决策的尺度就越好”。但是,如果把矩形的最大值限制得太小,就增加了矩形的数量,而由于每个RFB矩形都会有一定的开销,结果反而会使压缩度变差。所以应该选择一个比较恰当的数字。在目前的实现中,采用的最大值为48×48。
Hextile 编码
Hextile 是RRE编码的变种,矩形被分割成16×16 小片,允许每个小片的维数为4位,
总共16 位。
把原始矩形划分成小块是预定义的,这意味着每个块的位置与大小不需要明确地指定。
矩形被分割的小片从上开始,遵守自左到右,自顶向下的顺序。小片的编码内容按照预定的顺序进行编码。如果整个矩形的宽度不是16 的整数倍,那么每行最后的小片也相应减少。高度也类似。
每个小片可以使用raw 编码,也可以是RRE编码的变种,用一个类型字节来进行说明即可。每个小片有一个背景像素值。但是,如果小片的背景像素值和前一个小片相同,那么就不需要明确定义。如果小片的子矩形有相同的像素值,那么前景像素值就可以只定义一次。和背景像素值一样,前景像素值也可以通过前一个小片获得。
因此由小片组成的数据是按照顺序进行编码的。每一个小片以子编码类型的字节开始。它是位数的掩码组成。
 
如果Raw 位被设置,那么其余的位就无效;接着是宽X高像素值(宽和高是小片的宽
高)。否则其他的位就有效。
背景定义-如果设置,那么像素值就会跟着小片的背景色:
 
在矩形中的第一片非Raw 小片必须设置这一位,如果不设置,那么它的背景就会和上
一片相同。
前景定义-如果设置,那么像素值就会定义小片中所有子矩形的前景色。
 
如果这一位被设置,那么子矩形着色位必须为0。
任意子矩形-如果设置,那么一个字节包含着子矩形的个数。
 
如果这一位不设置,那么就不会有子矩形。(例如,整个小片就是背景颜色)
子矩形着色-如果设置,那么任意子矩形的像素值的优先级都高于子矩形的颜色定义,
因此子矩形是:
 
如果不设置,所有子矩形都是前景色的颜色,如果前景定义没有设置,那么前景色和
前一个片的相同。子矩形就是:
 
每一个子矩形的位置和大小都是使用两位进行定义,x - and - y - position 和width -
And - height。最重要的四位x - an d - y - posi tion 定义X的位置,不重要的定义Y位置。最
重要的四位width - and - height 定义宽度- 1,不重要的定义高度- 1。
ZRLE 编码
ZRLE(Zlib Run - Length Encoding),它结合了zlib 压缩,片技术、调色板和运行长度
编码。在传输中,矩形以4 字节长度区域开始,紧接着是zlib 压缩的数据,一个单一的
zlib“流”对象被用在RFB协议的连接上,因此ZRLE矩形必须严格的按照顺序进行编码和
译码。
 
zlibData 在没有压缩之前,代表了由64x64 像素组成的从左到右,从高到低的顺序
的片,和hextile 编码有点类似。如果整个矩形的宽度不是64 的整数倍,那么每行最后的
小片也相应减少。高度也类似。
ZRLE编码利用了一种新的压缩像素CPIXEL(Compres se d PIXEL)。这个和PIXEL有着
相同的像素格式,除了真彩标志是非零,位每像素是32,色深不大于24。所有的位组成红,
绿和蓝的亮度填充最不重要的或最重要的三字节。如果CPIXEL只有3 字节长,并且包含有
合适的最不重要或最重要3 字节。那么bytesPerCPixel 就是CPIXEL的字节数。
每片都是以子编码类型字节开始,如果片被使用运行长度编码,那么本字节的最高位
就会被设置。其余7 位表示绘图样式-零表示没有样式,1 表示片为单色,2 - 127 表示对应
的样式。可能的子编码值如下:
0 - Raw 像素数据 宽X高像素值(宽和高为对应片的宽和高,对应像素值如下:
 
2 - 16 -打包的样式类型。对应像素值是由palet teSize(=子编码)像素值,打包像素值组
成,每个打包像素值表示为一位区域服从样式索引(0 表示第一个条目),对应
palet teSize 2,1 位被使用,palet teSize 3,4 有两位被使用,从5 - 16 均有4 位区域被
使用。位的区域被打包成字节,最重要的位表示最左边像素。因为片并不是8,4,2 像素宽的
乘积,所以填充位被用来按照字节数排列每一个行。
 
m 表示打包像素的字节数。对于palet teSize 2 就是floor((width + 7) / 8) x height,
相应3,4 就是floor((width + 3) / 4) x height,而5 - 16 就是floor((width + 1) / 2)x
height。
17 - 127 未使用(对于palet te RLE并没有什么优势)。
128 -简单RLE 它由一些不断重复的执行组成,一直到片结束。执行可能从一行的结束到另一行的开始。每一次运行是通过一个像素值和像素值长度来表示的。长度一般为1 个或多个字节。经过计算多于所有字节总和+ 1 作为长度。除了255 任何字节值都隐含最后的字节。例如长度1 表示为[0],255 表示为[254],256 表示为[255,0],257 表示为[255,1],510 表示为[255,254],511 表示为[255,255,0]等等。
 
129 -未使用
130 - 255 调色RLE。调色紧跟其后,由palet teSize = (subencoding - 128) 像素值组成:
 
接下来就合简单RLE相似,一些不断重复的执行组成,一直到片结束。执行长度通过
调色板索引来表示。
 
如果执行长度使用多于一位来表示调色板索引,并且最高位被设置。那么就会带有执行长度。
 
伪编码 
指针/鼠标伪编码
如果客户端请求指针/鼠标伪编码,那么就是说它有能力进行本地绘制鼠标。这样就可以明显改善传输性能。服务器通过发送带有伪鼠标编码的伪矩形来设置鼠标的形状作为更新的一部分。伪矩形的x 和y 表示鼠标的热点,宽和高表示用像素来表示鼠标的宽和高。包含宽X高像素值的数据带有位掩码。位掩码是由从左到右,从上到下的扫描线组成,而每一扫描线被填充为floor((width +7) / 8)。对应每一字节最重要的位表示最左边像素,对应1 位表示相应指针的像素是正确的。
 
桌面大小伪编码
如果客户端请求桌面大小伪编码,那么就是说它能处理帧缓存宽/高的改变。服务器通过发送带有桌面大小伪编码的伪矩形作为上一个矩形来完成一次更新。伪矩形的x 和y 被忽略,而宽和高表示帧缓存新的宽和高。没有其他的数据与伪矩形有关。
协议漏洞及解决方法 
RealVNC VNC Server采用的RFB(远程帧缓冲区)协议允许客户端与服务端协商合适的认证方法,协议的实现上存在设计错误,远程攻击者可以绕过认证无需口令实现对服务器的访问。
具体操作细节如下:
1) 服务端发送其版本“RFB 003.008\n”
2) 客户端回复其版本“RFB 003.008\n”
3) 服务端发送1个字节,等于所提供安全类型的数量
3a) 服务端发送字节数组说明所提供的安全类型
4) 客户端回复1个字节,从3a的数组中选择安全类型
5) 如果需要的话执行握手,然后是服务端的“0000”
RealVNC 4.1.1或之前版本在实现RFB 003.008协议时没有检查判断在上面第4步中客户端所发送的字节是否为服务器在3a步中所提供的,因此认证就从服务端转移到了客户端。攻击者可以强制客户端请求“Type 1 - None”为安全类型,无需口令字段便可以访问服务器。
危害:远程攻击者可以绕过认证无需口令实现对服务器的访问。
解决方法:检查客户端请求的安全类型是否为服务器支持的类型之一,否则断开连接,或者禁止无认证的安全类型

你可能感兴趣的:(WINVNC源码阅读(七))