Windows中的进程都会绑定一个token(权限令牌),这个token记录了该进程的权限,不同的用户有不同的权限,所以不同用户下启动的进程时绑定的token是不同的,可以修改这个token,让进程开启或禁用已经拥有的权限(SetTokenInformation)。注意不是增加,是修改,因为token是用户的权限策略,如果该用户没有这个权限,那么在他启动的进程中是不可能增加这个权限的。
Win7之后,windows又引入了session隔离用来保证各个用户之间的安全,特别是session0,是为第一个用户创建的,也就是系统服务,自己创建的服务程序也在这个system用户下,session默认也在0,不过session0创建的子进程可以是其他session的,这个就是通过SetTokenInformation修改token中的sessionid来做到。也就是说我们可以创建一个system权限,又工作在session1下面的进程。
创建admin用户进程拥有admin权限:
void CreateProcessAsAdmin()
{
HANDLE hToken;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 9208);
OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
WCHAR szDesl[] = L"winsta0\\default";
auto token = GetTokenById(9208);
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.lpDesktop = szDesl;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
PROCESS_INFORMATION pi;
WCHAR szApp[] = L"notepad";
CreateProcessAsUser(token, NULL, szApp, 0, 0, FALSE, 0, 0, 0, &si, &pi);
}
9208是explore的进程id,获取了admin用户的进程token,然后启动子进程,这个子进程就拥有了admin权限,而且也和explore一样工作在session1下面,因为token中就包含了用户权限信息和session信息。
创建System用户权限,并且工作在session1下面:
int CreateProcessAsSystem()
{
auto activeSession = WTSGetActiveConsoleSessionId();
HANDLE hSystemUserToken;
BOOL ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY | TOKEN_WRITE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_PRIVILEGES,
&hSystemUserToken);
if (!ret)
{
return -1;
}
HANDLE hCopyToken;
ret = DuplicateTokenEx(hSystemUserToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hCopyToken);
if (!ret)
{
return -4;
}
ret = SetTokenInformation(hCopyToken, TokenSessionId, (void*)&activeSession, sizeof(activeSession));
if (!ret)
{
return GetLastError();
}
WCHAR szDesl[] = L"winsta0\\default";
auto token = GetTokenById(9208);
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.lpDesktop = szDesl;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
PROCESS_INFORMATION pi;
WCHAR szApp[] = L"notepad";
ret = CreateProcessAsUser(hCopyToken, NULL, szApp, 0, 0, FALSE, 0, 0, 0, &si, &pi);
if (!ret)
{
return -5;
}
return 0;
}
现获取session1的id,然后打开当前进程的token,当前是服务进程所以也就是system用户的token,然后复制,必须复制,因为我们要修改的是子进程的令牌session,然后设置token中的sessionid为session1。创建后的进程为system用户权限,工作在session1中。
至于为什么进程既要受用户权限令牌(token)的约束,还要受session的约束(session隔离), 这块的原因还需要细细研究一下。