Является ли первый поток, который запускается внутри процесса Win32, «основным потоком»?Нужно понять семантику
-
13-12-2019 - |
Вопрос
Я создаю процесс, используя CreateProcess()
с CREATE_SUSPENDED
а затем создайте небольшой патч кода внутри удаленного процесса для загрузки DLL и вызова функции (экспортируемой этой DLL), используя VirtualAllocEx()
(с ..., MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE
), WriteProcessMemory()
, тогда позвони FlushInstructionCache()
на том участке памяти с кодом.
После этого я звоню CreateRemoteThread()
чтобы вызвать этот код, создав мне hRemoteThread
.Я убедился, что удаленный код работает должным образом. Примечание: этот код просто возвращает, он не вызывает никаких API, кроме LoadLibrary()
и GetProcAddress()
, за которым следует вызов экспортированной функции-заглушки, которая в настоящее время просто возвращает значение, которое затем будет передано в качестве статуса завершения потока.
Теперь следует любопытное наблюдение:помните, что PROCESS_INFORMATION::hThread
все еще приостановлено.Когда я просто игнорирую hRemoteThread
код выхода, а также не ждите его выхода, все идет «отлично».Рутина, которая вызывает CreateRemoteThread()
возвращается и PROCESS_INFORMATION::hThread
возобновляется, и (удаленная) программа фактически запускается.
Однако, если я позвоню WaitForSingleObject(hRemoteThread, INFINITE)
или выполните следующее (что имеет тот же эффект):
DWORD exitCode = STILL_ACTIVE;
while(STILL_ACTIVE == exitCode)
{
Sleep(500);
if(!GetExitCodeThread(hRemoteThread, &exitCode))
break;
}
с последующим CloseHandle()
это ведет к hRemoteThread
заканчиваю раньше PROCESS_INFORMATION::hThread
возобновляется и процесс просто «исчезает».Этого достаточно, чтобы позволить hRemoteThread
закончить как-нибудь без PROCESS_INFORMATION::hThread
чтобы процесс умер.
Это подозрительно похоже на состояние гонки, поскольку при определенных обстоятельствах hRemoteThread
может быть быстрее, и процесс, скорее всего, все равно «исчезнет», даже если я оставлю код как есть.
Означает ли это, что первый поток, который запускается внутри процесса, автоматически становится основным потоком и что для этого основного потока существуют специальные правила?
У меня всегда сложилось впечатление, что процесс завершается, когда умирает его последний поток, а не когда особый нить умирает.
Также обратите внимание: нет звонка ExitProcess()
каким-либо образом здесь замешан, потому что hRemoteThread
просто возвращается и PROCESS_INFORMATION::hThread
все еще приостановлено, пока я жду hRemoteThread
вернуться.
Это происходит в 32-разрядной версии Windows XP SP3.
Редактировать: Я только что попробовал Sysinternals Process Monitor, чтобы увидеть, что происходит, и смог проверить свои предыдущие наблюдения.Внедренный код не дает сбоя или чего-то еще, вместо этого я вижу, что, если я не дождусь потока, он не закроется до того, как я закрою программу, в которую был внедрен код.Я думаю, стоит ли позвонить CloseHandle(hRemoteThread)
надо отложить что ли...
Редактировать+1: это не CloseHandle()
.Если я оставлю это просто для проверки, поведение не изменится при ожидании завершения потока.
Решение
Первый запущенный поток не является особенным.
Например, создайте консольное приложение, которое создает приостановленный поток и завершает исходный поток (путем вызова ExitThread).Этот процесс никогда не завершается (во всяком случае, в Windows 7).
Или заставьте новый поток подождать пять секунд, а затем выйти.Как и ожидалось, процесс будет жить пять секунд и завершится, когда вторичный поток завершится.
Я не знаю, что происходит с вашим примером.Самый простой способ избежать гонки — заставить новый поток возобновить исходный поток.
Размышляя сейчас, я задаюсь вопросом, не вызовет ли то, что вы делаете, проблемы в любом случае.Например, что происходит со всеми DllMain
вызывает неявно загруженные библиотеки DLL?Происходят ли они неожиданно не в том потоке, пропускаются или откладываются до тех пор, пока ваш код не запустится и не запустится основной поток?
Другие советы
Велика вероятность, что нить с main
(или эквивалентные) вызовы функций ExitProcess
(либо явно, либо в своей библиотеке времени выполнения). ExitProcess
, ну, завершает весь процесс, включая уничтожение всех потоков.Поскольку основной поток не знает о введенном вами коде, он не ждет его завершения.
Я не знаю, есть ли хороший способ заставить основной поток ждать завершения вашего...