Shutdown command is not working while system is locked - c#

(There is a very similar topic that never got solved:here)
Here is my code
for (int i = 0; i < 5; i++)
{
Thread.Sleep(10000);
Console.WriteLine("Hi");
Process.Start("shutdown", "/s /t 0");
}
If I locked my system the shutdown process is not working otherwise it's working fine. When I unlock my system after 2 min it's showing only hi (5 times).

I think the shutdown command requires elevated privilege, so whether you call it your way or use an API, I would expect a UAC prompt to be required. You don't specify which version of Windows you're on but I think it's safe to assume UAC is present.
When the system is locked, a UAC prompt can't be shown and it times out after a while. So without an interactive user's express permission your shutdown can't proceed.
I think, to achieve your aim, you'd need to create a windows service that runs with high privilege, and have it call the correct API to shut the machine down. Then you can have a normal process communicate with your service and tell it to do the shutdown.
I hope somebody else can come up with a simpler solution for you.

Did you try other parameters of shutdown.exe like /p and/or /f?

Related

C#: Programmatically Detect Windows Server Has Booted

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.

Find out if another application is running as admin

I'm trying to determine if another application is running as an administrator. There are 100's of SO questions about finding if your application is or not, but not for another application.
If I do var processes = Process.GetProcesses(); and loop through them, they don't appear to have any "Elevated" or "IsAdministrator" properties on them. I also found a function on here to get the Owner but they all come back as "My-Laptop\Me" so that's not working for me the way I hoped it would.
Is there a way to find this? I'm on Win 8.1 and would prefer something that works for 7 as well, but if it only works for 8.1+ that's totally fine.
Thanks!
EDIT:
My application has to run as an administrator.
You can use the technique from this answer, just replace Process.GetCurrentProcess().Handle with the Process.Handle of the other process.
If you look at the code (there's quite a bit of code there) it get the process "user token" (the permissions the process is running under) and checks if this token has the administrator role or not,
Assuming your process is not running as administrator, trying to get information about a process that is elevated via UAC (such as its MainWindowTitle) will throw an AccessDenied exception, where a non-elevated process will permit you access to that information. This assumes you also verify the owner of the process to check that it's you.
try
{
var foo = process.MainWindowTitle;
return false; //Process is not elevated
}
catch (Win32Exception ex)
{
return true; //Process is elevated if ex error code is AccessDenied
}

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.

ApplicationPool user vs StartInfo.Username?

I've been testing ( for the last 4 days) the variety of options to start a Process under iis7 ( asp.net)
I did find a solution.
As long as we don't need to interact with desktop, and only need to run cmd ( or something like that) the solution is simple:
w3wp user-> should be high privileged user.
Process start info (StartInfo.Username)-> should be also high privileged user.
However, there is a catch ( according to my testings):
Both users have to be the same (if we want cmd to execute)! this is the only way it will work.
So here are my 2 questions:
Why they both must be the same? Can't w3wp HighPrivilegedUSerA run (via process.startInfo) cmd as HighPriviligedUSerB?
Both users are domain admins.( which are also admins in my local group). Does only domain admin/local admin can run processes on the local machine?
p.s. All folders permissions are everyone : full controll ( including c:\windows\*.* /s and including cmd.exe permissions) and both users, as mentioned, are admins on local machine with same cloned permissions.IIS7 handler mapping * [static file] is set to read+execute
Also, the full cmd command is:
cmd /c time /t >c:\1.txt. A success is if the file exists.( and I succeed, only when both account were the same).
Full code:
Process proc = new Process();
proc.StartInfo.FileName = "cmd";
proc.StartInfo.UserName = "Royin"; //<-- only if this user is the same as w3wp user , the operation succeed !
proc.StartInfo.Domain = ...;
proc.StartInfo.WorkingFolder = #"c:\windows\system32";
proc.StartInfo.Password = ...
proc.StartInfo.Arguments = #"/c time /t >c:\1.txt"
proc.Start();
Ok, first, I HIGHLY recommend using the excellent SysInternals ProcessMonitor to help troubleshoot any issue like this: Process Monitor.
This app will basically tell you every action a process is attempting to take, so in your situation, you'd see it try to call QueryOpen, ProcessCreate, etc.
Have you checked what the ExitCode of the process is under the failing scenario? I'd be willing to bet real money it's coming back as 0xC0000142 (-1073741502), which means something like "failed to load DLL" or something. Running anything under the system32 path, even with privileged user credentials, is going to run into oddball issues with permissions, again due to the initialization procedure for creating a process.
Basically, the Process.Start flow looks something like this:
(assumptions: UserA == process running w3wp, UserB == impersonation ctx, UserC == credentials specified in process start info)
First, UserB isn't going to have much impact, as we've discussed in other conversations; any process creation stuff is going to be based on the process token of the "launching entity", so the credentials of UserA are the ones we'll be looking at.
The runtime says "Hey, can UserA access the file name specified in StartInfo.FileName?"
Windows responds yes/no, but also "BUT, to use that, you also need to be able to read all these other DLLs"
Runtime responds "Ok, can UserA access those DLLs?"
If the answer to all the above is yes, then the runtime says "Ok, logon this user and try creating a new process with cmd line and arguments..."
You are most-likely running into issues with either #2 or #4, as the default app pool identity has no read-access rights to the System32 folder. This is why when you switch the identity of the w3wp process to a privileged account, it works.
You could try a couple things, but the easiest option is probably switching back to a low-privilege account (like default app pool identity), but grant read-only access to the system32 folder to that account.
I would ABSOLUTELY not run w3wp as a privileged user, though - that is just asking for massive headaches should anything nasty happen, like somebody hacking you.
Oh, last thoughts:
DON'T set UseShellExecute to true if you can help it, it does weird stuff.
DON'T set LoadUserProfile to true if you can help it, it also does weird stuff, and is really slow as well.
DO set CreateNoWindow to true if you can, otherwise you'll see lotsa windows opening/closing on the server
DO use RedirectStandardOutput / RedirectStandardError instead of piping the output, it's way more controllable and gives better feedback when things go wrong.
DO always check the ExitCode of the process if it doesn't look like it worked

Deny Access to Kiosk program process

I have a kiosk app and have to disable task manager always to prevent closing the program by users .
But some users need the TaskManager to close hanging programs.
Any help would be appropriated.
However, I am sure there is a function in windows to prevent closing a program's process , as when one attempt to kill rundll.exe process. I want to know that function if I can call it with DllImport
Can anyone help with a trick?
A hack?
A function?
Any other solution?
EDIT:
At least if there is not a way to prevent the process from being closed, I need a way to hide it from processes list appeared in the task manager.
EDIT 2:
I can't find the solution so far
One approach, if you could access the process ID in an administrative context, is to deny the PROCESS_TERMINATE permission on the process to end users. Terminating the process (through task manager or other contexts) is by default granted to the owner, but can be explicitly denied. When it is denied, terminating the process would require the owner to manually change the ACL, and then terminate the process. If the user is neither an administrator nor the owner of the process, he will not be able to forcibly terminate the process (e.g., through Task Manager), although the process will be allowed to exit normally.
The following code puts an explicit deny ACE on the process with the PID processid for members of the Everyone group.
#include "Aclapi.h"
#include "Sddl.h"
DWORD RestrictTerminateOnProcessId(DWORD processid)
{
PACL dacl = NULL, newdacl = NULL;
HANDLE ph = NULL;
PSECURITY_DESCRIPTOR* desc = NULL;
PSID everyonesid = NULL;
ph = OpenProcess(WRITE_DAC | READ_CONTROL, false, processid);
if (!ph) goto cleanup;
if (ERROR_SUCCESS != GetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&dacl,
NULL,
desc)) goto cleanup;
SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
if (!AllocateAndInitializeSid(
&WorldAuth,1,SECURITY_WORLD_RID,
0,0,0,0,0,0,0,&everyonesid)) goto cleanup;
// begin copy dacl
_ACL_SIZE_INFORMATION si;
GetAclInformation(dacl,
&si,
sizeof(si),
AclSizeInformation);
DWORD dwNewAclSize = si.AclBytesInUse +
(2*sizeof(ACCESS_DENIED_ACE)) + (2*GetLengthSid(everyonesid)) -
(2*sizeof(DWORD));
newdacl = (PACL)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwNewAclSize);
if (newdacl == NULL) goto cleanup;
if (!InitializeAcl(newdacl, dwNewAclSize, ACL_REVISION_DS))
goto cleanup;
if (!AddAccessDeniedAce(newdacl,
ACL_REVISION_DS,
PROCESS_TERMINATE,
everyonesid)) goto cleanup;
for (int i = 0; i < si.AceCount; i++)
{
LPVOID pace = NULL;
if (!GetAce(dacl, i, &pace)) goto cleanup;
if (!AddAce(newdacl, ACL_REVISION_DS,
MAXDWORD, pace, ((PACE_HEADER)pace)->AceSize))
goto cleanup;
}
// end copy dacl
if (!SetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
newdacl,
NULL)) goto cleanup;
SetLastError(0);
cleanup:
DWORD ret = GetLastError();
if (desc) LocalFree(desc);
if (newdacl) HeapFree(GetProcessHeap(), 0, (LPVOID)newdacl);
if (ph) CloseHandle(ph);
if (everyonesid) FreeSid(everyonesid);
return !ret;
}
You could create a service that runs on boot. It then monitors when the user logs in and starts your program. From there you have two choices:
Have it wait on the program and revive it for as long as the user's session persists.
Run it under an administrator account and make sure the users are always running on limited accounts. Windows' privilege enforcement should take care of your program not dying.
If you must allow the users administrative privileges (and they can thus kill your service), make sure to register your service so that the SCM restarts it automatically.
Anything beyond that would require some kernel hacking, as mdm said, or diving into rootkit territory. Which I would suggest you avoid.
If by any chance you're still running Windows XP, instead of a system service, you can register a Winlogon notification package. They're basically unkillable, as they run in the context of winlogon.exe, which is the reason they were removed from Vista and above.
The proper way of doing this is:
Create a user for the Kiosk application. Lets say KioskUser
Create other users that do stuff in the computer. Lets say User1
NONE of them should be administrator. They do not need to be admins right? They can have privileges of course.
You are going to use User1 account as usual.
Then, you run the Kiosk application as the KioskUser. (use runas command)
- add the application to the session and make it visible (see the arguments for more info on this)
While the User1 is loged in and the Kiosk application runs from the KioskUser, the User1 cannot kill the process.
I wouldn't recommend "hacking" the system, nor changing something on the registry.
Also, you can make the Kiosk application a service, and run it under the Kiosk user.
It sounds like you can't prevent a process from being killed - see this question and specifically the link posted to an explanation.
You can however prevent the user from opening the task manager from the taskbar (right click -> open task manager), from pressing Ctrl-Shift-Esc, or from typing taskman at the command prompt using the following registry key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableTaskMgr
Setting it to 1 disables the task manager, 0 enables it again.
From the command prompt, disable it like so:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System /v DisableTaskMgr /t REG_DWORD /d 1 /f
And you can renable with this command:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System /v DisableTaskMgr /t REG_DWORD /d 0 /f
You'll also need to find a way of preventing the user from writing to this key in the registry - although on my Windows 7 64-bit machine I had to open an admin command prompt before I could change the key.
Edit
As suggested in the comments, you could intercept a low level system call to alter the list of processes reported. This answer eludes to it, you will basically have to write a kernel-mode rootkit - probably implemented as a device driver. This will likely only work in Windows XP and below due to kernel changes made in Vista and onwards. It can be done however, a friend of mine created a prototype of this for his BSc final year dissertation.
You'll also have to consider other methods of querying for processes - IIRC NtQuerySystemInformation isn't the only way that processes can be enumerated.
Short of protecting your Kiosk app with some rootkit/driver you can't... although the methods you would need to use in this context are "malware-like" so beware...
Here http://blogs.msdn.com/b/oldnewthing/archive/2004/02/16/73780.aspx you can see on one side why this is a task not really possible and on the other side the several possibilities you would have to defend against... which would be:
deny PROCESS_TERMINATE
deny PROCESS_CREATE_THREAD
deny PROCESS_VM_WRITE
deny PROCESS_SUSPEND_RESUME
Plus any defense you can get your hands on against debuggers - another very tricky business.
Sorry for not posting this as a comment (not enough reputation).
I'm using drfs solution, but it seems to induce a memory leak with the way GetSecurityInfo is used. I changed the code and got rid of the memory leak like this:
Declaration:
PSECURITY_DESCRIPTOR desc = NULL;
Initialisation:
desc = (PSECURITY_DESCRIPTOR)GlobalAlloc(GMEM_FIXED, sizeof(PSECURITY_DESCRIPTOR));
Cleanup:
if (desc) GlobalFree(desc);
Call:
GetSecurityInfo(ph, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &desc)
I also found this article on the issue: http://i1.blogs.msdn.com/b/oldnewthing/archive/2012/12/12/10376639.aspx
While you can't prevent an authorized user from killing your process, you can prevent users from having the necessary permissions to kill your process. That said, a local administrator will always have permission to kill any process. Is your hope to prevent closing of your application by any user or just non-admins? If the former, simply running the process under a dedicated user account may be enough to do the trick.

Categories