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;
}