浅析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
相关文章
相关标签/搜索