在Windows 7欢迎屏幕上运行一个进程

所以这里是独家新闻:

我写了一个很小的C#应用​​程序,显示主机名,IP地址,映像date,解冻状态(我们使用DeepFreeze),当前域和当前date/时间,显示在Windows 7实验室机器的欢迎屏幕上。 这是为了取代我们以前的信息块,它是在启动时静态设置的,并且实际上将文本embedded到背景中,并且具有更多的dynamic性和function性。 该应用程序使用一个计时器每秒更新IP地址,深度冻结状态和时钟,它会检查用户是否已经login并在检测到这种情况时自杀。

如果我们通过我们的启动脚本(通过组策略设置)运行它,它将保持脚本打开,并且机器永远不会进入login提示符。 如果我们使用类似start或cmd命令的方式在单独的shell /进程下启动它,它将一直运行,直到启动脚本完成,此时Windows似乎清理脚本的所有subprocess。 我们现在可以绕过使用psexec -s -d -i -x来closures它,这使得它在启动脚本完成之后仍然存在,但是速度可以非常慢,在5秒到1分钟之间我们的启动时间。

我们已经尝试过使用另一个C#应用程序来启动进程,通过Process类,使用带有各种启动标志的WMI调用(Win32_Process和Win32_ProcessStartup)等等,但是所有的脚本处理和信息块处理都得到相同的结果杀害。 我修改应用程序作为服务,但服务从来没有被devise为与桌面交互,更不用说login窗口,让事情在正确的上下文中运行似乎从来没有真正的工作。

所以对于这个问题:有没有人有一个很好的方法来完成这个? 启动一个任务,使其独立于启动脚本并在欢迎屏幕上运行?

这可以通过很多Win32 API调用来完成。 我已经设法将一个带有GUI的程序放到Winlogon桌面上(在任何人询问之前,这不是一个交互式GUI)。 基本上你需要运行一个装载器进程作为SYSTEM,然后产生新的进程。 由于您很可能希望此过程在启动时运行,您可以使用任务计划程序以SYSTEM身份运行装载程序,也可以使用服务来执行相同的操作。 我目前正在使用一个服务,但我尝试使用任务调度程序,它确实工作得很好。

简短的摘要:

  1. 抓取Winlogon.exe进程(作为一个进程)
  2. 使用OpenProcessToken使用进程的.handle获取winlogon的令牌
  3. 创build一个新的令牌并复制winlogon令牌
  4. 提升令牌的权限
  5. 使用CreateProcessAsUser创build该进程,确保将lpDesktop设置为“Winsta0 \ Winlogon”并使用您创build的令牌。

代码示例:

  // grab the winlogon process Process winLogon = null; foreach (Process p in Process.GetProcesses()) { if (p.ProcessName.Contains("winlogon")) { winLogon = p; break; } } // grab the winlogon's 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's 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"); } 

这是其中一个“你真的需要一个很好的理由来做这个”的问题。 微软很努力地阻止在启动屏幕上运行的应用程序–Windows中与login屏幕交互的代码中的每一个代码都经过了仔细的代码审查,因为在login屏幕上运行的代码中的错误的安全后果是可怕的 – 如果你拧即使稍微增加,你也会允许恶意软件进入电脑。

为什么要在login屏幕上运行程序? 也许有一个文件logging的方式,这不是冒险的。

我用C ++翻译了上面的代码,如果其他人需要它…注意到我的代码有部分的引用,但它可能有帮助:

 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's 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; } 

我认为你可以做到,但是很相关。 交互式应用程序通常不允许在欢迎屏幕上运行。 在较高的层面上,您需要:

  • 创build一个自动启动的Windows服务
  • 使用Windows服务在当前会话和桌面上创build另一个进程(使用Win32方法WTSGetActiveConsoleSessionIdOpenInputDesktop

我写了一个可以与login屏幕进行交互的应用程序,但它不显示任何用户界面。 这可能是可以做到的,但可能涉及更多。

注意:我发现我无法从Windows服务中获得OpenInputDesktop结果。 我不得不在另一个进程中进行调用,并通知服务在正确的桌面上重新启动进程。

我希望至less能让你开始。 祝你好运!