Joe Pearson
Joe Pearson

Reputation: 149

Is there a method to open CMD as Admin through a C# command?

I have a small command that allows me to query information from CMD, however it needs admin privileges (not at my application level but at the CMD level) Only reason I am going this route is because I couldn't get WMI to query bitlocker settings for the life of me and this project needs to get off of my desk.

if (bitA.Text == "Bitlocker Available")
{               
    Process cmd2 = new Process();
    cmd2.StartInfo.Verb = "runas";
    cmd2.StartInfo.FileName = "cmd.exe";
    cmd2.StartInfo.Arguments = "/c ping 8.8.8.8";
    cmd2.StartInfo.UseShellExecute = false;
    cmd2.StartInfo.RedirectStandardOutput = true;
    cmd2.StartInfo.RedirectStandardError = true;
    cmd2.Start();
    //* Read the output (or the error)
    string output2 = cmd2.StandardOutput.ReadToEnd();
    bitB.Text = output2;
    cmd2.WaitForExit();
}

Upvotes: 2

Views: 3809

Answers (3)

Mike Zboray
Mike Zboray

Reputation: 40818

As I said in my comment, the issue is that the "runas" verb requires UseShellExecute to be true, but redirection requires UseShellExecute to be false. This makes the problem sort of tricky, but the key is start a process as admin that you can communicate with via some kind of IPC, then this process starts whatever process you want to redirect output from. It can even be the same executable just switching on the arguments received. If I was writing a library, I would probably embed a shim executable as an assembly resource. Here is a simple example using named pipes:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.IO;

namespace AdminRedirect
{
    class Program
    {
        private static readonly int ProcessId = Process.GetCurrentProcess().Id;

        static void Main(string[] args)
        {
            bool isAdmin = IsAdministrator();
            Console.WriteLine("Id = {0}, IsAdmin = {1}", ProcessId, isAdmin);
            if (!isAdmin)
            {
                Console.WriteLine("Press any key to spawn the admin process");
                Console.ReadKey(intercept: true);
                string pipeName = "mypipe-" + Guid.NewGuid();
                Process cmd = new Process()
                {
                    StartInfo =
                    {
                        Verb = "runas",
                        Arguments = pipeName,
                        FileName = typeof(Program).Assembly.Location,
                        UseShellExecute = true
                    }
                };

                using (var pipeStream = new NamedPipeServerStream(pipeName))
                {
                    cmd.Start();
                    Console.WriteLine("Started {0}", cmd.Id);
                    pipeStream.WaitForConnection();
                    Console.WriteLine("Received connection from {0}", cmd.Id);
                    using (var reader = new StreamReader(pipeStream))
                    {
                        string line;
                        while((line = reader.ReadLine()) != null)
                        {
                            Console.WriteLine(line);
                        }
                    }                
                }

                Console.WriteLine("Hit any key to end");
                Console.ReadKey(intercept: true);
            }
            else
            {
                if (args.Length > 0)
                {
                    string pipeName = args[0];
                    Console.WriteLine("Opening client pipe: {0}", pipeName);
                    using (var pipeStream = new NamedPipeClientStream(pipeName))
                    {
                        pipeStream.Connect();
                        using (var writer = new StreamWriter(pipeStream))
                        {
                            StartChildProcess(writer);
                        }
                    }
                }
                else
                {
                    Console.WriteLine("We are admin and not piping, so just run");
                    StartChildProcess(Console.Out);
                    Console.WriteLine("Hit any key to end");
                    Console.ReadKey(intercept: true);
                }
            }
        }

        private static bool IsAdministrator()
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }

        private static void StartChildProcess(TextWriter output)
        {
            var cmd = new Process()
            {
                StartInfo =
                {
                    FileName = "cmd.exe",
                    Arguments = "/c ping 8.8.8.8",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true
                }
            };
            cmd.Start();
            string procOutput = cmd.StandardOutput.ReadToEnd();
            output.Write("Id: {0}, Output:{1}", cmd.Id, procOutput);
        }
    }
}

Upvotes: 1

Scott Chamberlain
Scott Chamberlain

Reputation: 127543

To use runas you must have UseShellExecute be true, however to use any of the redirect methods you must have UseShellExecute be false. You can not have both at the same time.

If you need to redirect the output and elevate at the same time you must do the following steps:

  1. Start another process you control1 using runas and UseShellExecute = true to generate a elevated process.
  2. The new process starts ping.exe with UseShellExecute = false and redirects the outputs.
  3. Use a form of IPC, like WCF over named pipes, to forward the output from your elevated 2nd process to your non elevated first process.

1: it could be your same EXE but passing some special command line arguments to put it in this new mode

Upvotes: 0

Cortright
Cortright

Reputation: 1174

You can indicate the new process should be started with elevated permissions by setting the Verb property of your startInfo object to 'runas', as follows:

startInfo.Verb = "runas";

This will cause Windows to behave as if the process has been started from Explorer with the "Run as Administrator" menu command. The user will be prompted with the UAC to confirm they want to do it.

Edit: I see you have that verb set already. Are you asking if you can circumvent the UAC? That would be kind of silly, otherwise virus-writers and so forth could get around the security check with one line of code.

Upvotes: 2

Related Questions