如何修改线程的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;
}