Является ли первый поток, который запускается внутри процесса Win32, «основным потоком»? Нужно понимать семантику - PullRequest
1 голос
/ 14 марта 2012

Я создаю процесс, используя 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 вернется.

Это происходит в Windows XP SP3, 32 бита.

Редактировать: Я только что попробовал Sysinternals Process Monitor, чтобы увидеть, что происходит, и я мог проверить свои наблюдения ранее. Внедренный код не аварийно завершает работу или что-то еще, вместо этого я вижу, что если я не жду потока, он не завершает работу, прежде чем закрыть программу, в которую был введен код. Я думаю, стоит ли отложить вызов CloseHandle(hRemoteThread) или что-то в этом роде ...

Редактировать + 1: это не CloseHandle(). Если я оставлю это только для теста, поведение не изменится при ожидании завершения потока.

Ответы [ 2 ]

0 голосов
/ 14 марта 2012

Первый поток, который нужно запустить, не особенный.

Например, создайте консольное приложение, которое создает приостановленный поток и завершает исходный поток (вызывая ExitThread). Этот процесс никогда не завершается (в любом случае, в Windows 7).

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

Я не знаю, что происходит с твоим примером. Самый простой способ избежать гонки - заставить новую нить возобновить исходную нить.

Размышляя сейчас, я удивляюсь, если то, что вы делаете, вряд ли вызовет проблемы. Например, что происходит со всеми вызовами DllMain для неявно загружаемых библиотек DLL? Они неожиданно происходят в неправильном потоке, пропускаются или откладываются до тех пор, пока ваш код не запустится и основной поток не запустится?

0 голосов
/ 14 марта 2012

Вероятно, что поток с функцией main (или эквивалентной) вызывает ExitProcess (либо явно, либо в своей библиотеке времени выполнения).ExitProcess, хорошо, завершает весь процесс, включая уничтожение всех потоков.Поскольку основной поток не знает о вашем введенном коде, он не ждет его завершения.

Я не знаю, есть ли хороший способ заставить основной поток ждать завершения вашего...

...