훈, IT 공부

C++ 프로세스의 권한을 관리자 혹은 일반 사용자 권한으로 실행하기

IT훈이 2021. 1. 26.
반응형

 일하다보면 권한에서 벗어날 수가 없는 상황히 종종 일어난다. 시간이 지나서 잊을때가 되면 다시금 나타나는 권한문제 때문에 정리를 해두어야 미래의 내가 편하겠다는 생각에 정리를 해보려한다.

 

상황

 현재 동작중인 프로세스가 시스템 권한으로 동작하고 있고, 현재 동작중인 프로세스를 A.exe 라고 한다면 

A.exe가 다른 B.exe를 실행해줘야하는 상황이다.

기본적으로는 A.exe -> B.exe를 실행해줄때 ShellExecute, CreateProcess 등의 함수로 실행을 해준다. 여기서 중요한 것은 권한인데, 기본적으로 부모 프로세스 A.exe가 B.exe를 실행시켜줄때 자식프로세스인 B.exe 부모프로세스의 권한을 얻어오게된다.

 

A.exe ( System ) ---> B.exe ( System )     기본적인 방식

A.exe ( System ) ---> B.exe ( User )        지금 필요한 방식

 

 현재 부모 프로세스가 사용자 권한일때 자식 프로세스를 관리자권한으로 실행시키는 방법은 어렵지 않다. ShellExcute 함수를 호출할때 두번째 인자에 "runas"로 넣어주면 간단하게 해결이 되기 때문이다.

 시스템권한에서 유저 권한으로 실행시켜주기 위해서는 일반적인 방식으로는 어렵다. MS에서 제공하는 부분이 없기에 어떻게 보면 꼼수를 써서 권한을 얻어와야 하는 것이다. 방법은 윈도우즈의 작업 스케줄러를 이용하는 것이다. 작업 스케줄러에 새 작업을 만들어 즉시 시작하도록 트리거 조건을 주고 프로세스를 실행하도록 동작을 추가하여 작업 스케줄러에 의해 자식 프로세스가 일반 사용자 권한으로 실행되도록 하는 것이다.

 

#include <comdef.h>
#include <taskschd.h>

#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "credui.lib")

BOOL ShellExecuteAs(BOOL bAdmin,
					LPCTSTR lpFile,
					LPCTSTR lpParameters = NULL,
					LPCTSTR lpDirectory = NULL)
{
	OSVERSIONINFO osvi;
	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osvi);

	// Under Windows Vista
	if (osvi.dwMajorVersion < 6)
		return ShellExecute(NULL, _T("open"), lpFile, lpParameters, lpDirectory, SW_SHOW) > (HINSTANCE)32;

	BOOL bUserAnAdmin = IsUserAnAdmin();

	if (bAdmin)
		return ShellExecute(NULL, bUserAnAdmin ? _T("open") : _T("runas"),
		lpFile, lpParameters, lpDirectory, SW_SHOW) > (HINSTANCE)32;

	if (!bUserAnAdmin)
		return ShellExecute(NULL, _T("open"), lpFile, lpParameters, lpDirectory, SW_SHOW) > (HINSTANCE)32;

	// Initialize COM.
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
	if (FAILED(hr))
	{
		TRACE(_T("CoInitializeEx failed: %x\n"), hr);
		return FALSE;
	}

	// Set general COM security levels.
	hr = CoInitializeSecurity(
		NULL,
		-1,
		NULL,
		NULL,
		RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
		RPC_C_IMP_LEVEL_IMPERSONATE,
		NULL,
		0,
		NULL);

	if (FAILED(hr))
	{
		TRACE(_T("CoInitializeSecurity failed: %x\n"), hr);
		CoUninitialize();
		return FALSE;
	}

	// Create a name for the task.
	LPCWSTR wszTaskName = L"Task_ShellExecuteAs";

	// Create an instance of the Task Service.
	ITaskService *pService = NULL;
	hr = CoCreateInstance(CLSID_TaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskService,
		(void**)&pService);

	if (FAILED(hr))
	{
		TRACE(_T("Failed to create an instance of ITaskService: %x\n"), hr);
		CoUninitialize();
		return FALSE;
	}

	// Connect to the task service.
	hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
	if (FAILED(hr))
	{
		TRACE(_T("ITaskService::Connect failed: %x\n"), hr);
		pService->Release();
		CoUninitialize();
		return FALSE;
	}

	// Get the pointer to the root task folder.  This folder will hold the
	// new task that is registered.
	ITaskFolder *pRootFolder = NULL;
	hr = pService->GetFolder(_bstr_t( L"\\") , &pRootFolder);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get Root Folder pointer: %x\n"), hr);
		pService->Release();
		CoUninitialize();
		return FALSE;
	}

	// If the same task exists, remove it.
	hr = pRootFolder->DeleteTask(_bstr_t(wszTaskName), 0);

	// Create the task builder object to create the task.
	ITaskDefinition *pTask = NULL;
	hr = pService->NewTask(0, &pTask);

	pService->Release();  // COM clean up.  Pointer is no longer used.
	if (FAILED(hr))
	{
		TRACE(_T("Failed to create a task definition: %x\n"), hr);
		pRootFolder->Release();
		CoUninitialize();
		return FALSE;
	}

	// Get the registration info for setting the identification.
	IRegistrationInfo *pRegInfo= NULL;
	hr = pTask->get_RegistrationInfo(&pRegInfo);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get identification pointer: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	hr = pRegInfo->put_Author(L"Author_ShellExecuteAs");
	pRegInfo->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot put identification info: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Create the principal for the task
	IPrincipal *pPrincipal = NULL;
	hr = pTask->get_Principal(&pPrincipal);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get principal pointer: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Set up principal information:
	hr = pPrincipal->put_Id( _bstr_t(L"Principal_ShellExecuteAs"));
	if (FAILED(hr))
		TRACE(_T("Cannot put the principal ID: %x\n"), hr);

	hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
	if (FAILED(hr))
		TRACE(_T("Cannot put principal logon type: %x\n"), hr);

	// Run the task with the least privileges (LUA)
	hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
	pPrincipal->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot put principal run level: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Create the settings for the task
	ITaskSettings *pSettings = NULL;
	hr = pTask->get_Settings(&pSettings);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get settings pointer: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Set setting values for the task.
	hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
	pSettings->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot put setting info: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Get the trigger collection to insert the registration trigger.
	ITriggerCollection *pTriggerCollection = NULL;
	hr = pTask->get_Triggers(&pTriggerCollection);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get trigger collection: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Add the registration trigger to the task.
	ITrigger *pTrigger = NULL;
	hr = pTriggerCollection->Create(TASK_TRIGGER_REGISTRATION, &pTrigger);
	pTriggerCollection->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot create a registration trigger: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	IRegistrationTrigger *pRegistrationTrigger = NULL;
	hr = pTrigger->QueryInterface(
		IID_IRegistrationTrigger, (void**) &pRegistrationTrigger);
	pTrigger->Release();
	if (FAILED(hr))
	{
		TRACE(_T("QueryInterface call failed on IRegistrationTrigger: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	hr = pRegistrationTrigger->put_Id(_bstr_t( L"Trigger_ShellExecuteAs"));
	if (FAILED(hr))
		TRACE(_T("Cannot put trigger ID: %x\n"), hr);

	// Define the delay for the registration trigger.
	hr = pRegistrationTrigger->put_Delay(L"PT0S");
	pRegistrationTrigger->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot put registration trigger delay: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Add an Action to the task.
	IActionCollection *pActionCollection = NULL;

	// Get the task action collection pointer.
	hr = pTask->get_Actions(&pActionCollection);
	if (FAILED(hr))
	{
		TRACE(_T("Cannot get Task collection pointer: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Create the action, specifying that it is an executable action.
	IAction *pAction = NULL;
	hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
	pActionCollection->Release();
	if (FAILED(hr))
	{
		TRACE(_T("Cannot create action: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	IExecAction *pExecAction = NULL;

	// QI for the executable task pointer.
	hr = pAction->QueryInterface(
		IID_IExecAction, (void**)&pExecAction);
	pAction->Release();
	if (FAILED(hr))
	{
		TRACE(_T("QueryInterface call failed for IExecAction: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Set the path of the executable
	hr = pExecAction->put_Path(_bstr_t(lpFile));
	if (FAILED(hr))
	{
		TRACE(_T("Cannot put the action executable path: %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	if (lpParameters)
	{
		pExecAction->put_Arguments(_bstr_t(lpParameters));
		if (FAILED(hr))
		{
			TRACE(_T("Cannot put the action executable arguments: %x\n"), hr);
			pRootFolder->Release();
			pTask->Release();
			CoUninitialize();
			return FALSE;
		}
	}

	if (lpDirectory)
	{
		pExecAction->put_WorkingDirectory(_bstr_t( lpDirectory));
		if (FAILED(hr))
		{
			TRACE(_T("Cannot put the action executable working directory: %x\n"), hr);
			pRootFolder->Release();
			pTask->Release();
			CoUninitialize();
			return FALSE;
		}
	}
	pExecAction->Release();

	// Save the task in the root folder.
	IRegisteredTask *pRegisteredTask = NULL;
	hr = pRootFolder->RegisterTaskDefinition(
		_bstr_t(wszTaskName),
		pTask,
		TASK_CREATE_OR_UPDATE,
		_variant_t(),
		_variant_t(),
		TASK_LOGON_INTERACTIVE_TOKEN,
		_variant_t(L""),
		&pRegisteredTask);

	if (FAILED(hr))
	{
		TRACE(_T("Error saving the Task : %x\n"), hr);
		pRootFolder->Release();
		pTask->Release();
		CoUninitialize();
		return FALSE;
	}

	// Clean up.
	pRootFolder->Release();
	pTask->Release();
	pRegisteredTask->Release();
	CoUninitialize();
	return TRUE;
}

 

호출은 아래와 같이 해주면된다.

// 일반 사용자 권한으로 계산기 실행
ShellExecuteAs(FALSE, _T("calc.exe"));
// 관리자 권한으로 메모장 실행
ShellExecuteAs(TRUE, _T("notepad.exe"));

 

 

원문은 아래 링크 참조

가시고기의 바깥 세상 둘러보기 (zum.com)

 

현재 프로세스의 권한과 상관없이 자식 프로세스를 관리자 또는 일반 사용자 권한으로 실행하기

Windows Vista 이상 버전에서는 보안 강화를 위해 UAC 기술이 적용되어 관리자가 권한 수준을 높일때까지 응용 프로그램들을 일반 사용자 권한으로 제한하고 있습니다. 그리고 ShellExecute, CreateProcess

egloos.zum.com

 

반응형

댓글