ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Service 프로세스에서 winlogon.exe
    Windows 프로그래밍 2019. 1. 3. 17:39

    Windows Service 프로그램을 개발하던 중 Winlogon.exe 프로세스의 세션 ID를 가져오는 것을 보았다.

    왜 하필이면 이 프로세스의 세션 ID를 가져오고, 이 프로세스가 무슨 일을 하는건지 궁금해서 찾아보게 되었다.


    일반적으로 Service 프로그램들은 GUI를 다룰 수 없다. Windows가 보안성을 위해 시스템 프로세스와 사용자 영역을 분리해 두었는데, Service는 시스템 프로세스에 속하기 때문이다.



    작업 관리자를 보면 사용자 이름이 컴퓨터 이름인 프로세스들의 세션 ID는 1로 표시되어 있고 SYSTEM이나 다른 몇몇 사용자 이름을 가진 프로세스들은 0번으로 표시되어 있다.

    즉, Service 프로세스라도 세션 번호가 1이라면 사용자 영역에 접근해 창을 띄울 수 있다는 뜻이다.


    이제 대충 감이 올 것이다. winlogon.exe 프로세스는 SYSTEM의 프로세스이지만 세션 ID가 1이다. 따라서 모든 프로세스를 검색해 그중winlogon.exe를 찾아내고 그 프로세스의 토큰 값을 가져오면 User의 토큰 값을 가져오는 것과 마찬가지인 것이다.


    요로케!!


    이해가 어려웠을 수도 있으니 소설을 써보겠다.


    어떤 학교가 있는데 그 학교가 워낙 엄격해 학생들이 학교 밖으로 나가지 못한다. 학교에 들어오려면 출입증이 필요한데 학교의 학생들과 학생들을 관리하는 수위아저씨만 출입증을 가지고 있다.

    수위아저씨는 시간이 되면 퇴근해야 하므로 출입증을 가지고 학교 밖으로 나갈 것이다. 그때 강도가 수위아저씨의 출입증을 빼앗는다면 강도는 학교에 출입할 수 있는 것이다.


    자 여기서 학교는 사용자 영역이다. 학생들은 User Process들이고 수위아저씨가 winlogon.exe 프로세스이다. 강도가 내가 만든 프로그램이 되는 것이다.


    이야기가 막 머리속에서 떠오르는걸 보니까 작가를 해야 했었나 보다...


    Service에서 winlogon.exe를 찾아내고 GUI를 가진 프로세스를 실행하는 과정까지의 코드는 아래와 같다.

    중요한 부분은 주석처리 되어있으니 개발 시에 참고하면 될 것 같다.
    	PROCESS_INFORMATION pi;
    	STARTUPINFO si;
    	BOOL bResult = FALSE;
    	DWORD dwSessionId, winlogonPid;
    	HANDLE hUserToken, hUserTokenDup, hPToken, hProcess;
    	DWORD dwCreationFlags;
    
    	// 현재 프로세스의 세션 아이디를 가져온다. (나중에 가져올 프로세스의 세션 아이디와 비교하기 위해)
    	// 굳이 필요는 없지만 혹시 몰라서 하는 짓이다
    	dwSessionId = WTSGetActiveConsoleSessionId();
    
    	PROCESSENTRY32 procEntry;
    
    	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    	if (hSnap == INVALID_HANDLE_VALUE){
    		return;
    	}
    
    	procEntry.dwSize = sizeof(PROCESSENTRY32);
    
    	if (!Process32First(hSnap, &procEntry)){
    		return;
    	}
    	
    	// 모든 프로세스의 정보를 가져와 winlogon.exe를 찾는다.
    	// winlogon.exe 프로세스를 찾는 이유는 system 계정 중 유일하게(?) user의 세션 아이디를 가지고 있기 때문이다.
    	do{
    		if (wcscmp(procEntry.szExeFile, L"winlogon.exe") == 0){
    			DWORD winlogonSessId = 0;
    			if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId){
    				winlogonPid = procEntry.th32ProcessID;
    				break;
    			}
    		}
    
    	} while (Process32Next(hSnap, &procEntry));
    
    	dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
    
    	ZeroMemory(&si, sizeof(STARTUPINFO));
    	si.cb = sizeof(STARTUPINFO);
    	ZeroMemory(&pi, sizeof(pi));
    	TOKEN_PRIVILEGES tp;
    	LUID luid;
    
    	// OpenProcess 함수를 이용해 winlogon.exe의 핸들값을 가져온다.
    	hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);
    
    	// 가져온 핸들값으로 해당 winlogon.exe 세션의 토큰 값 (User의 토큰 값)을 가져온다.
    	if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
    		| TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
    		| TOKEN_READ | TOKEN_WRITE, &hPToken))
    	{
    		int abcd = GetLastError();
    		return;
    	}
    
    	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
    	{
    		return;
    	}
    	tp.PrivilegeCount = 1;
    	tp.Privileges[0].Luid = luid;
    	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
    	// 가져온 토큰의 권한 등을 설정한다.
    	SetTokenInformation(hPToken, TokenSessionId, (void*)dwSessionId, sizeof(DWORD));
    
    	if (!AdjustTokenPrivileges(hPToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL))
    	{
    		int abc = GetLastError();
    		return;
    	}
    
    	LPVOID pEnv = NULL;
    
    	if (CreateEnvironmentBlock(&pEnv, hPToken, TRUE))
    	{
    		dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
    	}
    	else
    		pEnv = NULL;
    
    	// Launch the process in the client's logon session.  
    	bResult = CreateProcessAsUser(
    		hPToken,            // client's access token  
    		L"",              // file to execute  
    		NULL,     // command line  
    		NULL,              // pointer to process SECURITY_ATTRIBUTES  
    		NULL,              // pointer to thread SECURITY_ATTRIBUTES  
    		FALSE,             // handles are not inheritable  
    		dwCreationFlags,  // creation flags  
    		pEnv,              // pointer to new environment block   
    		NULL,              // name of current directory   
    		&si,               // pointer to STARTUPINFO structure  
    		&pi                // receives information about new process  
    	);
    


    'Windows 프로그래밍' 카테고리의 다른 글

    Windows 서비스 프로그램 개발 - 1  (0) 2019.01.08
    Volatile 변수란?  (2) 2019.01.07
    OnTimer 함수  (0) 2019.01.03
    Mutex와 Semaphore의 차이점과 사용방법  (0) 2019.01.03
    화면 캡쳐와 Printwindow 함수  (6) 2019.01.02

    댓글

Designed by Tistory.