一些Windows API返回一个主令牌,一些返回一个模仿令牌。 某些API需要主令牌,而其他API则需要模仿令牌。
例如,除使用LOGON32_LOGON_NETWORK
作为logintypes( dwLogonType
)时, LogonUser
通常会返回主令牌:
在大多数情况下,返回的句柄是可用于调用CreateProcessAsUser函数的主令牌。 但是,如果您指定LOGON32_LOGON_NETWORK标志,则LogonUser将返回一个您不能在CreateProcessAsUser中使用的模拟标记,除非您调用DuplicateTokenEx将其转换为主标记。
SetThreadToken
需要一个模仿令牌,而ImpersonateLoggedOnUser
似乎做了几乎相同的事情需要一个。
CreateProcessAsUser
和CreateProcessWithTokenW
都需要一个主令牌,并且都注意通过调用DuplicateTokenEx
可以从模仿令牌获取主令牌,但令牌types是什么意思 ?
该术语表述如下:
访问令牌
访问令牌包含login会话的安全信息。 系统在用户login时创build访问令牌,并且代表用户执行的每个进程都拥有该令牌的副本。 令牌标识用户,用户的组和用户的权限。 系统使用令牌来控制对安全对象的访问,并控制用户在本地计算机上执行各种与系统相关的操作的能力。 有两种访问令牌,主要和模拟。
主令牌
一个通常由Windows内核创build的访问令牌。 它可能被分配给一个进程来表示该进程的默认安全信息。
模拟令牌
访问令牌,用于捕获客户端进程的安全信息,允许服务器在安全操作中“模拟”客户端进程。
但这并不完全有用。 看起来有人想用“内核”这样的大男孩的话来说,但这只能提出更多的问题,比如除了分配给进程之外,还可以使用主令牌,还有除内核之外的其他人可以创build访问令牌?
(这是否意味着微软认为内核只是在内核模式下运行的一部分,还有执行程序等,或者是否意味着用户模式代码也可以创build令牌?无论如何,模式代码可以创build令牌,它必须通过系统调用来完成,就像任何对象pipe理器对象一样,所以令牌实际上将以内核模式创build)。
无论如何,这并不能回答这个基本问题:标记types有什么区别? 不是他们可能被用来做什么或者他们是如何创build的。
一位朋友向我介绍了Keith Brown 编写的Windows安全编程 ,它完全回答了这个问题。
主令牌可以而且应该被称为进程令牌,模仿令牌可以并且应该被称为线程令牌。 主要令牌只能附加到进程,模拟令牌只能附加到线程。 就这样。 他们确实可以自由转换使用DuplicateTokenEx
(假设你有必要的访问权限,你想要转换的句柄,显然)。
从书中的第115页开始:
BOOL DuplicateTokenEx( HANDLE ExistingToken, // in DWORD DesiredAccess, // in LPSECURITY_ATTRIBUTES Attributes, // in, optional SECURITY_IMPERSONATION_LEVEL ImpLevel, // in TOKEN_TYPE Type, // in PHANDLE NewToken); // out
…
Type
参数是历史文物。 如果您查看TOKEN_TYPE
枚举的定义,您会发现令牌已被分类为两类:模仿与主令牌。 不要挂在这个术语上, 其意义实际上比听起来简单得多。 冒充令牌只能附加到线程,而主令牌只能附加到进程。 这就是这个意思。 之前通过OpenProcessToken获得的进程令牌是主令牌。在Windows NT(3.x)的非常早期的版本中,根据你最初得到它的地方,你可以用一个标记来做什么有更严格的限制,因此引入了标记类型来跟踪令牌。 由于本文假设您使用的是Windows NT 4.0或更高版本,因此可以将模拟标记视为“线程标记”,将主标记视为“流程标记”,并在必要时使用
DuplicateTokenEx
在两者之间进行转换。 Windows NT 4.0通过引入DuplicateTokenEx
撕毁了两者之间的界限; 该函数的Windows NT 3.x版本DuplicateToken
被硬编码为仅生成模拟令牌。 事实上,现在你应该能够看到导致第一次调用SetThreadToken
失败的愚蠢的错误:代码试图将一个主令牌(从进程中获得的)附加到一个线程(这需要一个模拟令牌) 。 这是一个禁忌。 要解决逻辑问题和愚蠢的历史问题,下面是更正后的代码:
其他不是严格回答问题,而是在问题中提到的东西:
ImpersonateLoggedOnUser
去额外英里,并将主令牌转换为模拟令牌,而SetThreadToken
不打扰。 为什么? 谁知道? 可能出于同样的原因,一些API在通话期间启用特权,而另一些则需要呼叫者自行启用特权。 LogonUser
(和LsaLogonUser
)可能会返回网络登录的模拟令牌,因为通常会执行网络登录(例如,第83页)的假设。 ZwCreateToken
从用户模式创建令牌,这需要非常不寻常的权限(特别是唯一的SE_CREATE_TOKEN_NAME
)。 你可能不应该…