Шаг ноль; внедренная DLL должна иметь точку входа, давайте назовем ее Init()
, которая принимает LPCWSTR
в качестве единственного параметра и возвращает int
; то есть такая же подпись, как у LoadLibrary()
и, следовательно, одинаково действительна как адрес функции запуска потока ...
Шаг первый; внедрить, используя библиотеку загрузки и удаленный поток. Не делайте ничего умного во введенных DLL DLLMain()
. Сохраните HMODULE
, который возвращается как код завершения потока ввода, это HMODULE
введенной DLL и возвращаемое значение LoadLibrary()
.
Обратите внимание , что это более не надежный подход на x64, если /DYNAMICBASE
и ASLR (рандомизация размещения адресного пространства) включены, так как HMODULE
на x64 больше, чем DWORD
значение, возвращаемое из GetThreadExitCode()
, и изменение адресного пространства означают, что более не вероятно, что значение HMODULE
будет достаточно маленьким, чтобы поместиться в DWORD
. См. Комментарии ниже и связанный вопрос (здесь), чтобы обойти использование общей памяти для передачи HMODULE
Шаг второй; загрузить внедренную DLL с помощью LoadLibrary в процесс, который выполняет внедрение. Затем найдите смещение вашей Init()
точки входа в вашем адресном пространстве и вычтите из нее HMODULE
вашей внедренной DLL в вашем адресном пространстве. Теперь у вас есть относительное смещение функции Init()
. Возьмите HMODULE
внедренной DLL в целевом процессе (то есть значение, которое вы сохранили на первом этапе) и добавьте к нему относительный адрес Init()
. Теперь у вас есть адрес Init()
в целевом процессе.
Шаг третий; вызовите Init()
в целевом процессе, используя тот же подход «удаленного потока», который вы использовали для вызова LoadLibrary()
. Вы можете передать строку в вызов Init (), это может быть что угодно.
Я обычно передаю уникальный строковый ключ, который я использую как часть имени именованного канала. Теперь Injected DLL и процесс внедрения знают имя именованного канала, и вы можете общаться между ними. Функция Init()
не является DLLMain()
и не страдает от ограничений, влияющих на DLLMain()
(так как она не вызывается из LoadLibrary
и т. Д.), И поэтому вы можете делать в ней обычные вещи. Как только внедренная DLL и процесс внедрения соединены через именованный канал, вы можете передавать команды и результаты данных туда и обратно, как вам нравится. Поскольку вы передаете функции Init()
строку, вы можете убедиться, что именованный канал уникален для данного конкретного экземпляра вашего процесса инъекции и этой конкретной внедренной DLL, что означает, что вы можете запускать несколько экземпляров процесса инъекции одновременно и каждый Процесс может внедряться в несколько целевых процессов, и все эти каналы связи являются уникальными и управляемыми.