티스토리 뷰

일반적으로 MFC등과 같은 프로그램을 개발할 때, "C:\Program Files\..."와 같은 파일 위치를 참조하거나
레지스트리의 HKLM/softwoare/... 등의 키를 참조하는 등의 일을 진행한다.
더 나아가서는 C:\Windows\System32\drivers\etc\hosts 와 같은 파일을 읽거나
쓰는 등의 일을 담당할 수도 있다.
XP 이하, 9x, NT, 200x 등에서 어드민 계정으로 로그인 하여 아무 이상없이 작동하던 코드가 있다면,
이를 비스타 하에서 재 컴파일하서 돌려보면 제대로 되기는 커녕, 시스템 call에서 계속 에러가
발생하기 쉽다.
가장 큰 이유는, 기존과 같이 로그인 한 계정이 admin계정이면 거기에서 실행되는 모든 프로그램은
모두 admin 계정을 갖는 기존 경우에서는 너무나 많은 보안 문제를 야기시키기에,
비스타부터는 모든 실행 프로세스가 디폴트로 일반 사용자 등급의 접근 제어 (UAC, User Access Control)를
갖도록 했다. 따라서 시스템을 건드리는 거의 모든 시스템 call이 오류를 발생시키는 것이다.
예를 들어, 레지스트리의 키를 Read only 모드로 읽는 것은 가능하나, QUERY 이상의 등급을 요청하면
에러가 발생한다. 마찬가지 이유로 몇몇 허용된 디렉터리를 제외한 위치에 있는 파일을 read only가 아닌
write 권한을 얻으려고 하면 역시 에러가 발생한다.
최상의 방법은 UAC를 벋어나지 않도록 프로그램을 재작성하는 것이고,
그것도 아닐 경우에는 admin 권한을 얻도록 하는 방법이 있다.
크게 두가지 방법이 존재하는데,
1) 매너페스트를 이용하는 방법
mt.exe를 이용하여 (또는 Visual Studio 200x 이상에서 매너페스트를 지원한다)
매너페스트를 dll 또는 exe에 임베딩시키는 방법이 있다.
이 매너페스트에 다음의 예와 같이

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly>
레벨을 어드민으로 승급(elevation)시키도록 요청한다.

mt.exe -manifest "Foobar.exe.manifest" -outputresource:"Foobar.exe";#1   
또는
mt.exe -manifest "Foobar.dll.manifest" -outputresource:"Foobar.dll";#2   

일반적으로 기존의 방법으로는 제대로 수행되지 않는 권한을 위와 같이 설정하면
UAC 권한을 묻는 창이 나타나며 확인을 하면 admin 권한으로 수행된다.
2) 또 다른 방법이 있는데 프로그램 상에서 승급하는 방법이 있다.
이 경우는 프로그램이 일반 모드에서 잘 수행하도록 하다가, 특정 상황에서
admin 권한을 얻어 돌도록 하는 방법인데, 쉽게 설명하면
탐색기의 "runas admin (관리자모드로 실행)"를 호출하는 방법이다.
비록 윈도우 창이 종료되었다가 다시 기동되지만 admin 권한을 얻을 수 있다.
(아주 어렵사리 하루 정도의 공을 들여 찾아냈는데 더 좋은 방법이 있으면 알려주셔요)

//-------------------------------------------------------------------------------------------
BOOL IsVista()
{
 OSVERSIONINFO osver;
 osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
 
 if ( ::GetVersionEx( &osver ) && 
   osver.dwPlatformId == VER_PLATFORM_WIN32_NT && 
   (osver.dwMajorVersion >= 6 ) )
  return TRUE;
 return FALSE;
}
BOOL 
MyShellExec( HWND hwnd, 
    LPCTSTR pszVerb, 
    LPCTSTR pszPath, 
    LPCTSTR pszParameters,// = NULL,
    LPCTSTR pszDirectory)// = NULL )
{
 SHELLEXECUTEINFO shex;
 memset( &shex, 0, sizeof( shex) );
 shex.cbSize   = sizeof( SHELLEXECUTEINFO ); 
 shex.fMask   = 0; 
 shex.hwnd   = hwnd;
 shex.lpVerb   = pszVerb; 
 shex.lpFile   = pszPath; 
 shex.lpParameters = pszParameters; 
 shex.lpDirectory = pszDirectory; 
 shex.nShow   = SW_NORMAL; 
 
 return ::ShellExecuteEx( &shex );
}
BOOL RunElevated( 
 __in  HWND hwnd, 
 __in  LPCTSTR pszPath, 
 __in_opt LPCTSTR pszParameters, // = NULL, 
 __in_opt LPCTSTR pszDirectory ) // = NULL );
{
 return MyShellExec( hwnd, _T("runas"), pszPath, pszParameters, pszDirectory );
}
BOOL RunMyself( HWND hwnd, BOOL bElevated )
{
 TCHAR szPath[ MAX_PATH ];
 ::GetModuleFileName( NULL, szPath, sizeof(szPath) );
 //BOOL bSuccess = (bElevated ? RunElevated( hwnd, szPath ) : RunAsStdUser( hwnd, szPath ) );
 BOOL bSuccess = (bElevated ? RunElevated( hwnd, szPath ) : FALSE );
/***
 if ( !bSuccess )
 {
  ::MessageBox( NULL, 
   _T("Could not perform this action."),
   "권한 얻기 실패",
   MB_OK | MB_ICONSTOP );
 }
***/
 return bSuccess;
}
typedef struct _TOKEN_ELEVATION {
  DWORD TokenIsElevated;
} TOKEN_ELEVATION, 
 *PTOKEN_ELEVATION;
typedef enum _TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
} TOKEN_ELEVATION_TYPE;
typedef enum _MY_TOKEN_INFORMATION_CLASS
{
/***
TokenUser = 1,
TokenGroups = 2,
TokenPrivileges = 3,
TokenOwner = 4,
TokenPrimaryGroup = 5,
TokenDefaultDacl = 6,
TokenSource = 7,
TokenType = 8,
TokenImpersonationLevel = 9,
TokenStatistics = 10,
TokenRestrictedSids = 11,
TokenSessionId = 12,
TokenGroupsAndPrivileges = 13,
TokenSessionReference = 14,
TokenSandBoxInert = 15,
TokenAuditPolicy = 16,
TokenOrigin = 17, 우찌된 이유인지 아래의 것이 MSDN에 안 나옵니다

***/
TokenElevationType = 18,
TokenLinkedToken = 19,
TokenElevation = 20,
TokenHasRestrictions = 21,
TokenAccessInformation = 22,
TokenVirtualizationAllowed = 23,
TokenVirtualizationEnabled =24,
TokenIntegrityLevel = 25,
TokenUIAccess = 26,
TokenMandatoryPolicy = 27,
TokenLogonSid = 28,
//MaxTokenInfoClass = 29
} MY_TOKEN_INFORMATION_CLASS;
BOOL IsElevated() {
 BOOL bElevated = FALSE;
 HRESULT hResult = IsElevated_I(&bElevated);
 return (hResult == S_OK) ? TRUE : FALSE;
}
HRESULT cqUtil::IsElevated_I( __out_opt BOOL * pbElevated ) //= NULL )
{
 ASSERT( IsVista() );
 HRESULT hResult = E_FAIL; // assume an error occured
 HANDLE hToken = NULL;
 if ( !::OpenProcessToken( 
    ::GetCurrentProcess(), 
    TOKEN_QUERY, 
    &hToken ) )
 {
  ASSERT( FALSE );
  return hResult;
 }
 TOKEN_ELEVATION te = { 0 };
 DWORD dwReturnLength = 0;
 if ( !::GetTokenInformation(
    hToken,
    (TOKEN_INFORMATION_CLASS)TokenElevation,
    &te,
    sizeof( te ),
    &dwReturnLength ) )
 {
  ASSERT( FALSE );
 }
 else
 {
  ASSERT( dwReturnLength == sizeof( te ) );
  hResult = te.TokenIsElevated ? S_OK : S_FALSE; 
  if ( pbElevated)
   *pbElevated = (te.TokenIsElevated != 0);
 }
 ::CloseHandle( hToken );
 return hResult;
}
//-------------------------------------------------------------------------------------------
그러면 MFC 메인 다이얼로그에 다음과 같은 함수를 만들어
비스타에다가 관리자가 모드가 아닌경우 재시작하도록 하면 된다.
BOOL CyourDlg::ElevateMe()  // 비스타인 경우 관리자모드로 재시작
{
 if (IsVista() && !IsElevated()) {
  int rc = MessageBoxA("비스타에서 본 기능을 사용하시려면 관리자 권한으로 다시 시작하셔야 합니다. 다시 시작하시겠습니까?", 
   "경고", MB_ICONWARNING | MB_YESNO);
  if (rc != 6) return FALSE;
  if (RunMyself((HWND)(*this))) {
   OnOK();
   return TRUE;
  }
 }
 return FALSE;
}


출처:Vista에서 UAC elevation 방법(http://mcchae.egloos.com/5202217)
댓글
  • 질문 아래 방법이 안되서 이 방법을 응용해 보려고 하는데...창이 닫히기는 하는데 새루 생성되질 않습니다..ㅠ

    아래처럼 작성했는데 뜻데로 되질 않습니다...ㅠ
    BOOL MainDlg::IsVista()
    {
    OSVERSIONINFO osver;
    osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );

    if ( ::GetVersionEx( &osver ) &&
    osver.dwPlatformId == VER_PLATFORM_WIN32_NT &&
    (osver.dwMajorVersion >= 6 ) )
    return TRUE;
    return FALSE;
    }

    BOOL MainDlg::MyShellExec( HWND hwnd,
    LPCTSTR pszVerb,
    LPCTSTR pszPath,
    LPCTSTR pszParameters,// = NULL,
    LPCTSTR pszDirectory)// = NULL )
    {
    SHELLEXECUTEINFO shex;
    memset( &shex, 0, sizeof( shex) );
    shex.cbSize = sizeof( SHELLEXECUTEINFO );
    shex.fMask = 0;
    shex.hwnd = hwnd;
    shex.lpVerb = pszVerb;
    shex.lpFile = pszPath;
    shex.lpParameters = pszParameters;
    shex.lpDirectory = pszDirectory;
    shex.nShow = SW_NORMAL;

    return ::ShellExecuteEx( &shex );
    }

    BOOL MainDlg::RunElevated(
    __in HWND hwnd,
    __in LPCTSTR pszPath,
    __in_opt LPCTSTR pszParameters, // = NULL,
    __in_opt LPCTSTR pszDirectory ) // = NULL );
    {
    return MyShellExec( hwnd, _T("runas"), pszPath, pszParameters, pszDirectory );
    }

    BOOL MainDlg::RunMyself( HWND hwnd, BOOL bElevated )
    {
    TCHAR szPath[ MAX_PATH ];
    ::GetModuleFileName( NULL, szPath, sizeof(szPath) );
    //BOOL bSuccess = (bElevated ? RunElevated( hwnd, szPath ) : RunAsStdUser( hwnd, szPath ) );
    BOOL bSuccess = (bElevated ? RunElevated( hwnd, szPath) : FALSE );
    /***
    if ( !bSuccess )
    {
    ::MessageBox( NULL,
    _T("Could not perform this action."),
    "권한 얻기 실패",
    MB_OK | MB_ICONSTOP );
    }
    ***/
    return bSuccess;
    }

    HRESULT MainDlg::IsElevated_I( __out_opt BOOL * pbElevated ) //= NULL )
    {
    ASSERT( IsVista() );
    HRESULT hResult = E_FAIL; // assume an error occured
    HANDLE hToken = NULL;
    if ( !::OpenProcessToken(
    ::GetCurrentProcess(),
    TOKEN_QUERY,
    &hToken ) )
    {
    ASSERT( FALSE );
    return hResult;
    }
    TOKEN_ELEVATION te = { 0 };
    DWORD dwReturnLength = 0;
    if ( !::GetTokenInformation(
    hToken,
    (TOKEN_INFORMATION_CLASS)TokenElevation,
    &te,
    sizeof( te ),
    &dwReturnLength ) )
    {
    ASSERT( FALSE );
    }
    else
    {
    ASSERT( dwReturnLength == sizeof( te ) );
    hResult = te.TokenIsElevated ? S_OK : S_FALSE;
    if ( pbElevated)
    *pbElevated = (te.TokenIsElevated != 0);
    }
    ::CloseHandle( hToken );
    return hResult;
    }

    BOOL MainDlg::IsElevated() {
    BOOL bElevated = FALSE;
    HRESULT hResult = IsElevated_I(&bElevated);
    return (hResult == S_OK) ? TRUE : FALSE;
    }

    bool MainDlg::runAsAdmin()
    {
    if (IsVista() && !IsElevated()) {
    int rc = MessageBox(_T("다시 시작하시겠습니까?"), _T("경고"), MB_ICONWARNING | MB_YESNO);
    if (rc != 6) return FALSE;
    if (RunMyself(m_hWnd, TRUE)) {
    OnOK();
    return TRUE;
    }
    }
    return FALSE;
    }
    2010.08.11 11:12
  • BlogIcon jhbaek 실행이 왜 안될까요..
    보기엔 문제가 없어보이는데요..흠..

    szPath가 제대로 얻어졌는지 확인해 보셨나요?
    shex에서 한번 FILE을 메모장이나 계산기등을 넣고 테스트도 해보시길 바랍니다!!
    2010.08.18 11:39 신고
댓글쓰기 폼