I have 3 applications running:
Application A: Local server running as a background service under Local System with permission to interact with the desktop ( Atleast that is what Advanced Installer says )
Application B: A desktop capture software
Application C: A tray icon running as the logged on user.
What i am trying to achieve is to be able to capture the desktop UAC prompts and logon screen, so here is how i am trying to achieve it:
1. Application C calls WTSGetActiveConsoleSessionId() ( which return 2), Grab the desktop pointer:
OpenInputDesktop(0, false, (uint)(ACCESS_MASK.DESKTOP_CREATEMENU | ACCESS_MASK.DESKTOP_CREATEWINDOW | ACCESS_MASK.DESKTOP_ENUMERATE | ACCESS_MASK.DESKTOP_HOOKCONTROL | ACCESS_MASK.DESKTOP_WRITEOBJECTS | ACCESS_MASK.DESKTOP_READOBJECTS | ACCESS_MASK.DESKTOP_SWITCHDESKTOP))
and sends both to the application A ( local server )
2. The server runs the following code:
static void CreateApplicationBProcess()
{
IntPtr hToken = IntPtr.Zero;
IntPtr P = IntPtr.Zero;
/*if (!OpenProcessToken(OpenProcess(Process.GetProcessesByName("winlogon").First(), ProcessAccessFlags.All), 0x2000000, out hToken))
{
throw new Exception("OpenProcessToken error #" + Marshal.GetLastWin32Error());
}*/
if (!WTSQueryUserToken(WTSGetActiveConsoleSessionId(), out hToken))
{
throw new Exception("WTSQueryUserToken error #" + Marshal.GetLastWin32Error());
}
IntPtr duplicatedTokenHandle = IntPtr.Zero;
if(!DuplicateTokenEx(hToken, 0, IntPtr.Zero, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary, ref duplicatedTokenHandle))
{
throw new Exception("DuplicateTokenEx error #" + Marshal.GetLastWin32Error());
}
uint sessionId = SessionId;
if(!SetTokenInformation(duplicatedTokenHandle, TOKEN_INFORMATION_CLASS.TokenSessionId, ref sessionId, (uint)IntPtr.Size))
{
throw new Exception("SetTokenInformation error #" + Marshal.GetLastWin32Error());
}
if (CreateEnvironmentBlock(out P, duplicatedTokenHandle, false))
{
Win32.PROCESS_INFORMATION processInfo = new Win32.PROCESS_INFORMATION();
Win32.STARTUPINFO startInfo = new Win32.STARTUPINFO();
bool bResult = false;
uint uiResultWait = Win32.WAIT_FAILED;
try
{
// Create process
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
startInfo.dwFlags = 0x00000001;
startInfo.wShowWindow = (short)SW.SW_HIDE;
SetCurrentDirectory(GetDirectory());
Environment.CurrentDirectory = GetDirectory();
bResult = Win32.CreateProcessAsUser(duplicatedTokenHandle, Path.Combine(GetDirectory(), "ApplicationB.exe"), string.Format("{0}", (uint)DesktopPtr), IntPtr.Zero, IntPtr.Zero, false,
CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, P, GetDirectory(), ref startInfo, out processInfo);
if (!bResult)
{
throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error());
}
else
{
//IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)processInfo.dwThreadId);
ResumeThread(processInfo.hThread);
}
// Wait for process to end
uiResultWait = Win32.WaitForSingleObject(processInfo.hProcess, Win32.INFINITE);
if (uiResultWait == Win32.WAIT_FAILED)
{
throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error());
}
GetExitCodeProcess(processInfo.hProcess, out uint ExitCode);
Console.WriteLine("Exit code: " + ExitCode);
}
finally
{
// Close all handles
Win32.CloseHandle(hToken);
Win32.CloseHandle(processInfo.hProcess);
Win32.CloseHandle(processInfo.hThread);
DestroyEnvironmentBlock(P);
}
}
else
{
throw new Exception("CreateEnvironmentBlock error #" + Marshal.GetLastWin32Error());
}
}
private static string GetDirectory()
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
3. Now the CreateProcessAsUser returns a success but the process is exiting with the error code 3228369022 (C06D007E).
I monitored with ProcMon and i dont have any error with Load Image, however not all of my dlls are loaded.
source of error was
line, because CreateProcessAsUserW used (not visible from question code)
so must be
also code contains several another errors:
we not need open winlogon.exe (or another hardcoded exe name token) instead we can open self process token and use it here, if we want system token use.
not need
DuplicateTokenEx+setTokenSessionIdfor token returned byWTSQueryUserToken- because theWTSGetActiveConsoleSessionId()already was in this token (we get it by this session id)ResumeThreadsenseless call here - because thread created suspended when and only whenCREATE_SUSPENDEDflag used inCreateProcess