O primeiro thread executado dentro de um processo Win32 é o “thread principal”?Precisa entender a semântica
-
13-12-2019 - |
Pergunta
Eu crio um processo usando CreateProcess()
com o CREATE_SUSPENDED
e então crie um pequeno patch de código dentro do processo remoto para carregar uma DLL e chamar uma função (exportada por essa DLL), usando VirtualAllocEx()
(com ..., MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE
), WriteProcessMemory()
, Em seguida, ligue FlushInstructionCache()
naquele pedaço de memória com o código.
Depois disso eu ligo CreateRemoteThread()
invocar esse código, criando para mim um hRemoteThread
.Verifiquei que o código remoto funciona conforme planejado. Observação: esse código simplesmente retorna, ele não chama nenhuma API além de LoadLibrary()
e GetProcAddress()
, seguido pela chamada da função stub exportada que atualmente simplesmente retorna um valor que será passado como o status de saída do thread.
Agora vem a observação peculiar:lembre-se que o PROCESS_INFORMATION::hThread
ainda está suspenso.Quando eu simplesmente ignoro hRemoteThread
código de saída e também não espere que ele saia, tudo correrá "bem".A rotina que chama CreateRemoteThread()
retorna e PROCESS_INFORMATION::hThread
é retomado e o programa (remoto) realmente começa a ser executado.
No entanto, se eu ligar WaitForSingleObject(hRemoteThread, INFINITE)
ou faça o seguinte (que tem o mesmo efeito):
DWORD exitCode = STILL_ACTIVE;
while(STILL_ACTIVE == exitCode)
{
Sleep(500);
if(!GetExitCodeThread(hRemoteThread, &exitCode))
break;
}
seguido pela CloseHandle()
isto leva a hRemoteThread
terminando antes PROCESS_INFORMATION::hThread
é retomado e o processo simplesmente "desaparece".É o suficiente para permitir hRemoteThread
terminar de alguma forma sem PROCESS_INFORMATION::hThread
para fazer com que o processo morra.
Isto parece suspeitamente com uma condição de corrida, já que sob certas circunstâncias hRemoteThread
ainda pode ser mais rápido e o processo provavelmente ainda "desaparecerá", mesmo se eu deixar o código como está.
Isso implica que o primeiro thread executado em um processo se torna automaticamente o thread primário e que existem regras especiais para esse thread primário?
Sempre tive a impressão de que um processo termina quando seu último thread morre, não quando um especial o fio morre.
Observe também: não há chamada para ExitProcess()
envolvido aqui de alguma forma, porque hRemoteThread
simplesmente retorna e PROCESS_INFORMATION::hThread
ainda está suspenso enquanto espero hRemoteThread
para retornar.
Isso acontece no Windows XP SP3 de 32 bits.
Editar: Acabei de experimentar o Sysinternals Process Monitor para ver o que está acontecendo e pude verificar minhas observações anteriores.O código injetado não trava nem nada, em vez disso, vejo que, se não esperar pelo thread, ele não sai antes de fechar o programa onde o código foi injetado.Estou pensando se a chamada para CloseHandle(hRemoteThread)
deveria ser adiado ou algo assim ...
Editar +1: não é CloseHandle()
.Se eu deixar isso de fora apenas para um teste, o comportamento não muda ao aguardar o término do thread.
Solução
O primeiro thread a ser executado não é especial.
Por exemplo, crie um aplicativo de console que crie um thread suspenso e encerre o thread original (chamando ExitThread).Este processo nunca termina (pelo menos no Windows 7).
Ou faça o novo thread esperar cinco segundos e depois sair.Como esperado, o processo durará cinco segundos e será encerrado quando o thread secundário terminar.
Não sei o que está acontecendo com o seu exemplo.A maneira mais fácil de evitar a corrida é fazer com que o novo thread retome o thread original.
Especulando agora, me pergunto se o que você está fazendo provavelmente não causará problemas de qualquer maneira.Por exemplo, o que acontece com todos os DllMain
chama as DLLs carregadas implicitamente?Eles estão acontecendo inesperadamente no thread errado, estão sendo ignorados ou são adiados até depois que o código for executado e o thread principal for iniciado?
Outras dicas
As chances são boas de que o tópico com o main
(ou equivalente) chamadas de função ExitProcess
(explicitamente ou em sua biblioteca de tempo de execução). ExitProcess
, bem, sai de todo o processo, incluindo a eliminação de todos os threads.Como o thread principal não sabe sobre o seu código injetado, ele não espera que ele termine.
Não sei se existe uma boa maneira de fazer o thread principal aguardar a conclusão do seu ...