Есть процесс с засаспенженными тредами. Туда инжектится dll через CreateRemoteThread -> LoadLibraryW, через раз инжект зависает с таким вот стеком Код (Text): ntkrnlpa.exe!KiSwapContext+0x2f ntkrnlpa.exe!KiSwapThread+0x8a ntkrnlpa.exe!KeWaitForSingleObject+0x1c2 ntkrnlpa.exe!KiSuspendThread+0x18 ntkrnlpa.exe!KiDeliverApc+0x124 ntkrnlpa.exe!KiSwapThread+0xa8 ntkrnlpa.exe!KeWaitForSingleObject+0x1c2 ntkrnlpa.exe!NtWaitForSingleObject+0x9a ntkrnlpa.exe!KiFastCallEntry+0xfc ntdll.dll!KiFastSystemCallRet ntdll.dll!NtWaitForSingleObject+0xc ntdll.dll!RtlpWaitForCriticalSection+0x132 ntdll.dll!RtlEnterCriticalSection+0x46 ntdll.dll!_LdrpInitialize+0xf0 ntdll.dll!KiUserApcDispatcher+0x7 Судя по всему ожидание происходит на критической секции, кторорая в XP SP3 находится в ntdll по адресу 7C97E174. Я полагаю, что саспенд застал процесс в процессе загрузки или выгрузки модуля. У меня вопрос: возможно ли, скажем, сделать эту секцию неактивной, очистить списки модулей процесса, затем загрузить свою dll, выгрузить и всё восстановить? Или, может, есть какой-нибудь другой способ загрузки? Есть ли описание ntdll лоадера, где затрагивается этот вопрос? Спасибо
sergegers Дело в том что Ldr содержит критическую секцию, которая распространяется на все Апи связанное с модулями как минимум. ваш проблема в том что какой то поток обратился к этому апи, и захватил объект синхронизации. После чего вы этот тред остановили, и попытались захватить захваченный объект синхронизации ... так что файлед. Либо Не стопайте потоки. Либо убедитесь что потоки не используют это апи. ( Проверка контекста потока, либо ход тепм патч, на это апи. ) Как вариант можно проверить эту критическую секцию, а потом наложить патч с отложенным вызовом реальных Ldr* функций.
shchetinin то что вы написали, мне в целом понятно. я на всякий случай процитирую свой план мне бы хотелось получить соображения типа, это не сработает потому-то и потому-то. или - есть лучшее решение. это расширение windbg, так что невозможно не останавливать потоки, невозможно подождать пока секция освободится, а надо суметь воспользоваться лоадером в ситуации, когда он в середине транзакции. я понимаю, что это грязный хак
sergegers Можно сделать пачт, после которого, не будет происходить захват секции, но не понятно в каком состоянии будут находится переменные ldr. Делайте загрузку после того как windbg отпустит дебаг апи.
shchetinin Критическую секцию захватывает исследуемый процесс, а не дебаггер. моя длл загружается в тот момент (вручную), когда все потоки отлаживаемого процесса засаспенжены. Вы сами ответили на свою идею Теперь я попробую подробнее объяснить свою идею. Побитово скопировать в надёжное место LdrpLoaderLock, а на её месте создать свободную критическую секцию. в _PEB_LDR_DATA сохранить и обнулить списки InLoadOrderModuleList, InMemoryOrderModuleList, InInitializationOrderModuleList. Загрузить мою dll, выгрузить мою dll, восстановить всё, как было
sergegers Там сотни/тысячи ресурсов, помимо этих списков. Все они требуют синхронизации. В вашем случае единственный выход - не использовать загрузчик. Не существует задач, требующих останов всех потоков для инжекта. То, что вы придумали по своей сути инвалидно и не может использоваться. Если задачу рассматривать чисто ради интереса(она практически бесполезна), то следует рассмотреть свойства критических секций. Например ядро освобождает кс LdrpLoaderLock при завершении тредов. Иначе был бы деадлок(другие треды ждут освобождение кс, которое никогда не произойдёт, так как тред завершается ядром, для исключения этой ситуации ядро определяет ссылку на кс из PEB(для этого там и ссылка), сигнализирует эвент и корректирует кс, по сути эмулируя освобождение кс захватившим её тредом). Необходимо ввести захватывающие ресурс потоки в ожидание на этом ресурсе. Тоесть ресурс должен быть захвачем рабочим потоком. При этом необходимо либо обождать освобождение ресурса другим тредом, либо освободить его вручную из рабочего треда. В частности для кс необходимо скорректировать поля обьекта, такие как TID, после чего вызвать стандартную функцию освобождения ресурса, либо дождаться освобождения ресурса другим тредом, выполнив ресум. Последнее единственно верное решение - асинхронный доступ к ресурсу приведёт к не определённым последствиям.
gaeprust "Не существует задач, требующих останов всех потоков для инжекта" Он же сказал это дебаггер. sergegers Короче, выполняете раскрутку стека, и трэды которые работают в Ldrp, подмять адрессы возврата(думаю понятно куды). Все остальные треды в суспенд. После того как все (адрессы возврата) отработают, можно будет вгрузить библиотеку. и вернуть на круги свое. Либо зарание делать патчи для Ldrp АПИ, и в нужных ситуация, выполнив функцию, остановить управления, после чего отдать управления винньдебегу, ну и вгружать либу. Вообще думаю идея понятна.
shchetinin Все треды остановлены, создаётся левый(удалённый). Нормальная его работа невозможна. Подменяйте вы что хотите, если не будет дедлока, то будут крэши. Один тред сформировал ссылку, не заполнив поля, другой и читает - очевидно ничего хорошего из этого не получится и стековая маршрутизация тут ничем не поможет.
gaeprust Приведите пример хоть одного, который требовал бы синхронизации при остановленных тредах. Вы вообще читаете, то что я пишу? то, что написано вами дальше, я понять не могу. Как это всё можно проделать, если все потоки, кроме загружающих модуль, остановлены? Прежде чем писать, прочитайте предыдущие посты, пожалуста!
sergegers LDR_DATA_TABLE_ENTRY - годный пример. Читаете мсдн. Читаете сурс. Читаете дизасм. Думаете. Когда придёт понимание работы критических секций пишите код. Изменяете кс, синхронно ресумите треды. Освобождается кс, другими тредами, ранее захваченная вашим. Суспендите есчо раз. Подскажу даже вам сервис - NtSuspendProcess(если удалённо, тоесть его дёргаете и ресумите ваш тред). Если чтото не понятно, то учите матчасть, её полно - маны, рихтеры, сурсы etc. И кстате учитесь нормально задавать вопросы, вы слишком не благодарны.)
shchetinin gaeprust Я так до конца и не понял. Вот как я себе представляю работу лоадера. В процессе загрузки модуля захватывается LdrpLoaderLock, загружается модуль и изменяется поля структуры _PEB_LDR_DATA. Если я сохраню оригинальные секцию и структуру, подсуну фейковые, загружу свой модуль, выгружу его и восстановлю исходные секцию и структуру, то где я могу напортить?
sergegers Вы нарочно спрашиваете и делаете вид что не понимаете ? вам то и надо накатить патчи до этого события, либо во время. Разумеется не в windbg, а этот самый процесс. А вот когда он отработает(это события, бряк или что у вас там), то потребуется сделать останвку всем потоком, крому тех которые использую ldrp, после чего windbg отпускает ДЕБАГ апи(с вытекающим потоки те которые вы не остановили). После чего отрабатывают Ldrp, и начинают работать ваши патчи, ваши патчи должны сигнализировать windbg, потом может отработать ваш плагин с возможностью загрузки либы. Делать загрузку и можете спокойно рассаспендить потоки ... Не могу понять что здесь сложного?
sergegers GetModulehandle, EnumProcessModule и бла бла бла .... Перестаньте делать через одно место ...
gaeprust И кто к нему будет обращаться? Если честно, то вы не выглядите человеком, который имеет право давать такие советы
shchetinin здесь вроде уже всё остановлено, и потоки, которые используют ldrp и которые нет что такое windbg отпускают debug api? Я не прикалываюсь, мне действительно не совсем понятна ваша идея. И дальше мне совсем ничего не понятно. Объясните пожалуйста, поподробнее идею.
process { thread1 somefunction->Ldrp**** thread2 exec some loop' thread3 exec some loop' thread4 somefunc->somefunc2->Ldrp**** ->down ldrp api./ } windbg получает DEBUG_EVENT. (Если это конечно на локальной машине, но в целом это все равно будет DEBUG_EVENT). При этом windbg не чего не останавливает. Потоки останавливает ядро(дебаг подсистема). После чего происходит вызов вашего плагина. ваш плагин должен остановить все треды кроме тех которые юзают (Ldrpthread2. thread3 - остановить другие нет). После чего вам надо подменить адресса возвратов для (thread1,thread4) из Ldrp(Это должна быть функция с ожиданием и сигналом дебаггеру, в данном случае это может 0xСС). после чего, сказать windbg что сепшен обработан. После того как потоки отработают(которые юзали ldrp), то они сигнализурует дебагеру сепшен(с breakpoint нотификациеё), в этом момент вы делаете загрузку либы. после чего вам надо будет восстановить контексты потоков которые использовали ldrp(то и есть которые вам отреспонсились). И разумеет сделать ресюм, тем которым делали саспенд.
shchetinin ну это по фигу, это будут запросы из других процессов Я пока не делаю, а обсуждаю возможность это сделать