锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / C++API和类开源 / Windows访问控制模型的用户组和令牌
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

Windows访问控制模型的用户组和令牌


11. What Groups are you a member of?您是哪个组的成员?

Q. You need to determine if the current user is an administrator. There's an action you need to perform that doesn't work if your program is not run as an administrator.问:您需要确定当前用户是否是管理员。如果您的程序未以管理员身份运行,则您需要执行的一项操作无法执行。

First, why do you need to know if you are an administrator? If there's a directory or registry key that you are denied access to, why not check the security descriptor of that folder? If there's an API that doesn't work, it may be because you don't have the right privilege enabled. Second, what are you doing that requires administrative privileges? Are you trying to access Program Files (write to "Application data" or "My documents" instead)? Or are you trying install some kind of malware (which requires admin privileges to do its damage)? Third, why do you have to be an Administrator specifically? Why can't it be someone like Power User or Domain Account Operator? Fourth, note that you can accomplish this by using the Net*Info functions, or by calling WMI, or even using the Shell provided function: IsUserAnAdmin().首先,为什么需要知道您是否是管理员?如果存在拒绝您访问的目录或注册表项,为什么不检查该文件夹的安全描述符?如果某个API不起作用,则可能是因为您没有启用正确的特权。其次,您在做什么需要管理权限?您是否要访问程序文件(改为写入“应用程序数据”或“我的文档”)?还是您正在尝试安装某种恶意软件(需要管理员特权才能对其进行破坏)?第三,为什么必须专门成为管理员?为什么不能像Power User或Domain Account Operator这样的人?第四,请注意,您可以通过使用Net*Info函数,调用WMI甚至使用Shell提供的函数来完成此操作:IsUserAnAdmin()

  1. You can get your entire group membership from the thread token (or process token). Open up your thread token (or process token if that fails), then call GetTokenInformation(TokenGroups) on the returned token. With the list of groups returned, look up the group SID, then see if that's the SID for the Administrators group.
  2. This is easier in Windows 2000 now that you have CheckTokenMembership() instead of having to read the entire list of groups. This is the method that IsUserAnAdmin() uses to check if you are an admin.
  3. ATL wraps CheckTokenMembership(), the Access Token and the Groups into its CAccessToken class. It's just a matter of supplying the correct SID for the Administrators group. Getting your token is easier in ATL, thanks to the GetEffectiveToken() method.
  4. 您可以从线程令牌(或进程令牌)获取整个组成员身份。打开您的线程令牌(如果失败,则打开进程令牌),然后调用GetTokenInformation(TokenGroups)返回的令牌。在返回的组列表中,查找组SID,然后查看该ID是否为Administrators组的SID。
  5. 现在,您CheckTokenMembership()不必再读取整个组列表,就可以在Windows 2000中更轻松地进行操作。这是IsUserAnAdmin()用于检查您是否为管理员的方法。
  6. ATL封装了CheckTokenMembership(),访问令牌和组功能包装到其CAccessToken类中。只需为Administrators组提供正确的SID即可。借助该GetEffectiveToken()方法,在ATL中获取令牌更加容易。
int IsAdminRunning(void)
{
  bool IsMember = FALSE;
  ATL::CAccessToken ProcToken;
  ATL::CAccessToken ImpersonationToken;
  ATL::CSid UserSid(Sids::Admins());

  ProcToken.GetEffectiveToken(TOKEN_READ | TOKEN_DUPLICATE);
  ProcToken.CreateImpersonationToken(&ImpersonationToken);
  ImpersonationToken.CheckTokenMembership(UserSid, &IsMember);

  return IsMember;
}

Figure 11: Determining if the user is an admin.

If you're dealing with a domain situation, you also need to lookup the domain administrators SID ("DA" in SDDL). A sample in the Platform SDK contains another sample solution.如果要处理域问题,还需要查找域管理员SID(DA在SDDL中为“ ”)。Platform SDK中的一个示例包含另一个示例解决方案。

12. Enabling Token Privileges启用令牌特权

Q. You need to read and edit the SACL of an object, but you can only read SACLs by having the "Manage auditing and security log" policy enabled. How do you enable this "SeSecurityPrivilege"?问:您需要读取和编辑对象的SACL,但是只能通过启用“管理审核和安全日志”策略来读取SACL。如何启用此“ SeSecurityPrivilege”?

  1. Inside the Platform SDK is a function called SetPrivilege(). This sample code will allow us to enable the "SeSecurityPrivilege" (provided the administrator has allowed us), and therefore read SACLs. This function simplifies the task of enabling the privilege into just one line. Before you copy it though, you'll want to add error handling to the function, and make the function call OpenThreadToken() itself.
  2. Use method 1.
  3. The ATL team found the SetPrivilege() function so useful, they created a method called CAccessToken::EnablePrivilege(), which is just SetPrivilege() ATL style.
  4. Platform SDK内有一个名为的函数SetPrivilege()。此示例代码将使我们能够启用“ SeSecurityPrivilege”(如果管理员允许我们启用),因此可以读取SACL。此功能简化了仅将特权启用到一行的任务。不过,在复制之前,您需要向函数添加错误处理,并使函数OpenThreadToken()本身调用。
  5. 使用方法1。
  6. ATL小组发现该SetPrivilege()功能非常有用,因此他们创建了一个称为的方法CAccessToken::EnablePrivilege(),它只是SetPrivilege()ATL样式。
void SetPrivilege(
     const ATL::CStringT< <TCHAR,
     ATL::StrTraitATL< <TCHAR> > &lpszPrivilege,
     bool bEnablePrivilege)
{
  ATL::CAccessToken ProcToken;
  ProcToken.GetEffectiveToken(TOKEN_QUERY |
    TOKEN_ADJUST_PRIVILEGES);

  if(bEnablePrivilege)
  {
    ProcToken.EnablePrivilege(lpszPrivilege);
  }
  else
  {
    ProcToken.DisablePrivilege(lpszPrivilege);
  }
}

Group policy privileges are turned off by default, even when enabled in group policy. You have to turn them on by changing your access token. You should be prepared to handle the case when the privilege is disabled in group policy (and you can't turn it on, no matter how hard you try). When you've finished with the privilege, don't forget to turn it back off.即使在组策略中启用了组策略特权,默认情况下也会将其关闭。您必须通过更改访问令牌来打开它们。当组策略中的特权被禁用时,您应该准备好处理这种情况(无论您如何努力,都无法将其打开)。完成特权后,别忘了将其关闭。

Q. Print out the current user, the list of available privileges, the list of restricted SIDs and the list of groups from a token.问:打印当前用户,可用特权列表,受限SID列表和令牌中的组列表。

This is a Whoami clone. To make life easier, we won't decrypt the attributes from a number to text (unlike what Whoami does).这是Whoami克隆。为了使生活更轻松,我们不会解密从数字到文本的属性(与Whoami有所不同)。

  1. You can get the needed information by calling GetTokenInformation() with the TokenInformation parameter set to TokenGroupsAndPrivileges. Then it is simply a matter of printing out the contents of the returned structure.
  2. See method 1.
  3. To get the groups from the token, call the CAccessToken::GetGroups() method, to return a CSidArray and a CAtlArray of DWORDs (the SIDS_AND_ATTRIBUTES map). This list contains a merged view of mandatory SIDs and restricted SIDs. You can use CAccessToken::GetPrivileges() to get similar results for the privileges.
  4. 您可以通过GetTokenInformation()将TokenInformation参数设置为来获取所需的信息TokenGroupsAndPrivileges。然后,只需打印出返回结构的内容即可。
  5. 请参阅方法1。
  6. 为了从令牌获得团体,调用CAccessToken::GetGroups()方法,返回一个CSidArray和CAtlArray的DWORD秒(SIDS_AND_ATTRIBUTES图)。此列表包含强制SID和受限SID的合并视图。您可以使用CAccessToken::GetPrivileges()这些特权获得类似的结果。
void DoWhoAmI(void)
{
  size_t i = 0;
  ATL::CAccessToken ProcToken;
  ATL::CSid SidUser;
  ProcToken.GetEffectiveToken(TOKEN_QUERY);

  /* First print off the user. */
  ProcToken.GetUser(&SidUser);
  std::wcout << _T("Owner: ") << 
        SidUser.AccountName() << _T("\r\n");

  /* Now print the groups */
  ATL::CTokenGroups pGroups;
  ProcToken.GetGroups(&pGroups);
  ATL::CSid::CSidArray pSids;
  ATL::CAtlArray pAttributes;
  pGroups.GetSidsAndAttributes(&pSids, &pAttributes);

  /* Iterate both pSids and pAttributes simultaneously */
  std::wcout << _T("\r\nGroups\r\n");
  for(i = 0; i < pGroups.GetCount() ; i++)
    std::wcout << pSids[i].AccountName() << _T(": ") <<
        pAttributes.GetAt(i) << _T("\r\n");

  /* Get the list of Privileges */
  ATL::CTokenPrivileges pPrivileges;
  ProcToken.GetPrivileges(&pPrivileges);
  ATL::CTokenPrivileges::CNames pNames;
  ATL::CTokenPrivileges::CAttributes pGroupAttributes;
  pPrivileges.GetNamesAndAttributes(&pNames, &pGroupAttributes);

  /* Printing Privileges is very similar to */
  std::wcout << _T("\r\nPrivileges\r\n");
  for(i = 0; i < pGroups.GetCount() ; i++)
  std::wcout << static_cast(pNames.GetAt(i))
  << _T(": ") << pGroupAttributes.GetAt(i) << _T("\r\n");

  /** TODO: the DWORDs are printed out as numbers. Convert these
  * DWORDs into text, the same text that whoami displays.
  **/
}

Figure 13: Regenerating the information from Whoami.

Q. How do you run IE with low rights in Windows XP / Server 2003?问:如何在Windows XP / Server 2003中以低权限运行IE?

This technique only applies to just Windows XP and Server 2003. The next version of Windows will change this technique.

  1. An example of using this method is not available. You'd have to resort to method 2 or 3 if you want to implement this.
  2. You can either use the CreateRestrictedToken() function to handle the necessary tasks, or for XP and above, you can utilize the Software Restriction Policies (SAFER for short). The SAFER functions are basically a set of predefined restricted tokens you can use to lower the rights of a process token.
  3. ATL has encapsulated the list of privileges into a CAtlArray, which makes it quite easy to iterate and disable the privileges. It's just as easy to create restricted tokens. However, these tokens can be a little too restrictive (restrictive enough to prevent the application initializing). Therefore, you may want to consider using the Software Restriction Policies as an alternative.

For method 2, I wrapped the SAFER routines into a class (to abstract object management from the caller).

该技术仅适用于Windows XP和Server2003。Windows的下一版本更改此技术。

  1. 没有使用此方法的示例。如果要实现此方法,则必须求助于方法2或3。
  2. 您可以使用该CreateRestrictedToken()功能来处理必要的任务,或者对于XP及更高版本,可以使用软件限制策略(简称SAFER)。SAFER函数基本上是一组预定义的受限令牌,可用于降低进程令牌的权限。
  3. ATL已将特权列表封装到中CAtlArray,这使得迭代和禁用特权非常容易。创建受限令牌也很容易。但是,这些标记可能过于严格(足以阻止应用程序初始化)。因此,您可能要考虑使用软件限制策略作为替代方法。

对于方法2,我将SAFER例程包装到一个类中(以从调用方抽象对象管理)。

class SaferRaiiWrapper {
public:
  /** Error handling has been added in the
  *downloadable version of this class
  **/
  explicit SaferRaiiWrapper(
    const DWORD dwScopeIdIn = SAFER_LEVELID_NORMALUSER,
    const HANDLE hTokenIn = NULL) : hToken(hTokenIn),
    LevelHandle(NULL), dwScopeId(dwScopeIdIn)
  {
    ::SaferCreateLevel(SAFER_SCOPEID_USER, this->dwScopeId,
                       SAFER_LEVEL_OPEN, &LevelHandle, NULL);
    ::SaferComputeTokenFromLevel(this->get_LevelHandle(),
                       NULL, &hToken, NULL, NULL);
  } ;


  virtual PROCESS_INFORMATION CreateProcessAsUser(const
    const std::basic_string &lpCommandLine,
    STARTUPINFO *lpStartupInfoIn = NULL,
    DWORD dwCreationFlags = CREATE_NEW_CONSOLE,
    const std::basic_string &lpApplicationName = _T(""),
    const std::basic_string &lpCurrentDirectory = _T(""),
    LPVOID lpEnvironment = NULL, BOOL bInheritHandles = FALSE,
    SECURITY_ATTRIBUTES *lpProcessAttributes = NULL,
    SECURITY_ATTRIBUTES *lpThreadAttributes = NULL)
  {
    STARTUPINFO StartupInfoAlt = {0};
    LPSTARTUPINFO lpStartupInfoActual = (lpStartupInfoIn != NULL) ?
    lpStartupInfoIn : &StartupInfoAlt;
    PROCESS_INFORMATION Result = {0};

    TCHAR *lpCmdLineWritable = new TCHAR[sCmdLine.capacity() + 1];
    /** The command line needs to be writable.
    * So make a writable copy of our command line.
    **/
    sCmdLine.copy(lpCmdLineWritable, sCmdLine.size());
    lpCmdLineWritable[sCmdLine.size()] = _T('\0');

    lpStartupInfoActual->cb = sizeof(STARTUPINFO);
    lpStartupInfoActual->lpDesktop = NULL;
    ::CreateProcessAsUser(this->hToken,
      (sAppName.empty() ? NULL : sAppName.c_str()),
      lpCmdLineWritable, lpProcessAttributes,
      lpThreadAttributes, bInheritHandles,
      dwCreationFlags, lpEnvironment,
      (sCurDir.empty() ? NULL : sCurDir.c_str()),
      lpStartupInfoActual, &Result);

    delete [] lpCmdLineWritable;

    return Result;
  } ;

  HANDLE get_hToken(void) const
  {
    return hToken;
  } ;

  virtual ~SaferRaiiWrapper()
  {
    ::CloseHandle(this->hToken);
    ::SaferCloseLevel(this->LevelHandle);
  } ;

protected:

  const SAFER_LEVEL_HANDLE &get_LevelHandle(void) const
  {
    return LevelHandle;
  } ;
  void set_LevelHandle(const SAFER_LEVEL_HANDLE &LevelHandleIn)
  {
    this->LevelHandle = LevelHandleIn;
  } ;

  void set_hToken(const HANDLE hToken)
  {
    this->hToken = hToken;
  } ;

private:
  HANDLE hToken;
  SAFER_LEVEL_HANDLE LevelHandle;
  const DWORD dwScopeId;
};

Figure 14: Creating a restricted token using the Software Restriction Policies.

友情链接
版权所有 Copyright(c)2004-2015 锐英源软件

公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768

地址:郑州市文化路47号院1号楼4层(47-1楼位于文化路和红专路十字路口东北角,郑州大学工学院招待所南边,工学院科技报告厅西边。)