CreateProcessAsUser doesn't draw the GUI - c#

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).

Related

Creating a child Process C# win32 API

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?

Start browser from Windows service

I am a little new to the Microsoft world. I have read this answer and many other links and I'm aware that after Vista, it is not possible for windows service to interact with desktop.
However, I have kind of an emergency where I have to find a workaround quick. I need my windows service to somehow open a browser with a URL (any kind of an ugly hack will do for now).
The answer states :
you need to write a separate agent application, which will be automatically started on user login, and with which your service will communicate. Then the agent can start the browser or do whatever else you need.
Can someone please explain to me how I could do this in a simple way? How would that service talk to this 'agent'? What is this 'agent' exactly?
Any links or suggestions would be highly appreciated.
EDIT: Currently. my service tries to run the following code: System.Diagnostics.Process.Start("www.google.com");
which, I found out later, does not work
Yes, from a service you can run code on the desktop... The matter is on which desktop? because there may be various users logged at the same time (for example in a Windows Server there may be various users logged remotely).
If use Process.Start from a service as in your example:
System.Diagnostics.Process.Start("www.google.com");
The program will not appear in the desktop of any user, because the service is not associated with any of the desktops of the users.
The solution is to detect which user is logged locally and then execute the program as that user (and under the privileges of that user). For that I want to link you to another of my answers where I demonstrate how to invoke a program from a service. In that answer I present the code needed for this technique.
Note: That answer was about an screen capture, still the technique I describe is to call a program in the session of an user, and that method works from a service. So it applies to your problem.
You can use this method to execute the desired browser directly. As an alternative you can use it to invoke your service executable, where you can use System.Environment.UserInteractive to detect if the executable is not running as a service or not. If you pass the url as execution argument to your service executable then you can open it with the default browser using Process.Start just like your example above.
Note: The code was tested on Windows 7.
There is no "quick way" to do this, I'm afraid. (And since the service can't interact with the user's desktop, even if it could launch the browser the user couldn't see it anyway.)
The "agent application" you've mentioned is a totally separate application which runs under the user's desktop on login. It would have to use some method of inter-process communication (named pipes, TCP/IP, etc.) to communicate with the service.
The reason that services don't interact with the desktop is for reasons of security. There's no "quick workaround" available, and there shouldn't be one. There's also no "simple way" to circumvent that change (and again, there shouldn't be - if you can bypass security in a simple way, it's not much security).
use shellexecute using pinvoke
http://www.pinvoke.net/default.aspx/shell32.shellexecute
C# Signature:
public enum ShowCommands : int
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);
// Asks default mail client to send an email to the specified address.
ShellExecute( IntPtr.Zero, "open", "mailto:support#microsoft.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );
// Asks default browser to visit the specified site.
ShellExecute( IntPtr.Zero, "open", "http://channel9.msdn.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );
// Opens default HTML editing app to allow for edit of specified file
ShellExecute( IntPtr.Zero, "edit", #"c:\file.html", "", "", ShowCommands.SW_SHOWNOACTIVATE );
//Modified by Aljaz: Replaced the last zero in these calls with 4 otherwise it wouldn't show anything
// 0 stands for SW_HIDE contant, which means execute but don't show the window which is probably not
// what we want.
If you need something urgent - I'd try to put message into MSMQ from the service, and write some simple client that is waiting for queue messages, and proceed them.
Update:
Actually, if you need something very quick - you can even write to some file, and write simple desktop (winforms?) application to read this file in pool (each 5 sec?), and open browser when required.

create interactive elevated process from windows service and show to logged-on user

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.

Black backgrounds on interactive windows launched from a service

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.

CreateProcessAsUser() not showing CreateWindow of running Process [C#]

My system has 2 accounts (USER and ADMIN) and a service (Service.exe) running under LocalSystem.
The user logs into the USER account, the LocalService can then launch a process (CreateProcessAsUser(...)) as the ADMIN user.
The process the Service runs (Tool.exe) is a legacy C++ application that performs a job and also displays information to the user by using CreateWindow(...), but when launching it by the Service the Window does not show...
When the process is created by the Service I first load the Profile & Environment of the ADMIN user so that the correct context is used ...
(was still hoping the Window would display to USER)
Now, initialy I thought this would cause a problem as ADMIN is running the process so why would the CreateWindow(...) output in the USER desktop, so I tried with a simple command-line test.exe app and when launched as ADMIN the Command Prompt window appeared - so why does that work fine where-as the CreateWindow(...) doesn't display correctly?
Any clues on how I can get the STATUS (using CreateWindow in Tool.exe) running under ADMIN to show in the USER logged-on session?
Can I use the ENVIRONMENT somehow, I tried the following thinking it might work but didn't:
startInfo.lpDesktop = #"WinSta0\Default";
startInfo.dwFlags = STARTF_USESHOWWINDOW;
startInfo.wShowWindow = SW_SHOW;
The Window is created as follows (in Tool.exe):
HWND hwnd = CreateWindow
(
"Tool",
"Tool WINDOW",
WS_POPUP | WS_VISIBLE,
0,0,uWidth,uHeight,
NULL,
NULL,
hInstance,
Text
);
Is the lpDesktop wrong (not exactly sure how this works)?
I know how crazy this sounds - I just would rather not have to launch another ToolDisplay.exe as USER which communicates with Tool.exe as ADMIN as a display when the Tool.exe used to handle everything on its own - so checking to see if there isn't some nice way to handle this...
Any help would be much appreciated...
Thanks,
[Simplified Question:]
My system has 2 accounts (USER and ADMIN), the user is always logged on as USER but at some specific times a process (Tool.exe) is launched under the ADMIN account (by a LocalSystem Service using CreateProcessAsUser(...)), almost everything works fine except for the fact that the process (Tool.exe) is supposed to display status to the user using CreateWindow(...).
When Tool.exe is running (as ADMIN) and the user is logged-on as USER the window is not shown (obviously)...
Is there a way to show the window of Tool.exe running under ADMIN to the user logged-on as USER?
Any help would be much appreciated...
Thanks,
Just in case you want a window / GUI spawned by a service to be visible,
Go to-> run prompt -> services.msc -> go to your service's property, check "Allow service to interact with desktop".
Then restart your service.
Note that in Vista, the prompt will NOT be visible even after above. This is due to sessions issue. However there is a service which helps support prompts from services (I think it is Interactive Services Detection service).

Categories

Resources