Хочу исполнить некоторый код при старте процесса.
Код имеет зависимости от ntdll, kernel32, и еще от пары системных либ, которые он загрузит самостоятельно с помощью LoadLibrary (не принципиально, но очень желательно).
Хочу его исполнить сразу после загрузки kernel32.
Пробовал вот что:
1) ProcessCreateNotifyRoutine: создать системный тред, в котором ждать инициализации лоадера, создать тред в процессе и сделать дела. Не устроил, ибо через раз процесс успевает завершиться раньше чем шелл закончит исполнение
2) LoadImageNotifyRoutine на загрузку длл — хукнуть точку входа, когда она получит управление сделать дела и восстановить её. При выделении памяти при загрузке длл получаем deadlock (как и сказано в документации). И даже если извратится и выделить память раньше, например в ProcessCreateNotifyRoutine — в перехваченной dllmain всё не получится загрузить другую длл.
3) LoadImageNotifyRoutine на загрузку exe — как ни странно работает без deadlock'а, но пока точка входа получит управление может быть поздно.
Задача довольно специфичная, но может быть есть возможность её решить ?
Здравствуйте, smbdnew, Вы писали:
S>Хочу исполнить некоторый код при старте процесса. S>Код имеет зависимости от ntdll, kernel32, и еще от пары системных либ, которые он загрузит самостоятельно с помощью LoadLibrary (не принципиально, но очень желательно).
Посмотрите TLS callback, свой код можно запустить из него.
Вполне хороший документированный метод и достаточно прост в реализации.
Здравствуйте, enigmas, Вы писали:
E>Посмотрите TLS callback, свой код можно запустить из него. E>Вполне хороший документированный метод и достаточно прост в реализации.
Не совсем понял что вы имели ввиду. Если что, процесс не мой, а вообще любой произвольный.
Здравствуйте, smbdnew, Вы писали:
S>Пробовал вот что: S>... S>2) LoadImageNotifyRoutine на загрузку длл — хукнуть точку входа, когда она получит управление сделать дела и восстановить её. При выделении памяти при загрузке длл получаем deadlock (как и сказано в документации). И даже если извратится и выделить память раньше, например в ProcessCreateNotifyRoutine — в перехваченной dllmain всё не получится загрузить другую длл.
Патчить точку входа на последних версиях Windows так-то просто не выйдет.
ZwProtectVirtualMemory нельзя из-за DisableDynamicCode policy (она же Arbitrary Code Guard на последних версиях Win10),
будет 0xC0000604 (STATUS_DYNAMIC_CODE_BLOCKED). На более ранних версиях функция, кстати, вообще не экспортируется ядром.
Способы с MmProtectXxx или сброс WP-бита в CR0 вспоминать не будем, так как это явно некорректно.
Можно попробовать написать минифильтр FS, подменяющий exe при его отображении в память, но это дико
сложно (и к тому же будут слетать цифровые подписи исполняемых файлов).
S>3) LoadImageNotifyRoutine на загрузку exe — как ни странно работает без deadlock'а,...
Это только до поры.
Нельзя там ни ZwAllocateVirtualMemory, ни ZwProtectVirtualMemory (я ловил дедлоки и в этой ситуации).
S>...но пока точка входа получит управление может быть поздно.
Ага. А еще есть .NET-приложения, у которых точка входа не в exe, а в mscoree.dll...
S>Задача довольно специфичная, но может быть есть возможность её решить ?
Решить-то можно, вот только стоит оно того?
Затраты, ИМХО, будут несоизмеримо большими, чем профит...
Re[2]: Исполнение кода на ранней стадии инициализации процесса
Здравствуйте, smbdnew, Вы писали:
S>Хорошо, допустим я выделю код в ProcessCreateNotyRoutine. S>А как бы на него передать управление как можно раньше ?
Если надо совсем-совсем раньше, прямо сразу после загрузки ntdll/kernel32, то вариантов немного.
Можно попробовать вариант с APC (KeInitializeApc/KeInsertQueueApc и далее alertable-ожидание,
чтобы APC сразу прилетела в поток), хотя тут 100% будут конфликты с другим софтом (антивирусы) и
есть масса других проблем. Описан в статье "способ принудительной загрузки dll..." (и сразу не
советую слепо доверять тому, что там написано и приведенному коду — его еще фиксить и фиксить).
Еще можно на этапе загрузки exe пропатчить таблицу импорта и вставить туда свою dll.
Так делает McAfee, если память не изменяет.
IMHO, относительно надежное — это патчить на этапе запуска exe какую-нибудь структуру: TLS-калбэки,
CONTEXT, таблица импорта и т.п. Я патчил CONTEXT (лежит в стеке стартового потока), но у меня
не было цели запускаться так рано.
Еще, говорят, можно написать фильтрующий драйвер файловой системы (минифильтр) и в нем изменять
исполняемые файлы прямо во время их отображения в память (например, добавлять доп. секцию в
загруженный exe и там размещать свой код), при этом будут проблемы с проверкой целостности
файлов (цифровые подписи) и другие...
Re: Исполнение кода на ранней стадии инициализации процесса
4) QueueUserApc, с учетом битности процесса. LdrpInitilizeProcess вызывает NtTestAlert перед переходом на точку входа, после исполнения DllMain прилинкованных длл-ке, потому APC исполнится. Но есть тонкость — NtTestAlert может вызвать DllMain одна из длл-ек и могут быть сюрпризы с порядком инициализации, так что ничего кроме ntdll/kernel32 лучше не юзать.
Как много веселых ребят, и все делают велосипед...
Re[4]: Исполнение кода на ранней стадии инициализации процесса
Здравствуйте, okman, Вы писали:
O>Еще можно на этапе загрузки exe пропатчить таблицу импорта и вставить туда свою dll. O>Так делает McAfee, если память не изменяет.
Это как раз самый надёжный вариант.
Только нужно учитывать параллельную загрузку dll-ек в Windows 10.
Здравствуйте, okman, Вы писали:
O>Если надо совсем-совсем раньше, прямо сразу после загрузки ntdll/kernel32, то вариантов немного.
O>Можно попробовать вариант с APC (KeInitializeApc/KeInsertQueueApc и далее alertable-ожидание, O>чтобы APC сразу прилетела в поток), хотя тут 100% будут конфликты с другим софтом (антивирусы) и O>есть масса других проблем. Описан в статье "способ принудительной загрузки dll..." (и сразу не O>советую слепо доверять тому, что там написано и приведенному коду — его еще фиксить и фиксить).
Забыл отписать, на нём в итоге и остановился.
Вариант с форсированной доставкой APC в итоге не пригодился, меня устроил вызов перед точкой входа.
Просматривая чьи то исходники видел форсированную доставку не через alertable ожидание, а через доставку пустой kernel APC сразу после моей.
По причине ненадобности проверять не стал, но интересно — такой вариант жизнеспособен в теории ?
Re[5]: Исполнение кода на ранней стадии инициализации процесса
Здравствуйте, smbdnew, Вы писали:
S>Просматривая чьи то исходники видел форсированную доставку не через alertable ожидание, а через доставку пустой kernel APC сразу после моей. S>По причине ненадобности проверять не стал, но интересно — такой вариант жизнеспособен в теории ?
Насколько я знаю, доставка юзерских APC выполняется или при alertable-ожидании, или посредством вызова
специальных сервисов типа NtTestAlert/KeTestAlertThread. В итоге для потока выставляется флаг
"User APC Pending = TRUE" и при выходе из системного вызова как раз и происходит доставка.
Например, загрузчик в ntdll как раз и вызывает NtTestAlert ровно перед тем, как прыгнуть на точку входа;
на этом факте, как правило, и построены методы с внедрением dll через APC.
Как доставка kernel APC может "сама по себе" доставить пользовательскую APC — мне неизвестно.
Скорее всего, там какие-то детали остались за кадром...