9 管理員以標準用戶權限運行時shell
Windows vista以前的windows版本採用一刀切的方式,全部資源管理器的子進程都會獲得由資源管理器進程授予關聯的令牌環,這樣不安全。windows
Windows vista中,若是用戶使用管理員這樣的搞特權帳戶登陸,除了與這個帳戶對應的的安全令牌以外,還有一個通過篩選的令牌(filtered token)。 之後從包括windows資源管理器在內的第一個進程開始,這個篩選後的令牌會與系統表明最終用戶啓動更多全部新進程關聯, 權限受限的進程沒法訪問須要高權限的資源。api
怎麼提高權限呢,權限的提高只能在進程的邊界上提高,邊界也就是進程建立的時候,能夠右鍵:【以管理員身份運行】, 而後永固可能會看到三種類型的對話框:數組
① 藍色:應用程序是系統的一部分安全
② 灰色:應用程序進行了簽名服務器
③ 橙色:應用程序沒有簽名函數
若是用戶當前是以一個標準用戶的身份登陸,系統會彈出一個要求輸入管理員帳戶密碼的登陸框,這種機制稱爲over-the-shoulder,即管理員越過標準用戶的肩膀輸入管理員帳戶密碼,標準用戶乾瞪眼。工具
通常須要管理員權限運行的程序圖標上會有一個盾牌的圖標。 在windows任務管理器上有一個【顯示全部用戶的進程】的按鈕,這個按鈕的功能就須要管理員權限(有個盾牌圖標),單擊之後發現當前taskmgr.exe進程的PID變了,說明此時已經不是剛纔那個任務管理器了,再次說明進程權限的提高只能是在進程的邊界上。ui
一個未提高權限的進程能夠建立一個提高了權限的進程,後者將包含一個COM服務器,這個新進程將保持活動狀態,這樣一來,未提高權限的進程就能夠向已經提高了權限的進程發出IPC調用,從而沒必要爲了提高權限而啓動一個新的實例。spa
9.1 自動提高進程權限
在清單文件中,添加<trustinfo>段,以下:
<trustinfo xmlns=」urn:schemas-microsoft-com:asm.v2」>
<security>
<requestedPrivileges>
<requestedExecutionLevel
Level=」requiredAdministrator」 />
</requestedPrivileges>
</security>
</trustinfo>
在VS2010中,能夠在項目屬性中設置,
Level的取值能夠有以下三個:
① requireAdministrator:必須以管理員權限啓動,不然沒法運行
② highestAvaliable:按當前可用的最高權限,若是用戶使用管理員帳戶則會出現一個要求批准提高權限的對話框。 若是用戶使用普通用戶帳戶登陸,則用這些標準權限來啓動(不會提高用戶權限)
③ asInvoker:應用程序使用與主調應用程序同樣的權限來啓動
9.2 手動提高權限
BOOL ShellExecuteEx( LPSHELLEXECUTEINFO pExecInfo);
1 Typedef struct _SHELLEXECUTEINFO{ 2 3 DWORD cbSize, //結構體大小 4 5 ULONG fMask; 6 7 HWND hwnd; 8 9 PCTSTR lpVerb; //必須爲 _T(「runas」) 10 11 PCTSTR lpFile; //可執行文件名 12 13 PCTSTR lpParameters; 14 15 PCTSTR lpDirectory; 16 17 Int nShow; 18 19 HINSTANCE hInstApp; 20 21 PVOID lpIDList; 22 23 PCTSTR lpClass; 24 25 HKEY hKeyClass; 26 27 DWORD dwHotKey; 28 29 Union{ 30 31 HANDLE hIcon; 32 33 HANDLE hMonitor; 34 35 }DUMMYUNIONNAME; 36 37 38 39 HANDLE hProcess; 40 41 }SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO; 42 43 44 45 //eg. 46 47 void CShellExecuteExDlg::OnBnClickedButton1() 48 { 49 50 // TODO: 在此添加控件通知處理程序代碼; 51 52 PCTSTR lpFile = _T("C:\\Program Files (x86)\\EditPlus 3\\EditPlus.exe"); 53 54 55 SHELLEXECUTEINFO shellInfo = {sizeof(shellInfo)}; 56 57 58 shellInfo.lpVerb = _T("runas"); 59 60 shellInfo.lpFile = lpFile; 61 62 63 64 BOOL bRet = ShellExecuteEx(&shellInfo); 65 66 if (FALSE == bRet) 67 { 68 69 DWORD dwErrorCode = GetLastError(); 70 71 if (ERROR_CANCELLED == dwErrorCode) 72 73 { 74 75 AfxMessageBox(_T("ERROR_CANCELED")); 76 77 } 78 79 else if (ERROR_FILE_NOT_FOUND == dwErrorCode) 80 81 { 82 83 AfxMessageBox(_T("ERROR_FILE_NOT_FOUND")); 84 85 } 86 87 } 88 89 }
注意:當一個進程使用它提高後的權限啓動時,它每次調用CreateProcess來生成另外一個進程時,子進程都會得到和它的父進程同樣的提高後的權限,在這種狀況下不須要調用ShellExecuteEx。 假如一個應用程序是用一個篩選後的令牌來運行,那麼一旦視圖調用CreateProcess來建立一個要求提高權限的可執行文件時就會失敗,GetLastError返回ERROR_ELEVATION_ERQUIRED。
若是但願被調試的進程繼承什麼權限,就以那種權限來啓動VISUAL STUDIO,
9.3 當前權限上下文
BOOL OpenProcessToken(
__in HANDLE ProcessHandle, //要修改訪問權限的進程句柄
__in DWORD DesiredAccess, //指定你要進行的操做類型
__out PHANDLE TokenHandle //返回的訪問令牌指針
);
BOOL WINAPI GetTokenInformation(
_In_ HANDLE TokenHandle,
_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
_Out_opt_ LPVOID TokenInformation,
_In_ DWORD TokenInformationLength,
_Out_ PDWORD ReturnLength
);
BOOL WINAPI CreateWellKnownSid(
__in WELL_KNOWN_SID_TYPE WellKnownSidType, // WELL_KNOWN_SID_TYPE是枚舉類型,它包含一系列的安全描述符類型
__in_opt PSID DomainSid, // DomainSid 指向建立了SID的域的指針,爲NULL時表示使用本地計算機
__out_opt PSID pSid, // pSid 指向存儲SID的地址
__inout DWORD *cbSid // cbSid 指向存儲pSid的大小的地址
);
typedef enum _TOKEN_ELEVATION_TYPE {
TokenElevationTypeDefault = 1,//進程以默認用戶運行,或者UAC被禁用
TokenElevationTypeFull,//進程權限被成功提高&&令牌沒有被篩選過
TokenElevationTypeLimited,//進程以受限的權限運行,它對應於一個篩選過的令牌
} TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE;
1 //eg. 2 3 void CShellExecuteExDlg::OnBnClickedButton2() 4 { 5 6 // TODO: 在此添加控件通知處理程序代碼; 7 8 9 TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeLimited; 10 11 BOOL bret = FALSE; 12 13 14 BOOL bResult = GetProcessElevation(&elevationType, &bret); 15 16 17 } 18 19 20 21 BOOL CShellExecuteExDlg::GetProcessElevation( TOKEN_ELEVATION_TYPE *pElevationType, BOOL *pIsAdmin ) 22 23 { 24 25 HANDLE hToken = NULL; 26 27 DWORD dwSize = 0; 28 29 30 if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) 31 { 32 33 return FALSE; 34 35 } 36 37 38 BOOL bRetult = FALSE; 39 40 if (GetTokenInformation(hToken, TokenElevationType, 41 42 pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) 43 44 { 45 46 BYTE adminSID[SECURITY_MAX_SID_SIZE]; 47 48 dwSize = sizeof(adminSID); 49 50 CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize); 51 52 if (*pElevationType == TokenElevationTypeLimited) 53 54 { 55 56 HANDLE hUnfilteredToken = NULL; 57 58 GetTokenInformation(hToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize); 59 60 61 62 if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin)) 63 { 64 65 bRetult = TRUE; 66 67 } 68 69 70 CloseHandle(hUnfilteredToken); 71 72 } 73 74 else 75 { 76 77 *pIsAdmin = IsUserAnAdmin(); 78 79 bRetult = TRUE; 80 81 } 82 83 } 84 85 86 CloseHandle(hToken); 87 88 89 return bRetult; 90 91 }
首先判斷令牌是否被篩選過,若是令牌沒有被篩選過,則要判斷是不是以管理員身份在運行,IsUserAnAdmin()函數來判斷是否以管理員身份運行。 在令牌已被篩選的狀況下,須要把未篩選的令牌(把TokenLinkedToken傳給GetTokenInformation),而後判斷其中是否包含一個管理員SID(藉助CreateWellKnownSid和CheckTokenMembership)。
關於盾牌圖標:
① LRESULT Button_SetElevationRequiredState(
[in] HWND hwnd, //Button控件句柄
[in] BOOL fRequired //TRUE須要盾牌圖標,FALSE不須要
);
② SHGetStockIconInfo傳入SIID_SHIELD參數也能夠獲取盾牌圖標。
10 枚舉系統中正在運行的進程
Win95/win98:Process32First, Process32Next
Win NT: EnumProcesses
BOOL WINAPI EnumProcesses(
_Out_ DWORD * pProcessIds,//保存進程ID的數組,要分配足夠大
_In_ DWORD CB, //數組的大小(字節)
_Out_ DWORD * pBytesReturned //返回的數組的字節數
);
1 void CEnumProcessDlg::OnBnClickedButton1() 2 { 3 4 // TODO: 在此添加控件通知處理程序代碼; 5 6 DWORD dwArr[300] = {0}; 7 8 DWORD dwBytes = 0; 9 10 11 typedef BOOL (WINAPI *MYFUNC)(DWORD*, DWORD, DWORD*); 12 13 14 HINSTANCE hInst = LoadLibrary(_T("psapi.dll")); 15 16 if (NULL != hInst) 17 { 18 19 MYFUNC myFunc = (MYFUNC) GetProcAddress(hInst, "EnumProcesses"); 20 21 BOOL bRet = myFunc(dwArr, sizeof(dwArr), &dwBytes); 22 23 if (! AllocConsole()) //MFC程序輸出到控制檯 24 { 25 26 freopen("CONOUT$","w+t",stdout); 27 28 freopen("CONIN$","r+t",stdin); 29 30 31 int count = 0; 32 33 for (int i = 0; i < sizeof(dwArr) / sizeof(DWORD); ++ i) 34 { 35 36 if (dwArr[i] != (DWORD)(0)) 37 38 { 39 40 std::cout << dwArr[i] <<std::endl; 41 42 ++count; 43 44 } 45 46 } 47 48 49 50 std::cout<<std::endl<<"****************"<<count<<"*************"<<std::endl; 51 52 53 fclose(stdout); 54 55 fclose(stdin); 56 57 system("pause"); 58 59 FreeConsole(); 60 61 } 62 63 64 FreeLibrary(hInst); 65 66 } 67 68 }
//得到一個模塊的首選基地址
PVOID GetModulePreferredBaseAddr(
DWORD dwProcessId, //進程ID
PVOID pvModuleRomete //進程內一個模塊的地址
)
關於完整性級別(Integrity level):
除了衆所周知的的安全描述符(SID)和訪問控制列表(access control list, ACL),系統還經過在系統訪問控制列表(SACL)中新增一個名爲強制標籤的訪問控制項(access control entry, ACE)來爲受保護的資源分配一個所謂的完整性級別(integrity level)。 凡是沒有這個ACE的安全對象,操做系統將默認其擁有「中」(Medium)完整性級別。 另外每一個進程都有一個基於其安全令牌的完整性級別,它與系統授予的一個信任級別是對應的,以下:
低----保護模式中的IE是以「低」的信任級別來運行的,目的在於拒絕從網上下載的代碼修改其餘應用程序和windows環境
中----默認狀況下,應用程序都以「中」信任級別來啓動&&使用一個篩選過的令牌來運行
高----若是程序以提高後的權限來啓動,則以「高」信任級別來運行
系統---只有以local system 或 local service的身份來運行的進程才能得到這個信任級別。
使用process explorer工具能夠查看進程的完整性級別。
GetTokenInformation傳入TokenMandatoryPolicy和進程的安全令牌句柄,返回的是一個DWORD值,其中包含了一個位掩碼(bitwise mask),詳細描述了使用的策略。
POLICY_NO_WRITE_UP---在這個安全令牌下運行的代碼不能向具備更高完整性級別的資源寫入
POLICY_NEW_PROCESS_MIN---在這個安全令牌下運行的代碼啓動一個新的進程時,子進程將檢查父進程和清單中描述的優先級,並從中選擇最低的一個優先級。 若是沒有清單就假定清單中的優先級爲「中」。
用戶界面特權隔離:
對於窗口,使用完整性級別來拒絕低完整性級別的【進程】訪問/更新高完整性級別的【進程】的用戶界面UI,這種機制稱爲【用戶界面特權隔離】。
操做系統將阻止低完整性級別的【進程】經過PostMessage/SendMessage/HOOK向高完整性級別的【進程】發送windows消息,或HOOK完整性級別高的進程的windows消息。
//終於寫完了 第四章 時間:2013年5月25日 23:59:17