Сокрытие процессов во всеми любимой Windows занятие довольно интересное, которое, можно сказать, иногда становиться хобби. 🙂 В этой ОС очень много хороших, но к сожалению не доведённых до ума вещей, которые при
ближайшем рассмотрении дают интересные "недокументированные" возможности. Сейчас мы рассмотри тему существования процессов и модулей без загрузки исполняемых файлов. Эта тема вообще-то была незаслуженно забыта со времён MS-Dos, и у многих людей сложилось впечатление, что это невозможно (или очень сложно) сделать в Win2K/XP.

Способ нумбер уно

Все знают, что Маленькие и Мягкие друзья вовсю продвигают в массы свою
"замечательную" (но далеко не завершённую) файловую систему – NTFS. Она, конечно, очень полезна на серверах и в научных центрах, где приходится шифровать файлы и квотировать место для
пользователей/групп, но БГ рекомендует её и для домашнего использования. 🙂 Хотя 99% вирусов, троянов и прочей
нечисти живёт именно в домах юзеров (прямо домашние животные какие-то :)). Вот и мы сейчас подкинем
экспериментаторам ещё один маленький трюк резидентности.

В NTFS реализована файловая многопоточность – каждый файл, помимо основного
неименованного потока (содержащего, собственно, главную информацию) может иметь
именованные потоки. Они никак не сказываются
на размере файла (!!!) и призваны (по мнению создателей) хранить служебную информацию и авторские права. Но раз можно что-то записать, и это не будет видно в Explorer’e, то это уже интересно. 😉 Доступ к потоку можно получить так:

x:\some_path\file_name:stream_name

Так что можно с помощью консоли записать в поток файл:

type some_file > some_file:some_stream

и прочесть:

more < some_file:some_stream Но самое забавное это то, что узнать есть ли у файла потоки и какие нельзя "общеюзерскими средствами". Что ж, придётся написать просматривалку потоков. Для этого Delphi 7 как раз подойдёт… Стоп. На Delphi просто так это не написать. Дело в том, что для поиска имён потоков надо использовать BackUpSeek из юнита Windows.pas, но толи злобные зулусские хакеры ломанули Бормана, толи программист который писал Windows.pas опился пива (хотя судя по юниту употребляли кое-что покрепче :)), но в результате чего функция объявлена так: function BackupSeek(hFile: THandle; dwLowBytesToSeek, dwHighBytesToSeek: DWORD;
var lpdwLowByteSeeked, lpdwHighByteSeeked: DWORD; lpContext: Pointer): BOOL; stdcall;


lpContext – задекларирован как указатель. А у MS это задекларировано так:

BOOL BackupSeek(
HANDLE hFile,
DWORD dwLowBytesToSeek,
DWORD dwHighBytesToSeek,
LPDWORD lpdwLowByteSeeked,
LPDWORD lpdwHighByteSeeked,
LPVOID* lpContext
);

Придётся декларировать BackupSeek самим:

function BackupSeekA(hFile: THandle; dwLowBytesToSeek, dwHighBytesToSeek: DWORD;
lpdwLowByteSeeked, lpdwHighByteSeeked: PDWORD;var lpContext: Pointer): BOOL; stdcall; external kernel32 name 'BackupSeek';

Так… Ну теперь всё готово. 🙂 Можно приступать к самому способу перечисления потоков. Для этого используются функции BackupSeek и BackupRead:

Procedure EnumStreamsInfo(path:PChar);
var s:string;
wszStreamName:array[0..MAX_PATH-1] of WCHAR;
sid:WIN32_STREAM_ID absolute wszStreamName;
h:thandle;
p:pointer;
temp,dw1, dw2:cardinal;
begin
p:=nil;
h:=CreateFile(path,GENERIC_READ, 0, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_POSIX_SEMANTICS, 0);
while true do begin
memo1.Lines.Add('_-_-_-_-_-_-'); //
разграничиваем записи 😉
BackupRead(h, @wszStreamName, sizeof(sid), temp, FALSE, TRUE, p);
if temp = 0 then break;
if sid.dwStreamNameSize > 0 then begin //
если у потока есть имя, то узнаем его
BackupRead(h, pointer(integer(@wszStreamName) + sizeof(sid)), sid.dwStreamNameSize, temp, FALSE, TRUE, p);
if temp <> sid.dwStreamNameSize then break;
{
для простоты всё выводиться в TMemo }
memo1.Lines.Add(PwideChar(@sid.cStreamName)); 
end;
memo1.Lines.Add('Stream size: '+IntToStr(sid.Size));
s:='Type of data: ';
case sid.dwStreamId of //
определяем тип потока
BACKUP_DATA: s := s+'data';
BACKUP_EA_DATA: s := s+'extended attributes';
BACKUP_SECURITY_DATA: s := s+'security';
BACKUP_ALTERNATE_DATA: s := s+'other streams';
BACKUP_LINK: s := s+'link';
else s := s+'unknown';
end;
memo1.Lines.Add(s);
if sid.Size>0 then
{
я не думаю, что у кого-то есть потоки больше 4ГБ, поэтому второй параметр поиска = 0 }
BackupSeekA(h, sid.Size, 0, @dw1, @dw2, p);
end;
BackupRead(h, @sid, 0, temp, TRUE, FALSE, p);
CloseHandle(h);
end;

Вот теперь мы знаем о потоках "почти всё". 🙂 Осталось самое интересное –
обещанная возможность выполнения программы без основного exe или самоуничтожение exe. Эту фишку обнаружил X[ei]NOmorph, за что ему большое спасибо.
Как уже многие подозревают, раз в потоки можно записать всё что угодно, значит можно и exe. Так-с… Для начала запишем какую-нить прогу в текстовый файл и запустим (для запуска подойдёт WinExec(‘some_file:somestream’, SW_SHOWDEFAULT)). Вот что мы видим в
"Диспетчере задач Windows":

Значит можно запускать не только exe. 🙂 Неподготовленный юзер не слабо удивиться увидев такое. Но это мелочь, сейчас будет веселее – удаляем 2.txt… Файла больше нет, а программа работает, и довольно уверенно работает (главное чтобы она вся была загружена, т.е. не содержала большого объёма данных). Это говорит о том, что в MS плохо тестируют своё ПО… Если ещё и сокрыть процесс от глаз
"Диспетчера задач", то эта дырка позволяет сделать всеми любимый в прошлом резидент Ring3. Можно создать
"невидимое юзером" окно и при закрытии винды записывать себя на диск, а при запуске удалять себя. Вот небольшой пример
"самоудаления":

program KillYourself;

{$APPTYPE CONSOLE}

uses
Windows;

const StreamName=':dead_stream';

function CopyFileI(source, dest:pchar):boolean;
var h,h2:integer;
mas:array[1..2048] of byte;
i:cardinal;
done:cardinal;
begin
result:=false;
h:=CreateFile(source,GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
if h=-1 then exit;
h2:=CreateFile(dest,GENERIC_WRITE,FILE_SHARE_WRITE,nil,OPEN_ALWAYS,0,0);
while ReadFile(h,mas,sizeof(mas),done,nil) and(done<>0) do
WriteFile(h2,mas,done,i,nil);
CloseHandle(h);
CloseHandle(h2);
result:=true;
end;

var
s:string;
begin
if pos(StreamName,Paramstr(0))>0 then begin
Sleep(2000);
DeleteFile(PChar(Paramstr(1)));
MessageBox(0,'Killed',PChar(Paramstr(1)),mb_ok);
Exit;
end;
readln(s);
if s='kill_yourself' then
begin
s:=Paramstr(0)+StreamName;
if CopyFileI(PChar(Paramstr(0)),PChar(s)) then
begin
s:=s+' '+ParamStr(0);
WinExec(PChar(s),SW_SHOWDEFAULT);
end;
end;
end.

Вот так можно жить "без тела". И ещё одно – потоки никак не сказываются на
индикаторе занятого объёма раздела. 🙂 Получается, что один
"маленький" файл с размером 0 может замусорить весь винт (и найти
"грязнулю" вручную очень сложно).

Способ нумбер ту

Данная фишка позволяет удалять загруженные и используемые
DLL’ки в 9x/ME/NT/2K/XP. Для этого скачиваем http://www.anticracking.sk/EliCZ/export/DelMod.zip
и находим в архиве (среди кучи всего хорошего) DeleteModule.dll (собственно сама удалялка) и DeleteModule.bat (сорс для таких любопытных как мы :)). Из сорса становиться ясно, что дллка работает хитро – резервирует память для сохранения модуля, потом копирует snapshot, выгружает и удаляет модуль, после чего восстанавливает
"модуль" из сохранённого snapshot’a. (Для NT вызывается ещё и недокументированная функция NtQuerySection из ntdll.dll). Теперь подключим эту дллку к нашей программе:

function DeleteModuleA(ModuleName:PChar):longbool;stdcall;external 'DeleteModule.dll';
function DeleteModuleW(ModuleName:PWideChar):longbool;stdcall;external 'DeleteModule.dll';

и удостоверимся что она работает. 🙂

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии