Мелкомягкие хуки: Microsoft Detours — честное средство для настоящего хакераПомнишь, как ты писал код, подменяющий API-вызовы в своем крутом трояне? Или хотя бы читал статью о том, как это делается? Дай угадаю — тебе не понравилось отсутствие хоть какого-нибудь инструментария от Microsoft? Так вот, хочу тебя обрадовать — теперь ситуация кардинально изменилась. Встречай Microsoft Detours! WTF?Microsoft Detours — проект, разрабатываемый в лабораториях Microsoft Research (хотя, судя по дате последнего обновления — уже не разрабатываемый), позволяющий перехватывать Win32 API-вызовы. 64 бита он тоже умеет, но.. за 10 килобаксов :). Да, коммерческая (и 64-битная) версия стоит именно столько, а вот для образовательных (хе-хе, конечно, конечно) целей он абсолютно бесплатен. Несмотря на то, что проект давненько не обновлялся, он содержит весь необходимый для перехвата вызовов функционал, и работает безглючно (по крайней мере, я каких-либо багов не заметил). Скачать инсталлятор Detours можно на research.microsoft.com. По умолчанию он установится в Program Files\Microsoft Research. Если ты считаешь, что на этом установка окончена, ты глубоко заблуждаешься. Сначала нужно собрать бинарники самой библиотеки и примеры к ней — для этого в консоли VC++ надо выполнить make, и подождать, пока все это добро соберется. А вот ху[Артем, не используй это слово, у нас же приличный журнал — прим. ред.]. Как бы не так! Мелкомягкий компилятор, датируемый 2008-м годом, наотрез отказался его собирать. Поковырявшись самостоятельно, я решил погуглить. Оказалось, что Microsoft в курсе этой проблемы, но делать ничего не собирается. Почесав недолго репу, на detours я благополучно забил. Некоторое время спустя, поставив ради интереса 2005-ю студию, я решил попытать счастья снова — и опа, собралось! Так что учти, что для сборки сей замечательной библиотеки тебе понадобится Visual C++ 2005 (можно и express). Насчет 2010-й не в курсе — в то время ее не было, а сейчас нет желания иметь какие-то дела со студией вообще, поэтому весь код был написан в Visual C++ 2005/2008 (хотя, писать можно и в блокноте). От detours нам понадобится заголовочный файл detours.h, а также библиотеки detoured.lib, detours.lib и detoured.dll. А писать мы с тобой будем соксификатор на SOCKS4 — программка полезная, отлично показывающая возможности Microsoft Detours, а заодно вспомним спецификации этого несложного протокола. Как оно работает?Принцип работы прост, как дырка от жо[Артем, опять ты за свое! — прим.ред.] — мы пишем DLL, в которой реализуем нашу версию вызова cool_call(), пишем приложение, которое запускает нужный нам софт с нужной библиотекой, и подсовывающий этой софтине наш API-вызов, устанавливая jump с адресом левого вызова в начало оригинального вызова. Для примера, запустим в дебаггере любое приложение, использующее, к примеру, функцию connect(), поставим бряк на эту функцию, при остановке выполнения нажмем на Go To Disassembly, и увидим:
Перейдем по F10 до этой строчки, нажмем F11 и окажемся в
Знаешь, что такое mov edi, edi? Это, по сути, NOP, пропуск такта процессора, неиспользуемая инструкция. Сюда-то и вставляется jump на нашу функцию. Теперь запустим эту программу с «левым» вызовом, подсунутым ей Detours’ом. И что же мы видим?
Да-да, вместо NOP’а появился jump на адрес нашей реализации функции connect() =). От простого к сложномуНапишем простую программку, которая будет запускать приложение с нашей библиотекой. Для запуска понадобится всего одна функция — DetourCreateProcessWithDll(), и мы должны передать 7 что-то значащих параметров, остальное, в лучших традициях WinAPI — NULL.
Запускать его нужно в приостановленном режиме, поэтому CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
В итоге код запуска приложения выглядит так:
Не забудь обнулить структуры перед передачей в функцию, если собираешься их потом использовать! Если приложение запустилось нормально — нужно возобновить (в нашем случае — запустить) выполнение его главного потока функцией ResumeThread, передав ей в качестве параметра pi.hThread. Собственно, с «Запускатором», все. Четверо носковТеперь переходим к более сложному этапу — написанию самой DLL. Для соксификации приложения нам надо перехватить всегонавсего один вызов — всем известный connect(). Создавай в студии пустой проект — DLL. Перед перехватом нужно сохранить адрес оригинальной функции, чтобы можно было ее вызвать:
После этого пишем свою функцию, полностью повторяющую оригинал. Обрати внимание — полностью повторяющую, то есть не только по параметрам, но и по соглашению о вызове! На эти грабли мне уже «посчастливилось» наступить, поэтому считаю своим долгом предупредить об этом тебя :). Что мы должны сделать, чтобы соксифицировать приложение? Нам надо перехватить вызов функции connect(), и, вместо установления соединения с указанным сервером, соединиться с SOCKS-сервером, передать ему запрос на соединение, и вернуть получившийся дескриптор сокета приложению, которое запросило соединение. А приложение даже и не поймет, что работает через SOCKS-прокси, ибо протокол SOCKS прозрачен и для клиента, и для сервера. Моя реализация этой функции проста до безобразия:
connectToSocks4() — функция, устанавливающая соединение с SOCKS-сервером. На всякий случай, я установил хуки еще и на send() и recv(), хотя в конкретно данном случае это не требуется. Их реализацию приводить тут я не буду — она аналогична функции connect(). В точке входа нашей динамически подгружаемой библиотеки нужно, собственно, установить хуки (или снять, при завершении работы с ней). Делается это с помощью нескольких функций:
После выполнения подмены нужно завершить транзакцию вызовом функции DetourTransactionCommit(). В случае, если нужно убрать хук, выполняются те же самые действия, только DetourAttach() заменяется на DetourDetach(). Теперь давай посмотрим спецификацию протокола SOCKS4. После установки коннекта к SOCKS-серверу нужно отправить пакет с информацией о наших намерениях. Пакет выглядит так:
После этого сервер отправит нам пакет, в котором укажет, все ли его устраивает:
Пример «общения» клиента с сервером: Клиент:
Сервер:
Где 0xXX — случайное значение (эти байты должны игнорироваться) После этого можно общаться с SOCKS-сервером, как с обыкновенным сервером, к которому мы и подключаемся. Вот как я реализовал подключение к SOCKS-у:
Не забудь — при коннекте к сокс-серверу нужно использовать оригинальную функцию connect(), а то получится бесконечная рекурсия. ВердиктНу, вот и все — сегодня я научил тебя перехватывать API-вызовы абсолютно легальным, с точки зрения программирования, путем, используя костыль от самого Microsoft. Как всегда, не оставлю тебя без задания — слабо реализовать тут SOCKS5? (не бойся, пятый протокол ненамного сложнее, чем четвертый). Я надеюсь, ты понимаешь, что эту библиотеку можно использовать несколько в других, более «крупных», целях. Ну, ты меня понял :). Только не забывай про законодательство, а то дяди в серой форме поставят вполне легальные хуки на все твои действия. Возможно, даже сроком в несколько лет. |