Меня всегда интересовал вопрос о программах-невидимках. Представьте, есть резидентный код, который выполняет некие действия на вашем компьютере, а обнаружить его вы не можете. Возмутительно, не правда ли? А потому иметь такую программу мне очень хотелось
.В Интернете эта тема также широко обсуждалась, обсуждается и, думаю, будет обсуждаться. Среди огромного количества мнений, суждений и прочего словесного мусора, мне попался вердикт одного гуру, гласивший:
"Вы не можете скрыть процесс от функции NtQuerySystemInformation." Это было похоже на окончательный приговор. Хрустальная мечта моего детства была готова разбиться раз и навсегда. Но:Очень надеюсь, что о программах-невидимках мечтал не только я. Вам, мои духовные собратья, и посвящается эта статейка. Серьезным дядям (в народе их кличут системными администраторами), работа которых заключается в удушении свободы простого народа : виноват, простых юзеров, читать это не следует. Все дальнейшие рассуждения приводятся для процессора
i386 ОС Windows NT 4.0 (SP4 или SP6), так как в Unix системах я не разбираюсь. Что "как"? Да, вы не ослышались! Каюсь!! Простите!!! Позор на мою седую голову!!!! Пощады!!!! Ну а теперь, после того как самая интеллектуальная часть аудитории громко хлопнула дверью, вступление можно закончить и перейти к делу.Сначала поговорим о том, как и для чего, собственно, используются программы-невидимки. Предположим, что вы зашли в Интернет и закачали замечательную программку, картинку, документ или что-то еще. А потом оказалось, что злобные хакеры заложили в эту программку вирус или троянского коня. Как опытный и продвинутый юзер, вы сразу запускаете
Task Manager и видите там ЕГО. После чего ОН убивается кнопочкой End Process, и вы спокойно можете наслаждаться праведно выкачанным добром. Некоторые особо злобные и зубастые хакеры пытаются запустить драйвер, но, к счастью для нас, для этого необходимы права администратора. Это обычное развитие событий. А теперь предположим, что в Task Manager вы ЕГО не видите. Что делать? Обычные юзеры могут подумать, что на этот раз его пронесло, и в файле трояна не оказалось. Но это не наши юзеры. Нормальный параноидальный юзер сразу почувствует подвох и будет прав.Сразу хочу отметить, речь не идет о классическом вирусе. Безусловно, вирусы - прекраснейшее изобретение человечества, но у них есть один серьезнейший недостаток. А именно, работает он всего ОДИН раз при запуске инфицированной программы. Выполнив свой кусочек кода, он вынужден отдать управление программе-носителю. Если же он хочет стать резидентом,
то ему опять же надо где-то "жить", то есть создавать процесс.Так возможно ли это: быть резидентом (это звучит гордо!) и не "светиться" в Task Manager? Отвечаю, ЭТО ВОЗМОЖНО! Позвоните прямо сейчас, и всего за 49.99$ ваша мечта станет ре: Гм, простите, никак не выведу эту рекламную бациллу (язык не поворачивается, назвать ее благородным словом
"вирус").Более того, что нам этот микрософтовский
Task Manager или Spy++, любой мало-мальски серьезный программер напишет свой Task Manager. Поэтому будем прятаться от всего их порочного семейства до седьмого колена включительно.Сердцем любого Task Manager является функция NtQuerySystemInformation. Остальное - это оболочки, рамочки и прочая мишура. Хотя эта функция и недокументирована компанией
Microsoft, но в Интернете про нее написано предостаточно. Поэтому здесь в подробности вникать не будем, а скажем, что всю информацию, которую вы видите на экране, предоставляет эта, и только эта, функция. И увы, гуру был прав! Вы действительно не можете спрятать процесс от NtQuerySystemInformation (по крайней мере, я не знаю, как это можно сделать). Но кто сказал, что резидентами становятся только процессы! Ведь кто такой резидент? Это всего навсего кусочек памяти, который периодически получает управление. Поэтому помимо процесса резидентом может быть драйвер, поток (thread), волокно (fiber), APC и DPC процедуры, функции обработки исключений и прерываний. Возможно, и еще куча всего такого, о чем я не знаю. (Кстати, если говорить совсем строго, то исполняется не процесс, а его потоки, поэтом процесс резидентом не является).Итак, на сегодняшний день, я знаю следующие способы прятать резидентный код:
При использовании данного метода не создаются ни новый процесс, ни новый поток. Загрузка библиотеки в другие процессы тоже не требуется. Таким образом, единственным косвенным признаком существования постороннего резидентного кода является разовое увеличение используемой памяти. Но память - наиболее активно расходуемый ресурс. Практически все программы
постоянно выделяют и освобождают память и определить, какое количество занятых килобайт является нормой для того или иного процесса, очень затруднительно.Разумеется, есть и недостатки. Во-первых, резидентный код может выполняться, только когда система передает управление потоку-носителю. Но возможна ситуация, что поток имеет весьма низкий приоритет, и из-за вытесняющей многозадачности редко будет получать управление. Более того, поток может перейти в состояние ожидания, и оставаться в нем неопределенно долго. Если же поток будет завершен, то завершится и резидентный код. Суммируя, работа резидентного кода в решающей степени зависит от потока-носителя.
Вкратце, алгоритм предлагаемого метода состоит в следующем:
Вот, собственно, и все. Очень просто, не правда ли? (Это я у своих гуру собезъянничал). Разумеется, некоторые непринципиальные детали были опущены. По сути, во многом то же самое выполняет сама ОС при исполнении
DPC и APC процедур (по книге Хелен Кастер).Чтобы не оставаться голословным, я написал маленькую программку (
baby.exe) с небольшой библиотечкой (zzz.dll). В данном примере резидентный код просто пишет в файл. Использовать их очень просто. Скопируйте библиотеку в системную директорию system32 и запустите baby.exe. Если все прошло успешно, то в корневой директории диска C: появится файл 2_.2 с цифрами (если у вас уже есть такой файл (да вы батенька, маньяк!), то цифры будут дописываться в его конец). По прошествии времени (в зависимости от загруженности вашего компьютера) файл (по идее) будет медленно расти (если долго не будет, то удалите его, и он опять (наверное) появится).В
TaskManager никаких новых процессов или потоков не появится. (Библиотек тоже, но в TaskManager вы этого не увидите.) Да! Чуть не забыл, чтобы "убить резидента", перезапустите Explorer.exe.Наконец, последнее. Программа не создана ни для каких практических целей. Это всего лишь демонстрация работы алгоритма и проба сил. В ней могут быть различного рода ошибки. Она может работать плохо или вообще не работать. Короче, "мы отклоняй претензий любой вид, любой форма, любой содержания
". В конце концов, я не бог, не царь и не герой, то есть не Великий Рулез, не Microsoft и не гуру.
В заключении автор хотел бы выразить благодарность всем, кто помогал и поддерживал его. Я особенно благодарен книге А.В. Коберниченко
"Недокументированные возможности Windows NT".Если у вас есть вопросы, предложения, комментарии и т.д., то мой почтовый адрес
tanaka@rambler.ruTanaka
Реакция читателей на мою статью оказалась для меня неожиданной.
Во-первых, я поражен благожелательностью читателей (и очень благодарен Вам за это
), во-вторых, удивителен столь живой интерес к этой весьма отвлеченной теоретической теме, в-третьих, меня поняли не совсем так, как я рассчитывал. Поэтому, на мой взгляд, необходимы некоторые пояснения.Что предлагаемый мною способ НЕ ДЕЛАЕТ:
Процесс не создается, поэтому скрывать его отсутствие не нужно.
Поток создается, но
, быстро отработав, нормально завершает работу, поэтому скрывать его тоже не нужно. (Кстати, может кто-нибудь, действительно, умеет скрывать поток без драйвера? Было бы очень интересно!)Драйвер мне вообще не нужен.
Скрывать что-либо с помощью драйвера, это хорошая идея, более того
, уже реализованная. Проблема только в запуске драйвера.Еще один вопрос ко всем. Как все-таки обнаружить подобного резидента? Или, как детектировать вызов
APC и DPC процедур операционной системой?Все хотят видеть исходники, но почему
? Основную идею я описал, а техническое исполнение программы оставляет желать лучшего (не все рождаются программистами).Но раз очень хочется, то пожалуйста (но я предупреждал
!). По-моему, все, чтобы написать аналогичную прогу, описано. помещаю полностью, но только в нем нет ничего интересного, да и написан он ужасным стилем. Все то же самое можно найти у Рихтера и гораздо лучше написанное.Все, что делает
exe - это загрузка библиотеки в Explorer.exe и запуск библиотечной функции zzzInit.Исходник
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_INFORMATION;
Надеюсь, что мое предположение верно, и данная структура описывает волокно
(fiber). Если кто-то знает больше, сообщите свои мысли, пожалуйста, особенно про E0C.typedef struct _MYFIBER { DWORD arg; DWORD Exceptions; DWORD StackBase; DWORD StackLimit; DWORD E0C; //???????????????????? CONTEXT context; } MYFIBER, *PMYFIBER;
Основная функция переключения контекста потока
1 аргумент -
handle на поток, куда внедряется наш код2 аргумент - указатель на предварительно созданный
fiber3
аргумент - указатель на исполняемый кодvoid my_switch( HANDLE hThread, //thread to be interrupted PVOID fib, //fib to switch to DWORD function) //function to process { CONTEXT cont; BOOL bo; THREAD_BASIC_INFORMATION threadbuff; ULONG l; MYFIBER temp_fiber; memset(&cont,0x00,sizeof(CONTEXT)); memset(&threadbuff,0x00,sizeof(THREAD_BASIC_INFORMATION)); // Read context and stack of target stack cont.ContextFlags=CONTEXT_CONTROL; bo=GetThreadContext(hThread,&cont); bo=NtQueryInformationThread(hThread,0,&threadbuff,sizeof(THREAD_BASIC_INFORMATION),&l); //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); memmove((BYTE *)&temp_fiber.StackBase,(BYTE *)threadbuff.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.context,(BYTE *)&cont,sizeof(CONTEXT)); //Помещаем в блок окружения (Teb) и контекст потока значения из созданного //и проиницилизированного волокна (fiber) //2. 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+0xe0c,(BYTE *)fib+16,4); memmove((BYTE *)threadbuff.Teb+20,(BYTE *)&fib,4); //pointer to saved fib memmove((BYTE *)&cont.Edi,(BYTE *)fib+0xb0,4); memmove((BYTE *)&cont.Esi,(BYTE *)fib+0xb4,4); memmove((BYTE *)&cont.Ebp,(BYTE *)fib+0xc8,4); memmove((BYTE *)&cont.Ebx,(BYTE *)fib+0xb8,4); memmove((BYTE *)&cont.Ecx,(BYTE *)fib+0xcc,4); memmove((BYTE *)&cont.Esp,(BYTE *)fib+0xd8,4); cont.Eip=(DWORD)function; bo=SetThreadContext(hThread,&cont); memmove((BYTE *)fib,(BYTE *)&temp_fiber,sizeof(MYFIBER)); }
Поток, внедряющий код в контекст заданного потока.
Аргумент в основном - это идентификатор потока, куда внедряется код.
DWORD WINAPI thread2(LPVOID arg2) { ULONG i,l; PVOID fib1; BOOL bo; DWORD zero=0; HANDLE hThread=0; OBJECT_ATTRIBUTES ObjectAttributes; CLIENT_ID thread1; MYPAR * par3; MYARG * pmyarg; //Подготовка параметров (несущественно) par3=(MYPAR *)malloc(sizeof(MYPAR)); pmyarg=(MYARG *)arg2; thread1.UniqueThread=(HANDLE)pmyarg->id1; thread1.UniqueProcess=(HANDLE)GetCurrentProcessId(); (*par3).client_id.UniqueThread=(HANDLE)pmyarg->id2; (*par3).client_id.UniqueProcess=(HANDLE)GetCurrentProcessId(); par3->fib=0; par3->st=0; memset(&ObjectAttributes,0x00,sizeof(OBJECT_ATTRIBUTES)); //Открыть handle потока по его идентификатору. bo=NtOpenThread(&hThread,MAXIMUM_ALLOWED,&ObjectAttributes,&thread1); //Создать волокно fib1=CreateFiber(NULL,(LPFIBER_START_ROUTINE)f1,(void *)par3); i=SuspendThread(hThread); if(i==0xffffffff) { l=GetLastError(); } //Переключить контекст заданного потока my_switch(hThread, fib1,(DWORD)f1); i=ResumeThread(hThread); //Очистить память CloseHandle(hThread); if(arg2!=0) free(arg2); return 1; }
Вот, собственно, и ВСЕ.
Если этот хлам Вам понятен, и Вы извлекли из этих "исходников" что-то полезное, то Вы
- Истинный Монстр программинга, хакинга, кракинга и бодибилдинга.Если возникли вопросы
, пишите tanaka@rambler.ruобсудить | все отзывы (3) | |
[64525; 44; 8.27] |
|
|