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