Реферат: Вызов функции в другом процессе
Название: Вызов функции в другом процессе Раздел: Рефераты по информатике Тип: реферат | ||||||||||||||||||||||||||
Вызов функции в другом процессе Сергей Холодилов I just called to say I love you, And I mean it from the bottom of my heart. Stevie Wonder Внедрению DLL так или иначе (обычно в связи с перехватом API) посвящено достаточно большое количество статей. Но ни в одной из тех, которые я читал, не говорится, как извне заставить эту DLL сделать что-нибудь полезное. Обычно авторы ограничиваются перехватом необходимых API-функций где-нибудь в DllMain и последующей реакцией на вызовы этих самых функций. Между тем, взаимодействие с внедрённой DLL даёт возможность корректировать и направлять её работу и, тем самым, позволяет добиваться значительно большего эффекта. Если внедрённая DLL создаёт свой поток, задача взаимодействия легко решается, так как в этом случае можно использовать любые методы IPC: сообщения, сокеты, именованные каналы, … , при желании можно даже COM-сервер сделать :)
Но это всё более-менее очевидные и не очень красивые (на мой взгляд) способы. Мне кажется, я нашёл более интересный и элегантный метод. Ему и посвящена эта статья. Идея тривиальна. Алгоритм состоит всего из четырёх шагов (плюс ещё один по желанию): Так или иначе загрузить в адресное пространство процесса-жертвы DLL, содержащую нужную функцию.
Получить адрес загрузки DLL. Получить адрес функции. Вызвать функцию при помощи CreateRemoteThread. (опционально) Дождаться завершения потока и получить возвращаемое значение функции вызовом GetExitCodeThread. При желании можно напрямую записать весь исполняемый код в адресное пространство процесса-жертвы и запустить его тем же CreateRemoteThread. При большом желании можно добиться, чтобы это заработало... Основная проблема, подстерегающая вас на этом пути, заключается в том, что все функции, которые вызывает ваш код, должны находиться точно по тем адресам, куда передаётся управление. С учётом того, что: код будет расположен в случайном месте адресного пространства, так как вам вряд ли удастся выделить память по тому же адресу; DLL могут быть загружены по другим адресам, «само собой» ничего не получится. Чтобы добиться работоспособности кода, нужно модифицировать используемые вашим кодом адреса, то есть, фактически, выполнить задачу загрузчика. А зачем выполнять её вручную, если можно положиться на загрузчик :) ? Использование CreateRemoteThread связано с очевидными ограничениями: Поддерживается только линейка Windows NT/2000/XP.
Прототип вызываемой функции должен соответствовать прототипу функции потока. Кроме того, нужно иметь солидные права доступа к процессу-жертве: PROCESS_CREATE_THREAD для запуска потока. PROCESS_VM_READ для определения адреса. PROCESS_VM_OPERATION + PROCESS_VM_WRITE (разрешение на выделение памяти и запись в адресное пространство процесса) может пригодиться, если вы хотите передать вызываемой функции что-нибудь посущественнее, чем четыре байта.
В общем случае, при помощи функций EnumProcessModules и GetModuleFileNameEx можно перебрать все загруженные в процесс-жертву модули, найти среди них нужный и получить адрес его загрузки.
Но если DLL внедрялась с помощью создания в процессе-жертве потока, поточной функцией которого является LoadLibrary, можно поступить проще. В этом случае код завершения потока является возвращаемым значением LoadLibrary, то есть как раз адресом загрузки DLL в процессе-жертве.
Есть два способа получить адрес функции: простой и для настоящих программистов. :) Простой способ основан на том, что смещение начала функции от начала DLL – величина постоянная, от процесса не зависящая. Это значит, что если: загрузить в свой процесс ту же DLL; получить адрес нужной функции; вычесть из адреса функции адрес загрузки DLL; прибавить к получившемуся смещению адрес загрузки DLL в процессе-жертве, то получится адрес функции в процессе-жертве.
Если по каким-то причинам DLL уже загружена в процесс, то, наверное, этот способ можно рекомендовать даже самым-самым настоящим программистам. А вот если DLL нужно специально грузить, то, по-моему, опять получается некрасиво. :) Способ для настоящих программистов Реализовать функцию GetProcAddressInOtherProcess, принимающую в первом параметре описатель процесса. Она будет разбирать таблицу экспорта указанной DLL из указанного процесса, находить там нужную функцию и возвращать её адрес. Если добавить функции LoadLibararyInOtherProcess и FreeLibraryInOtherProcess (которые несложно написать), получится совсем красиво, так как с чужим процессом можно будет работать почти так же, как и со своим. Именно этот способ кажется мне интересным и элегантным, и именно его реализации посвящена статья. Поиск экспортируемой функции в PE-файле Как вы, наверное, знаете, формат всех исполняемых файлов в Windows (включая DLL, ocx, sys, и прочие) называется PE (расшифровывается как Portable Executable, но большого смысла не несёт, просто название, ничем не хуже других) форматом, а сами файлы, соответственно, PE-файлами. Чтобы отыскать адрес нужной функции в DLL, придётся разобраться с той частью PE-формата, которая отвечает за экспорт.
Как в PE-файле добраться до секции экспорта Любой PE-файл начинается с заголовка DOS, формат которого отражён в структуре IMAGE_DOS_HEADER.
Из всех полей этой структуры для нас интерес представляет только поле e_lfanew, которое является смещением от начала файла (в терминологии PE-формата такие смещения называются RVA – Relative Virtual Address) до PE-заголовка. Формат PE-заголовка представлен структурой IMAGE_NT_HEADERS (она определена с использованием препроцессора и, на данный момент, соответствует структуре IMAGE_NT_HEADERS32):
Из неё нас интересует только поле OptionalHeader, которое разворачивается в ещё одну структуру:
И опять, нам нужно только одно поле – DataDirectory, а, точнее, только элемент DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. Структура IMAGE_DATA_DIRECTORY описывает расположение в памяти одной из секций PE-файла. Она определёна следующим образом:
Элемент DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] относится к секции экспорта. Итого: В начале файла расположен IMAGE_DOS_HEADER. По смещению IMAGE_DOS_HEADER::e_lfanew находится IMAGE_NT_HEADERS. IMAGE_NT_HEADERS::OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] описывает секцию экспорта. Он содержит RVA и размер секции. Как в секции экспорта найти адрес функции Секция экспорта начинается со структуры IMAGE_EXPORT_DIRECTORY.
Здесь: AddressOfFunctions – RVA (смещение от начала файла) массива, содержащего RVA функций. AddressOfNames – RVA массива, содержащего RVA имён функций. AddressOfNameOrdinals – RVA массива индексов функций. Элемент n этого массива содержит индекс в массиве адресов функций, соответствующей n-ному элементу в массиве имён функций.
NumberOfFunctions – количество элементов массива адресов функций. NumberOfNames – количество элементов массива имён функций и массива индексов функций. Base – базовое значение ординала экспортируемых функций. Для получения индекса функции, экспортируемой по ординалу, надо вычесть из её ординала значение Base. В результате, для поиска адреса функции, экспортируемой по имени, нужно сделать примерно следующее (в псевдокоде):
В конце концов у меня получилось три функции. Первая находит секцию экспорта:
Вторая перебирает массив имён функций в поиске заданного имени:
Третья функция использует первые две и находит нужную функцию в указанной DLL в указанном процессе:
В качестве примера я написал три приложения: aggressor.exe, victim.exe и insider.dll. Victim и insider абсолютно пассивны, все действия выполняются aggressor-ом. Aggressor: запускает victim.exe; загружает в него insider.dll; получает адреса трёх экспортируемых функций; вызывает эти функции; выгружает insider.dll из victim.exe .
Для реализации перечисленных действий, да и вообще на будущее, в aggressor реализованы следующие полезные функции:
Предназначение функций, я надеюсь, понятно из их названий и кратких комментариев. Понимание реализации также не должно вызвать затруднений, прокомментировано всё достаточно подробно, да и сам код не такой уж головоломный. Успешных вам вызовов! Список литературы Джеффри Рихтер, «Programming Application for Microsoft Windows», четвёртое издание. Тихомиров В.А. «Перехват API-функций в Windows NT/2000/XP». Мэтт Питрек «Форматы PE и COFF объектных файлов» Максим М. Гумеров «Загрузчик PE-файлов» |