C# 在运行时提升程序

mri*_*rid 0 c# uac winforms elevated-privileges

我有一个 C# 程序,我希望其中的某些功能需要管理员密码。为了解决这个问题,我启动了应用程序的另一个实例作为提升的进程,并向其传递命令行参数,以便该进程知道它必须执行什么任务。

Process proc = new Process();
proc.StartInfo.Arguments = "PARAMETERS HERE");
proc.StartInfo.FileName = Application.ExecutablePath;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";
proc.Start();
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我有一个小问题。我刚刚注意到启动新进程时弹出的 UAC 提示符不仅显示应用程序名称和路径,还显示传递给它的命令行参数。这样,用户可以看到正在传递的参数,并直接从run命令或传递参数command prompt

有什么办法可以防止这种情况吗?或者提升正在运行的程序的更好方法?

ren*_*ene 5

您可以在第二个实例启动后传递它们,而不是在命令行上提供参数,例如通过在两个实例之间使用命名管道。为了确定启动的进程是否是第一个进程,我使用了命名互斥体,很大程度上受到了What is a good pattern for using a Global Mutex in C#? 的启发。除了我在这里使用本地互斥体,将其限制为(终端)会话。

主要的

在这里,您可以看到互斥体的创建,并根据互斥体是否创建,我们知道我们是第一个实例还是第二个实例。

static string MyPipeName = $"MyApp_{Environment.UserDomainName}_{Environment.UserName}";

static void Main(string[] args)
{
    bool created; // true if Mutex is created by this process 
    using(var mutex = new Mutex(false, @"Local\" + MyPipeName, out created)) // this needs proper securing
    {
        var gotit = mutex.WaitOne(2000); // take ownership
        if (!created)
        {
            if (gotit) 
            {
                args = SecondInstance(mutex);
                Console.WriteLine("I'm the second instance");
            }
            else 
            {
                // no idea what to do here, log? crash? explode?
            }
        } 
        else 
        {
            FirstInstance(mutex);
            Console.WriteLine("I'm the first instance");
        }

        ProgramLoop(args); // our main program, this can be Application.Run for Winforms apps.
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个例子

在 FirstInstance 方法中,我们设置一个委托,该委托将在调用时启动NamedPipeServerStream、释放互斥锁(以便在第二个进程中提供额外的保护)、再次启动自身并等待客户端连接到命名管道。完成后,它发送参数并等待确认。一旦互斥体被释放,它就会继续。

static void FirstInstance(Mutex mutex)
{
    StartSecondInstanceHandler += (args) => 
    {
        using(var srv = new NamedPipeServerStream(MyPipeName)) // this needs proper securing
        {
            mutex.ReleaseMutex();
            // kick off a second instance of this app
            Relaunch();

            srv.WaitForConnection();
            using(var sr = new StreamReader(srv))
            {
                using(var sw = new StreamWriter(srv))    
                {
                    Trace.WriteLine("Server Started and writing");
                    // send the arguments to the second instance
                    sw.WriteLine(args);
                    sw.Flush();
                    Trace.WriteLine("Server done writing");
                    // the client send back an ack, is not strictly needed
                    Trace.WriteLine("ack: {0}", sr.ReadLine());
                }
            }
            mutex.WaitOne();
        }
    };
}

// our public static delegate, accessible by calling
// Program.StartSecondInstanceHandler("/fubar");
public static Action<string> StartSecondInstanceHandler = null;

// just launch the app
static void Relaunch()
{
    var p = new ProcessStartInfo();
    p.FileName = Environment.CommandLine;
    p.UseShellExecute = true;
    p.Verb = "runas";
    var started = Process.Start(p);
} 
Run Code Online (Sandbox Code Playgroud)

第二实例

当第二个实例启动时,我们设置一个NamedPipeClientStream,连接到服务器并读取响应并发回确认。互斥体被释放并返回参数(我通过在空格上分割来使用快速破解)。

static string[] SecondInstance(Mutex mutex) 
{
    string arguments = String.Empty;
    Console.WriteLine("Client NamedPipe starting");
    using(var nps = new NamedPipeClientStream(MyPipeName))
    {
        nps.Connect();  // we expect the server to be running

        using(var sr = new StreamReader(nps))
        {
            arguments = sr.ReadLine();
            Console.WriteLine($"received args: {arguments}");
            using(var sw = new StreamWriter(nps))
            {
                sw.WriteLine("Arguments received!");
            }
        }
        mutex.ReleaseMutex(); // we're done
    }
    return arguments.Split(' '); // quick hack, this breaks when you send /b:"with spaces" /c:foobar
}
Run Code Online (Sandbox Code Playgroud)

程序循环

这里要完成的是一个枯燥的程序循环

static void ProgramLoop(string[] args)
{
    // main loop
    string line;
    while((line = Console.ReadLine()) != String.Empty)
    {
        switch(line)
        {
            case "admin":
                if (StartSecondInstanceHandler != null)
                {
                    Console.WriteLine("elevating ...");
                    StartSecondInstanceHandler("/foo:bar /baz:fu");
                    Console.WriteLine("... elevation started");
                } 
                else
                {
                    Console.WriteLine("you are elevated with these arguments: {0}", String.Join(' ',args));
                }
            break;
            default:
                Console.WriteLine("you typed '{0}', type 'admin' or leave empty to leave", line);
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

把它们放在一起......

这就是你最终得到的结果:

第一和第二

你必须相信我 UAC 提示不包含命令参数...:(