6

I'm trying to send commands to to the input of a cmd.exe application using the low level read/write console functions. I have no trouble reading the text (scraping) using the ReadConsole...() and WriteConsole() functions after having attached to the process console, but I've not figured out how to write for example "dir" and have the console interpret it as a sent command.

Here's a bit of my code:

CreateProcess(NULL, "cmd.exe", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
AttachConsole(pi.dwProcessId);

strcpy(buffer, "dir");
WriteConsole(GetStdHandle(STD_INPUT_HANDLE), buffer, strlen(buffer), &charRead, NULL);

STARTUPINFO attributes of the process are all set to zero, except, of course, the .cb attribute.

Nothing changes on the screen, however I'm getting an Error 6: Invalid Handle returned from WriteConsole to STD_INPUT_HANDLE. If I write to (STD_OUTPUT_HANDLE) I do get my dir written on the screen, but nothing of course happens. I'm guessing SetConsoleMode() might be of help, but I've tried many mode combinations, nothing helped. I've also created a quick console application that waits for input (scanf()) and echoes back whatever goes in, didn't work.

I've also tried typing into the scanf() promp and then peek into the input buffer using PeekConsoleInput(), returns 0, but the INPUT_RECORD array is empty.

I'm aware that there is another way around this using WriteConsoleInput() to directly inject INPUT_RECORD structured events into the console, but this would be way too long, I'll have to send each keypress into it.

I hope the question is clear. Please let me know if you need any further information. Thanks for your help.

Update 1:

I am able to send keypresses to a cmd process using WriteConsoleInput() with INPUT_RECORD structs, however, the AttachConsole sometimes throws ERROR_GEN_FAILURE #31: A device attached to the system is not functioning., and thus the INPUT_RECORD are not sent (Error 6: Invalid Handle). Sleep(1000) after CreateProcess() before AttachConsole() solves this. The characters dir are typed in automatically, but I can't figure out how to send the RETURN key:

ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = '\n';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = FALSE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = '\n';
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);

WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &charRead);

WriteConsoleInput returns 0, but nothing happens in the console, I've tried setting SetConsoleMode() to ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT and a combination thereof, with no results, though. If I press enter from the keyboard, however, the automatically-typed dir command executes (unlike the times when I just WriteConsole()), so I guess I'm on the right track.

Doesn't SSH send over the actual keypresses and gets the actual screen buffer (like TAB, and CTRL+C CTRL+D work)? I'm after something of the like.

Update 2:

I found the problem with injecting the return command. Should have been ir[1].Event.KeyEvent.uChar.AsciiChar = '\r'; i.e. an \r instead of a \n, super simple.

It seems that there is no way of using WriteConsole() to input commands, one should get by by sending WriteConsoleInput() INPUT_RECORDs or by creating pipes (which are not always perfect, but great for most straight forward applications). One great advantage of using WriteConsoleInput() is that you one can send VK_UP and VK_DOWN, to access console history, (if we're in CMD) and VK_TAB for auto-completion, all CTRL+_ sequences, ESC and FUNCTION keys and even MOUSE CLICKS.

More information here: http://msdn.microsoft.com/en-us/library/ms687403%28v=vs.85%29.aspx plus tonnes of examples here: http://controllingtheinter.net/forums/viewtopic.php?f=116&t=366

If anyone has other great ideas feel free to chip in. Thank you to all those who took interest in this. Hope this helps someone in the future.

soulseekah
  • 8,770
  • 3
  • 53
  • 58

1 Answers1

3

Right now you're trying to write to your own stdin handle, not the one for the cmd.exe process. You'll have to do a lot more work to redirect the input handle for that one. It requires a pipe. Here's a KB article that shows the boilerplate code.

Btw: always check the return value of API functions.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I thought I attached to the new process' console. `AttachConsole()` allows for this; when I `printf()` after attaching I'm actually printing into `cmd.exe`. I've updated the Error Code that I got from `WriteConsole()` to `STD_INPUT_HANDLE` – soulseekah Jan 17 '11 at 10:29
  • You only share the console window, that's it. Yes, the error code shows you what is going wrong, you cannot write to an input handle. – Hans Passant Jan 17 '11 at 10:33
  • I have read that code and tried it yesterday, works like a charm, alas such spawned programs like `FTP.exe` and `EDIT.exe` have their own buffers, which are not redirected, this calls for low level read and write to the console. Essentially I want to clone a console window, by redirecting everything from one to another. – soulseekah Jan 17 '11 at 10:37
  • I can try to use a pipe to direct input into the spawned process like in the KB and then read its console output buffer by attaching to it, because `stdout` does not contain all the data printed on screen. See http://stackoverflow.com/questions/2537180/why-i-cannot-get-the-output-of-ftp-exe-by-code – soulseekah Jan 17 '11 at 10:45
  • The community addition to http://msdn.microsoft.com/en-us/library/ms687403%28VS.85%29.aspx `WriteConsoleInput()` shows how to send keys using `INPUT_RECORD` s to send the output to a `system("edit")` process, with no problem and no pipes as far as I see. – soulseekah Jan 17 '11 at 10:55