VC++分析数据包实现Telnet协议分析


Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。
简介


  它最初是由ARPANET开发的,但是现在它主要用于Internet会话。它的基本功能是,允许用户登录进入远程主机系统。起初,它只是让用户的本地计算机与远程计算机连接,从而成为远程主机的一个终端。它的一些较新的版本在本地执行更多的处理,于是可以提供更好的响应,并且减少了通过链路发送到远程主机的信息数量。
  Telnet的应用不仅方便了我们进行远程登录,也给黑客们提供了又一种入侵手段和后门,但无论如何,在你尽情享受Telnet所带给你的便捷的同时,你是否真正的了解Telnet呢?


远程登录


  Telnet服务虽然也属于客户机/服务器模型的服务,但它更大的意义在于实现了基于Telnet协议的远程登录(远程交互式计算),那么就让我们来认识一下远程登录。
基本概念
  先来看看什么叫登录:分时系统允许多个用户同时使用一台计算机,为了保证系统的安全和记账方便,系统要求每个用户有单独的帐号作为登录标识,系统还为每个用户指定了一个口令。用户在使用该系统之前要输入标识和口令,这个过程被称为'登录'。 远程登录是指用户使用Telnet命令,使自己的计算机暂时成为远程主机的一个仿真终端的过程。仿真终端等效于一个非智能的机器,它只负责把用户输入的每个字符传递给主机,再将主机输出的每个信息回显在屏幕上。
产生和发展
  我们可以先构想一个提供远程文字编辑的服务,这个服务的实现需要一个接受编辑文件请求和数据的服务器以及一个发送此请求的客户机。客户机将建立一个从本地机到服务器的TCP连接,当然这需要服务器的应答,然后向服务器发送键入的信息(文件编辑信息),并读取从服务器返回的输出。以上便是一个标准而普通的客户机/服务器模型的服务。
  似乎有了客户机/服务器模型的服务,一切远程问题都可以解决了。然而实际并非你想象的那样简单,如果我们仅需要远程编辑文件,那么刚才所构想的服务完全可以胜任,但假如我们的要求并不是这么简单,我们还想实现远程用户管理,远程数据录入,远程系统维护,想实现一切可以在远程主机上实现的操作,那么我们将需要大量专用的服务器程序并为每一个可计算服务都使用一个服务器进程,随之而来的问题是:远程机器会很快对服务器进程应接不暇,并淹没在进程的海洋里(我们在这里排除最专业化的远程机器)。
  那么有没有办法解决呢?当然有,我们可以用远程登录来解决这一切。我们允许用户在远地机器上建立一个登录会话,然后通过执行命令来实现更一般的服务,就像在本地操作一样。这样,我们便可以访问远地系统上所有可用的命令,并且系统设计员不需提供多个专用地服务器程序。
  问题发展到这里好像前途一片光明了,用远程登录总应该解决问题了吧,但要实现远程登录并不简单。不考虑网络设计的计算机系统期望用户只从直接相连的键盘和显示器上登录,在这种机器上增加远程登录功能需要修改机器的操作系统,这是极其艰巨也是我们尽量避免的。因此我们应该集中力量构造远程登录服务器软件,虽然这样也是比较困难的。为什么说这样做也比较困难呢?
  举个例子来说:一般,操作系统会为一些特殊按键分配特殊的含义,比如本地系统将'Ctrl+C'解释为:'终止当前运行的命令进程'。但假设我们已经运行了远程登录服务器软件,'Ctrl+C'也有可能无法被传送到远地机器,如果客户机真的将'Ctrl+C'传到了远地机器,那么'Ctrl+C'这个命令有可能不能终止本地的进程,也就是说在这里很可能会产生混乱。而且这仅仅是遇到的难题之一。
  但尽管有技术上的困难,系统编程人员还是设法构造了能够应用于大多数操作系统的远程登录服务器软件,并构造了充当客户机的应用软件。通常,客户机软件取消了除一个键以外的所有键的本地解释,并将这些本地解释相应的转换成远地解释,这就使得客户机软件与远地机器的交互,就如同坐在远程主机面前一样,从而避免了上述所提到的混乱。而那个唯一例外的键,可以使用户回到本地环境。
  将远程登录服务器设计为应用级软件,还有另一个要求,那就是需要操作系统提供对伪终端(pseudo terminal)的支持。我们用伪终端描述操作系统的入口点,它允许像Telnet服务器一样的程序向操作系统传送字符,并且使得字符像是来自本地键盘一样。只有使用这样的操作系统,才能将远程登录服务器设计为应用级软件(比如Telnet服务器软件),否则,本地操作系统和远地系统传送将不能识别从对方传送过来的信息(因为它们仅能识别从本地键盘所键入的信息),远程登录将宣告失败。
  将远程登陆服务器设计为应用级软件虽然有其显著的优点:比将代码嵌入操作系统更易修改和控制服务器。但其也有效率不高的缺点(后面的内容将会给予解释),好在用户键入信息的速率不高,这种设计还是可以接受的。
工作过程
  使用Telnet协议进行远程登录时需要满足以下条件:在本地计算机上必须装有包含Telnet协议的客户程序;必须知道远程主机的Ip地址或域名;必须知道登录标识与口令。 
  Telnet远程登录服务分为以下4个过程: 
  1)本地与远程主机建立连接。该过程实际上是建立一个TCP连接,用户必须知道远程主机的Ip地址或域名;
  2)将本地终端上输入的用户名和口令及以后输入的任何命令或字符以NVT(Net Virtual Terminal)格式传送到远程主机。该过程实际上是从本地主机向远程主机发送一个IP数据包; 
  3)将远程主机输出的NVT格式的数据转化为本地所接受的格式送回本地终端,包括输入命令回显和命令执行结果; 
  4)最后,本地终端对远程主机进行撤消连接。该过程是撤销一个TCP连接。
  上面的内容只是讨论了远程登陆最基本的东西,其中的复杂和编程人员的艰辛是我们难以想象的,不知道你在舒服的使用Telnet的同时,是否想到了这些!


TeInet协议


  我们知道Telnet服务器软件是我们最常用的远程登录服务器软件,是一种典型的客户机/服务器模型的服务,它应用Telnet协议来工作。那么,什么是Telnet协议?它都具备哪些特点呢?
1、 基本内容
  Telnet是位于OSI模型的第7层---应用层上的一种协议,是一个通过创建虚拟终端提供连接到远程主机终端仿真的TCP/IP协议。这一协议需要通过用户名和口令进行认证,是Internet远程登陆服务的标准协议。应用Telnet协议能够把本地用户所使用的计算机变成远程主机系统的一个终端。它提供了三种基本服务: 
  1)Telnet定义一个网络虚拟终端为远程系统提供一个标准接口。客户机程序不必详细了解远程系统,他们只需构造使用标准接口的程序; 
  2)Telnet包括一个允许客户机和服务器协商选项的机制,而且它还提供一组标准选项; . 
  3)Telnet对称处理连接的两端,即Telnet不强迫客户机从键盘输入,也不强迫客户机在屏幕上显示输出。
2 、适应异构
  为了使多个操作系统间的Telnet交互操作成为可能,就必须详细了解异构计算机和操作系统。比如,一些操作系统需要每行文本用ASCⅡ回车控制符(CR)结束,另一些系统则需要使用ASCⅡ换行符(LF),还有一些系统需要用两个字符的序列回车-换行(CR-LF);再比如,大多数操作系统为用户提供了一个中断程序运行的快捷键,但这个快捷键在各个系统中有可能不同(一些系统使用CTRL+C,而另一些系统使用ESCAPE)。如果不考虑系统间的异构性,那么在本地发出的字符或命令,传送到远地并被远程系统解释后很可能会不准确或者出现错误。因此,Telnet协议必须解决这个问题。 为了适应异构环境,Telnet协议定义了数据和命令在Internet上的传输方式,此定义被称作网络虚拟终端NVT(Net Virtual Terminal)。它的应用过程如下: 对于发送的数据:客户机软件把来自用户终端的按键和命令序列转换为NVT格式,并发送到服务器,服务器软件将收到的数据和命令,从NVT格式转换为远地系统需要的格式; 对于返回的数据:远地服务器将数据从远地机器的格式转换为NVT格式,而本地客户机将接收到的NVT格式数据再转换为本地的格式。 对于NVT格式的详细定义,有兴趣的朋友可以去查找相关资料。
3 、传送远程命令
  我们知道绝大多数操作系统都提供各种快捷键来实现相应的控制命令,当用户在本地终端键入这些快捷键的时候,本地系统将执行相应的控制命令,而不把这些快捷键作为输入。那么对于Telnet来说,它是用什么来实现控制命令的远程传送呢? Telnet同样使用NVT来定义如何从客户机将控制功能传送到服务器。我们知道ASCⅡ字符集包括95个可打印字符和33个控制码。当用户从本地键入普通字符时,NVT将按照其原始含义传送;当用户键入快捷键(组合键)时,NVT将把它转化为特殊的ASCⅡ字符在网络上传送,并在其到达远地机器后转化为相应的控制命令。
  将正常ASCⅡ字符集与控制命令区分的原因: 
  1)这种区分意味着Telnet具有更大的灵活性:它可在客户机与服务器间传送所有可能的ASCⅡ字符以及所有控制功能; 
  2)这种区分使得客户机可以无二义性的指定信令,而不会产生控制功能与普通字符的混乱。 .
4 、数据流向
  上面我们提到过将Telnet设计为应用级软件有一个缺点,那就是:效率不高。这是为什么呢?下面给出Telnet中的数据流向: 
  数据信息被用户从本地键盘键入并通过操作系统传到客户机程序,客户机程序将其处理后返回操作系统,并由操作系统经过网络传送到远程机器,远程操作系统将所接收数据传给服务器程序,并经服务器程序再次处理后返回到操作系统上的伪终端入口点,最后,远程操作系统将数据传送到用户正在运行的应用程序,这便是一次完整的输入过程;输出将按照同一通路从服务器传送到客户机。 因为每一次的输入和输出,计算机将切换进程环境好几次,这个开销是很昂贵的。还好用户的键入速率并不算高,这个缺点我们仍然能够接受。 .
5、 强制命令
  我们应该考虑到这样一种情况:假设本地用户运行了远地机器的一个无休止循环的错误命令或程序,且此命令或程序已经停止读取输入,那么操作系统的缓冲区可能因此而被占满,如果这样,远程服务器也无法再将数据写入伪终端,并且最终导致停止从TCP连接读取数据,TCP连接的缓冲区最终也会被占满,从而导致阻止数据流流入此连接。如果以上事情真的发生了,那么本地用户将失去对远程机器的控制。
  为了解决此问题,Telnet协议必须使用外带信令以便强制服务器读取一个控制命令。我们知道TCP用紧急数据机制实现外带数据信令,那么Telnet只要再附加一个被称为数据标记(date mark)的保留八位组,并通过让TCP发送已设置紧急数据比特的报文段通知服务器便可以了,携带紧急数据的报文段将绕过流量控制直接到达服务器。作为对紧急信令的相应,服务器将读取并抛弃所有数据,直到找到了一个数据标记。服务器在遇到了数据标记后将返回正常的处理过程。
  由于Telnet两端的机器和操作系统的异构性,使得Telnet不可能也不应该严格规定每一个telnet连接的详细配置,否则将大大影响Telnet的适应异构性。因此,Telnet采用选项协商机制来解决这一问题。
  Telnet选项的范围很广:一些选项扩充了大方向的功能,而一些选项制涉及一些微小细节。例如:有一个选项可以控制Telnet是在半双工还是全双工模式下工作(大方向);还有一个选项允许远地机器上的服务器决定用户终端类型(小细节)。
  Telnet选项的协商方式也很有意思,它对于每个选项的处理都是对称的,即任何一端都可以发出协商申请;任何一端都可以接受或拒绝这个申请。另外,如果一端试图协商另一端不了解的选项,接受请求的一端可简单的拒绝协商。因此,有可能将更新,更复杂的Telnet客户机服务器版本与较老的,不太复杂的版本进行交互操作。如果客户机和服务器都理解新的选项,可能会对交互有所改善。否则,它们将一起转到效率较低但可工作的方式下运行。所有的这些设计,都是为了增强适应异构性,可见Telnet的适应异构性对其的应用和发展是多么重要。
  上面讨论了一些原理方面的东西,虽然我们在Telnet的使用过程中很难接触到这一层面,但我认为了解这些是有意义的,它会给我们带来许多启示。下面让我们来看看Win2000的Telnet服务。
6 、子选项协商
  有些选项不是仅仅用“激活”或“禁止”就能够表达的。指定终端类型就是一个例子,客户进程必须发送用一个A S C I I字符串来表示终端类型。为了处理这种选项,我们必须定义子选项协商机制。
  在R F C 1 0 9 1 [ VanBokkelen 1989]中定义了如何表示终端类型这样的子选项协商机制。首先连接的某一方(通常是客户进程)发送3个字节的字符序列来请求激活该选项。
  <IAC,WILL,24>
  这里的2 4(十进制)是终端类型选项的I D号。如果收端(通常是服务器进程)同意,那么响应数据是:
  <IAC,DO,24>
  然后服务器进程再发送如下的字符串:
  <IAC,SB,24,1,IAC,SE>
  该字符串询问客户进程的终端类型。其中S B是子选项协商的起始命令标志。下一个字节的“2 4”代表这是终端类型选项的子选项(通常S B后面的选项值就是子选项所要提交的内容)。下一个字节的“ 1”表示“发送你的终端类型”。子选项协商的结束命令标志也是I A C,就像S B是起始命令标志一样。如果终端类型是i b m p c,客户进程的响应命令将是:
  第4个字节“0”代表“我的终端类型是”(在Assigned Numbers RFC文档中有正式的关于终端类型的数值定义,但是最起码在U n i x系统之间,终端类型可以用任何对方可理解的数据进行表示。只要这些数据在t e r m c a p或t e r m i n f o数据库中有定义)。在Te l n e t子选项协商过程中,终端类型用大写表示,当服务器收到该字符串后会自动转换为小写字符。


TeInet服务基本配置


  其实从应用层面上,Win2000的Telnet服务并没有什么可说的,绝大部分内容你都可以从HELP文件中得到,我在此只是把它稍微整理一下而已。 Win2000为我们提供了Telnet客户机和服务器程序:Telnet.exe是客户机程序(Client),tlntsvr.exe是服务器程序(server),同时它还为我们提供了Telnet服务器管理程序tlntadmn.exe。
  Windows 2000 默认安装了 Telnet 服务,但是并没有默认启动。下面给出HELP文件中 Telnet 服务的一部分默认设置:
  AllowTrustedDomain:是否允许域用户访问。默认值是1,允许信任域用户访问。可以改为0: 不允许域用户访问(只允许本地用户)。
  DefaultDomain:可以对与该计算机具有信任关系的任何域设置。默认值是"."。
  DefaultShell:显示 shell 安装的路径位置。默认值是:%systemroot%\System32\Cmd.exe /q /k
  MaxFailedLogins:在连接终止之前显示尝试登录失败的最大次数。默认是3。
  LoginScript:显示 Telnet 服务器登录脚本的路径位置。默认的位置就是“%systemroot%\System32\login.cmd”,你可以更改脚本内容,这样登录进Telnet的欢迎屏幕就不一样了。
  NTLM:NTLM身份验证选项。默认是2。可以有下面这些值:
  0: 不使用 NTLM 身份验证。
  1: 先尝试 NTLM 身份验证,如果失败,再使用用户名和密码。
  2: 只使用 NTLM 身份验证。
  TelnetPort:显示 telnet 服务器侦听 telnet 请求的端口。默认是:23。你也可以更改为其他端口。
  以上各项设置你可以使用tlntadmn.exe(Telnet服务器管理程序)来进行非常方便的配置,配置后需要重新启动Telnet服务。如图1


NTLM


  提到了telnet就不能不提NTLM,我想这也是让入侵者最为头痛的一件事,哪怕你获得了管理员帐号和密码,想简单通过NTLM也并非易事,况且win2000中的telnet默认仅以NTLM方式验证身份,这就让我们不得不关注NTLM这个东东,那么什么是NTLM呢?
  早期的SMB协议在网络上明文传输口令,后来出现了"LAN Manager Challenge/Response"验证机制,简称LM,它十分简单以至很容易被破解,微软随后提出了WindowsNT挑战/响应验证机制,即NTLM。现在已经有了更新的NTLMv2以及Kerberos验证体系。


NTLM工作流程是这样的


  1.客户端首先在本地加密当前用户的密码成为密码散列
  2.客户端向服务器发送自己的帐号,这个帐号是没有经过加密的,明文直接传输
  3.服务器产生一个16位的随机数字发送给客户端,作为一个 challenge(挑战)
  4.客户端再用加密后的密码散列来加密这个 challenge ,然后把这个返回给服务器。作为 response(响应)
  5.服务器把用户名、给客户端的challenge 、客户端返回的 response 这三个东西,发送域控制器
  6.域控制器用这个用户名在 SAM密码管理库中找到这个用户的密码散列,然后使用这个密码散列来加密 challenge。
  7.域控制器比较两次加密的 challenge ,如果一样,那么认证成功。
  从上面的过程我们可以看出,NTLM是以当前用户的身份向Telnet服务器发送登录请求的,而不是用你扫到的对方管理员的帐户和密码登录,显然,你的登录将会失败。举个例子来说,你家的机器名为A(本地机器),你入侵的机器名为B(远地机器),你在A上的帐户是xinxin,密码是1234,你扫到B的管理员帐号是Administrator,密码是5678,当你想Telnet到B时,NTLM将自动以当前用户的帐号和密码作为登录的凭据来进行上面的7项操作,即用xinxin和1234,而并非用你扫到的Administrator和5678,且这些都是自动完成的,根本不给你插手的机会,因此你的登录操作将失败。










#include "nids.h"   
char ascii_string[10000];   
char *char_to_ascii(char ch)   
{   
    char *string;   
    ascii_string[0] = 0;   
    string = ascii_string;   
    if (isgraph(ch))   
        *string++ = ch;   
    else if (ch == ' ')   
        *string++ = ch;   
    else if (ch == '\n' || ch == '\r')   
        *string++ = ch;   
    else   
        *string++ = '.';   
    *string = 0;   
    return ascii_string;   
}   
/*  
=======================================================================================================================  
下面是分析Telnet协议的回调函数  
=======================================================================================================================  
 */   
void telnet_protocol_callback(struct tcp_stream *telnet_connection, void **arg)   
{   
    int i;   
    char address_string[1024];   
    char content[65535];   
    char content_urgent[65535];   
    struct tuple4 ip_and_port = telnet_connection->addr;   
    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));   
    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);   
    strcat(address_string, " <---> ");   
    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));   
    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);   
    strcat(address_string, "\n");   
    switch (telnet_connection->nids_state)   
    {   
        case NIDS_JUST_EST:   
            if (telnet_connection->addr.dest == 23)   
            {   
                /* Telnet客户端和Telnet服务器端建立连接 */   
                telnet_connection->client.collect++;   
                /* Telnet客户端接收数据 */   
                telnet_connection->server.collect++;   
                /* Telnet服务器接收数据 */   
                telnet_connection->server.collect_urg++;   
                /* Telnet服务器接收紧急数据 */   
                telnet_connection->client.collect_urg++;   
                /* Telnet客户端接收紧急数据 */   
                printf("%sTELNET客户端与TELNET服务器建立连接\n", address_string);   
            }   
            return ;   
        case NIDS_CLOSE:   
            /* Telnet协议连接正常关闭 */   
            printf("--------------------------------\n");   
            printf("%sTELNET客户端与TELNET服务器连接正常关闭\n", address_string);   
            return ;   
        case NIDS_RESET:   
            /* Telnet协议连接被RST关闭 */   
            printf("--------------------------------\n");   
            printf("%sTELNET客户端与TELNET服务器连接被REST关闭\n", address_string);   
            return ;   
        case NIDS_DATA:   
            {   
                /* Telnet协议有新的数据达到 */   
                struct half_stream *hlf;   
                if (telnet_connection->server.count_new_urg)   
                {   
                    /* Telnet服务器接收到新的紧急数据 */   
                    printf("--------------------------------\n");   
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));   
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);   
                    strcat(address_string, " urgent---> ");   
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));   
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);   
                    strcat(address_string, "\n");   
                    address_string[strlen(address_string) + 1] = 0;   
                    address_string[strlen(address_string)] = telnet_connection->server.urgdata;   
                    printf("%s", address_string);   
                    return ;   
                }   
                if (telnet_connection->client.count_new_urg)   
                {   
                    /* Telnet客户端接收到新的紧急数据 */   
                    printf("--------------------------------\n");   
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));   
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);   
                    strcat(address_string, " <--- urgent ");   
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));   
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);   
                    strcat(address_string, "\n");   
                    address_string[strlen(address_string) + 1] = 0;   
                    address_string[strlen(address_string)] = telnet_connection->client.urgdata;   
                    printf("%s", address_string);   
                    return ;   
                }   
                if (telnet_connection->client.count_new)   
                {   
                    /* Telnet客户端接收到新的数据 */   
                    hlf = &telnet_connection->client;   
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));   
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);   
                    strcat(address_string, " <--- ");   
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));   
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);   
                    strcat(address_string, "\n");   
                    printf("--------------------------------\n");   
                    printf("%s", address_string);   
                    /* 输出Telnet客户端接收到的新的数据 */   
                    memcpy(content, hlf->data, hlf->count_new);   
                    content[hlf->count_new] = '\0';   
                    for (i = 0; i < hlf->count_new; i++)   
                    {   
                        printf("%s", char_to_ascii(content[i]));   
                    }   
                    printf("\n");   
                }   
                else   
                {   
                    /* Telnet服务器接收到新的数据 */   
                    hlf = &telnet_connection->server;   
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));   
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);   
                    strcat(address_string, " ---> ");   
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));   
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);   
                    strcat(address_string, "\n");   
                    printf("--------------------------------\n");   
                    printf("%s", address_string);   
                    /* 输出Telnet服务器接收到的新的数据 */   
                    memcpy(content, hlf->data, hlf->count_new);   
                    content[hlf->count_new] = '\0';   
                    for (i = 0; i < hlf->count_new; i++)   
                    {   
                        printf("%s", char_to_ascii(content[i]));   
                    }   
                    printf("\n");   
                }   
            }   
        default:   
            break;   
    }   
    return ;   
}   
/*  
=======================================================================================================================  
主函数  
=======================================================================================================================  
 */   
void main()   
{   
    if (!nids_init())   
     /* Libnids初始化 */   
    {   
        printf("%s\n", nids_errbuf);   
        exit(1);   
    }   
    nids_register_tcp(telnet_protocol_callback);   
    /* 注册分析Telnet协议的回调函数 */   
    nids_run();   
    /* 进入循环捕获数据包的状态 */   
}   


你可能感兴趣的:(VC++分析数据包实现Telnet协议分析)