пятница, 11 февраля 2011 г.

в кулацком хозяйстве все пригодится

Давно вычитал о том что windows позволяет даже в пользовательском режиме запросто контролировать запуск процессов.
И чесались у меня руки эту штуку задействовать, но не к чему было ее приложить.
А тут понадобилось узнать параметры запускаемого процесса который стартует из-под другого процесса и быстро-быстро завершается.


Сначала коротенько о контроле запуска:

создать dll с экспортируемой функцией
NTSTATUS CreateProcessNotify( LPCWSTR lpApplicationName, ULONG uReason )

прописать dll в ветке реестра
HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls

и windows при создании процесса будет слать уведомления
#define APPCERT_IMAGE_OK_TO_RUN  1
#define APPCERT_CREATION_ALLOWED 2
#define APPCERT_CREATION_DENIED  3

сначала с кодом 1: хочу запустить lpApplicationName - возражания есть ? если нет возвращаем STATUS_SUCCESS
затем либо с кодом 2 если процесс будет запущен, либо с кодом 3 если кто-то был против.

И подумалось мне что в момент такого уведомления можно получить командную строку через банальный GetCommandLine()

Быстро сделана AppCertDll, прописана в реестре и... как говорится, две новости:
хорошая: AppCertDll отрабатывает, командная строка получается вполне корректной, хотя не совсем ожидаемой
плохая:  ничего больше не запускается, т.е. вообще ничего. The system cannot execute the specified program.

Наверное что-то с кодом возврата не того и надо возвращать для успеха не 0 - не помогло...

Добавил вывод параметра uReason - все как написано выше, возвращаешь 0 следующим приходит APPCERT_CREATION_ALLOWED, возвращаешь не 0 приходит APPCERT_CREATION_DENIED, но процессы больше не стартуют в любом случае...

Когда смотришь исходники и не понимаешь почему не работает, надо смотреть как это работает в коде.

Но как найти откуда система вызывает AppCertDll ? конечно можно добавить '_asm int 3' в CreateProcessNotify, но тема не про отладчики.

А ведь появился шикарный DSymLoad.hem, позволяющий грузить отладочные символы прямо в hiew, ща испытаем его в серьезном деле!

То что вызов AppCertDll искать надо в kernel32.dll почти очевидно, если нет то поискать строку CreateProcessNotify в системных dll.


Итак, hiew kernel32.dll и загрузка DSymLoad.hem через F11.

Сначала надо указать пути к dbghelp.dll и к серверу символов (подробнее см. документацию к DSymLoad)







F12 - грузим отладочные символы, если они уже на диске, это очень быстро, если будут загружены с интернета - зависит от скорости интернета. загрузились, F12 (посмотреть имена)



но что искать, какое имя ? для начала поищем имена содержащие appcertdll, это есть и в ветке реестра и константы для uReason тоже где-то рядом по написанию
F7-F7



вау! какой приятный сюрприз, есть имя BasepAppCertDllsList и оно наверняка указыает на список этих самых dll,
в первом случае мало интересного, но зато как расписаны имена!



дальше (Ctrl-Enter) тоже не возбуждает, но имена по-прежнему красиво смотрятся



а вот здесь уже любопытно



сразу бросаются в глаза push 2, push 1, push 3, - очень похожи на три константы define для uReason,

разбираем код:
в начале в ebx положили APPCERT_CREATION_ALLOWED - запуск процесса разрешен
затем цикл (4) по всему списку BasepAppCertDllsList - первый проход с uReason = APPCERT_IMAGE_OK_TO_RUN
если возврат не 0 (STATUS_SUCCESS) то ebx заменяется на APPCERT_CREATION_DENIED
и второй цикл (6) опять по всему списку с результатом 'голосования' в ebx



очевидно, это как раз код вызова AppCertDll, тем более что 'искать дальше' говорит о том что больше ничего нет.

Внимательно смотрим на call и видим что параметры из стека чистит вызываемая функция, а у нас в AppCertDll...
...открываем AppCertDll в hiew и enter-F8-F9 (просмотр экспорта)



Enter и мы в CreateProcessNotify,
... а в AppCertDll - простой ret, параметры не чищены, а при поломанном стеке может быть все что угодно.
'Шарик, Шарик, ты - балбес' (c) Матроскин, у CreateProcessNotify должен быть тип вызова WINAPI он же stdcall.
вот только почему-то в моих записях о контроле запуска про тип не было ничего указано, или в том месте где брал не было или потерял.

Причина глюка понятна, как починить - тоже, ради интереса можно еще в kernel32 побродить, например поискать из какого места вызывается 'голосование'.
Но опять проблема в том что это самое голосование не оформлена как явная функция с прологом типа push ebp, mov ebp,esp и т.д.
Хорошо бы видеть откуда есть ссылки на вышележащий (выше на экране,ниже по адресам) код, и поможет в этом еще один hem - WhereComesFrom, который составляет список куда-откуда были переходы они же перекрестные ссылки.
Вот такой список возле вызова AppCertDll, несколько близких переходов это рядом, плюс/минус 20 байтов, а вот был переход издалека с адреса .7C81958C



Enter на этой строке и, оба-на, опять сюрприз, мы в функции с говорящим названием BasepIsProcessAllowed,


 а она в свою очередь вызывается из... опять на помощь зовем WhereComesFrom.hem



... вот из этого места



дальше все скучно, stdcall для CreateProcessNotify, немного войнушки с декорированием функции (в случае stdcall в экспорт попадает не совсем то имя, а _CreateProcessNotify@8)
и все работает, а я опять наступил на тяпку calling conversion 

2 комментария: