DLL_PROCESS_DETACH의 비밀

@codemaru · July 31, 2011 · 4 min read

예전 글에서 DllMain이 얼마나 위험한 곳인지, 로더락이란 것이 얼마나 무서운 놈인지에 대해서 소개했었습니다. 그 때 주로 로더락 관점에서 DllMain에서 하지 말아야 할 것들을 소개 했는데요. 이 로더락을 배제하고도 DLL_PROCESS_DETACH가 호출되는 시점에는 주의해야 할 것이 있습니다. 바로 이 놈이 호출되는 시점입니다. 보통 여러분이 알고 있는 것처럼 DLL_PROCESS_DETACH로 DllMain이 호출되는 시점은 DLL이 프로세스로부터 떨어져 나가는 시점. 즉, FreeLibrary가 호출되서 해당 DLL의 레퍼런스 카운트가 0이 되는 시점에 호출됩니다. 하지만 이 경우 외에도 다른 한 가지 경우가 더 있습니다. 바로 프로세스가 종료되는 시점에도 호출되죠.

이런 특성 때문에 DLL_PROCESS_DETACH에 대해서 가지는 흔한 오해가 있는데요. 바로 TerminateProcess에 의해서 종료될 때에도 DLL_PROCESS_DETACH가 실행된다는 것과, DLL_PROCESS_DETACH에서 종료 작업을 하는 것이 좋다라는 생각입니다. 물론 이 두 가지 생각 모두 제대로 잘못된 생각들입니다.

우선 프로세스의 종료를 만들어내는 두 가지 API, ExitProcess와 TerminateProcess의 구현을 살펴보면 아래와 같이 되어 있습니다. 보이는 것처럼 둘 다 NtTerminateProcess를 사용합니다. 둘의 차이라면 ExitProcess는 이 NtTerminateProcess 이후에 shutdown process라는 작업을 진행하는 것이라고 보면 되겠습니다. 이 shutdown process의 진행 과정의 일환으로 호출되는 것이 DLL_PROCESS_DETACH입니다. 따라서 TerminateProcess로 프로세스가 종료될 때에는 이 shutdown process라는 것이 존재하지 않기 때문에 당연히 DLL_PROCESS_DETACH의 호출 또한 받을 수 없습니다.

ExitProcess(...)
{
    NtTerminateProcess(NULL, ExitCode);
    // shutdown process
    NtTerminateProcess(GetCurrentProcess(), ExitCode);
}

TerminateProcess(...)
{
    NtTerminateProcess(Process, ExitCode);
}

우리가 ExitProcess의 구현에서 눈치채야할 다른 중요한 시사점은 바로 NtTerminateProcess를 호출한 다음 shutdown process를 진행한다는 점입니다. 여기에는 굉장히 중요한 점이 내포되어 있는데요. 바로 여러분의 DLL_PROCESS_DETACH가 호출되는 시점에는 그 어떠한 스레드도 살아 있지 않다는 점입니다. NtTerminateProcess(NULL, ExitCode)를 거치는 순간 해당 프로세스의 모든 스레드는 강제로 터미네이트(Terminate)되기 때문입니다.

요약하면 이렇습니다. DLL_PROCESS_DETACH가 호출되는 시점에는 스레드가 다 죽고 없는 시점이다. 따라서 DLL_PROCESS_DETACH에서 복잡한 클린업 작업을 하는 것은 **짓이나 다름 없다. 여기서 복잡하다는 것은 적어도 스레드를 하나 이상 생성해서 종료 작업에 해당 스레드가 필요한 녀석으로 이해하면 되겠습니다. 물론 이런 작업 외에 간단한 작업도 하지 않는 것이 정신 건강에는 무척 유익합니다.

 0  0

 

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중