4

I am launching a binary in windows using:

process = subprocess.Popen(cmd, stderr = subprocess.PIPE, stdin = subprocess.PIPE, stdout = ch.input)

ch.input comes from:

ch = InputStreamChunker('\n')
ch.daemon = True
ch.start()

which was a cool non blocking method of reading from stdout. It was the accepted answer for this SO question: How can I read all availably data from subprocess.Popen.stdout (non blocking)?

This is the bulk of my script that attempts to launch/monitor the process:

import subprocess
import time
from e import *
import msvcrt

ch = InputStreamChunker('\n')
ch.daemon = True
ch.start()


cmd = "C:\TEST.EXE"
process = subprocess.Popen(cmd, stderr = subprocess.PIPE, stdin = subprocess.PIPE, stdout = ch.input)
i = process.stdin
answers = []
waitforresults(ch, answers, expect = 5)
an = 1
for i in answers:
    print "ANSWER # " + str(an) + ":" + i.read()
    an= an+1

answers = []    
time.sleep(5)
msvcrt.ungetch('s')
ch.flush()
waitforresults(ch, answers, expect = 1)
for a in answers:
    print a.getvalue()

process.terminate()
ch.stop()
del process, ch
print "DONE"

Anyway, I can read from the process using this method fine. When I attempt to write to the process using:

i = process.stdin i.write("s\n") or i.write("s")

The output is printed in the console window but not captured by the binary. I took a look at the binary in IDA Pro and find that it's using a non standard method of capturing user input while it is running.

The process is an interactive cli tool that waits for S, R, P or Q (Status, Resume, Pause, Quit) ***and it captures the input withput the user hitting ente***r. This is accomplished using getche(). I threw the binary at IDA Pro to confirm:

v7 = getche();
if ( (unsigned int)dword_41F060 > 1 )
  sub_4030A0(&unk_41C111, v5);
WaitForSingleObject((HANDLE)dword_41F040, 0xFFFFFFFFu);
if ( v7 == 113 )
{
  if ( dword_41F060 != 1 )
  {
    if ( dword_41F060 )
      dword_41F060 = 6;
  }
}
else
{
  if ( v7 <= 113 )
  {
    if ( v7 == 112 )
    {
      if ( dword_41F060 == 2 )
        QueryPerformanceCounter((LARGE_INTEGER *)&qword_41F128);
      dword_41F060 = 3;
      sub_4030A0("Paused", v5);
    }
  }
  else
  {
    if ( v7 == 114 )
    {
      if ( dword_41F060 == 3 )
      {
        QueryPerformanceFrequency((LARGE_INTEGER *)&v9);
        QueryPerformanceCounter((LARGE_INTEGER *)&v8);
        v3 = (double)(v8 - qword_41F128);
        LODWORD(v4) = sub_413FF0(v9, v10, 1000, 0);
        dbl_41F138 = v3 / (double)v4 + dbl_41F138;
      }
      dword_41F060 = 2;
      sub_4030A0("Resumed", v5);
    }
    else
    {
      if ( v7 == 115 )
        sub_40AFC0();
    }
  }
}

Does anyone know how to trigger this event? This looked promising: http://docs.python.org/library/msvcrt.html and I tried using:

msvcrt.ungetch("s") "Cause the character char to be “pushed back” into the console buffer; it will be the next character read by getch() or getche()."

which DID push the letter "s" to the console but did not trigger my breakpoint on getche(). Hitting the letter S manually does work and does cause IDA pro to hit the breakpoint

halp? :)

EDIT:

I created two small windows console apps to demonstrate what works, what doesn't, and ensure my python is sane.

This first one I can NOT get to recognize input by writing to stdin, the second one I can.

#include "stdafx.h"
#include <conio.h>


int _tmain(int argc, _TCHAR* argv[])
{
    char response;
    printf("Enter \"s\":\n");
    response = _getch();
    printf("You entered %c\n", response);
    return 0;
}

can write to this one:

#include "stdafx.h"
#include <conio.h>


int _tmain(int argc, _TCHAR* argv[])
{
    char response [2];
    printf("Enter \"s\":\n");
    gets(response);
    printf("You entered %s", response);
    return 0;
}

EDIT 2 I've also tried: import subprocess import time from e import * import msvcrt

ch = InputStreamChunker('\n')
ch.daemon = True
ch.start()


cmd = ["test-getch.exe"]
process = subprocess.Popen(cmd, stderr = subprocess.PIPE, stdin = subprocess.PIPE, stdout = ch.input)
i = process.stdin
answers = []
msvcrt.putch('s')
ch.flush
waitforresults(ch, answers, expect = 1)
for answer in answers:
    print answer.getvalue()
i.close()
time.sleep(3)
#process.terminate()
ch.stop()
del process, ch
print "DONE"
Community
  • 1
  • 1
  • have you tried `msvcrt.putch()`? – jfs Nov 07 '11 at 03:27
  • I have indeed tried.. "C:\code>C:\Python27\python.exe te.py sMain talking: 0 answers received, expecting 1" ..... as you can see s gets pushed to the console but is not picked up by the text.exe – python_crazy Nov 07 '11 at 03:44
  • I'd ask `getche()` as a separated question using only plain `subprocess` functions (without `InputStreamChunker()` e.g., `Popen(..stdin=PIPE).communicate('s')`) and a simple C program. Add `Windows` tag. – jfs Nov 07 '11 at 04:26
  • ungetch() is for backing up a bit while *you* are reading a stream. It's usually only guaranteed to back up by one character, and it's not meant for output. You shouldn't have to go through all these contortions to write into a pipe, anyway. Have you tried setting `bufsize=0` in Popen? – alexis Mar 05 '12 at 22:43

1 Answers1

2

Excerpts from MSDN:

These routines read and write on your console. The console I/O routines are not compatible with stream I/O or low-level I/O library routines. In the Windows operating systems, the output from these functions is always directed to the console and cannot be redirected.

So I see 2 possible options and both are not promising:

  1. Create a pseudo Windows console and run your program from it. Then you will get your output there. (Analog of pty on UNIX)
  2. Somehow get data from existing Windows console. Console2 http://sourceforge.net/projects/console/ is doing something like that.

I do not know how to do any of these things or if they are possible at all.

In any case I don't see any practical solution, basically saying that you are out of luck, in my opinion.

Alx_s
  • 21
  • 2