Интересная задача. Прибитие не завершение, втч добровольное. Один процесс прибивает другой TerminateProcess(). Потоки прибиваемого процесса должны успеть получить это событие, так как механизм завершения тяжелый во всех смыслах. Нужно для очистки ресурсов локально, тоесть нет второго серверного процесса, который может ожидать на портах(lpc etc). Если посмотреть базовые(те младших версий) реализации терминации, видны способы: - освобождение кс загрузчика, ntsetevent - освобождается юзер apc очередь, тоесть юзер потокам доставляется апк, но асинхронно. - спящие(suspend) треды выходят из сна ? Реально ли такое провернуть ?
Это кстати не так: она очищается, но без доставки - все оставшиеся APC в очереди отменяются, у них вызываются Rundown-рутины. Похоже, что после того, как потоку прилетит ядерная APC с PspExitSpecialApc+PspExitNormalApc, он больше в юзермод не выходит. Поймать NtSetEvent - наверно можно успеть в каком-то потоке, но вряд ли успеешь его обработать, прежде чем тебе тоже прилетит ядерная APC. В общем, "реально ли такое провернуть" - скорее нет, чем да.
GetExitCodeProcess --- Сообщение объединено, 16 янв 2025 --- или Мониторинг Handle Table и Rundown Protection --- Сообщение объединено, 16 янв 2025 --- второй получше код)
HoShiMin Часто приходилось видеть как ждущий поток выходит из ожидания при завершении процесса. А если ждать на обьектах, они просигналятся до завершения потоков ? Что будет если два потока будут ждать друг друга ? galenkane Спасибо, но в этих семплах просто циклы проверок, тоесть поток должен крутиться и проверять. Можно в несколько инструкций реализовать спин, ожидая изменение памяти.
Да, просигналят: там сначала отменяются все ожидания (IO, таймеры, етц), и только потом поток прибивается. Теоретически ты можешь успеть что-то поймать, но проблема в том, что никак не получится притормозить завершение, чтобы дать тебе его спокойно обработать. Это чистый рандом - или успеешь до APC, или нет. Если потоки будут ждать потоков - да, тоже просигналят, и кто-то может даже успеет ещё вернуться из ожидания в юзермод и выполнить пару сотен инструкций, но в любом случае, сложную логику так не выполнишь
А если приоритет накрутить ? Может способ есть как то время продлить потока, если ресурсы увеличить, память к примеру ? Помню была тема, да и факт - некоторые процессы не прибивались.
А вам надо именно что-то обработать? Может проще создать разделяемую память и запустить процесс, который будет её сохранять на диск или скидывать в сеть. Если получится за некоторое число инструкций записать в нее сообщение о завершении процесса, то и сохранить/передать информацию получится. Хотя тогда уже проще запустить исходный процесс под отладкой во втором и не придумывать велосипед. Вот, скорее всего, они как раз под отладкой в другом процессе и находились.
Приоритет - точно нет, поскольку поток завершает сам себя (а значит, просто будет делать это с бОльшим приоритетом). Единственное, что можно сделать - задержать потоки в ядре. Например, в тех APC’шках мы видим, что там есть цикл, атомарно декрементящий лоадерлок, если в момент завершения он был захвачен этим потоком. То есть, можно просто его захватить и пропатчить счётчик на максимальный (он же в юзермоде) - и всё, поток будет два часа его декрементить. Также потоки не смогут завершиться, пока у них есть незавершённые IRP - когда они запросили какую-нибудь файловую операцию, а ядро не сделало IoCompleteRequest. Но проблема в том, что всё это в ядре, а потоки завершаются независимо: они могут там повиснуть, процесс может стать неубиваемым, но делать полезную работу в юзермоде всё равно будет некому. Более того, ты не сможешь даже заспавнить новые потоки в таком процессе, т.к. он практически сразу запрещает ядру создавать их в себе. — Если говорить абстрактно, рассматривая вообще любые способы, включая ядерные хаки - то можно конечно: зайти в Guarded-регион или руками пропатчить в ETHREAD флажок, который говорит, что потоку запрещена доставка APC, и всё, потоку никто не сможет доставить APC - он станет неубиваемым. Но если делать без хаков - no way…
Интересно найти возможность в базовой сборке wrk/w2k, что опенсорс. HoShiMin полагает что задача не решаема, нельзя тормознуть прибитие, кроме как блокирнуть загрузчик. Не верю просто в это, надо подумать, может найдется способ. А новые фичи не интересны.
Ты писал, что это нужно для очистки ресурсов. Расскажи подробнее: почему нельзя заспавнить процесс-чистильщик, который будет ждать родителя? То, что ты пытаешься решить, явно должно решаться не так.
HoShiMin Это был вопрос на кл., в твоих работах/реализациях ведь довольно просто находили уязвимости, это обсуждали. Эта тема интересна, можно поискать синхронные ошибки, проблема в том, что я не могу никакую статистику снять пока, те нет пэвм. Есть лишь знание, опыт и сурки
avanguard если по памяти. Какая в принципе разница, я например помню что ты ошибся с диапазоном ресурсов, неверно собрал, ошибку найдут. Может можно классику посмотреть, рейсконд атаку к примеру на область памяти или обьект, как будет вести себя при этом ос не известно. Это можно лишь измерить. Напомню что статистика или иной вероятностный путь решения не признается за метод.
есть еще такая штука как объкты Job AssignProcessToJobObject при уничтожении процесса ты точно получишь евент и сможешь освободить то, что не успелось освободиться. как пример Код (C++): #include <windows.h> #include <iostream> int main() { // Create a job object HANDLE hJob = CreateJobObject(NULL, NULL); if (hJob == NULL) { std::cerr << "Error creating job object: " << GetLastError() << std::endl; return 1; } // Set up start info for the child process STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); // Create a child process (for example, notepad) if (!CreateProcess( "notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, // Start in a suspended state NULL, NULL, &si, &pi)) { std::cerr << "Error creating process: " << GetLastError() << std::endl; CloseHandle(hJob); return 1; } // Assign the child process to the job if (!AssignProcessToJobObject(hJob, pi.hProcess)) { std::cerr << "Error assigning process to job: " << GetLastError() << std::endl; TerminateProcess(pi.hProcess, 1); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hJob); return 1; } // Resume the child process to allow it to run ResumeThread(pi.hThread); // Wait for the child process to exit WaitForSingleObject(pi.hProcess, INFINITE); // There are no direct notifications here, but you know the process has ended std::cout << "Child process has exited!" << std::endl; // Clean up handles CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hJob); return 0; }
TermoSINteZ, Суть в том, что исполняющийся поток завершается извне, более того снимается задача(процесс), тоесть обьекты память и прочие ядерные штуки. При этом событии поток должен проснуться и успеть пайлод отработать, пока как выше описал HoShiMin ядро не завершит все.
Пока мысль вращается вокруг отладки и события DBG_TERMINATE_THREAD Значит наверняка можно, но с ходу не знаю, как застивить "отлаживать самого себя".