An access token is an object that describes the security context of a process or thread. The information in a token includes the identity and privileges of the user account associated with the process or thread. When a user logs on, the system verifies the user's password by comparing it with information stored in a security database. If the password is authenticated, the system produces an access token. Every process executed on behalf of this user has a copy of this access token.
一个访问令牌是一个对象:用来描述一个进程或者是线程的安全上下文。在一个令牌里的信息包含与这一个进程或者线程相关的用户帐户的标识符(identity)和(访问)特权。当一个用户登陆的时候,系统会验证用户的密码通过与存储在一个安全数据库中的信息比较。如果密码通过验证,则系统会产生一个访问令牌。每一个代表该用户执行的进程有一份这个访问令牌的拷贝。
The system uses an access token to identify the user when a thread interacts with a securable object or tries to perform a system task that requires privileges. Access tokens contain the following information:
当一个线程与一个安全的对象相互作用或者要执行一个需要特权级的系统任务,此时系统会用访问令牌来验证用户。访问令牌包含以下的信息。
very process has a primary token that describes the security context of the user account associated with the process. By default, the system uses the primary token when a thread of the process interacts with a securable object. Moreover, a thread can impersonate a client account. Impersonation allows the thread to interact with securable objects using the client's security context. A thread that is impersonating a client has both a primary token and an impersonation token.
Use the OpenProcessToken function to retrieve a handle to the primary token of a process. Use theOpenThreadToken function to retrieve a handle to the impersonation token of a thread. For more information, see Impersonation.
每一个进程有一个主要的令牌,它描述了与当前进程相关的用户帐户的安全上下文。默认情况下,系统用主令牌当一个进程的线程与一个安全对象相互作用时。而且(此外),一个线程可以模拟一个客户端帐户。模拟允许此线程与安全对象交互时用客户端的安全上下文。一个正模拟客户端的线程拥有一个主令牌和一个模拟令牌。
用函数:OpenProcessToken 来接收一个指向一个进程的主令牌句柄。用OpenThreadToken函数来得到一个指向线程模拟令牌的句柄。更多的信息可以看下面的客户端模拟。
你可以用下面的函数来操作访问令牌
Function |
Description |
AdjustTokenGroups | Changes the group information in an access token. |
AdjustTokenPrivileges | Enables or disables the privileges in an access token. It does not grant new privileges or revoke existing ones. |
CheckTokenMembership |
Determines whether a specified SID is enabled in a specified access token. |
CreateRestrictedToken | Creates a new token that is a restricted version of an existing token. The restricted token can have disabled SIDs, deleted privileges, and a list of restricted SIDs. |
DuplicateToken | Creates a new impersonation token that duplicates an existing token. |
DuplicateTokenEx | Creates a new primary token or impersonation token that duplicates an existing token. |
GetTokenInformation | Retrieves information about a token. |
IsTokenRestricted | Determines whether a token has a list of restricting SIDs. |
OpenProcessToken | Retrieves a handle to the primary access token for a process. |
OpenThreadToken | Retrieves a handle to the impersonation access token for a thread. |
SetThreadToken |
Assigns or removes an impersonation token for a thread. |
SetTokenInformation |
Changes a token's owner, primary group, or default DACL. |
The access token functions use the following structures to describe the parts of an access token.
Structure |
Description |
TOKEN_CONTROL | Information that identifies an access token. |
TOKEN_DEFAULT_DACL | The default DACL that the system uses in the security descriptors of new objects created by a thread. |
TOKEN_GROUPS | Specifies the SIDs and attributes of the group SIDs in an access token. |
TOKEN_OWNER | The default owner SID for the security descriptors of new objects. |
TOKEN_PRIMARY_GROUP | The default primary group SID for the security descriptors of new objects. |
TOKEN_PRIVILEGES | The privileges associated with an access token. Also determines whether the privileges are enabled. |
TOKEN_SOURCE | The source of an access token. |
TOKEN_STATISTICS | Statistics associated with an access token. |
TOKEN_USER | The SID of the user associated with an access token. |
typedef struct _TOKEN_CONTROL {
LUID TokenId;
LUID AuthenticationId;
LUID ModifiedId;
TOKEN_SOURCE TokenSource;
} TOKEN_CONTROL,*PTOKEN_CONTROL; |
typedef struct _TOKEN_DEFAULT_DACL {
PACL DefaultDacl;
} TOKEN_DEFAULT_DACL, *PTOKEN_DEFAULT_DACL; |
typedef struct _TOKEN_GROUPS {
DWORD GroupCount; SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY];
} TOKEN_GROUPS, *PTOKEN_GROUPS; |
typedef struct _TOKEN_OWNER {
PSID Owner;
} TOKEN_OWNER, *PTOKEN_OWNER; |
typedef struct _TOKEN_PRIMARY_GROUP {
PSID PrimaryGroup;
} TOKEN_PRIMARY_GROUP, *PTOKEN_PRIMARY_GROUP; |
typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; |
typedef struct _TOKEN_SOURCE { CHAR SourceName[TOKEN_SOURCE_LENGTH];
LUID SourceIdentifier;
} TOKEN_SOURCE, *PTOKEN_SOURCE; |
typedef struct _TOKEN_STATISTICS {
LUID TokenId;
LUID AuthenticationId; LARGE_INTEGER ExpirationTime; TOKEN_TYPE TokenType; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; DWORD DynamicCharged;
DWORD DynamicAvailable;
DWORD GroupCount;
DWORD PrivilegeCount;
LUID ModifiedId;
} TOKEN_STATISTICS, *PTOKEN_STATISTICS; |
typedef struct _TOKEN_USER {
SID_AND_ATTRIBUTES User;
} TOKEN_USER, *PTOKEN_USER; |
访问令牌函数用下面的枚举类型.
Enumeration type |
Specifies |
TOKEN_INFORMATION_CLASS | Identifies the type of information being set or retrieved from an access token. |
TOKEN_TYPE | Identifies an access token as a primary or impersonation token. |
Impersonation is the ability of a thread to execute using different security information than the process that owns the thread. Typically, a thread in a server application impersonates a client. This allows the server thread to act on behalf of that client to access objects on the server or validate access to the client's own objects.
模拟(令牌)是一个线程用不同安全信息执行的能力,而不是用拥有这一个线程的进程之安全信息。典型地,一个在一个服务程序中的线程模拟了一个客户端。这样允许这一个服务线程去活动代表那个客户端线程来访问在服务上的对象,或者是使对客户端自己的对象的访问生效。
微软的api提供了下面的函数来开始一个模拟。
The Microsoft Windows API provides the following functions to begin an impersonation:
For most of these impersonations, the impersonating thread can revert to its own security context by calling the RevertToSelf function. The exception is the RPC impersonation, in which the RPC server application calls RpcRevertToSelf or RpcRevertToSelfEx to revert to its own security context.
对于大多数的模拟,这个模拟线程可以恢复到原来的安全描述符通过调用函数RevertToSelf 。一个例外是RPC模拟,在这一种情况下RPC服务程序调用函数RpcRevertToSelf or RpcRevertToSelfEx 来恢复到原来的安全描述符。
访问令牌是一个被保护的对象,包含了与用户帐户相关的辨识和特权信息。当用户登陆到一台windows计算机,登陆进程会验证用户的登陆凭据。成功后,登陆进程返回一个对应用户的SID和一个用户的安全组SID列表。计算机LSA使用这些信息创建一个访问令牌(主访问令牌)。该访问令牌包括了由登录进程返回的SIDs和一份由本地安全策略分发给用户以及用户安全组的特权列表。此后,这份访问令牌的拷贝会跟每个代表用户执行的线程和进程链接。
如果拿到访问令牌,登录成功,要是访问某台本域内计算机的共享资源时,则必须出示访问令牌。从而来决定将拥有何种权限来访问。
在使用OpenProcess()函数打开一个进程时,通常是没有权限的,这时就需要设置进程访问令牌,更改进程的权限。
我们要修改一个进程的访问令牌,首先要获得进程访问令牌的句柄,这可以通过 OpenProcessToken得到,函数的原型如下:
BOOL OpenProcessToken(
HANDLE ProcessHandle,
DWORD DesiredAccess,
PHANDLE TokenHandle
);
第一参数是要修改访问权限的进程句柄;第三个参数就是返回的访问令牌指针;第二个参数指定你要进行的操作类型,如要修改令牌我们要指定第二个参数为TOKEN_ADJUST_PRIVILEGES(其它一些参数可参考Platform SDK)。通过这个函数我们就可以得到当前进程的访问令牌的句柄(指定函数的第一个参数为GetCurrentProcess()就可以了)。
要修改进程的什么权限?通过函数LookupPrivilegevalue()设定
BOOL LookupPrivilegevalue(
LPCTSTR lpSystemName, // system name
LPCTSTR lpName, // privilege name
PLUID lpLuid // locally unique identifier
);
第一个参数是系统的名称,如果是本地系统只要指明为NULL就可以了,第三个参数就是返回LUID的指针,第二个参数就是指明了权限的名称,如“SeDebugPrivilege”(Winnt.h有详细定义),如果要对一个任意进程进行指定了写相关的访问权限,设为"SeDeDebug"权限就可以了。
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount ;
LUID_AND_ATTRIBUTES Privileges [ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES
该结构包含一个数组,数据组的每个项指明了权限的类型和要进行的操作,PrivilegeCount指的数组元素的个数,接着是一个LUID_AND_ATTRIBUTES类型的数组,再来看一下LUID_AND_ATTRIBUTES这个结构的内容,声明如下:
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES
第一个参数就是指权限的类型,是一个LUID的值,LUID就是指locally unique identifier,保证局部唯一,就是指在系统的每一次运行期间保证是唯一的就可以了。这个就是LookupPrivilegevalue()返回的值。第二个参数就指明了我们要进行的操作类型,有3个可选项: SE_PRIVILEGE_ENABLED、SE_PRIVILEGE_ENABLED_BY_DEFAULT、SE_PRIVILEGE_USED_FOR_ACCESS。
最后,调用AdjustTokenPrivileges对这个访问令牌进行修改。AdjustTokenPrivileges的原型如下:
BOOL AdjustTokenPrivileges(
HANDLE TokenHandle,
BOOL DisableAllPrivileges,
PTOKEN_PRIVILEGES NewState,
DWORD BufferLength,
PTOKEN_PRIVILEGES PreviousState,
PDWORD ReturnLength
);
第一个参数是访问令牌的句柄(OpenProcessToken()返回的),第二个参数决定是进行权限修改还是Disable所有权限;第三个参数指明要修改的权限,是一个指向 TOKEN_PRIVILEGES结构的指针; 第四个参数是结构NewStateS的长度,如果NewState为空,该参数应为NULL;第五个参数也是一个指向 TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息,可空;最后一个参数为实际PreviousState结构返回的大小。
通过上述的调用,就可以提升OpenProcess()访问权限了。
举例说明,在OpenProcess()之前调用该函数。
bool enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
return false;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
CloseHandle(hToken);
return false;
}
return true;
}
PS :关于访问令牌的另一篇文章
From CTOBlog