Search code examples
powershellgoipc

Is it possible to execute a memory-mapped, PowerShell script file?


I have a Go application that takes in PowerShell commands as input, creates a new ps1 file, writes the commands to the file, then executes the script file.

It would be great if the file were not visible to users.

If there were some way to create the script file as a memory-mapped file and then execute that memory-mapped file, that would be a good solution.

Using CGo, I can call into C or C++ from the Go application. Since PowerShell can work with C# objects, I'm thinking that maybe we can somehow call into C# code (either from C/C++ or Go) and create a C# memory-mapped file object that PowerShell can then use to execute as a script.

Admittedly, I know nothing about how calling managed code from unmanaged code works.

So, is there some way to take this string containing PowerShell commands and execute it as a script without creating an on-disk file?


Solution

  • To make powershell.exe read script input as standard input, pass - as a command line argument:

    package main
    
    import (
        "bytes"
        "fmt"
        "log"
        "os/exec"
        "strings"
    )
    
    func main() {
        cmd := exec.Command("powershell", "-")
        cmd.Stdin = strings.NewReader("Write-Output 'Hello, World!'")
        var out bytes.Buffer
        cmd.Stdout = &out
        err := cmd.Run()
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("pwsh out: %q\n", out.String())
    }
    

    With Go 1.16 you can embed the script in the executable at build time:

    package main
    
    import (
        "bytes"
        _ "embed"
        "fmt"
        "log"
        "os/exec"
        "strings"
    )
    
    //go:embed script.ps1
    var scriptBytes []byte
    
    func main() {
        cmd := exec.Command("powershell", "-")
        cmd.Stdin = strings.NewReader(string(scriptBytes))
        var out bytes.Buffer
        cmd.Stdout = &out
        err := cmd.Run()
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("pwsh out: %q\n", out.String())
    }