У нас есть проблема, которая возникает только в новом сценарии, который мы должны поддерживать.
Вот сценарий, в котором он работает уже много лет:
update-service, который затем запускает второй экземпляр загрузчика wix как local system. Этот второй экземпляр с разрешениями local system используется для установки нашего программного обеспечения. Поскольку нам также нужно выполнять некоторые действия с базой данных, мы запускаем инструмент под названием dbinit в пользовательском действии, и поскольку этот инструмент должен получить доступ к серверу sql, он должен работать с привилегиями исходного пользователя. Это прекрасно работает.Как видите, в новом сценарии CreateProcessAsUser также завершается успешно, но dbinit-процесс тут же завершается с кодом ошибки C0000142.
Мы попытались создать пользовательский WindowStation/Desktop. Разрешения должны быть правильными (проверено с помощью ProcessHacker), и мы также пометили ручки как inheritable. Но независимо от того, являются ли они inheritable или мы устанавливаем lpDesktop соответственно, это просто ничего не меняет.
Мы выяснили, что если пользователь, под которым запущена служба Windows, входит в группу локальных администраторов, она работает, но мы не можем этого сделать в продакшене.
Во многих примерах кода я обнаружил, что люди используют LogonUser для получения токена, но поскольку наш пользователь большую часть времени является MSA (управляемой учетной записью службы) и у нас нет паролей, я не думаю, что это возможно.
Если я устанавливаю lpDesktop на пустую строку, она работает примерно в 50% случаев, поэтому кажется, что она должна что-то делать с WindowStation и Desktop. Чего я не понимаю, так это почему это не работает последовательно и почему это не помогает создавать собственные WindowStation и Desktop с соответствующими правами.
Некоторый код:
internal static SafeTokenHandle GetProcessAccessToken(int processId)
{
var process = Process.GetProcessById(processId);
if (OpenProcessToken(process.Handle, TOKEN_DUPLICATE, out IntPtr tokenHandle))
return new SafeTokenHandle(tokenHandle);
else
throw new Win32Exception();
}
internal static SafeTokenHandle DuplicateAccessToken(SafeTokenHandle token)
{
var success = DuplicateTokenEx(token,
TOKEN_ALL_ACCESS,
null,
IMPERSONATION_LEVEL_SecurityIdentification,
TOKEN_TYPE_TokenPrimary,
out IntPtr newToken);
return success ? new SafeTokenHandle(newToken) : throw new Win32Exception();
}
private bool Start()
{
using (var processToken = GetProcessAccessToken(StartInfo.ProcessIdToImpersonateUserContext))
{
using (var newToken = DuplicateAccessToken(processToken))
{
var si = new STARTUPINFO();
var pi = new PROCESS_INFORMATION();
var safeProcessHandle = new SafeProcessHandle();
var safeThreadHandle = new SafeThreadHandle();
SafeFileHandle redirectedStandardOutputParentHandle = null;
try
{
var profileInfo = new PROFILEINFO();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = "LimitedUser";
var succeeded = LoadUserProfile(newToken, ref profileInfo);
if (!succeeded)
throw new Win32Exception();
var cmdLine = $"\"{StartInfo.FileName}\" {StartInfo.Arguments}".Trim();
if (StartInfo.RedirectStandardOutput)
{
CreatePipe(out redirectedStandardOutputParentHandle, out si.hStdOutput);
si.dwFlags = STARTF_USESTDHANDLES;
}
int creationFlags = 0;
if (StartInfo.CreateNoWindow)
creationFlags |= CREATE_NO_WINDOW;
creationFlags |= CREATE_UNICODE_ENVIRONMENT;
int logonFlags = 0;
if (StartInfo.LoadUserProfile)
logonFlags |= (int)LogonFlags.LOGON_WITH_PROFILE;
string workingDirectory = StartInfo.WorkingDirectory;
if (string.IsNullOrEmpty(workingDirectory))
workingDirectory = Environment.CurrentDirectory;
var envBlock = GetEnvironmentBlock(newToken);
succeeded = CreateProcessAsUserW(newToken,
null,
cmdLine,
null,
null,
true,
creationFlags,
new HandleRef(null, envBlock.DangerousGetHandle()),
workingDirectory,
si,
pi);
if (!succeeded)
throw new Win32Exception();
if (pi.hProcess != (IntPtr)0 && pi.hProcess != INVALID_HANDLE_VALUE)
safeProcessHandle.InitialSetHandle(pi.hProcess);
if (pi.hThread != (IntPtr)0 && pi.hThread != INVALID_HANDLE_VALUE)
safeThreadHandle.InitialSetHandle(pi.hThread);
DestroyEnvironmentBlock(envBlock.DangerousGetHandle());
}
finally
{
si.Dispose();
}
if (StartInfo.RedirectStandardOutput)
{
var enc = StartInfo.StandardOutputEncoding ?? Console.OutputEncoding;
StandardOutput = new StreamReader(new FileStream(redirectedStandardOutputParentHandle, FileAccess.Read, 4096, false), enc, true, 4096);
}
_processHandle = safeProcessHandle;
safeThreadHandle.Dispose();
return true;
}
}
}
C0000142 = STATUS_DLL_INIT_FAILED, что означает, что dbinit не может загрузить/инициализировать DLL. Попробуйте запустить свой сценарий либо с помощью Process Explorer, либо с помощью Windbg, чтобы выяснить, что не удается.
запустите свой процесс (*dbinit *) в приостановленном состоянии. ищите его токен. проверьте, что отличается в обоих случаях. прикрепите отладчик. посмотрите, какой вызов не удался. часто это NtGdiInit
0xC0000142 STATUS_DLL_INIT_FAILED :{Ошибка инициализации DLL} Ошибка инициализации библиотеки динамической компоновки %hs. Процесс завершается аварийно. Я предлагаю вам попробовать добавить LoadUserProfile. Когда пользователь входит в систему в интерактивном режиме, система автоматически загружает профиль пользователя. Если служба или приложение выдает себя за пользователя, система не загружает профиль пользователя. Поэтому служба или приложение должны загрузить профиль пользователя с помощью LoadUserProfile.
@Jeaninez-MSFT Спасибо за совет, но мы это уже пробовали. К сожалению, это ничего не меняет :(
Я предлагаю вам обратиться к документу: Что случилось с ошибкой «Не удалось правильно инициализировать приложение (0xc0000142)»?





Мы до сих пор точно не знаем, почему Windows в нашем сценарии всегда использует winsta0\default, но мы думаем, что это как-то связано с участием rundll32.
Причина, по которой установка lpDesktop на пользовательские WindowStation и Desktop никогда не работала, заключается в том, что мы перепутали Ansi и Unicode. Как только мы явно установили CharSet на Unicode, все заработало. Вот код:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal class STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
...
}
[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
[SuppressUnmanagedCodeSecurity()]
internal extern static bool CreateProcessAsUser(SafeHandle hToken, string lpApplicationName, string lpCommandLine,
SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles,
int dwCreationFlags, HandleRef lpEnvironment, string lpCurrentDirectory, STARTUPINFO lpStartupInfo,
PROCESS_INFORMATION lpProcessInformation);
Теперь наше решение работает следующим образом:
WindowStation и Desktop, чтобы не столкнуться с проблемами с разрешениями.winsta0\default, что совершенно нормально, потому что пользователь интерактивного процесса, похоже, имеет соответствующие разрешения.
Если вы не искали ошибку, ее символическая константа —
STATUS_DLL_INIT_FAILED.