在当前用户会话中从服务执行应用程序



使用Delphi 10.2.3,我正在实现一项服务,该服务需要在数据库恢复前关闭用户应用程序,然后重新启动应用程序。关闭应用程序没有问题,但由于会话0的原因,启动应用程序备份是有问题的。我在网上找到了以下代码来完成这项工作,它运行良好,只有一个例外。

function CreateEnvironmentBlock(var lpEnvironment: Pointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; external 'userenv.dll';
function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall; external 'userenv.dll';
function SvcLaunchAppInCurrUserSession(const AppToLaunch: String;
const Params: String = '';
WaitForIt: Boolean = False;
HideIt: Boolean = False): Cardinal;
var
PI: PROCESS_INFORMATION;
SI: STARTUPINFO;
bResult: Boolean;
dwSessionId: DWORD;
hUserTokenDup, hPToken: THANDLE;
dwCreationFlags: DWORD;
CommandLine: string;
Directory: string;
tp: TOKEN_PRIVILEGES;
pEnv: Pointer;
begin
Result := S_OK;
try
try
pEnv := nil;
dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE;
CommandLine := Trim('"'+Trim(AppToLaunch)+'" '+Params);
Directory := ExtractFileDir(AppToLaunch);
// get the current active session and the token
dwSessionId := WtsGetActiveConsoleSessionID;
// initialize startup info
ZeroMemory(@SI, SizeOf(SI));
SI.cb := SizeOf(STARTUPINFO);
SI.lpDesktop := nil; //PChar('winsta0Default');
SI.dwFlags := STARTF_USESHOWWINDOW;
if HideIt then
SI.wShowWindow := SW_HIDE
else
SI.wShowWindow := SW_SHOWNORMAL;
if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or
TOKEN_QUERY or
TOKEN_DUPLICATE or
TOKEN_ASSIGN_PRIMARY or
TOKEN_ADJUST_SESSIONID or
TOKEN_READ or
TOKEN_WRITE,
hPToken) then begin
tp.PrivilegeCount := 1;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin
DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserTokenDup);
SetTokenInformation(hUserTokenDup, TokenSessionId, @dwSessionId, SizeOf(DWORD));
if CreateEnvironmentBlock(pEnv, hUserTokenDup, True) then 
dwCreationFlags := dwCreationFlags or CREATE_UNICODE_ENVIRONMENT
else
pEnv := nil;
// Launch the process in the client's logon session.
bResult := CreateProcessAsUser(hUserTokenDup,      // client's access token
nil,                // file to execute
PChar(CommandLine), // command line
nil,                // pointer to process SECURITY_ATTRIBUTES
nil,                // pointer to thread SECURITY_ATTRIBUTES
False,              // handles are not inheritable
dwCreationFlags,    // creation flags
pEnv,               // pointer to new environment block
PChar(Directory),   // name of current directory
si,                 // pointer to STARTUPINFO structure
pi);                // receives information about new process
if not bResult then begin
Result := GetLastError;
Exit;
end;
end
else begin
Result := GetLastError;
Exit;
end;
end
else begin
Result := GetLastError;
Exit;
end;
if WaitForIt then begin
WaitForSingleObject(PI.hProcess, INFINITE);
GetExitCodeProcess(PI.hProcess, Result);
end;
finally
// close all handles
if Assigned(pEnv) then
DestroyEnvironmentBlock(pEnv);
CloseHandle(hUserTokenDup);
CloseHandle(PI.hProcess);
CloseHandle(PI.hThread);
CloseHandle(hPToken);
end;
except
on E:Exception do begin
DbgLogFmt('SvcLaunchApp %s: %s', [E.ClassName, E.Message]);
raise;
end;
end;
end;

问题是,它使用服务权限(SYSTEM(启动应用程序,这是一个巨大的安全漏洞。我希望它以当前用户的权限启动应用程序,无论是用户还是管理员,但不是系统。我对Windows安全的来龙去脉一无所知,但我相信有办法做到这一点——我只是不知道上面的哪些部分需要调整,以便使用正确的权限。或者,如果有更好的方法,我愿意接受。建议?

谢谢。

您正在使用服务作为(SYSTEM(运行的用户令牌。请改用WTSQueryUserToken()获取目标会话的用户令牌。

您必须首先模拟用户,然后运行应用程序。我写了一篇关于模拟的博客文章,这可能会对你有所帮助。为此,您需要用户凭据。

模拟将使您的程序(此处为服务(充当另一个用户。博客文章使用该功能向当前用户隐藏数据。在您的情况下,您的服务将首先模拟为目标用户,然后启动应用程序,该应用程序将在模拟用户的上下文中运行。

我在github上发布了源代码。这段代码是StackOverflow讨论的结果。

最新更新