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

2016. 3. 6. 19:21IT-개발/winapi 및 MFC

반응형

원문 링크

 

DevMachine's Blog


(펌 : 원본 - http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=8668)

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


실제해보니 잘되네요~ 권한 낮춰서 실행시켜보려고 이래저래 수고 하다가 ~ 아래 방법을 찾았습니다. Process Token을 통해 특정 권한을 부여하는 방법을 하려고 했는데, 그건 service Process들만 가진 권한이라는 것도 더불어 알게 됐구요. ㅠㅠ

잡설이 길었네요. 아래 내용 그대로 참고하시면 됩니다.


Windows Vista 이상 버전에서는 보안 강화를 위해 UAC 기술이 적용되어 관리자가 권한 수준을 높일때까지 응용 프로그램들을 일반 사용자 권한으로 제한하고 있습니다. 그리고 ShellExecute, CreateProcess 등의 함수로 실행된 자식 프로세스는 현재 프로세스의 권한을 그대로 부여받게되죠. 

 

그렇다면 현재 프로세스와는 다른 권한으로 자식 프로세스를 실행하려면 어떻게 해야할까요? 현재 프로세스가 일반 사용자 권한으로 실행되고 있을때 자식 프로세스를 관리자 권한으로 실행하고 싶다면 ShellExecute 함수의 두번째 파라미터를 _T("runas") 로 넣어주면 간단히 해결됩니다. 

 

하지만 현재 프로세스가 관리자 권한으로 실행되고 있을때 자식 프로세스를 일반 사용자 권한으로 실행하는 것은 API 함수 레벨에서 제공되는 것이 없기 때문에 조금 까다롭습니다. 사실은 까다롭다기 보다는 정석적인 방법으로는 불가능하기 때문에 약간의 꼼수로 우회하는 방법을 사용해야 합니다. 바로 윈도우즈의 '작업 스케줄러' 를 이용하는 것인데요, 작업 스케줄러에 새 작업을 만들어 즉시 시작하도록 트리거 조건을 주고 프로세스를 실행하도록 동작을 추가하여 작업 스케줄러에 의해 자식 프로세스가 일반 사용자 권한으로 실행되도록 하는 것입니다.

 

위에서 설명한 방식을 사용하여 현재 프로세스의 권한과 상관없이 자식 프로세스를 관리자 또는 일반 사용자 권한으로 실행하는 함수를 만들어보았습니다. 이 함수의 사용 방식은 기존 ShellExecute 함수와 유사하며 첫번째 파라미터로 관리자 권한으로 실행할지 일반 사용자 권한으로 실행할지 선택하여 넘겨주면 됩니다. COM 인터페이스를 통해 작업 스케줄러에 새 작업을 등록하는 과정이 생각보다 복잡하기 때문에 엄청난 스크롤의 압박이 있습니다만 내부 구현을 전부 이해하실 필요는 없으니 간단하게 사용법만 익히시기 바랍니다. 


#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"));

 

 

Reference

 

Registration Trigger Example (C++)