I am writing a program that can be loaded by another service (under our control), or by the logged-on user. The program needs to know if the window station is interactive in order to display dialogs. I know GetProcessWindowStation function, but this one returns a handle. Is there a way to find out?
The interactive window station is always winsta0. So you need to get the window station name to determine it. Here is some pseudo code:
wchar_t buffer[256] = {0};
DWORD length = 0;
GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, buffer, 256, &length);
if (!lstrcmp(buffer, "winsta0")) {
// Interactive!
}
From http://msdn.microsoft.com/en-us/library/ms687096(VS.85).aspx:
The interactive window station, Winsta0, is the only window station that can display a user interface or receive user input
I suggest having the service pass command line parameters that let the program know it was launched by the service and not a user.
Please note that this only works on Windows XP (and then only sometimes) - on Windows Vista and beyond, services run in a separate session from interactive users, so you'll never be able to attach to the console on those OSs.
In addition on Windows XP, your application won't work if there are multiple users on the machine (Fast User Switching) because only the first user is logged onto session 0 (where services run).
You'd be far better off splitting your service into two pieces - the service which does the work and a small piece of code which runs as a task (using the Win32 task scheduler APIs) that runs the UI.
Related
Introduction:
I'm an administrator currently working on the dedicated servers of the game "Sniper Elite V2". SEV2's dedicated servers are running under windows in a console application. We are able to run this application under Linux too, with wineHQ. Everything is good, excepted that the console is launched in a new window, so no way to have the console inputs/outputs (link: http://img802.imageshack.us/img802/650/sev22.png). Moreover, we can read (with an HEX editor) that "This program cannot be run in DOS mode". My objective is to bring back DOS compatibility.
My idea is to create another console application written in C++ (with CodeBlocks).
This "Hook" should retrieve the content (text) of the dedicated server "window" and will display it, with the possibility of course to input commands to the dedicated server console. Like that, we will be able to work with inputs/outputs both on Windows and Linux.
I took the time to draw a little schema : http://img195.imageshack.us/img195/3017/29585679.png
My question is: How to do this ? Which functions should I use ?
At this point, I'm stuck. I've tried the following approaches :
FUNC "AttachConsole" has failed.
The following pseudo-code has failed too:
mainWindow = FindWindow( TEXT("ConsoleWindowClass"), NULL) );
console = FindWindowEx( mainWindow, NULL, TEXT("ConsoleWindowClass"), NULL );
SendMessage(
console,
WM_GETTEXT,
sizeof(buffer) / sizeof(TCHAR),
(LPARAM)buffer);
I'm using a tool called "WinSpy++" to help me, but the tool is not able itself to retrieve/input data to the Sniper Elite V2 Dedicated Server.
Any ideas/examples/reflections is welcome :-)
"This program cannot be run in DOS mode"
You make a mistake. Don't mix terms "DOS mode" and "Console". Windows console is NOT a DOS mode. Console is native terminal, where you can run any windows application 32/64 bit, written for console subsystem. "DOS mode" is pre Windows operating system, e.g. DOS, TR-DOS, FreeDOS and so on.
As for question, one console app can not work with two consoles simultaneously.
Upd
Seems that server was builded for GUI subsystem and creates console with AllocConsole.
Run server with 'CreateProcess'
Wait a little while new console wIndow appears.
Call 'FreeConsole' and 'AttachConsole(ServerPID)'
Old
So, if you want to read console_1 from process belonging to console_2, you must
start third process, for example with DETACHED_PROCESS
call FreeConsole (required) and AttachConsole(RootPidFromConsole_1)
communicate between third process and console_2's process (pipes, shared memory, ...)
this works in windows.
I'm writing a program to immediately track and kill when a user runs command prompt (and regedit if that's possible). This is to stop users from running commands I would rather they not have.
I've already written code that sees when a process is launched and checks its name using QueryFullProcessImageName. The issue is that if someone were to rename command prompt then I could no longer detect it via process name. The way I detect command prompt is currently "\cmd.exe" but clearly this is not very secure.
Posted below is what I have for the code. I removed all error checking for brevity. Please let me know if you need more clarity. Thanks!
TCHAR exeName[MAX_PATH];
DWORD exeNameSize = MAX_PATH;
//the pid comes into the function as a parameter
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
if (handle)
{
if (QueryFullProcessImageName(handle, 0, exeName, &exeNameSize))
{
tstring name = exeName;
/*
badProcs would contain the path identifiers such as
"\\cmd.exe" or "\\regedit.exe". This detection is
what I want to make better.
*/
for(int i=0; i < badProcs.size(); i++)
{
if(tstring::npos != name.find(badProcs.at(i)))
{
if(TerminateProcess(handle,0))
OutputDebugString(_T("Process should be dead\n\n"));
}
}
}
CloseHandle(handle);
}
Some additional information: The reason I'm writing this is to control what goes on in other desktops. I want to make it so that when a user launches a different desktop (via whatever proprietary program) I can control whether or not they have access to items which present the biggest security holes to the system. Given that I only want to control actions does on the other desktop, I do not want to change settings for fear of corrupting data outside of the target desktop. Is corruption not something to worry about?
I'm only interested in controlling a proprietary desktop, not mucking with what users do in their own space. Essentially the separate desktop is for corporate work, and I want to be able to limit what people can do with company information, etc.
Don't. Windows has internal means for that. Read up on the policy editor, and/or file access control.
If you're admin and the "user" is not, policy (or simple ACL) will do the job; if the "user" is also an admin, they'll be able to defeat your program fairly easily.
The best way to block the command prompt and registry editor is through the windows registry. These work even if you copy the executables to a different location.
Both the Registry Editor and Command Prompt cannot be run if the registry keys are set:
HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\System\DisableRegistryTools
or for the whole machine
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\System\DisableRegistryTools
Setting this to 1 will disable regedit, and setting to 0 will enable it.
HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\System\DisableCMD
(the local machine varient works here as well).
Setting this to 1 will disable the command prompt and batch files, setting this to 2 will only disable the command line, and setting to 0 will enable it.
I want to make some debug console for my application. It should output some data and take input commands. How can I do this? The best way is updating console like: drawing information and prompt for input after the data.
I'm developing under Linux. For example, gdb could take input from console.
If you're familiar with socket programming (or actually, any other kind of IPC mechanism), you might want to enable some listener within your application, and develop an external application that will do all the "console" stuff for you, while communicating with the main application.
Let's suppose you have an application that has a single button and a single text label, and every time you press that button - the text label goes up by 1, from 1 to 2 to 3 etc.
You can build a socket listener into that application. When the socket listener accepts a new incoming connection, you'd start a connection thread that can:
Receive a "shutdown" command
Receive a "reset counter" command
Send an update regarding the current count on every click
etc.
Then you build another, external application, which connects to the main application, and sends messages to it, based on console input it gets from the user. It would also listen to incoming updates and show them to the user.
Using an external application for debug-controlling your main application is extremely helpful, with the following reasons being some of the advantages:
No matter how the debug application is buggy, it cannot hurt the release version of your main application.
All the code that deals with the console management, which is redundant to your main application, can be kept outside of the main app.
Making two projects out of it can make it easier to collaborate your work with someone else, as long as you are both aware of the protocol between the two sides.
Implementing what I suggested means you can debug your application remotely, in case you don't have access to the main application (for example, if it's on a customer site).
I'm using QTP 10 together with VMWare to test a Siebel Application.
I'm executing the following code to click on a Save button.
Browser("Siebel").Dialog("Filedownload").WinButton("Save").Click
The code works perfectly fine when I'm connected to the VM via Remote Desktop.
On the other side, when I'm starting the QTP test through the scheduler, without having a Remote Desktop connection, the code above fails without any error message. The WinButton is simply not clicked.
Any idea?
Just to add from my experience.
In some companies I worked for I couldn't change screensaver or standby settings due to security policy. A PC was bringing up screensaver during long synchronization periods (like generating really big report), and execution was broken.
To avoid that I created simple "Anti Sleep" function that slightly "moves" mouse every 5 minutes.
http://automation-beyond.com/2009/08/18/anti-sleep-function/
Private Const SleepTime_Max = 300 ‘ 5 minutes
Public Function AntiSleep()
Dim iter
Dim objTimer
Dim objDeviceReplay
Dim intTimeElapsed
Set objTimer = MercuryTimers(“AntiSleep”)
intTimeElapsed = CInt(objTimer.ElapsedTime/1000)
If intTimeElapsed = 0 Then
MercuryTimers(“AntiSleep”).Start
Exit Function
End If
If intTimeElapsed < SleepTime_Max Then
Exit Function
End If
Set objDeviceReplay = CreateObject(“Mercury.DeviceReplay”)
For iter = 100 To 110
objDeviceReplay.MouseMove iter,300
Next
MercuryTimers(“AntiSleep”).Start
Set objDeviceReplay = Nothing
End Function
Example of using it in a custom synchronization function:
http://automation-beyond.com/2009/08/20/gui-object-synchronization-custom-function/
Thank you,
Albert Gareev
QTP can't interact with a locked desktop, that's why it'll only work for you when logged in interactively either locally or over RDP. It's a well known limitation of QTP, most automation engineers go through this pain at some point. :)
To be more specific, it can't interact with Win32 objects (can't think of a better way of putting it), so it'll interact with basic browser controls on a locked desktop no problem, but browser popups and Windows applications can't be interacted with in those circumstances.
I strongly recommend (if your system policy allows) that you install something like UltraVNC or another VNC variant to interact with your remote machines. That way you can leave the remote machine's desktop logged on and active at all times. Since it's a VM that shouldn't cause you any major security problems either. Make sure you turn off any screen savers and don't auto-lock the desktop too. QTP should work just fine for you if you do that.
Environment - VS2008, Vista SP1.
I have written a process management service which can launch applications either in session 0 or the interactive console (usually 1). Please note this is NOT the normal mode of operation, it's for in-house debug purposes only. In the field, these processes will be safely hidden away in session 0. Security concerns do not apply.
Clearly people aren't reading this: security concerns do not apply. We have dozens of existing server apps (NOT services) written like this. We're not about to completely revamp these applications, we just need to be able to get at their inbuilt debug dialogs when running release versions in-house. I already know all about the canonical solution and pipes etc. If it was acceptable to add remote interfaces into all these apps, that's what we'd be doing.
I use the following code to do this:
ZeroMemory (&sui, sizeof(STARTUPINFO));
sui.cb = sizeof (STARTUPINFO);
sui.wShowWindow = pTask->GetWinStartState() ;
sui.dwFlags = STARTF_USESHOWWINDOW ;
ZeroMemory (&pi,sizeof(pi));
if (bInteractive)
{
HANDLE hToken = NULL;
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
WTSQueryUserToken (dwSessionId, &hToken);
sui.lpDesktop = TEXT("winsta0\\default");
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
HMODULE hModu = LoadLibrary(TEXT("Userenv.dll"));
if (hModu )
{
if (CreateEnvironmentBlock (&pEnv, hToken, FALSE))
dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;
else
pEnv = NULL;
}
bCreatedOk = CreateProcessAsUser (hToken,
NULL,
(LPTSTR)(pTask->GetExeName()),
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&sui,
&pi);
}
else
{
bCreatedOk = CreateProcess (NULL, ... blah...);
}
This all works fine and I can run and monitor native processes both in the Vista service session and the console. Great. Cakes and ale for everyone.
So here's the problem. If I try to run a winforms (C#) app interactively like this, it appears to run, shows up in Process Explorer as running in session 1, but on the desktop... nada. No window appears at all. The process runs up and shuts down all fine, but no window ever appears. The exact same winform exe run from explorer also shows up in session 1, but this time appears on the desktop just fine.
Any ideas ?
Despite the evident hysteria there is nothing wrong with launching an application from a service into an interactive session provided it is done with the same privileges as the interactive user or lower. Since you are launching as the interactive user there can be no privilege escalation.
What you are doing does work. I suspect that the issue has something to do with your STARTUPINFO struct. You appear to be creating your sui on the stack but you don't show what you are doing with it. Are you initializing it to all 0s, if not you may be getting some garbage from the stack that is causing the window not to show or to show at some co-ordinates off the screen.
It looks as if you're trying to break Session Isolation. Like Mystere Man said, I don't think it's the right thing to do.
http://support.microsoft.com/?scid=kb%3Ben-us%3B165194&x=5&y=3 might be useful if you really want to do this. In the code sample over there, they seem to initialize more thing than you do.
EDIT If it's for debugging purposes, i-e VS 2008 step-in, just add a wait loop at the beginning, and launch VS with administrator rights. You can attach to your service process without encountering system isolation.
In a word, "don't".
Services typically run with reduced privileges and NOT as the current user. As such, in Vista+ they're not allowed to interact with the users desktop. On top of all that, services get a null Window Station.
You used to be able to check a box that said something like "Allow to interact with the desktop" but not anymore. It's bad practice.
Your best bet is to create a helper app that runs in the users context and communicates with the service via a named pipe, LRPC or a socket, then have your helper app launch the program for the user. This is the way most anti-virus now works.
Also, read this whitepaper from Microsoft on the subject. Services can't run in anything other than session 0.
NOTE: a little research seems to indicate that you need to duplicate the token using something like this:
DuplicateTokenEx(hTokenNew,MAXIMUM_ALLOWED,NULL,
SecurityIdentification,TokenPrimary,&hTokenDup);