Реферат: Вызов функции в другом процессе
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-сервер сделать :)
ПРЕДУПРЕЖДЕНИЕ В описании DllMain сказано, что некоторые функции, в том числе CreateThread, из неё вызывать нельзя. Объяснение «почему они говорят, что нельзя» можно найти у Рихтера (в русском четвёртом издании это глава «DLL: более сложные методы программирования», раздел «Как система упорядочивает вызовы DllMain»), у него же написано, что на самом деле можно, если осторожно. :) Просто при создании потока надо не забывать, что его выполнение начнётся не раньше, чем текущий поток покинет DllMain. |
Но это всё более-менее очевидные и не очень красивые (на мой взгляд) способы. Мне кажется, я нашёл более интересный и элегантный метод. Ему и посвящена эта статья.
Идея
Идея тривиальна. Алгоритм состоит всего из четырёх шагов (плюс ещё один по желанию):
Так или иначе загрузить в адресное пространство процесса-жертвы DLL, содержащую нужную функцию.
ПРИМЕЧАНИЕ «Так или иначе» означает, что DLL может быть загружена любым способом. Например, это может быть advapi32.DLL, которую процесс-жертва грузит сам. Если вы хотите, чтобы исполнялся ваш код, скорее всего, DLL придётся внедрять. Описание внедрения DLL смотрите в дополнительных источниках в конце статьи. |
Получить адрес загрузки DLL.
Получить адрес функции.
Вызвать функцию при помощи CreateRemoteThread.
(опционально) Дождаться завершения потока и получить возвращаемое значение функции вызовом GetExitCodeThread.
А зачем нам DLL?
При желании можно напрямую записать весь исполняемый код в адресное пространство процесса-жертвы и запустить его тем же CreateRemoteThread. При большом желании можно добиться, чтобы это заработало... Основная проблема, подстерегающая вас на этом пути, заключается в том, что все функции, которые вызывает ваш код, должны находиться точно по тем адресам, куда передаётся управление. С учётом того, что:
код будет расположен в случайном месте адресного пространства, так как вам вряд ли удастся выделить память по тому же адресу;
DLL могут быть загружены по другим адресам,
«само собой» ничего не получится. Чтобы добиться работоспособности кода, нужно модифицировать используемые вашим кодом адреса, то есть, фактически, выполнить задачу загрузчика. А зачем выполнять её вручную, если можно положиться на загрузчик :) ?
Ограничения
Использование CreateRemoteThread связано с очевидными ограничениями:
Поддерживается только линейка Windows NT/2000/XP.
ПРИМЕЧАНИЕ Существует платная реализация CreateRemoteThread для Windows 9x, смотрите сайт http://www.apihooks.com раздел «PrcHelp». |
Прототип вызываемой функции должен соответствовать прототипу функции потока.
Кроме того, нужно иметь солидные права доступа к процессу-жертве:
PROCESS_CREATE_THREAD для запуска потока.
PROCESS_VM_READ для определения адреса.
--> ЧИТАТЬ ПОЛНОСТЬЮ <--