4

Using stdout=subprocess.PIPE stops the output from going to the console, but nothing is captured.

>>> import subprocess
>>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'])
>>> ERROR: please provide an argument
// TRUNCATED USAGE OUTPUT
proc.wait()
0
>>> proc = subprocess.Popen([''C:\\Users\\me\\program.exe''], stdout=subprocess.PIPE)
>>> proc.communicate()
('', None)

I've tried every combination available on stackoverflow. shell=True hasn't worked. Spawning a sub cmd hasn't worked. subprocess.check_output captures nothing. I'm happy to retry any of these commands in the comments.

I am guessing this has something to do with out the program is attaching to a shell.

This is the assembly the program uses to output (mcall is just a macro to align memory to 16 bits). The reason I include this is in case GetStdHandle is affecting things.

console_write PROC
; rcx   MSG
; rdx   LEN
  prologue
  push    rcx
  push    rdx
  xor     rcx, rcx
  mov     ecx, [stdout]
  mcall   GetStdHandle
  mov     rcx, rax
  xor     rdx, rdx
  pop     r8    ; len
  pop     rdx   ; msg
  push    0
  mov     r9, rsp
  push    0
  mcall   WriteConsoleA
  pop     rcx
  pop     rcx
  epilogue
console_write ENDP

I'm stumped on this one. I've done this so many times on Linux. I just don't know enough about Windows internals to sort this out. Thanks!


Edit: Additional things I have tried:

Admin (also with STDERR capture)

C:\Windows\system32>C:\Python27\python.exe
Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:19:30) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> proc.communicate()
('', '')

STDOUT File Redirect

>>> import os
>>> os.system('C:\\Users\\me\\program.exe > out.txt')
0
>>> f = open('out.txt', 'r')
>>> f.read()
''

STDERR File Redirect

>>> import os
>>> os.system('C:\\Users\\me\\program.exe 2>out.txt')
ERROR: please provide an argument
// TRUNCATED USAGE OUTPUT
0
>>> open('out.txt', 'r').read()
''

Command Line Capture Fails (suppresses output from going to cmd, but nothing is captured)

program.exe > out.txt
9
  • did you try stderr? Commented Oct 31, 2017 at 15:30
  • Yes. Same issue. >>> proc.communicate() ('', '') (full command in another comment) Commented Oct 31, 2017 at 15:32
  • or maybe you should run it as admin, did you check that? Commented Oct 31, 2017 at 15:34
  • 1
    you can see its output on cmd right? so, write that in a .bat file, then launch that bat file with your python script and and see what happens ... Commented Oct 31, 2017 at 15:43
  • 1
    EXE is fine. Assembled with ML64 (I've rebuilt it today with no change). It outputs to console and returns 0. Capturing stdout suppresses the output but fails to capture. Commented Oct 31, 2017 at 15:54

3 Answers 3

4

A coworker pointed out the issue. It turns out that WriteConsole cannot be redirected to a file, it can only be used with console handles. [1] [2]

This StackOverflow answer gives an outline for how to solve it. I've implemented it in Python if anyone else has this problem.

import win32console
import subprocess
import msvcrt
import os

ccsb = win32console.CreateConsoleScreenBuffer()
fd = msvcrt.open_osfhandle(ccsb, os.O_APPEND)
proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=fd)
proc.wait()
ccsb.ReadConsoleOutputCharacter(100, win32console.PyCOORDType(0,0)) # reads 100 characters from the first line

The better solution for me was to change WriteConsoleA to WriteFile, using the same StdHandle.

console_write PROC
; rcx   MSG
; rdx   LEN
  prologue
  push    rcx
  push    rdx
  xor     rcx, rcx
  mov     ecx, [stdout]
  mcall   GetStdHandle
  mov     rcx, rax
  ; Write File
  pop     r8    ; len
  pop     rdx   ; msg
  ; unalign for odd number of pushed args
  mov     rbx, rsp
  sub     rbx, 8        
  and     rbx, 0Fh
  sub     rsp, rbx
  ; args
  mov     rcx, rax        ; handle
  xor     r9, r9
  push    0               ; 1 arg (ununaligns)
  sub     rsp, 20h        ; shadow 
  call    WriteFile
  add     rsp, 28h        ; shadow + arg
  add     rsp, rbx        ; realign
  epilogue
console_write ENDP
Sign up to request clarification or add additional context in comments.

7 Comments

very unusual for x64 asm code. i mean how you work with stack. all this push/pop in the middle. need not forget about stack align before every call. why not use usual technique with mov params (4,5..) to stack ?
@RbMm Any example of the usual code? I have a macro for align and call for 0-4 arguments. This was the one call I was doing with 5. I will eventually write a new macro where you provide the number of arguments and it does the align / unalign based on whether it is even or odd. Most the code I saw relied on tracking if the stack was aligned or not. This doesn't care if it is aligned or unaligned. If there is a better way though, I'm always open to learn.
look for any windows x64 binary. really you only complicate your own life by this. special macros absolute not need. you need once in function prolog align stack. and then never use push or pop for args. simply use mov. say mov [rsp+20h],arg5, mov [rsp+28h],arg6 and so on. and code became smaller and more redable.
@RbMm The sub rsp, 0xNN requires knowledge of whether the stack is aligned or unaligned. 0xNN would need to be shadow_space + 8 * n_stack args + alignment. My code just tests for alignment and subtracts it first. I could do the same thing and just use the result of and rbx, 0Fh as the alignment in the above equation, but it makes no difference. I'm not a compiler and don't want to track whether things are aligned or unaligned, so the test is useful. Then if I change the code above a call it doesn't mess up the alignment for the call.
If you're in control of the source, then certainly change to WriteFile. Using WriteConsoleA offers no advantages and comes with the downside that it only accepts console screen-buffer handles. WriteConsoleW would be a different story. It's the only reliable way to write the full range of Unicode to the console as UTF-16 text in Windows 7, since the older console's support for codepage 65001 (UTF-8) is buggy for output. (Even in Windows 10 the console is still buggy for reading input as UTF-8.)
|
1

Did you try to catch stderr as well?

proc = subprocess.Popen([''C:\\Users\\me\\program.exe''], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

UPDATE

You can try to read output line by line while the process is running, for example:

p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
    retcode = p.poll()
    print p.stdout.readline()
    if retcode is not None:
        break

4 Comments

Yep. Same deal. >>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) >>> proc.communicate() ('', '')
os.system() does not capture output. Redirecting to a file like os.system(program.exe > out.txt) causes no output to be written to the file.
And os.system(program.exe 2> out.txt)?
It's printing to stdout. When I do that the output goes to the console and nothing is captured in the file. Will edit it in above.
1

I don't think I have enough rep to suggest a question is a possible duplicate, nor comment, so I'm posting this as an answer because although douggard's answer led me down the right track thinking-wise, it didn't actually work in my case and I had a hard time getting a working solution.

There's an answer here that captures CONOUT and worked for me: https://stackoverflow.com/a/38749458/1675668

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.