Перехват ввода и вывода консольных программ
Перехват ввода и вывода консольных программ бывает нужен, когда требуется получить результат их работы для обработки в нашем приложении. Также мы получаем возможность передавать консольным программам собственные данные. Как обычно в FASM'е готовых решений нет, пришлось разбираться самому и портировать с языков высокого уровня. Технически перехват ввода и вывода консоли выполняется с использованием специальных структур, называемых "Pipe". По принципу действия они и вправду похожи на трубы: в один конец информация "вливается", из другого "выливается", а перехват является просто подключением нашего "крана" к тому или иному концу трубы. Для перехвата требуется переопределить стандартные дескрипторы ввода и вывода консольного приложения на наши. Создать новые дескрипторы можно при помощи функции CreatePipe, а затем прописать в структуру STARTUPINFO запускаемого приложения. После этого новые дескрипторы будут доступны для чтения и записи как обычный файл.В сегменте данных родительского приложения требуется определить следующие переменные и структуры:
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ; Данные для перехвата консоли
- newstdin dd ? ; Новый дескриптор стандартного ввода
- newstdout dd ? ; Новый дескриптор стандартного вывода
- read_stdout dd ? ; Дескриптор для использования ReadFile
- write_stdin dd ? ; Дескриптор для использования WriteFile
- bytestoread dd ? ; Всего байт в буфере консоли
- available dd ? ; Счетчик байт, доступных для чтения из консоли
- ; Эта структура по умолчанию не определена, сделаем это сами
- struct SECURITY_ATTRIBUTES
- nLength dd ?
- lpSecurityDescriptor dd ?
- bInheritHandle dd ?
- ends
- ; Описание структур для запуска консольной программы и настройки дескрипторов
- sinfo STARTUPINFO
- sattr SECURITY_ATTRIBUTES
- pinfo PROCESS_INFORMATION
- ; Дополнительно зарезервируем буфер для чтения информации
- buff rb 1024
Code (Assembler) : Убрать нумерацию
- ; Сегмент кода
- section '.code' code readable executable
- ; Заполнить структуру SECURITY_ATTRIBUTES
- mov [sattr.nLength],sizeof.SECURITY_ATTRIBUTES
- mov [sattr.lpSecurityDescriptor],NULL
- mov [sattr.bInheritHandle],TRUE
- ; Создать новые дескрипторы для консольного ввода и вывода
- invoke CreatePipe,newstdin,write_stdin,sattr,NULL
- invoke CreatePipe,read_stdout,newstdout,sattr,NULL
- ; Заполнить структуру STARTUPINFO данными процесса
- invoke GetStartupInfo,sinfo
- ; Установить флаги: использовать собственные дескрипторы
- ; ввода-вывода и возможность изменять видимость окна процесса
- mov [sinfo.dwFlags],STARTF_USESTDHANDLES+STARTF_USESHOWWINDOW
- mov [sinfo.wShowWindow],SW_HIDE
- mov eax,[newstdout]
- ; Установить наш дескриптор для стандартного вывода и ошибок
- mov [sinfo.hStdOutput],eax
- mov [sinfo.hStdError],eax
- mov eax,[newstdin]
- ; Установить наш дескриптор для стандартного ввода
- mov [sinfo.hStdInput],eax
- ; Запустить консольное приложение в режиме suspended
- invoke CreateProcess, NULL, fname, NULL, NULL, TRUE,\
- CREATE_SUSPENDED+NORMAL_PRIORITY_CLASS+CREATE_NEW_CONSOLE,\
- NULL,NULL,sinfo,pinfo
- ; Тут можно выполнить какие-то промежуточные действия, например
- ; спросить у пользователя куда сохранять файл, или проверить
- ; доступность каких-либо данных
- ; Отпустить замороженный процесс на выполнение
- invoke ResumeThread,[pinfo.hThread]
- wait_for_finish:
- ; Проверить активен ли еще запущенный процесс
- invoke GetExitCodeProcess,[pinfo.hProcess],tmp
- cmp [tmp],STILL_ACTIVE
- jne finish
- ; Небольшая пауза чтобы не грузить систему
- invoke Sleep,10
- ; Прочитать данные из буфера консоли без удаления
- invoke PeekNamedPipe,[read_stdout],buff,1023,bytestoread,available,NULL
- ; Если консоль еще ничего не вывела, то продолжать ждать результат
- cmp [bytestoread],0
- je wait_for_finish
- read_data_from_pipe:
- ; Прочитать данные из буфера консоли и записать в наш файл
- invoke ReadFile,[read_stdout],buff,1023,bytestoread,NULL
- ; Теперь в буфере buff содержатся данные из консоли. Их можно
- ; проанализировать, вывести в форму оконного приложения, сохранить
- ; в файл или просто проигнорировать
- ; Дополнительная проверка если весь вывод был меньше размера буфера
- cmp [available],1023
- jna wait_for_finish
- ; Продолжать чтение до окончания данных
- cmp [bytestoread],1023
- je read_data_from_pipe
- jmp wait_for_finish
- finish:
- ; Завершить обработку
Этот способ подходит только для консольных приложений, которые запускаем мы сами. Если надо получить данные из консольного окна другого приложения, то придется воспользоваться другим способом.
В аттаче рабочий пример программы, перехватывающей консольный вывод и сохраняющей его в файл console.txt. В примере выполняется команда "cmd /c dir c:\", выводящая список файлов и каталогов в корне диска C:
Просмотров: 10830 | Комментариев: 11
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ded
(14.06.2010 в 22:09):
пардон, я хотел сказать определить новые хэндлы и структуры под новый процесс..
скажем, как то так
file db 'cmd.exe',0
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov [sinfo1.hStdOutput],eax
invoke CreateProcess, NULL, file, NULL, NULL, TRUE,\
CREATE_NEW_CONSOLE,NULL,NULL,sinfo1,pinfo1
invoke WriteFile,[sinfo1.hStdOutput],buff,1023,bytestoread,NULL
скажем, как то так
file db 'cmd.exe',0
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov [sinfo1.hStdOutput],eax
invoke CreateProcess, NULL, file, NULL, NULL, TRUE,\
CREATE_NEW_CONSOLE,NULL,NULL,sinfo1,pinfo1
invoke WriteFile,[sinfo1.hStdOutput],buff,1023,bytestoread,NULL
ManHunter
(14.06.2010 в 21:18):
Опять мимо. Хэндл [write_stdin] завернут на исполняемое приложение, а не на вывод консоли, и он используется только для передачи каких-то данных исполняемой ПРОГРАММЕ, а не консоли.
ded
(14.06.2010 в 21:13):
Вы намекаете... на 54-ю строку?
Все равно не работает :(
Хотя.. надо же, наверно, восстанавливать стантартные хэндлы ин\оут-ов, заново заполнять структуры под новый процесс.. чтоб туда чота посылать..? так ведь?
Все равно не работает :(
Хотя.. надо же, наверно, восстанавливать стантартные хэндлы ин\оут-ов, заново заполнять структуры под новый процесс.. чтоб туда чота посылать..? так ведь?
ManHunter
(14.06.2010 в 02:30):
А если задействовать головной мозг? К 68-й строке в буфере будет пусто, а bytestoread=0.
ded
(14.06.2010 в 02:22):
и как же вывести на консоль, то, что "выливается" из трубы?
пишем 68-й строкой:
invoke WriteFile,[write_stdin],buff,1023,bytestoread,NULL
и ничо не наблюдаем...
пишем 68-й строкой:
invoke WriteFile,[write_stdin],buff,1023,bytestoread,NULL
и ничо не наблюдаем...
ELM
(23.03.2010 в 10:54):
а да, я тож с такой ерундой сталкевалсо
от биндшелл на фасмах mytechblog.net/?p=21
от биндшелл на фасмах mytechblog.net/?p=21
ManHunter
(03.08.2009 в 19:56):
Поэкспериметрировал, выяснил причину. Нулевой файл создается на очень шустрых машинах. Надо видоизменить код:
wait_for_finish:
; Проверить активен ли еще запущенный процесс
invoke GetExitCodeProcess,[pinfo.hProcess],tmp
cmp [tmp],STILL_ACTIVE
jne finish
; Небольшая пауза чтобы не грузить систему
invoke Sleep,10
Чтобы сперва была проверка активности процесса, а уже потом пауза. И паузу чуть побольше. Исходники перезалил.
wait_for_finish:
; Проверить активен ли еще запущенный процесс
invoke GetExitCodeProcess,[pinfo.hProcess],tmp
cmp [tmp],STILL_ACTIVE
jne finish
; Небольшая пауза чтобы не грузить систему
invoke Sleep,10
Чтобы сперва была проверка активности процесса, а уже потом пауза. И паузу чуть побольше. Исходники перезалил.
Dima
(28.04.2009 в 21:23):
Система Хрюша сп 2, права админские
ManHunter
(27.04.2009 в 08:46):
Система какая? Я проверял на WinXP SP3, на двух стационарных машинах и ноутбуке все работает. Учетная запись с правами администратора.
Dima
(27.04.2009 в 08:40):
Hi
У меня отрабатывает файл но, создается пустой файл console.txt 0 bytes (((
У меня отрабатывает файл но, создается пустой файл console.txt 0 bytes (((
Добавить комментарий
Заполните форму для добавления комментария
Подскажи пожалуйста что не так в моей программе?
Не выводит в созданную консоль текст "123456789"
Код не поместился поэтому даю ссылку
... на васме не отвечают.
http://wasm.ru/forum/viewtopic.php?id=51280
Читать из пайпа читает, но писать туда не хочет
WriteFile() не работает (((
Где я затупил как всегда?
>Опять мимо. Хэндл [write_stdin] завернут на исполняемое приложение, а не >на вывод консоли, и он используется только для передачи каких-то данных >исполняемой ПРОГРАММЕ, а не консоли.
Я конечно медитирую над этой фразой, но тогда как вывести в новосозданную консоль "123456789"? Только через VkKeyScan() и найденный хендл окна получается?