【问题标题】:CreateProcessAsUser is successful, but no process is createdCreateProcessAsUser 成功,但是没有创建进程
【发布时间】:2016-09-23 16:02:14
【问题描述】:

我们有一个服务应用程序,它在控制台会话 (WTSGetActiveConsoleSessionId) 中生成一个进程,以允许以桌面控件样式访问机器。这在大多数情况下都很好用,但是就 CreateProcessAsUser 的结果而言,有些 VM 似乎成功创建了进程,但是没有创建进程。

服务在 LocalSystem 帐户下运行。正在启动的进程尚未运行。没有病毒防护程序正在运行。我们只在 Windows Server 2008 R2 上看到过这种行为(但这并不是说它是独有的)。

我们使用的代码如下:

function StartProcessInSession(strProcess: String; bLocalSystem: Boolean = True; iSessionID: Integer = -1): Boolean;

  procedure SPISLog(strLog: String; bError: Boolean = False);
  begin
    Log(strLog);
    if bError then Abort;
  end;

var pi: PROCESS_INFORMATION;
  si: STARTUPINFO;
  winlogonPid, dwSessionId: DWord;
  hUserToken, hUserTokenDup, hPToken, hProcess: THANDLE;
  dwCreationFlags: DWORD;
  tp: TOKEN_PRIVILEGES;
  lpenv: Pointer;
  bError: Boolean;
  strClone: String;
begin
  if GetProcessID(strProcess, iSessionID) > 0 then
  begin
    Result := True;
    Exit;
  end;
  Result := False;
  bError := False;
  if not InitProcLibs then Exit;
  if bLocalSystem then strClone := 'winlogon.exe' else strClone := 'explorer.exe';
  winlogonPid := GetProcessID(strClone, iSessionID);
  try
    dwSessionId := WTSGetActiveConsoleSessionId();
    dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE;
    ZeroMemory(@si, sizeof(STARTUPINFO));
    si.cb := sizeof(STARTUPINFO);
    si.lpDesktop := PChar('Winsta0\Default'); 
    ZeroMemory(@pi, sizeof(pi));
    hProcess := OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);
    if (not OpenProcessToken(hProcess, 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
        bError := True;
    if bError then SPISLog('SPIS - OpenProcessToken failed (' + SysErrorMessage(GetLastError) + ').', True);
    if (not LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid)) then bError := True;
    if bError then SPISLog('SPIS - LookupPrivilegeValue failed (' + SysErrorMessage(GetLastError) + ').', True);
    tp.PrivilegeCount := 1;
    tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
    DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserTokenDup);
    SetTokenInformation(hUserTokenDup, TokenSessionId, Pointer(dwSessionId), SizeOf(DWORD));
    if (not AdjustTokenPrivileges(hUserTokenDup, FALSE, @tp, SizeOf(TOKEN_PRIVILEGES), nil, nil)) then bError := True;
    if bError then SPISLog('SPIS - AdjustTokenPrivileges failed (' + SysErrorMessage(GetLastError) + ').', True);
    if (GetLastError() = ERROR_NOT_ALL_ASSIGNED) then bError := True;
    if bError then SPISLog('SPIS - AdjustTokenPrivileges: ERROR_NOT_ALL_ASSIGNED (' + SysErrorMessage(GetLastError) + ').', True);
    lpEnv := nil;
    if (CreateEnvironmentBlock(lpEnv, hUserTokenDup, TRUE)) then
      dwCreationFlags := dwCreationFlags or CREATE_UNICODE_ENVIRONMENT
    else
      lpEnv := nil;
    if not Assigned(lpEnv) then SPISLog('SPIS - CreateEnvironmentBlock failed (' + SysErrorMessage(GetLastError) + ').', True);
    try
      UniqueString(strProcess);
      if not CreateProcessAsUser(hUserTokenDup, nil, PChar(strProcess), nil, nil, FALSE,
        dwCreationFlags, lpEnv, PChar(ExtractFilePath(strProcess)), si, pi) then bError := True;
      if bError then 
        SPISLog('SPIS - CreateProcessAsUser failed (' + SysErrorMessage(GetLastError) + ').', True)
      else
        SPISLog('Started process in ' + IntToStr(dwSessionId) + ' using token from ' + IntToStr(winlogonPid) + '.');
      try
        try CloseHandle(hProcess); except {} end;
        try CloseHandle(hUserToken); except {} end;
        try CloseHandle(hUserTokenDup); except {} end;
        try CloseHandle(hPToken); except {} end;
      except
        {}
      end;
    finally
      DestroyEnvironmentBlock(lpEnv);
    end;
  except
    on E: Exception do
    begin
      bError := True;
      if not (E is EAbort) then
        SPISLog('SPIS - ' + E.Message + ' (' + SysErrorMessage(GetLastError) + ').', True);
    end;
  end;
  Result := not bError;
end;

function GetProcessID(strProcess: String; iSessionID: Integer = -1): DWORD;
var dwSessionId, winlogonSessId: DWord;
  hsnap: THandle;
  procEntry: TProcessEntry32;
  myPID: Cardinal;
begin
  Result := 0;
  if not InitProcLibs then Exit;
  { check running processes and return ID of process in current session... }
  if iSessionID = -1 then
    dwSessionId := WTSGetActiveConsoleSessionId
  else
    dwSessionId := iSessionID;
  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSnap = INVALID_HANDLE_VALUE) then Exit;
  strProcess := UpperCase(ExtractFileName(strProcess));
  myPID:= GetCurrentProcessId;
  procEntry.dwSize := sizeof(TProcessEntry32);
  if (not Process32First(hSnap, procEntry)) then Exit;
  repeat
    if (procEntry.th32ProcessID <> myPID) and ((UpperCase(procEntry.szExeFile) = strProcess) or
      (UpperCase(ExtractFileName(procEntry.szExeFile)) = strProcess)) then
    begin
      winlogonSessId := 0;
      if (ProcessIdToSessionId(procEntry.th32ProcessID, winlogonSessId) and (winlogonSessId = dwSessionId)) then
      begin
        Result := procEntry.th32ProcessID;
        break;
      end;
    end;
  until (not Process32Next(hSnap, procEntry));
end;

有谁知道它为什么会失败,或者是否有办法通过这个调用来解决 API 中发生的事情?

【问题讨论】:

  • 使用 WTSEnumerateSessions 并搜索第一个活动会话而不是 WTSGetActiveConsoleSession。它不适用于 RDP 连接。
  • “用这个调用计算 API 中发生了什么的方法?” - 需要创建处于挂起状态的进程,将调试器附加到新创建的进程,然后恢复它
  • 这段代码几乎无法阅读。错误处理是难以理解的。
  • @FredS 谢谢,尽管我一直在尝试注入控制台,但我会试一试,不管任何 RDP 会话,我都会对第一次物理会话感到满意。
  • @RbMm 这很有趣,我会尝试,不知道如何创建处于挂起状态的进程,或者使用哪个调试器附加 - 您是否建议调试 Delphi 服务在启动后挂起?

标签: delphi winapi uac createprocessasuser


【解决方案1】:

非常感谢大家的帮助,我终于找到了问题所在,我开始的进程静态链接了一个 DLL(特别是 aw_sas64.dll),这在大多数机器上都有效,但在其他机器上无效,我仍然不确定为什么( DLL 与 EXE 位于同一文件夹中)。

我无法通过动态链接来使 DLL 工作(尽管 32 位版本动态链接正常),但是一旦我注释掉静态链接和用法,进程就可以通过上述过程启动。

再次感谢大家,我还有一些问题,但这解决了这个谜。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 2021-01-30
    • 1970-01-01
    • 2018-04-05
    • 2020-05-21
    相关资源
    最近更新 更多