|
|
黑客技术 第9章 特洛伊木马实例及其简单实现 |
|
|
作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站 |
第九章
特洛伊木马实例及其简单实现
这里介绍一个比较阴险的威胁网络安全的方法:特洛伊木马(trojan horse,或trojan)。
第一节 什么是特洛伊木马
特洛伊木马是一个程序,它驻留在目标计算里。在目标计算机系统启动的时候,自动启动。然后在某一端口进行侦听。如果在该端口受到数据,对这些数据进行识别,然后按识别后的命令,在目标计算机上执行一些操作。比如窃取口令,拷贝或删除文件,或重新启动计算机。 攻击者一般在入侵某个系统后,想办法将特洛伊拷贝到目标计算机中。并设法运行这个程序,从而留下后门。以后,通过运行该特洛伊的客户端程序,对远程计算机进行操作。 特洛伊木马的一个特点是,它能巧妙地运行在目标计算机系统里,而不容易被发现。 现在有许多这样的程序。如NetCat,Back Orifice,NetBus等等。
Back Orifice Back Orifice简介 Back Orifice是Cult of the Dead Cow (cDc)在1998年8月3日发布的。目前的下载量达到了100,000。许多人都在善意或恶意地使用这个程序。尽管这个程序并不是最优秀的黑客工具,但由于媒体的炒做,使得这个工具给人么一个很坏的印象。 Back Orifice被称为“远程管理工具”。它可以附加在别的文件或程序后,也可以单独运行。它的服务器程序必须在目标计算机上运行之后,才能起到作用。一旦运行后,用户就不大容易感觉到它的存在。在任务列表里,根本就看不到它。该工具的服务器运行后,就一直在一个端口侦听从客户机来的命令,根据不同的命令,在目标机器上执行相应的操作。
Back Orifice的使用 Back Orifice(以下简称BO)是一个客户机/服务器(C/S)应用程序,其客户机程序(以下简称BO客户机)可以监视、管理和使用其它网络中运行服务器程序(以下简称BO服务器)的目标计算机所在的网络资源。基于文本和基于图形的BO客户机是运行在Microsoft Windows机器上。当前版本的BO服务器只能在Windows 95/98中运行。
Back Orifice软件包里包括: bo.txt 软件包说明文档。 plugin.txt 插件编程文档。 boserve.exe Back Orifice服务器自安装程序。 bogui.exe 图形界面的Back Orifice客户机。 boclient.exe 文本界面的Back Orifice客户机。 boconfig.exe 配置BO服务器程序文件名、端口、密码和插件的工具。 melt.exe 对由freeze命令压缩的文档解压缩。 freeze.exe 压缩文档。压缩文档可被metl命令解压缩。
只要运行BO服务器程序,就可以安装BO服务器了。当BO服务器程序运行时,它安装BO服务器,然后删除自安装程序。此方法有助于网络环境下的安装:只要BO服务器程序被复制到Startup目录下就行了(译者注:因为Windows 95/98每次启动时都会运行该目录下的程序)。因为BO服务器程序在自安装BO服务器后就会删除自已。一旦BO服务器被安装到一台机器上,它会在每次机器启动时运行。 需要远程更新Back Orifice时,只要上载新版本的BO服务器程序到远程机上,使用Process spawn命令运行它。一旦运行,BO服务器程序将自动删除与它将要安装的文件同名的文件,安装自已(覆盖旧版本),然后在安装目录中运行自己,最后删除BO服务器程序。 在安装前,可以配置BO服务器程序的一些参数。如安装后的BO文件名、监听端口、加密密码,都可以使用boconfig.exe工具配置。如果不进行配置,缺省是监听31337端口、不使用加密密码(数据包仍然会加密)和以" .exe"文件名安装。 BO客户机通过加密了的UDP包与BO服务器通讯。要实现成功通讯,BO客户机城建发送数据到BO服务器监听的端口,而且BO客户机密码必须匹配BO服务器已配置好的密码。 基于图形和文本的BO客户机都可以通过使用-p选项来设置BO客户机数据包的发送端口。如果数据包被过滤或者有防火墙屏蔽,就可能需要从一个特别的、不会被过滤和屏蔽的端口发送。如果UDP连接通讯不能成功,则可能是数据包在发送或回送路径中被过滤或者屏蔽了。 从BO客户机向特定的IP地址发送命令即可对BO服务器操作。如果BO服务器无静态IP地址,则可使用以下方法: (1) 在基于文本的BO客户机使用sweep或sweeplist命令; (2) 在基于图形的BO客户机使用"Ping..."对话框; (3) 设定目标IP如"1.2.3.*"。如果扫描子网列表,当有BO服务器响应时,BO客户机在子网列表目录中浏览,并显示所匹配的行和子网地址。(译者注:虽然我知道如何使用,但却无法按原文的内容表达出来。我在以后再作详细说明。)
以下是在现在版本的Back Orifice中已经实现的命令。在基于图形和基于文本的BO客户机里有些命令名称不相同,但几乎所有命令的语法格式都是一致的。在基于文本的BO客户机中输入 "help command"可得到更多关于命令的信息。在基于图形的BO客户机中有两个参数输入区域,这些参数作为在"Command"列表中所选择的命令的参数。如果未给出命令所需要的参数,BO服务器将返回"Missing data"(丢失数据)。
Back Orifice命令如下: (基于图形的BO客户机命令/基于文本的BO客户机命令) App add/appadd 在TCP端口输出一个基于文本的应用程序。它允许你通过Telnet对话控制基于文本或DOS的应用程序。
App del/appdel从监听的连接中关闭一个应用程序。 Apps list/applist列出当前监听的连接中的应用程序。 Directory create/md创建目录 Directory list/dir列出文件和目录。如要显示多文件/目录则须使用通配符。
Directory remove/rd删除目录 Export add/shareadd在BO服务器上创建一个“出口”(共享)。被输出(共享)的目录或驱动器图标不会出现共享图标。 Export delete/sharedel删除一个(共享)“出口”。 Exports list/sharelist列出当前共享名、共享驱动器、共享目录、共享权限和共享密码。 File copy/copy拷贝文件。 File delete/del删除文件。 File find/find在目录中查找符合条件(支持通配符)的文件。 File freeze/freeze压缩文件。 File melt/melt解压缩文件。 File view/view查看文件内容。 HTTP Disable/httpoff使HTTP服务器失效。 HTTP Enable/httpon使HTTP服务器有效。 Keylog begin/keylog将BO服务器上的击键记录在一个文本文件中,同时还记录执行输入的窗口名。 Keylog end停止击键记录。基于文本的BO客户机使用"keylog stop"命令。 MM Capture avi/capavi从视频输入设备(如果存在)捕捉视频和音频信号到avi文件中。 MM Capture frame/capframe从视频输入设备捕捉一个视频帧到一个位图文件中。 MM Capture screen/capscreen捕捉BO服务器屏幕影像到一们位图文件中。 MM List capture devices/listcaps列出视频输入设备。 MM Play sound/sound在BO服务器上播放一个avi文件。 Net connections/netlist列出当前接入和接出的连接。 Net delete/netdisconnect断开BO服务器的一个网络资源连接。 Net use/netconnect把BO服务器连接到一个网络资源。 Net view/netview查看BO服务器上所有的网络接口、域名、服务器和可见的共享“出口”。 Ping host/pingPing主机。返回主机名和BO版本。 Plugin execute/pluginexec运行BO插件。运行不符合BO插件接口的函数可能使B)服务器当机。 Plugin kill/pluginkill命令一个插件关闭。 Plugins list/pluginlist列出当前激活的插件和已存在的插件返回值。 Process kill/prockill终止一个进程。 Process list/proclist列出运行中的进程。 Process spawn/procspawn运行一个程序。在基于图形的BO客户机程序中,如果需要确定第二个参数,进程可能以一个正常的、可见的方式运行,否则进程的运行将是隐蔽或独立的。 Redir add/rediradd重定向接入的TCP连接或UDP数据包到另一个IP地址。 Redir del/redirdel停止端口重定向。 Redir list/redirlist列出激活的端口重定向。 Reg create key/regmakekey在注册表中创建中一个主键。 注:对于所有的注册表命令,不要在注册表键值前加入前导"\\"。 Reg delete key/regdelkey从注册表中删除一个主键。 Reg delete value/regdelval删除注册表中的一个键值。 Reg list keys/reglistkeys列出注册表中一个主键下的子键。 Reg list values/reglistvals列出注册表中一个主键的键值。 Reg set value/regsetval设置注册表一个主键的一个键值。键值格式为“类型,值”。对于二进制值(类型为B),值是一个两位的16进制数;对于DWORD(双字)值(类型为D),值是一个十进制数;对于字符串值(类型为S),值是一个文本串。 Resolve host/resolve解析BO服务器的主机名的IP地址。主机名可能是一个Internet主机名或本地网络机器名。 System dialogbox/dialog用所给出的文本和一个"OK"按钮, 在BO服务器上创建一个对话框。可以创建任意多的对话框,对话框的显示是堆叠式的。 System info/info显示BO服务器上的系统信息。包括机器名、当前用户、CPU类型、内存容量及可用内存、Windows版本、驱动器信息(类型(硬盘、CDROM、可拆卸型、远程驱动器)、硬盘驱动器容量及未使用空间)。 System lockup/lockup锁住BO服务器机器。 System passwords/passes显示被缓存的当前用户密码和屏幕保护密码。所显示的密码中可能含有一些无用信息。(译者注:如果密码未被系统缓存,则不能显示密码。) System reboot/reboot关闭BO服务器主机并重启动。 TCP file receive/tcprecv将BO服务器主机连接到一个特定的IP地址和端口,并保存所接收到的数据到特定文件中。 TCP file send/tcpsend将BO服务器主机连接到一个特定的IP地址和端口,发送特定文件中的内容,然后断开此连接。 注:对于TCP文件传输,必须监听特定的IP地址和端口,直到TCP文件命令被发送,否则传输将会失败。 从BO服务器传输文件,可使用TCP文件发送命令和如下格式的netcat命令: netcat -l -p 666 > file 传输文件到BO服务器,可使用TCP文件接收命令和如下格式的netcat命令: netcat -l -p 666 < file 注:Win32版本的netcat命令在到达输入文件末部时并不断开连接。因此应在文件内容传输完毕后用ctrl-c或ctrl-break终止netcat命令。
BOConfig: BOConfig.exe允许在BO服务器安装前配置一些可选项。首先询问BO服务器在系统目录中安装的可执行文件名。它不一定是.exe,但如果你不给出扩展名,它不会自动添加.exe扩展名;接着询问exe文件的描述,它描述了在注册表中记录的、系统启动时运行的exe文件;接着询问BO服务器监听(数据包)端口;接着询问用于加密的密码。要实现BO客户机到BO服务器的通讯,客户机必须配置有相同的密码,此密码可以为空;接着询问启动时缺省运行的插件。这个在BO服务器启动时自动运行的BO插件是以"DLL:_Function"格式定义的DLL和函数。此项可以为空;然后让你输入启动时传送给插件的参数,此项也可以为空;最后,询问被附加到BO服务器上的文件的路径。该文件将在BO服务器启动时写入系统目录。此文件可以是一个自动启动的BO插件。 BO服务器在没有进行配置时也能运行。缺省地,安装BO服务器文件名为" .exe",无密码,使用端口31337通讯。
已知的Bugs和问题: 多媒体捕捉屏幕——所产生的位图是按BO服务器端的显示分辨率和像素深度保存的。因此,它可能是16位或24位颜色的。大多数图形应用程序只能处理8位或32位位图,因而不能打开此位图,或者显示不正常(此类软件包括Graphics Workshop for Windows、Photoshop和WANG Imaging distributed with Windows)。但是,Windows本身有一个应用程序Paint.exe可以浏览这些位图,按其提示操作即可。 击键记录——很显然,MS-DOS窗口未提供信息循环机制,这就使得BO无法记录输入到其中的击键。 基于文本的应用程序的TCP重定向——有几个Bugs。 当用command.com的重定向名柄输出command.com时,系统同时输出REDIR32.EXE,此程序似乎是无法终止的。这可能是由于操作系统接口与一个tsr模块(该模块在DOS对话中被装载以重定向输入/输出句柄)通讯所造成的。因此,如果在应用程序被终止(或退出)前终止TCP连接,REDIR32.exe和WINOA386.MOD(用于封装管理旧16位应用程序)将仍然运行,BO和操作系统均无法终止它们。这会导致系统显示"Please wait..."(请等待)屏幕且无法关机。 某些控制台应用程序重定向了输出时也可能会发生问题,如FTP.exe和boclient.exe。虽然程序的输出因此而不能传送出去,但仍然可能传送输入,所以你要通过TCP对话使该程序退出。否则使用BO杀死该进程。
Back Orifice的检查和清除 打开注册表编辑器,检查HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices主键的键值。如果你在主键看到的如下的一个键值:
Name Data (缺省) " .exe" (一个空格,一个点号和exe后缀)
那么你可能已经感染上了Back Orifice了。然后在C:\WINDOWS\SYSTEM目录下,如果发现一个" .exe"文件,文件大小为122K左右,那么你肯定感染了这个程序了。 清除的方法很简单。首先将上述主键中的有关" .exe"的项目删除,然后重新启动计算机。接着,将C:\WINDOWS\SYSTEM下的" .exe"删除,最后,找一个叫WINDLL.DLL的文件,也将它删除。 注意,有可能你的系统里有好几个Back Orifice的拷贝,要逐一清除。
NetBus Netbus 是一个类似于著名的 Back Orifice 的黑客软件,区别在于它的能力要强出太多。Netbus 通过 TCP/IP 协议,可以远程将应用程序指派到某一套接字端口来运行。这就相当于说可以远程运行目标机器上的 cmd.exe,想想这是多么危险的事情。 如果不是 the Cult of the Dead Cow 黑客组织在1998年的 DefCon 大会上发布 BackOrifice 工具而引起轩然大波的话,可能大多数人还不会注意到三月份发行的 Netbus。据说 Netbus 是瑞典程序员 Carl-Fredrik Neikter 为了“和朋友们消遣”而编写的。 粗粗一看,Netbus 似乎没什么危害,只允许黑客控制鼠标,播放声音文件,甚或打开 CD-ROM 托架。但如果深入分析,就不难发现其中大量的破坏性功能,特别它是基于 TCP/IP 协议在 Windows 95、Windows 98、和 Windows NT 上运行的(与 BackOrifice 不同),这大大增加了各种入侵用户系统的可能性。 Netbus 1.6 版能实现一些相当危险的操作:黑客能够运行远程程序,进行屏幕抓图,在所侵入的计算机浏览器中打开 URL,显示位图,进行服务器管理操作(如更改口令),甚至利用远端的麦克风录制一段声音。更可怕的是:它能在侵入的计算机上显示信息,向毫无戒心的用户提示输入口令,再把该口令返回到入侵者的屏幕上。Netbus 还能关闭 Windows 系统,下载、上载或删除文件。 11 月 14 日发行 的 Netbus 1.7 新增了更多不正当的功能。如:重定向功能(Redirection)使黑客能够控制网络中的第三台机器,从而伪装成内部客户机。这样,即使路由器拒绝外部地址,只允许内部地址相互通信,黑客也依然可以占领其中一台客户机并对其它无数台机器进行控制。 V1.7 甚至还能指派应用软件至某个端口。以前只有 Netcat — 黑客的梦幻工具— 用于 Unix 和 NT 时才具有这种功能。例如,黑客可以将 cmd.exe 指派至 Telnet port 23,然后 Telnet 进入该机器,从而接管系统的命令提示符。其危险后果不言自明。 Netbus 的默认状态是在 port 12345 接收指令,在 port 12346 作应答。Telnet 登录到接收端口就会看到产品名称及版本号,还可以修改口令。Netbus 能通过编辑 patch.ini 配置文件,把 1 到 65535 之间的任意数字指定为端口。当需要绕过防火墙或路由过滤器时,端口通常就会设为 53(DNS)或 80(HTTP)。 所有的特洛伊木马都分成两个部分:服务器和客户机。 V1.7版本的NetBus的服务器的默认文件名是patch.exe。运行这个程序后,它将自己拷贝到Windows目录下,并从中解开一个叫KeyHook.dll的动态连接库。默认的,它创建一个主键HKEY_CURRENT_USER\PATCH。并在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run下创建了一个键,它的值是patch.exe文件的路径名。这使得在每次系统启动时,都能自动运行patch.exe这个程序。除此外,还创建下面两个键:HKEY_CURRENT_USER\NETBUS和HKEY_CURRENT_USER\NETBUS\Settings 按照上面的描述,清除方法就自然出来了。
第二节 特洛伊木马的一个简单实现
通过上面的两个实例介绍,基本上就能看出特洛伊木马的工作原理。这里我们仅仅介绍用Winsock实现的一个客户机程序和一个服务端程序。 这个实例中的服务器在接到客户机的命令后会重新启动计算机。 可以在这两个程序的基础上,加入一些命令,对目标系统进行一些修改。比如拷贝文件等等。 这两个程序是从微软的MSDN上拿下来的,略微作了点增加。在VC++6.0中编译运行的。注意在连接的时候要加入:wsock32.lib库。 ExitWindowsEx 函数介绍 ExitWindowsEx函数的功能是关闭系统,注销用户和重新启动系统。 它的函数原型是: BOOL ExitWindowsEx( UINT uFlags, DWORD dwReserved); 第一个参数用来指定操作的类型。 常见的有下面几个: EWX_POWEROFF:关闭系统及关闭电源。 EWX_REBOOT:重新启动计算机。 EWX_SHUTDOWN:关闭系统,但不关闭电源。 第二个参数可以指定任意值,并没有特定意义。 具体有关在Linux和Windows下进行SOCKET编程的细节,请参见相关章节。
服务器程序: #include < windows.h> #include < winsock.h>
#define PORTNUM 5000 // Port number #define MAX_PENDING_CONNECTS 4 // Maximum length of the queue // of pending connections int WINAPI WinMain ( HINSTANCE hInstance, // Handle to the current instance HINSTANCE hPrevInstance,// Handle to the previous instance LPTSTR lpCmdLine, // Pointer to the command line int nCmdShow) // Show state of the window { int index = 0, // Integer index iReturn; // Return value of recv function char szServerA[100]; // ASCII string TCHAR szServerW[100]; // UNICODE string TCHAR szError[100]; // Error message string
SOCKET WinSocket = INVALID_SOCKET, // Window socket ClientSock = INVALID_SOCKET; // Socket for communicating // between the server and client SOCKADDR_IN local_sin, // Local socket address accept_sin; // Receives the address of the // connecting entity int accept_sin_len; // Length of accept_sin
WSADATA WSAData; // Contains details of the Windows // Sockets implementation
// Initiate Windows Sockets. if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0) { wsprintf (szError, TEXT("WSAStartup failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); return FALSE; }
// Create a TCP/IP socket, WinSocket. if ((WinSocket = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { wsprintf (szError, TEXT("Allocating socket failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); return FALSE; }
// Fill out the local socket's address information. local_sin.sin_family = AF_INET; local_sin.sin_port = htons (PORTNUM); local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
// Associate the local address with WinSocket. if (bind (WinSocket, (struct sockaddr *) &local_sin, sizeof (local_sin)) == SOCKET_ERROR) { wsprintf (szError, TEXT("Binding socket failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); closesocket (WinSocket); return FALSE; }
// Establish a socket to listen for incoming connections. if (listen (WinSocket, MAX_PENDING_CONNECTS) == SOCKET_ERROR) { wsprintf (szError, TEXT("Listening to the client failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); closesocket (WinSocket); return FALSE; }
accept_sin_len = sizeof (accept_sin);
// Accept an incoming connection attempt on WinSocket. ClientSock = accept (WinSocket, (struct sockaddr *) &accept_sin, (int *) &accept_sin_len);
// Stop listening for connections from clients. closesocket (WinSocket);
if (ClientSock == INVALID_SOCKET) { wsprintf (szError, TEXT("Accepting connection with client failed.") TEXT(" Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); return FALSE; }
for (;;) { // Receive data from the client. iReturn = recv (ClientSock, szServerA, sizeof (szServerA), 0);
// Check if there is any data received. If there is, display it. if (iReturn == SOCKET_ERROR) { wsprintf (szError, TEXT("No data is received, recv failed.") TEXT(" Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Server"), MB_OK); break; } else if (iReturn == 0) { MessageBox (NULL, TEXT("Finished receiving data"), TEXT("Server"), MB_OK); ExitWindowsEx(EWX_REBOOT,0); //restart windows break; } else { // Convert the ASCII string to the UNICODE string. for (index = 0; index < = sizeof (szServerA); index++) szServerW[index] = szServerA[index];
// Display the string received from the client. MessageBox (NULL, szServerW, TEXT("Received From Client"), MB_OK); } }
// Send a string from the server to the client. if (send (ClientSock, "To Client.", strlen ("To Client.") + 1, 0) == SOCKET_ERROR) { wsprintf (szError, TEXT("Sending data to the client failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); }
// Disable both sending and receiving on ClientSock. shutdown (ClientSock, 0x02);
// Close ClientSock. closesocket (ClientSock);
WSACleanup ();
return TRUE; }
客户端程序: #include < windows.h> #include < winsock.h>
#define PORTNUM 5000 // Port number #define HOSTNAME "localhost" // Server name string // This should be changed // according to the server int WINAPI WinMain ( HINSTANCE hInstance, // Handle to the current instance HINSTANCE hPrevInstance,// Handle to the previous instance LPTSTR lpCmdLine, // Pointer to the command line int nCmdShow) // Show state of the window { int index = 0, // Integer index iReturn; // Return value of recv function char szClientA[100]; // ASCII string TCHAR szClientW[100]; // UNICODE string TCHAR szError[100]; // Error message string
SOCKET ServerSock = INVALID_SOCKET; // Socket bound to the server SOCKADDR_IN destination_sin; // Server socket address PHOSTENT phostent = NULL; // Points to the HOSTENT structure // of the server WSADATA WSAData; // Contains details of the Windows // Sockets implementation
// Initiate Windows Sockets. if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0) { wsprintf (szError, TEXT("WSAStartup failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); return FALSE; }
// Create a TCP/IP socket that is bound to the server. if ((ServerSock = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { wsprintf (szError, TEXT("Allocating socket failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); return FALSE; }
// Fill out the server socket's address information. destination_sin.sin_family = AF_INET;
// Retrieve the host information corresponding to the host name. if ((phostent = gethostbyname (HOSTNAME)) == NULL) { wsprintf (szError, TEXT("Unable to get the host name. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); closesocket (ServerSock); return FALSE; }
// Assign the socket IP address. memcpy ((char FAR *)&(destination_sin.sin_addr), phostent->h_addr, phostent->h_length);
// Convert to network ordering. destination_sin.sin_port = htons (PORTNUM);
// Establish a connection to the server socket. if (connect (ServerSock, (PSOCKADDR) &destination_sin, sizeof (destination_sin)) == SOCKET_ERROR) { wsprintf (szError, TEXT("Connecting to the server failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); closesocket (ServerSock); return FALSE; }
// Send a string to the server. if (send (ServerSock, "To Server.", strlen ("To Server.") + 1, 0) == SOCKET_ERROR) { wsprintf (szError, TEXT("Sending data to the server failed. Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Error"), MB_OK); }
// Disable sending on ServerSock. shutdown (ServerSock, 0x01);
for (;;) { // Receive data from the server socket. iReturn = recv (ServerSock, szClientA, sizeof (szClientA), 0);
// Check if there is any data received. If there is, display it. if (iReturn == SOCKET_ERROR) { wsprintf (szError, TEXT("No data is received, recv failed.") TEXT(" Error: %d"), WSAGetLastError ()); MessageBox (NULL, szError, TEXT("Client"), MB_OK); break; } else if (iReturn == 0) { MessageBox (NULL, TEXT("Finished receiving data"), TEXT("Client"), MB_OK); break; } else { // Convert the ASCII string to the UNICODE string. for (index = 0; index < = sizeof (szClientA); index++) szClientW[index] = szClientA[index];
// Display the string received from the server. MessageBox (NULL, szClientW, TEXT("Received From Server"), MB_OK); } }
// Disable receiving on ServerSock. shutdown (ServerSock, 0x00);
// Close the socket. closesocket (ServerSock);
WSACleanup ();
return TRUE; }

|
|
相关文章:相关软件: |
|