I am implementing a Windows service in C#. This service calls a separate application that launches interactive windows. I have been able to work through the problems imposed by Session 0 Isolation by using the following series of steps:
LogonUser() to get a logon token for the user who will execute the separate application
SetTokenInformation() to transfer the user's logon token into session 1
CreateProcessAsUser() to launch the application in the user's session.
This works; When the service launches the application, I see the application's windows appear in my console session. However, the application's windows have black backgrounds and all of the controls are invisible. If I click in an area where I know there is a button, the window responds, so it is clearly able to receive user input.
Here is (a simplified and stripped down version of) the code I'm using:
IntPtr logonToken;
LogonUser(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out logonToken);
SetTokenInformation(logonToken, TOKEN_INFORMATION_CLASS.TokenSessionId, sessionIdValuePtr, sessionIdSize);
STARTUPINFO startupinfo = new STARTUPINFO();
startupinfo.cb = Marshal.SizeOf(startupinfo);
startupinfo.lpDesktop = #"winsta0\default";
PROCESS_INFORMATION processinfo;
SECURITY_ATTRIBUTES processAttributes = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
ImpersonateLoggedOnUser(logonToken);
CreateProcessAsUser(
logonToken,
null,
cmdLine,
ref processAttributes,
ref threadAttributes,
false,
0,
IntPtr.Zero,
workingDirectory,
ref startupinfo,
out processinfo)
RevertToSelf();
I have tried adding code to load the user's profile before calling CreateProcessAsUser, but this did not help.
What could be causing the black backgrounds on my windows, and how should I go about fixing this problem? Any help would be most appreciated.
UPDATE: This appears to be very similar to the problem in this question: CreateProcessAsUser doesn't draw the GUI. He is using XP SP3, and I am having this problem in Windows 7 and Server 2008, meaning that I have the additional problem of dealing with Session 0 Isolation, but the symptoms in the two cases seem similar.
Related
I have a .NET c# service running as LocalSystem on Windows10 64bit. It's job is to alter the "ProxyEnable" and "ProxyServer" values located under
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
in the registry. It does so, for all "human" Users of the System (that is, the key starting with "S-1-5-21.."). It works fine and all browsers the user starts up use this configuration (proxy or no proxy).
However, browsers that are still running won't notice changes (turning the proxy setting on or off) until a long time - OR until i manually just open and close the Windows "Internet Options > Proxy Settings" (without doing changes or saving them) or restart the browser(s). The latter two make the System re-read and propagate the settings to all (other) open browsers.
Now DllImport'ing wininet.dll and using the InternetSetOption does work
[DllImport("wininet.dll", SetLastError = true)]
private static extern bool InternetSetOption(
IntPtr hInternet,
int dwOption,
IntPtr lpBuffer,
int lpdwBufferLength);
private const int INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95;
private const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
private const int INTERNET_OPTION_REFRESH = 37;
...
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
but only when i perform this piece of code as the logged in user. When the service performs it, it doesn't do anything. I guess it's because as LocalSystem the service is applying it to the wrong scope.
I have tinkered around a lot. Trying to start another process as the user is no option as this would require credentials. Also the service using LocalSystem is set.
I am just convinced that as LocalSystem, that is close to Administrator in terms of privileges, there must be a way to tell all applications and browsers, that the Proxy Settings have changed. Its just the propagation like opening and closing the Proxy Settings-GUI invokes, that i need to trigger somehow. But i don't know how. Do you?
This is how environment variables & Reg keys function in windows - as I understand it all new processes get a snapshot of all variables when they start, but thats it. They will only update when you restart the process.
See C# : How to change windows registry and take effect immediately
I am using the CreateProcess MSDN call() to manually launch an application and here's my code
void LaunchProg()
{
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "testProg";
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
var lpCurrentDirectory = Path.GetDirectoryName(executablePath);
CreateProcess("c:\Windows\System32\notepad.exe", null,
IntPtr.Zero, IntPtr.Zero, true,
NORMAL_PRIORITY_CLASS, IntPtr.Zero, lpCurrentDirectory, ref si, ref pi);
}
This code works perfectly fine and launches the program in the specified desktop "testProg" but the issue is that when notepad.exe creates a child process which has a new window. This window is displayed in the Default desktop and not within the "testprog" desktop view (Active desktop view)
Not sure as to which parameter is not set correctly for all the child windows to be spawned within the same active desktop. I looked at the documentation and it is not clearer to me.
Update on an observation: The child process is not inherited from the application launched but its a child process of a system process running in the default desktop.
Any pointers? Thanks in advance!
From the MSDN documentation about threads and desktops, if a desktop is specified in the STARTUPINFO, that is used, otherwise the default desktop for the windows station to which the process is connected will be used. In your case, it seems likely that notepad is not marking the handle to the desktop inheritable, or it is doing a CreateProcess that doesn't inherit handles. Maybe you could attach a debugger and see what parameters the CreateProcess is being called with?
I'm trying to launch Internet Explorer as another user from our WPF app, so that when our users visit the (internal) website, they silently authenticate via Integrated Windows Authentication.
We do not want to launch iexplore.exe as the other user because of weird deployment/environment issues that occur when you launch a process for the first time on a computer and it attempts to set up IE7/8 for the first time. Though, if you have a solution for how to mute every IE installer on every machine, I'd love to hear it.
Back to my intended question. I'm able to get the exact IE impersonating behavior I want* from the command prompt using runas (thanks to https://serverfault.com/questions/70376/runas-domain-account-still-asks-for-password):
c:\> runas /noprofile /netonly /user:MyDomain\MyUser iexplore.exe
*note: I can't use runas for our WPF app for many reasons, but the end result is what I want.
Anyway, I'd like the C# equivalent code that does a runas /noprofile /netonly iexplore.exe.
I am halfway there with P/Invoke on CreateProcessWithLogonW. This is what I have:
uint LOGON_NETCREDENTIALS_ONLY = 2;
var lpStartupInfo = new CreateProcessWithLogonW_PInvoke.STARTUPINFO();
CreateProcessWithLogonW_PInvoke.PROCESS_INFORMATION processInformation;
CreateProcessWithLogonW_PInvoke.CreateProcessWithLogonW(
userName,
domain,
pw,
LOGON_NETCREDENTIALS_ONLY,
null,
commandLine,
0,
null,
null,
ref lpStartupInfo,
out processInformation);
This successfully launches Internet Explorer, but does not seem to impersonate the user at all. I am able to impersonate the user via the runas command, so I'm 98% sure the failure to authenticate isn't an IE/zone/password/IIS setting, it's just something I'm not doing right in my call to CreateProcessWithLogonW().
One thing I've noticed is that the runas /netonly command only works if I add the /noprofile switch, which is something that is stumping me. I have no idea how to set the equivalent of this switch via P/Invoke in C#.
Help is appreciated with either solution (solving the "IE runs a wizard when I launch it the first time", or finding the weirdo P/Invoke setting I'm missing).
Okay, I was very very close. The magic fix is adding -noframemerging to the iexplore.exe call, which...honestly I'm not sure what it does, it uses the phrase "process frame" which is awesome and perhaps means something to you.
In any case, this appears to be resolved.
var arguments = "-noframemerging " + url;
var pathToIExploreExe = GetFullPathToIExploreExe();
var commandLine = string.Format("\"{0}\" {1}", pathToIExploreExe, arguments);
uint LOGON_NETCREDENTIALS_ONLY = 2;
var lpStartupInfo = new CreateProcessWithLogonW_PInvoke.STARTUPINFO();
CreateProcessWithLogonW_PInvoke.PROCESS_INFORMATION processInformation;
CreateProcessWithLogonW_PInvoke.CreateProcessWithLogonW(
userName,
domain,
pw,
LOGON_NETCREDENTIALS_ONLY,
null,
commandLine,
0,
null,
null,
ref lpStartupInfo,
out processInformation);
CreateProcessWithLogonW requires that the specified user account must be allowed to log on interactively. Could it be a problem? Try CreateProcessAsUser function if that works.
I have a service that spawns a WPF application process when a user logs on.
But for some reason the WPF application gets killed about 10 minutes after it has been created? The termination is immediate with no traces found in the Event Log nor are any normal close/exit events called in the WPF application.
In fact, when the termination occurs, Windows 7 seems to hang for a second, the mouse becoming unresponsive and then acting out the mouse gestures after a short delay (when it normalizes, but now lacking the created process).
The When
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
CanHandleSessionChangeEvent = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLogon
&& changeDescription.SessionId > 0)
{
ApplicationLoader.PROCESS_INFORMATION procInfo;
ApplicationLoader.StartProcessAndBypassUAC(#"myapp.exe", out procInfo);
}
base.OnSessionChange(changeDescription);
}
}
Process Creation As Per Pero Matic Code
// ...
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
applicationName, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out procInfo // receives information about new process
);
the termination does not seem to happen if i target notepad.exe, however?
tested it with a vanilla\empty WPF application (.NET 4), and that crashed as well
Process Creation with Administrative Privileges and No Prompt
It seems that the issue is trying to duplicate the administrative SYSTEM token from winlogon.exe (but which is running in session 1+), because if you duplicate the specific user token instead (e.g. from explorer.exe) then the crashes are no more!
this is confirmed with the same vanilla/empty WPF application, and with running Marcel Roma code here - note that he uses explorer.exe instead of winlogon.exe
although using explorer.exe gets rid of the termination I lose the administrative privileges with that, which does not work for me
any ideas how to get it to work with the winlogon process token?
or is it possible to adjust the exlorer.exe token to make the duplicate elevated? im guessing somehow using TokenElevation and SetTokenInformation or AdjustTokenPrivileges
or could it be that Windows 7 has been patched to disallow such process impersonation?
alternatively, is there any way to get the specific user token with administrative privileges (rather than the owner being SYSTEM), but again, without password knowledge/prompts (excluding CreateProcessWithLogonW)
is this maybe to do with garbage collection somehow?
Well I'm just suggesting you a work around:
Why you don't put your core functionalities in a windows service, and then use the wpf app as a frontend ? So that if the user kill it, it doesn't stop the service. Then the service can regularly check that the wpf front end is started, and if needed restart it.
I think it'll be a more "trusted" design that the one you're trying to do, which could let the antivirus think you're a bad software and block you.
And to protect the windows service there is another question here: Protecting a Windows Service from untrusted users
I don't think you can (and definitly should not be able) to do this. Your best bet is to create an application that doesn't need elevated privileges and then use IPC to talk back to your service which then performs administrative tasks on the users behalf.
I have a windows service running under "SYSTEM" account that checks if a specific application is running for each logged in user. If the application is not running, the service starts it (under corresponding user name).
I'm trying to accomplish my goal using CreateProcessAsUser(). The service does start the application under corresponding user name, but the GUI is not drawn. (Yes, I'm making sure that "Allow service to interact with desktop" check box is enabled).
System: XP SP3, language: C#
Here is some code that might be of interest:
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
bResult = Win32.CreateProcessAsUser(hToken, null, strCommand, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startInfo, out processInfo);
As far as I understand, setting startInfo.lpDesktop = "winsta0\default"; should have used the desktop of corresponding user.
Even contrary to what is stated here: http://support.microsoft.com/kb/165194, I tried setting lpDesktop to null, or not setting it at all, both giving the same result: process was started in the name of expected user and I could see a part of window's title bar. The "invisible" window intercepts mouse click events, handles them as expected. It just doesn't draw itself.
Is anyone familiar with such a problem and knows what am I doing wrong?
MSDN has a sample of how to create a process as another user setting explicit permissions on the window station and desktop objects:
CreateProcessAsUser() windowstations and desktops
You can port the code to C# using P/Invoke or you could use a C++/CLI assembly.
However, be aware that your scenario is not supported and likely to break with Vista's (and Windows 7's) Session-0 isolation (download the whitepaper on the right).