Starting the process on the Windows 7 Welcome screen

So here is the scoop:

I wrote a tiny C # application that displays the host name, ip address, date displayed, defrost status (we use DeepFreeze), current domain and current date / time to display the welcome screen of our Windows 7 lab machines. This was supposed to replace our previous information block, which was installed statically at startup and actually embedded text in the background, with something more dynamic and functional. The application uses a timer to update the IP address, deep freeze state, and hours per second, and it checks to see if the user is logged in and kills himself when he finds this condition.

If we just run it, through our script run (set using Group Policy), it will contain a script open, and the machine will never enter the login prompt. If we use something like the start or cmd commands to run it under a separate shell / process, it runs until the script finishes running, after which Windows seems to clear all the child processes of the script. Currently, we can get around this with psexec -s -d -i -x to disable it, which allows us to save it after running the script, but can be incredibly slow, adding somewhere between 5 seconds and a minute of our startup time .

We experimented using another C # application to start a process through the Process class using WMI calls (Win32_Process and Win32_ProcessStartup) with different launch flags, etc., but they all end with the same script completion result and the information block process will be killed. I was busy rewriting the application as a service, but the services were never designed to interact with the desktop, not to mention the login window, and that everything that works in the right context would never look difficult.

So, to the question: does anyone have a good way to do this? Run the task so that it does not depend on the launch of the script and runs on top of the welcome screen?

+7
windows-7 login-script
source share
4 answers

This can be done using many Win32 API calls. I managed to get a GUI program on the Winlogon desktop (before anyone asks, this is not an interactive GUI). Basically you need to start the bootloader process as SYSTEM, which will then call the new process. Since you most likely want this process to start at startup, you can use the task scheduler to start the bootloader as SYSTEM or you can use the service to do the same. I am currently using the service, but I tried using the task scheduler and it really worked fine.

Short description:

  • Capture the Winlogon.exe process (as a process)
  • Take winlogon token using OpenProcessToken using .handle process
  • Create a new token and duplicate the winlogon token
  • Raise Token Privileges
  • Create a process using CreateProcessAsUser, making sure to set lpDesktop to "Winsta0 \ Winlogon" and using the marker you created.

Code example:

  // grab the winlogon process Process winLogon = null; foreach (Process p in Process.GetProcesses()) { if (p.ProcessName.Contains("winlogon")) { winLogon = p; break; } } // grab the winlogon token IntPtr userToken = IntPtr.Zero; if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken)) { log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error()); } // create a new token IntPtr newToken = IntPtr.Zero; SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES(); tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes); SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES(); threadAttributes.nLength = Marshal.SizeOf(threadAttributes); // duplicate the winlogon token to the new token if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out newToken)) { log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error()); } TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES(); tokPrivs.PrivilegeCount = 1; LUID seDebugNameValue = new LUID(); if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue)) { log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error()); } tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1]; tokPrivs.Privileges[0].Luid = seDebugNameValue; tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // escalate the new token privileges if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero)) { log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error()); } PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = "Winsta0\\Winlogon"; // start the process using the new token if (!CreateProcessAsUser(newToken, process, process, ref tokenAttributes, ref threadAttributes, true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero, logInfoDir, ref si, out pi)) { log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error()); } Process _p = Process.GetProcessById(pi.dwProcessId); if (_p != null) { log("Process " + _p.Id + " Name " + _p.ProcessName); } else { log("Process not found"); } 
+9
source share

This is one of those questions that you "really need to do this." Microsoft is trying very hard to block applications running on the startup screen - every bit of Windows code that interacts with the login screen is checked very carefully by the code, because the security consequences of errors in the code running on the login screen are terrible - if you screw in even a bit, you allow malware to get on your computer.

Why do you want to run your program on the login screen? Maybe there is a documented way to do this, which is not so risky.

+6
source share

I translated the code above into C ++, if someone needs it ... Please note that there are links to parts of my code, but this may help:

 static bool StartProcess(LPCTSTR lpApplicationPath) { CAutoGeneralHandle hWinlogonProcess = FindWinlogonProcess(); if (hWinlogonProcess == INVALID_HANDLE_VALUE) { DU_OutputDebugStringff(L"ERROR: Can't find the 'winlogon' process"); return false; } CAutoGeneralHandle hUserToken; if (!OpenProcessToken(hWinlogonProcess, TOKEN_QUERY|TOKEN_IMPERSONATE|TOKEN_DUPLICATE, &hUserToken)) { DU_OutputDebugStringff(L"ERROR: OpenProcessToken returned false (error %u)", GetLastError()); return false; } // Create a new token SECURITY_ATTRIBUTES tokenAttributes = {0}; tokenAttributes.nLength = sizeof tokenAttributes; SECURITY_ATTRIBUTES threadAttributes = {0}; threadAttributes.nLength = sizeof threadAttributes; // Duplicate the winlogon token to the new token CAutoGeneralHandle hNewToken; if (!DuplicateTokenEx(hUserToken, 0x10000000, &tokenAttributes, SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation, TOKEN_TYPE::TokenImpersonation, &hNewToken)) { DU_OutputDebugStringff(L"ERROR: DuplicateTokenEx returned false (error %u)", GetLastError()); return false; } TOKEN_PRIVILEGES tokPrivs = {0}; tokPrivs.PrivilegeCount = 1; LUID seDebugNameValue = {0}; if (!LookupPrivilegeValue(nullptr, SE_DEBUG_NAME, &seDebugNameValue)) { DU_OutputDebugStringff(L"ERROR: LookupPrivilegeValue returned false (error %u)", GetLastError()); return false; } tokPrivs.Privileges[0].Luid = seDebugNameValue; tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // Escalate the new token privileges if (!AdjustTokenPrivileges(hNewToken, false, &tokPrivs, 0, nullptr, nullptr)) { DU_OutputDebugStringff(L"ERROR: AdjustTokenPrivileges returned false (error %u)", GetLastError()); return false; } PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; si.cb = sizeof si; si.lpDesktop = L"Winsta0\\Winlogon"; // Start the process using the new token if (!CreateProcessAsUser(hNewToken, lpApplicationPath, nullptr, &tokenAttributes, &threadAttributes, true, CREATE_NEW_CONSOLE|INHERIT_CALLER_PRIORITY, nullptr, nullptr, &si, &pi)) { DU_OutputDebugStringff(L"ERROR: CreateProcessAsUser returned false (error %u)", GetLastError()); return false; } return true; } 
+6
source share

I think you can do it, but it is very important. Interactive applications are usually not allowed to run on the welcome screen. At a high level you need:

  • Create a Windows service that starts automatically
  • Use the Windows service to create another process in the current session and on the desktop (using the Win32 WTSGetActiveConsoleSessionId and OpenInputDesktop )

I wrote an application that can interact somewhat with the login screen, but it does not show any user interface. It can probably be done, but it can be even more.

Note. I found that I was unable to get the results from OpenInputDesktop from my Windows service. Instead, I had to make a call in another process and tell the service to restart the process on the desktop.

I hope you can at least start. Good luck

+1
source share

All Articles