淺析Windows安全相關的一些概念

Session

咱們日常所說的Session是指一次終端登陸, 這裏的終端登陸是指要有本身的顯示器和鼠標鍵盤等, 它包括本地登陸和遠程登陸。在XP時代每次終端登陸纔會建立一個Session,可是在Vista後全部的服務程序都運行在Session 0, 其餘終端會依次運行在session 1, session 2...

Logon Session

登陸Session是指不一樣賬號的登陸,它包括System登陸, 網絡登陸及活動交互登陸等。 咱們在任務管理器裏能夠看到各類進程運行在不一樣的賬號下,好比System, Local Service, xxx Account等, 這些賬號有不一樣的權限。這裏要注意區分上面的終端登陸Session, 每一個終端登陸Session內有好幾個Logon Session.

Window Station

按MSDN的說法,一個Window Station管理一個剪貼板(Clipboard),一個原子表(Atom Table)和一組桌面(Desktop)。爲何要有Window Station這個概念? 實際上每一個Window Station對應一個Logon Session, 也就是說經過Window Station, 把不一樣的賬號進行隔離,防止他們相互影響, 試想其餘人在你機器上執行一個DCOM對象,若是沒有Window Station隔離,他能夠直接操做你的桌面了。一個終端登陸Session能夠有多個Window Station,但只能有一個可交互的活動Window Station, 也就是Winsta0. 

Desktop

每一個Window Station能夠建立多個Desktop, 咱們平時和3個Desktop打交道比較多(WinLogon, Disconnect, Default), 他們分別表明登陸桌面,屏保桌面和咱們工做的桌面。咱們也能夠本身經過CreateDesktop建立桌面, 並經過SwitchDesktop進行切換。


Sid

Sid表示Security Identifier, 它是一串惟一標誌符, 它能夠表示表明一個賬號, 一個用戶組或是一次用戶登陸等, 具體能夠參考這裏

Token

Token和進程相關聯, 每一個進程建立時都會根據Logon Session權限由LSA(Local Security Authority)分配一個Token(若是CreaeProcess時本身指定了Token,  LSA會用該Token, 不然就用父進程Token的一份拷貝,由大部分進程是由Explorer.exe建立, 因此咱們大部分時候都複製了explorer.exe的Token), 裏面含有該進程的安全信息,包括用戶賬號, 組信息, 權限信息和默認安全描述符(Security Descriptor)等, 咱們能夠經過GetTokenInformation查詢某個Token的詳細狀況。具體能夠參考 這裏


DACL和SACL

DACL(discretionary access control list)用來標誌某個安全對象容許被哪些對象訪問。SACL(system access control list )用來記錄某個安全對象被訪問的狀況。具體能夠參考這裏


Security Descriptor

每一個安全對象在建立時均可以指定一個安全描述符(Security Descriptor), 若是沒有指定就用進程默認的, 該描述符指定了哪些對象能夠訪問該安全對象。大部分狀況下咱們都是傳NULL, 也就是用該進程Token中默認的。具體能夠參考這裏

Integrity level

這是UAC提供的新特性, 強制完整性控制(Mandatory Integrity Control), 它標誌某進程的安全性級別, 安全級別的高低很大程度和該標誌相關聯。


下面的圖表示了Session, Window Station和Desktop的關係:


下面的圖表示當某程序試圖訪問某個安全對象時, 系統是如何檢測的:系統會檢測Object的DACL列表, 根據當前進程的Token,判斷當前進程(線程)是否容許訪問該Object。



咱們用Process Explorer查看某個進程的屬性時, Security頁的信息如何理解? 



User和SID項表示建立該進程的用戶狀況,能夠經過GetTokenInformation, 將第二個參數指定成 TokenInformationClass來查詢。

Session項上面提到過了,表示終端登陸session ID, 能夠經過GetTokenInformation, 將第二個參數指定成 TokenSessionId 來查詢, 也能夠經過 API ProcessIDToSessionID來獲取

Logon Session表示Logon Session的authority id, 能夠經過 GetTokenInformation, 將第二個參數指定成 TokenStatistics來查詢。系統登陸的Logon Session id是999(0x3E7), 這裏要區分還有一個概念是Logon Session SID, 他們是不一樣的概念, 前者某種程度上反映了Logon Session的類型, 後則是某次登錄的標誌(SID)。

Virtualized是Vista以後纔有的概念,表示該程序是否啓用了UAC virtualization, 對於沒有指定manifest的老程序會使用數據重定向機制。能夠經過 GetTokenInformation的 TokenVirtualizationAllowed/ TokenVirtualizationEnabled來查詢。

Group是指該用戶所在的用戶組。咱們能夠看到儘管咱們的用戶在Administrators組裏,可是上面卻顯示是Deny的,爲何?由於在Vista以後, UAC打開時, 除非咱們顯式的Run As Admin, 不然咱們的程序都默認運行在標準用戶權限下。 同時咱們注意到上面還有Mandatory label\Medium Mandatory Level項,表示該程序運行的完整性級別, 它包括Untrust, Low, Medium, Hight, System等, 級別越低,權限也就越低。咱們能夠經過GetTokenInformation的 TokenIntegrityLevel來進行查詢。

Privilege表示該進程的權限, 咱們能夠看到好多權限默認是Disabled, 實際上咱們能夠經過 AdjustTokenPrivileges進行提高。 咱們能夠經過 GetTokenInformation的 TokenPrivileges進行查詢。

Kernel Object, User Object, GDI Object的使用範圍?

Kernel Object能夠跨進程使用, 若是指定成Global, 還能夠跨session.  XP時代即便不指定成Global, 服務程序和普通應用程序也能夠經過Kernel Object通信,可是Vista以後就不行了, 由於他們在不一樣的Session了。
User Object能夠跨進程使用, 可是User Object的使用範圍是Window Station, 它不能垮Window Station, 更別說跨session了。咱們看不到服務程序彈出的界面, 那是由於服務程序和咱們的桌面運行在不一樣的Window Station, 除非你指定「容許服務程序與桌面交互」, 顯式讓服務程序運行在活動桌面的Window Station (WinStat0) 。
GDI Object只有在建立它的進程裏有效。


怎樣以管理員的身份運行某個程序?

其實就是右鍵Run as Admin,  UAC打開時會有確認窗口。
::ShellExecute(0, L"runas",L"C:\\Windows\\Notepad.exe",0,0,SW_SHOWNORMAL);


如何判斷當前進程是否運行在管理員帳號下?

這裏包含2個概念 一個是運行程序的帳號是管理員帳號, 另一個是當前運行的環境是管理員環境。
咱們下面的Am_I_In_Admin_Group(TRUE)至關於Windows API  IsUserAnAdmin()

//若是bCheckAdminMode是TRUE, 則除了檢測Admin帳號外,還檢測是真的運行在Admin環境, 不然只是檢測Admin帳號。

BOOL Am_I_In_Admin_Group(BOOL bCheckAdminMode /*= FALSE*/)
{
 BOOL   fAdmin;
 HANDLE  hThread;
 TOKEN_GROUPS *ptg = NULL;
 DWORD  cbTokenGroups;
 DWORD  dwGroup;
 PSID   psidAdmin;windows

 SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;安全

 if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread))
 {
  if ( GetLastError() == ERROR_NO_TOKEN)
  {
   if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY, 
    &hThread))
    return ( FALSE);
  }
  else 
   return ( FALSE);
 }網絡


 if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups))
  return ( FALSE);session


 if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  return ( FALSE);測試


 if ( ! ( ptg= (TOKEN_GROUPS*)_alloca ( cbTokenGroups))) 
  return ( FALSE);ui


 if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups,
  &cbTokenGroups) )
  return ( FALSE);spa


 if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2, 
  SECURITY_BUILTIN_DOMAIN_RID, 
  DOMAIN_ALIAS_RID_ADMINS,
  0, 0, 0, 0, 0, 0, &psidAdmin) )
  return ( FALSE);線程

 fAdmin= FALSE;orm

 for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++)
 {
  if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin))
  {
   if(bCheckAdminMode)
   {
    if((ptg->Groups[dwGroup].Attributes) & SE_GROUP_ENABLED)
    {
     fAdmin = TRUE;
    }
   }
   else
   {
    fAdmin = TRUE;
   }
   break;
  }
 }對象

 FreeSid ( psidAdmin);

 return ( fAdmin);
}



如何提高權限?


注意只有原來是Disable的權限才能夠提成Enable, 若是原來就沒有這個權限, 是提不上去的。

BOOL EnablePrivilege(HANDLE hToken, LPCTSTR lpszPrivilegeName)
{
    TOKEN_PRIVILEGES tkp = {0};
    BOOL bRet = LookupPrivilegeValue( NULL, lpszPrivilegeName, &tkp.Privileges[0].Luid );
    
if(!bRet) return FALSE;

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    bRet = AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL );

    
return bRet;
}


如何判斷用戶的進程完整性級別?

該信息包含在Integrity  Level的SID裏,經過GetTokenInformation, 第二個參數設置成TokenIntegrityLevel,大概代碼以下, 詳細能夠參考後面下載的源代碼。

void  CIntegrityLevel::Print(std::wostream &  os)  const
{
    SID* pSid = (SID*)m_pIntegrity->Label.Sid;
    DWORD rid = pSid->SubAuthority[0];

    LPCTSTR lpszIntegrity = L"Unknown";
    
switch (rid)
    
{
    
case SECURITY_MANDATORY_UNTRUSTED_RID:
        
{
            lpszIntegrity = L"Untrusted";
            
break;
        }

    case SECURITY_MANDATORY_LOW_RID:
        
{
            lpszIntegrity = L"Low";
            
break;
        }

    case SECURITY_MANDATORY_MEDIUM_RID:
        
{
            lpszIntegrity = L"Medium";
            
break;
        }


    case SECURITY_MANDATORY_MEDIUM_PLUS_RID:
        
{
            lpszIntegrity = L"Medium +";
            
break;
        }

    case SECURITY_MANDATORY_HIGH_RID:
        
{
            lpszIntegrity = L"High";
            
break;
        }

    case SECURITY_MANDATORY_SYSTEM_RID:
        
{
            lpszIntegrity = L"System";
            
break;
        }

    default:
        
{
            lpszIntegrity = L"XXXXX";
        }

    }


    os << L"Integrity: " << lpszIntegrity << endl;
}



如何指定程序默認啓動運行的級別?

在VC裏配置Manifest文件。
asInvoker:默認選項,新的進程將簡單地繼承其父進程的訪問令牌
highestAvailable:應用程序會選擇該用戶容許範圍內儘量高的權限。對於標準用戶來講,該選項與asInvoker同樣,而對於管理員來講,這就意味着請求Admin令牌。
requireAdministrator:應用程序須要Admin令牌。運行該程序時,標準用戶將要輸入管理員的用戶名和密碼,而管理員則要在彈出的確認對話框中進行確認。



上面只是我本身的一些理解和總結, 因爲不是專門搞安全相關的,  若是有不正確的地方, 歡迎指正。

注,這是部分測試代碼: MySecurityTest
相關文章
相關標籤/搜索