以前一直以为启动一个 SYSTEM 权限的进程是件很麻烦的事,查资料发现在 XP 及 2003 下可以通过获取 winlogon.exe 的令牌来启动 SYSTEM 权限的进程,而这招在 Windows 7 下却行不通了。因此唯一的方法就是在服务里启动进程,但 Windows 7 又存在 Session 0 隔离机制,无法直接访问创建的进程,这让我感到很困惑。
后来发现 MSDN 上有篇文章(http://msdn.microsoft.com/en-us/library/windows/desktop/aa379608(v=vs.85).aspx),它允许在服务中以指定的身份启动一个交互式进程,但是我看了半天都没发现怎么才能以 SYSTEM 权限启动。。。。。。。。。直到偶然间看到了一个 API SetTokenInformation,我才发现原来要启动一个 SYSTEM 权限的进程是如此的简单。。。
主要思路是:首先保证服务是运行在 SYSTEM 权限下的,然后获取自身的进程令牌,接着设置令牌的 Session 信息,最后用 CreateProcessAsUser 启动就行了。
function WTSGetActiveConsoleSessionId(): THandle; stdcall; external 'kernel32.dll'; function CreateSystemProcess(lpCommandLine: String): BOOL; var hToken, hDuplicatedToken: THandle; dwSessionId: DWORD; si: STARTUPINFO; pi: PROCESS_INFORMATION; begin //打开自身进程令牌 if OpenProcessToken(GetCurrentProcess,TOKEN_ALL_ACCESS,hToken) then begin //将令牌复制一份,成为主令牌,以保证能够用来启动进程 if DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hDuplicatedToken) then begin CloseHandle(hToken); hToken := hDuplicatedToken; //获取当前活动的 Session dwSessionId := WTSGetActiveConsoleSessionId; //设置令牌的 Session 为活动的 Session if SetTokenInformation(hToken, TokenSessionId, @dwSessionId, SizeOf(DWORD)) then begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.lpDesktop := 'winsta0\default'; si.wShowWindow := SW_SHOWNORMAL; //创建进程 Result := CreateProcessAsUser(hToken, PChar(lpCommandLine), nil, nil, nil, True, 0, nil, nil, si, pi); end else Result := False; end else Result := False; end else Result := False; CloseHandle(hToken); end;