C#: Programmatically Detect Windows Server Has Booted - c#

I'm working on an automation process in C# that is going to remotely reboot a Windows (2008/2012/2016) server and I need to wait until that server is back online before proceeding.
I know 'back online' can be ambiguous, so for my requirements, I need the server to be back at the Ctrl-Alt-Del screen.
The reason for this is to have the server in a consistent state before proceeding. In my experience, there are several factors that could prevent the server from reaching this screen, such as installing windows updates that gets stuck in a reboot cycle or getting stuck at 'Waiting for Local Session Manager' etc.
I've spent a few days looking in to this to no avail:
The server obviously starts responding to ping requests before it is available
System Boot Time occurs before the Server reaches the desired state
Any events indicating the system has booted are logged before the desired state
I can't simply poll for an essential service - when Windows is applying computer updates prior to logon these services can be already started. Additionally, sometimes a server will reboot itself whilst installing updates at this stage which could result in false positives.
Polling CPU activity could also produce false positives or introduce delays
Is there anyway to detect a Windows server has finished booting and is available for an interactive logon?

It sounds like you've covered most of the possible ways I know of. Which makes me revert to brute force ideas. I am curious what you're doing where you can't install a windows service on the box (or is that just not very viable because of the number)
First would just be trying to remote login or whatever, and having some way to test if it fails or not, wait 1 minute, try again. But seems like that might cause side-issues for you somehow?
My idea of a brute force method that wouldn't affect state:
Ping every 1-5seconds
Once it starts responding
wait 5 or 10 or even 15 minutes, whilst still pinging it
If pings fail reset that timer (windows updates restart case)
Then be pretty confident you're at the right state.
With potentially thousands of servers, I can't imagine 15 minutes each would be a big deal, especially if it is consistent enough to be able to run in larger batches

So I've been able to accomplish this by using a hacky method put seems to work in my test environment.
Note that the el.Current.Name property will equate to the Ctrl-Alt-Del text, so on 2008R2 this is 'Press CTRL-ALT-DEL to log on' and 'Press CTRL-ALT-DEL to sign in.' on 2012R2
I've built a C# console application that uses UI Automation:
using System;
using System.Windows.Automation;
namespace WorkstationLocked
{
class Program
{
static void Main()
{
AutomationElement el = AutomationUI.FindElementFromAutomationID("LockedMessage");
if (el !=null)
{
Console.WriteLine(el.Current.Name);
}
}
}
class AutomationUI
{
public static AutomationElement FindElementFromAutomationID(string automationID)
{
string className = "AUTHUI.DLL: LogonUI Logon Window";
PropertyCondition condition = new PropertyCondition(AutomationElement.ClassNameProperty, className);
AutomationElement logonui = AutomationElement.RootElement.FindFirst(TreeScope.Children, condition);
if (logonui != null)
{
condition = new PropertyCondition(AutomationElement.AutomationIdProperty, automationID);
return logonui.FindFirst(TreeScope.Descendants, condition);
}
else
{
return null;
}
}
}
}
I can then execute this console application via PsExec, however, because this needs to be launched in the winlogon desktop, which can only be done by running under the local system, PsExec is invoked twice. For example:
psexec.exe \\ServerA -s -d C:\PsTools\PsExec.exe -accepteula -d -x C:\Utils\WorkstationLocked.exe
This is very much a work in progress right now as I can't get the output of the command to pass through to the calling process so I may just look to populate a registry value or write to a file that can be subsequently interrogated.

Related

Process.Start won't work

I am trying to launch a process from a web page's back-end code/app pool. This process will launch an App that i built myself.
For some reason, the process only works / runs when i start it from VS2013... it never works when i launch it from IIS(7.5) itself.
I am on a Windows 7 machine (both IIS host, and App location), and I've setup my web site to only be accessible via internal network.
Here's the code, followed by the config / attempts to fix the issue:
protected void btn_DoIt_Click(object sender, EventArgs e)
{
string file_text = this.txt_Urls.Text;
if (!String.IsNullOrWhiteSpace(file_text))
File.WriteAllText(ConfigurationManager.AppSettings["filePath"], file_text);
ProcessStartInfo inf = new ProcessStartInfo();
SecureString ss = GetSecureString("SomePassword");
inf.FileName = #"........\bin\Release\SomeExecutable.exe";
inf.Arguments = ConfigurationManager.AppSettings["filePath"];
inf.UserName = "SomeUserName";
inf.Password = ss;
inf.UseShellExecute = false;
//launch desktop app, but don't close it in case we want to see the results!
try
{
Process.Start(inf);
}
catch(Exception ex)
{
this.txt_Urls.Text = ex.Message;
}
this.txt_Urls.Enabled = false;
this.btn_DoIt.Enabled = false;
this.txt_Urls.Text = "Entries received and process started. Check local machine for status update, or use refresh below.";
}
Here are the things I've tried to resolve the issue:
Made sure the executing assembly was built with AnyCPU instead of
x86
Ensured that the AppPool that runs the app, also runs under the same account (SomeUsername) as the ProcessStartInfo specified.
Ensured that the specific user account has full access to the executable's folder.
Ensured that IIS_USR has full access to the executable's folder.
Restarted both the app pool and IIS itself many times over implementing these fixes
I am now at a loss as to why this simply will not launch the app... when i first looked into the event log, i saw that the app would die immediately with code 1000:KERNELBASE.dll, which got me on the AnyCPU config instead of X86 fix... that fixed the event log entries but the app still doesn't start (nothing comes up in task manager), and i get no errors in the event log...
if someone could help me fix this problem i would really appreciate it. This would allow me to perform specific tasks on my main computer from any device on my network (phone, tablet, laptop, etc etc) without having to be in front of my main PC...
UPDATE
The comment to my OP, and ultimate answer from #Bradley Uffner actually nailed the problem on the head: My "app" is actually a desktop application with a UI, and in order to run that application, IIS would need to be able to get access to the desktop and the UI, just like if it were a person sitting down in front of the PC. This of course is not the case since IIS is running only as a service account and it makes sense that it shouldn't be launching UI programs in the background. Also see his answer for one way of getting around this.
Your best bet might be to try writing this as 2 parts. A web site that posts commands to a text file (or database, or some other persistent storage), and a desktop application that periodically polls that file (database, etc) for changes and executes those commands. You could write out the entire command line, including exe path command arguments, and switches.
This is the only way I can really think of to allow a service application like IIS to execute applications that require a desktop context with a logged in user.
You should assign a technical user with enough high priviliges to the running application pool. By default the application pool is running with ApplicationPoolIdentity identy which has a very low priviliges.

Reliable method for detecting if Webex Client is running

What is the best method for determining if the Cisco Webex client is running on a user's computer? Currently, I'm checking for a running process like this:
public static bool IsWebExClientRunning()
{
// webex process name started from internet browser (could change). Just use Process Explorer to find the sub process name.
// alternate name - CiscoWebexWebService
Process[] pname = Process.GetProcessesByName("atmgr");
return pname.Length > 0;
}
While this method works, there could be an instance where Cisco pushes out updates to their client that changes the process name which would break this code if we're looking for a specific process name.
The Webex client starts as a child process from an Internet browser since it is technically a browser plugin and it doesn't show up on its own in Windows Task Manager. I have seen both atmgr and CiscoWebexWebService using Process Explorer to find the process. Sometimes, depending on the host operating system, Windows XP/Windows 7, it will just display atmgr and not the child process CiscoWebexWebService belonging to atmgr. It also varies slightly based on the browser that is used. It runs as a browser plugin for all supported browsers and for unsupported browsers, it will give the option to run as a standalone application.
The process tree can vary (i.e. other browsers/operating systems), but it looks something like this:
iexplore.exe
-> atmgr.exe
-> CiscoWebexWebService.exe
Obviously, all checks must be done client side and not server side, but is there a better method for approaching this?
I spoke with a Cisco specialist and they said that my current approach should be safe for detecting if the Webex client is running a user's machine. They were able to confirm that the process name is atmgr.exe and should not change in the near future.

Denial of service (DoS) Application in Visual Studio 2010

Its a lengthy question and Its might be amature, sorry I'm new at programming;
I want to design a console based application for testing with a Denial of service (DOS) attack. Suppose ping example.com -t -l 65000 is the only thing that needs to be executed in a console. I want the console to run in background. But I want lets say, 5 different console performing the ICMP flooding in the background. to kill the process, we manually need to kill the process. The testing will be done in a real-time environment. I need to make a .exe file for running it on windows.
I am building the program in c# using MS Visual studio 2010
what can be done?
Any suggestions will be greatly appreciated.
Are you trying to kill a server with ping requests ? Ping requests are very easy for the server to handle. You may in theory attack via layer 4, flooding the pipe lines, but you're better off with anything else than a echo request.
If your trying DoS attacks (I hope it is for research only) you may want to take a look at partial GET requests. I've managed to perform a very effective attack with them, the server is doomed unresponsive for as long as I want it. Althought the HTTP server was not responding, ping request kept returning the exact same trip time, as if nothing happen.
Partial GET / POST requests, work very well for more time than they should now, it seems very difficult to overcome this problem, Apache is well aware of this situation.
I've heard that IIS also has the same reaction to partial POSTs but not to partial GETs.
Some numbers about this techinique :
~5000bytes/s for 2-3 sec, every 30 seconds. That is bandwith use, basically 150 GET requests from once in a while.
all open slots of apache server fill up immediately.
Very hard to detect something is going on, CPU is at around 0.05% during the attack, network connection isn't very stressed either.
And, of course, almost no other HTTP request managed to reach the server. They can reach when the server reading time out reaches and all connections have to restart again.
I haven't tried this on servers with more than 150 slots for HTTP connections.
System.Diagnostics.Process proc;
cmd = "/C ping example.com -t -l 65000";
for (i = 0; i < 5; i++)
{
proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.FileName = "cmd";
proc.StartInfo.Arguments = cmd;
proc.Start();
}

MK_E_UNAVAILABLE in Marshal.GetActiveObject("Word.Application")

0x800401E3 (MK_E_UNAVAILABLE) error occurs in my case when UAC (User Account Control) isn't set to the un-restrictive "Never Notify Me".
Microsoft.Office.Interop.Word.Application wd =
(Microsoft.Office.Interop.Word.Application)
System.Runtime.InteropServices.Marshal.GetActiveObject("Word.Application");
The error is thrown when the code is run after publishing and installing the project. While debugging in the editor instead, everything is fine.
Is this due to security settings or credentials ? How to write such code correctly pls ?
Win Word is open and a document is open too, of course, and this code has always worked fine with UAC set to "Never Notify Me".
Running word as a service is nasty business see for instance here . I went through quite a few problems making it work. The way I made it work was to run it in separate process which launches it only once. The main program communicates with the process by sending commands as strings to stdin of the process and waiting for response on the stdout. If the response does not come in time allotted the process is killed and restarted

GetLastInputInfo API doesn't work if windows is set to "Automatic Login"

I use a Windows API call to GetLastInputInfo to determine if the system is idle. This works in pretty much any scenario - except when Windows is set to bypass the username/password and login automatically.
In this case, querying GetLastInputInfo always returns 0. In normal conditions, it would return the system tick count when the last input occurred.
Does anyone know why? Or does anyone know an alternate method to determine if a windows session is idle?
Edit
This is how the code is written to detect the current system idle time. The detection occurs on a timer and regardless of how long the timer runs, GetLastInputInfo will always return 0 while windows is set to "auto login".
long lastInputTicks = 0;
long idleTicks = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
if (GetLastInputInfo(ref lastInputInfo)) {
lastInputTicks = lastInputInfo.dwTime;
var systemUptime = GetTickCount();
idleTicks = systemUptime - lastInputTicks;
}
return new TimeSpan(0, 0, (int)(idleTicks / 1000));
Thanks
Update
I've confirmed that this only happens because the code runs in a service.
To clarify: Under a normal scenario where auto-login is not being used, the user logs in at the console and the above code -- even if running as a service -- will return the proper user inactivity time.
However, if auto-login is turned on, then the windows session appears to not run as a typical console session, and is therefore NOT tracked. This applies only when the code is ran from a service and not from the "user space" - it works great in all cases if running from a user space.
That said, I'd really love to find a solution that works from a service in every scenario.
It returns 0 seconds when GetLastInput() returns FALSE. Which is obviously incorrect. You cannot use code like this in a service, it doesn't have a way to retrieve user input. If GetLastInput() ever returns FALSE (likely with auto-login) then it will return FALSE forever.
Having services be aware of user sessions is a lost cause in Windows, a service runs in its own session and doesn't need a user to be logged-in. Which is the major reason to use a service in the first place. Instead of a program that's started with, say, a shortcut in the Startup folder. This code might have worked by accident in XP but it definitely won't work in Vista and Windows 7. Google "session 0 isolation" for more details.
The Remarks section of the documentation pretty much explains why it won't work:
This function is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function.
So that's that.

Categories