Entry point로 진입하기 전의 프로세스 초기화 과정

다음은 윈도우 프로세스가 실행되었을 때, 과연 어떤 초기화 과정을 거치는지에 대한
일반적인 순서도를 글로 표현한 것이다.



1. process 가 Win32 API 인 CreateProcess를 호출하면, 이 API는
프로세스를 위한 "process object" 와 "new memory space" 을 생성한다.

2. CreateProcess함수는 이렇게 새롭게 생성된 메모리 공간으로 ntdll.dll 과 
프로그램 실행파일을 맵핑시키고 프로세스의 첫번째 쓰레드를 생성하여 그것을
위한 stack 공간을 할당한다.

3. 스택공간을 할당시키고 나면 프로세스의 첫번째 쓰레드는 재 시작하게 되는데,
그러기 위해선 ntdll.dll에 있는 LdrpInitialize 함수를 실행하여야한다.
다음은 LdrpInitialize 함수의 코드이다.

--------------------------------------------------
* NtContinue 함수의 원형(Undocumented) *

NtContinue( IN PCONTEXT            TheadContext

     IN BOOLEAN              RaiseAlert );
--------------------------------------------------

0:000> u
ntdll!KiUserApcDispatcher+0x7:
77f767ff 6a01             push    0x1  ( 셋팅되면, 현재의 쓰레드 오브젝트로부터 Alert state를 지운다.)
77f76801 57               push    edi
77f76802 e8950effff call    ntdll!ZwContinue (77f6769c)
77f76807 90               nop
-----------------------------------------------------------------------------------------


LdrpInitialize 함수에 포함되어 있는 ZwContinue는 세이브된 CONTEXT의 실행(쓰레드)을
재 시작할 때 사용한다.

위에서 보는거와 같이, RaiseAlert 변수명에 hex값 0x1를 스택에 삽입한후 현재 쓰레드의
CONTEXT structure 값의 포인터값인 edi를 삽입하고 있는걸 볼 수 있다.

그리고 이 매개변수 값을 토대로 ZwContinue 함수를 호출한다.
이것은 인텔의 환경에서 컴파일 되어진 거 같다. little endian의 방식으로 인수가 삽입된다.

4. LdrpInitialize 함수는 최초 실행의 import table를 반복적으로 면밀히 조사하고 
실행되기 위해 요구되어진 모든 실행을 메모리로 맵핑한다.

5. 이 시점에서 제어는 ntdll.dll 안에 존재하는 LdrpRunInitializeRoutines로 넘어간다.

LdrpRunInitializeRoutines 는 EXE 나 Dll 에서 명시된 엔트리 포인트로 호출되어지기 전에

실행되는 마지막 정류장이다. 그리고 이 함수는 Dll 코드가 실행되기 전에 실행되는 커널의

마지막 코드로써, LoadLibrary의 콜 때문에, 한번 또는 그 이상의 Dll 이 동적으로 로드되어질

때 마다 호출되는 특징이 있다.

이렇게  이 초기화 프로세스는 DLL_PROCESS_ATTACH 와 함께 각각의 Dll의 엔트리 포인트를

호출하는 것으로 이루어져 있다.

6. 일단 모든 Dll 초기화가 이루어지면, LdrpInitialize는 KERNEL32.DLL 에 있는

BaseProcessStart 즉, 쓰레드의 실제 초기화 루틴을 호출한다. 이 함수는  프로세스의

초기화 과정을 완료한 시점에서  실행파일의 WinMain 엔트리 포인트를  호출한다.

☆ 프로세스는 이와같은 단계로 프로그램을 실행하기전에 초기화 과정을 거치게 된다. ☆ 




--------------------------------------------------------------------------------
다음은 BaseProcessStart의 PseudoCode입니다.

BaseProcessStart( PVOID lpfnEntryPoint )
{
DWORD retValue
DWORD currentESP;
DWORD exceptionCode;

currentESP = ESP;

_try
{
NtSetInformationThread( GetCurrentThread(),
                  ThreadQuerySetWin32StartAddress,
                  &lpfnEntryPoint, sizeof(lpfnEntryPoint) );

retValue = lpfnEntryPoint();

ExitThread( retValue );
}
_except(
exceptionCode = GetExceptionInformation(),
UnhandledExceptionFilter( GetExceptionInformation() ) )
{
ESP = currentESP;

if ( !_BaseRunningInServerProcess )        
ExitProcess( exceptionCode );
else                                        
ExitThread( exceptionCode );
}
}

위와 같이, BaseProcessStart는 쓰레드의 엔트리 포인틀 값을 매개변수로 받아와서 쓰레드를
시작하기 위해 엔트리 포인트를 호출하고 난 후 제어가 ExitThread로 넘어가게 된다.

그러나 만약 쓰레드가 faults 나고 그에 대한 exception을 어떤  다른 handler에서 처리하지
않는다면  다음의 실행은 _except 인사이드 코드에 존재하는 UnhandledExceptionFilter가
실행되고 만약 이 함수가 EXCEPTION_EXECUTE_HANDLER 를 리턴하면 제어가 다시
_except 안의 다음라인으로 넘어가고 그리고 나서 _except하는것은 ExitProcess 함수를
호출함으로써 프로세스를 종료할 것이다.

그러나 fault 난 쓰레드가 thread-base-service로 실행되고 있는 중이였다면, 
_except code는 ExitProcess를 호출하지 않는다.  그것은 대신에 ExitThread를 호출할 것이다.
그 이유는 한개의 서비스가 잘못된 것 때문에 단지 전체의 서비스 프로세스가 종료시키는 걸
원하진 않기 때문이다.

--------------------------------------------------------------------------------------

by codexb | 2006/04/08 14:41 | Ring0 | 트랙백(1)

트랙백 주소 : http://leony.egloos.com/tb/1770308
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from 휘바골드's Blog at 2008/09/10 14:36

제목 : Entry point로 진입하기 전의 프로세스 초기..
다음은 윈도우 프로세스가 실행되었을 때, 과연 어떤 초기화 과정을 거치는지에 대한 일반적인 순서도를 글로 표현한 것이다. 1. process 가 Win32 API 인 CreateProcess를 호출하면, 이 API는 프로세스를 위한 "process object" 와 "new memory space" 을 생성한다. 2. CreateProcess함수는 이렇게 새롭게 생성된 메모리 공간으로 ntdll.dll 과 프로그램 실행파일을 맵핑시키고 프로세스......more

※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.

◀ 이전 페이지          다음 페이지 ▶