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

时间:2010-06-18 13:50:26

标签: windows-7 login-script

所以这是独家新闻:

我在前一段时间写了一个小小的C#应用​​程序,显示主机名,IP地址,图像日期,解冻状态(我们使用DeepFreeze),当前域和当前日期/时间,以显示在我们Windows的欢迎屏幕上7台实验室机器。这是为了取代我们以前的信息块,它是在启动时静态设置的,实际上是嵌入到背景中的文本,带有更多动态和功能。该应用程序使用Timer来每秒更新IP地址,deepfreeze状态和时钟,并检查用户是否已登录并在检测到此类情况时自行终止。

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

我们已经尝试使用另一个C#应用程序来启动进程,通过Process类,使用WMI调用(Win32_Process和Win32_ProcessStartup)以及各种启动标志等,但所有结束都与脚本完成和信息的结果相同阻止进程被杀死。我修改了将应用程序重写为服务,但服务从未设计为与桌面交互,更不用说登录窗口,并且在正确的上下文中运行的东西似乎从未真正解决过。

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

4 个答案:

答案 0 :(得分:11)

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

简短摘要:

  1. 抓住Winlogon.exe进程(作为进程)
  2. 使用Process的.handle使用OpenProcessToken获取winlogon的令牌
  3. 创建新令牌并将winlogon令牌复制到其中
  4. 提升令牌的权限
  5. 使用CreateProcessAsUser创建进程,确保将lpDesktop设置为“Winsta0 \ Winlogon”并使用您创建的令牌。
  6. 代码示例:

            // 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");
            }
    

答案 1 :(得分:6)

这是其中一个“你真的需要一个很好的理由去做这个”的问题。 Microsoft非常努力地阻止在启动屏幕上运行的应用程序 - Windows中与登录屏幕交互的每一段代码都经过仔细的代码审查,因为在登录屏幕上运行的代码中的错误的安全后果是可怕的 - 如果你搞砸了甚至稍微提高,您将允许恶意软件进入计算机。

为什么要在登录屏幕上运行程序?也许有一种记录在案的方式并没有风险。

答案 2 :(得分:6)

我在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;
}

答案 3 :(得分:1)

我认为你可以做到,但它非常复杂。通常不允许在欢迎屏幕上运行交互式应用程序。在较高的层次上,您需要:

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

我编写了一个可以与登录屏幕进行某种程度交互的应用程序,但它没有显示任何UI。它可能已经完成,但可能更为复杂。

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

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