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
。
有什么办法可以防止这种情况吗?或者提升正在运行的程序的更好方法?
您可以在第二个实例启动后传递它们,而不是在命令行上提供参数,例如通过在两个实例之间使用命名管道。为了确定启动的进程是否是第一个进程,我使用了命名互斥体,很大程度上受到了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 提示不包含命令参数...:(