Is there a way to execute a single command as an administrator (other than the entire program)?

Asked

Viewed 171 times

8

Did not want to run the entire program with administrator privilege, for security reasons and for convenience.

However I need to execute a command:

exec.Command(`cmd`, `/c`, `CheckNetIsolation.exe`, `LoopbackExempt`, `-a`, `-n=....`)

This ONLY works when the program opens "as an administrator". That is, I have open the meu-programa.exe as administrator for the command to work. Without this, it results in an error.

The entire program runs without administrator permission, EXCEPT for this code above. It is the only command that requires this privilege.


It doesn’t make much sense to have to open as an administrator just because of a single line of code, and everything would work without it.

Is there any way to JUST require the administrator to execute that command? That is, something like (that doesn’t exist!):

exec.Command(`cmd`, `/c`, `/admin`, `....`)

That is, the /admin would appear that "screen" requesting the user authorization to run as administrator... Anyway, the entire program would still be without the privileges and only this command would be executed as administrator.

Is there anything like?

I have noticed that some programs, during execution and at some random time, require you to grant administrator permission. How this is done is something similar to this case?


Note: The entire program is only one .exe, without .dll or others .exe or related. I can even include other files (such as a .bat) and extract it at runtime, but I don’t know if it’s a good way, but it might ease the process.

  • I just looked over, see if there’s any... https://stackoverflow.com/questions/31558066/how-to-ask-for-privileges-administeron-windows-with-go

  • There is the possibility to open this program with powershell

  • 2

    I believe that these other programs have inside them mini-programs or else it is installed together, being internal is "extracted" (or generated) in the TMP of the operating system and probably run using the start command (https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/start) to run as admin (to request the UAC), the PAI programme, must have a communication signal with the "son" (the extracted one), the father probably expects a signal from the son, and the child shut down arbitrarily it also detects. Finally, this sign should be a simple "socket" or even a file.

  • I think that already gives you a north of how to develop something.

  • His 'Note' does not correspond to what is practiced, even because Rust and Go language that are modern have interoperability with C/C++ and Wrappers. Go has the default syscall library, to load Dlls and so make calls. In complex and coherent projects divide to conquer, for analogy, the layers of TCP/IP networks were created precisely because it is impossible to do everything together and mixed.

  • @aldison What I emphasize is a single executable, take two clicks and run the application without using installers or other dependencies. It is possible to load Dlls, I did not say it is not. The problem is that if it is not static, it will be necessary to include them within the application (such as var DLL = []byte{ a dll }, IN THIS CASE. Then extract it and use the windows.NewLazyDLL. This works but continues "a single . exe". So if there are two .exe, the second would be inside the first (var SegundoEXE = ... :Q), as @Guilherme Nascimento mentioned. The problem with this is that it looks kind of Overkill.

  • @Inkeliz I understand! I made an update on the answer, I realized it’s possible with GOROUTINE, and congratulations on the question.

  • Only one detail, instead of using PHYSICALDRIVE0 would be preferable to use TMP, remember that if you have multiple users accessing the machine, be it two logged in accounts or even one logged in and another "remote" will already have problems, all this requires a attention and will have problems if you do not, so much so that I did not formulate the answer, because it is something that depends on a number of situations.

Show 3 more comments

2 answers

5

I found a solution based on @Augusto Vasques, that is to use Powershell.


It is possible to use the Start-Process combined with the -Verb RunAs, to run as administrator. But, there is a, you can’t get the result. You can get the result when you use -NoNewWindow, but this does not work combined with the -Verb RunAs. An alternative would be to use, through CMD, something like > SeuArquivo.txt and then read this file later.

In my case, specifically, it is not necessary to get the return of the command directly. The result can be obtained using another command, such as CheckNetIsolation.exe LoopbackExempt -s, then checking what you requested is listed and this action does not require administrative permission.


In total, instead of:

exec.Command(`cmd`, `/c`, `CheckNetIsolation.exe`, `LoopbackExempt`, `-a`, `-n=....`)

Became:

exec.Command(
   `powershell.exe`, 
   `-Command`,
   `Start-Process cmd '/c CheckNetIsolation.exe LoopbackExempt -a -n=....' -Verb RunAs -WindowStyle hidden -Wait`,
)

In general would be:

exec.Command(
   `powershell.exe`, 
   `-Command`,
   `Start-Process cmd '/c {{O QUE VOCÊ QUER EXECUTAR}}' -Verb RunAs -WindowStyle hidden -Wait`,
)

That was enough. Now, when executing is requested the rights of administrator, only for execution of cmd '/c CheckNetIsolation.exe LoopbackExempt -a -n=....'. This does not affect the main application, which continues without administrative rights.


In my case, it is not necessary to answer (stdout?!), but if necessary you can use something like:

name := make([]byte, 24)
rand.Read(name)

path := filepath.Join(os.TempDir(), hex.EncodeToString(name))
defer os.Remove(path)


err = exec.Command(
    `powershell.exe`, 
    `-Command`,
    `Start-Process cmd '/c CheckNetIsolation.exe LoopbackExempt -a -n=.... > `+path+`' -Verb RunAs -WindowStyle hidden -Wait`).Run()
if err != nil {
    return err
}

result, err := ioutil.ReadFile(path)
if err != nil {
    return err
}

It’s not very elegant, but it looks functional.


I’m still seeing if there’s any problem, but it seems to work.

3


It is possible yes to be admin, there is this gist of Jeremy Black

To see the result of Command, add .CombinedOutput() at the end of exec.Command(args).CombineOutput()

package main

import (
    "fmt"
    "os"
    "os/exec"
    "strings"
    "syscall"

    "time"

    "golang.org/x/sys/windows"
)

func main() {
    if !amAdmin() {
        runMeElevated()
    } else {
        res, _ := exec.Command(`cmd`, `/c`, `CheckNetIsolation.exe`, `LoopbackExempt`, `-a`, `-n=....`).CombinedOutput()
        fmt.Println(string(res))
        time.Sleep(5 * time.Second)
    }
}

func runMeElevated() {
    verb := "runas"
    exe, _ := os.Executable()
    cwd, _ := os.Getwd()
    args := strings.Join(os.Args[1:], " ")

    verbPtr, _ := syscall.UTF16PtrFromString(verb)
    exePtr, _ := syscall.UTF16PtrFromString(exe)
    cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
    argPtr, _ := syscall.UTF16PtrFromString(args)

    var showCmd int32 = 1 //SW_NORMAL

    err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
    if err != nil {
        fmt.Println(err)
    }
}

func amAdmin() bool {
    _, err := os.Open("\\\\.\\PHYSICALDRIVE0")
    if err != nil {
        fmt.Println("admin no")
        return false
    }
    fmt.Println("admin yes")
    return true
}

As you would like a temporary admin, the ideal is to create another executable:

AppFolder
  --programa1.exe // normal sem privilégios
  --programa2.exe // solicitará privilégios

Program 1:

func main() {
    local, _ := os.Getwd()
    call := fmt.Sprintf("%s/programa2.exe", local)
    exec.Command(`cmd`, `/c`, call)
}

Program 2:

func main() {
    if !amAdmin() {
        runMeElevated()
    } else {
        res, _ := exec.Command(`cmd`, `/c`, `CheckNetIsolation.exe`, `LoopbackExempt`, `-a`, `-n=....`).CombinedOutput()

        file, _ := os.OpenFile("result.txt", os.O_CREATE|os.O_WRONLY, 0644)
        file.Write(res)
    }
}

UPDATE:

to be free of other 'exes' I observed that it is possible using goroutine:

var wg sync.WaitGroup
func main() {

    wg.Add(1)
    go goroutineAdmin()
    wg.Wait()
    fmt.Println("Programa continua executando sem privilegios")
    //check
    amAdmin()

}

func goroutineAdmin() {
    if !amAdmin() {
        runMeElevated()
        wg.Done()
    } else {
        res, _ := exec.Command(`cmd`, `/c`, `CheckNetIsolation.exe`, `LoopbackExempt`, `-a`, `-n=....`).CombinedOutput()

        file, _ := os.OpenFile("result.txt", os.O_CREATE|os.O_WRONLY, 0644)
        file.Write(res)

    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.