1. 什么是Access Token
Access Token用来标识一个用户,其中包括用户的SID和用户所属组的SID,还包括这个用户和所属用户组的所拥有的权限列表。
当用户输入用户名和密码登陆到Windows之后,Windows会创建一个Acess Token,用来标识这个用户。 在这个用户下创建的进程都将得到一份这个Access Token的备份。当进程需要访问一个可以加密的对象(如文件,事件等)时,windows利用这个Access Token来跟对象的Security Descriptor进行比较,以确认这个进程是否有权限来访问这个对象。(关于SID和Security Descriptor请参阅其他资料)
2. 线程的Access Token
进程被创建时,从系统中得到Access Token是属于进程,线程刚开始创建的时候并没有Access Token。我们可以通过一个叫Impersonate的方法赋予线程一个Access Token,这样就使得线程具有跟进程不同的Access Token,用来完成一些使用进程的Access Token无法完成的任务。
Impersonate一般用于Client/Server模式中,Client发送一个请求访问服务期内的资源,如果Server接到请求后直接去访问资源的话,可能会导致Server无法控制Client可以访问哪些资源。Windows提供了Impersonate这种解决方案,Server的一个线程可以Impersonate Client,这样这个线程就将以Client的身份来访问资源,从而可以直接控制Client是否可以访问资源。
3. Impersonate的方法
有以下几个函数可以实现Impersonate
a) DdeImpersonateClient用于在DDE中Impersonate
b) ImpersonateNamedPipeClient用于在NamedPipe中Impersonate
c) ImpersonateLoggedOnUser利用Access Token来Impersonate
d) ImpersonateSelf 利用进程的Access Token来Impersonate
e) SetThreadToken 使用Access Token来Impersonate其他的线程
f) RpcImpersonateClient用于在RPC中Impersonate
g) ImpersonateSecurityContext 用于在security package中 Impersonate
4. 利用ImpersonateLoggedOnUser来实现Impersonate
其他的一些Impersonate的方法大都是使用在特定的环境下,ImpersonateLoggedOnUser则仅需要有用户的Access Token就可以。
获取用户的Access Token有两种方法
a) 利用LogonAsUser,通过输入用户名,密码,域信息来获取用户的Access Token
b) 利用OpenProcessToken来直接进程的Access Token
5. 例子
下面一个利用ImpersonateLoggedOnUser来实现Impersonate的例子。BOOL Impersonate(LPCSTR szFileName)函数中,输入参数为需要Impersonate的进程名,比如说explorer.exe
Int main()
{
OSVERSIONINFO os = {sizeof(OSVERSIONINFO)};
GetVersionEx(&os);
if(os.dwMajorVersion >= 6 ){//Only Impersonate when it is vista and later
Impersonate(_T("explorer.exe"));
//now this thread have the Access Token of explorer.exe
}
}
BOOL Impersonate(LPCSTR szFileName)
{
BOOL bResult = FALSE;
HANDLE hProcess = NULL;
HANDLE hToken = NULL;
for(DWORD dw=0; dw<0xFFFFFF; Sleep((++dw)*100)){//loop until success
DWORD dwSessionID = 0;//GetActiveConsoleSessionId();
if(!ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionID))
continue;
DWORD dwPID = GetPIDOFSpecifiedSession(dwSessionID, szFileName, NULL);
hProcess = OpenProcess(PROCESS_ALL_ACCESS,
TRUE, dwPID);
if(hProcess == NULL)
continue;
if(!OpenProcessToken(hProcess,
TOKEN_READ | TOKEN_DUPLICATE | TOKEN_QUERY|TOKEN_ASSIGN_PRIMARY ,
&hToken ))
{
CloseHandle(hProcess);
hProcess = NULL;
DbgPrint("In CreateProcessAsActiveWinlogon, Open Process Failed");
continue;
}
bResult = ImpersonateLoggedOnUser(hToken);
break;
}
return bResult;
}
DWORD GetActiveConsoleSessionId()
{
DWORD dwResult = 0;
HINSTANCE hKernal32 = NULL;
do {
OSVERSIONINFO os = {sizeof(OSVERSIONINFO)};
GetVersionEx(&os);
if(os.dwMajorVersion == 5 && os.dwMinorVersion==0){//2000
break;
}
else{//xp and vista
typedef DWORD (WINAPI* PWTSActiveSessionID)();
hKernal32 = LoadLibrary(_T("kernel32.dll"));
if(hKernal32 == NULL)
break;
PWTSActiveSessionID pWTSActiveSessionID =
(PWTSActiveSessionID)GetProcAddress(hKernal32,
_T("WTSGetActiveConsoleSessionId"));
if(pWTSActiveSessionID == NULL)
break;
dwResult = pWTSActiveSessionID();
}
} while(FALSE);
if(hKernal32 != NULL)
FreeLibrary(hKernal32);
return dwResult;
}
DWORD GetPIDOFSpecifiedSession(DWORD dwSessionID, LPCTSTR lpProcessName,
LPCTSTR lpUserName)
{
DWORD dwRet = 0;
PWTS_PROCESS_INFO lpProcessInfo = NULL;
do {
if (lpProcessName==NULL)
break;
DWORD dwProcessCount = 0;
if(!WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &lpProcessInfo,
&dwProcessCount))
{//This functions will fail if it is run in windows 2000
//and in windows 2000, I can just using the process id of current process instead
DbgPrint("In CCheckPIN::GetPIDOFSpecifiedSession, WTSEnumerateProcesses returns FALSE,\
Last Error:%d", GetLastError());
dwRet = GetCurrentProcessId();//using current process id
break;
}
// dump each process description
for (DWORD dwIndex = 0; dwIndex < dwProcessCount;
dwIndex++)
{
if(lpProcessInfo[dwIndex].SessionId != dwSessionID)
continue;
if(lstrcmpi(lpProcessInfo[dwIndex].pProcessName, lpProcessName) != 0 )
continue;
if(lpUserName != NULL){
TCHAR szUser[MAX_PATH] = {0};
DWORD chUser = MAX_PATH;
TCHAR szDomain[MAX_PATH] = {0};
DWORD chDomain = MAX_PATH;
SID_NAME_USE snu;
if(!LookupAccountSid(NULL, lpProcessInfo[dwIndex].pUserSid, szUser,
&chUser, szDomain, &chDomain, &snu))
{
DbgPrint("LookupAccountSid returns FALSE, LastError:%d", GetLastError());
break;
}
DbgPrint("szUser:%s, lpUserName:%s", szUser, lpUserName);
if(lstrcmpi(szUser, lpUserName) != 0)
{
break;
}
}
dwRet = lpProcessInfo[dwIndex].ProcessId;
break;
}//end of for loop
}while(FALSE);
if(lpProcessName != NULL){
WTSFreeMemory(lpProcessInfo);
}
return dwRet;
}