Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Попытка записи в stdin


Автор: Selestin 29.10.2010, 22:30
Пишу програму, которая бы проверяла студенческие лабораторные работы, являющиеся консольными приложениями.

Задача: Записывать строку в STDIN студенческой программы, и читать STDOUT.

С проблеммами столкнулся попытавшись использовать пайпы, т.к. приложение не всегда имеет инициализированную консоль изначально, я получаю ошибку доступа, обращаясь к хендлу пайпа.

Решением было внедрить ДЛЛ в адресное пространство дочернего процесса и работать уже там.
Внедрение происходит через CreateProcess с подменой кода.

Суть в том что в данной реализации я никак не могу открыть STDIN, чтобы туда что-то записать, в STDOUT пишется нормально. Пытался уже разными способами, результата получить никак не могу.

Способы использованные для открытия:
Код

1) CreateFile( (LPCWSTR)"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 );
 
2) DuplicateHandle(GetCurrentProcess(),GetStdHandle(STD_INPUT_HANDLE),GetCurrentProcess(),&stdin_pseudo,GENERIC_READ | GENERIC_WRITE,FALSE,NULL)
 
3)  lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
 fp = _fdopen( hConHandle, "w" );
 
4) CreatePipe(&stdin_pipe_output, &stdin_pipe_input, lpsa,25000);
 
5) WriteFile(GetStdHandle(STD_INPUT_HANDLE),"1234\n",5,&BytesBuffer,NULL)

Результатов не получено.

Приложения которые корректно создаются CreateProcess(С изначально инициализированной консолью), позволяют корректно записывать данные в stdin через пайп.

Спасибо.

Автор: xvr 1.11.2010, 13:00
Цитата(Selestin @  29.10.2010,  22:30 Найти цитируемый пост)
С проблеммами столкнулся попытавшись использовать пайпы, т.к. приложение не всегда имеет инициализированную консоль изначально, я получаю ошибку доступа, обращаясь к хендлу пайпа.

Решением было внедрить ДЛЛ в адресное пространство дочернего процесса и работать уже там.
Внедрение происходит через CreateProcess с подменой кода.
Удаление гланд через задницу?

А создать пару хэндлов и передать их в качестве stdin и stdout запускаемому процессу (студенческой консольной задаче) через CreateProcess не проще?


Автор: GremlinProg 1.11.2010, 13:06
Цитата(xvr @  1.11.2010,  15:00 Найти цитируемый пост)
А создать пару хэндлов и передать их в качестве stdin и stdout запускаемому процессу (студенческой консольной задаче) через CreateProcess не проще?


Цитата(Selestin @  30.10.2010,  00:30 Найти цитируемый пост)
С проблеммами столкнулся попытавшись использовать пайпы, т.к. приложение не всегда имеет инициализированную консоль изначально, я получаю ошибку доступа, обращаясь к хендлу пайпа.


полагаю, это уже было сделано, но для уточнения: http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx
Selestin, ты имеешь ввиду этот вариант, или что-то другое, когда пишешь о пайпах?

Автор: GoldFinch 1.11.2010, 14:04
Цитата(Selestin @  29.10.2010,  23:30 Найти цитируемый пост)
Решением было внедрить ДЛЛ в адресное пространство дочернего процесса и работать уже там.

 smile 

Вот это я понимаю, правильный подход. Не работает что-то удаленно - берем и инжектим длл.

---

Цитата(Selestin @  29.10.2010,  23:30 Найти цитируемый пост)
приложение не всегда имеет инициализированную консоль изначально

Если приложение консольное (в PE заголовке в OptionalHeader.Subsystem==IMAGE_SUBSYSTEM_WINDOWS_CUI) то консоль будет инициализирована изначально. (если приложение не консольное, то перед его запуском этот флаг можно поменять)

Автор: Selestin 2.11.2010, 14:30
Цитата

полагаю, это уже было сделано, но для уточнения: http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx
Selestin, ты имеешь ввиду этот вариант, или что-то другое, когда пишешь о пайпах?

Да, этот. При использовании такого способа, на процессе, который изначально не имеет консоли, возникает ошибка доступа.

Цитата

Если приложение консольное (в PE заголовке в OptionalHeader.Subsystem==IMAGE_SUBSYSTEM_WINDOWS_CUI) то консоль будет инициализирована изначально. (если приложение не консольное, то перед его запуском этот флаг можно поменять) 

Чем это мне поможет?
Т.к. приложение имеет всегда одну консоль, то предположим AllocConsole студенческой проги вернет мне ошибку, и работа будет выполнятся уже в созданной изначально консоли, но над тестить, в теории может сработать.

Автор: GremlinProg 2.11.2010, 14:40
Цитата(Selestin @  2.11.2010,  16:30 Найти цитируемый пост)
и работа будет выполнятся уже в созданной изначально консоли

если конечно в студенческой проге нет например такой проверки:
Код

if( !::AllocConsole() ){
  // без консоли я работать не буду
  return 0;
}


Автор: Selestin 2.11.2010, 14:54
Такой точно нет. А на каком этапе нужно ставить флаг в PE хидер? Создать с флагом CREATE_SUSPENDED и после изменения ресюм делать?

Автор: xvr 2.11.2010, 16:45
Цитата(Selestin @  2.11.2010,  14:30 Найти цитируемый пост)
При использовании такого способа, на процессе, который изначально не имеет консоли, возникает ошибка доступа.
Уточни, пожалуйста - 'процессе, который изначально не имеет консоли' имеется в виду запускаемый (студенческий) процесс? Или твой, который всех запускает?
Если первое, то скорее всего эта студенческая программа вообще не консольная (а GUI), снабжать ее любыми консольными хэндлами (как снаружи, так и изнутри) бесполезно. Runtime от GUI приложения скорее всего даже не станет инициализировать stdin/stdout. И попытка сделать в них scanf/printf будет проигнорирована, сколько бы консолей потом не понавешали этой программе  smile 

Автор: Selestin 2.11.2010, 17:27
Имеется ввиду запускаемый процесс. Вот его дебаг:
http://paste.org/pastebin/view/20366

Автор: leniviy 2.11.2010, 17:32
Чтобы прочитать, что вывела тестируемая прога в консоль, необязательно что-то перехватывать. 
Как только была вызвана AllocConsole(), можно запустить дочерний процесс. В нужный момент он будет читать буфер консоли и отправлять на проверку.
DLL внедрять всё же надо, чтобы она по таймеру проверяла, открыта ли консоль и запускала дочерний процесс.

И ввод лучше делать не через поток, а через WriteConsoleInput() в том же дочернем процессе.

Так можно протестировать хоть FAR , хоть игрушку


Автор: xvr 2.11.2010, 20:33
Цитата(Selestin @ 2.11.2010,  17:27)
Имеется ввиду запускаемый процесс. Вот его дебаг:
http://paste.org/pastebin/view/20366

Понятно. Эта прога открывает свою консоль. Ее снаружи перехватить нельзя  smile Тут действительно нужен inject. Посмотри библиотеку Detour от MS - она как раз для этого

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)